Index: System.Data.SQLite/SQLiteConnection.cs
==================================================================
--- System.Data.SQLite/SQLiteConnection.cs
+++ System.Data.SQLite/SQLiteConnection.cs
@@ -1381,15 +1381,15 @@
///
/// Attempts to bind the specified object
/// instance to this connection.
///
///
- /// The object instance containing
+ /// The object instance containing
/// the metadata for the function to be bound.
///
///
- /// The object instance that implements the
+ /// The object instance that implements the
/// function to be bound.
///
public void BindFunction(
SQLiteFunctionAttribute functionAttribute,
SQLiteFunction function
@@ -1401,10 +1401,39 @@
throw new InvalidOperationException(
"Database connection not valid for binding functions.");
_sql.BindFunction(functionAttribute, function, _flags);
}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to bind the specified object
+ /// instance to this connection.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be bound.
+ ///
+ ///
+ /// The object instance that implements the
+ /// function to be bound.
+ ///
+ public void BindFunction(
+ SQLiteFunctionAttribute functionAttribute,
+ Delegate callback
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(
+ "Database connection not valid for binding functions.");
+
+ _sql.BindFunction(functionAttribute,
+ new SQLiteDelegateFunction(callback), _flags);
+ }
///////////////////////////////////////////////////////////////////////////////////////////////
///
/// Attempts to unbind the specified object
Index: System.Data.SQLite/SQLiteFunction.cs
==================================================================
--- System.Data.SQLite/SQLiteFunction.cs
+++ System.Data.SQLite/SQLiteFunction.cs
@@ -65,11 +65,11 @@
///
/// Holds a reference to the callback function for finalizing an aggregate function
///
private SQLiteFinalCallback _FinalFunc;
///
- /// Holds a reference to the callback function for collation sequences
+ /// Holds a reference to the callback function for collating sequences
///
private SQLiteCollation _CompareFunc;
private SQLiteCollation _CompareFunc16;
@@ -275,11 +275,11 @@
CheckDisposed();
return null;
}
///
- /// User-defined collation sequences override this method to provide a custom string sorting algorithm.
+ /// User-defined collating sequences override this method to provide a custom string sorting algorithm.
///
/// The first string to compare
/// The second strnig to compare
/// 1 if param1 is greater than param2, 0 if they are equal, or -1 if param1 is less than param2
public virtual int Compare(string param1, string param2)
@@ -428,11 +428,11 @@
}
}
}
///
- /// Internal collation sequence function, which wraps up the raw string pointers and executes the Compare() virtual function.
+ /// Internal collating sequence function, which wraps up the raw string pointers and executes the Compare() virtual function.
/// WARNING: Must not throw exceptions.
///
/// Not used
/// Length of the string pv1
/// Pointer to the first string to compare
@@ -475,11 +475,11 @@
return 0;
}
///
- /// Internal collation sequence function, which wraps up the raw string pointers and executes the Compare() virtual function.
+ /// Internal collating sequence function, which wraps up the raw string pointers and executes the Compare() virtual function.
/// WARNING: Must not throw exceptions.
///
/// Not used
/// Length of the string pv1
/// Pointer to the first string to compare
@@ -726,23 +726,111 @@
/// properly, but this is a workaround for the Compact Framework where enumerating assemblies is not currently supported.
///
/// The type of the function to register
public static void RegisterFunction(Type typ)
{
- object[] arAtt = typ.GetCustomAttributes(typeof(SQLiteFunctionAttribute), false);
- int u = arAtt.Length;
- SQLiteFunctionAttribute at;
-
- for (int y = 0; y < u; y++)
- {
- at = arAtt[y] as SQLiteFunctionAttribute;
- if (at != null)
- {
- at.InstanceType = typ;
- _registeredFunctions.Add(at, null);
- }
- }
+ object[] arAtt = typ.GetCustomAttributes(
+ typeof(SQLiteFunctionAttribute), false);
+
+ for (int y = 0; y < arAtt.Length; y++)
+ {
+ SQLiteFunctionAttribute at = arAtt[y] as SQLiteFunctionAttribute;
+
+ if (at == null)
+ continue;
+
+ RegisterFunction(
+ at.Name, at.Arguments, at.FuncType, at.InstanceType,
+ at.Callback);
+ }
+ }
+
+ ///
+ /// Alternative method of registering a function. This method
+ /// does not require the specified type to be annotated with
+ /// .
+ ///
+ ///
+ /// The name of the function to register.
+ ///
+ ///
+ /// The number of arguments accepted by the function.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// The that implements the function. If
+ /// this is non-null, the parameter
+ /// will be ignored when the function is invoked.
+ ///
+ public static void RegisterFunction(
+ string name,
+ int argumentCount,
+ FunctionType functionType,
+ Type instanceType,
+ Delegate callback
+ )
+ {
+ SQLiteFunctionAttribute at = new SQLiteFunctionAttribute(
+ name, argumentCount, functionType);
+
+ at.InstanceType = instanceType;
+ at.Callback = callback;
+
+ _registeredFunctions.Add(at, null);
+ }
+
+ ///
+ /// Creates a instance based on the specified
+ /// .
+ ///
+ ///
+ /// The containing the metadata about
+ /// the function to create.
+ ///
+ ///
+ /// The created function -OR- null if the function could not be created.
+ ///
+ ///
+ /// Non-zero if the function was created; otherwise, zero.
+ ///
+ private static bool CreateFunction(
+ SQLiteFunctionAttribute functionAttribute,
+ out SQLiteFunction function
+ )
+ {
+ if (functionAttribute == null)
+ {
+ function = null;
+ return false;
+ }
+ else if (functionAttribute.Callback != null)
+ {
+ function = new SQLiteDelegateFunction(
+ functionAttribute.Callback);
+
+ return true;
+ }
+ else if (functionAttribute.InstanceType != null)
+ {
+ function = (SQLiteFunction)Activator.CreateInstance(
+ functionAttribute.InstanceType);
+
+ return true;
+ }
+ else
+ {
+ function = null;
+ return false;
+ }
}
///
/// Called by the SQLiteBase derived classes, this method binds all registered (known) user-defined functions to a connection.
/// It is done this way so that all user-defined functions will access the database using the same encoding scheme
@@ -769,15 +857,21 @@
SQLiteFunctionAttribute pr = pair.Key;
if (pr == null)
continue;
- SQLiteFunction f = (SQLiteFunction)Activator.CreateInstance(
- pr.InstanceType);
+ SQLiteFunction f;
- BindFunction(sqlbase, pr, f, flags);
- lFunctions[pr] = f;
+ if (CreateFunction(pr, out f))
+ {
+ BindFunction(sqlbase, pr, f, flags);
+ lFunctions[pr] = f;
+ }
+ else
+ {
+ lFunctions[pr] = null;
+ }
}
return lFunctions;
}
@@ -859,11 +953,11 @@
return result;
}
///
- /// This function binds a user-defined functions to a connection.
+ /// This function binds a user-defined function to a connection.
///
///
/// The object instance associated with the
/// that the function should be bound to.
///
@@ -987,10 +1081,337 @@
}
}
+ /////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This class implements a SQLite function using a .
+ /// All the virtual methods of the class are
+ /// implemented using calls to the
+ /// method. The arguments are presented in the same order they appear in
+ /// the associated methods with one exception:
+ /// the first argument is the name of the virtual method being implemented.
+ ///
+ public class SQLiteDelegateFunction : SQLiteFunction
+ {
+ #region Private Constants
+ ///
+ /// This error message is used by the overridden virtual methods when the
+ /// callback has not been set.
+ ///
+ private const string NoCallbackError = "No callback is set.";
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This error message is used by the overridden
+ /// method when the result does not have a type of .
+ ///
+ private const string ResultInt32Error = "\"{0}\" result must be Int32.";
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an empty instance of this class.
+ ///
+ public SQLiteDelegateFunction()
+ : this(null)
+ {
+ // do nothing.
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class using the specified
+ /// as the
+ /// implementation.
+ ///
+ ///
+ /// The to be used for all calls into the
+ /// virtual methods needed by the
+ /// class.
+ ///
+ public SQLiteDelegateFunction(
+ Delegate callback
+ )
+ {
+ this.callback = callback;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Returns the list of arguments for the method,
+ /// as an of . The first
+ /// argument is always the literal string "Invoke".
+ ///
+ ///
+ /// The original arguments received by the method.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual object[] GetInvokeArgs(
+ object[] args
+ ) /* CANNOT RETURN NULL */
+ {
+ if (args == null)
+ return new object[] { "Invoke" };
+
+ object[] newArgs = new object[args.Length + 1];
+
+ newArgs[0] = "Invoke";
+
+ for (int index = 0; index < args.Length; index++)
+ newArgs[index + 1] = args[index];
+
+ return newArgs;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the list of arguments for the method,
+ /// as an of . The first
+ /// argument is always the literal string "Step".
+ ///
+ ///
+ /// The original arguments received by the method.
+ ///
+ ///
+ /// The step number (one based). This is incrememted each time the
+ /// method is called.
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual object[] GetStepArgs(
+ object[] args,
+ int stepNumber,
+ object contextData
+ ) /* CANNOT RETURN NULL */
+ {
+ int newLength = 3; /* "Step", stepNumber, contextData */
+
+ if (args != null)
+ newLength += args.Length;
+
+ object[] newArgs = new object[newLength];
+
+ newArgs[0] = "Step";
+
+ if (args != null)
+ for (int index = 0; index < args.Length; index++)
+ newArgs[index + 1] = args[index];
+
+ newArgs[newLength - 2] = stepNumber;
+ newArgs[newLength - 1] = contextData;
+
+ return newArgs;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the list of arguments for the method,
+ /// as an of . The first
+ /// argument is always the literal string "Final".
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual object[] GetFinalArgs(
+ object contextData
+ ) /* CANNOT RETURN NULL */
+ {
+ return new object[] { "Final", contextData };
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the list of arguments for the method,
+ /// as an of . The first
+ /// argument is always the literal string "Compare".
+ ///
+ ///
+ /// The first string to compare.
+ ///
+ ///
+ /// The second strnig to compare.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual object[] GetCompareArgs(
+ string param1,
+ string param2
+ ) /* CANNOT RETURN NULL */
+ {
+ return new object[] { "Compare", param1, param2 };
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ private Delegate callback;
+ ///
+ /// The to be used for all calls into the
+ /// virtual methods needed by the
+ /// class.
+ ///
+ public virtual Delegate Callback
+ {
+ get { return callback; }
+ set { callback = value; }
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region System.Data.SQLite.SQLiteFunction Overrides
+ ///
+ /// This virtual method is the implementation for scalar functions.
+ /// See the method for more
+ /// details.
+ ///
+ ///
+ /// The arguments for the scalar function. The first argument is always
+ /// the literal string "Invoke". The remaining arguments, if any, are
+ /// passed exactly as they are received.
+ ///
+ ///
+ /// The result of the scalar function.
+ ///
+ public override object Invoke(
+ object[] args /* in */
+ )
+ {
+ if (callback == null)
+ throw new InvalidOperationException(NoCallbackError);
+
+ return callback.DynamicInvoke(GetInvokeArgs(args)); /* throw */
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This virtual method is part of the implementation for aggregate
+ /// functions. See the method
+ /// for more details.
+ ///
+ ///
+ /// The arguments for the aggregate function. The first argument is
+ /// always the literal string "Step". The remaining arguments, if
+ /// any, are passed exactly as they are received.
+ ///
+ ///
+ /// The step number (one based). This is incrememted each time the
+ /// method is called.
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ public override void Step(
+ object[] args, /* in */
+ int stepNumber, /* in */
+ ref object contextData /* in, out */
+ )
+ {
+ if (callback == null)
+ throw new InvalidOperationException(NoCallbackError);
+
+ object[] newArgs = GetStepArgs(args, stepNumber, contextData);
+
+ /* IGNORED */
+ callback.DynamicInvoke(newArgs); /* throw */
+
+ contextData = newArgs[newArgs.Length - 1]; /* out */
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This virtual method is part of the implementation for aggregate
+ /// functions. See the method
+ /// for more details.
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ ///
+ /// The result of the aggregate function.
+ ///
+ public override object Final(
+ object contextData /* in */
+ )
+ {
+ if (callback == null)
+ throw new InvalidOperationException(NoCallbackError);
+
+ return callback.DynamicInvoke(GetFinalArgs(contextData)); /* throw */
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This virtual method is part of the implementation for collating
+ /// sequences. See the method
+ /// for more details.
+ ///
+ ///
+ /// The first string to compare.
+ ///
+ ///
+ /// The second strnig to compare.
+ ///
+ ///
+ /// A positive integer if the parameter is
+ /// greater than the parameter, a negative
+ /// integer if the parameter is less than
+ /// the parameter, or zero if they are
+ /// equal.
+ ///
+ public override int Compare(
+ string param1, /* in */
+ string param2 /* in */
+ )
+ {
+ if (callback == null)
+ throw new InvalidOperationException(NoCallbackError);
+
+ object[] newArgs = GetCompareArgs(param1, param2);
+ object result = callback.DynamicInvoke(newArgs); /* throw */
+
+ if (result is int)
+ return (int)result;
+
+ throw new InvalidOperationException(String.Format(
+ ResultInt32Error, newArgs[0]));
+ }
+ #endregion
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
///
/// Extends SQLiteFunction and allows an inherited class to obtain the collating sequence associated with a function call.
///
///
@@ -1065,11 +1486,11 @@
/// Aggregate functions are designed to accumulate data until the end of a call and then return a result gleaned from the accumulated data.
/// Examples include SUM(), COUNT(), AVG(), etc.
///
Aggregate = 1,
///
- /// Collation sequences are used to sort textual data in a custom manner, and appear in an ORDER BY clause. Typically text in an ORDER BY is
+ /// Collating sequences are used to sort textual data in a custom manner, and appear in an ORDER BY clause. Typically text in an ORDER BY is
/// sorted using a straight case-insensitive comparison function. Custom collating sequences can be used to alter the behavior of text sorting
/// in a user-defined manner.
///
Collation = 2,
}
@@ -1091,11 +1512,11 @@
#if !PLATFORM_COMPACTFRAMEWORK
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
internal delegate void SQLiteFinalCallback(IntPtr context);
///
- /// Internal callback delegate for implementing collation sequences
+ /// Internal callback delegate for implementing collating sequences
///
/// Not used
/// Length of the string pv1
/// Pointer to the first string to compare
/// Length of the string pv2
Index: System.Data.SQLite/SQLiteFunctionAttribute.cs
==================================================================
--- System.Data.SQLite/SQLiteFunctionAttribute.cs
+++ System.Data.SQLite/SQLiteFunctionAttribute.cs
@@ -1,60 +1,62 @@
/********************************************************
* ADO.NET 2.0 Data Provider for SQLite Version 3.X
* Written by Robert Simpson (robert@blackcastlesoft.com)
- *
+ *
* Released to the public domain, use at your own risk!
********************************************************/
namespace System.Data.SQLite
{
- using System;
-
+ using System;
+
///
/// A simple custom attribute to enable us to easily find user-defined functions in
/// the loaded assemblies and initialize them in SQLite as connections are made.
///
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public sealed class SQLiteFunctionAttribute : Attribute
{
- private string _name;
+ private string _name;
private int _argumentCount;
private FunctionType _functionType;
private Type _instanceType;
+ private Delegate _callback;
///
/// Default constructor, initializes the internal variables for the function.
- ///
- public SQLiteFunctionAttribute()
- : this(String.Empty, -1, FunctionType.Scalar)
- {
- // do nothing.
- }
-
- ///
- /// Constructs an instance of this class.
- ///
- ///
- /// The name of the function, as seen by the SQLite core library.
- ///
- ///
- /// The number of arguments that the function will accept.
- ///
- ///
- /// The type of function being declared. This will either be Scalar,
- /// Aggregate, or Collation.
- ///
- public SQLiteFunctionAttribute(
- string name,
- int argumentCount,
- FunctionType functionType
- )
- {
- _name = name;
- _argumentCount = argumentCount;
- _functionType = functionType;
- _instanceType = null;
+ ///
+ public SQLiteFunctionAttribute()
+ : this(String.Empty, -1, FunctionType.Scalar)
+ {
+ // do nothing.
+ }
+
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The name of the function, as seen by the SQLite core library.
+ ///
+ ///
+ /// The number of arguments that the function will accept.
+ ///
+ ///
+ /// The type of function being declared. This will either be Scalar,
+ /// Aggregate, or Collation.
+ ///
+ public SQLiteFunctionAttribute(
+ string name,
+ int argumentCount,
+ FunctionType functionType
+ )
+ {
+ _name = name;
+ _argumentCount = argumentCount;
+ _functionType = functionType;
+ _instanceType = null;
+ _callback = null;
}
///
/// The function's name as it will be used in SQLite command text.
///
@@ -66,12 +68,12 @@
///
/// The number of arguments this function expects. -1 if the number of arguments is variable.
///
public int Arguments
- {
- get { return _argumentCount; }
+ {
+ get { return _argumentCount; }
set { _argumentCount = value; }
}
///
/// The type of function this implementation will be.
@@ -80,16 +82,29 @@
{
get { return _functionType; }
set { _functionType = value; }
}
- ///
- /// The object instance that describes the class
- /// containing the implementation for the associated function.
+ ///
+ /// 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.
///
internal Type InstanceType
- {
- get { return _instanceType; }
+ {
+ get { return _instanceType; }
set { _instanceType = 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 Callback
+ {
+ get { return _callback; }
+ set { _callback = value; }
+ }
}
}