Add high-level window function wrapper to the JNI wrapper1 interface.
FossilOrigin-Name: a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708
This commit is contained in:
parent
166c8d0067
commit
96aa4d344d
@ -120,6 +120,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
|
||||
Sqlite.java \
|
||||
SqliteException.java \
|
||||
ValueHolder.java \
|
||||
WindowFunction.java \
|
||||
)
|
||||
|
||||
JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
|
||||
|
@ -42,9 +42,75 @@ public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
/**
|
||||
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(sqlite3_context cx, T initialValue){
|
||||
final Long key = cx.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 cx.getAggregateContext() 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(sqlite3_context cx){
|
||||
final ValueHolder<T> h = map.remove(cx.getAggregateContext(false));
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-invocation state for the UDF. */
|
||||
private final SQLFunction.PerContextState<T> map =
|
||||
new SQLFunction.PerContextState<>();
|
||||
private final PerContextState<T> map = new PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
|
@ -33,71 +33,4 @@ package org.sqlite.jni.capi;
|
||||
*/
|
||||
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(sqlite3_context cx, T initialValue){
|
||||
final Long key = cx.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 cx.getAggregateContext() 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(sqlite3_context cx){
|
||||
final ValueHolder<T> h = map.remove(cx.getAggregateContext(false));
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -929,7 +929,6 @@ public class Tester1 implements Runnable {
|
||||
"ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+
|
||||
") AS sum_y "+
|
||||
"FROM twin ORDER BY x;");
|
||||
affirm( 0 == rc );
|
||||
int n = 0;
|
||||
while( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||
final String s = sqlite3_column_text16(stmt, 0);
|
||||
|
@ -51,9 +51,75 @@ public abstract class AggregateFunction<T> implements SqlFunction {
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
/**
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-invocation state for the UDF. */
|
||||
private final SqlFunction.PerContextState<T> map =
|
||||
new SqlFunction.PerContextState<>();
|
||||
private final PerContextState<T> map = new PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
|
@ -22,6 +22,17 @@ import org.sqlite.jni.capi.sqlite3_value;
|
||||
*/
|
||||
public interface SqlFunction {
|
||||
|
||||
public static final int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC;
|
||||
public static final int INNOCUOUS = CApi.SQLITE_INNOCUOUS;
|
||||
public static final int DIRECTONLY = CApi.SQLITE_DIRECTONLY;
|
||||
public static final int UTF8 = CApi.SQLITE_UTF8;
|
||||
public static final int UTF16 = CApi.SQLITE_UTF16;
|
||||
// /**
|
||||
// For Window functions only and is not currently bound because
|
||||
// doing so may require exposing sqlite3_value for effective use.
|
||||
// */
|
||||
// public static final int SUBTYPE = CApi.SQLITE_SUBTYPE;
|
||||
|
||||
/**
|
||||
The Arguments type is an abstraction on top of the lower-level
|
||||
UDF function argument types. It provides _most_ of the functionality
|
||||
@ -49,6 +60,82 @@ public interface SqlFunction {
|
||||
this.length = this.args.length;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the sqlite3_value at the given argument index or throws
|
||||
an IllegalArgumentException exception if ndx is out of range.
|
||||
*/
|
||||
private sqlite3_value valueAt(int ndx){
|
||||
if(ndx<0 || ndx>=args.length){
|
||||
throw new IllegalArgumentException(
|
||||
"SQL function argument index "+ndx+" is out of range."
|
||||
);
|
||||
}
|
||||
return args[ndx];
|
||||
}
|
||||
|
||||
//! Returns the underlying sqlite3_context for these arguments.
|
||||
sqlite3_context getContext(){return cx;}
|
||||
|
||||
public int getArgCount(){ return args.length; }
|
||||
|
||||
public int getInt(int argNdx){return CApi.sqlite3_value_int(valueAt(argNdx));}
|
||||
public long getInt64(int argNdx){return CApi.sqlite3_value_int64(valueAt(argNdx));}
|
||||
public double getDouble(int argNdx){return CApi.sqlite3_value_double(valueAt(argNdx));}
|
||||
public byte[] getBlob(int argNdx){return CApi.sqlite3_value_blob(valueAt(argNdx));}
|
||||
public byte[] getText(int argNdx){return CApi.sqlite3_value_text(valueAt(argNdx));}
|
||||
public String getText16(int argNdx){return CApi.sqlite3_value_text16(valueAt(argNdx));}
|
||||
public int getBytes(int argNdx){return CApi.sqlite3_value_bytes(valueAt(argNdx));}
|
||||
public int getBytes16(int argNdx){return CApi.sqlite3_value_bytes16(valueAt(argNdx));}
|
||||
public Object getObject(int argNdx){return CApi.sqlite3_value_java_object(valueAt(argNdx));}
|
||||
public <T> T getObject(int argNdx, Class<T> type){
|
||||
return CApi.sqlite3_value_java_object(valueAt(argNdx), type);
|
||||
}
|
||||
|
||||
public int getType(int argNdx){return CApi.sqlite3_value_type(valueAt(argNdx));}
|
||||
public int getSubtype(int argNdx){return CApi.sqlite3_value_subtype(valueAt(argNdx));}
|
||||
public int getNumericType(int argNdx){return CApi.sqlite3_value_numeric_type(valueAt(argNdx));}
|
||||
public int getNoChange(int argNdx){return CApi.sqlite3_value_nochange(valueAt(argNdx));}
|
||||
public boolean getFromBind(int argNdx){return CApi.sqlite3_value_frombind(valueAt(argNdx));}
|
||||
public int getEncoding(int argNdx){return CApi.sqlite3_value_encoding(valueAt(argNdx));}
|
||||
|
||||
public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); }
|
||||
public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); }
|
||||
public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); }
|
||||
public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);}
|
||||
public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);}
|
||||
public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);}
|
||||
public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);}
|
||||
public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);}
|
||||
public void resultNull(){CApi.sqlite3_result_null(cx);}
|
||||
public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));}
|
||||
public void resultZeroBlob(long n){
|
||||
// Throw on error? If n is too big,
|
||||
// sqlite3_result_error_toobig() is automatically called.
|
||||
CApi.sqlite3_result_zeroblob64(cx, n);
|
||||
}
|
||||
|
||||
public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);}
|
||||
public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);}
|
||||
public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);}
|
||||
public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);}
|
||||
public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);}
|
||||
|
||||
public void setAuxData(int argNdx, Object o){
|
||||
/* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html
|
||||
|
||||
The value of the N parameter to these interfaces should be
|
||||
non-negative. Future enhancements may make use of negative N
|
||||
values to define new kinds of function caching behavior.
|
||||
*/
|
||||
valueAt(argNdx);
|
||||
CApi.sqlite3_set_auxdata(cx, argNdx, o);
|
||||
}
|
||||
|
||||
public Object getAuxData(int argNdx){
|
||||
valueAt(argNdx);
|
||||
return CApi.sqlite3_get_auxdata(cx, argNdx);
|
||||
}
|
||||
|
||||
/**
|
||||
Wrapper for a single SqlFunction argument. Primarily intended
|
||||
for use with the Arguments class's Iterable interface.
|
||||
@ -87,147 +174,6 @@ public interface SqlFunction {
|
||||
return java.util.Arrays.stream(proxies).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the sqlite3_value at the given argument index or throws
|
||||
an IllegalArgumentException exception if ndx is out of range.
|
||||
*/
|
||||
private sqlite3_value valueAt(int ndx){
|
||||
if(ndx<0 || ndx>=args.length){
|
||||
throw new IllegalArgumentException(
|
||||
"SQL function argument index "+ndx+" is out of range."
|
||||
);
|
||||
}
|
||||
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));}
|
||||
public long getInt64(int arg){return CApi.sqlite3_value_int64(valueAt(arg));}
|
||||
public double getDouble(int arg){return CApi.sqlite3_value_double(valueAt(arg));}
|
||||
public byte[] getBlob(int arg){return CApi.sqlite3_value_blob(valueAt(arg));}
|
||||
public byte[] getText(int arg){return CApi.sqlite3_value_text(valueAt(arg));}
|
||||
public String getText16(int arg){return CApi.sqlite3_value_text16(valueAt(arg));}
|
||||
public int getBytes(int arg){return CApi.sqlite3_value_bytes(valueAt(arg));}
|
||||
public int getBytes16(int arg){return CApi.sqlite3_value_bytes16(valueAt(arg));}
|
||||
public Object getObject(int arg){return CApi.sqlite3_value_java_object(valueAt(arg));}
|
||||
public <T> T getObject(int arg, Class<T> type){
|
||||
return CApi.sqlite3_value_java_object(valueAt(arg), type);
|
||||
}
|
||||
|
||||
public int getType(int arg){return CApi.sqlite3_value_type(valueAt(arg));}
|
||||
public int getSubtype(int arg){return CApi.sqlite3_value_subtype(valueAt(arg));}
|
||||
public int getNumericType(int arg){return CApi.sqlite3_value_numeric_type(valueAt(arg));}
|
||||
public int getNoChange(int arg){return CApi.sqlite3_value_nochange(valueAt(arg));}
|
||||
public boolean getFromBind(int arg){return CApi.sqlite3_value_frombind(valueAt(arg));}
|
||||
public int getEncoding(int arg){return CApi.sqlite3_value_encoding(valueAt(arg));}
|
||||
|
||||
public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); }
|
||||
public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); }
|
||||
public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); }
|
||||
public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);}
|
||||
public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);}
|
||||
public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);}
|
||||
public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);}
|
||||
public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);}
|
||||
public void resultNull(){CApi.sqlite3_result_null(cx);}
|
||||
public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));}
|
||||
public void resultZeroBlob(long n){
|
||||
// Throw on error? If n is too big,
|
||||
// sqlite3_result_error_toobig() is automatically called.
|
||||
CApi.sqlite3_result_zeroblob64(cx, n);
|
||||
}
|
||||
|
||||
public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);}
|
||||
public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);}
|
||||
public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);}
|
||||
public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);}
|
||||
public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);}
|
||||
|
||||
public void setAuxData(int arg, Object o){
|
||||
/* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html
|
||||
|
||||
The value of the N parameter to these interfaces should be
|
||||
non-negative. Future enhancements may make use of negative N
|
||||
values to define new kinds of function caching behavior.
|
||||
*/
|
||||
valueAt(arg);
|
||||
CApi.sqlite3_set_auxdata(cx, arg, o);
|
||||
}
|
||||
|
||||
public Object getAuxData(int arg){
|
||||
valueAt(arg);
|
||||
return CApi.sqlite3_get_auxdata(cx, arg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -235,7 +181,7 @@ public interface SqlFunction {
|
||||
for use with the org.sqlite.jni.capi.ScalarFunction interface.
|
||||
*/
|
||||
static final class ScalarAdapter extends org.sqlite.jni.capi.ScalarFunction {
|
||||
final ScalarFunction impl;
|
||||
private final ScalarFunction impl;
|
||||
ScalarAdapter(ScalarFunction impl){
|
||||
this.impl = impl;
|
||||
}
|
||||
@ -261,8 +207,9 @@ public interface SqlFunction {
|
||||
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;
|
||||
static /*cannot be final without duplicating the whole body in WindowAdapter*/
|
||||
class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction {
|
||||
private final AggregateFunction impl;
|
||||
AggregateAdapter(AggregateFunction impl){
|
||||
this.impl = impl;
|
||||
}
|
||||
@ -282,8 +229,9 @@ public interface SqlFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
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().
|
||||
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{
|
||||
@ -298,4 +246,46 @@ public interface SqlFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Internal-use adapter for wrapping this package's WindowFunction
|
||||
for use with the org.sqlite.jni.capi.WindowFunction interface.
|
||||
*/
|
||||
static final class WindowAdapter extends AggregateAdapter {
|
||||
private final WindowFunction impl;
|
||||
WindowAdapter(WindowFunction impl){
|
||||
super(impl);
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
Proxies this.impl.xInverse(), 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 xInverse(sqlite3_context cx, sqlite3_value[] args){
|
||||
try{
|
||||
impl.xInverse( new SqlFunction.Arguments(cx, args) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
As for the xValue() argument of the C API's sqlite3_create_window_function().
|
||||
If the proxied function throws, it is translated into a sqlite3_result_error().
|
||||
*/
|
||||
public void xValue(sqlite3_context cx){
|
||||
try{
|
||||
impl.xValue( new SqlFunction.Arguments(cx, null) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void xDestroy(){
|
||||
impl.xDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,6 +29,10 @@ import org.sqlite.jni.capi.OutputPointer;
|
||||
public final class Sqlite implements AutoCloseable {
|
||||
private sqlite3 db;
|
||||
|
||||
public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE;
|
||||
public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE;
|
||||
public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE;
|
||||
|
||||
//! Used only by the open() factory functions.
|
||||
private Sqlite(sqlite3 db){
|
||||
this.db = db;
|
||||
@ -120,7 +124,7 @@ public final class Sqlite implements AutoCloseable {
|
||||
return prepare(sql, 0);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f ){
|
||||
public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f){
|
||||
int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep,
|
||||
new SqlFunction.ScalarAdapter(f));
|
||||
if( 0!=rc ) throw new SqliteException(db);
|
||||
@ -130,7 +134,7 @@ 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 ){
|
||||
public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f){
|
||||
int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep,
|
||||
new SqlFunction.AggregateAdapter(f));
|
||||
if( 0!=rc ) throw new SqliteException(db);
|
||||
@ -140,6 +144,16 @@ public final class Sqlite implements AutoCloseable {
|
||||
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, int eTextRep, WindowFunction f){
|
||||
int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep,
|
||||
new SqlFunction.WindowAdapter(f));
|
||||
if( 0!=rc ) throw new SqliteException(db);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, WindowFunction f){
|
||||
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
|
||||
}
|
||||
|
||||
/**
|
||||
Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to
|
||||
create new instances.
|
||||
@ -223,11 +237,19 @@ public final class Sqlite implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
Works like sqlite3_step() but throws SqliteException for any
|
||||
result other than 0, SQLITE_ROW, or SQLITE_DONE.
|
||||
Works like sqlite3_step() but returns true for SQLITE_ROW,
|
||||
false for SQLITE_DONE, and throws SqliteException for any other
|
||||
result.
|
||||
*/
|
||||
public int step(){
|
||||
return checkRc(sqlite3_step(thisStmt()));
|
||||
public boolean step(){
|
||||
switch(checkRc(sqlite3_step(thisStmt()))){
|
||||
case CApi.SQLITE_ROW: return true;
|
||||
case CApi.SQLITE_DONE: return false;
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"This \"cannot happen\": all possible result codes were checked already."
|
||||
);
|
||||
}
|
||||
/*
|
||||
Potential signature change TODO:
|
||||
|
||||
|
@ -129,6 +129,11 @@ public class Tester2 implements Runnable {
|
||||
execSql(db, String.join("", sql));
|
||||
}
|
||||
|
||||
/**
|
||||
Executes all SQL statements in the given string. If throwOnError
|
||||
is true then it will throw for any prepare/step errors, else it
|
||||
will return the corresponding non-0 result code.
|
||||
*/
|
||||
public static int execSql(Sqlite dbw, boolean throwOnError, String sql){
|
||||
final sqlite3 db = dbw.nativeHandle();
|
||||
OutputPointer.Int32 oTail = new OutputPointer.Int32();
|
||||
@ -145,7 +150,7 @@ public class Tester2 implements Runnable {
|
||||
}
|
||||
if( 0==sqlChunk.length ) break;
|
||||
rc = CApi.sqlite3_prepare_v2(db, sqlChunk, outStmt, oTail);
|
||||
if(throwOnError) affirm(0 == rc);
|
||||
if( throwOnError ) affirm(0 == rc);
|
||||
else if( 0!=rc ) break;
|
||||
pos = oTail.value;
|
||||
stmt = outStmt.take();
|
||||
@ -158,7 +163,7 @@ public class Tester2 implements Runnable {
|
||||
}
|
||||
CApi.sqlite3_finalize(stmt);
|
||||
affirm(0 == stmt.getNativePointer());
|
||||
if(0!=rc && CApi.SQLITE_ROW!=rc && CApi.SQLITE_DONE!=rc){
|
||||
if(CApi.SQLITE_DONE!=rc){
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -194,9 +199,9 @@ public class Tester2 implements Runnable {
|
||||
}
|
||||
|
||||
Sqlite openDb(String name){
|
||||
final Sqlite db = Sqlite.open(name, CApi.SQLITE_OPEN_READWRITE|
|
||||
CApi.SQLITE_OPEN_CREATE|
|
||||
CApi.SQLITE_OPEN_EXRESCODE);
|
||||
final Sqlite db = Sqlite.open(name, Sqlite.OPEN_READWRITE|
|
||||
Sqlite.OPEN_CREATE|
|
||||
Sqlite.OPEN_EXRESCODE);
|
||||
++metrics.dbOpen;
|
||||
return db;
|
||||
}
|
||||
@ -245,7 +250,7 @@ public class Tester2 implements Runnable {
|
||||
catch(Exception ex){ e = ex; }
|
||||
affirm( null!=e );
|
||||
e = null;
|
||||
affirm( CApi.SQLITE_ROW == stmt.step() );
|
||||
affirm( stmt.step() );
|
||||
try{ stmt.columnInt(1); }
|
||||
catch(Exception ex){ e = ex; }
|
||||
affirm( null!=e );
|
||||
@ -254,16 +259,16 @@ public class Tester2 implements Runnable {
|
||||
affirm( 17L == stmt.columnInt64(0) );
|
||||
affirm( 17.0 == stmt.columnDouble(0) );
|
||||
affirm( "17".equals(stmt.columnText16(0)) );
|
||||
affirm( CApi.SQLITE_DONE == stmt.step() );
|
||||
affirm( !stmt.step() );
|
||||
stmt.reset();
|
||||
affirm( CApi.SQLITE_ROW == stmt.step() );
|
||||
affirm( CApi.SQLITE_DONE == stmt.step() );
|
||||
affirm( stmt.step() );
|
||||
affirm( !stmt.step() );
|
||||
affirm( 0 == stmt.finalizeStmt() );
|
||||
affirm( null==stmt.nativeHandle() );
|
||||
|
||||
stmt = db.prepare("SELECT ?");
|
||||
stmt.bindObject(1, db);
|
||||
affirm( CApi.SQLITE_ROW == stmt.step() );
|
||||
affirm( stmt.step() );
|
||||
affirm( db==stmt.columnObject(0) );
|
||||
affirm( db==stmt.columnObject(0, Sqlite.class ) );
|
||||
affirm( null==stmt.columnObject(0, Sqlite.Stmt.class ) );
|
||||
@ -328,7 +333,7 @@ public class Tester2 implements Runnable {
|
||||
/* ------------------^^^^^^^^^^^ ensures that we're handling
|
||||
sqlite3_aggregate_context() properly. */
|
||||
);
|
||||
affirm( CApi.SQLITE_ROW==q.step() );
|
||||
affirm( q.step() );
|
||||
affirm( 15==q.columnInt(0) );
|
||||
q.finalizeStmt();
|
||||
q = null;
|
||||
@ -336,7 +341,7 @@ public class Tester2 implements Runnable {
|
||||
db.createFunction("summerN", -1, f);
|
||||
|
||||
q = db.prepare("select summerN(1,8,9), summerN(2,3,4)");
|
||||
affirm( CApi.SQLITE_ROW==q.step() );
|
||||
affirm( q.step() );
|
||||
affirm( 18==q.columnInt(0) );
|
||||
affirm( 9==q.columnInt(1) );
|
||||
q.finalizeStmt();
|
||||
@ -350,6 +355,63 @@ public class Tester2 implements Runnable {
|
||||
/* because we've bound the same instance twice */ );
|
||||
}
|
||||
|
||||
private void testUdfWindow(){
|
||||
final Sqlite db = openDb();
|
||||
/* Example window function, table, and results taken from:
|
||||
https://sqlite.org/windowfunctions.html#udfwinfunc */
|
||||
final WindowFunction func = new WindowFunction<Integer>(){
|
||||
//! Impl of xStep() and xInverse()
|
||||
private void xStepInverse(SqlFunction.Arguments args, int v){
|
||||
this.getAggregateState(args,0).value += v;
|
||||
}
|
||||
@Override public void xStep(SqlFunction.Arguments args){
|
||||
this.xStepInverse(args, args.getInt(0));
|
||||
}
|
||||
@Override public void xInverse(SqlFunction.Arguments args){
|
||||
this.xStepInverse(args, -args.getInt(0));
|
||||
}
|
||||
//! Impl of xFinal() and xValue()
|
||||
private void xFinalValue(SqlFunction.Arguments args, Integer v){
|
||||
if(null == v) args.resultNull();
|
||||
else args.resultInt(v);
|
||||
}
|
||||
@Override public void xFinal(SqlFunction.Arguments args){
|
||||
xFinalValue(args, this.takeAggregateState(args));
|
||||
affirm( null == this.getAggregateState(args,null).value );
|
||||
}
|
||||
@Override public void xValue(SqlFunction.Arguments args){
|
||||
xFinalValue(args, this.getAggregateState(args,null).value);
|
||||
}
|
||||
};
|
||||
db.createFunction("winsumint", 1, func);
|
||||
execSql(db, new String[] {
|
||||
"CREATE TEMP TABLE twin(x, y); INSERT INTO twin VALUES",
|
||||
"('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)"
|
||||
});
|
||||
final Sqlite.Stmt stmt = db.prepare(
|
||||
"SELECT x, winsumint(y) OVER ("+
|
||||
"ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+
|
||||
") AS sum_y "+
|
||||
"FROM twin ORDER BY x;"
|
||||
);
|
||||
int n = 0;
|
||||
while( stmt.step() ){
|
||||
final String s = stmt.columnText16(0);
|
||||
final int i = stmt.columnInt(1);
|
||||
switch(++n){
|
||||
case 1: affirm( "a".equals(s) && 9==i ); break;
|
||||
case 2: affirm( "b".equals(s) && 12==i ); break;
|
||||
case 3: affirm( "c".equals(s) && 16==i ); break;
|
||||
case 4: affirm( "d".equals(s) && 12==i ); break;
|
||||
case 5: affirm( "e".equals(s) && 9==i ); break;
|
||||
default: affirm( false /* cannot happen */ );
|
||||
}
|
||||
}
|
||||
stmt.close();
|
||||
affirm( 5 == n );
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void runTests(boolean fromThread) throws Exception {
|
||||
List<java.lang.reflect.Method> mlist = testMethods;
|
||||
affirm( null!=mlist );
|
||||
|
46
ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java
Normal file
46
ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
** 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;
|
||||
|
||||
/**
|
||||
A SqlFunction implementation for window functions. The T type
|
||||
represents the type of data accumulated by this function 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 WindowFunction<T> extends AggregateFunction<T> {
|
||||
|
||||
/**
|
||||
As for the xInverse() argument of the C API's
|
||||
sqlite3_create_window_function(). If this function throws, the
|
||||
exception is reported via sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xInverse(SqlFunction.Arguments args);
|
||||
|
||||
/**
|
||||
As for the xValue() argument of the C API's
|
||||
sqlite3_create_window_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 xValue() but will contain the context object needed
|
||||
for setting the call's result or error state.
|
||||
*/
|
||||
public abstract void xValue(SqlFunction.Arguments args);
|
||||
|
||||
}
|
27
manifest
27
manifest
@ -1,5 +1,5 @@
|
||||
C JNI:\sflesh\sout\sand\ssimplify\sthe\sAPIs\sfor\sbinding\sand\sfetching\sarbitrary\sJava\sobjects.
|
||||
D 2023-10-22T14:25:37.634
|
||||
C Add\shigh-level\swindow\sfunction\swrapper\sto\sthe\sJNI\swrapper1\sinterface.
|
||||
D 2023-10-22T23:36:16.690
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -236,7 +236,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 5c3ac326bf3853486ebe0d70819abc790cc65c412182ce4ebd5012b008d9b059
|
||||
F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072
|
||||
F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4
|
||||
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
|
||||
F ext/jni/src/c/sqlite3-jni.c dcd6534b65b732ad927a49185c76c76abbd5ccadfa972d02f699abc45678e329
|
||||
@ -245,7 +245,7 @@ F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a
|
||||
F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba
|
||||
F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca
|
||||
F ext/jni/src/org/sqlite/jni/capi/AbstractCollationCallback.java 1afa90d3f236f79cc7fcd2497e111992644f7596fbc8e8bcf7f1908ae00acd6c
|
||||
F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java bc29e986c866c2ddbbb9f935f5b7264c1c1026864e50a4a735192864f75e37c0
|
||||
F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63418129656daa9a9f30e7e7be982bd5ab394b1dbd0
|
||||
F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013
|
||||
F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759
|
||||
F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca
|
||||
@ -263,11 +263,11 @@ F ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java 819d938e26208adde17
|
||||
F ext/jni/src/org/sqlite/jni/capi/ProgressHandlerCallback.java 01bc0c238eed2d5f93c73522cb7849a445cc9098c2ed1e78248fa20ed1cfde5b
|
||||
F ext/jni/src/org/sqlite/jni/capi/ResultCode.java 8141171f1bcf9f46eef303b9d3c5dc2537a25ad1628f3638398d8a60cacefa7f
|
||||
F ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java 105e324d09c207100485e7667ad172e64322c62426bb49b547e9b0dc9c33f5f0
|
||||
F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java fef556adbc3624292423083a648bdf97fa8a4f6b3b6577c9660dd7bd6a6d3c4a
|
||||
F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385155fa3b8011a5cca0bb3c28468c7131c1a5
|
||||
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 5c4e7ba5034aeb5c5be0361b9fa0c23fe993774e634750c775d7ad8fa19b22f3
|
||||
F ext/jni/src/org/sqlite/jni/capi/Tester1.java b6b2f3354ba68956a6bcd1c586b8eb25a0bd66eed2b58b340405e1129da15de9
|
||||
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
|
||||
@ -291,13 +291,14 @@ 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/AggregateFunction.java 5ad99bd74c85f56bbef324d9ec29b4048f4620547c9a80093d8586c3557f9f9a
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 92c28b9de358407c8c5e772e0408db528e47eeeb50ffd87b86563a5f078198ad
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 882e345d925a79b575b1182efd816dcc72d6814922b4f58e7f4d29f04ece1f64
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 585309311ffce6f39626024bf2ea3add91339f6a146b674720165c1955efbe68
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 3e6cdb5fe1b01a592ba5ca6ae7d11681a85d081786ce8d046ef631a08ae82dde
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java e224efb77dae2f0abe18f2010c0eb5a09df991f2743597a1aff7f9283f71da7d
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 13008f8d3c34c1dd55c3afe6dd18dcf94316874cde893ab0661a973fc51a87a4
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4
|
||||
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
|
||||
@ -2136,8 +2137,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 60a0e82db26270af9d0a5f55c6173e4fd0bdc90a885e838480ed75f8ef193287
|
||||
R 5a1e4de3384c2859d02b694c480544f0
|
||||
P 89fecf1dd8b97941f9b45130a3c8a67af36ec65cc6f70f5026c569c058a4963f
|
||||
R 160b97c24140d26b449a4e16b3331ec4
|
||||
U stephan
|
||||
Z 0b6f35d12f929147f1a8dbbd84db96f1
|
||||
Z 05c9adecf2f51f54547f1d6b2c6aaa3c
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
89fecf1dd8b97941f9b45130a3c8a67af36ec65cc6f70f5026c569c058a4963f
|
||||
a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708
|
Loading…
Reference in New Issue
Block a user