-//
-// Mono.Data.Sqlite.SQLiteFunction.cs
-//
-// Author(s):
-// Robert Simpson (robert@blackcastlesoft.com)
-//
-// Adapted and modified for the Mono Project by
-// Marek Habersack (grendello@gmail.com)
-//
-//
-// Copyright (C) 2006 Novell, Inc (http://www.novell.com)
-// Copyright (C) 2007 Marek Habersack
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/********************************************************
- * 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!
- ********************************************************/
-#if NET_2_0
-namespace Mono.Data.Sqlite
-{
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Runtime.InteropServices;
- using System.Globalization;
-
- /// <summary>
- /// The type of user-defined function to declare
- /// </summary>
- public enum FunctionType
- {
- /// <summary>
- /// Scalar functions are designed to be called and return a result immediately. Examples include ABS(), Upper(), Lower(), etc.
- /// </summary>
- Scalar = 0,
- /// <summary>
- /// 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.
- /// </summary>
- Aggregate = 1,
- /// <summary>
- /// 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
- /// 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.
- /// </summary>
- Collation = 2,
- }
-
- /// <summary>
- /// An internal callback delegate declaration.
- /// </summary>
- /// <param name="context">Raw context pointer for the user function</param>
- /// <param name="nArgs">Count of arguments to the function</param>
- /// <param name="argsptr">A pointer to the array of argument pointers</param>
- internal delegate void SqliteCallback(IntPtr context, int nArgs, IntPtr argsptr);
- /// <summary>
- /// Internal callback delegate for implementing collation sequences
- /// </summary>
- /// <param name="len1">Length of the string pv1</param>
- /// <param name="pv1">Pointer to the first string to compare</param>
- /// <param name="len2">Length of the string pv2</param>
- /// <param name="pv2">Pointer to the second string to compare</param>
- /// <returns>Returns -1 if the first string is less than the second. 0 if they are equal, or 1 if the first string is greater
- /// than the second.</returns>
- internal delegate int SqliteCollation(int len1, IntPtr pv1, int len2, IntPtr pv2);
-
- /// <summary>
- /// This abstract class is designed to handle user-defined functions easily. An instance of the derived class is made for each
- /// connection to the database.
- /// </summary>
- /// <remarks>
- /// Although there is one instance of a class derived from SqliteFunction per database connection, the derived class has no access
- /// to the underlying connection. This is necessary to deter implementers from thinking it would be a good idea to make database
- /// calls during processing.
- ///
- /// It is important to distinguish between a per-connection instance, and a per-SQL statement context. One instance of this class
- /// services all SQL statements being stepped through on that connection, and there can be many. One should never store per-statement
- /// information in member variables of user-defined function classes.
- ///
- /// For aggregate functions, always create and store your per-statement data in the contextData object on the 1st step. This data will
- /// be automatically freed for you (and Dispose() called if the item supports IDisposable) when the statement completes.
- /// </remarks>
- public abstract class SqliteFunction : IDisposable
- {
- /// <summary>
- /// The base connection this function is attached to
- /// </summary>
- private SqliteBase _base;
- /// <summary>
- /// Used internally to keep track of memory allocated for aggregate functions
- /// </summary>
- private IntPtr _interopCookie;
- /// <summary>
- /// Internal array used to keep track of aggregate function context data
- /// </summary>
- private Dictionary<long, object> _contextDataList;
-
- /// <summary>
- /// Holds a reference to the callback function for user functions
- /// </summary>
- private SqliteCallback _InvokeFunc;
- /// <summary>
- /// Holds a reference to the callbakc function for stepping in an aggregate function
- /// </summary>
- private SqliteCallback _StepFunc;
- /// <summary>
- /// Holds a reference to the callback function for finalizing an aggregate function
- /// </summary>
- private SqliteCallback _FinalFunc;
- /// <summary>
- /// Holds a reference to the callback function for collation sequences
- /// </summary>
- private SqliteCollation _CompareFunc;
-
- /// <summary>
- /// This static list contains all the user-defined functions declared using the proper attributes.
- /// </summary>
- private static List<SqliteFunctionAttribute> _registeredFunctions = new List<SqliteFunctionAttribute>();
-
- /// <summary>
- /// Internal constructor, initializes the function's internal variables.
- /// </summary>
- protected SqliteFunction()
- {
- _contextDataList = new Dictionary<long, object>();
- }
-
- /// <summary>
- /// Returns a reference to the underlying connection's SqliteConvert class, which can be used to convert
- /// strings and DateTime's into the current connection's encoding schema.
- /// </summary>
- public SqliteConvert SqliteConvert
- {
- get
- {
- return _base;
- }
- }
-
- /// <summary>
- /// Scalar functions override this method to do their magic.
- /// </summary>
- /// <remarks>
- /// Parameters passed to functions have only an affinity for a certain data type, there is no underlying schema available
- /// to force them into a certain type. Therefore the only types you will ever see as parameters are
- /// DBNull.Value, Int64, Double, String or byte[] array.
- /// </remarks>
- /// <param name="args">The arguments for the command to process</param>
- /// <returns>You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or
- /// you may return an Exception-derived class if you wish to return an error to Sqlite. Do not actually throw the error,
- /// just return it!</returns>
- public virtual object Invoke(object[] args)
- {
- return null;
- }
-
- /// <summary>
- /// Aggregate functions override this method to do their magic.
- /// </summary>
- /// <remarks>
- /// Typically you'll be updating whatever you've placed in the contextData field and returning as quickly as possible.
- /// </remarks>
- /// <param name="args">The arguments for the command to process</param>
- /// <param name="stepNumber">The 1-based step number. This is incrememted each time the step method is called.</param>
- /// <param name="contextData">A placeholder for implementers to store contextual data pertaining to the current context.</param>
- public virtual void Step(object[] args, int stepNumber, ref object contextData)
- {
- }
-
- /// <summary>
- /// Aggregate functions override this method to finish their aggregate processing.
- /// </summary>
- /// <remarks>
- /// If you implemented your aggregate function properly,
- /// you've been recording and keeping track of your data in the contextData object provided, and now at this stage you should have
- /// all the information you need in there to figure out what to return.
- /// NOTE: It is possible to arrive here without receiving a previous call to Step(), in which case the contextData will
- /// be null. This can happen when no rows were returned. You can either return null, or 0 or some other custom return value
- /// if that is the case.
- /// </remarks>
- /// <param name="contextData">Your own assigned contextData, provided for you so you can return your final results.</param>
- /// <returns>You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or
- /// you may return an Exception-derived class if you wish to return an error to Sqlite. Do not actually throw the error,
- /// just return it!
- /// </returns>
- public virtual object Final(object contextData)
- {
- return null;
- }
-
- /// <summary>
- /// User-defined collation sequences override this method to provide a custom string sorting algorithm.
- /// </summary>
- /// <param name="param1">The first string to compare</param>
- /// <param name="param2">The second strnig to compare</param>
- /// <returns>1 if param1 is greater than param2, 0 if they are equal, or -1 if param1 is less than param2</returns>
- public virtual int Compare(string param1, string param2)
- {
- return 0;
- }
-
- /// <summary>
- /// Converts an IntPtr array of context arguments to an object array containing the resolved parameters the pointers point to.
- /// </summary>
- /// <remarks>
- /// Parameters passed to functions have only an affinity for a certain data type, there is no underlying schema available
- /// to force them into a certain type. Therefore the only types you will ever see as parameters are
- /// DBNull.Value, Int64, Double, String or byte[] array.
- /// </remarks>
- /// <param name="nArgs">The number of arguments</param>
- /// <param name="argsptr">A pointer to the array of arguments</param>
- /// <returns>An object array of the arguments once they've been converted to .NET values</returns>
- internal object[] ConvertParams(int nArgs, IntPtr argsptr)
- {
- object[] parms = new object[nArgs];
-#if !PLATFORM_COMPACTFRAMEWORK
- IntPtr[] argint = new IntPtr[nArgs];
-#else
- int[] argint = new int[nArgs];
-#endif
- Marshal.Copy(argsptr, argint, 0, nArgs);
-
- for (int n = 0; n < nArgs; n++)
- {
- switch (_base.GetParamValueType((IntPtr)argint[n]))
- {
- case TypeAffinity.Null:
- parms[n] = DBNull.Value;
- break;
- case TypeAffinity.Int64:
- parms[n] = _base.GetParamValueInt64((IntPtr)argint[n]);
- break;
- case TypeAffinity.Double:
- parms[n] = _base.GetParamValueDouble((IntPtr)argint[n]);
- break;
- case TypeAffinity.Text:
- parms[n] = _base.GetParamValueText((IntPtr)argint[n]);
- break;
- case TypeAffinity.Blob:
- {
- int x;
- byte[] blob;
-
- x = (int)_base.GetParamValueBytes((IntPtr)argint[n], 0, null, 0, 0);
- blob = new byte[x];
- _base.GetParamValueBytes((IntPtr)argint[n], 0, blob, 0, x);
- parms[n] = blob;
- }
- break;
- case TypeAffinity.DateTime: // Never happens here but what the heck, maybe it will one day.
- parms[n] = _base.ToDateTime(_base.GetParamValueText((IntPtr)argint[n]));
- break;
- }
- }
- return parms;
- }
-
- /// <summary>
- /// Takes the return value from Invoke() and Final() and figures out how to return it to Sqlite's context.
- /// </summary>
- /// <param name="context">The context the return value applies to</param>
- /// <param name="returnValue">The parameter to return to Sqlite</param>
- void SetReturnValue(IntPtr context, object returnValue)
- {
- if (returnValue == null || returnValue == DBNull.Value)
- {
- _base.ReturnNull(context);
- return;
- }
-
- Type t = returnValue.GetType();
- if (t == typeof(DateTime))
- {
- _base.ReturnText(context, _base.ToString((DateTime)returnValue));
- return;
- }
- else
- {
- Exception r = returnValue as Exception;
-
- if (r != null)
- {
- _base.ReturnError(context, r.Message);
- return;
- }
- }
-
- switch (SqliteConvert.TypeToAffinity(t))
- {
- case TypeAffinity.Null:
- _base.ReturnNull(context);
- return;
- case TypeAffinity.Int64:
- _base.ReturnInt64(context, Convert.ToInt64(returnValue, CultureInfo.CurrentCulture));
- return;
- case TypeAffinity.Double:
- _base.ReturnDouble(context, Convert.ToDouble(returnValue, CultureInfo.CurrentCulture));
- return;
- case TypeAffinity.Text:
- _base.ReturnText(context, returnValue.ToString());
- return;
- case TypeAffinity.Blob:
- _base.ReturnBlob(context, (byte[])returnValue);
- return;
- }
- }
-
- /// <summary>
- /// Internal scalar callback function, which wraps the raw context pointer and calls the virtual Invoke() method.
- /// </summary>
- /// <param name="context">A raw context pointer</param>
- /// <param name="nArgs">Number of arguments passed in</param>
- /// <param name="argsptr">A pointer to the array of arguments</param>
- internal void ScalarCallback(IntPtr context, int nArgs, IntPtr argsptr)
- {
- SetReturnValue(context, Invoke(ConvertParams(nArgs, argsptr)));
- }
-
- /// <summary>
- /// Internal collation sequence function, which wraps up the raw string pointers and executes the Compare() virtual function.
- /// </summary>
- /// <param name="len1">Length of the string pv1</param>
- /// <param name="ptr1">Pointer to the first string to compare</param>
- /// <param name="len2">Length of the string pv2</param>
- /// <param name="ptr2">Pointer to the second string to compare</param>
- /// <returns>Returns -1 if the first string is less than the second. 0 if they are equal, or 1 if the first string is greater
- /// than the second.</returns>
- internal int CompareCallback(int len1, IntPtr ptr1, int len2, IntPtr ptr2)
- {
- return Compare(_base.ToString(ptr1), _base.ToString(ptr2));
- }
-
- /// <summary>
- /// The internal aggregate Step function callback, which wraps the raw context pointer and calls the virtual Step() method.
- /// </summary>
- /// <remarks>
- /// This function takes care of doing the lookups and getting the important information put together to call the Step() function.
- /// That includes pulling out the user's contextData and updating it after the call is made. We use a sorted list for this so
- /// binary searches can be done to find the data.
- /// </remarks>
- /// <param name="context">A raw context pointer</param>
- /// <param name="nArgs">Number of arguments passed in</param>
- /// <param name="argsptr">A pointer to the array of arguments</param>
- internal void StepCallback(IntPtr context, int nArgs, IntPtr argsptr)
- {
- int n = _base.AggregateCount(context);
- long nAux;
- object obj = null;
-
- nAux = (long)_base.AggregateContext(context);
- if (n > 1) obj = _contextDataList[nAux];
-
- Step(ConvertParams(nArgs, argsptr), n, ref obj);
- _contextDataList[nAux] = obj;
- }
-
- /// <summary>
- /// An internal aggregate Final function callback, which wraps the context pointer and calls the virtual Final() method.
- /// </summary>
- /// <param name="context">A raw context pointer</param>
- /// <param name="nArgs">Not used, always zero</param>
- /// <param name="argsptr">Not used, always zero</param>
- internal void FinalCallback(IntPtr context, int nArgs, IntPtr argsptr)
- {
- long n = (long)_base.AggregateContext(context);
- object obj = null;
-
- if (_contextDataList.ContainsKey(n))
- {
- obj = _contextDataList[n];
- _contextDataList.Remove(n);
- }
-
- SetReturnValue(context, Final(obj));
-
- IDisposable disp = obj as IDisposable;
- if (disp != null) disp.Dispose();
- }
-
- /// <summary>
- /// Placeholder for a user-defined disposal routine
- /// </summary>
- /// <param name="disposing">True if the object is being disposed explicitly</param>
- protected virtual void Dispose(bool disposing)
- {
- }
-
- /// <summary>
- /// Disposes of any active contextData variables that were not automatically cleaned up. Sometimes this can happen if
- /// someone closes the connection while a DataReader is open.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
-
- IDisposable disp;
-
- foreach (KeyValuePair<long, object> kv in _contextDataList)
- {
- disp = kv.Value as IDisposable;
- if (disp != null)
- disp.Dispose();
- }
- _contextDataList.Clear();
-
- _InvokeFunc = null;
- _StepFunc = null;
- _FinalFunc = null;
- _CompareFunc = null;
- _base = null;
- _contextDataList = null;
-
- GC.SuppressFinalize(this);
- }
-
-#if !PLATFORM_COMPACTFRAMEWORK
- /// <summary>
- /// Using reflection, enumerate all assemblies in the current appdomain looking for classes that
- /// have a SqliteFunctionAttribute attribute, and registering them accordingly.
- /// </summary>
- static SqliteFunction()
- {
- SqliteFunctionAttribute at;
- System.Reflection.Assembly[] arAssemblies = System.AppDomain.CurrentDomain.GetAssemblies();
- int w = arAssemblies.Length;
- System.Reflection.AssemblyName sqlite = System.Reflection.Assembly.GetCallingAssembly().GetName();
-
- for (int n = 0; n < w; n++)
- {
- Type[] arTypes;
- bool found = false;
- System.Reflection.AssemblyName[] references;
- try
- {
- // Inspect only assemblies that reference SQLite
- references = arAssemblies[n].GetReferencedAssemblies();
- int t = references.Length;
- for (int z = 0; z < t; z++)
- {
- if (references[z].Name == sqlite.Name)
- {
- found = true;
- break;
- }
- }
-
- if (found == false)
- continue;
-
- arTypes = arAssemblies[n].GetTypes();
- }
- catch (System.Reflection.ReflectionTypeLoadException e)
- {
- arTypes = e.Types;
- }
-
- int v = arTypes.Length;
- for (int x = 0; x < v; x++)
- {
- if (arTypes[x] == null) continue;
-
- object[] arAtt = arTypes[x].GetCustomAttributes(typeof(SqliteFunctionAttribute), false);
- int u = arAtt.Length;
- for (int y = 0; y < u; y++)
- {
- at = arAtt[y] as SqliteFunctionAttribute;
- if (at != null)
- {
- at._instanceType = arTypes[x];
- _registeredFunctions.Add(at);
- }
- }
- }
- }
- }
-#else
- /// <summary>
- /// Manual method of registering a function. The type must still have the SqliteFunctionAttributes in order to work
- /// properly, but this is a workaround for the Compact Framework where enumerating assemblies is not currently supported.
- /// </summary>
- /// <param name="typ">The type of the function to register</param>
- 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);
- }
- }
- }
-#endif
-
- /// <summary>
- /// Called by SqliteBase derived classes, this function binds all 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
- /// as the connection (UTF-8 or UTF-16).
- /// </summary>
- /// <remarks>
- /// The wrapper functions that interop with Sqlite will create a unique cooke value, which internally is a pointer to
- /// all the wrapped callback functions. The interop function uses it to map CDecl callbacks to StdCall callbacks.
- /// </remarks>
- /// <param name="sqlbase">The base object on which the functions are to bind</param>
- /// <returns>Returns an array of functions which the connection object should retain until the connection is closed.</returns>
- internal static SqliteFunction[] BindFunctions(SqliteBase sqlbase)
- {
- SqliteFunction f;
- List<SqliteFunction> lFunctions = new List<SqliteFunction>();
-
- foreach (SqliteFunctionAttribute pr in _registeredFunctions)
- {
- f = (SqliteFunction)Activator.CreateInstance(pr._instanceType);
- f._base = sqlbase;
- f._InvokeFunc = (pr.FuncType == FunctionType.Scalar) ? new SqliteCallback(f.ScalarCallback) : null;
- f._StepFunc = (pr.FuncType == FunctionType.Aggregate) ? new SqliteCallback(f.StepCallback) : null;
- f._FinalFunc = (pr.FuncType == FunctionType.Aggregate) ? new SqliteCallback(f.FinalCallback) : null;
- f._CompareFunc = (pr.FuncType == FunctionType.Collation) ? new SqliteCollation(f.CompareCallback) : null;
-
- if (pr.FuncType != FunctionType.Collation)
- f._interopCookie = sqlbase.CreateFunction(pr.Name, pr.Arguments, f._InvokeFunc, f._StepFunc, f._FinalFunc);
- else
- f._interopCookie = sqlbase.CreateCollation(pr.Name, f._CompareFunc);
-
-
- lFunctions.Add(f);
- }
-
- SqliteFunction[] arFunctions = new SqliteFunction[lFunctions.Count];
- lFunctions.CopyTo(arFunctions, 0);
-
- return arFunctions;
- }
-
- /// <summary>
- /// Issued after the base connection is closed, this function cleans up all user-defined functions and disposes of them.
- /// </summary>
- /// <remarks>
- /// Cleaning up here is done mainly because of the interop wrapper. It allocated memory to hold a reference to all the
- /// delegates, and now must free that memory.
- /// Freeing is done after the connection is closed to ensure no callbacks get hit after we've freed the cookie.
- /// </remarks>
- /// <param name="sqlbase">The base Sqlite connection object</param>
- /// <param name="ar">An array of user-defined functions for this object</param>
- internal static void UnbindFunctions(SqliteBase sqlbase, SqliteFunction[] ar)
- {
- if (ar == null) return;
-
- int x = ar.Length;
- for (int n = 0; n < x; n++)
- {
- sqlbase.FreeFunction(ar[n]._interopCookie);
- ar[n].Dispose();
- }
- }
- }
-}
-#endif
+/********************************************************\r
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X\r
+ * Written by Robert Simpson (robert@blackcastlesoft.com)\r
+ * \r
+ * Released to the public domain, use at your own risk!\r
+ ********************************************************/\r
+\r
+namespace Mono.Data.Sqlite\r
+{\r
+ using System;\r
+ using System.Collections;\r
+ using System.Collections.Generic;\r
+ using System.Runtime.InteropServices;\r
+ using System.Globalization;\r
+\r
+ /// <summary>\r
+ /// This abstract class is designed to handle user-defined functions easily. An instance of the derived class is made for each\r
+ /// connection to the database.\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// Although there is one instance of a class derived from SqliteFunction per database connection, the derived class has no access\r
+ /// to the underlying connection. This is necessary to deter implementers from thinking it would be a good idea to make database\r
+ /// calls during processing.\r
+ /// \r
+ /// It is important to distinguish between a per-connection instance, and a per-SQL statement context. One instance of this class\r
+ /// services all SQL statements being stepped through on that connection, and there can be many. One should never store per-statement\r
+ /// information in member variables of user-defined function classes.\r
+ /// \r
+ /// For aggregate functions, always create and store your per-statement data in the contextData object on the 1st step. This data will\r
+ /// be automatically freed for you (and Dispose() called if the item supports IDisposable) when the statement completes.\r
+ /// </remarks>\r
+ public abstract class SqliteFunction : IDisposable\r
+ {\r
+ private class AggregateData\r
+ {\r
+ internal int _count = 1;\r
+ internal object _data = null;\r
+ }\r
+\r
+ /// <summary>\r
+ /// The base connection this function is attached to\r
+ /// </summary>\r
+ internal SQLiteBase _base;\r
+\r
+ /// <summary>\r
+ /// Internal array used to keep track of aggregate function context data\r
+ /// </summary>\r
+ private Dictionary<long, AggregateData> _contextDataList;\r
+\r
+ /// <summary>\r
+ /// Holds a reference to the callback function for user functions\r
+ /// </summary>\r
+ private SQLiteCallback _InvokeFunc;\r
+ /// <summary>\r
+ /// Holds a reference to the callbakc function for stepping in an aggregate function\r
+ /// </summary>\r
+ private SQLiteCallback _StepFunc;\r
+ /// <summary>\r
+ /// Holds a reference to the callback function for finalizing an aggregate function\r
+ /// </summary>\r
+ private SQLiteFinalCallback _FinalFunc;\r
+ /// <summary>\r
+ /// Holds a reference to the callback function for collation sequences\r
+ /// </summary>\r
+ private SQLiteCollation _CompareFunc;\r
+\r
+ private SQLiteCollation _CompareFunc16;\r
+\r
+ /// <summary>\r
+ /// Current context of the current callback. Only valid during a callback\r
+ /// </summary>\r
+ internal IntPtr _context;\r
+\r
+ /// <summary>\r
+ /// This static list contains all the user-defined functions declared using the proper attributes.\r
+ /// </summary>\r
+ private static List<SqliteFunctionAttribute> _registeredFunctions = new List<SqliteFunctionAttribute>();\r
+\r
+ /// <summary>\r
+ /// Internal constructor, initializes the function's internal variables.\r
+ /// </summary>\r
+ protected SqliteFunction()\r
+ {\r
+ _contextDataList = new Dictionary<long, AggregateData>();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Returns a reference to the underlying connection's SqliteConvert class, which can be used to convert\r
+ /// strings and DateTime's into the current connection's encoding schema.\r
+ /// </summary>\r
+ public SqliteConvert SqliteConvert\r
+ {\r
+ get\r
+ {\r
+ return _base;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Scalar functions override this method to do their magic.\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// Parameters passed to functions have only an affinity for a certain data type, there is no underlying schema available\r
+ /// to force them into a certain type. Therefore the only types you will ever see as parameters are\r
+ /// DBNull.Value, Int64, Double, String or byte[] array.\r
+ /// </remarks>\r
+ /// <param name="args">The arguments for the command to process</param>\r
+ /// <returns>You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or\r
+ /// you may return an Exception-derived class if you wish to return an error to SQLite. Do not actually throw the error,\r
+ /// just return it!</returns>\r
+ public virtual object Invoke(object[] args)\r
+ {\r
+ return null;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Aggregate functions override this method to do their magic.\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// Typically you'll be updating whatever you've placed in the contextData field and returning as quickly as possible.\r
+ /// </remarks>\r
+ /// <param name="args">The arguments for the command to process</param>\r
+ /// <param name="stepNumber">The 1-based step number. This is incrememted each time the step method is called.</param>\r
+ /// <param name="contextData">A placeholder for implementers to store contextual data pertaining to the current context.</param>\r
+ public virtual void Step(object[] args, int stepNumber, ref object contextData)\r
+ {\r
+ }\r
+\r
+ /// <summary>\r
+ /// Aggregate functions override this method to finish their aggregate processing.\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// If you implemented your aggregate function properly,\r
+ /// you've been recording and keeping track of your data in the contextData object provided, and now at this stage you should have\r
+ /// all the information you need in there to figure out what to return.\r
+ /// NOTE: It is possible to arrive here without receiving a previous call to Step(), in which case the contextData will\r
+ /// be null. This can happen when no rows were returned. You can either return null, or 0 or some other custom return value\r
+ /// if that is the case.\r
+ /// </remarks>\r
+ /// <param name="contextData">Your own assigned contextData, provided for you so you can return your final results.</param>\r
+ /// <returns>You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or\r
+ /// you may return an Exception-derived class if you wish to return an error to SQLite. Do not actually throw the error,\r
+ /// just return it!\r
+ /// </returns>\r
+ public virtual object Final(object contextData)\r
+ {\r
+ return null;\r
+ }\r
+\r
+ /// <summary>\r
+ /// User-defined collation sequences override this method to provide a custom string sorting algorithm.\r
+ /// </summary>\r
+ /// <param name="param1">The first string to compare</param>\r
+ /// <param name="param2">The second strnig to compare</param>\r
+ /// <returns>1 if param1 is greater than param2, 0 if they are equal, or -1 if param1 is less than param2</returns>\r
+ public virtual int Compare(string param1, string param2)\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Converts an IntPtr array of context arguments to an object array containing the resolved parameters the pointers point to.\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// Parameters passed to functions have only an affinity for a certain data type, there is no underlying schema available\r
+ /// to force them into a certain type. Therefore the only types you will ever see as parameters are\r
+ /// DBNull.Value, Int64, Double, String or byte[] array.\r
+ /// </remarks>\r
+ /// <param name="nArgs">The number of arguments</param>\r
+ /// <param name="argsptr">A pointer to the array of arguments</param>\r
+ /// <returns>An object array of the arguments once they've been converted to .NET values</returns>\r
+ internal object[] ConvertParams(int nArgs, IntPtr argsptr)\r
+ {\r
+ object[] parms = new object[nArgs];\r
+#if !PLATFORM_COMPACTFRAMEWORK\r
+ IntPtr[] argint = new IntPtr[nArgs];\r
+#else\r
+ int[] argint = new int[nArgs];\r
+#endif\r
+ Marshal.Copy(argsptr, argint, 0, nArgs);\r
+\r
+ for (int n = 0; n < nArgs; n++)\r
+ {\r
+ switch (_base.GetParamValueType((IntPtr)argint[n]))\r
+ {\r
+ case TypeAffinity.Null:\r
+ parms[n] = DBNull.Value;\r
+ break;\r
+ case TypeAffinity.Int64:\r
+ parms[n] = _base.GetParamValueInt64((IntPtr)argint[n]);\r
+ break;\r
+ case TypeAffinity.Double:\r
+ parms[n] = _base.GetParamValueDouble((IntPtr)argint[n]);\r
+ break;\r
+ case TypeAffinity.Text:\r
+ parms[n] = _base.GetParamValueText((IntPtr)argint[n]);\r
+ break;\r
+ case TypeAffinity.Blob:\r
+ {\r
+ int x;\r
+ byte[] blob;\r
+\r
+ x = (int)_base.GetParamValueBytes((IntPtr)argint[n], 0, null, 0, 0);\r
+ blob = new byte[x];\r
+ _base.GetParamValueBytes((IntPtr)argint[n], 0, blob, 0, x);\r
+ parms[n] = blob;\r
+ }\r
+ break;\r
+ case TypeAffinity.DateTime: // Never happens here but what the heck, maybe it will one day.\r
+ parms[n] = _base.ToDateTime(_base.GetParamValueText((IntPtr)argint[n]));\r
+ break;\r
+ }\r
+ }\r
+ return parms;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Takes the return value from Invoke() and Final() and figures out how to return it to SQLite's context.\r
+ /// </summary>\r
+ /// <param name="context">The context the return value applies to</param>\r
+ /// <param name="returnValue">The parameter to return to SQLite</param>\r
+ void SetReturnValue(IntPtr context, object returnValue)\r
+ {\r
+ if (returnValue == null || returnValue == DBNull.Value)\r
+ {\r
+ _base.ReturnNull(context);\r
+ return;\r
+ }\r
+\r
+ Type t = returnValue.GetType();\r
+ if (t == typeof(DateTime))\r
+ {\r
+ _base.ReturnText(context, _base.ToString((DateTime)returnValue));\r
+ return;\r
+ }\r
+ else\r
+ {\r
+ Exception r = returnValue as Exception;\r
+\r
+ if (r != null)\r
+ {\r
+ _base.ReturnError(context, r.Message);\r
+ return;\r
+ }\r
+ }\r
+\r
+ switch (SqliteConvert.TypeToAffinity(t))\r
+ {\r
+ case TypeAffinity.Null:\r
+ _base.ReturnNull(context);\r
+ return;\r
+ case TypeAffinity.Int64:\r
+ _base.ReturnInt64(context, Convert.ToInt64(returnValue, CultureInfo.CurrentCulture));\r
+ return;\r
+ case TypeAffinity.Double:\r
+ _base.ReturnDouble(context, Convert.ToDouble(returnValue, CultureInfo.CurrentCulture));\r
+ return;\r
+ case TypeAffinity.Text:\r
+ _base.ReturnText(context, returnValue.ToString());\r
+ return;\r
+ case TypeAffinity.Blob:\r
+ _base.ReturnBlob(context, (byte[])returnValue);\r
+ return;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Internal scalar callback function, which wraps the raw context pointer and calls the virtual Invoke() method.\r
+ /// </summary>\r
+ /// <param name="context">A raw context pointer</param>\r
+ /// <param name="nArgs">Number of arguments passed in</param>\r
+ /// <param name="argsptr">A pointer to the array of arguments</param>\r
+ internal void ScalarCallback(IntPtr context, int nArgs, IntPtr argsptr)\r
+ {\r
+ _context = context;\r
+ SetReturnValue(context, Invoke(ConvertParams(nArgs, argsptr)));\r
+ }\r
+\r
+ /// <summary>\r
+ /// Internal collation sequence function, which wraps up the raw string pointers and executes the Compare() virtual function.\r
+ /// </summary>\r
+ /// <param name="ptr">Not used</param>\r
+ /// <param name="len1">Length of the string pv1</param>\r
+ /// <param name="ptr1">Pointer to the first string to compare</param>\r
+ /// <param name="len2">Length of the string pv2</param>\r
+ /// <param name="ptr2">Pointer to the second string to compare</param>\r
+ /// <returns>Returns -1 if the first string is less than the second. 0 if they are equal, or 1 if the first string is greater\r
+ /// than the second.</returns>\r
+ internal int CompareCallback(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)\r
+ {\r
+ return Compare(SqliteConvert.UTF8ToString(ptr1, len1), SqliteConvert.UTF8ToString(ptr2, len2));\r
+ }\r
+\r
+ internal int CompareCallback16(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)\r
+ {\r
+ return Compare(SQLite3_UTF16.UTF16ToString(ptr1, len1), SQLite3_UTF16.UTF16ToString(ptr2, len2));\r
+ }\r
+\r
+ /// <summary>\r
+ /// The internal aggregate Step function callback, which wraps the raw context pointer and calls the virtual Step() method.\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// This function takes care of doing the lookups and getting the important information put together to call the Step() function.\r
+ /// That includes pulling out the user's contextData and updating it after the call is made. We use a sorted list for this so\r
+ /// binary searches can be done to find the data.\r
+ /// </remarks>\r
+ /// <param name="context">A raw context pointer</param>\r
+ /// <param name="nArgs">Number of arguments passed in</param>\r
+ /// <param name="argsptr">A pointer to the array of arguments</param>\r
+ internal void StepCallback(IntPtr context, int nArgs, IntPtr argsptr)\r
+ {\r
+ long nAux;\r
+ AggregateData data;\r
+\r
+ nAux = (long)_base.AggregateContext(context);\r
+ if (_contextDataList.TryGetValue(nAux, out data) == false)\r
+ {\r
+ data = new AggregateData();\r
+ _contextDataList[nAux] = data;\r
+ }\r
+\r
+ try\r
+ {\r
+ _context = context;\r
+ Step(ConvertParams(nArgs, argsptr), data._count, ref data._data);\r
+ }\r
+ finally\r
+ {\r
+ data._count++;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// An internal aggregate Final function callback, which wraps the context pointer and calls the virtual Final() method.\r
+ /// </summary>\r
+ /// <param name="context">A raw context pointer</param>\r
+ internal void FinalCallback(IntPtr context)\r
+ {\r
+ long n = (long)_base.AggregateContext(context);\r
+ object obj = null;\r
+\r
+ if (_contextDataList.ContainsKey(n))\r
+ {\r
+ obj = _contextDataList[n]._data;\r
+ _contextDataList.Remove(n);\r
+ }\r
+\r
+ _context = context;\r
+ SetReturnValue(context, Final(obj));\r
+\r
+ IDisposable disp = obj as IDisposable;\r
+ if (disp != null) disp.Dispose();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Placeholder for a user-defined disposal routine\r
+ /// </summary>\r
+ /// <param name="disposing">True if the object is being disposed explicitly</param>\r
+ protected virtual void Dispose(bool disposing)\r
+ {\r
+ if (disposing)\r
+ {\r
+ IDisposable disp;\r
+\r
+ foreach (KeyValuePair<long, AggregateData> kv in _contextDataList)\r
+ {\r
+ disp = kv.Value._data as IDisposable;\r
+ if (disp != null)\r
+ disp.Dispose();\r
+ }\r
+ _contextDataList.Clear();\r
+\r
+ _InvokeFunc = null;\r
+ _StepFunc = null;\r
+ _FinalFunc = null;\r
+ _CompareFunc = null;\r
+ _base = null;\r
+ _contextDataList = null;\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Disposes of any active contextData variables that were not automatically cleaned up. Sometimes this can happen if\r
+ /// someone closes the connection while a DataReader is open.\r
+ /// </summary>\r
+ public void Dispose()\r
+ {\r
+ Dispose(true);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Using reflection, enumerate all assemblies in the current appdomain looking for classes that\r
+ /// have a SqliteFunctionAttribute attribute, and registering them accordingly.\r
+ /// </summary>\r
+#if !PLATFORM_COMPACTFRAMEWORK\r
+ [global::System.Security.Permissions.FileIOPermission(global::System.Security.Permissions.SecurityAction.Assert, AllFiles = global::System.Security.Permissions.FileIOPermissionAccess.PathDiscovery)]\r
+#endif\r
+ static SqliteFunction()\r
+ {\r
+ try\r
+ {\r
+#if !PLATFORM_COMPACTFRAMEWORK\r
+ SqliteFunctionAttribute at;\r
+ System.Reflection.Assembly[] arAssemblies = System.AppDomain.CurrentDomain.GetAssemblies();\r
+ int w = arAssemblies.Length;\r
+ System.Reflection.AssemblyName sqlite = System.Reflection.Assembly.GetCallingAssembly().GetName();\r
+\r
+ for (int n = 0; n < w; n++)\r
+ {\r
+ Type[] arTypes;\r
+ bool found = false;\r
+ System.Reflection.AssemblyName[] references;\r
+ try\r
+ {\r
+ // Inspect only assemblies that reference SQLite\r
+ references = arAssemblies[n].GetReferencedAssemblies();\r
+ int t = references.Length;\r
+ for (int z = 0; z < t; z++)\r
+ {\r
+ if (references[z].Name == sqlite.Name)\r
+ {\r
+ found = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (found == false)\r
+ continue;\r
+\r
+ arTypes = arAssemblies[n].GetTypes();\r
+ }\r
+ catch (global::System.Reflection.ReflectionTypeLoadException e)\r
+ {\r
+ arTypes = e.Types;\r
+ }\r
+\r
+ int v = arTypes.Length;\r
+ for (int x = 0; x < v; x++)\r
+ {\r
+ if (arTypes[x] == null) continue;\r
+\r
+ object[] arAtt = arTypes[x].GetCustomAttributes(typeof(SqliteFunctionAttribute), false);\r
+ int u = arAtt.Length;\r
+ for (int y = 0; y < u; y++)\r
+ {\r
+ at = arAtt[y] as SqliteFunctionAttribute;\r
+ if (at != null)\r
+ {\r
+ at._instanceType = arTypes[x];\r
+ _registeredFunctions.Add(at);\r
+ }\r
+ }\r
+ }\r
+ }\r
+#endif\r
+ }\r
+ catch // SQLite provider can continue without being able to find built-in functions\r
+ {\r
+ }\r
+ }\r
+ /// <summary>\r
+ /// Manual method of registering a function. The type must still have the SqliteFunctionAttributes in order to work\r
+ /// properly, but this is a workaround for the Compact Framework where enumerating assemblies is not currently supported.\r
+ /// </summary>\r
+ /// <param name="typ">The type of the function to register</param>\r
+ public static void RegisterFunction(Type typ)\r
+ {\r
+ object[] arAtt = typ.GetCustomAttributes(typeof(SqliteFunctionAttribute), false);\r
+ int u = arAtt.Length;\r
+ SqliteFunctionAttribute at;\r
+\r
+ for (int y = 0; y < u; y++)\r
+ {\r
+ at = arAtt[y] as SqliteFunctionAttribute;\r
+ if (at != null)\r
+ {\r
+ at._instanceType = typ;\r
+ _registeredFunctions.Add(at);\r
+ }\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Called by SQLiteBase derived classes, this function binds all user-defined functions to a connection.\r
+ /// It is done this way so that all user-defined functions will access the database using the same encoding scheme\r
+ /// as the connection (UTF-8 or UTF-16).\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// The wrapper functions that interop with SQLite will create a unique cookie value, which internally is a pointer to\r
+ /// all the wrapped callback functions. The interop function uses it to map CDecl callbacks to StdCall callbacks.\r
+ /// </remarks>\r
+ /// <param name="sqlbase">The base object on which the functions are to bind</param>\r
+ /// <returns>Returns an array of functions which the connection object should retain until the connection is closed.</returns>\r
+ internal static SqliteFunction[] BindFunctions(SQLiteBase sqlbase)\r
+ {\r
+ SqliteFunction f;\r
+ List<SqliteFunction> lFunctions = new List<SqliteFunction>();\r
+\r
+ foreach (SqliteFunctionAttribute pr in _registeredFunctions)\r
+ {\r
+ f = (SqliteFunction)Activator.CreateInstance(pr._instanceType);\r
+ f._base = sqlbase;\r
+ f._InvokeFunc = (pr.FuncType == FunctionType.Scalar) ? new SQLiteCallback(f.ScalarCallback) : null;\r
+ f._StepFunc = (pr.FuncType == FunctionType.Aggregate) ? new SQLiteCallback(f.StepCallback) : null;\r
+ f._FinalFunc = (pr.FuncType == FunctionType.Aggregate) ? new SQLiteFinalCallback(f.FinalCallback) : null;\r
+ f._CompareFunc = (pr.FuncType == FunctionType.Collation) ? new SQLiteCollation(f.CompareCallback) : null;\r
+ f._CompareFunc16 = (pr.FuncType == FunctionType.Collation) ? new SQLiteCollation(f.CompareCallback16) : null;\r
+\r
+ if (pr.FuncType != FunctionType.Collation)\r
+ sqlbase.CreateFunction(pr.Name, pr.Arguments, (f is SqliteFunctionEx), f._InvokeFunc, f._StepFunc, f._FinalFunc);\r
+ else {\r
+#if MONOTOUCH\r
+ GCHandle handle = GCHandle.Alloc (f);\r
+ sqlbase.CreateCollation(pr.Name, collation_callback, collation_callback16, GCHandle.ToIntPtr (handle));\r
+#else\r
+ sqlbase.CreateCollation(pr.Name, f._CompareFunc, f._CompareFunc16, IntPtr.Zero);\r
+#endif\r
+ }\r
+\r
+\r
+ lFunctions.Add(f);\r
+ }\r
+\r
+ SqliteFunction[] arFunctions = new SqliteFunction[lFunctions.Count];\r
+ lFunctions.CopyTo(arFunctions, 0);\r
+\r
+ return arFunctions;\r
+ }\r
+\r
+#if MONOTOUCH\r
+ [MonoTouch.MonoPInvokeCallback (typeof (SQLiteCollation))]\r
+ internal static int collation_callback (IntPtr puser, int len1, IntPtr pv1, int len2, IntPtr pv2)\r
+ {\r
+ var handle = GCHandle.FromIntPtr (puser);\r
+ var func = (SqliteFunction) handle.Target;\r
+ return func._CompareFunc (IntPtr.Zero, len1, pv1, len2, pv2);\r
+ }\r
+\r
+ [MonoTouch.MonoPInvokeCallback (typeof (SQLiteCollation))]\r
+ internal static int collation_callback16 (IntPtr puser, int len1, IntPtr pv1, int len2, IntPtr pv2)\r
+ {\r
+ var handle = GCHandle.FromIntPtr (puser);\r
+ var func = (SqliteFunction) handle.Target;\r
+ return func._CompareFunc16 (IntPtr.Zero, len1, pv1, len2, pv2);\r
+ }\r
+#endif\r
+ }\r
+\r
+ /// <summary>\r
+ /// Extends SqliteFunction and allows an inherited class to obtain the collating sequence associated with a function call.\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// User-defined functions can call the GetCollationSequence() method in this class and use it to compare strings and char arrays.\r
+ /// </remarks>\r
+ public class SqliteFunctionEx : SqliteFunction\r
+ {\r
+ /// <summary>\r
+ /// Obtains the collating sequence in effect for the given function.\r
+ /// </summary>\r
+ /// <returns></returns>\r
+ protected CollationSequence GetCollationSequence()\r
+ {\r
+ return _base.GetCollationSequence(this, _context);\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// The type of user-defined function to declare\r
+ /// </summary>\r
+ public enum FunctionType\r
+ {\r
+ /// <summary>\r
+ /// Scalar functions are designed to be called and return a result immediately. Examples include ABS(), Upper(), Lower(), etc.\r
+ /// </summary>\r
+ Scalar = 0,\r
+ /// <summary>\r
+ /// Aggregate functions are designed to accumulate data until the end of a call and then return a result gleaned from the accumulated data.\r
+ /// Examples include SUM(), COUNT(), AVG(), etc.\r
+ /// </summary>\r
+ Aggregate = 1,\r
+ /// <summary>\r
+ /// 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\r
+ /// sorted using a straight case-insensitive comparison function. Custom collating sequences can be used to alter the behavior of text sorting\r
+ /// in a user-defined manner.\r
+ /// </summary>\r
+ Collation = 2,\r
+ }\r
+\r
+ /// <summary>\r
+ /// An internal callback delegate declaration.\r
+ /// </summary>\r
+ /// <param name="context">Raw context pointer for the user function</param>\r
+ /// <param name="nArgs">Count of arguments to the function</param>\r
+ /// <param name="argsptr">A pointer to the array of argument pointers</param>\r
+#if !PLATFORM_COMPACTFRAMEWORK\r
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\r
+#endif\r
+ internal delegate void SQLiteCallback(IntPtr context, int nArgs, IntPtr argsptr);\r
+ /// <summary>\r
+ /// An internal final callback delegate declaration.\r
+ /// </summary>\r
+ /// <param name="context">Raw context pointer for the user function</param>\r
+#if !PLATFORM_COMPACTFRAMEWORK\r
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\r
+#endif\r
+ internal delegate void SQLiteFinalCallback(IntPtr context);\r
+ /// <summary>\r
+ /// Internal callback delegate for implementing collation sequences\r
+ /// </summary>\r
+ /// <param name="puser">Not used</param>\r
+ /// <param name="len1">Length of the string pv1</param>\r
+ /// <param name="pv1">Pointer to the first string to compare</param>\r
+ /// <param name="len2">Length of the string pv2</param>\r
+ /// <param name="pv2">Pointer to the second string to compare</param>\r
+ /// <returns>Returns -1 if the first string is less than the second. 0 if they are equal, or 1 if the first string is greater\r
+ /// than the second.</returns>\r
+#if !PLATFORM_COMPACTFRAMEWORK\r
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\r
+#endif\r
+ internal delegate int SQLiteCollation(IntPtr puser, int len1, IntPtr pv1, int len2, IntPtr pv2);\r
+\r
+ /// <summary>\r
+ /// The type of collating sequence\r
+ /// </summary>\r
+ public enum CollationTypeEnum\r
+ {\r
+ /// <summary>\r
+ /// The built-in BINARY collating sequence\r
+ /// </summary>\r
+ Binary = 1,\r
+ /// <summary>\r
+ /// The built-in NOCASE collating sequence\r
+ /// </summary>\r
+ NoCase = 2,\r
+ /// <summary>\r
+ /// The built-in REVERSE collating sequence\r
+ /// </summary>\r
+ Reverse = 3,\r
+ /// <summary>\r
+ /// A custom user-defined collating sequence\r
+ /// </summary>\r
+ Custom = 0,\r
+ }\r
+\r
+ /// <summary>\r
+ /// The encoding type the collation sequence uses\r
+ /// </summary>\r
+ public enum CollationEncodingEnum\r
+ {\r
+ /// <summary>\r
+ /// The collation sequence is UTF8\r
+ /// </summary>\r
+ UTF8 = 1,\r
+ /// <summary>\r
+ /// The collation sequence is UTF16 little-endian\r
+ /// </summary>\r
+ UTF16LE = 2,\r
+ /// <summary>\r
+ /// The collation sequence is UTF16 big-endian\r
+ /// </summary>\r
+ UTF16BE = 3,\r
+ }\r
+\r
+ /// <summary>\r
+ /// A struct describing the collating sequence a function is executing in\r
+ /// </summary>\r
+ public struct CollationSequence\r
+ {\r
+ /// <summary>\r
+ /// The name of the collating sequence\r
+ /// </summary>\r
+ public string Name;\r
+ /// <summary>\r
+ /// The type of collating sequence\r
+ /// </summary>\r
+ public CollationTypeEnum Type;\r
+\r
+ /// <summary>\r
+ /// The text encoding of the collation sequence\r
+ /// </summary>\r
+ public CollationEncodingEnum Encoding;\r
+\r
+ /// <summary>\r
+ /// Context of the function that requested the collating sequence\r
+ /// </summary>\r
+ internal SqliteFunction _func;\r
+\r
+ /// <summary>\r
+ /// Calls the base collating sequence to compare two strings\r
+ /// </summary>\r
+ /// <param name="s1">The first string to compare</param>\r
+ /// <param name="s2">The second string to compare</param>\r
+ /// <returns>-1 if s1 is less than s2, 0 if s1 is equal to s2, and 1 if s1 is greater than s2</returns>\r
+ public int Compare(string s1, string s2)\r
+ {\r
+ return _func._base.ContextCollateCompare(Encoding, _func._context, s1, s2);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Calls the base collating sequence to compare two character arrays\r
+ /// </summary>\r
+ /// <param name="c1">The first array to compare</param>\r
+ /// <param name="c2">The second array to compare</param>\r
+ /// <returns>-1 if c1 is less than c2, 0 if c1 is equal to c2, and 1 if c1 is greater than c2</returns>\r
+ public int Compare(char[] c1, char[] c2)\r
+ {\r
+ return _func._base.ContextCollateCompare(Encoding, _func._context, c1, c2);\r
+ }\r
+ }\r
+}\r