Index: System.Data.SQLite/SQLiteConnection.cs
==================================================================
--- System.Data.SQLite/SQLiteConnection.cs
+++ System.Data.SQLite/SQLiteConnection.cs
@@ -1412,27 +1412,35 @@
///
///
/// The object instance containing
/// the metadata for the function to be bound.
///
- ///
- /// The object instance that implements the
- /// function to be bound.
+ ///
+ /// A object instance that helps implement the
+ /// function to be bound. For aggregate functions, this corresponds to the
+ /// callback.
+ ///
+ ///
+ /// A object instance that helps implement the
+ /// function to be bound. For aggregate functions, this corresponds to the
+ /// callback. For other callback types,
+ /// it is not used and must be null.
///
public void BindFunction(
SQLiteFunctionAttribute functionAttribute,
- Delegate callback
+ Delegate callback1,
+ Delegate callback2
)
{
CheckDisposed();
if (_sql == null)
throw new InvalidOperationException(
"Database connection not valid for binding functions.");
_sql.BindFunction(functionAttribute,
- new SQLiteDelegateFunction(callback), _flags);
+ new SQLiteDelegateFunction(callback1, callback2), _flags);
}
///////////////////////////////////////////////////////////////////////////////////////////////
///
Index: System.Data.SQLite/SQLiteFunction.cs
==================================================================
--- System.Data.SQLite/SQLiteFunction.cs
+++ System.Data.SQLite/SQLiteFunction.cs
@@ -738,11 +738,11 @@
if (at == null)
continue;
RegisterFunction(
at.Name, at.Arguments, at.FuncType, at.InstanceType,
- at.Callback);
+ at.Callback1, at.Callback2);
}
}
///
/// Alternative method of registering a function. This method
@@ -759,31 +759,38 @@
/// The type of SQLite function being resitered (e.g. scalar,
/// aggregate, or collating sequence).
///
///
/// The that actually implements the function.
- /// This will only be used if the
- /// parameter is null.
+ /// This will only be used if the
+ /// and parameters are null.
+ ///
+ ///
+ /// The to be used for all calls into the
+ /// ,
+ /// ,
+ /// and virtual methods.
///
- ///
- /// The that implements the function. If
- /// this is non-null, the parameter
- /// will be ignored when the function is invoked.
+ ///
+ /// The to be used for all calls into the
+ /// virtual method.
///
public static void RegisterFunction(
string name,
int argumentCount,
FunctionType functionType,
Type instanceType,
- Delegate callback
+ Delegate callback1,
+ Delegate callback2
)
{
SQLiteFunctionAttribute at = new SQLiteFunctionAttribute(
name, argumentCount, functionType);
at.InstanceType = instanceType;
- at.Callback = callback;
+ at.Callback1 = callback1;
+ at.Callback2 = callback2;
_registeredFunctions.Add(at, null);
}
///
@@ -808,14 +815,16 @@
if (functionAttribute == null)
{
function = null;
return false;
}
- else if (functionAttribute.Callback != null)
+ else if ((functionAttribute.Callback1 != null) ||
+ (functionAttribute.Callback2 != null))
{
function = new SQLiteDelegateFunction(
- functionAttribute.Callback);
+ functionAttribute.Callback1,
+ functionAttribute.Callback2);
return true;
}
else if (functionAttribute.InstanceType != null)
{
@@ -1189,14 +1198,16 @@
///
public class SQLiteDelegateFunction : SQLiteFunction
{
#region Private Constants
///
- /// This error message is used by the overridden virtual methods when the
- /// callback has not been set.
+ /// This error message is used by the overridden virtual methods when
+ /// a required property (e.g.
+ /// or ) has not been
+ /// set.
///
- private const string NoCallbackError = "No callback is set.";
+ private const string NoCallbackError = "No \"{0}\" callback is set.";
/////////////////////////////////////////////////////////////////////////
///
/// This error message is used by the overridden
@@ -1210,11 +1221,11 @@
#region Public Constructors
///
/// Constructs an empty instance of this class.
///
public SQLiteDelegateFunction()
- : this(null)
+ : this(null, null)
{
// do nothing.
}
/////////////////////////////////////////////////////////////////////////
@@ -1222,20 +1233,28 @@
///
/// Constructs an instance of this class using the specified
/// as the
/// implementation.
///
- ///
+ ///
+ /// The to be used for all calls into the
+ /// , , and
+ /// virtual methods needed by the
+ /// base class.
+ ///
+ ///
/// The to be used for all calls into the
- /// virtual methods needed by the
- /// class.
+ /// virtual methods needed by the
+ /// base class.
///
public SQLiteDelegateFunction(
- Delegate callback
+ Delegate callback1,
+ Delegate callback2
)
{
- this.callback = callback;
+ this.callback1 = callback1;
+ this.callback2 = callback2;
}
#endregion
/////////////////////////////////////////////////////////////////////////
@@ -1418,20 +1437,35 @@
#endregion
/////////////////////////////////////////////////////////////////////////
#region Public Properties
- private Delegate callback;
+ private Delegate callback1;
+ ///
+ /// The to be used for all calls into the
+ /// , , and
+ /// virtual methods needed by the
+ /// base class.
+ ///
+ public virtual Delegate Callback1
+ {
+ get { return callback1; }
+ set { callback1 = value; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ private Delegate callback2;
///
/// The to be used for all calls into the
- /// virtual methods needed by the
- /// class.
+ /// virtual methods needed by the
+ /// base class.
///
- public virtual Delegate Callback
+ public virtual Delegate Callback2
{
- get { return callback; }
- set { callback = value; }
+ get { return callback2; }
+ set { callback2 = value; }
}
#endregion
/////////////////////////////////////////////////////////////////////////
@@ -1449,23 +1483,26 @@
///
public override object Invoke(
object[] args /* in */
)
{
- if (callback == null)
- throw new InvalidOperationException(NoCallbackError);
+ if (callback1 == null)
+ {
+ throw new InvalidOperationException(String.Format(
+ NoCallbackError, "Invoke"));
+ }
SQLiteInvokeDelegate invokeDelegate =
- callback as SQLiteInvokeDelegate;
+ callback1 as SQLiteInvokeDelegate;
if (invokeDelegate != null)
{
return invokeDelegate.Invoke("Invoke", args); /* throw */
}
else
{
- return callback.DynamicInvoke(
+ return callback1.DynamicInvoke(
GetInvokeArgs(args, false)); /* throw */
}
}
/////////////////////////////////////////////////////////////////////////
@@ -1490,14 +1527,17 @@
object[] args, /* in */
int stepNumber, /* in */
ref object contextData /* in, out */
)
{
- if (callback == null)
- throw new InvalidOperationException(NoCallbackError);
+ if (callback1 == null)
+ {
+ throw new InvalidOperationException(String.Format(
+ NoCallbackError, "Step"));
+ }
- SQLiteStepDelegate stepDelegate = callback as SQLiteStepDelegate;
+ SQLiteStepDelegate stepDelegate = callback1 as SQLiteStepDelegate;
if (stepDelegate != null)
{
stepDelegate.Invoke(
"Step", args, stepNumber, ref contextData); /* throw */
@@ -1506,11 +1546,11 @@
{
object[] newArgs = GetStepArgs(
args, stepNumber, contextData, false);
/* IGNORED */
- callback.DynamicInvoke(newArgs); /* throw */
+ callback1.DynamicInvoke(newArgs); /* throw */
UpdateStepArgs(newArgs, ref contextData, false);
}
}
@@ -1530,23 +1570,26 @@
///
public override object Final(
object contextData /* in */
)
{
- if (callback == null)
- throw new InvalidOperationException(NoCallbackError);
+ if (callback2 == null)
+ {
+ throw new InvalidOperationException(String.Format(
+ NoCallbackError, "Final"));
+ }
- SQLiteFinalDelegate finalDelegate = callback as SQLiteFinalDelegate;
+ SQLiteFinalDelegate finalDelegate = callback2 as SQLiteFinalDelegate;
if (finalDelegate != null)
{
- return finalDelegate.Invoke("Final", contextData);
+ return finalDelegate.Invoke("Final", contextData); /* throw */
}
else
{
- return callback.DynamicInvoke(GetFinalArgs(
- contextData, callback is SQLiteFinalDelegate)); /* throw */
+ return callback1.DynamicInvoke(GetFinalArgs(
+ contextData, false)); /* throw */
}
}
/////////////////////////////////////////////////////////////////////////
@@ -1571,23 +1614,27 @@
public override int Compare(
string param1, /* in */
string param2 /* in */
)
{
- if (callback == null)
- throw new InvalidOperationException(NoCallbackError);
+ if (callback1 == null)
+ {
+ throw new InvalidOperationException(String.Format(
+ NoCallbackError, "Compare"));
+ }
SQLiteCompareDelegate compareDelegate =
- callback as SQLiteCompareDelegate;
+ callback1 as SQLiteCompareDelegate;
if (compareDelegate != null)
{
- return compareDelegate.Invoke("Compare", param1, param2);
+ return compareDelegate.Invoke(
+ "Compare", param1, param2); /* throw */
}
else
{
- object result = callback.DynamicInvoke(GetCompareArgs(
+ object result = callback1.DynamicInvoke(GetCompareArgs(
param1, param2, false)); /* throw */
if (result is int)
return (int)result;
Index: System.Data.SQLite/SQLiteFunctionAttribute.cs
==================================================================
--- System.Data.SQLite/SQLiteFunctionAttribute.cs
+++ System.Data.SQLite/SQLiteFunctionAttribute.cs
@@ -18,11 +18,12 @@
{
private string _name;
private int _argumentCount;
private FunctionType _functionType;
private Type _instanceType;
- private Delegate _callback;
+ private Delegate _callback1;
+ private Delegate _callback2;
///
/// Default constructor, initializes the internal variables for the function.
///
public SQLiteFunctionAttribute()
@@ -52,11 +53,12 @@
{
_name = name;
_argumentCount = argumentCount;
_functionType = functionType;
_instanceType = null;
- _callback = null;
+ _callback1 = null;
+ _callback2 = null;
}
///
/// The function's name as it will be used in SQLite command text.
///
@@ -85,12 +87,12 @@
}
///
/// The object instance that describes the class
/// containing the implementation for the associated function. The value of
- /// this property will not be used if the property
- /// value is set to non-null.
+ /// this property will not be used if either the or
+ /// property values are set to non-null.
///
internal Type InstanceType
{
get { return _instanceType; }
set { _instanceType = value; }
@@ -99,12 +101,23 @@
///
/// The that refers to the implementation for the
/// associated function. If this property value is set to non-null, it will
/// be used instead of the property value.
///
- internal Delegate Callback
+ internal Delegate Callback1
{
- get { return _callback; }
- set { _callback = value; }
- }
+ get { return _callback1; }
+ set { _callback1 = value; }
+ }
+
+ ///
+ /// The that refers to the implementation for the
+ /// associated function. If this property value is set to non-null, it will
+ /// be used instead of the property value.
+ ///
+ internal Delegate Callback2
+ {
+ get { return _callback2; }
+ set { _callback2 = value; }
+ }
}
}
Index: Tests/basic.eagle
==================================================================
--- Tests/basic.eagle
+++ Tests/basic.eagle
@@ -3600,64 +3600,134 @@
lappend result [appendArgs 'myFuncArg [expr {$index + 1}] ']
}
return $result
}
+
+ proc getHashCode { value } {
+ if {[isObjectHandle $value]} then {
+ if {$value eq "null"} then {
+ return 0
+ } else {
+ return [object invoke $value GetHashCode]
+ }
+ } else {
+ if {[string length $value] == 0} then {
+ return 0
+ } else {
+ set string [object create String $value]
+
+ return [object invoke $string GetHashCode]
+ }
+ }
+ }
+
+ proc hashManagedArray { array } {
+ set data ""
+
+ if {[isObjectHandle $array] && $array ne "null"} then {
+ if {[object invoke $array GetType.IsArray]} then {
+ for {set index 0} {$index < [$array Length]} {incr index} {
+ set element [$array -create -alias GetValue $index]
+
+ if {[string length $element] > 0} then {
+ append data [$element ToString]
+ } else {
+ append data null
+ }
+ }
+ }
+ }
+
+ return [getHashCode [hash normal sha1 $data]]
+ }
proc myFuncCallback { args } {
if {[llength $args] == 0} then {
error "no function arguments"
}
set name [lindex $args 0]
- switch -exact -- $name {
- Invoke {
- return $args
- }
- Step {
- set ctx [lindex $args 1]
-
- if {[string length $ctx] == 0} then {
- error "invalid aggregate context"
- }
-
- global aggregateData
-
- if {[info exists aggregateData($ctx)]} then {
- incr aggregateData($ctx)
- } else {
- set aggregateData($ctx) 1
- }
- }
- Final {
- set ctx [lindex $args 1]
-
- if {[string length $ctx] == 0} then {
- error "invalid aggregate context"
- }
-
- global aggregateData
-
- if {[info exists aggregateData($ctx)]} then {
- return $aggregateData($ctx)
- } else {
- error "missing aggregate context data"
- }
- }
- Compare {
- return [string compare -nocase [lindex $args 1] [lindex $args 2]]
+ if {[isObjectHandle $name] && $name ne "null"} then {
+ set name [object invoke $name ToString]
+ }
+
+ switch -exact -- $name {
+ Invoke {
+ return [hashManagedArray [lindex $args end]]
+ }
+ Step {
+ set varName [lindex $args end]
+
+ if {[string length $varName] == 0} then {
+ error "invalid aggregate context variable name"
+ }
+
+ upvar 1 $varName ctx
+
+ if {![info exists ctx] || [string length $ctx] == 0} then {
+ set ctx [pid]
+ }
+
+ set hashCtx [getHashCode $ctx]
+ set hashArgs [hashManagedArray [lindex $args end-2]]
+
+ if {[info exists ::aggregateData($hashCtx)]} then {
+ incr ::aggregateData($hashCtx) $hashArgs
+ } else {
+ set ::aggregateData($hashCtx) $hashArgs
+ }
+ }
+ Final {
+ set ctx [lindex $args end]
+
+ if {[string length $ctx] == 0} then {
+ error "invalid aggregate context"
+ }
+
+ set hashCtx [getHashCode $ctx]
+
+ if {[info exists ::aggregateData($hashCtx)]} then {
+ return $::aggregateData($hashCtx)
+ } else {
+ error "missing aggregate context data"
+ }
+ }
+ Compare {
+ lappend ::compareResults [object invoke -create \
+ Int32 Parse [string compare -nocase [lindex \
+ $args 1] [lindex $args 2]]]
+
+ return [lindex $::compareResults end]
}
default {
error [appendArgs "unknown function callback \"" $name \"]
}
}
}
+
+ proc myFuncInvokeCallback { param0 objs } {
+ return [myFuncCallback $param0 $objs]
+ }
+
+ proc myFuncStepCallback { param0 objs stepNumber contextDataVarName } {
+ upvar 1 $contextDataVarName $contextDataVarName
+ return [myFuncCallback $param0 $objs $stepNumber $contextDataVarName]
+ }
+
+ proc myFuncFinalCallback { param0 contextData } {
+ return [myFuncCallback $param0 $contextData ]
+ }
+
+ proc myFuncCompareCallback { param0 param1 param2 } {
+ return [myFuncCallback $param0 $param1 $param2]
+ }
setupDb [set fileName data-1.74.db]
} -body {
- sql execute $db "CREATE TABLE t1(x INTEGER);"
+ sql execute $db "CREATE TABLE t1(x);"
sql execute $db "INSERT INTO t1 (x) VALUES(1);"
sql execute $db "INSERT INTO t1 (x) VALUES(2);"
sql execute $db "INSERT INTO t1 (x) VALUES(3);"
sql execute $db "INSERT INTO t1 (x) VALUES('A');"
sql execute $db "INSERT INTO t1 (x) VALUES('a');"
@@ -3671,23 +3741,40 @@
for {set argumentCount 0} {$argumentCount < 3} {incr argumentCount} {
set attribute(1,$argumentCount) [object create \
System.Data.SQLite.SQLiteFunctionAttribute [appendArgs \
myFunc1_74_1_ $argumentCount] $argumentCount Scalar]
- $connection BindFunction $attribute(1,$argumentCount) myFuncCallback
+ $connection -marshalflags \
+ {-StrictMatchType +DynamicCallback ForceParameterType} \
+ -parametertypes [list System.Data.SQLite.SQLiteFunctionAttribute \
+ System.Data.SQLite.SQLiteInvokeDelegate Delegate] \
+ BindFunction $attribute(1,$argumentCount) \
+ myFuncInvokeCallback null
set attribute(2,$argumentCount) [object create \
System.Data.SQLite.SQLiteFunctionAttribute [appendArgs \
myFunc1_74_2_ $argumentCount] $argumentCount Aggregate]
- $connection BindFunction $attribute(2,$argumentCount) myFuncCallback
+ $connection -marshalflags \
+ {-StrictMatchType +DynamicCallback ForceParameterType} \
+ -parametertypes [list System.Data.SQLite.SQLiteFunctionAttribute \
+ System.Data.SQLite.SQLiteStepDelegate \
+ System.Data.SQLite.SQLiteFinalDelegate] \
+ BindFunction $attribute(2,$argumentCount) \
+ myFuncStepCallback myFuncFinalCallback
}
set attribute(3,0) [object create \
- System.Data.SQLite.SQLiteFunctionAttribute myFunc1_74_3 0 Collation]
+ System.Data.SQLite.SQLiteFunctionAttribute myFunc1_74_3 0 \
+ Collation]
- $connection BindFunction $attribute(3,0) myFuncCallback
+ $connection -marshalflags \
+ {-StrictMatchType +DynamicCallback ForceParameterType} \
+ -parametertypes [list System.Data.SQLite.SQLiteFunctionAttribute \
+ System.Data.SQLite.SQLiteCompareDelegate Delegate] \
+ BindFunction $attribute(3,0) \
+ myFuncCompareCallback null
for {set argumentCount 0} {$argumentCount < 3} {incr argumentCount} {
lappend result [catch {
sql execute $db [appendArgs \
"SELECT " myFunc1_74_1_ $argumentCount ( \
@@ -3700,11 +3787,12 @@
[join [getMyFuncArgs $argumentCount] ,] )\;]
} error] $error
}
lappend result [catch {
- sql execute $db "SELECT x FROM t1 ORDER BY x COLLATE myFunc1_74_3;"
+ sql execute -execute reader -format list $db \
+ "SELECT x FROM t1 ORDER BY x COLLATE myFunc1_74_3;"
} error] $error
lappend result [$connection UnbindAllFunctions false]
for {set argumentCount 0} {$argumentCount < 3} {incr argumentCount} {
@@ -3722,29 +3810,50 @@
} error] [expr {[string first [appendArgs \
"no such function: myFunc1_74_2_" $argumentCount] $error] != -1}]
}
lappend result [catch {
- sql execute $db "SELECT x FROM t1 ORDER BY x COLLATE myFunc1_74_3;"
+ sql execute -execute reader -format list $db \
+ "SELECT x FROM t1 ORDER BY x COLLATE myFunc1_74_3;"
} error] [expr {[string first "no such collation sequence: myFunc1_74_3" \
$error] != -1}]
+ lappend result [array size aggregateData]
+ lappend result [testArrayGet aggregateData]
+
set result
} -cleanup {
cleanupDb $fileName
freeDbConnection
- catch {object removecallback myFuncCallback}
+ catch {object removecallback myFuncCompareCallback}
+ catch {object removecallback myFuncFinalCallback}
+ catch {object removecallback myFuncStepCallback}
+ catch {object removecallback myFuncInvokeCallback}
+
+ catch {
+ foreach compareResult $compareResults {
+ catch {object dispose $compareResult}
+ }
+ }
- unset -nocomplain result error aggregateData argumentCount attribute \
- connection db fileName
+ unset -nocomplain result error compareResult compareResults \
+ aggregateData argumentCount attribute connection db fileName
+ rename myFuncCompareCallback ""
+ rename myFuncFinalCallback ""
+ rename myFuncStepCallback ""
+ rename myFuncInvokeCallback ""
rename myFuncCallback ""
+ rename hashManagedArray ""
+ rename getHashCode ""
rename getMyFuncArgs ""
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
-System.Data.SQLite} -result {}}
+System.Data.SQLite} -match regexp -result {^0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 \{1\
+2 3 A a M m Z z\} True 1 True 1 True 1 True 1 True 1 True 1 True 1 True 1\
+\{(?:-)?\d+ (?:-)?\d+\}$}}
###############################################################################
reportSQLiteResources $test_channel