Index: Doc/Extra/environment.html ================================================================== --- Doc/Extra/environment.html +++ Doc/Extra/environment.html @@ -72,10 +72,19 @@ the native library pre-loading will attempt to load the native SQLite library from architecture-specific (e.g. "x86", "amd64", "x64") or platform-specific (e.g. "Win32") directories that reside underneath the application base directory. + + No_SQLiteFunctions + If this environment variable is set [to anything], the initial + search for types in all loaded assemblies that are tagged with the + SQLiteFunction attribute will be skipped. Normally, this search is + conducted only once per application domain by the static constructor + of the SQLiteFunction class; however, these implementation details + are subject to change. + PreLoadSQLite_BaseDirectory If this environment variable is set [to anything], it will be used instead of the application base directory by the native library pre-loader. This environment variable can be especially Index: Doc/Extra/version.html ================================================================== --- Doc/Extra/version.html +++ Doc/Extra/version.html @@ -44,10 +44,11 @@

Version History

1.0.85.0 - March XX, 2013 (release scheduled)

1.0.84.0 - January 9, 2013

Index: System.Data.SQLite/SQLiteFunction.cs ================================================================== --- System.Data.SQLite/SQLiteFunction.cs +++ System.Data.SQLite/SQLiteFunction.cs @@ -1,41 +1,41 @@ -/******************************************************** - * 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 -{ +/******************************************************** + * 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.Collections.Generic; - using System.Runtime.InteropServices; - using System.Globalization; - - /// - /// 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. - /// - /// - /// 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. - /// - public abstract class SQLiteFunction : IDisposable - { - private class AggregateData - { - internal int _count = 1; - internal object _data; + using System.Collections.Generic; + using System.Runtime.InteropServices; + using System.Globalization; + + /// + /// 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. + /// + /// + /// 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. + /// + public abstract class SQLiteFunction : IDisposable + { + private class AggregateData + { + internal int _count = 1; + internal object _data; } ///////////////////////////////////////////////////////////////////////// #region Private Constants @@ -45,62 +45,62 @@ /// private const int COR_E_EXCEPTION = unchecked((int)0x80131500); #endregion ///////////////////////////////////////////////////////////////////////// - - /// - /// The base connection this function is attached to - /// - internal SQLiteBase _base; - - /// - /// Internal array used to keep track of aggregate function context data + + /// + /// The base connection this function is attached to + /// + internal SQLiteBase _base; + + /// + /// Internal array used to keep track of aggregate function context data /// private Dictionary _contextDataList; /// /// The connection flags associated with this object (this should be the /// same value as the flags associated with the parent connection object). /// private SQLiteConnectionFlags _flags; - - /// - /// Holds a reference to the callback function for user functions - /// - private SQLiteCallback _InvokeFunc; - /// - /// Holds a reference to the callbakc function for stepping in an aggregate function - /// - private SQLiteCallback _StepFunc; - /// - /// 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 - /// - private SQLiteCollation _CompareFunc; - - private SQLiteCollation _CompareFunc16; - - /// - /// Current context of the current callback. Only valid during a callback - /// - internal IntPtr _context; - - /// - /// This static list contains all the user-defined functions declared using the proper attributes. - /// - private static List _registeredFunctions; - - /// - /// Internal constructor, initializes the function's internal variables. - /// - protected SQLiteFunction() - { - _contextDataList = new Dictionary(); + + /// + /// Holds a reference to the callback function for user functions + /// + private SQLiteCallback _InvokeFunc; + /// + /// Holds a reference to the callbakc function for stepping in an aggregate function + /// + private SQLiteCallback _StepFunc; + /// + /// 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 + /// + private SQLiteCollation _CompareFunc; + + private SQLiteCollation _CompareFunc16; + + /// + /// Current context of the current callback. Only valid during a callback + /// + internal IntPtr _context; + + /// + /// This static list contains all the user-defined functions declared using the proper attributes. + /// + private static List _registeredFunctions; + + /// + /// Internal constructor, initializes the function's internal variables. + /// + protected SQLiteFunction() + { + _contextDataList = new Dictionary(); } /////////////////////////////////////////////////////////////////////////////////////////////// #region IDisposable Members @@ -181,201 +181,201 @@ } #endregion /////////////////////////////////////////////////////////////////////////////////////////////// - /// - /// 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. - /// - public SQLiteConvert SQLiteConvert - { - get - { - CheckDisposed(); - return _base; - } - } - - /// - /// Scalar functions override this method to do their magic. - /// - /// - /// 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. - /// - /// The arguments for the command to process - /// 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 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. + /// + public SQLiteConvert SQLiteConvert + { + get + { + CheckDisposed(); + return _base; + } + } + + /// + /// Scalar functions override this method to do their magic. + /// + /// + /// 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. + /// + /// The arguments for the command to process + /// 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! public virtual object Invoke(object[] args) { - CheckDisposed(); - return null; - } - - /// - /// Aggregate functions override this method to do their magic. - /// - /// - /// Typically you'll be updating whatever you've placed in the contextData field and returning as quickly as possible. - /// - /// The arguments for the command to process - /// The 1-based step number. This is incrememted each time the step method is called. - /// A placeholder for implementers to store contextual data pertaining to the current context. + CheckDisposed(); + return null; + } + + /// + /// Aggregate functions override this method to do their magic. + /// + /// + /// Typically you'll be updating whatever you've placed in the contextData field and returning as quickly as possible. + /// + /// The arguments for the command to process + /// The 1-based step number. This is incrememted each time the step method is called. + /// A placeholder for implementers to store contextual data pertaining to the current context. public virtual void Step(object[] args, int stepNumber, ref object contextData) { - CheckDisposed(); - } - - /// - /// Aggregate functions override this method to finish their aggregate processing. - /// - /// - /// 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. - /// - /// Your own assigned contextData, provided for you so you can return your final results. - /// 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! - /// - public virtual object Final(object contextData) - { - CheckDisposed(); - return null; - } - - /// - /// User-defined collation 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 + CheckDisposed(); + } + + /// + /// Aggregate functions override this method to finish their aggregate processing. + /// + /// + /// 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. + /// + /// Your own assigned contextData, provided for you so you can return your final results. + /// 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! + /// + public virtual object Final(object contextData) + { + CheckDisposed(); + return null; + } + + /// + /// User-defined collation 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) { - CheckDisposed(); - return 0; - } - - /// - /// Converts an IntPtr array of context arguments to an object array containing the resolved parameters the pointers point to. - /// - /// - /// 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. - /// - /// The number of arguments - /// A pointer to the array of arguments - /// An object array of the arguments once they've been converted to .NET values - 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; - } - - /// - /// Takes the return value from Invoke() and Final() and figures out how to return it to SQLite's context. - /// - /// The context the return value applies to - /// The parameter to return to SQLite - private 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; - } - } - - /// + CheckDisposed(); + return 0; + } + + /// + /// Converts an IntPtr array of context arguments to an object array containing the resolved parameters the pointers point to. + /// + /// + /// 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. + /// + /// The number of arguments + /// A pointer to the array of arguments + /// An object array of the arguments once they've been converted to .NET values + 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; + } + + /// + /// Takes the return value from Invoke() and Final() and figures out how to return it to SQLite's context. + /// + /// The context the return value applies to + /// The parameter to return to SQLite + private 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; + } + } + + /// /// Internal scalar callback function, which wraps the raw context pointer and calls the virtual Invoke() method. - /// WARNING: Must not throw exceptions. - /// - /// A raw context pointer - /// Number of arguments passed in + /// WARNING: Must not throw exceptions. + /// + /// A raw context pointer + /// Number of arguments passed in /// A pointer to the array of arguments internal void ScalarCallback(IntPtr context, int nArgs, IntPtr argsptr) { try { @@ -406,22 +406,22 @@ catch /* NOTE: Must catch ALL. */ { // do nothing (Windows CE). } #endif - } - - /// + } + + /// /// Internal collation 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 - /// Length of the string pv2 - /// Pointer to the second string to compare - /// Returns -1 if the first string is less than the second. 0 if they are equal, or 1 if the first string is greater + /// WARNING: Must not throw exceptions. + /// + /// Not used + /// Length of the string pv1 + /// Pointer to the first string to compare + /// Length of the string pv2 + /// Pointer to the second string to compare + /// 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 0 if an exception is caught. internal int CompareCallback(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2) { try { @@ -514,23 +514,23 @@ // if (_base != null) _base.Cancel(); return 0; - } - - /// + } + + /// /// The internal aggregate Step function callback, which wraps the raw context pointer and calls the virtual Step() method. - /// WARNING: Must not throw exceptions. - /// - /// - /// 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. - /// - /// A raw context pointer - /// Number of arguments passed in + /// WARNING: Must not throw exceptions. + /// + /// + /// 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. + /// + /// A raw context pointer + /// Number of arguments passed in /// A pointer to the array of arguments internal void StepCallback(IntPtr context, int nArgs, IntPtr argsptr) { try { @@ -585,16 +585,16 @@ catch /* NOTE: Must catch ALL. */ { // do nothing (Windows CE). } #endif - } - - /// + } + + /// /// An internal aggregate Final function callback, which wraps the context pointer and calls the virtual Final() method. - /// WARNING: Must not throw exceptions. - /// + /// WARNING: Must not throw exceptions. + /// /// A raw context pointer internal void FinalCallback(IntPtr context) { try { @@ -647,166 +647,173 @@ catch /* NOTE: Must catch ALL. */ { // do nothing (Windows CE). } #endif - } - - /// - /// Using reflection, enumerate all assemblies in the current appdomain looking for classes that - /// have a SQLiteFunctionAttribute attribute, and registering them accordingly. - /// -#if !PLATFORM_COMPACTFRAMEWORK - [Security.Permissions.FileIOPermission(Security.Permissions.SecurityAction.Assert, AllFiles = Security.Permissions.FileIOPermissionAccess.PathDiscovery)] -#endif - static SQLiteFunction() - { - _registeredFunctions = new List(); - try - { -#if !PLATFORM_COMPACTFRAMEWORK - 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 (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); - } - } - } - } -#endif - } - catch // SQLite provider can continue without being able to find built-in functions - { - } - } - - /// - /// 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. - /// - /// 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); - } - } - } - - /// - /// 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). - /// - /// - /// The wrapper functions that interop with SQLite will create a unique cookie value, which internally is a pointer to - /// all the wrapped callback functions. The interop function uses it to map CDecl callbacks to StdCall callbacks. - /// + } + + /// + /// Using reflection, enumerate all assemblies in the current appdomain looking for classes that + /// have a SQLiteFunctionAttribute attribute, and registering them accordingly. + /// +#if !PLATFORM_COMPACTFRAMEWORK + [Security.Permissions.FileIOPermission(Security.Permissions.SecurityAction.Assert, AllFiles = Security.Permissions.FileIOPermissionAccess.PathDiscovery)] +#endif + static SQLiteFunction() + { + _registeredFunctions = new List(); + try + { +#if !PLATFORM_COMPACTFRAMEWORK + // + // NOTE: If the "No_SQLiteFunctions" environment variable is set, + // skip all our special code and simply return. + // + if (Environment.GetEnvironmentVariable("No_SQLiteFunctions") != null) + return; + + 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 (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); + } + } + } + } +#endif + } + catch // SQLite provider can continue without being able to find built-in functions + { + } + } + + /// + /// 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. + /// + /// 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); + } + } + } + + /// + /// 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). + /// + /// + /// The wrapper functions that interop with SQLite will create a unique cookie value, which internally is a pointer to + /// all the wrapped callback functions. The interop function uses it to map CDecl callbacks to StdCall callbacks. + /// /// The base object on which the functions are to bind - /// The flags associated with the parent connection object - /// Returns an array of functions which the connection object should retain until the connection is closed. - internal static SQLiteFunction[] BindFunctions(SQLiteBase sqlbase, SQLiteConnectionFlags flags) - { - SQLiteFunction f; - List lFunctions = new List(); - - foreach (SQLiteFunctionAttribute pr in _registeredFunctions) - { + /// The flags associated with the parent connection object + /// Returns an array of functions which the connection object should retain until the connection is closed. + internal static SQLiteFunction[] BindFunctions(SQLiteBase sqlbase, SQLiteConnectionFlags flags) + { + SQLiteFunction f; + List lFunctions = new List(); + + foreach (SQLiteFunctionAttribute pr in _registeredFunctions) + { f = (SQLiteFunction)Activator.CreateInstance(pr._instanceType); f._base = sqlbase; f._flags = flags; - 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 SQLiteFinalCallback(f.FinalCallback) : null; - f._CompareFunc = (pr.FuncType == FunctionType.Collation) ? new SQLiteCollation(f.CompareCallback) : null; - f._CompareFunc16 = (pr.FuncType == FunctionType.Collation) ? new SQLiteCollation(f.CompareCallback16) : null; - - if (pr.FuncType != FunctionType.Collation) - sqlbase.CreateFunction(pr.Name, pr.Arguments, (f is SQLiteFunctionEx), f._InvokeFunc, f._StepFunc, f._FinalFunc); - else - sqlbase.CreateCollation(pr.Name, f._CompareFunc, f._CompareFunc16); - - - lFunctions.Add(f); - } - - SQLiteFunction[] arFunctions = new SQLiteFunction[lFunctions.Count]; - lFunctions.CopyTo(arFunctions, 0); - - return arFunctions; - } - } - - /// - /// Extends SQLiteFunction and allows an inherited class to obtain the collating sequence associated with a function call. - /// - /// - /// User-defined functions can call the GetCollationSequence() method in this class and use it to compare strings and char arrays. - /// - public class SQLiteFunctionEx : SQLiteFunction - { - /// - /// Obtains the collating sequence in effect for the given function. - /// - /// - protected CollationSequence GetCollationSequence() - { - return _base.GetCollationSequence(this, _context); + 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 SQLiteFinalCallback(f.FinalCallback) : null; + f._CompareFunc = (pr.FuncType == FunctionType.Collation) ? new SQLiteCollation(f.CompareCallback) : null; + f._CompareFunc16 = (pr.FuncType == FunctionType.Collation) ? new SQLiteCollation(f.CompareCallback16) : null; + + if (pr.FuncType != FunctionType.Collation) + sqlbase.CreateFunction(pr.Name, pr.Arguments, (f is SQLiteFunctionEx), f._InvokeFunc, f._StepFunc, f._FinalFunc); + else + sqlbase.CreateCollation(pr.Name, f._CompareFunc, f._CompareFunc16); + + + lFunctions.Add(f); + } + + SQLiteFunction[] arFunctions = new SQLiteFunction[lFunctions.Count]; + lFunctions.CopyTo(arFunctions, 0); + + return arFunctions; + } + } + + /// + /// Extends SQLiteFunction and allows an inherited class to obtain the collating sequence associated with a function call. + /// + /// + /// User-defined functions can call the GetCollationSequence() method in this class and use it to compare strings and char arrays. + /// + public class SQLiteFunctionEx : SQLiteFunction + { + /// + /// Obtains the collating sequence in effect for the given function. + /// + /// + protected CollationSequence GetCollationSequence() + { + return _base.GetCollationSequence(this, _context); } /////////////////////////////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members @@ -844,152 +851,152 @@ finally { base.Dispose(disposing); } } - #endregion - } - - /// - /// The type of user-defined function to declare - /// - public enum FunctionType - { - /// - /// Scalar functions are designed to be called and return a result immediately. Examples include ABS(), Upper(), Lower(), etc. - /// - Scalar = 0, - /// - /// 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 - /// 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, - } - - /// - /// An internal callback delegate declaration. - /// - /// Raw context pointer for the user function - /// Count of arguments to the function - /// A pointer to the array of argument pointers -#if !PLATFORM_COMPACTFRAMEWORK - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -#endif - internal delegate void SQLiteCallback(IntPtr context, int nArgs, IntPtr argsptr); - /// - /// An internal final callback delegate declaration. - /// - /// Raw context pointer for the user function -#if !PLATFORM_COMPACTFRAMEWORK - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -#endif - internal delegate void SQLiteFinalCallback(IntPtr context); - /// - /// Internal callback delegate for implementing collation sequences - /// - /// Not used - /// Length of the string pv1 - /// Pointer to the first string to compare - /// Length of the string pv2 - /// Pointer to the second string to compare - /// 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. -#if !PLATFORM_COMPACTFRAMEWORK - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -#endif - internal delegate int SQLiteCollation(IntPtr puser, int len1, IntPtr pv1, int len2, IntPtr pv2); - - /// - /// The type of collating sequence - /// - public enum CollationTypeEnum - { - /// - /// The built-in BINARY collating sequence - /// - Binary = 1, - /// - /// The built-in NOCASE collating sequence - /// - NoCase = 2, - /// - /// The built-in REVERSE collating sequence - /// - Reverse = 3, - /// - /// A custom user-defined collating sequence - /// - Custom = 0, - } - - /// - /// The encoding type the collation sequence uses - /// - public enum CollationEncodingEnum - { - /// - /// The collation sequence is UTF8 - /// - UTF8 = 1, - /// - /// The collation sequence is UTF16 little-endian - /// - UTF16LE = 2, - /// - /// The collation sequence is UTF16 big-endian - /// - UTF16BE = 3, - } - - /// - /// A struct describing the collating sequence a function is executing in - /// - public struct CollationSequence - { - /// - /// The name of the collating sequence - /// - public string Name; - /// - /// The type of collating sequence - /// - public CollationTypeEnum Type; - - /// - /// The text encoding of the collation sequence - /// - public CollationEncodingEnum Encoding; - - /// - /// Context of the function that requested the collating sequence - /// - internal SQLiteFunction _func; - - /// - /// Calls the base collating sequence to compare two strings - /// - /// The first string to compare - /// The second string to compare - /// -1 if s1 is less than s2, 0 if s1 is equal to s2, and 1 if s1 is greater than s2 - public int Compare(string s1, string s2) - { - return _func._base.ContextCollateCompare(Encoding, _func._context, s1, s2); - } - - /// - /// Calls the base collating sequence to compare two character arrays - /// - /// The first array to compare - /// The second array to compare - /// -1 if c1 is less than c2, 0 if c1 is equal to c2, and 1 if c1 is greater than c2 - public int Compare(char[] c1, char[] c2) - { - return _func._base.ContextCollateCompare(Encoding, _func._context, c1, c2); - } - } -} + #endregion + } + + /// + /// The type of user-defined function to declare + /// + public enum FunctionType + { + /// + /// Scalar functions are designed to be called and return a result immediately. Examples include ABS(), Upper(), Lower(), etc. + /// + Scalar = 0, + /// + /// 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 + /// 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, + } + + /// + /// An internal callback delegate declaration. + /// + /// Raw context pointer for the user function + /// Count of arguments to the function + /// A pointer to the array of argument pointers +#if !PLATFORM_COMPACTFRAMEWORK + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] +#endif + internal delegate void SQLiteCallback(IntPtr context, int nArgs, IntPtr argsptr); + /// + /// An internal final callback delegate declaration. + /// + /// Raw context pointer for the user function +#if !PLATFORM_COMPACTFRAMEWORK + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] +#endif + internal delegate void SQLiteFinalCallback(IntPtr context); + /// + /// Internal callback delegate for implementing collation sequences + /// + /// Not used + /// Length of the string pv1 + /// Pointer to the first string to compare + /// Length of the string pv2 + /// Pointer to the second string to compare + /// 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. +#if !PLATFORM_COMPACTFRAMEWORK + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] +#endif + internal delegate int SQLiteCollation(IntPtr puser, int len1, IntPtr pv1, int len2, IntPtr pv2); + + /// + /// The type of collating sequence + /// + public enum CollationTypeEnum + { + /// + /// The built-in BINARY collating sequence + /// + Binary = 1, + /// + /// The built-in NOCASE collating sequence + /// + NoCase = 2, + /// + /// The built-in REVERSE collating sequence + /// + Reverse = 3, + /// + /// A custom user-defined collating sequence + /// + Custom = 0, + } + + /// + /// The encoding type the collation sequence uses + /// + public enum CollationEncodingEnum + { + /// + /// The collation sequence is UTF8 + /// + UTF8 = 1, + /// + /// The collation sequence is UTF16 little-endian + /// + UTF16LE = 2, + /// + /// The collation sequence is UTF16 big-endian + /// + UTF16BE = 3, + } + + /// + /// A struct describing the collating sequence a function is executing in + /// + public struct CollationSequence + { + /// + /// The name of the collating sequence + /// + public string Name; + /// + /// The type of collating sequence + /// + public CollationTypeEnum Type; + + /// + /// The text encoding of the collation sequence + /// + public CollationEncodingEnum Encoding; + + /// + /// Context of the function that requested the collating sequence + /// + internal SQLiteFunction _func; + + /// + /// Calls the base collating sequence to compare two strings + /// + /// The first string to compare + /// The second string to compare + /// -1 if s1 is less than s2, 0 if s1 is equal to s2, and 1 if s1 is greater than s2 + public int Compare(string s1, string s2) + { + return _func._base.ContextCollateCompare(Encoding, _func._context, s1, s2); + } + + /// + /// Calls the base collating sequence to compare two character arrays + /// + /// The first array to compare + /// The second array to compare + /// -1 if c1 is less than c2, 0 if c1 is equal to c2, and 1 if c1 is greater than c2 + public int Compare(char[] c1, char[] c2) + { + return _func._base.ContextCollateCompare(Encoding, _func._context, c1, c2); + } + } +} Index: System.Data.SQLite/UnsafeNativeMethods.cs ================================================================== --- System.Data.SQLite/UnsafeNativeMethods.cs +++ System.Data.SQLite/UnsafeNativeMethods.cs @@ -139,11 +139,11 @@ /// internal static void Initialize() { #if !PLATFORM_COMPACTFRAMEWORK // - // NOTE: If the "NoPreLoadSQLite" environment variable is set, skip + // NOTE: If the "No_PreLoadSQLite" environment variable is set, skip // all our special code and simply return. // if (Environment.GetEnvironmentVariable("No_PreLoadSQLite") != null) return; #endif Index: readme.htm ================================================================== --- readme.htm +++ readme.htm @@ -189,10 +189,11 @@

1.0.85.0 - March XX, 2013 (release scheduled)

Index: www/news.wiki ================================================================== --- www/news.wiki +++ www/news.wiki @@ -5,10 +5,11 @@

1.0.85.0 - March XX, 2013 (release scheduled)