Version History
-1.0.84.0 - January 9, 2013
+1.0.85.0 - March XX, 2013 (release scheduled)
+-
+
- Updated to SQLite 3.7.16. +
- Properly handle embedded NUL characters in parameter and column values. Fix for [3567020edf]. +
- Make use of the sqlite3_prepare_v2 function when applicable. +
- Check for a valid row in the SQLiteDataReader.GetValue method. +
- Implement processor architecture detection when running on the .NET Compact Framework (via P/Invoke). +
- Support automated testing when running on the .NET Compact Framework 2.0. +
- Skip checking loaded assemblies for types tagged with the SQLiteFunction attribute when the No_SQLiteFunctions environment variable is set. Pursuant to [e4c8121f7b]. +
- Add HexPassword connection string property to work around the inability to include a literal semicolon in a connection string property value. Pursuant to [1c456ae75f]. +
- Add static Execute method to the SQLiteCommand class. +
- Support custom connection pool implementations by adding the ISQLiteConnectionPool interface, the static SQLiteConnection.ConnectionPool property, and the static CreateHandle method in addition to modifying the SQLiteConnectionPool class. Pursuant to [393d954be0]. +
- Add public constructor to the SQLiteDataAdapter class that allows passing the parseViaFramework parameter to the SQLiteConnection constructor. +
- When built with the CHECK_STATE compile-time option, skip throwing exceptions from the SQLiteDataReader class when the object is being disposed. +
- Support automatic value conversions for columns with a declared type of BIGUINT, INTEGER8, INTEGER16, INTEGER32, INTEGER64, SMALLUINT, TINYSINT, UNSIGNEDINTEGER, UNSIGNEDINTEGER8, UNSIGNEDINTEGER16, UNSIGNEDINTEGER32, UNSIGNEDINTEGER64, INT8, INT16, INT32, INT64, UINT, UINT8, UINT16, UINT32, UINT64, or ULONG. +
- Add BindUInt32AsInt64 connection flag to force binding of UInt32 values as Int64 instead. Pursuant to [c010fa6584]. +
- Add BindAllAsText and GetAllAsText connection flags to force binding and returning of all values as text. +
- Remove AUTOINCREMENT from the column type name map. ** Potentially Incompatible Change ** +
- Avoid throwing overflow exceptions from the SQLite3.GetValue method for integral column types. Partial fix for [c010fa6584]. ** Potentially Incompatible Change ** +
- Use the legacy connection closing algorithm when built with the INTEROP_LEGACY_CLOSE compile-time option. +
- Support using the directory containing the primary managed-only assembly as the basis for native library pre-loading. +
- Still further enhancements to the build and test automation. +
1.0.84.0 - January 9, 2013
- Updated to SQLite 3.7.15.2.
- Explicitly dispose of all SQLiteCommand objects managed by the DbDataAdapter class. Fix for [6434e23a0f].
- Add Cancel method to the SQLiteConnection class to interrupt a long running query.
- Improve thread safety of the SQLiteLog.LogMessage method.
1.0.83.0 - December 29, 2012
+1.0.83.0 - December 29, 2012
- Updated to SQLite 3.7.15.1.
- Add Visual Studio 2012 support to all the applicable solution/project files, their associated supporting files, and the test suite.
- Add Visual Studio 2012 support to the redesigned designer support installer.
- Allow opened connections to skip adding the extension functions included in the interop assembly via the new NoExtensionFunctions connection flag. Index: Doc/Extra/welcome.html ================================================================== --- Doc/Extra/welcome.html +++ Doc/Extra/welcome.html @@ -158,11 +158,11 @@
-
+ ///
HexPassword + ///{hexPassword} - Must contain a sequence of zero or more hexadecimal encoded byte values without a leading "0x" prefix. Using this parameter requires that the CryptoAPI based codec be enabled at compile-time for both the native interop assembly and the core managed assemblies; otherwise, using this parameter may result in an exception being thrown when attempting to open the connection. + ///N + ///+ ///
/// -
///
Enlist ///Y - Automatically enlist in distributed transactions ///
N - No automatic enlistmentN ///Y ///
/// -
///
Pooling - ///True - Use connection pooling + ///
False - Do not use connection pooling+ /// True - Use connection pooling. ///
+ /// False - Do not use connection pooling.
+ /// WARNING: When using the default connection pool implementation, + /// setting this property to True should be avoided by applications that make + /// use of COM (either directly or indirectly) due to possible deadlocks that + /// can occur during the finalization of some COM objects. + ///N ///False ///
/// -
///
FailIfMissing @@ -294,10 +308,11 @@ private const SQLiteDateFormats DefaultDateTimeFormat = SQLiteDateFormats.ISO8601; private const DateTimeKind DefaultDateTimeKind = DateTimeKind.Unspecified; private const string DefaultDataSource = null; private const string DefaultUri = null; private const string DefaultFullUri = null; + private const string DefaultHexPassword = null; private const string DefaultPassword = null; private const int DefaultVersion = 3; private const int DefaultPageSize = 1024; private const int DefaultMaxPageCount = 0; private const int DefaultCacheSize = 2000; @@ -474,14 +489,15 @@ { #if (SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK) && PRELOAD_NATIVE_LIBRARY UnsafeNativeMethods.Initialize(); #endif -#if !PLATFORM_COMPACTFRAMEWORK +#if !INTEROP_LOG SQLiteLog.Initialize(); +#endif -#if SQLITE_STANDARD +#if !PLATFORM_COMPACTFRAMEWORK && !INTEROP_LEGACY_CLOSE && SQLITE_STANDARD // // NOTE: Check if the sqlite3_close_v2() native API should be available // to use. This must be done dynamically because the delegate set // here is used by the SQLiteConnectionHandle class, which is a // CriticalHandle derived class (i.e. protected by a constrained @@ -500,11 +516,12 @@ if (_versionNumber >= 3007014) SQLiteConnectionHandle.closeConnection = SQLiteBase.CloseConnectionV2; } } #endif -#elif INTEROP_LOG + +#if INTEROP_LOG if (UnsafeNativeMethods.sqlite3_config_log_interop() == SQLiteErrorCode.Ok) { UnsafeNativeMethods.sqlite3_log( SQLiteErrorCode.Ok, SQLiteConvert.ToUTF8("logging initialized.")); } @@ -622,10 +639,48 @@ } } /////////////////////////////////////////////////////////////////////////////////////////////// + ///+ /// This property is used to obtain or set the custom connection pool + /// implementation to use, if any. Setting this property to null will + /// cause the default connection pool implementation to be used. + /// + public static ISQLiteConnectionPool ConnectionPool + { + get { return SQLiteConnectionPool.GetConnectionPool(); } + set { SQLiteConnectionPool.SetConnectionPool(value); } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + ///+ /// Creates and returns a new managed database connection handle. This + /// method is intended to be used by implementations of the + /// + /// + /// This must be a native database connection handle returned by the + /// SQLite core library and it must remain valid and open during the + /// entire duration of the calling method. + /// + ///interface only. In theory, it + /// could be used by other classes; however, that usage is not supported. + /// + /// The new managed database connection handle or null if it cannot be + /// created. + /// + public static object CreateHandle( + IntPtr nativeHandle + ) + { + if (nativeHandle == IntPtr.Zero) return null; + return new SQLiteConnectionHandle(nativeHandle); + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + #region Backup API Members ////// Backs up the database, using the specified database connection as the /// destination. /// @@ -717,11 +772,10 @@ // if (pages == 0) break; } } -#if !PLATFORM_COMPACTFRAMEWORK catch (Exception e) { if ((_flags & SQLiteConnectionFlags.LogBackup) == SQLiteConnectionFlags.LogBackup) { SQLiteLog.LogMessage(String.Format( @@ -729,11 +783,10 @@ "Caught exception while backing up database: {0}", e)); } throw; } -#endif finally { if (backup != null) sqliteBase.FinishBackup(backup); /* throw */ } @@ -1143,19 +1196,32 @@ ///Password ///{password} - Using this parameter requires that the CryptoAPI based codec be enabled at compile-time for both the native interop assembly and the core managed assemblies; otherwise, using this parameter may result in an exception being thrown when attempting to open the connection. ///N //////
+ /// -
+ ///
HexPassword + ///{hexPassword} - Must contain a sequence of zero or more hexadecimal encoded byte values without a leading "0x" prefix. Using this parameter requires that the CryptoAPI based codec be enabled at compile-time for both the native interop assembly and the core managed assemblies; otherwise, using this parameter may result in an exception being thrown when attempting to open the connection. + ///N + ///+ ///
/// -
///
Enlist ///Y - Automatically enlist in distributed transactions ///
N - No automatic enlistmentN ///Y ///
/// -
///
Pooling - ///True - Use connection pooling + ///
False - Do not use connection pooling+ /// True - Use connection pooling. ///
+ /// False - Do not use connection pooling.
+ /// WARNING: When using the default connection pool implementation, + /// setting this property to True should be avoided by applications that + /// make use of COM (either directly or indirectly) due to possible + /// deadlocks that can occur during the finalization of some COM objects. + ///N ///False ///
/// -
///
FailIfMissing @@ -1333,12 +1399,11 @@ { string s = connectionString; int n; SortedListls = new SortedList (StringComparer.OrdinalIgnoreCase); - // First split into semi-colon delimited values. The Split() function of SQLiteBase accounts for and properly - // skips semi-colons in quoted strings + // First split into semi-colon delimited values. string[] arParts = s.Split(';'); int x = arParts.Length; // For each semi-colon piece, split into key and value pairs by the presence of the = sign for (n = 0; n < x; n++) @@ -1489,10 +1554,48 @@ // do nothing. } return null; } + + /// + /// Attempts to convert an input string into a byte value. + /// + /// + /// The string value to be converted. + /// + /// + /// The number styles to use for the conversion. + /// + /// + /// Upon sucess, this will contain the parsed byte value. + /// Upon failure, the value of this parameter is undefined. + /// + ///+ /// Non-zero upon success; zero on failure. + /// + private static bool TryParseByte( + string value, + NumberStyles style, + out byte result + ) + { +#if !PLATFORM_COMPACTFRAMEWORK + return byte.TryParse(value, style, null, out result); +#else + try + { + result = byte.Parse(value, style); + return true; + } + catch + { + result = 0; + return false; + } +#endif + } ////// Enables or disabled extension loading. /// /// @@ -1548,10 +1651,116 @@ throw new InvalidOperationException( "Database connection not valid for loading extensions."); _sql.LoadExtension(fileName, procName); } + + ///+ /// Parses a string containing a sequence of zero or more hexadecimal + /// encoded byte values and returns the resulting byte array. The + /// "0x" prefix is not allowed on the input string. + /// + /// + /// The input string containing zero or more hexadecimal encoded byte + /// values. + /// + ///+ /// A byte array containing the parsed byte values or null if an error + /// was encountered. + /// + internal static byte[] FromHexString( + string text + ) + { + string error = null; + + return FromHexString(text, ref error); + } + + ///+ /// Creates and returns a string containing the hexadecimal encoded byte + /// values from the input array. + /// + /// + /// The input array of bytes. + /// + ///+ /// The resulting string or null upon failure. + /// + internal static string ToHexString( + byte[] array + ) + { + if (array == null) + return null; + + StringBuilder result = new StringBuilder(); + + int length = array.Length; + + for (int index = 0; index < length; index++) +#if NET_COMPACT_20 + result.Append(String.Format("{0:x2}", array[index])); +#else + result.AppendFormat("{0:x2}", array[index]); +#endif + + return result.ToString(); + } + + ///+ /// Parses a string containing a sequence of zero or more hexadecimal + /// encoded byte values and returns the resulting byte array. The + /// "0x" prefix is not allowed on the input string. + /// + /// + /// The input string containing zero or more hexadecimal encoded byte + /// values. + /// + /// + /// Upon failure, this will contain an appropriate error message. + /// + ///+ /// A byte array containing the parsed byte values or null if an error + /// was encountered. + /// + private static byte[] FromHexString( + string text, + ref string error + ) + { + if (String.IsNullOrEmpty(text)) + { + error = "string is null or empty"; + return null; + } + + if (text.Length % 2 != 0) + { + error = "string contains an odd number of characters"; + return null; + } + + byte[] result = new byte[text.Length / 2]; + + for (int index = 0; index < text.Length; index += 2) + { + string value = text.Substring(index, 2); + + if (!TryParseByte(value, + NumberStyles.HexNumber, out result[index / 2])) + { + error = String.Format( + "string contains \"{0}\", which cannot be converted to a byte value", + value); + + return null; + } + } + + return result; + } ////// Opens the connection using the parameters found in the public override void Open() @@ -1684,17 +1893,35 @@ _sql.Open(fileName, _flags, flags, maxPoolSize, usePooling); _binaryGuid = SQLiteConvert.ToBoolean(FindKey(opts, "BinaryGUID", DefaultBinaryGUID.ToString())); #if INTEROP_CODEC - string password = FindKey(opts, "Password", DefaultPassword); + string hexPassword = FindKey(opts, "HexPassword", DefaultHexPassword); - if (!String.IsNullOrEmpty(password)) - _sql.SetPassword(System.Text.UTF8Encoding.UTF8.GetBytes(password)); - else if (_password != null) - _sql.SetPassword(_password); + if (!String.IsNullOrEmpty(hexPassword)) + { + string error = null; + byte[] hexPasswordBytes = FromHexString(hexPassword, ref error); + if (hexPasswordBytes == null) + { + throw new FormatException(String.Format( + "Cannot parse 'HexPassword' property value into byte values: {0}", + error)); + } + + _sql.SetPassword(hexPasswordBytes); + } + else + { + string password = FindKey(opts, "Password", DefaultPassword); + + if (!String.IsNullOrEmpty(password)) + _sql.SetPassword(UTF8Encoding.UTF8.GetBytes(password)); + else if (_password != null) + _sql.SetPassword(_password); + } _password = null; #endif if (!fullUri) _dataSource = Path.GetFileNameWithoutExtension(fileName); @@ -2135,11 +2362,11 @@ /// The new password to assign to the database public void ChangePassword(string newPassword) { CheckDisposed(); - ChangePassword(String.IsNullOrEmpty(newPassword) ? null : System.Text.UTF8Encoding.UTF8.GetBytes(newPassword)); + ChangePassword(String.IsNullOrEmpty(newPassword) ? null : UTF8Encoding.UTF8.GetBytes(newPassword)); } ///. /// /// Change the password (or assign a password) to an open database. /// @@ -2165,11 +2392,11 @@ /// The password for the database public void SetPassword(string databasePassword) { CheckDisposed(); - SetPassword(String.IsNullOrEmpty(databasePassword) ? null : System.Text.UTF8Encoding.UTF8.GetBytes(databasePassword)); + SetPassword(String.IsNullOrEmpty(databasePassword) ? null : UTF8Encoding.UTF8.GetBytes(databasePassword)); } ////// Sets the password for a password-protected database. A password-protected database is /// unusable for any operation until the password has been set. Index: System.Data.SQLite/SQLiteConnectionPool.cs ================================================================== --- System.Data.SQLite/SQLiteConnectionPool.cs +++ System.Data.SQLite/SQLiteConnectionPool.cs @@ -1,410 +1,1028 @@ -/******************************************************** - * 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.Threading; - - internal static class SQLiteConnectionPool - { - /// private const int COR_E_EXCEPTION = unchecked((int)0x80131500); #endregion ///////////////////////////////////////////////////////////////////////// - - ///- /// Keeps track of connections made on a specified file. The PoolVersion dictates whether old objects get - /// returned to the pool or discarded when no longer in use. - /// - internal class Pool - { - internal readonly QueueQueue = new Queue (); - internal int PoolVersion; - internal int MaxPoolSize; - - internal Pool(int version, int maxSize) - { - PoolVersion = version; - MaxPoolSize = maxSize; - } - } - - /// - /// The connection pool object - /// - private static SortedList_connections = new SortedList (StringComparer.OrdinalIgnoreCase); - - /// - /// The default version number new pools will get - /// - private static int _poolVersion = 1; - - ///- /// The number of connections successfully opened from any pool. - /// This value is incremented by the Remove method. - /// - private static int _poolOpened = 0; - - ///- /// The number of connections successfully closed from any pool. - /// This value is incremented by the Add method. - /// - private static int _poolClosed = 0; - - ///- /// Counts the number of pool entries matching the specified file name. - /// - /// The file name to match or null to match all files. - /// The pool entry counts for each matching file. - /// The total number of connections successfully opened from any pool. - /// The total number of connections successfully closed from any pool. - /// The total number of pool entries for all matching files. - internal static void GetCounts( - string fileName, - ref Dictionarycounts, - ref int openCount, - ref int closeCount, - ref int totalCount - ) - { - lock (_connections) - { - openCount = _poolOpened; - closeCount = _poolClosed; - - if (counts == null) - { - counts = new Dictionary ( - StringComparer.OrdinalIgnoreCase); - } - - if (fileName != null) - { - Pool queue; - - if (_connections.TryGetValue(fileName, out queue)) - { - Queue poolQueue = queue.Queue; - int count = (poolQueue != null) ? poolQueue.Count : 0; - - counts.Add(fileName, count); - totalCount += count; - } - } - else - { - foreach (KeyValuePair pair in _connections) - { - if (pair.Value == null) - continue; - - Queue poolQueue = pair.Value.Queue; - int count = (poolQueue != null) ? poolQueue.Count : 0; - - counts.Add(pair.Key, count); - totalCount += count; - } - } - } - } - - /// - /// Attempt to pull a pooled connection out of the queue for active duty - /// - /// The filename for a desired connection - /// The maximum size the connection pool for the filename can be - /// The pool version the returned connection will belong to - ///Returns NULL if no connections were available. Even if none are, the poolversion will still be a valid pool version - internal static SQLiteConnectionHandle Remove(string fileName, int maxPoolSize, out int version) - { - int localVersion; - QueuepoolQueue; - - // - // NOTE: This lock cannot be held while checking the queue for available - // connections because other methods of this class are called from - // the GC finalizer thread and we use the WaitForPendingFinalizers - // method (below). Holding this lock while calling that method - // would therefore result in a deadlock. Instead, this lock is - // held only while a temporary copy of the queue is created, and - // if necessary, when committing changes back to that original - // queue prior to returning from this method. - // - lock (_connections) - { - Pool queue; - - // Default to the highest pool version - version = _poolVersion; - - // If we didn't find a pool for this file, create one even though it will be empty. - // We have to do this here because otherwise calling ClearPool() on the file will not work for active connections - // that have never seen the pool yet. - if (_connections.TryGetValue(fileName, out queue) == false) - { - queue = new Pool(_poolVersion, maxPoolSize); - _connections.Add(fileName, queue); - - return null; - } - - // We found a pool for this file, so use its version number - version = localVersion = queue.PoolVersion; - queue.MaxPoolSize = maxPoolSize; - - ResizePool(queue, false); - - // Try and get a pooled connection from the queue - poolQueue = queue.Queue; - if (poolQueue == null) return null; - - // - // NOTE: Temporarily tranfer the queue for this file into a local - // variable. The queue for this file will be modified and - // then committed back to the real pool list (below) prior - // to returning from this method. - // - _connections.Remove(fileName); - poolQueue = new Queue (poolQueue); - } - - try - { - while (poolQueue.Count > 0) - { - WeakReference cnn = poolQueue.Dequeue(); - if (cnn == null) continue; - - SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle; - if (hdl == null) continue; - - // - // BUGFIX: For ticket [996d13cd87], step #1. After this point, - // make sure that the finalizer for the connection - // handle just obtained from the queue cannot START - // running (i.e. it may still be pending but it will no - // longer start after this point). - // - GC.SuppressFinalize(hdl); - - try - { - // - // BUGFIX: For ticket [996d13cd87], step #2. Now, we must wait - // for all pending finalizers which have STARTED running - // and have not yet COMPLETED. This must be done just - // in case the finalizer for the connection handle just - // obtained from the queue has STARTED running at some - // point before SuppressFinalize was called on it. - // - // After this point, checking properties of the - // connection handle (e.g. IsClosed) should work - // reliably without having to worry that they will - // (due to the finalizer) change out from under us. - // - GC.WaitForPendingFinalizers(); - - // - // BUGFIX: For ticket [996d13cd87], step #3. Next, verify that - // the connection handle is actually valid and [still?] - // not closed prior to actually returning it to our - // caller. - // - if (!hdl.IsInvalid && !hdl.IsClosed) - { - Interlocked.Increment(ref _poolOpened); - return hdl; - } - } - finally - { - // - // BUGFIX: For ticket [996d13cd87], step #4. Next, we must - // re-register the connection handle for finalization - // now that we have a strong reference to it (i.e. the - // finalizer will not run at least until the connection - // is subsequently closed). - // - GC.ReRegisterForFinalize(hdl); - } - - GC.KeepAlive(hdl); - } - } - finally - { - // - // BUGFIX: For ticket [996d13cd87], step #5. Finally, commit any - // changes to the pool/queue for this database file. - // - lock (_connections) - { - // - // NOTE: We must check [again] if a pool exists for this file - // because one may have been added while the search for - // an available connection was in progress (above). - // - Pool queue; - Queue newPoolQueue; - bool addPool; - - if (_connections.TryGetValue(fileName, out queue)) - { - addPool = false; - } - else - { - addPool = true; - queue = new Pool(localVersion, maxPoolSize); - } - - newPoolQueue = queue.Queue; - - while (poolQueue.Count > 0) - newPoolQueue.Enqueue(poolQueue.Dequeue()); - - ResizePool(queue, false); - - if (addPool) - _connections.Add(fileName, queue); - } - } - - return null; - } - - /// - /// Clears out all pooled connections and rev's up the default pool version to force all old active objects - /// not in the pool to get discarded rather than returned to their pools. - /// - internal static void ClearAllPools() - { - lock (_connections) - { - foreach (KeyValuePairpair in _connections) - { - if (pair.Value == null) - continue; - - Queue poolQueue = pair.Value.Queue; - - while (poolQueue.Count > 0) - { - WeakReference cnn = poolQueue.Dequeue(); - if (cnn == null) continue; - SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle; - if (hdl != null) - { - hdl.Dispose(); - } - GC.KeepAlive(hdl); - } - - // Keep track of the highest revision so we can go one higher when we're finished - if (_poolVersion <= pair.Value.PoolVersion) - _poolVersion = pair.Value.PoolVersion + 1; - } - // All pools are cleared and we have a new highest version number to force all old version active items to get discarded - // instead of going back to the queue when they are closed. - // We can get away with this because we've pumped up the _poolVersion out of range of all active connections, so they - // will all get discarded when they try to put themselves back in their pool. - _connections.Clear(); - } - } - - /// - /// Clear a given pool for a given filename. Discards anything in the pool for the given file, and revs the pool - /// version so current active objects on the old version of the pool will get discarded rather than be returned to the pool. - /// - /// The filename of the pool to clear - internal static void ClearPool(string fileName) - { - lock (_connections) - { - Pool queue; - if (_connections.TryGetValue(fileName, out queue) == true) - { - queue.PoolVersion++; - - QueuepoolQueue = queue.Queue; - if (poolQueue == null) return; - - while (poolQueue.Count > 0) - { - WeakReference cnn = poolQueue.Dequeue(); - if (cnn == null) continue; - SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle; - if (hdl != null) - { - hdl.Dispose(); - } - GC.KeepAlive(hdl); - } - } - } - } - - /// - /// Return a connection to the pool for someone else to use. - /// - /// The filename of the pool to use - /// The connection handle to pool - /// The pool version the handle was created under - ///- /// If the version numbers don't match between the connection and the pool, then the handle is discarded. - /// - internal static void Add(string fileName, SQLiteConnectionHandle hdl, int version) - { - lock (_connections) - { - // If the queue doesn't exist in the pool, then it must've been cleared sometime after the connection was created. - Pool queue; - if (_connections.TryGetValue(fileName, out queue) == true && version == queue.PoolVersion) - { - ResizePool(queue, true); - - QueuepoolQueue = queue.Queue; - if (poolQueue == null) return; - - poolQueue.Enqueue(new WeakReference(hdl, false)); - Interlocked.Increment(ref _poolClosed); - } - else - { - hdl.Close(); - } - GC.KeepAlive(hdl); - } - } - - /// - /// We don't have to thread-lock anything in this function, because it's only called by other functions above - /// which already have a thread-safe lock. - /// - /// The queue to resize - /// If a function intends to add to the pool, this is true, which forces the resize - /// to take one more than it needs from the pool - private static void ResizePool(Pool queue, bool forAdding) - { - int target = queue.MaxPoolSize; - - if (forAdding && target > 0) target--; - - QueuepoolQueue = queue.Queue; - if (poolQueue == null) return; - - while (poolQueue.Count > target) - { - WeakReference cnn = poolQueue.Dequeue(); - if (cnn == null) continue; - SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle; - if (hdl != null) - { - hdl.Dispose(); - } - GC.KeepAlive(hdl); - } - } - } -} +/******************************************************** + * 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; + +#if !PLATFORM_COMPACTFRAMEWORK && DEBUG + using System.Text; +#endif + + using System.Threading; + + /////////////////////////////////////////////////////////////////////////// + + #region Null Connection Pool Class +#if !PLATFORM_COMPACTFRAMEWORK && DEBUG + /// + /// This class implements a connection pool where all methods of the + /// + internal sealed class NullConnectionPool : ISQLiteConnectionPool + { + #region Private Data + ///interface are NOPs. This class + /// is used for testing purposes only. + /// + /// This field keeps track of all method calls made into the + /// + private StringBuilder log; + + /////////////////////////////////////////////////////////////////////// + + ///interface methods of this + /// class. + /// + /// Non-zero to dispose of database connection handles received via the + /// + private bool dispose; + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Private Constructors + ///method. + /// + /// Constructs a connection pool object where all methods of the + /// + private NullConnectionPool() + { + log = new StringBuilder(); + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Public Constructors + ///interface are NOPs. This + /// class is used for testing purposes only. + /// + /// Constructs a connection pool object where all methods of the + /// + /// + /// Non-zero to dispose of database connection handles received via the + ///interface are NOPs. This + /// class is used for testing purposes only. + /// method. + /// + public NullConnectionPool( + bool dispose + ) + : this() + { + this.dispose = dispose; + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region ISQLiteConnectionPool Members + /// + /// Counts the number of pool entries matching the specified file name. + /// + /// + /// The file name to match or null to match all files. + /// + /// + /// The pool entry counts for each matching file. + /// + /// + /// The total number of connections successfully opened from any pool. + /// + /// + /// The total number of connections successfully closed from any pool. + /// + /// + /// The total number of pool entries for all matching files. + /// + public void GetCounts( + string fileName, + ref Dictionarycounts, + ref int openCount, + ref int closeCount, + ref int totalCount + ) + { + if (log != null) + { + log.AppendFormat( + "GetCounts(\"{0}\", {1}, {2}, {3}, {4}){5}", fileName, + counts, openCount, closeCount, totalCount, + Environment.NewLine); + } + } + + /////////////////////////////////////////////////////////////////////// + + /// + /// Disposes of all pooled connections associated with the specified + /// database file name. + /// + /// + /// The database file name. + /// + public void ClearPool( + string fileName + ) + { + if (log != null) + { + log.AppendFormat( + "ClearPool(\"{0}\"){1}", fileName, Environment.NewLine); + } + } + + /////////////////////////////////////////////////////////////////////// + + ///+ /// Disposes of all pooled connections. + /// + public void ClearAllPools() + { + if (log != null) + { + log.AppendFormat( + "ClearAllPools(){0}", Environment.NewLine); + } + } + + /////////////////////////////////////////////////////////////////////// + + ///+ /// Adds a connection to the pool of those associated with the + /// specified database file name. + /// + /// + /// The database file name. + /// + /// + /// The database connection handle. + /// + /// + /// The connection pool version at the point the database connection + /// handle was received from the connection pool. This is also the + /// connection pool version that the database connection handle was + /// created under. + /// + public void Add( + string fileName, + object handle, + int version + ) + { + if (log != null) + { + log.AppendFormat( + "Add(\"{0}\", {1}, {2}){3}", fileName, handle, version, + Environment.NewLine); + } + + // + // NOTE: If configured to do so, dispose of the received connection + // handle now. + // + if (dispose) + { + IDisposable disposable = handle as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /////////////////////////////////////////////////////////////////////// + + ///+ /// Removes a connection from the pool of those associated with the + /// specified database file name with the intent of using it to + /// interact with the database. + /// + /// + /// The database file name. + /// + /// + /// The new maximum size of the connection pool for the specified + /// database file name. + /// + /// + /// The connection pool version associated with the returned database + /// connection handle, if any. + /// + ///+ /// The database connection handle associated with the specified + /// database file name or null if it cannot be obtained. + /// + public object Remove( + string fileName, + int maxPoolSize, + out int version + ) + { + version = 0; + + if (log != null) + { + log.AppendFormat( + "Remove(\"{0}\", {1}, {2}){3}", fileName, maxPoolSize, + version, Environment.NewLine); + } + + return null; + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region System.Object Overrides + ///+ /// Overrides the default + ///method + /// to provide a log of all methods called on the + /// interface. + /// + /// A string containing a log of all method calls into the + /// + public override string ToString() + { + return (log != null) ? log.ToString() : String.Empty; + } + #endregion + } +#endif + #endregion + + /////////////////////////////////////////////////////////////////////////// + + #region Public Connection Pool Interface + ///interface, along with their + /// parameters, delimited by . + /// + /// This interface represents a custom connection pool implementation + /// usable by System.Data.SQLite. + /// + public interface ISQLiteConnectionPool + { + ///+ /// Counts the number of pool entries matching the specified file name. + /// + /// + /// The file name to match or null to match all files. + /// + /// + /// The pool entry counts for each matching file. + /// + /// + /// The total number of connections successfully opened from any pool. + /// + /// + /// The total number of connections successfully closed from any pool. + /// + /// + /// The total number of pool entries for all matching files. + /// + void GetCounts(string fileName, ref Dictionarycounts, + ref int openCount, ref int closeCount, ref int totalCount); + + /// + /// Disposes of all pooled connections associated with the specified + /// database file name. + /// + /// + /// The database file name. + /// + void ClearPool(string fileName); + + ///+ /// Disposes of all pooled connections. + /// + void ClearAllPools(); + + ///+ /// Adds a connection to the pool of those associated with the + /// specified database file name. + /// + /// + /// The database file name. + /// + /// + /// The database connection handle. + /// + /// + /// The connection pool version at the point the database connection + /// handle was received from the connection pool. This is also the + /// connection pool version that the database connection handle was + /// created under. + /// + void Add(string fileName, object handle, int version); + + ///+ /// Removes a connection from the pool of those associated with the + /// specified database file name with the intent of using it to + /// interact with the database. + /// + /// + /// The database file name. + /// + /// + /// The new maximum size of the connection pool for the specified + /// database file name. + /// + /// + /// The connection pool version associated with the returned database + /// connection handle, if any. + /// + ///+ /// The database connection handle associated with the specified + /// database file name or null if it cannot be obtained. + /// + object Remove(string fileName, int maxPoolSize, out int version); + } + #endregion + + /////////////////////////////////////////////////////////////////////////// + + #region Connection Pool Subsystem & Default Implementation + ///+ /// This default method implementations in this class should not be used by + /// applications that make use of COM (either directly or indirectly) due + /// to possible deadlocks that can occur during finalization of some COM + /// objects. + /// + internal static class SQLiteConnectionPool + { + #region Private Pool Class + ///+ /// Keeps track of connections made on a specified file. The PoolVersion + /// dictates whether old objects get returned to the pool or discarded + /// when no longer in use. + /// + private sealed class PoolQueue + { + #region Private Data + ///+ /// The queue of weak references to the actual database connection + /// handles. + /// + internal readonly QueueQueue = + new Queue (); + + /////////////////////////////////////////////////////////////////// + + /// + /// This pool version associated with the database connection + /// handles in this pool queue. + /// + internal int PoolVersion; + + /////////////////////////////////////////////////////////////////// + + ///+ /// The maximum size of this pool queue. + /// + internal int MaxPoolSize; + #endregion + + /////////////////////////////////////////////////////////////////// + + #region Private Constructors + ///+ /// Constructs a connection pool queue using the specified version + /// and maximum size. Normally, all the database connection + /// handles in this pool are associated with a single database file + /// name. + /// + /// + /// The initial pool version for this connection pool queue. + /// + /// + /// The initial maximum size for this connection pool queue. + /// + internal PoolQueue( + int version, + int maxSize + ) + { + PoolVersion = version; + MaxPoolSize = maxSize; + } + #endregion + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Private Static Data + ///+ /// This field is used to synchronize access to the private static data + /// in this class. + /// + private static readonly object _syncRoot = new object(); + + /////////////////////////////////////////////////////////////////////// + + ///+ /// When this field is non-null, it will be used to provide the + /// implementation of all the connection pool methods; otherwise, + /// the default method implementations will be used. + /// + private static ISQLiteConnectionPool _connectionPool = null; + + /////////////////////////////////////////////////////////////////////// + + ///+ /// The dictionary of connection pools, based on the normalized file + /// name of the SQLite database. + /// + private static SortedList_queueList = + new SortedList (StringComparer.OrdinalIgnoreCase); + + /////////////////////////////////////////////////////////////////////// + + /// + /// The default version number new pools will get. + /// + private static int _poolVersion = 1; + + /////////////////////////////////////////////////////////////////////// + + ///+ /// The number of connections successfully opened from any pool. + /// This value is incremented by the Remove method. + /// + private static int _poolOpened = 0; + + /////////////////////////////////////////////////////////////////////// + + ///+ /// The number of connections successfully closed from any pool. + /// This value is incremented by the Add method. + /// + private static int _poolClosed = 0; + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region ISQLiteConnectionPool Members (Static, Non-Formal) + ///+ /// Counts the number of pool entries matching the specified file name. + /// + /// + /// The file name to match or null to match all files. + /// + /// + /// The pool entry counts for each matching file. + /// + /// + /// The total number of connections successfully opened from any pool. + /// + /// + /// The total number of connections successfully closed from any pool. + /// + /// + /// The total number of pool entries for all matching files. + /// + internal static void GetCounts( + string fileName, + ref Dictionarycounts, + ref int openCount, + ref int closeCount, + ref int totalCount + ) + { + ISQLiteConnectionPool connectionPool = GetConnectionPool(); + + if (connectionPool != null) + { + connectionPool.GetCounts( + fileName, ref counts, ref openCount, ref closeCount, + ref totalCount); + } + else + { + lock (_syncRoot) + { + openCount = _poolOpened; + closeCount = _poolClosed; + + if (counts == null) + { + counts = new Dictionary ( + StringComparer.OrdinalIgnoreCase); + } + + if (fileName != null) + { + PoolQueue queue; + + if (_queueList.TryGetValue(fileName, out queue)) + { + Queue poolQueue = queue.Queue; + int count = (poolQueue != null) ? poolQueue.Count : 0; + + counts.Add(fileName, count); + totalCount += count; + } + } + else + { + foreach (KeyValuePair pair in _queueList) + { + if (pair.Value == null) + continue; + + Queue poolQueue = pair.Value.Queue; + int count = (poolQueue != null) ? poolQueue.Count : 0; + + counts.Add(pair.Key, count); + totalCount += count; + } + } + } + } + } + + /////////////////////////////////////////////////////////////////////// + + /// + /// Disposes of all pooled connections associated with the specified + /// database file name. + /// + /// + /// The database file name. + /// + internal static void ClearPool(string fileName) + { + ISQLiteConnectionPool connectionPool = GetConnectionPool(); + + if (connectionPool != null) + { + connectionPool.ClearPool(fileName); + } + else + { + lock (_syncRoot) + { + PoolQueue queue; + + if (_queueList.TryGetValue(fileName, out queue)) + { + queue.PoolVersion++; + + QueuepoolQueue = queue.Queue; + if (poolQueue == null) return; + + while (poolQueue.Count > 0) + { + WeakReference connection = poolQueue.Dequeue(); + + if (connection == null) continue; + + SQLiteConnectionHandle handle = + connection.Target as SQLiteConnectionHandle; + + if (handle != null) + handle.Dispose(); + + GC.KeepAlive(handle); + } + } + } + } + } + + /////////////////////////////////////////////////////////////////////// + + /// + /// Disposes of all pooled connections. + /// + internal static void ClearAllPools() + { + ISQLiteConnectionPool connectionPool = GetConnectionPool(); + + if (connectionPool != null) + { + connectionPool.ClearAllPools(); + } + else + { + lock (_syncRoot) + { + foreach (KeyValuePairpair in _queueList) + { + if (pair.Value == null) + continue; + + Queue poolQueue = pair.Value.Queue; + + while (poolQueue.Count > 0) + { + WeakReference connection = poolQueue.Dequeue(); + + if (connection == null) continue; + + SQLiteConnectionHandle handle = + connection.Target as SQLiteConnectionHandle; + + if (handle != null) + handle.Dispose(); + + GC.KeepAlive(handle); + } + + // + // NOTE: Keep track of the highest revision so we can + // go one higher when we are finished. + // + if (_poolVersion <= pair.Value.PoolVersion) + _poolVersion = pair.Value.PoolVersion + 1; + } + + // + // NOTE: All pools are cleared and we have a new highest + // version number to force all old version active + // items to get discarded instead of going back to + // the queue when they are closed. We can get away + // with this because we have pumped up the pool + // version out of range of all active connections, + // so they will all get discarded when they try to + // put themselves back into their pools. + // + _queueList.Clear(); + } + } + } + + /////////////////////////////////////////////////////////////////////// + + /// + /// Adds a connection to the pool of those associated with the + /// specified database file name. + /// + /// + /// The database file name. + /// + /// + /// The database connection handle. + /// + /// + /// The connection pool version at the point the database connection + /// handle was received from the connection pool. This is also the + /// connection pool version that the database connection handle was + /// created under. + /// + internal static void Add( + string fileName, + SQLiteConnectionHandle handle, + int version + ) + { + ISQLiteConnectionPool connectionPool = GetConnectionPool(); + + if (connectionPool != null) + { + connectionPool.Add(fileName, handle, version); + } + else + { + lock (_syncRoot) + { + // + // NOTE: If the queue does not exist in the pool, then it + // must have been cleared sometime after the + // connection was created. + // + PoolQueue queue; + + if (_queueList.TryGetValue(fileName, out queue) && + (version == queue.PoolVersion)) + { + ResizePool(queue, true); + + QueuepoolQueue = queue.Queue; + if (poolQueue == null) return; + + poolQueue.Enqueue(new WeakReference(handle, false)); + Interlocked.Increment(ref _poolClosed); + } + else + { + handle.Close(); + } + + GC.KeepAlive(handle); + } + } + } + + /////////////////////////////////////////////////////////////////////// + + /// + /// Removes a connection from the pool of those associated with the + /// specified database file name with the intent of using it to + /// interact with the database. + /// + /// + /// The database file name. + /// + /// + /// The new maximum size of the connection pool for the specified + /// database file name. + /// + /// + /// The connection pool version associated with the returned database + /// connection handle, if any. + /// + ///+ /// The database connection handle associated with the specified + /// database file name or null if it cannot be obtained. + /// + internal static SQLiteConnectionHandle Remove( + string fileName, + int maxPoolSize, + out int version + ) + { + ISQLiteConnectionPool connectionPool = GetConnectionPool(); + + if (connectionPool != null) + { + return connectionPool.Remove(fileName, maxPoolSize, + out version) as SQLiteConnectionHandle; + } + else + { + int localVersion; + QueuepoolQueue; + + // + // NOTE: This lock cannot be held while checking the queue for + // available connections because other methods of this + // class are called from the GC finalizer thread and we + // use the WaitForPendingFinalizers method (below). + // Holding this lock while calling that method would + // therefore result in a deadlock. Instead, this lock + // is held only while a temporary copy of the queue is + // created, and if necessary, when committing changes + // back to that original queue prior to returning from + // this method. + // + lock (_syncRoot) + { + PoolQueue queue; + + // + // NOTE: Default to the highest pool version. + // + version = _poolVersion; + + // + // NOTE: If we didn't find a pool for this file, create one + // even though it will be empty. We have to do this + // here because otherwise calling ClearPool() on the + // file will not work for active connections that have + // never seen the pool yet. + // + if (!_queueList.TryGetValue(fileName, out queue)) + { + queue = new PoolQueue(_poolVersion, maxPoolSize); + _queueList.Add(fileName, queue); + + return null; + } + + // + // NOTE: We found a pool for this file, so use its version + // number. + // + version = localVersion = queue.PoolVersion; + queue.MaxPoolSize = maxPoolSize; + + // + // NOTE: Now, resize the pool to the new maximum size, if + // necessary. + // + ResizePool(queue, false); + + // + // NOTE: Try and get a pooled connection from the queue. + // + poolQueue = queue.Queue; + if (poolQueue == null) return null; + + // + // NOTE: Temporarily tranfer the queue for this file into + // a local variable. The queue for this file will + // be modified and then committed back to the real + // pool list (below) prior to returning from this + // method. + // + _queueList.Remove(fileName); + poolQueue = new Queue (poolQueue); + } + + try + { + while (poolQueue.Count > 0) + { + WeakReference connection = poolQueue.Dequeue(); + + if (connection == null) continue; + + SQLiteConnectionHandle handle = + connection.Target as SQLiteConnectionHandle; + + if (handle == null) continue; + + // + // BUGFIX: For ticket [996d13cd87], step #1. After + // this point, make sure that the finalizer for + // the connection handle just obtained from the + // queue cannot START running (i.e. it may + // still be pending but it will no longer start + // after this point). + // + GC.SuppressFinalize(handle); + + try + { + // + // BUGFIX: For ticket [996d13cd87], step #2. Now, + // we must wait for all pending finalizers + // which have STARTED running and have not + // yet COMPLETED. This must be done just + // in case the finalizer for the connection + // handle just obtained from the queue has + // STARTED running at some point before + // SuppressFinalize was called on it. + // + // After this point, checking properties of + // the connection handle (e.g. IsClosed) + // should work reliably without having to + // worry that they will (due to the + // finalizer) change out from under us. + // + GC.WaitForPendingFinalizers(); + + // + // BUGFIX: For ticket [996d13cd87], step #3. Next, + // verify that the connection handle is + // actually valid and [still?] not closed + // prior to actually returning it to our + // caller. + // + if (!handle.IsInvalid && !handle.IsClosed) + { + Interlocked.Increment(ref _poolOpened); + return handle; + } + } + finally + { + // + // BUGFIX: For ticket [996d13cd87], step #4. Next, + // we must re-register the connection + // handle for finalization now that we have + // a strong reference to it (i.e. the + // finalizer will not run at least until + // the connection is subsequently closed). + // + GC.ReRegisterForFinalize(handle); + } + + GC.KeepAlive(handle); + } + } + finally + { + // + // BUGFIX: For ticket [996d13cd87], step #5. Finally, + // commit any changes to the pool/queue for this + // database file. + // + lock (_syncRoot) + { + // + // NOTE: We must check [again] if a pool exists for + // this file because one may have been added + // while the search for an available connection + // was in progress (above). + // + PoolQueue queue; + Queue newPoolQueue; + bool addPool; + + if (_queueList.TryGetValue(fileName, out queue)) + { + addPool = false; + } + else + { + addPool = true; + queue = new PoolQueue(localVersion, maxPoolSize); + } + + newPoolQueue = queue.Queue; + + while (poolQueue.Count > 0) + newPoolQueue.Enqueue(poolQueue.Dequeue()); + + ResizePool(queue, false); + + if (addPool) + _queueList.Add(fileName, queue); + } + } + + return null; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Private Helper Methods + /// + /// This method is used to obtain a reference to the custom connection + /// pool implementation currently in use, if any. + /// + ///+ /// The custom connection pool implementation or null if the default + /// connection pool implementation should be used. + /// + internal static ISQLiteConnectionPool GetConnectionPool() + { + lock (_syncRoot) + { + return _connectionPool; + } + } + + /////////////////////////////////////////////////////////////////////// + + ///+ /// This method is used to set the reference to the custom connection + /// pool implementation to use, if any. + /// + /// + /// The custom connection pool implementation to use or null if the + /// default connection pool implementation should be used. + /// + internal static void SetConnectionPool( + ISQLiteConnectionPool connectionPool + ) + { + lock (_syncRoot) + { + _connectionPool = connectionPool; + } + } + + /////////////////////////////////////////////////////////////////////// + + ///+ /// We do not have to thread-lock anything in this function, because it + /// is only called by other functions above which already take the lock. + /// + /// + /// The pool queue to resize. + /// + /// + /// If a function intends to add to the pool, this is true, which + /// forces the resize to take one more than it needs from the pool. + /// + private static void ResizePool( + PoolQueue queue, + bool add + ) + { + int target = queue.MaxPoolSize; + + if (add && target > 0) target--; + + QueuepoolQueue = queue.Queue; + if (poolQueue == null) return; + + while (poolQueue.Count > target) + { + WeakReference connection = poolQueue.Dequeue(); + + if (connection == null) continue; + + SQLiteConnectionHandle handle = + connection.Target as SQLiteConnectionHandle; + + if (handle != null) + handle.Dispose(); + + GC.KeepAlive(handle); + } + } + #endregion + } + #endregion +} Index: System.Data.SQLite/SQLiteConnectionStringBuilder.cs ================================================================== --- System.Data.SQLite/SQLiteConnectionStringBuilder.cs +++ System.Data.SQLite/SQLiteConnectionStringBuilder.cs @@ -1,212 +1,212 @@ -/******************************************************** - * 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.Data.Common; - using System.ComponentModel; - using System.Collections; - using System.Globalization; - using System.Reflection; - -#if !PLATFORM_COMPACTFRAMEWORK - /// - /// SQLite implementation of DbConnectionStringBuilder. - /// - [DefaultProperty("DataSource")] - [DefaultMember("Item")] - public sealed class SQLiteConnectionStringBuilder : DbConnectionStringBuilder - { - ///- /// Properties of this class - /// - private Hashtable _properties; - - ///- /// Constructs a new instance of the class - /// - ///- /// Default constructor - /// - public SQLiteConnectionStringBuilder() - { - Initialize(null); - } - - ///- /// Constructs a new instance of the class using the specified connection string. - /// - /// The connection string to parse - public SQLiteConnectionStringBuilder(string connectionString) - { - Initialize(connectionString); - } - - ///- /// Private initializer, which assigns the connection string and resets the builder - /// - /// The connection string to assign - private void Initialize(string cnnString) - { - _properties = new Hashtable(StringComparer.OrdinalIgnoreCase); - try - { - base.GetProperties(_properties); - } - catch(NotImplementedException) - { - FallbackGetProperties(_properties); - } - - if (String.IsNullOrEmpty(cnnString) == false) - ConnectionString = cnnString; - } - - ///- /// Gets/Sets the default version of the SQLite engine to instantiate. Currently the only valid value is 3, indicating version 3 of the sqlite library. - /// - [Browsable(true)] - [DefaultValue(3)] - public int Version - { - get - { - object value; - TryGetValue("version", out value); - return Convert.ToInt32(value, CultureInfo.CurrentCulture); - } - set - { - if (value != 3) - throw new NotSupportedException(); - - this["version"] = value; - } - } - - ///- /// Gets/Sets the synchronization mode (file flushing) of the connection string. Default is "Normal". - /// - [DisplayName("Synchronous")] - [Browsable(true)] - [DefaultValue(SynchronizationModes.Normal)] - public SynchronizationModes SyncMode - { - get - { - object value; - TryGetValue("synchronous", out value); - if (value is string) - return (SynchronizationModes)TypeDescriptor.GetConverter(typeof(SynchronizationModes)).ConvertFrom(value); - else return (SynchronizationModes)value; - } - set - { - this["synchronous"] = value; - } - } - - ///- /// Gets/Sets the encoding for the connection string. The default is "False" which indicates UTF-8 encoding. - /// - [Browsable(true)] - [DefaultValue(false)] - public bool UseUTF16Encoding - { - get - { - object value; - TryGetValue("useutf16encoding", out value); - return SQLiteConvert.ToBoolean(value); - } - set - { - this["useutf16encoding"] = value; - } - } - - ///- /// Gets/Sets whether or not to use connection pooling. The default is "False" - /// - [Browsable(true)] - [DefaultValue(false)] - public bool Pooling - { - get - { - object value; - TryGetValue("pooling", out value); - return SQLiteConvert.ToBoolean(value); - } - set - { - this["pooling"] = value; - } - } - - ///- /// Gets/Sets whethor not to store GUID's in binary format. The default is True - /// which saves space in the database. - /// - [Browsable(true)] - [DefaultValue(true)] - public bool BinaryGUID - { - get - { - object value; - TryGetValue("binaryguid", out value); - return SQLiteConvert.ToBoolean(value); - } - set - { - this["binaryguid"] = value; - } - } - - ///- /// Gets/Sets the filename to open on the connection string. - /// - [DisplayName("Data Source")] - [Browsable(true)] - [DefaultValue("")] - public string DataSource - { - get - { - object value; - TryGetValue("data source", out value); - return value.ToString(); - } - set - { - this["data source"] = value; - } - } - - ///- /// An alternate to the data source property - /// - [Browsable(false)] - public string Uri - { - get - { - object value; - TryGetValue("uri", out value); - return value.ToString(); - } - set - { - this["uri"] = value; - } +/******************************************************** + * 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.Data.Common; + using System.ComponentModel; + using System.Collections; + using System.Globalization; + using System.Reflection; + +#if !PLATFORM_COMPACTFRAMEWORK + ///+ /// SQLite implementation of DbConnectionStringBuilder. + /// + [DefaultProperty("DataSource")] + [DefaultMember("Item")] + public sealed class SQLiteConnectionStringBuilder : DbConnectionStringBuilder + { + ///+ /// Properties of this class + /// + private Hashtable _properties; + + ///+ /// Constructs a new instance of the class + /// + ///+ /// Default constructor + /// + public SQLiteConnectionStringBuilder() + { + Initialize(null); + } + + ///+ /// Constructs a new instance of the class using the specified connection string. + /// + /// The connection string to parse + public SQLiteConnectionStringBuilder(string connectionString) + { + Initialize(connectionString); + } + + ///+ /// Private initializer, which assigns the connection string and resets the builder + /// + /// The connection string to assign + private void Initialize(string cnnString) + { + _properties = new Hashtable(StringComparer.OrdinalIgnoreCase); + try + { + base.GetProperties(_properties); + } + catch(NotImplementedException) + { + FallbackGetProperties(_properties); + } + + if (String.IsNullOrEmpty(cnnString) == false) + ConnectionString = cnnString; + } + + ///+ /// Gets/Sets the default version of the SQLite engine to instantiate. Currently the only valid value is 3, indicating version 3 of the sqlite library. + /// + [Browsable(true)] + [DefaultValue(3)] + public int Version + { + get + { + object value; + TryGetValue("version", out value); + return Convert.ToInt32(value, CultureInfo.CurrentCulture); + } + set + { + if (value != 3) + throw new NotSupportedException(); + + this["version"] = value; + } + } + + ///+ /// Gets/Sets the synchronization mode (file flushing) of the connection string. Default is "Normal". + /// + [DisplayName("Synchronous")] + [Browsable(true)] + [DefaultValue(SynchronizationModes.Normal)] + public SynchronizationModes SyncMode + { + get + { + object value; + TryGetValue("synchronous", out value); + if (value is string) + return (SynchronizationModes)TypeDescriptor.GetConverter(typeof(SynchronizationModes)).ConvertFrom(value); + else return (SynchronizationModes)value; + } + set + { + this["synchronous"] = value; + } + } + + ///+ /// Gets/Sets the encoding for the connection string. The default is "False" which indicates UTF-8 encoding. + /// + [Browsable(true)] + [DefaultValue(false)] + public bool UseUTF16Encoding + { + get + { + object value; + TryGetValue("useutf16encoding", out value); + return SQLiteConvert.ToBoolean(value); + } + set + { + this["useutf16encoding"] = value; + } + } + + ///+ /// Gets/Sets whether or not to use connection pooling. The default is "False" + /// + [Browsable(true)] + [DefaultValue(false)] + public bool Pooling + { + get + { + object value; + TryGetValue("pooling", out value); + return SQLiteConvert.ToBoolean(value); + } + set + { + this["pooling"] = value; + } + } + + ///+ /// Gets/Sets whethor not to store GUID's in binary format. The default is True + /// which saves space in the database. + /// + [Browsable(true)] + [DefaultValue(true)] + public bool BinaryGUID + { + get + { + object value; + TryGetValue("binaryguid", out value); + return SQLiteConvert.ToBoolean(value); + } + set + { + this["binaryguid"] = value; + } + } + + ///+ /// Gets/Sets the filename to open on the connection string. + /// + [DisplayName("Data Source")] + [Browsable(true)] + [DefaultValue("")] + public string DataSource + { + get + { + object value; + TryGetValue("data source", out value); + return value.ToString(); + } + set + { + this["data source"] = value; + } + } + + ///+ /// An alternate to the data source property + /// + [Browsable(false)] + public string Uri + { + get + { + object value; + TryGetValue("uri", out value); + return value.ToString(); + } + set + { + this["uri"] = value; + } } ////// An alternate to the data source property that uses the SQLite URI syntax. /// @@ -221,196 +221,224 @@ } set { this["fulluri"] = value; } - } - - ///- /// Gets/sets the default command timeout for newly-created commands. This is especially useful for - /// commands used internally such as inside a SQLiteTransaction, where setting the timeout is not possible. - /// - [DisplayName("Default Timeout")] - [Browsable(true)] - [DefaultValue(30)] - public int DefaultTimeout - { - get - { - object value; - TryGetValue("default timeout", out value); - return Convert.ToInt32(value, CultureInfo.CurrentCulture); - } - set - { - this["default timeout"] = value; - } - } - - ///- /// Determines whether or not the connection will automatically participate - /// in the current distributed transaction (if one exists) - /// - [Browsable(true)] - [DefaultValue(true)] - public bool Enlist - { - get - { - object value; - TryGetValue("enlist", out value); - return SQLiteConvert.ToBoolean(value); - } - set - { - this["enlist"] = value; - } - } - - ///- /// If set to true, will throw an exception if the database specified in the connection - /// string does not exist. If false, the database will be created automatically. - /// - [Browsable(true)] - [DefaultValue(false)] - public bool FailIfMissing - { - get - { - object value; - TryGetValue("failifmissing", out value); - return SQLiteConvert.ToBoolean(value); - } - set - { - this["failifmissing"] = value; - } - } - - ///- /// If enabled, uses the legacy 3.xx format for maximum compatibility, but results in larger - /// database sizes. - /// - [DisplayName("Legacy Format")] - [Browsable(true)] - [DefaultValue(false)] - public bool LegacyFormat - { - get - { - object value; - TryGetValue("legacy format", out value); - return SQLiteConvert.ToBoolean(value); - } - set - { - this["legacy format"] = value; - } - } - - ///- /// When enabled, the database will be opened for read-only access and writing will be disabled. - /// - [DisplayName("Read Only")] - [Browsable(true)] - [DefaultValue(false)] - public bool ReadOnly - { - get - { - object value; - TryGetValue("read only", out value); - return SQLiteConvert.ToBoolean(value); - } - set - { - this["read only"] = value; - } - } - - ///- /// Gets/sets the database encryption password - /// - [Browsable(true)] - [PasswordPropertyText(true)] - [DefaultValue("")] - public string Password - { - get - { - object value; - TryGetValue("password", out value); - return value.ToString(); - } - set - { - this["password"] = value; - } - } - - ///- /// Gets/Sets the page size for the connection. - /// - [DisplayName("Page Size")] - [Browsable(true)] - [DefaultValue(1024)] - public int PageSize - { - get - { - object value; - TryGetValue("page size", out value); - return Convert.ToInt32(value, CultureInfo.CurrentCulture); - } - set - { - this["page size"] = value; - } - } - - ///- /// Gets/Sets the maximum number of pages the database may hold - /// - [DisplayName("Max Page Count")] - [Browsable(true)] - [DefaultValue(0)] - public int MaxPageCount - { - get - { - object value; - TryGetValue("max page count", out value); - return Convert.ToInt32(value, CultureInfo.CurrentCulture); - } - set - { - this["max page count"] = value; - } - } - - ///- /// Gets/Sets the cache size for the connection. - /// - [DisplayName("Cache Size")] - [Browsable(true)] - [DefaultValue(2000)] - public int CacheSize - { - get - { - object value; - TryGetValue("cache size", out value); - return Convert.ToInt32(value, CultureInfo.CurrentCulture); - } - set - { - this["cache size"] = value; - } - } - - ///- /// Gets/Sets the DateTime format for the connection. + } + + /// Full = 2 } + + ///+ /// Gets/sets the default command timeout for newly-created commands. This is especially useful for + /// commands used internally such as inside a SQLiteTransaction, where setting the timeout is not possible. + /// + [DisplayName("Default Timeout")] + [Browsable(true)] + [DefaultValue(30)] + public int DefaultTimeout + { + get + { + object value; + TryGetValue("default timeout", out value); + return Convert.ToInt32(value, CultureInfo.CurrentCulture); + } + set + { + this["default timeout"] = value; + } + } + + ///+ /// Determines whether or not the connection will automatically participate + /// in the current distributed transaction (if one exists) + /// + [Browsable(true)] + [DefaultValue(true)] + public bool Enlist + { + get + { + object value; + TryGetValue("enlist", out value); + return SQLiteConvert.ToBoolean(value); + } + set + { + this["enlist"] = value; + } + } + + ///+ /// If set to true, will throw an exception if the database specified in the connection + /// string does not exist. If false, the database will be created automatically. + /// + [Browsable(true)] + [DefaultValue(false)] + public bool FailIfMissing + { + get + { + object value; + TryGetValue("failifmissing", out value); + return SQLiteConvert.ToBoolean(value); + } + set + { + this["failifmissing"] = value; + } + } + + ///+ /// If enabled, uses the legacy 3.xx format for maximum compatibility, but results in larger + /// database sizes. + /// + [DisplayName("Legacy Format")] + [Browsable(true)] + [DefaultValue(false)] + public bool LegacyFormat + { + get + { + object value; + TryGetValue("legacy format", out value); + return SQLiteConvert.ToBoolean(value); + } + set + { + this["legacy format"] = value; + } + } + + ///+ /// When enabled, the database will be opened for read-only access and writing will be disabled. + /// + [DisplayName("Read Only")] + [Browsable(true)] + [DefaultValue(false)] + public bool ReadOnly + { + get + { + object value; + TryGetValue("read only", out value); + return SQLiteConvert.ToBoolean(value); + } + set + { + this["read only"] = value; + } + } + + ///+ /// Gets/sets the database encryption password + /// + [Browsable(true)] + [PasswordPropertyText(true)] + [DefaultValue("")] + public string Password + { + get + { + object value; + TryGetValue("password", out value); + return value.ToString(); + } + set + { + this["password"] = value; + } + } + + ///+ /// Gets/sets the database encryption hexadecimal password + /// + [Browsable(true)] + [PasswordPropertyText(true)] + [DefaultValue(null)] + public byte[] HexPassword + { + get + { + object value; + + if (TryGetValue("hexpassword", out value)) + { + if (value is string) + return SQLiteConnection.FromHexString((string)value); + else if (value != null) + return (byte[])value; + } + + return null; + } + set + { + this["hexpassword"] = SQLiteConnection.ToHexString(value); + } + } + + ///+ /// Gets/Sets the page size for the connection. + /// + [DisplayName("Page Size")] + [Browsable(true)] + [DefaultValue(1024)] + public int PageSize + { + get + { + object value; + TryGetValue("page size", out value); + return Convert.ToInt32(value, CultureInfo.CurrentCulture); + } + set + { + this["page size"] = value; + } + } + + ///+ /// Gets/Sets the maximum number of pages the database may hold + /// + [DisplayName("Max Page Count")] + [Browsable(true)] + [DefaultValue(0)] + public int MaxPageCount + { + get + { + object value; + TryGetValue("max page count", out value); + return Convert.ToInt32(value, CultureInfo.CurrentCulture); + } + set + { + this["max page count"] = value; + } + } + + ///+ /// Gets/Sets the cache size for the connection. + /// + [DisplayName("Cache Size")] + [Browsable(true)] + [DefaultValue(2000)] + public int CacheSize + { + get + { + object value; + TryGetValue("cache size", out value); + return Convert.ToInt32(value, CultureInfo.CurrentCulture); + } + set + { + this["cache size"] = value; + } + } + + ///+ /// Gets/Sets the DateTime format for the connection. /// [Browsable(true)] [DefaultValue(SQLiteDateFormats.Default)] public SQLiteDateFormats DateTimeFormat { @@ -487,76 +515,76 @@ } set { this["baseschemaname"] = value; } - } - - ///- /// Determines how SQLite handles the transaction journal file. - /// - [Browsable(true)] - [DefaultValue(SQLiteJournalModeEnum.Default)] - [DisplayName("Journal Mode")] - public SQLiteJournalModeEnum JournalMode - { - get - { - object value; - TryGetValue("journal mode", out value); - if (value is string) - return (SQLiteJournalModeEnum)TypeDescriptor.GetConverter(typeof(SQLiteJournalModeEnum)).ConvertFrom(value); - else - return (SQLiteJournalModeEnum)value; - } - set - { - this["journal mode"] = value; - } - } - - ///- /// Sets the default isolation level for transactions on the connection. - /// - [Browsable(true)] - [DefaultValue(IsolationLevel.Serializable)] - [DisplayName("Default Isolation Level")] - public IsolationLevel DefaultIsolationLevel - { - get - { - object value; - TryGetValue("default isolationlevel", out value); - if (value is string) - return (IsolationLevel)TypeDescriptor.GetConverter(typeof(IsolationLevel)).ConvertFrom(value); - else - return (IsolationLevel)value; - } - set - { - this["default isolationlevel"] = value; - } - } - - ///- /// If enabled, use foreign key constraints - /// - [DisplayName("Foreign Keys")] - [Browsable(true)] - [DefaultValue(false)] - public bool ForeignKeys - { - get - { - object value; - TryGetValue("foreign keys", out value); - return SQLiteConvert.ToBoolean(value); - } - set - { - this["foreign keys"] = value; - } + } + + ///+ /// Determines how SQLite handles the transaction journal file. + /// + [Browsable(true)] + [DefaultValue(SQLiteJournalModeEnum.Default)] + [DisplayName("Journal Mode")] + public SQLiteJournalModeEnum JournalMode + { + get + { + object value; + TryGetValue("journal mode", out value); + if (value is string) + return (SQLiteJournalModeEnum)TypeDescriptor.GetConverter(typeof(SQLiteJournalModeEnum)).ConvertFrom(value); + else + return (SQLiteJournalModeEnum)value; + } + set + { + this["journal mode"] = value; + } + } + + ///+ /// Sets the default isolation level for transactions on the connection. + /// + [Browsable(true)] + [DefaultValue(IsolationLevel.Serializable)] + [DisplayName("Default Isolation Level")] + public IsolationLevel DefaultIsolationLevel + { + get + { + object value; + TryGetValue("default isolationlevel", out value); + if (value is string) + return (IsolationLevel)TypeDescriptor.GetConverter(typeof(IsolationLevel)).ConvertFrom(value); + else + return (IsolationLevel)value; + } + set + { + this["default isolationlevel"] = value; + } + } + + ///+ /// If enabled, use foreign key constraints + /// + [DisplayName("Foreign Keys")] + [Browsable(true)] + [DefaultValue(false)] + public bool ForeignKeys + { + get + { + object value; + TryGetValue("foreign keys", out value); + return SQLiteConvert.ToBoolean(value); + } + set + { + this["foreign keys"] = value; + } } ////// Gets/Sets the extra behavioral flags. /// @@ -623,59 +651,59 @@ set { this["tofullpath"] = value; } } - - ///- /// Helper function for retrieving values from the connectionstring - /// - /// The keyword to retrieve settings for - /// The resulting parameter value - ///Returns true if the value was found and returned - public override bool TryGetValue(string keyword, out object value) - { - bool b = base.TryGetValue(keyword, out value); - - if (!_properties.ContainsKey(keyword)) return b; - - PropertyDescriptor pd = _properties[keyword] as PropertyDescriptor; - - if (pd == null) return b; - - // Attempt to coerce the value into something more solid - if (b) - { - if (pd.PropertyType == typeof(Boolean)) - value = SQLiteConvert.ToBoolean(value); - else - value = TypeDescriptor.GetConverter(pd.PropertyType).ConvertFrom(value); - } - else - { - DefaultValueAttribute att = pd.Attributes[typeof(DefaultValueAttribute)] as DefaultValueAttribute; - if (att != null) - { - value = att.Value; - b = true; - } - } - return b; - } - - ///- /// Fallback method for MONO, which doesn't implement DbConnectionStringBuilder.GetProperties() - /// - /// The hashtable to fill with property descriptors - private void FallbackGetProperties(Hashtable propertyList) - { - foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(this, true)) - { - if (descriptor.Name != "ConnectionString" && propertyList.ContainsKey(descriptor.DisplayName) == false) - { - propertyList.Add(descriptor.DisplayName, descriptor); - } - } - } - } -#endif -} + + ///+ /// Helper function for retrieving values from the connectionstring + /// + /// The keyword to retrieve settings for + /// The resulting parameter value + ///Returns true if the value was found and returned + public override bool TryGetValue(string keyword, out object value) + { + bool b = base.TryGetValue(keyword, out value); + + if (!_properties.ContainsKey(keyword)) return b; + + PropertyDescriptor pd = _properties[keyword] as PropertyDescriptor; + + if (pd == null) return b; + + // Attempt to coerce the value into something more solid + if (b) + { + if (pd.PropertyType == typeof(Boolean)) + value = SQLiteConvert.ToBoolean(value); + else if (pd.PropertyType != typeof(byte[])) + value = TypeDescriptor.GetConverter(pd.PropertyType).ConvertFrom(value); + } + else + { + DefaultValueAttribute att = pd.Attributes[typeof(DefaultValueAttribute)] as DefaultValueAttribute; + if (att != null) + { + value = att.Value; + b = true; + } + } + return b; + } + + ///+ /// Fallback method for MONO, which doesn't implement DbConnectionStringBuilder.GetProperties() + /// + /// The hashtable to fill with property descriptors + private void FallbackGetProperties(Hashtable propertyList) + { + foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(this, true)) + { + if (descriptor.Name != "ConnectionString" && propertyList.ContainsKey(descriptor.DisplayName) == false) + { + propertyList.Add(descriptor.DisplayName, descriptor); + } + } + } + } +#endif +} Index: System.Data.SQLite/SQLiteConvert.cs ================================================================== --- System.Data.SQLite/SQLiteConvert.cs +++ System.Data.SQLite/SQLiteConvert.cs @@ -1,9 +1,9 @@ /******************************************************** * 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 { @@ -160,21 +160,23 @@ /// The pointer to the memory where the UTF-8 string is encoded /// The number of bytes to decode ///A string containing the translated character(s) public static string UTF8ToString(IntPtr nativestring, int nativestringlen) { - if (nativestringlen == 0 || nativestring == IntPtr.Zero) return ""; - if (nativestringlen == -1) + if (nativestring == IntPtr.Zero || nativestringlen == 0) return String.Empty; + if (nativestringlen < 0) { - do - { + nativestringlen = 0; + + while (Marshal.ReadByte(nativestring, nativestringlen) != 0) nativestringlen++; - } while (Marshal.ReadByte(nativestring, nativestringlen) != 0); + + if (nativestringlen == 0) return String.Empty; } byte[] byteArray = new byte[nativestringlen]; - + Marshal.Copy(nativestring, byteArray, 0, nativestringlen); return _utf8.GetString(byteArray, 0, nativestringlen); } @@ -572,18 +574,18 @@ else return SQLiteConvert.DbTypeToType(t.Type); } private static Type[] _affinitytotype = { - typeof(object), - typeof(Int64), - typeof(Double), - typeof(string), - typeof(byte[]), - typeof(object), - typeof(DateTime), - typeof(object) + typeof(object), // Uninitialized (0) + typeof(Int64), // Int64 (1) + typeof(Double), // Double (2) + typeof(string), // Text (3) + typeof(byte[]), // Blob (4) + typeof(DBNull), // Null (5) + typeof(DateTime), // DateTime (10) + typeof(object) // None (11) }; ////// For a given intrinsic type, return a DbType /// @@ -600,29 +602,29 @@ } return _typetodbtype[(int)tc]; } private static DbType[] _typetodbtype = { - DbType.Object, - DbType.Binary, - DbType.Object, - DbType.Boolean, - DbType.SByte, - DbType.SByte, - DbType.Byte, - DbType.Int16, // 7 - DbType.UInt16, - DbType.Int32, - DbType.UInt32, - DbType.Int64, // 11 - DbType.UInt64, - DbType.Single, - DbType.Double, - DbType.Decimal, - DbType.DateTime, - DbType.Object, - DbType.String, + DbType.Object, // Empty (0) + DbType.Binary, // Object (1) + DbType.Object, // DBNull (2) + DbType.Boolean, // Boolean (3) + DbType.SByte, // Char (4) + DbType.SByte, // SByte (5) + DbType.Byte, // Byte (6) + DbType.Int16, // Int16 (7) + DbType.UInt16, // UInt16 (8) + DbType.Int32, // Int32 (9) + DbType.UInt32, // UInt32 (10) + DbType.Int64, // Int64 (11) + DbType.UInt64, // UInt64 (12) + DbType.Single, // Single (13) + DbType.Double, // Double (14) + DbType.Decimal, // Decimal (15) + DbType.DateTime, // DateTime (16) + DbType.Object, // ?? (17) + DbType.String // String (18) }; ////// Returns the ColumnSize for the given DbType /// @@ -632,102 +634,104 @@ { return _dbtypetocolumnsize[(int)typ]; } private static int[] _dbtypetocolumnsize = { - 2147483647, // 0 - 2147483647, // 1 - 1, // 2 - 1, // 3 - 8, // 4 - 8, // 5 - 8, // 6 - 8, // 7 - 8, // 8 - 16, // 9 - 2, - 4, - 8, - 2147483647, - 1, - 4, - 2147483647, - 8, - 2, - 4, - 8, - 8, - 2147483647, - 2147483647, - 2147483647, - 2147483647, // 25 (Xml) + int.MaxValue, // AnsiString (0) + int.MaxValue, // Binary (1) + 1, // Byte (2) + 1, // Boolean (3) + 8, // Currency (4) + 8, // Date (5) + 8, // DateTime (6) + 8, // Decimal (7) + 8, // Double (8) + 16, // Guid (9) + 2, // Int16 (10) + 4, // Int32 (11) + 8, // Int64 (12) + int.MaxValue, // Object (13) + 1, // SByte (14) + 4, // Single (15) + int.MaxValue, // String (16) + 8, // Time (17) + 2, // UInt16 (18) + 4, // UInt32 (19) + 8, // UInt64 (20) + 8, // VarNumeric (21) + int.MaxValue, // AnsiStringFixedLength (22) + int.MaxValue, // StringFixedLength (23) + int.MaxValue, // ?? (24) + int.MaxValue // Xml (25) }; internal static object DbTypeToNumericPrecision(DbType typ) { return _dbtypetonumericprecision[(int)typ]; } private static object[] _dbtypetonumericprecision = { - DBNull.Value, // 0 - DBNull.Value, // 1 - 3, - DBNull.Value, - 19, - DBNull.Value, // 5 - DBNull.Value, // 6 - 53, - 53, - DBNull.Value, - 5, - 10, - 19, - DBNull.Value, - 3, - 24, - DBNull.Value, - DBNull.Value, - 5, - 10, - 19, - 53, - DBNull.Value, - DBNull.Value, - DBNull.Value + DBNull.Value, // AnsiString (0) + DBNull.Value, // Binary (1) + 3, // Byte (2) + DBNull.Value, // Boolean (3) + 19, // Currency (4) + DBNull.Value, // Date (5) + DBNull.Value, // DateTime (6) + 53, // Decimal (7) + 53, // Double (8) + DBNull.Value, // Guid (9) + 5, // Int16 (10) + 10, // Int32 (11) + 19, // Int64 (12) + DBNull.Value, // Object (13) + 3, // SByte (14) + 24, // Single (15) + DBNull.Value, // String (16) + DBNull.Value, // Time (17) + 5, // UInt16 (18) + 10, // UInt32 (19) + 19, // UInt64 (20) + 53, // VarNumeric (21) + DBNull.Value, // AnsiStringFixedLength (22) + DBNull.Value, // StringFixedLength (23) + DBNull.Value, // ?? (24) + DBNull.Value // Xml (25) }; internal static object DbTypeToNumericScale(DbType typ) { return _dbtypetonumericscale[(int)typ]; } private static object[] _dbtypetonumericscale = { - DBNull.Value, // 0 - DBNull.Value, // 1 - 0, - DBNull.Value, - 4, - DBNull.Value, // 5 - DBNull.Value, // 6 - DBNull.Value, - DBNull.Value, - DBNull.Value, - 0, - 0, - 0, - DBNull.Value, - 0, - DBNull.Value, - DBNull.Value, - DBNull.Value, - 0, - 0, - 0, - 0, - DBNull.Value, - DBNull.Value, - DBNull.Value + DBNull.Value, // AnsiString (0) + DBNull.Value, // Binary (1) + 0, // Byte (2) + DBNull.Value, // Boolean (3) + 4, // Currency (4) + DBNull.Value, // Date (5) + DBNull.Value, // DateTime (6) + DBNull.Value, // Decimal (7) + DBNull.Value, // Double (8) + DBNull.Value, // Guid (9) + 0, // Int16 (10) + 0, // Int32 (11) + 0, // Int64 (12) + DBNull.Value, // Object (13) + 0, // SByte (14) + DBNull.Value, // Single (15) + DBNull.Value, // String (16) + DBNull.Value, // Time (17) + 0, // UInt16 (18) + 0, // UInt32 (19) + 0, // UInt64 (20) + 0, // VarNumeric (21) + DBNull.Value, // AnsiStringFixedLength (22) + DBNull.Value, // StringFixedLength (23) + DBNull.Value, // ?? (24) + DBNull.Value // Xml (25) }; internal static string DbTypeToTypeName(DbType typ) { for (int n = 0; n < _dbtypeNames.Length; n++) @@ -745,27 +749,12 @@ #endif return defaultTypeName; } - private static SQLiteTypeNames[] _dbtypeNames = { - new SQLiteTypeNames("INTEGER", DbType.Int64), - new SQLiteTypeNames("TINYINT", DbType.Byte), - new SQLiteTypeNames("INT", DbType.Int32), - new SQLiteTypeNames("VARCHAR", DbType.AnsiString), - new SQLiteTypeNames("NVARCHAR", DbType.String), - new SQLiteTypeNames("CHAR", DbType.AnsiStringFixedLength), - new SQLiteTypeNames("NCHAR", DbType.StringFixedLength), - new SQLiteTypeNames("FLOAT", DbType.Double), - new SQLiteTypeNames("REAL", DbType.Double), - new SQLiteTypeNames("BIT", DbType.Boolean), - new SQLiteTypeNames("DECIMAL", DbType.Decimal), - new SQLiteTypeNames("DATETIME", DbType.DateTime), - new SQLiteTypeNames("BLOB", DbType.Binary), - new SQLiteTypeNames("UNIQUEIDENTIFIER", DbType.Guid), - new SQLiteTypeNames("SMALLINT", DbType.Int16), - }; + private static SQLiteTypeNames[] _dbtypeNames = GetSQLiteTypeNames(); + ////// Convert a DbType to a Type /// /// The DbType to convert from ///The closest-match .NET type @@ -773,36 +762,36 @@ { return _dbtypeToType[(int)typ]; } private static Type[] _dbtypeToType = { - typeof(string), // 0 - typeof(byte[]), // 1 - typeof(byte), // 2 - typeof(bool), // 3 - typeof(decimal), // 4 - typeof(DateTime), // 5 - typeof(DateTime), // 6 - typeof(decimal), // 7 - typeof(double), // 8 - typeof(Guid), // 9 - typeof(Int16), - typeof(Int32), - typeof(Int64), - typeof(object), - typeof(sbyte), - typeof(float), - typeof(string), - typeof(DateTime), - typeof(UInt16), - typeof(UInt32), - typeof(UInt64), - typeof(double), - typeof(string), - typeof(string), - typeof(string), - typeof(string), // 25 (Xml) + typeof(string), // AnsiString (0) + typeof(byte[]), // Binary (1) + typeof(byte), // Byte (2) + typeof(bool), // Boolean (3) + typeof(decimal), // Currency (4) + typeof(DateTime), // Date (5) + typeof(DateTime), // DateTime (6) + typeof(decimal), // Decimal (7) + typeof(double), // Double (8) + typeof(Guid), // Guid (9) + typeof(Int16), // Int16 (10) + typeof(Int32), // Int32 (11) + typeof(Int64), // Int64 (12) + typeof(object), // Object (13) + typeof(sbyte), // SByte (14) + typeof(float), // Single (15) + typeof(string), // String (16) + typeof(DateTime), // Time (17) + typeof(UInt16), // UInt16 (18) + typeof(UInt32), // UInt32 (19) + typeof(UInt64), // UInt64 (20) + typeof(double), // VarNumeric (21) + typeof(string), // AnsiStringFixedLength (22) + typeof(string), // StringFixedLength (23) + typeof(string), // ?? (24) + typeof(string), // Xml (25) }; ////// For a given type, return the closest-match SQLite TypeAffinity, which only understands a very limited subset of types. /// @@ -820,30 +809,111 @@ } return _typecodeAffinities[(int)tc]; } private static TypeAffinity[] _typecodeAffinities = { - TypeAffinity.Null, - TypeAffinity.Blob, - TypeAffinity.Null, - TypeAffinity.Int64, - TypeAffinity.Int64, - TypeAffinity.Int64, - TypeAffinity.Int64, - TypeAffinity.Int64, // 7 - TypeAffinity.Int64, - TypeAffinity.Int64, - TypeAffinity.Int64, - TypeAffinity.Int64, // 11 - TypeAffinity.Int64, - TypeAffinity.Double, - TypeAffinity.Double, - TypeAffinity.Double, - TypeAffinity.DateTime, - TypeAffinity.Null, - TypeAffinity.Text, + TypeAffinity.Null, // Empty (0) + TypeAffinity.Blob, // Object (1) + TypeAffinity.Null, // DBNull (2) + TypeAffinity.Int64, // Boolean (3) + TypeAffinity.Int64, // Char (4) + TypeAffinity.Int64, // SByte (5) + TypeAffinity.Int64, // Byte (6) + TypeAffinity.Int64, // Int16 (7) + TypeAffinity.Int64, // UInt16 (8) + TypeAffinity.Int64, // Int32 (9) + TypeAffinity.Int64, // UInt32 (10) + TypeAffinity.Int64, // Int64 (11) + TypeAffinity.Int64, // UInt64 (12) + TypeAffinity.Double, // Single (13) + TypeAffinity.Double, // Double (14) + TypeAffinity.Double, // Decimal (15) + TypeAffinity.DateTime, // DateTime (16) + TypeAffinity.Null, // ?? (17) + TypeAffinity.Text // String (18) }; + + ///+ /// Builds and returns an array containing the database column types + /// recognized by this provider. + /// + ///+ /// An array containing the database column types recognized by this + /// provider. + /// + private static SQLiteTypeNames[] GetSQLiteTypeNames() + { + return new SQLiteTypeNames[] { + new SQLiteTypeNames("BIGINT", DbType.Int64), + new SQLiteTypeNames("BIGUINT", DbType.UInt64), + new SQLiteTypeNames("BINARY", DbType.Binary), + new SQLiteTypeNames("BIT", DbType.Boolean), + new SQLiteTypeNames("BLOB", DbType.Binary), + new SQLiteTypeNames("BOOL", DbType.Boolean), + new SQLiteTypeNames("BOOLEAN", DbType.Boolean), + new SQLiteTypeNames("CHAR", DbType.AnsiStringFixedLength), + new SQLiteTypeNames("COUNTER", DbType.Int64), + new SQLiteTypeNames("CURRENCY", DbType.Decimal), + new SQLiteTypeNames("DATE", DbType.DateTime), + new SQLiteTypeNames("DATETIME", DbType.DateTime), + new SQLiteTypeNames("DECIMAL", DbType.Decimal), + new SQLiteTypeNames("DOUBLE", DbType.Double), + new SQLiteTypeNames("FLOAT", DbType.Double), + new SQLiteTypeNames("GENERAL", DbType.Binary), + new SQLiteTypeNames("GUID", DbType.Guid), + new SQLiteTypeNames("IDENTITY", DbType.Int64), + new SQLiteTypeNames("IMAGE", DbType.Binary), + new SQLiteTypeNames("INT", DbType.Int32), + new SQLiteTypeNames("INT8", DbType.SByte), + new SQLiteTypeNames("INT16", DbType.Int16), + new SQLiteTypeNames("INT32", DbType.Int32), + new SQLiteTypeNames("INT64", DbType.Int64), + new SQLiteTypeNames("INTEGER", DbType.Int64), + new SQLiteTypeNames("INTEGER8", DbType.SByte), + new SQLiteTypeNames("INTEGER16", DbType.Int16), + new SQLiteTypeNames("INTEGER32", DbType.Int32), + new SQLiteTypeNames("INTEGER64", DbType.Int64), + new SQLiteTypeNames("LOGICAL", DbType.Boolean), + new SQLiteTypeNames("LONG", DbType.Int64), + new SQLiteTypeNames("LONGCHAR", DbType.String), + new SQLiteTypeNames("LONGTEXT", DbType.String), + new SQLiteTypeNames("LONGVARCHAR", DbType.String), + new SQLiteTypeNames("MEMO", DbType.String), + new SQLiteTypeNames("MONEY", DbType.Decimal), + new SQLiteTypeNames("NCHAR", DbType.StringFixedLength), + new SQLiteTypeNames("NOTE", DbType.String), + new SQLiteTypeNames("NTEXT", DbType.String), + new SQLiteTypeNames("NUMERIC", DbType.Decimal), + new SQLiteTypeNames("NVARCHAR", DbType.String), + new SQLiteTypeNames("OLEOBJECT", DbType.Binary), + new SQLiteTypeNames("REAL", DbType.Double), + new SQLiteTypeNames("SMALLDATE", DbType.DateTime), + new SQLiteTypeNames("SMALLINT", DbType.Int16), + new SQLiteTypeNames("SMALLUINT", DbType.UInt16), + new SQLiteTypeNames("STRING", DbType.String), + new SQLiteTypeNames("TEXT", DbType.String), + new SQLiteTypeNames("TIME", DbType.DateTime), + new SQLiteTypeNames("TIMESTAMP", DbType.DateTime), + new SQLiteTypeNames("TINYINT", DbType.Byte), + new SQLiteTypeNames("TINYSINT", DbType.SByte), + new SQLiteTypeNames("UINT", DbType.UInt32), + new SQLiteTypeNames("UINT8", DbType.Byte), + new SQLiteTypeNames("UINT16", DbType.UInt16), + new SQLiteTypeNames("UINT32", DbType.UInt32), + new SQLiteTypeNames("UINT64", DbType.UInt64), + new SQLiteTypeNames("ULONG", DbType.UInt64), + new SQLiteTypeNames("UNIQUEIDENTIFIER", DbType.Guid), + new SQLiteTypeNames("UNSIGNEDINTEGER", DbType.UInt64), + new SQLiteTypeNames("UNSIGNEDINTEGER8", DbType.Byte), + new SQLiteTypeNames("UNSIGNEDINTEGER16", DbType.UInt16), + new SQLiteTypeNames("UNSIGNEDINTEGER32", DbType.UInt32), + new SQLiteTypeNames("UNSIGNEDINTEGER64", DbType.UInt64), + new SQLiteTypeNames("VARBINARY", DbType.Binary), + new SQLiteTypeNames("VARCHAR", DbType.AnsiString), + new SQLiteTypeNames("YESNO", DbType.Boolean) + }; + } ////// For a given type name, return a closest-match .NET type /// /// The name of the type to match @@ -855,61 +925,12 @@ if (_typeNames == null) { _typeNames = new Dictionary( new TypeNameStringComparer()); - foreach (SQLiteTypeNames typeName in new SQLiteTypeNames[] { - new SQLiteTypeNames("COUNTER", DbType.Int64), - new SQLiteTypeNames("AUTOINCREMENT", DbType.Int64), - new SQLiteTypeNames("IDENTITY", DbType.Int64), - new SQLiteTypeNames("LONGTEXT", DbType.String), - new SQLiteTypeNames("LONGCHAR", DbType.String), - new SQLiteTypeNames("LONGVARCHAR", DbType.String), - new SQLiteTypeNames("LONG", DbType.Int64), - new SQLiteTypeNames("TINYINT", DbType.Byte), - new SQLiteTypeNames("INTEGER", DbType.Int64), - new SQLiteTypeNames("INT", DbType.Int32), - new SQLiteTypeNames("VARCHAR", DbType.String), - new SQLiteTypeNames("NVARCHAR", DbType.String), - new SQLiteTypeNames("CHAR", DbType.String), - new SQLiteTypeNames("NCHAR", DbType.String), - new SQLiteTypeNames("TEXT", DbType.String), - new SQLiteTypeNames("NTEXT", DbType.String), - new SQLiteTypeNames("STRING", DbType.String), - new SQLiteTypeNames("DOUBLE", DbType.Double), - new SQLiteTypeNames("FLOAT", DbType.Double), - new SQLiteTypeNames("REAL", DbType.Double), - new SQLiteTypeNames("BIT", DbType.Boolean), - new SQLiteTypeNames("YESNO", DbType.Boolean), - new SQLiteTypeNames("LOGICAL", DbType.Boolean), - new SQLiteTypeNames("BOOL", DbType.Boolean), - new SQLiteTypeNames("BOOLEAN", DbType.Boolean), - new SQLiteTypeNames("NUMERIC", DbType.Decimal), - new SQLiteTypeNames("DECIMAL", DbType.Decimal), - new SQLiteTypeNames("MONEY", DbType.Decimal), - new SQLiteTypeNames("CURRENCY", DbType.Decimal), - new SQLiteTypeNames("TIME", DbType.DateTime), - new SQLiteTypeNames("DATE", DbType.DateTime), - new SQLiteTypeNames("DATETIME", DbType.DateTime), - new SQLiteTypeNames("SMALLDATE", DbType.DateTime), - new SQLiteTypeNames("TIMESTAMP", DbType.DateTime), - new SQLiteTypeNames("BLOB", DbType.Binary), - new SQLiteTypeNames("BINARY", DbType.Binary), - new SQLiteTypeNames("VARBINARY", DbType.Binary), - new SQLiteTypeNames("IMAGE", DbType.Binary), - new SQLiteTypeNames("GENERAL", DbType.Binary), - new SQLiteTypeNames("OLEOBJECT", DbType.Binary), - new SQLiteTypeNames("GUID", DbType.Guid), - new SQLiteTypeNames("UNIQUEIDENTIFIER", DbType.Guid), - new SQLiteTypeNames("MEMO", DbType.String), - new SQLiteTypeNames("NOTE", DbType.String), - new SQLiteTypeNames("SMALLINT", DbType.Int16), - new SQLiteTypeNames("BIGINT", DbType.Int64) - }) - { + foreach (SQLiteTypeNames typeName in GetSQLiteTypeNames()) _typeNames.Add(typeName.typeName, typeName); - } } } if (String.IsNullOrEmpty(Name)) return DbType.Object; @@ -1058,12 +1079,12 @@ /// /// ISO8601 is more compatible, readable, fully-processable, but less accurate as it doesn't provide time down to fractions of a second. /// JulianDay is the numeric format the SQLite uses internally and is arguably the most compatible with 3rd party tools. It is /// not readable as text without post-processing. /// Ticks less compatible with 3rd party tools that query the database, and renders the DateTime field unreadable as text without post-processing. - /// - /// The preferred order of choosing a datetime format is JulianDay, ISO8601, and then Ticks. Ticks is mainly present for legacy + /// + /// The preferred order of choosing a datetime format is JulianDay, ISO8601, and then Ticks. Ticks is mainly present for legacy /// code support. /// public enum SQLiteDateFormats { ///@@ -1102,11 +1123,11 @@ /// ////// By default SQLite will create and delete the journal file when needed during a transaction. /// However, for some computers running certain filesystem monitoring tools, the rapid /// creation and deletion of the journal file can cause those programs to fail, or to interfere with SQLite. - /// + /// /// If a program or virus scanner is interfering with SQLite's journal file, you may receive errors like "unable to open database file" /// when starting a transaction. If this is happening, you may want to change the default journal mode to Persist. /// public enum SQLiteJournalModeEnum { @@ -1182,10 +1203,48 @@ /// corrupt the database. FULL synchronous is very safe, but it is also /// slower. ///+ /// The requested command execution type. This controls which method of the + /// + public enum SQLiteExecuteType + { + ///object will be called. + /// + /// Do nothing. No method will be called. + /// + None = 0, + + ///+ /// The command is not expected to return a result -OR- the result is not + /// needed. The + NonQuery = 1, + + ///method will + /// be called. + /// + /// The command is expected to return a scalar result -OR- the result should + /// be limited to a scalar result. The + Scalar = 2, + + ///+ /// method will be called. + /// + /// The command is expected to return + Reader = 3, + + ///result. + /// The method will be called. + /// + /// Use the default command execution type. Using this value is the same + /// as using the + Default = NonQuery /* TODO: Good default? */ + } ///value. + /// /// Struct used internally to determine the datatype of a column in a resultset /// internal class SQLiteType @@ -1229,12 +1288,12 @@ string value ) { // // NOTE: The only thing that we must guarantee here, according - // to the MSDN documentation for IEqualityComparer, is - // that for two given strings, if Equals return true then + // to the MSDN documentation for IEqualityComparer, is + // that for two given strings, if Equals return true then // the two strings must hash to the same value. // if (value != null) #if !PLATFORM_COMPACTFRAMEWORK return value.ToLowerInvariant().GetHashCode(); Index: System.Data.SQLite/SQLiteDataAdapter.cs ================================================================== --- System.Data.SQLite/SQLiteDataAdapter.cs +++ System.Data.SQLite/SQLiteDataAdapter.cs @@ -22,49 +22,105 @@ public sealed class SQLiteDataAdapter : DbDataAdapter { private static object _updatingEventPH = new object(); private static object _updatedEventPH = new object(); + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Public Constructors ///- /// This class is just a shell around the DbDataAdapter. Nothing from DbDataAdapter is overridden here, just a few constructors are defined. + /// This class is just a shell around the DbDataAdapter. Nothing from + /// DbDataAdapter is overridden here, just a few constructors are defined. /// ////// Default constructor. /// public SQLiteDataAdapter() { } + /////////////////////////////////////////////////////////////////////////////////////////////// + ////// Constructs a data adapter using the specified select command. /// - /// The select command to associate with the adapter. + /// + /// The select command to associate with the adapter. + /// public SQLiteDataAdapter(SQLiteCommand cmd) { SelectCommand = cmd; } + /////////////////////////////////////////////////////////////////////////////////////////////// + ///- /// Constructs a data adapter with the supplied select command text and associated with the specified connection. + /// Constructs a data adapter with the supplied select command text and + /// associated with the specified connection. /// - /// The select command text to associate with the data adapter. - /// The connection to associate with the select command. + /// + /// The select command text to associate with the data adapter. + /// + /// + /// The connection to associate with the select command. + /// public SQLiteDataAdapter(string commandText, SQLiteConnection connection) { SelectCommand = new SQLiteCommand(commandText, connection); } + /////////////////////////////////////////////////////////////////////////////////////////////// + + ///+ /// Constructs a data adapter with the specified select command text, + /// and using the specified database connection string. + /// + /// + /// The select command text to use to construct a select command. + /// + /// + /// A connection string suitable for passing to a new SQLiteConnection, + /// which is associated with the select command. + /// + public SQLiteDataAdapter( + string commandText, + string connectionString + ) + : this(commandText, connectionString, false) + { + // do nothing. + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + ///- /// Constructs a data adapter with the specified select command text, and using the specified database connection string. + /// Constructs a data adapter with the specified select command text, + /// and using the specified database connection string. /// - /// The select command text to use to construct a select command. - /// A connection string suitable for passing to a new SQLiteConnection, which is associated with the select command. - public SQLiteDataAdapter(string commandText, string connectionString) + /// + /// The select command text to use to construct a select command. + /// + /// + /// A connection string suitable for passing to a new SQLiteConnection, + /// which is associated with the select command. + /// + /// + /// Non-zero to parse the connection string using the built-in (i.e. + /// framework provided) parser when opening the connection. + /// + public SQLiteDataAdapter( + string commandText, + string connectionString, + bool parseViaFramework + ) { - SQLiteConnection cnn = new SQLiteConnection(connectionString); + SQLiteConnection cnn = new SQLiteConnection( + connectionString, parseViaFramework); + SelectCommand = new SQLiteCommand(commandText, cnn); } + #endregion /////////////////////////////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; Index: System.Data.SQLite/SQLiteDataReader.cs ================================================================== --- System.Data.SQLite/SQLiteDataReader.cs +++ System.Data.SQLite/SQLiteDataReader.cs @@ -644,11 +644,11 @@ ///The int i of the column public override int GetOrdinal(string name) { CheckDisposed(); CheckClosed(); - SQLiteCommand.Check(_command); + if (_throwOnDisposed) SQLiteCommand.Check(_command); // // NOTE: First, check if the column name cache has been initialized yet. // If not, do it now. // @@ -724,11 +724,11 @@ } #endregion /////////////////////////////////////////////////////////////////////////// - #region ColumnParent Class + #region ColumnParent Class private sealed class ColumnParent : IEqualityComparer{ #region Public Fields public string DatabaseName; public string TableName; @@ -852,11 +852,11 @@ } internal DataTable GetSchemaTable(bool wantUniqueInfo, bool wantDefaultValue) { CheckClosed(); - SQLiteCommand.Check(_command); + if (_throwOnDisposed) SQLiteCommand.Check(_command); // // BUGFIX: We need to quickly scan all the fields in the current // "result set" to see how many distinct tables are actually // involved. This information is necessary so that some @@ -1115,17 +1115,19 @@ /// The index of the column to retrieve /// object public override object GetValue(int i) { CheckDisposed(); + CheckValidRow(); if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetValue(i - VisibleFieldCount); SQLiteType typ = GetSQLiteType(i); - return _activeStatement._sql.GetValue(_activeStatement, i, typ); + return _activeStatement._sql.GetValue( + _activeStatement, SQLiteCommand.GetFlags(_command), i, typ); } ////// Retreives the values of multiple columns, up to the size of the supplied array /// @@ -1219,11 +1221,11 @@ ///True if the command was successful and a new resultset is available, False otherwise. public override bool NextResult() { CheckDisposed(); CheckClosed(); - SQLiteCommand.Check(_command); + if (_throwOnDisposed) SQLiteCommand.Check(_command); SQLiteStatement stmt = null; int fieldCount; while (true) @@ -1244,15 +1246,15 @@ _activeStatementIndex++; stmt._sql.Step(stmt); if (stmt._sql.ColumnCount(stmt) == 0) { - if (_rowsAffected == -1) _rowsAffected = 0; - int changes = 0; - if (stmt.TryGetChanges(ref changes)) - _rowsAffected += changes; - else + if (_rowsAffected == -1) _rowsAffected = 0; + int changes = 0; + if (stmt.TryGetChanges(ref changes)) + _rowsAffected += changes; + else return false; } stmt._sql.Reset(stmt); // Gotta reset after every step to release any locks and such! } return false; @@ -1281,15 +1283,15 @@ { _readingState = -1; } else if (fieldCount == 0) // No rows returned, if fieldCount is zero, skip to the next statement { - if (_rowsAffected == -1) _rowsAffected = 0; - int changes = 0; - if (stmt.TryGetChanges(ref changes)) - _rowsAffected += changes; - else + if (_rowsAffected == -1) _rowsAffected = 0; + int changes = 0; + if (stmt.TryGetChanges(ref changes)) + _rowsAffected += changes; + else return false; stmt._sql.Reset(stmt); continue; // Skip this command and move to the next, it was not a row-returning resultset } else // No rows, fieldCount is non-zero so stop here @@ -1345,11 +1347,11 @@ ///True if a new row was successfully loaded and is ready for processing public override bool Read() { CheckDisposed(); CheckClosed(); - SQLiteCommand.Check(_command); + if (_throwOnDisposed) SQLiteCommand.Check(_command); if (_readingState == -1) // First step was already done at the NextResult() level, so don't step again, just return true. { _readingState = 0; return true; Index: System.Data.SQLite/SQLiteDefineConstants.cs ================================================================== --- System.Data.SQLite/SQLiteDefineConstants.cs +++ System.Data.SQLite/SQLiteDefineConstants.cs @@ -33,10 +33,14 @@ #endif #if INTEROP_EXTENSION_FUNCTIONS "INTEROP_EXTENSION_FUNCTIONS", #endif + +#if INTEROP_LEGACY_CLOSE + "INTEROP_LEGACY_CLOSE", +#endif #if INTEROP_LOG "INTEROP_LOG", #endif @@ -109,10 +113,18 @@ #endif #if USE_INTEROP_DLL "USE_INTEROP_DLL", #endif + +#if USE_PREPARE_V2 + "USE_PREPARE_V2", +#endif + +#if WINDOWS + "WINDOWS", +#endif null }); } } 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 @@ ///- /// The base connection this function is attached to - /// - internal SQLiteBase _base; - - ///- /// Internal array used to keep track of aggregate function context data + + /// private static object syncRoot = new object(); + /////////////////////////////////////////////////////////////////////// + +#if !PLATFORM_COMPACTFRAMEWORK ///+ /// 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,211 +181,210 @@ } #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; + } + + /// + /// 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 { _context = context; SetReturnValue(context, Invoke(ConvertParams(nArgs, argsptr))); /* throw */ } -#if !PLATFORM_COMPACTFRAMEWORK catch (Exception e) /* NOTE: Must catch ALL. */ { try { if ((_flags & SQLiteConnectionFlags.LogCallbackException) == @@ -400,37 +399,30 @@ catch { // do nothing. } } -#else - catch /* NOTE: Must catch ALL. */ - { - // do nothing (Windows CE). - } -#endif - } - - ///+ /// 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. + ///+ } + + /// + /// 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 + ////// 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. + /// 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 { return Compare(SQLiteConvert.UTF8ToString(ptr1, len1), SQLiteConvert.UTF8ToString(ptr2, len2)); /* throw */ } -#if !PLATFORM_COMPACTFRAMEWORK catch (Exception e) /* NOTE: Must catch ALL. */ { try { if ((_flags & SQLiteConnectionFlags.LogCallbackException) == @@ -445,16 +437,10 @@ catch { // do nothing. } } -#else - catch /* NOTE: Must catch ALL. */ - { - // do nothing (Windows CE). - } -#endif // // NOTE: This must be done to prevent the core SQLite library from // using our (invalid) result. // @@ -480,11 +466,10 @@ try { return Compare(SQLite3_UTF16.UTF16ToString(ptr1, len1), SQLite3_UTF16.UTF16ToString(ptr2, len2)); /* throw */ } -#if !PLATFORM_COMPACTFRAMEWORK catch (Exception e) /* NOTE: Must catch ALL. */ { try { if ((_flags & SQLiteConnectionFlags.LogCallbackException) == @@ -499,38 +484,32 @@ catch { // do nothing. } } -#else - catch /* NOTE: Must catch ALL. */ - { - // do nothing (Windows CE). - } -#endif // // NOTE: This must be done to prevent the core SQLite library from // using our (invalid) result. // 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 { @@ -560,11 +539,10 @@ finally { data._count++; } } -#if !PLATFORM_COMPACTFRAMEWORK catch (Exception e) /* NOTE: Must catch ALL. */ { try { if ((_flags & SQLiteConnectionFlags.LogCallbackException) == @@ -579,22 +557,16 @@ catch { // do nothing. } } -#else - catch /* NOTE: Must catch ALL. */ - { - // do nothing (Windows CE). - } -#endif - } - - ///+ } + + /// /// A raw context pointer internal void FinalCallback(IntPtr context) { try { @@ -622,11 +594,10 @@ { IDisposable disp = obj as IDisposable; if (disp != null) disp.Dispose(); /* throw */ } } -#if !PLATFORM_COMPACTFRAMEWORK catch (Exception e) /* NOTE: Must catch ALL. */ { try { if ((_flags & SQLiteConnectionFlags.LogCallbackException) == @@ -641,172 +612,173 @@ catch { // do nothing. } } -#else - 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. + ///- /// 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. - /// - /// 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; - ListlFunctions = new List (); - - foreach (SQLiteFunctionAttribute pr in _registeredFunctions) - { + } + + /// + /// 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; + ListlFunctions = 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 +816,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/SQLiteLog.cs ================================================================== --- System.Data.SQLite/SQLiteLog.cs +++ System.Data.SQLite/SQLiteLog.cs @@ -64,11 +64,10 @@ /// Event arguments of the trace public delegate void SQLiteLogEventHandler(object sender, LogEventArgs e); /////////////////////////////////////////////////////////////////////////// -#if !PLATFORM_COMPACTFRAMEWORK ////// Manages the SQLite custom logging functionality and the associated /// callback for the whole process. /// public static class SQLiteLog @@ -77,40 +76,57 @@ /// Object used to synchronize access to the static instance data /// for this class. ////// Member variable to store the AppDomain.DomainUnload event handler. /// private static EventHandler _domainUnload; +#endif + + /////////////////////////////////////////////////////////////////////// ////// Member variable to store the application log handler to call. /// private static event SQLiteLogEventHandler _handlers; + + /////////////////////////////////////////////////////////////////////// + ////// The default log event handler. /// private static SQLiteLogEventHandler _defaultHandler; + + /////////////////////////////////////////////////////////////////////// ////// The log callback passed to native SQLite engine. This must live /// as long as the SQLite library has a pointer to it. /// private static SQLiteLogCallback _callback; + /////////////////////////////////////////////////////////////////////// + ////// The base SQLite object to interop with. /// private static SQLiteBase _sql; + /////////////////////////////////////////////////////////////////////// + ////// This will be non-zero if logging is currently enabled. /// private static bool _enabled; + + /////////////////////////////////////////////////////////////////////// ////// Initializes the SQLite logging facilities. /// public static void Initialize() @@ -121,10 +137,11 @@ // the process (see ticket [2ce0870fad]). // if (SQLite3.StaticIsInitialized()) return; +#if !PLATFORM_COMPACTFRAMEWORK // // BUGFIX: To avoid nasty situations where multiple AppDomains are // attempting to initialize and/or shutdown what is really // a shared native resource (i.e. the SQLite core library // is loaded per-process and has only one logging callback, @@ -136,13 +153,15 @@ if (!AppDomain.CurrentDomain.IsDefaultAppDomain() && Environment.GetEnvironmentVariable("Force_SQLiteLog") == null) { return; } +#endif lock (syncRoot) { +#if !PLATFORM_COMPACTFRAMEWORK // // NOTE: Add an event handler for the DomainUnload event so // that we can unhook our logging managed function // pointer from the native SQLite code prior to it // being invalidated. @@ -153,10 +172,11 @@ if (_domainUnload == null) { _domainUnload = new EventHandler(DomainUnload); AppDomain.CurrentDomain.DomainUnload += _domainUnload; } +#endif // // NOTE: Create an instance of the SQLite wrapper class. // if (_sql == null) @@ -190,10 +210,13 @@ // AddDefaultHandler(); } } + /////////////////////////////////////////////////////////////////////// + +#if !PLATFORM_COMPACTFRAMEWORK ////// Handles the AppDomain being unloaded. /// /// Should be null. /// The data associated with this event. @@ -256,10 +279,13 @@ AppDomain.CurrentDomain.DomainUnload -= _domainUnload; _domainUnload = null; } } } +#endif + + /////////////////////////////////////////////////////////////////////// ////// This event is raised whenever SQLite raises a logging event. /// Note that this should be set as one of the first things in the /// application. @@ -286,19 +312,23 @@ _handlers -= value; } } } + /////////////////////////////////////////////////////////////////////// + /// ////// If this property is true, logging is enabled; otherwise, logging is /// disabled. When logging is disabled, no logging events will fire. /// public static bool Enabled { get { lock (syncRoot) { return _enabled; } } set { lock (syncRoot) { _enabled = value; } } } + + /////////////////////////////////////////////////////////////////////// ////// Log a message to all the registered log event handlers without going /// through the SQLite library. /// @@ -307,10 +337,12 @@ string message ) { LogMessage(null, message); } + + /////////////////////////////////////////////////////////////////////// ////// Log a message to all the registered log event handlers without going /// through the SQLite library. /// @@ -322,10 +354,12 @@ ) { LogMessage((object)errorCode, message); } + /////////////////////////////////////////////////////////////////////// + ////// Log a message to all the registered log event handlers without going /// through the SQLite library. /// /// The integer error code. @@ -335,10 +369,12 @@ string message ) { LogMessage((object)errorCode, message); } + + /////////////////////////////////////////////////////////////////////// ////// Log a message to all the registered log event handlers without going /// through the SQLite library. /// @@ -368,10 +404,12 @@ if (enabled && (handlers != null)) handlers(null, new LogEventArgs( IntPtr.Zero, errorCode, message, null)); } + /////////////////////////////////////////////////////////////////////// + ////// Creates and initializes the default log event handler. /// private static void InitializeDefaultHandler() { @@ -380,27 +418,33 @@ if (_defaultHandler == null) _defaultHandler = new SQLiteLogEventHandler(LogEventHandler); } } + /////////////////////////////////////////////////////////////////////// + ////// Adds the default log event handler to the list of handlers. /// public static void AddDefaultHandler() { InitializeDefaultHandler(); Log += _defaultHandler; } + /////////////////////////////////////////////////////////////////////// + ////// Removes the default log event handler from the list of handlers. /// public static void RemoveDefaultHandler() { InitializeDefaultHandler(); Log -= _defaultHandler; } + + /////////////////////////////////////////////////////////////////////// ////// Internal proxy function that calls any registered application log /// event handlers. /// @@ -438,10 +482,12 @@ if (enabled && (handlers != null)) handlers(null, new LogEventArgs(pUserData, errorCode, SQLiteBase.UTF8ToString(pMessage, -1), null)); } + /////////////////////////////////////////////////////////////////////// + /// internal static void Initialize() { #if !PLATFORM_COMPACTFRAMEWORK // - // NOTE: If the "NoPreLoadSQLite" environment variable is set, skip - // all our special code and simply return. + // NOTE: If the "No_PreLoadSQLite" environment variable is set (to + // anything), skip all our special code and simply return. // if (Environment.GetEnvironmentVariable("No_PreLoadSQLite") != null) return; #endif @@ -171,10 +233,11 @@ // the supported processor architectures. // processorArchitecturePlatforms.Add("x86", "Win32"); processorArchitecturePlatforms.Add("AMD64", "x64"); processorArchitecturePlatforms.Add("IA64", "Itanium"); + processorArchitecturePlatforms.Add("ARM", "WinCE"); } // // BUGBUG: What about other application domains? // @@ -202,10 +265,38 @@ string directory = Environment.GetEnvironmentVariable( "PreLoadSQLite_BaseDirectory"); if (directory != null) return directory; + + // + // NOTE: If the "PreLoadSQLite_UseAssemblyDirectory" environment + // variable is set (to anything), attempt to use the directory + // containing the currently executing assembly (i.e. + // System.Data.SQLite) intsead of the application domain base + // directory. + // + if (Environment.GetEnvironmentVariable( + "PreLoadSQLite_UseAssemblyDirectory") != null) + { + try + { + Assembly assembly = Assembly.GetExecutingAssembly(); + + if (assembly != null) + { + directory = Path.GetDirectoryName(assembly.Location); + + if (!String.IsNullOrEmpty(directory)) + return directory; + } + } + catch + { + // do nothing. + } + } // // NOTE: Otherwise, fallback on using the base directory of the // current application domain. // @@ -275,12 +366,11 @@ /// Queries and returns the processor architecture of the current /// process. ////// Default logger. Currently, uses the Trace class (i.e. sends events /// to the current trace listeners, if any). /// /// Should be null. @@ -449,10 +495,11 @@ private static void LogEventHandler( object sender, LogEventArgs e ) { +#if !NET_COMPACT_20 if (e == null) return; string message = e.Message; @@ -476,9 +523,9 @@ else if (errorCode is int) success = ((int)errorCode == 0); Trace.WriteLine(String.Format("SQLite {0} ({1}): {2}", success ? "message" : "error", errorCode, message)); +#endif } } -#endif } Index: System.Data.SQLite/SQLiteStatement.cs ================================================================== --- System.Data.SQLite/SQLiteStatement.cs +++ System.Data.SQLite/SQLiteStatement.cs @@ -298,25 +298,29 @@ DbType objType = param.DbType; if ((obj != null) && (objType == DbType.Object)) objType = SQLiteConvert.TypeToDbType(obj.GetType()); -#if !PLATFORM_COMPACTFRAMEWORK if ((_flags & SQLiteConnectionFlags.LogPreBind) == SQLiteConnectionFlags.LogPreBind) { IntPtr handle = _sqlite_stmt; SQLiteLog.LogMessage(String.Format( "Binding statement {0} paramter #{1} with database type {2} and raw value {{{3}}}...", handle, index, objType, obj)); } -#endif if ((obj == null) || Convert.IsDBNull(obj)) { _sql.Bind_Null(this, _flags, index); return; + } + + if ((_flags & SQLiteConnectionFlags.BindAllAsText) == SQLiteConnectionFlags.BindAllAsText) + { + _sql.Bind_Text(this, _flags, index, obj.ToString()); + return; } switch (objType) { case DbType.Date: Index: System.Data.SQLite/System.Data.SQLite.Properties.targets ================================================================== --- System.Data.SQLite/System.Data.SQLite.Properties.targets +++ System.Data.SQLite/System.Data.SQLite.Properties.targets @@ -83,10 +83,18 @@ -->$(DefineConstants);USE_INTEROP_DLL + +$(DefineConstants);USE_PREPARE_V2 +@@ -151,10 +159,18 @@ --> $(DefineConstants);INTEROP_DEBUG + +$(DefineConstants);INTEROP_LEGACY_CLOSE +Index: System.Data.SQLite/UnsafeNativeMethods.cs ================================================================== --- System.Data.SQLite/UnsafeNativeMethods.cs +++ System.Data.SQLite/UnsafeNativeMethods.cs @@ -1,9 +1,9 @@ /******************************************************** * 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 { @@ -93,18 +93,80 @@ /// The name of the executable library. /// /// /// The native module handle upon success -OR- IntPtr.Zero on failure. /// +#if !PLATFORM_COMPACTFRAMEWORK [DllImport("kernel32", +#else + [DllImport("coredll", +#endif CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, #if !PLATFORM_COMPACTFRAMEWORK BestFitMapping = false, ThrowOnUnmappableChar = true, #endif SetLastError = true)] private static extern IntPtr LoadLibrary(string fileName); + ///////////////////////////////////////////////////////////////////////// + +#if PLATFORM_COMPACTFRAMEWORK + ///+ /// This is the P/Invoke method that wraps the native Win32 GetSystemInfo + /// function. See the MSDN documentation for full details on what it + /// does. + /// + /// + /// The system information structure to be filled in by the function. + /// + [DllImport("coredll", CallingConvention = CallingConvention.Winapi)] + private static extern void GetSystemInfo(out SYSTEM_INFO systemInfo); + + ///////////////////////////////////////////////////////////////////////// + ///+ /// This enumeration contains the possible values for the processor + /// architecture field of the system information structure. + /// + private enum ProcessorArchitecture : ushort /* COMPAT: Win32. */ + { + Intel = 0, + MIPS = 1, + Alpha = 2, + PowerPC = 3, + SHx = 4, + ARM = 5, + IA64 = 6, + Alpha64 = 7, + MSIL = 8, + AMD64 = 9, + IA32_on_Win64 = 10, + Unknown = 0xFFFF + } + + ///////////////////////////////////////////////////////////////////////// + ///+ /// This structure contains information about the current computer. This + /// includes the processor type, page size, memory addresses, etc. + /// + [StructLayout(LayoutKind.Sequential)] + private struct SYSTEM_INFO + { + public ProcessorArchitecture wProcessorArchitecture; + public ushort wReserved; /* NOT USED */ + public uint dwPageSize; /* NOT USED */ + public IntPtr lpMinimumApplicationAddress; /* NOT USED */ + public IntPtr lpMaximumApplicationAddress; /* NOT USED */ + public uint dwActiveProcessorMask; /* NOT USED */ + public uint dwNumberOfProcessors; /* NOT USED */ + public uint dwProcessorType; /* NOT USED */ + public uint dwAllocationGranularity; /* NOT USED */ + public ushort wProcessorLevel; /* NOT USED */ + public ushort wProcessorRevision; /* NOT USED */ + } +#endif + + ///////////////////////////////////////////////////////////////////////// ////// This lock is used to protect the static _SQLiteModule and /// processorArchitecturePlatforms fields, below. /// private static readonly object staticSyncRoot = new object(); @@ -139,12 +201,12 @@ ////// The processor architecture of the current process -OR- null if it - /// cannot be determined. Always returns an empty string when running on - /// the .NET Compact Framework. + /// cannot be determined. /// private static string GetProcessorArchitecture() { #if !PLATFORM_COMPACTFRAMEWORK // @@ -298,12 +388,41 @@ // BUGBUG: Will this always be reliable? // return Environment.GetEnvironmentVariable(PROCESSOR_ARCHITECTURE); #else // - // BUGBUG: No way to determine this value on the .NET Compact - // Framework (running on Windows CE, etc). + // NOTE: On the .NET Compact Framework, attempt to use the native + // Win32 API function (via P/Invoke) that can provide us with + // the processor architecture. + // + try + { + // + // NOTE: The output of the GetSystemInfo function will be placed + // here. Only the processor architecture field is used by + // this method. + // + SYSTEM_INFO systemInfo; + + // + // NOTE: Query the system information via P/Invoke, thus filling + // the structure. + // + GetSystemInfo(out systemInfo); + + // + // NOTE: Return the processor architecture value as a string. + // + return systemInfo.wProcessorArchitecture.ToString(); + } + catch + { + // do nothing. + } + + // + // NOTE: Upon failure, return an empty string. // return String.Empty; #endif } @@ -506,11 +625,11 @@ // be used because it provides several workarounds to .NET Compact // Framework limitations important for proper operation of the core // System.Data.SQLite functionality (e.g. being able to bind // parameters and handle column values of types Int64 and Double). // - internal const string SQLITE_DLL = "SQLite.Interop.084.dll"; + internal const string SQLITE_DLL = "SQLite.Interop.085.dll"; #elif SQLITE_STANDARD // // NOTE: Otherwise, if the standard SQLite library is enabled, use it. // private const string SQLITE_DLL = "sqlite3"; @@ -525,12 +644,12 @@ // NOTE: Finally, assume that the mixed-mode assembly is being used. // private const string SQLITE_DLL = "System.Data.SQLite.dll"; #endif - // This section uses interop calls that also fetch text length to optimize conversion. - // When using the standard dll, we can replace these calls with normal sqlite calls and + // This section uses interop calls that also fetch text length to optimize conversion. + // When using the standard dll, we can replace these calls with normal sqlite calls and // do unoptimized conversions instead afterwards #region interop added textlength calls #if !SQLITE_STANDARD @@ -641,16 +760,18 @@ #else [DllImport(SQLITE_DLL)] #endif internal static extern SQLiteErrorCode sqlite3_close(IntPtr db); +#if !INTEROP_LEGACY_CLOSE #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else [DllImport(SQLITE_DLL)] #endif internal static extern SQLiteErrorCode sqlite3_close_v2(IntPtr db); /* 3.7.14+ */ +#endif #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else [DllImport(SQLITE_DLL)] @@ -773,10 +894,19 @@ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else [DllImport(SQLITE_DLL)] #endif internal static extern SQLiteErrorCode sqlite3_prepare(IntPtr db, IntPtr pSql, int nBytes, out IntPtr stmt, out IntPtr ptrRemain); + +#if USE_PREPARE_V2 +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport(SQLITE_DLL)] +#endif + internal static extern SQLiteErrorCode sqlite3_prepare_v2(IntPtr db, IntPtr pSql, int nBytes, out IntPtr stmt, out IntPtr ptrRemain); +#endif #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else [DllImport(SQLITE_DLL)] @@ -796,11 +926,11 @@ [DllImport(SQLITE_DLL)] #endif internal static extern IntPtr sqlite3_value_text16(IntPtr p); #endif -// SQLITE_STANDARD + // SQLITE_STANDARD #endregion // These functions are custom and have no equivalent standard library method. // All of them are "nice to haves" and not necessarily "need to haves". @@ -1098,10 +1228,17 @@ internal static extern int sqlite3_column_bytes(IntPtr stmt, int index); #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else + [DllImport(SQLITE_DLL)] +#endif + internal static extern int sqlite3_column_bytes16(IntPtr stmt, int index); + +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else [DllImport(SQLITE_DLL)] #endif internal static extern TypeAffinity sqlite3_column_type(IntPtr stmt, int index); #if !PLATFORM_COMPACTFRAMEWORK @@ -1130,10 +1267,17 @@ #else [DllImport(SQLITE_DLL)] #endif internal static extern int sqlite3_value_bytes(IntPtr p); +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport(SQLITE_DLL)] +#endif + internal static extern int sqlite3_value_bytes16(IntPtr p); + #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] internal static extern double sqlite3_value_double(IntPtr p); #else [DllImport(SQLITE_DLL)] @@ -1363,11 +1507,11 @@ [DllImport(SQLITE_DLL)] #endif internal static extern IntPtr sqlite3_errstr(SQLiteErrorCode rc); /* 3.7.15+ */ // Since sqlite3_log() takes a variable argument list, we have to overload declarations - // for all possible calls. For now, we are only exposing a single string, and + // for all possible calls. For now, we are only exposing a single string, and // depend on the caller to format the string. #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else [DllImport(SQLITE_DLL)] @@ -1421,11 +1565,11 @@ #if PLATFORM_COMPACTFRAMEWORK internal abstract class CriticalHandle : IDisposable { private bool _isClosed; protected IntPtr handle; - + protected CriticalHandle(IntPtr invalidHandleValue) { handle = invalidHandleValue; _isClosed = false; } Index: Tests/basic.eagle ================================================================== --- Tests/basic.eagle +++ Tests/basic.eagle @@ -312,11 +312,10 @@ set dataSource [file join [getDatabaseDirectory] $fileName] unset -nocomplain results errors set code [compileCSharpWith [subst { - using System.Data; using System.Data.SQLite; namespace _Dynamic${id} { public static class Test${id} @@ -421,11 +420,10 @@ } unset -nocomplain results errors set code [compileCSharpWith [subst { - using System.Data; using System.Data.SQLite; namespace _Dynamic${id} { public static class Test${id} @@ -479,11 +477,10 @@ } unset -nocomplain results errors set code [compileCSharpWith [subst { - using System.Data; using System.Data.SQLite; namespace _Dynamic${id} { public static class Test${id} @@ -935,15 +932,35 @@ set code [compileCSharpWith [subst { using System; using System.Data.SQLite; using System.Reflection; + using System.Text; namespace _Dynamic${id} { public static class Test${id} { + private static string ToHexString( + byte\[\] array + ) + { + if (array == null) + return null; + + StringBuilder result = new StringBuilder(); + + int length = array.Length; + + for (int index = 0; index < length; index++) + result.AppendFormat("{0:x2}", array\[index\]); + + return result.ToString(); + } + + /////////////////////////////////////////////////////////////////////// + public static string GetConnectionString( string key, string value, string propertyName ) @@ -961,11 +978,12 @@ propertyValue = typeof(SQLiteConnectionStringBuilder).InvokeMember( propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty, null, builder, null); } - return String.Format("{0}, {1}", propertyValue, builder); + return String.Format("{0}, {1}", (propertyValue is byte\[\]) ? + ToHexString((byte\[\])propertyValue) : propertyValue, builder); } /////////////////////////////////////////////////////////////////////// public static void Main() @@ -983,27 +1001,27 @@ BinaryGUID "Data Source" Uri FullUri "Default Timeout" \ Enlist FailIfMissing "Legacy Format" "Read Only" \ Password "Page Size" "Max Page Count" "Cache Size" \ DateTimeFormat DateTimeKind BaseSchemaName \ "Journal Mode" "Default IsolationLevel" "Foreign Keys" \ - Flags SetDefaults ToFullPath] + Flags SetDefaults ToFullPath HexPassword] set values [list null 3 Normal True False \ True test.db test.db file:test.db 60 \ False True False True \ secret 4096 1024 8192 \ UnixEpoch Utc sqlite_schema \ Memory Serializable False \ - Default False False] + Default False False 736563726574] set propertyNames [list null Version SyncMode UseUTF16Encoding Pooling \ BinaryGUID DataSource Uri FullUri DefaultTimeout \ Enlist FailIfMissing LegacyFormat ReadOnly \ Password PageSize MaxPageCount CacheSize \ DateTimeFormat DateTimeKind BaseSchemaName \ JournalMode DefaultIsolationLevel ForeignKeys \ - Flags SetDefaults ToFullPath] + Flags SetDefaults ToFullPath HexPassword] foreach key $keys value $values propertyName $propertyNames { set code [catch { object invoke _Dynamic${id}.Test${id} GetConnectionString \ $key $value $propertyName @@ -1030,11 +1048,11 @@ \{UnixEpoch, DateTimeFormat=UnixEpoch\} 0 \{Utc, DateTimeKind=Utc\} 0\ \{sqlite_schema, BaseSchemaName=sqlite_schema\} 0 \{Memory, Journal\ Mode=Memory\} 0 \{Serializable, Default IsolationLevel=Serializable\} 0\ \{False, Foreign Keys=False\} 0 \{(?:Default|LogCallbackException),\ Flags=(?:Default|LogCallbackException)\} 0 \{False, SetDefaults=False\} 0\ -\{False, ToFullPath=False\}$}} +\{False, ToFullPath=False\} 0 {736563726574, HexPassword=736563726574}$}} ############################################################################### runTest {test data-1.17 {SQLiteConvert ToDateTime (Julian Day)} -body { set dateTime [object invoke System.Data.SQLite.SQLiteConvert ToDateTime \ @@ -1434,11 +1452,11 @@ [object invoke -flags +NonPublic System.Data.SQLite.SQLiteConvert \ TypeNameToDbType "VARCHAR (1)"] \ [object invoke -flags +NonPublic System.Data.SQLite.SQLiteConvert \ TypeNameToDbType "NVARCHAR (1)"] \ } -constraints {eagle System.Data.SQLite} -result \ -{String String String String String String}} +{AnsiString String AnsiString String AnsiString String}} ############################################################################### runTest {test data-1.28 {SetMemoryStatus method} -setup { # @@ -2277,10 +2295,59 @@ {eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ regexp -result {^\{0 x System#DBNull#\d+ 1\ \{System\.Reflection\.TargetInvocationException: Exception has been thrown by\ the target of an invocation\. ---> System\.InvalidCastException:.*\}\ System#DBNull#\d+\} \{0 x 1 0 1 1\}$}} + +############################################################################### + +runTest {test data-1.48 {static SQLiteCommand.Execute method} -setup { + unset -nocomplain result sql +} -body { + set sql(1) { \ + CREATE TABLE t1(x); \ + INSERT INTO t1 (x) VALUES (NULL); \ + SELECT x FROM t1 ORDER BY x; \ + } + + set sql(2) { \ + CREATE TABLE t1(x); \ + INSERT INTO t1 (x) VALUES (?); \ + SELECT x FROM t1 ORDER BY x; \ + } + + set result(1) [object invoke System.Data.SQLite.SQLiteCommand Execute \ + "this will not execute" None null] + + set result(2) [object invoke System.Data.SQLite.SQLiteCommand Execute \ + $sql(1) NonQuery null] + + set result(3) [object invoke System.Data.SQLite.SQLiteCommand Execute \ + $sql(1) Scalar null] + + set result(4) [object invoke System.Data.SQLite.SQLiteCommand Execute \ + $sql(1) Reader null] + + set result(5) [object invoke System.Data.SQLite.SQLiteCommand Execute \ + "this will not execute" None null 1] + + set result(6) [object invoke System.Data.SQLite.SQLiteCommand Execute \ + $sql(2) NonQuery null 1] + + set result(7) [object invoke System.Data.SQLite.SQLiteCommand Execute \ + $sql(2) Scalar null 1] + + set result(8) [object invoke System.Data.SQLite.SQLiteCommand Execute \ + $sql(2) Reader null 1] + + list $result(1) $result(2) $result(3) $result(4) $result(5) $result(6) \ + $result(7) $result(8) +} -cleanup { + unset -nocomplain result sql +} -constraints {eagle monoBug28 SQLite System.Data.SQLite} -match regexp \ +-result {^\{\} 1 System#DBNull#\d+ System#Data#SQLite#SQLiteDataReader#\d+ \{\}\ +1 1 System#Data#SQLite#SQLiteDataReader#\d+$}} ############################################################################### unset -nocomplain systemDataSQLiteDllFile systemDataSQLiteLinqDllFile \ testExeFile testLinqExeFile northwindEfDbFile testLinqOutFile Index: Tests/common.eagle ================================================================== --- Tests/common.eagle +++ Tests/common.eagle @@ -87,19 +87,68 @@ return 2008; # TODO: Good "fallback" default? } } } } + + proc getBuildYears {} { + # + # NOTE: See if the list of test years has been overridden by the user + # (e.g. on the command line). + # + if {[info exists ::test_years] && [llength $::test_years] > 0} then { + # + # NOTE: Use the specified list of test years. + # + return $::test_years + } else { + # + # NOTE: Use the default list of test years. + # + return [list 2005 2008 2010 2012] + } + } + + proc getBuildPlatform { native } { + if {[info exists ::test_platform] && \ + [string length $::test_platform] > 0} then { + # + # NOTE: Use the specified test platform. If this variable is not set, + # the default value will be based on the machine architecture. + # + return [expr {$native ? $::test_platform : ""}] + } elseif {[info exists ::test_machine] && \ + [string length $::test_machine] > 0} then { + # + # NOTE: For native builds, return the platform name corresponding to + # the test machine architecture; otherwise, return an empty + # string. + # + return [expr {$native ? [machineToPlatform $::test_machine] : ""}] + } elseif {[info exists ::tcl_platform(machine)]} then { + # + # NOTE: For native builds, return the platform name corresponding to + # the machine architecture; otherwise, return an empty string. + # + return [expr {$native ? \ + [machineToPlatform $::tcl_platform(machine)] : ""}] + } else { + # + # NOTE: No machine architecture is available, return an empty string. + # + return "" + } + } proc getBuildConfiguration {} { # - # NOTE: See if the "configuration" setting has been overridden by the user - # (e.g. on the command line). This helps control exactly which set - # of binaries we are testing (i.e. those built in the "Debug" or - # "Release" build configurations). To override this value via the - # command line, enter a command similar to one of the following (all - # on one line): + # NOTE: See if the "configuration" setting has been overridden by the + # user (e.g. on the command line). This helps control exactly + # which set of binaries we are testing (i.e. those built in the + # "Debug" or "Release" build configurations). To override this + # value via the command line, enter a command similar to one of + # the following (all on one line): # # EagleShell.exe -preInitialize "set test_configuration Debug" # -file .\path\to\all.eagle # # EagleShell.exe -preInitialize "set test_configuration Release" @@ -110,11 +159,12 @@ # if {[info exists ::test_configuration] && \ [string length $::test_configuration] > 0} then { # # NOTE: Use the specified test configuration. The default value used - # for this variable is "Release", as set by the test suite itself. + # for this variable is typically "Release", as set by the test + # suite itself. # return $::test_configuration } else { # # NOTE: Normally, we will never hit this case because the value of the @@ -123,10 +173,79 @@ # from the -preTest option to the test suite. # return $::eagle_platform(configuration) } } + + proc getBuildConfigurations {} { + # + # NOTE: See if the list of test configurations has been overridden by + # the user (e.g. on the command line). + # + if {[info exists ::test_configurations] && \ + [llength $::test_configurations] > 0} then { + # + # NOTE: Use the specified list of test configurations. + # + return $::test_configurations + } else { + # + # NOTE: Use the default list of test configurations. + # + return [list Debug Release] + } + } + + proc getBuildBaseDirectory {} { + # + # NOTE: Figure out the base directory where all the builds should be + # located. This will be the directory that contains the actual + # build output directory (e.g. parent of "bin"). + # + if {[info exists ::build_base_directory] && \ + [string length $::build_base_directory] > 0} then { + # + # NOTE: The location of the build base directory has been overridden; + # therefore, use it verbatim. + # + return $::build_base_directory + } elseif {[info exists ::common_directory] && \ + [string length $::common_directory] > 0} then { + # + # NOTE: Next, fallback to the parent directory of the one containing + # this file (i.e. "common.eagle"), if available. + # + return [file dirname $::common_directory] + } elseif {[info exists ::path] && \ + [string length $::path] > 0} then { + # + # NOTE: Finally, fallback to the parent directory of the EagleTest + # path. The EagleTest package guarantees that this variable + # will be set to the directory containing the first file to + # execute the [runTestPrologue] script library procedure. + # + return [file dirname $::path] + } else { + # + # NOTE: No path is available, return an empty string. This point + # should not be reached. + # + return "" + } + } + + proc joinBuildDirectory { native path year platform configuration } { + # + # NOTE: Figure out and then return the fully qualified path to the build + # directory based on all the arguments provided by our caller. + # + if {$native} then { + return [file join $path bin $year $platform $configuration] + } else { + return [file join $path bin $year $configuration bin] + } + } proc getBuildDirectory {} { # # NOTE: See if the "native" runtime option has been set. If so, use the # directory for the mixed-mode assembly (a.k.a. the native interop @@ -150,38 +269,10 @@ # NOTE: The location of the build directory has been overridden; # therefore, use it verbatim. # return $::build_directory } else { - # - # NOTE: Figure out the build base directory. This will be the directory - # that contains the actual build output directory (e.g. "bin"). - # - if {[info exists ::build_base_directory] && \ - [string length $::build_base_directory] > 0} then { - # - # NOTE: The location of the build base directory has been overridden; - # therefore, use it verbatim. - # - set path $::build_base_directory - } elseif {[info exists ::common_directory] && \ - [string length $::common_directory] > 0} then { - # - # NOTE: Next, fallback to the parent directory of the one containing - # this file (i.e. "common.eagle"), if available. - # - set path [file dirname $::common_directory] - } else { - # - # NOTE: Finally, fallback to the parent directory of the EagleTest - # path. The EagleTest package guarantees that this variable - # will be set to the directory containing the first file to - # execute the [runTestPrologue] script library procedure. - # - set path [file dirname $::path] - } - # # NOTE: If the "native" runtime option is set, the mixed-mode assembly # is being tested. In that case, the path to the build directory # will contain the platform name and all the binaries under test # should be present in that directory. If the "native" runtime @@ -218,17 +309,14 @@ # the latest version of MSBuild available and the "test_year" may # need to be adjusted accordingly to actually run the test suite. # Refer to the comments in [getBuildYear] for more information on # how to set this variable. # - if {[hasRuntimeOption native]} then { - return [file join $path bin [getBuildYear] [machineToPlatform \ - $::tcl_platform(machine)] [getBuildConfiguration]] - } else { - return [file join $path bin [getBuildYear] [getBuildConfiguration] \ - bin] - } + set native [hasRuntimeOption native] + + return [joinBuildDirectory $native [getBuildBaseDirectory] \ + [getBuildYear] [getBuildPlatform $native] [getBuildConfiguration]] } } proc getBuildFileName { fileName } { # @@ -624,10 +712,37 @@ # always return zero. # return [expr {[haveConstraint System.Data.SQLite] && \ [haveConstraint SQLite]}] } + + proc checkForSQLiteBuilds { channel } { + # + # NOTE: Check for every possible valid combination of values used when + # locating out the build output directory, showing each available + # build variation along the way. + # + foreach native [list false true] { + foreach year [getBuildYears] { + foreach configuration [getBuildConfigurations] { + tputs $channel [appendArgs \ + "---- checking for System.Data.SQLite build \"" [expr \ + {$native ? "native/" : ""}] $year / $configuration "\"... "] + + set fileName [file nativename [file join [joinBuildDirectory \ + $native [getBuildBaseDirectory] $year [getBuildPlatform \ + $native] $configuration] System.Data.SQLite.dll]] + + if {[file exists $fileName]} then { + tputs $channel yes\n + } else { + tputs $channel no\n + } + } + } + } + } proc checkForSQLite { channel } { tputs $channel "---- checking for core SQLite library... " if {[catch { @@ -824,13 +939,14 @@ # NOTE: First, see if our caller has requested an in-memory database. # set isMemory [isMemoryDb $fileName] # - # NOTE: For now, all test databases used by the test suite are placed into - # the temporary directory. Each database used by a test should be - # cleaned up by that test using the "cleanupDb" procedure, below. + # NOTE: For now, all test databases used by the test suite are placed + # into the database directory. Each database and related files + # used by a test should be cleaned up by that test using the + # "cleanupDb" procedure, below. # if {!$isMemory && $qualify} then { set fileName [file join [getDatabaseDirectory] [file tail $fileName]] } @@ -925,20 +1041,20 @@ tputs $::test_channel [appendArgs \ "---- combined connection flags are: " $flags \n] } # - # NOTE: If our caller specified a SQLiteConnectionFlags, add the necessary - # portion of the connection string now. + # NOTE: If our caller specified some SQLiteConnectionFlags, add the + # necessary portion of the connection string now. # if {[string length $flags] > 0} then { append connection {;Flags=${flags}} } # - # NOTE: If our caller specified an extra payload to the connection string, - # append it now. + # NOTE: If our caller specified an extra payload to the connection + # string, append it now. # if {[string length $extra] > 0} then { append connection \; $extra } @@ -964,12 +1080,12 @@ } proc getDbConnection { {varName db} } { # # NOTE: Refer to the specified variable (e.g. "db") in the context of our - # caller. The handle to the database previously opened by [setupDb] - # should be stored there. + # caller. The handle to the database previously opened via the + # [setupDb] procedure should be stored there. # upvar 1 $varName db # # NOTE: This returns the ADO.NET IDbConnection object instance for the @@ -997,21 +1113,22 @@ } } proc freeDbConnection { {varName connection} } { # - # NOTE: Refer to the specified variable (e.g. "connection") in the context - # of our caller. The opaque object handle for an ADO.NET connection - # previously returned by [getDbConnection] should be stored there. + # NOTE: Refer to the specified variable (e.g. "connection") in the + # context of our caller. The opaque object handle for an ADO.NET + # connection previously returned by [getDbConnection] should be + # stored there. # upvar 1 $varName connection # # NOTE: Attempt to remove the opaque object handle from the interpreter # now. This [object dispose] call will not actually dispose of the - # underlying object because the +NoDispose flag was set on it during - # creation of the opaque object handle. + # underlying object because the +NoDispose flag was set on it + # during creation of the opaque object handle. # if {[info exists connection] && \ [catch {object dispose $connection} error]} then { # # NOTE: We somehow failed to remove the handle, report why. @@ -1056,18 +1173,18 @@ collectGarbage $::test_channel } # # NOTE: Refer to the specified variable (e.g. "db") in the context of our - # caller. The handle to the database previously opened by [setupDb] - # should be stored there. + # caller. The handle to the database previously opened via the + # [setupDb] procedure should be stored there. # upvar 1 $varName db # - # NOTE: Close the connection to the database now. This should allow us to - # delete the underlying database file. + # NOTE: Close the connection to the database now. This should allow us + # to delete the underlying database file. # if {[info exists db] && [catch {sql close $db} error]} then { # # NOTE: We somehow failed to close the database, report why. # @@ -1731,10 +1848,15 @@ } else { tputs $::test_channel unknown\n } } + # + # NOTE: Check the available builds of SQLite and System.Data.SQLite. + # + checkForSQLiteBuilds $::test_channel + # # NOTE: Now, we need to know if the SQLite core library is available # (i.e. because the managed-only System.Data.SQLite assembly can # load without it; however, it cannot do anything useful without # it). If we are using the mixed-mode assembly and we already @@ -1753,15 +1875,18 @@ # are some compile-time options that must also have been enabled # for the interop assembly in order to be effective. For those # options, it will be assumed that it was enabled for the interop # assembly if it was enabled for the managed assembly. # - foreach defineConstant [list CHECK_STATE COUNT_HANDLE INTEROP_CODEC \ - INTEROP_DEBUG INTEROP_LOG \ - INTEROP_EXTENSION_FUNCTIONS \ - INTEROP_TEST_EXTENSION SQLITE_STANDARD \ - USE_INTEROP_DLL] { + foreach defineConstant [list \ + CHECK_STATE COUNT_HANDLE DEBUG INTEROP_CODEC INTEROP_DEBUG \ + INTEROP_EXTENSION_FUNCTIONS INTEROP_LEGACY_CLOSE INTEROP_LOG \ + INTEROP_TEST_EXTENSION NET_20 NET_35 NET_40 NET_45 NET_COMPACT_20 \ + PLATFORM_COMPACTFRAMEWORK PRELOAD_NATIVE_LIBRARY RETARGETABLE \ + SQLITE_STANDARD THROW_ON_DISPOSED TRACE TRACE_CONNECTION \ + TRACE_HANDLE TRACE_PRELOAD TRACE_STATEMENT TRACE_WARNING \ + USE_INTEROP_DLL USE_PREPARE_V2 WINDOWS] { # # NOTE: Check if the compile-time option is listed in the list of # "define constants" kept track of by the managed assembly. # checkForSQLiteDefineConstant $::test_channel $defineConstant @@ -1774,11 +1899,11 @@ # tputs $::test_channel \ "---- checking for System.Data.SQLite build year... " set year [getBuildYear] - addConstraint [appendArgs buildYear $year] + addConstraint [appendArgs buildYear. $year] tputs $::test_channel [appendArgs \" $year \"\n] # # NOTE: Check the current build configuration. This should normally # be either "Debug" or "Release". @@ -1785,11 +1910,11 @@ # tputs $::test_channel \ "---- checking for System.Data.SQLite build configuration... " set configuration [getBuildConfiguration] - addConstraint [appendArgs buildConfiguration $configuration] + addConstraint [appendArgs buildConfiguration. $configuration] tputs $::test_channel [appendArgs \" $configuration \"\n] # # NOTE: Try to setup an interrupt callback using the script debugger # that will cancel all SQL queries in progress for all database @@ -1884,14 +2009,14 @@ } # # NOTE: Save the name of the directory containing this file. # - if {![info exists ::common_directory]} then { - set ::common_directory [file dirname [info script]] + if {![info exists common_directory]} then { + set common_directory [file dirname [info script]] } # # NOTE: Provide the System.Data.SQLite test package to the interpreter. # package provide System.Data.SQLite.Test 1.0 } Index: Tests/installer.eagle ================================================================== --- Tests/installer.eagle +++ Tests/installer.eagle @@ -151,11 +151,11 @@ [subst -nobackslashes [readFile $testInstallVs2005LogFile]]] : $error}] } -cleanup { cleanupFile $fileName unset -nocomplain wow64 is64 code output error fileName -} -constraints {eagle administrator buildYear2005 visualStudio2005\ +} -constraints {eagle administrator buildYear.2005 visualStudio2005\ System.Data.SQLite.dll_v2.0.50727 file_Installer.exe\ file_System.Data.SQLite.dll file_Installer_Test_Vs2005.log} -result {0 True}} ############################################################################### @@ -188,11 +188,11 @@ [subst -nobackslashes [readFile $testUninstallVs2005LogFile]]] : $error}] } -cleanup { cleanupFile $fileName unset -nocomplain wow64 is64 code output error fileName -} -constraints {eagle administrator buildYear2005 visualStudio2005\ +} -constraints {eagle administrator buildYear.2005 visualStudio2005\ System.Data.SQLite.dll_v2.0.50727 file_Installer.exe\ file_System.Data.SQLite.dll file_Uninstaller_Test_Vs2005.log} -result {0 True}} ############################################################################### @@ -225,11 +225,11 @@ [subst -nobackslashes [readFile $testInstallVs2008LogFile]]] : $error}] } -cleanup { cleanupFile $fileName unset -nocomplain wow64 is64 code output error fileName -} -constraints {eagle administrator buildYear2008 visualStudio2008\ +} -constraints {eagle administrator buildYear.2008 visualStudio2008\ System.Data.SQLite.dll_v2.0.50727 file_Installer.exe\ file_System.Data.SQLite.dll file_System.Data.SQLite.Linq.dll\ file_Installer_Test_Vs2008.log} -result {0 True}} ############################################################################### @@ -263,11 +263,11 @@ [subst -nobackslashes [readFile $testUninstallVs2008LogFile]]] : $error}] } -cleanup { cleanupFile $fileName unset -nocomplain wow64 is64 code output error fileName -} -constraints {eagle administrator buildYear2008 visualStudio2008\ +} -constraints {eagle administrator buildYear.2008 visualStudio2008\ System.Data.SQLite.dll_v2.0.50727 file_Installer.exe\ file_System.Data.SQLite.dll file_System.Data.SQLite.Linq.dll\ file_Uninstaller_Test_Vs2008.log} -result {0 True}} ############################################################################### @@ -301,11 +301,11 @@ [subst -nobackslashes [readFile $testInstallVs2010LogFile]]] : $error}] } -cleanup { cleanupFile $fileName unset -nocomplain wow64 is64 code output error fileName -} -constraints {eagle administrator buildYear2010 visualStudio2010\ +} -constraints {eagle administrator buildYear.2010 visualStudio2010\ System.Data.SQLite.dll_v4.0.30319 file_Installer.exe\ file_System.Data.SQLite.dll file_System.Data.SQLite.Linq.dll\ file_Installer_Test_Vs2010.log} -result {0 True}} ############################################################################### @@ -339,11 +339,11 @@ [subst -nobackslashes [readFile $testUninstallVs2010LogFile]]] : $error}] } -cleanup { cleanupFile $fileName unset -nocomplain wow64 is64 code output error fileName -} -constraints {eagle administrator buildYear2010 visualStudio2010\ +} -constraints {eagle administrator buildYear.2010 visualStudio2010\ System.Data.SQLite.dll_v4.0.30319 file_Installer.exe\ file_System.Data.SQLite.dll file_System.Data.SQLite.Linq.dll\ file_Uninstaller_Test_Vs2010.log} -result {0 True}} ############################################################################### @@ -377,11 +377,11 @@ [subst -nobackslashes [readFile $testInstallVs2012LogFile]]] : $error}] } -cleanup { cleanupFile $fileName unset -nocomplain wow64 is64 code output error fileName -} -constraints {eagle administrator buildYear2012 visualStudio2012\ +} -constraints {eagle administrator buildYear.2012 visualStudio2012\ System.Data.SQLite.dll_v4.0.30319 file_Installer.exe\ file_System.Data.SQLite.dll file_System.Data.SQLite.Linq.dll\ file_Installer_Test_Vs2012.log} -result {0 True}} ############################################################################### @@ -415,11 +415,11 @@ [subst -nobackslashes [readFile $testUninstallVs2012LogFile]]] : $error}] } -cleanup { cleanupFile $fileName unset -nocomplain wow64 is64 code output error fileName -} -constraints {eagle administrator buildYear2012 visualStudio2012\ +} -constraints {eagle administrator buildYear.2012 visualStudio2012\ System.Data.SQLite.dll_v4.0.30319 file_Installer.exe\ file_System.Data.SQLite.dll file_System.Data.SQLite.Linq.dll\ file_Uninstaller_Test_Vs2012.log} -result {0 True}} ############################################################################### Index: Tests/stress.eagle ================================================================== --- Tests/stress.eagle +++ Tests/stress.eagle @@ -47,12 +47,12 @@ ############################################################################### runTest {test stress-1.1 {multithreaded stress testing} -setup { unset -nocomplain result thread index workload priority noWorkload \ priorities srcDb db fileName compiled options count times logFileName \ - logListener connection indicators iterations exitOnFail coTaskMem \ - noTrace failures status + logListener event timeout connection indicators iterations exitOnFail \ + coTaskMem noTrace failures status ############################################################################# proc setupWorkloadMemDb { fileName {varName db} } { # @@ -141,10 +141,19 @@ ############################################################################# proc delayTest { {extra 0} } { after [expr {int((rand() * 1000) + $extra)}] } + + ############################################################################# + + proc waitTest { indicator } { + if {![$::event WaitOne $::timeout]} then { + error [appendArgs "timeout while starting workload #" \ + [expr {[string ordinal $indicator 0] - [string ordinal A 0] + 1}]] + } + } ############################################################################# proc showTest { indicator } { tputs $::test_channel $indicator @@ -466,10 +475,24 @@ # set connection [getDbConnection] $connection LogMessage 0 [appendArgs \ "starting stress test using database \"" $fileName(2) \"...] + + ############################################################################# + + set timeout [object invoke -flags +NonPublic \ + Eagle._Components.Private.ThreadOps DefaultJoinTimeout] + + tputs $test_channel [appendArgs \ + "---- workloads will start before or timeout after " $timeout \ + " millisecond(s)\n"] + + ############################################################################# + + set event [object create -alias \ + System.Threading.EventWaitHandle false ManualReset] ############################################################################# # WORKLOAD #1 (A) # ############################################################################# @@ -476,10 +499,11 @@ set workload(1) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #1, CREATE TABLE statements. # + waitTest A lappend ::times(1) [lindex [time { initTest A setupWorkloadFileDb $dstFileName for {set index 2} {$index <= $count1} {incr index} { if {[catch { @@ -507,10 +531,11 @@ set workload(2) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #2, DROP TABLE statements. # + waitTest B lappend ::times(2) [lindex [time { initTest B setupWorkloadFileDb $dstFileName for {set index 2} {$index <= $count1} {incr index} { if {[catch { @@ -537,10 +562,11 @@ set workload(3) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #3, "small" SELECT statements. # + waitTest C lappend ::times(3) [lindex [time { initTest C setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -574,10 +600,11 @@ set workload(4) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #4, "big" SELECT statements. # + waitTest D lappend ::times(4) [lindex [time { initTest D setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -611,10 +638,11 @@ set workload(5) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #5, "small" INSERT statements. # + waitTest E lappend ::times(5) [lindex [time { initTest E setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -643,10 +671,11 @@ set workload(6) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #6, "big" INSERT statements. # + waitTest F lappend ::times(6) [lindex [time { initTest F setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -674,10 +703,11 @@ set workload(7) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #7, "small" UPDATE statements. # + waitTest G lappend ::times(7) [lindex [time { initTest G setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -705,10 +735,11 @@ set workload(8) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #8, "big" UPDATE statements. # + waitTest H lappend ::times(8) [lindex [time { initTest H setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -736,10 +767,11 @@ set workload(9) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #9, "small" DELETE statements. # + waitTest I lappend ::times(9) [lindex [time { initTest I setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -766,10 +798,11 @@ set workload(10) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #10, "big" DELETE statements. # + waitTest J lappend ::times(10) [lindex [time { initTest J setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -796,10 +829,11 @@ set workload(11) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #11, VACUUM statement. # + waitTest K lappend ::times(11) [lindex [time { initTest K setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -825,10 +859,11 @@ set workload(12) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #12, backup to in-memory database. # + waitTest L lappend ::times(12) [lindex [time { initTest L for {set index 1} {$index <= $count1} {incr index} { if {[string is integer -strict $::compiled(12)]} then { set id $::compiled(12); # NOTE: Already compiled. @@ -907,10 +942,11 @@ set workload(13) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #13, backup from an in-memory database. # + waitTest M lappend ::times(13) [lindex [time { initTest M for {set index 1} {$index <= $count1} {incr index} { if {[string is integer -strict $::compiled(13)]} then { set id $::compiled(13); # NOTE: Already compiled. @@ -989,10 +1025,11 @@ set workload(14) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #14, PRAGMA integrity check statement. # + waitTest N lappend ::times(14) [lindex [time { initTest N setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -1022,10 +1059,11 @@ set workload(15) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #15, force managed garbage collection # + waitTest O lappend ::times(15) [lindex [time { initTest O for {set index 1} {$index <= $count1} {incr index} { if {[catch { collectGarbage $::test_channel @@ -1049,10 +1087,11 @@ set workload(16) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #16, allocate (exclude) some native heap memory # + waitTest P lappend ::times(16) [lindex [time { initTest P set maxSize $::count(5) object invoke GC AddMemoryPressure $maxSize try { @@ -1088,10 +1127,11 @@ set workload(17) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #17, change the database journal mode # + waitTest Q lappend ::times(17) [lindex [time { initTest Q setupWorkloadFileDb $dstFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -1118,10 +1158,11 @@ set workload(18) [list \ [list srcFileName dstFileName table count1 count2 count3] { # # NOTE: Workload #18, execute queries against the in-memory database # + waitTest R lappend ::times(18) [lindex [time { initTest R setupWorkloadMemDb $srcFileName for {set index 1} {$index <= $count1} {incr index} { if {[catch { @@ -1235,22 +1276,26 @@ set thread($index(1)) [object create -alias System.Threading.Thread \ [list apply $workload($index(1)) $fileName(1) $fileName(2) t1 \ $count(1) $count(3) $count(4)] 1048576] $thread($index(1)) Name [appendArgs \ - [file rootname [file tail $fileName(2)]] " #" $index(1)] + [file rootname [file tail $fileName(2)]] " #" $index(1)] if {[info exists priority($index(1))]} then { $thread($index(1)) Priority $priority($index(1)) } } } - foreach index(1) [list Start Join] { - foreach index(2) [array names thread] { - $thread($index(2)) $index(1) - } + foreach index(1) [array names thread] { + $thread($index(1)) Start + } + + $event Set; # GO + + foreach index(1) [array names thread] { + $thread($index(1)) Join } foreach index(1) [array names thread] { if {[info exists thread($index(1))] && \ [cleanupThread $thread($index(1))]} then { @@ -1350,10 +1395,11 @@ rename useMem "" rename allocMem "" rename failTest "" rename doneTest "" rename showTest "" + rename waitTest "" rename delayTest "" rename initTest "" rename isExpectedError "" rename formatWorkloadTime "" rename formatWorkloadResult "" @@ -1379,12 +1425,12 @@ rename cleanupLogging "" rename setupLogging "" unset -nocomplain result thread index workload priority noWorkload \ priorities srcDb db fileName compiled options count times logFileName \ - logListener connection indicators iterations exitOnFail coTaskMem \ - noTrace failures status + logListener event timeout connection indicators iterations exitOnFail \ + coTaskMem noTrace failures status } -time true -constraints {eagle monoBug28 command.sql compile.DATA SQLite\ System.Data.SQLite} -result {0}} ############################################################################### Index: Tests/tkt-17045010df.eagle ================================================================== --- Tests/tkt-17045010df.eagle +++ Tests/tkt-17045010df.eagle @@ -53,21 +53,24 @@ unset -nocomplain db fileName } -constraints \ {eagle monoBug28 defineConstant.System.Data.SQLite.INTEROP_TEST_EXTENSION\ command.sql compile.DATA SQLite System.Data.SQLite} -result {{0 {}} {1\ -{EXCEPTION System.Data.SQLite.SQLiteException}\ +{EXCEPTION System.Data.SQLite.SQLiteException\ +System.Data.SQLite.SQLite3.LoadExtension}\ {System.Reflection.TargetInvocationException: Exception has been thrown by the\ target of an invocation. ---> System.Data.SQLite.SQLiteException: SQL logic\ error or missing database, not authorized}} {1 {EXCEPTION\ -System.Data.SQLite.SQLiteException} {System.Data.SQLite.SQLiteException\ -(0x80004005): SQL logic error or missing database, no such function:\ -interopTest}} {1 {EXCEPTION System.Data.SQLite.SQLiteException}\ +System.Data.SQLite.SQLiteException System.Data.SQLite.SQLite3.Prepare}\ +{System.Data.SQLite.SQLiteException (0x80004005): SQL logic error or missing\ +database, no such function: interopTest}} {1 {EXCEPTION\ +System.Data.SQLite.SQLiteException System.Data.SQLite.SQLite3.Prepare}\ {System.Data.SQLite.SQLiteException (0x80004005): SQL logic error or missing\ database, no such function: interopTest}} {0 {}} {0 {}} {0 test2} {1 {EXCEPTION\ -System.Data.SQLite.SQLiteException} {System.Data.SQLite.SQLiteException\ -(0x80004005): SQL logic error or missing database, need exactly one argument}}}} +System.Data.SQLite.SQLiteException System.Data.SQLite.SQLite3.Reset}\ +{System.Data.SQLite.SQLiteException (0x80004005): SQL logic error or missing\ +database, need exactly one argument}}}} ############################################################################### runSQLiteTestEpilogue runTestEpilogue ADDED Tests/tkt-1c456ae75f.eagle Index: Tests/tkt-1c456ae75f.eagle ================================================================== --- /dev/null +++ Tests/tkt-1c456ae75f.eagle @@ -0,0 +1,192 @@ +############################################################################### +# +# tkt-1c456ae75f.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require Eagle.Library +package require Eagle.Test + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-1c456ae75f-1.1 {unencrypted database, hex password} -setup { + setupDb [set fileName tkt-1c456ae75f-1.1.db] +} -body { + sql execute $db "CREATE TABLE t1(x);" + sql execute $db "INSERT INTO t1 (x) VALUES(1);" + + cleanupDb $fileName db true false false + setupDb $fileName "" "" "" "" "HexPassword=3132333435;" true false + + set result [list] + + lappend result [catch {sql execute -execute scalar $db \ + "SELECT COUNT(*) FROM t1;"} error] $error + + lappend result [catch {sql execute $db \ + "INSERT INTO t1 (x) VALUES(1);"} error] $error + + cleanupDb $fileName db true false false + setupDb $fileName "" "" "" "" "" true false + + lappend result [catch {sql execute $db \ + "INSERT INTO t1 (x) VALUES(1);"} error] $error + + lappend result [catch {sql execute -execute scalar $db \ + "SELECT COUNT(*) FROM t1;"} error] $error + + set result +} -cleanup { + cleanupDb $fileName + + unset -nocomplain error result db fileName +} -constraints {eagle defineConstant.System.Data.SQLite.INTEROP_CODEC monoBug28\ +command.sql compile.DATA SQLite System.Data.SQLite} -match regexp -result {^1\ +\{System\.Data\.SQLite\.SQLiteException \(0x80004005\): file is encrypted or is\ +not a database.*?\} 1 \{System\.Data\.SQLite\.SQLiteException \(0x80004005\):\ +file is encrypted or is not a database.*?\} 0 1 0 2$}} + +############################################################################### + +runTest {test tkt-1c456ae75f-1.2 {database, hex password} -setup { + setupDb [set fileName tkt-1c456ae75f-1.2.db] "" "" "" "" \ + "HexPassword=3132333435;" +} -body { + sql execute $db "CREATE TABLE t1(x);" + sql execute $db "INSERT INTO t1 (x) VALUES(1);" + + cleanupDb $fileName db true false false + setupDb $fileName "" "" "" "" "Password=12345;" true false + + set result [list] + + lappend result [catch {sql execute -execute scalar $db \ + "SELECT COUNT(*) FROM t1;"} error] $error + + lappend result [catch {sql execute $db \ + "INSERT INTO t1 (x) VALUES(1);"} error] $error + + cleanupDb $fileName db true false false + setupDb $fileName "" "" "" "" "HexPassword=3132333435;" true false + + lappend result [catch {sql execute $db \ + "INSERT INTO t1 (x) VALUES(1);"} error] $error + + lappend result [catch {sql execute -execute scalar $db \ + "SELECT COUNT(*) FROM t1;"} error] $error + + set result +} -cleanup { + cleanupDb $fileName + + unset -nocomplain error result db fileName +} -constraints {eagle defineConstant.System.Data.SQLite.INTEROP_CODEC monoBug28\ +command.sql compile.DATA SQLite System.Data.SQLite} -result {0 1 0 1 0 1 0 3}} + +############################################################################### + +runTest {test tkt-1c456ae75f-1.3 {database, wrong hex password} -setup { + setupDb [set fileName tkt-1c456ae75f-1.3.db] "" "" "" "" \ + "HexPassword=3132333435;" +} -body { + sql execute $db "CREATE TABLE t1(x);" + sql execute $db "INSERT INTO t1 (x) VALUES(1);" + + cleanupDb $fileName db true false false + setupDb $fileName "" "" "" "" "HexPassword=3132333436;" true false + + set result [list] + + lappend result [catch {sql execute -execute scalar $db \ + "SELECT COUNT(*) FROM t1;"} error] $error + + lappend result [catch {sql execute $db \ + "INSERT INTO t1 (x) VALUES(1);"} error] $error + + cleanupDb $fileName db true false false + setupDb $fileName "" "" "" "" "HexPassword=3132333435;" true false + + lappend result [catch {sql execute $db \ + "INSERT INTO t1 (x) VALUES(1);"} error] $error + + lappend result [catch {sql execute -execute scalar $db \ + "SELECT COUNT(*) FROM t1;"} error] $error + + set result +} -cleanup { + cleanupDb $fileName + + unset -nocomplain error result db fileName +} -constraints {eagle defineConstant.System.Data.SQLite.INTEROP_CODEC monoBug28\ +command.sql compile.DATA SQLite System.Data.SQLite} -match regexp -result {^1\ +\{System\.Data\.SQLite\.SQLiteException \(0x80004005\): file is encrypted or is\ +not a database.*?\} 1 \{System\.Data\.SQLite\.SQLiteException \(0x80004005\):\ +file is encrypted or is not a database.*?\} 0 1 0 2$}} + +############################################################################### + +runTest {test tkt-1c456ae75f-1.4 {database, hex password via builder} -setup { + setupDb [set fileName tkt-1c456ae75f-1.4.db] "" "" "" "" \ + "HexPassword=3132333435;" +} -body { + sql execute $db "CREATE TABLE t1(x);" + sql execute $db "INSERT INTO t1 (x) VALUES(1);" + + cleanupDb $fileName db true false false + + set connectionStringBuilder [object create -alias \ + System.Data.SQLite.SQLiteConnectionStringBuilder] + + $connectionStringBuilder DataSource \ + [file join [getDatabaseDirectory] $fileName] + + $connectionStringBuilder HexPassword [list 0x31 0x32 0x33 0x34 0x35] + + set connection [object create -alias \ + System.Data.SQLite.SQLiteConnection \ + [$connectionStringBuilder ToString] true] + + $connection Open; addDbConnection $connection + + set result [list] + + lappend result [catch {sql execute -execute scalar $db \ + "SELECT COUNT(*) FROM t1;"} error] $error + + lappend result [catch {sql execute $db \ + "INSERT INTO t1 (x) VALUES(1);"} error] $error + + cleanupDb $fileName db true false false + setupDb $fileName "" "" "" "" "Password=\"12345\";" true false + + lappend result [catch {sql execute $db \ + "INSERT INTO t1 (x) VALUES(1);"} error] $error + + lappend result [catch {sql execute -execute scalar $db \ + "SELECT COUNT(*) FROM t1;"} error] $error + + set result +} -cleanup { + unset -nocomplain connection + + cleanupDb $fileName; # NOTE: After object disposal. + + unset -nocomplain connectionStringBuilder error result db fileName +} -constraints {eagle defineConstant.System.Data.SQLite.INTEROP_CODEC monoBug28\ +command.sql compile.DATA SQLite System.Data.SQLite} -result {0 1 0 1 0 1 0 3}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue ADDED Tests/tkt-3567020edf.eagle Index: Tests/tkt-3567020edf.eagle ================================================================== --- /dev/null +++ Tests/tkt-3567020edf.eagle @@ -0,0 +1,63 @@ +############################################################################### +# +# tkt-3567020edf.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require Eagle.Library +package require Eagle.Test + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-3567020edf-1.1 {embedded NUL characters (UTF-8)} -setup { + setupDb [set fileName tkt-3567020edf-1.1.db] +} -body { + sql execute $db "CREATE TABLE t1(x);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 String one\x00two] + + sql execute -execute reader -format list $db "SELECT x FROM t1;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +"one\x00two"} + +############################################################################### + +runTest {test tkt-3567020edf-1.2 {embedded NUL characters (UTF-16)} -setup { + setupDb [set fileName tkt-3567020edf-1.2.db] "" "" "" "" \ + UseUTF16Encoding=True +} -body { + sql execute $db "CREATE TABLE t1(x);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 String one\x00two] + + sql execute -execute reader -format list $db "SELECT x FROM t1;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +"one\x00two"} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue ADDED Tests/tkt-393d954be0.eagle Index: Tests/tkt-393d954be0.eagle ================================================================== --- /dev/null +++ Tests/tkt-393d954be0.eagle @@ -0,0 +1,72 @@ +############################################################################### +# +# tkt-393d954be0.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require Eagle.Library +package require Eagle.Test + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-393d954be0-1.1 {custom connection pool} -setup { + set nullPool [object create -flags +NonPublic -alias \ + System.Data.SQLite.NullConnectionPool true] + + object invoke System.Data.SQLite.SQLiteConnection ConnectionPool $nullPool + + setupDb [set fileName tkt-393d954be0-1.1.db] "" "" "" "" "Pooling=True;" +} -body { + set exists(0) [file exists [file join [getDatabaseDirectory] [file tail \ + $fileName]]] + + cleanupDb $fileName + + set exists(1) [file exists [file join [getDatabaseDirectory] [file tail \ + $fileName]]] + + set counts null; set openCount 0; set closeCount 0; set totalCount 0 + object invoke -flags +NonPublic System.Data.SQLite.SQLiteConnectionPool \ + GetCounts $fileName counts openCount closeCount totalCount + + object invoke -flags +NonPublic System.Data.SQLite.SQLiteConnectionPool \ + ClearPool $fileName + + object invoke -flags +NonPublic System.Data.SQLite.SQLiteConnectionPool \ + ClearAllPools + + list $exists(0) $exists(1) $counts $openCount $closeCount $totalCount \ + [object invoke $nullPool ToString] +} -cleanup { + cleanupDb $fileName + + catch {object invoke System.Data.SQLite.SQLiteConnection ConnectionPool null} + + unset -nocomplain db fileName exists counts openCount closeCount totalCount \ + nullPool +} -constraints {eagle monoBug28 buildConfiguration.Debug command.sql\ +compile.DATA SQLite System.Data.SQLite} -match regexp -result [string map \ +[list \n \r\n] {^True False \{\} 0 0 0\ +\{Remove\(".*?\\tkt-393d954be0-1\.1\.db",\ +100, 0\) +Add\(".*?\\tkt-393d954be0-1\.1\.db", -?\d+, 0\) +GetCounts\("tkt-393d954be0-1\.1\.db", , 0, 0, 0\) +ClearPool\("tkt-393d954be0-1\.1\.db"\) +ClearAllPools\(\) +\}$}]} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/tkt-84718e79fa.eagle ================================================================== --- Tests/tkt-84718e79fa.eagle +++ Tests/tkt-84718e79fa.eagle @@ -39,11 +39,11 @@ for {set i 0} {$i < $c} {incr i} { set t($i) [object create -alias Thread threadStart 1048576] } - set ::results [list] + set results [list] for {set i 0} {$i < $c} {incr i} { $t($i) Start } @@ -51,11 +51,11 @@ for {set i 0} {$i < $c} {incr i} { $t($i) Join } - set ::results + set results } -cleanup { cleanupDb $fileName object unimport -importpattern System.Threading Index: Tests/tkt-ae5267b863.eagle ================================================================== --- Tests/tkt-ae5267b863.eagle +++ Tests/tkt-ae5267b863.eagle @@ -145,12 +145,13 @@ } -cleanup { cleanupDb $fileName unset -nocomplain result code results errors sql dataSource id db fileName } -constraints [fixConstraints {eagle monoBug28 command.sql compile.DATA\ -SQLite System.Data.SQLite !mda}] -match regexp -result {^Ok\ -System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}$}} +SQLite System.Data.SQLite !mda\ +!defineConstant.System.Data.SQLite.INTEROP_LEGACY_CLOSE}] -match regexp \ +-result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}$}} ############################################################################### runSQLiteTestEpilogue runTestEpilogue ADDED Tests/tkt-c010fa6584.eagle Index: Tests/tkt-c010fa6584.eagle ================================================================== --- /dev/null +++ Tests/tkt-c010fa6584.eagle @@ -0,0 +1,566 @@ +############################################################################### +# +# tkt-c010fa6584.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require Eagle.Library +package require Eagle.Test + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-c010fa6584-1.1 {column "integral" type handling} -setup { + setupDb [set fileName tkt-c010fa6584-1.1.db] +} -body { + # + # NOTE: Create a database table with all the integral type names that are + # recognized by System.Data.SQLite. Subsequently, each column will + # be tested against a list of values that meet and/or exceed the + # bounds for the underlying primitive integral CLR type. + # + sql execute $db { + CREATE TABLE t1( + a00 SYSNAME, -- This is the 'payload' column name. + a01, -- String + a02 TEXT, -- String + a03 BIGINT, -- Int64 + a04 BIGUINT, -- UInt64 + a05 COUNTER, -- Int64 + a06 IDENTITY, -- Int64 + a07 INT, -- Int32 + a08 INT8, -- SByte + a09 INT16, -- Int16 + a10 INT32, -- Int32 + a11 INT64, -- Int64 + a12 INTEGER, -- Int64 + a13 INTEGER8, -- SByte + a14 INTEGER16, -- Int16 + a15 INTEGER32, -- Int32 + a16 INTEGER64, -- Int64 + a17 LONG, -- Int64 + a18 SMALLINT, -- Int16 + a19 SMALLUINT, -- UInt16 + a20 TINYINT, -- Byte + a21 TINYSINT, -- SByte + a22 UINT, -- UInt32 + a23 UINT8, -- Byte + a24 UINT16, -- UInt16 + a25 UINT32, -- UInt32 + a26 UINT64, -- UInt64 + a27 ULONG, -- UInt64 + a28 UNSIGNEDINTEGER, -- UInt32 + a29 UNSIGNEDINTEGER8, -- Byte + a30 UNSIGNEDINTEGER16, -- UInt16 + a31 UNSIGNEDINTEGER32, -- UInt32 + a32 UNSIGNEDINTEGER64 -- UInt64 + ); + } + + # + # NOTE: These are the numeric values being tested against all the types in + # the database table defined above. This list includes values that + # are out-of-bounds for each primitive integral type. + # + set values [list \ + -9223372036854775809 -9223372036854775808 \ + -2147483649 -2147483648 \ + -32769 -32768 \ + -129 -128 \ + -1 0 1 \ + 127 128 \ + 255 256 \ + 32767 32768 \ + 65535 65536 \ + 2147483647 2147483648 \ + 4294967295 4294967296 \ + 9223372036854775807 9223372036854775808 \ + 18446744073709551615 18446744073709551616] + + for {set index 1} {$index <= 32} {incr index} { + set name [appendArgs a [format %02d $index]] + foreach value $values { + sql execute $db [subst { + INSERT INTO t1 (a00, $name) VALUES('$name', $value); + }] + } + } + + set results [list] + + for {set index 1} {$index <= 32} {incr index} { + set name [appendArgs a [format %02d $index]] + set count [sql execute -execute scalar $db [subst { + SELECT COUNT(*) FROM t1 WHERE a00 = '$name'; + }]] + for {set offset 0} {$offset < $count} {incr offset} { + set code [catch { + sql execute -execute scalar $db [subst { + SELECT $name FROM t1 WHERE a00 = '$name' LIMIT 1 OFFSET $offset; + }] + } result] + + set match [expr {$result eq [lindex $values $offset]}] + + lappend results [list \ + $name $offset $match [lindex $values $offset] \ + $code [expr {$code == 0 ? $result : $errorCode}]] + } + } + + set results +} -cleanup { + cleanupDb $fileName + + unset -nocomplain match result code offset count results value name index \ + values db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{{a01 0 False -9223372036854775809 0 -9.22337203685478E+18} {a01 1 True\ +-9223372036854775808 0 -9223372036854775808} {a01 2 True -2147483649 0\ +-2147483649} {a01 3 True -2147483648 0 -2147483648} {a01 4 True -32769 0\ +-32769} {a01 5 True -32768 0 -32768} {a01 6 True -129 0 -129} {a01 7 True -128\ +0 -128} {a01 8 True -1 0 -1} {a01 9 True 0 0 0} {a01 10 True 1 0 1} {a01 11\ +True 127 0 127} {a01 12 True 128 0 128} {a01 13 True 255 0 255} {a01 14 True\ +256 0 256} {a01 15 True 32767 0 32767} {a01 16 True 32768 0 32768} {a01 17 True\ +65535 0 65535} {a01 18 True 65536 0 65536} {a01 19 True 2147483647 0\ +2147483647} {a01 20 True 2147483648 0 2147483648} {a01 21 True 4294967295 0\ +4294967295} {a01 22 True 4294967296 0 4294967296} {a01 23 True\ +9223372036854775807 0 9223372036854775807} {a01 24 False 9223372036854775808 0\ +9.22337203685478E+18} {a01 25 False 18446744073709551615 0\ +1.84467440737096E+19} {a01 26 False 18446744073709551616 0\ +1.84467440737096E+19} {a02 0 False -9223372036854775809 0\ +-9.22337203685478e+18} {a02 1 True -9223372036854775808 0 -9223372036854775808}\ +{a02 2 True -2147483649 0 -2147483649} {a02 3 True -2147483648 0 -2147483648}\ +{a02 4 True -32769 0 -32769} {a02 5 True -32768 0 -32768} {a02 6 True -129 0\ +-129} {a02 7 True -128 0 -128} {a02 8 True -1 0 -1} {a02 9 True 0 0 0} {a02 10\ +True 1 0 1} {a02 11 True 127 0 127} {a02 12 True 128 0 128} {a02 13 True 255 0\ +255} {a02 14 True 256 0 256} {a02 15 True 32767 0 32767} {a02 16 True 32768 0\ +32768} {a02 17 True 65535 0 65535} {a02 18 True 65536 0 65536} {a02 19 True\ +2147483647 0 2147483647} {a02 20 True 2147483648 0 2147483648} {a02 21 True\ +4294967295 0 4294967295} {a02 22 True 4294967296 0 4294967296} {a02 23 True\ +9223372036854775807 0 9223372036854775807} {a02 24 False 9223372036854775808 0\ +9.22337203685478e+18} {a02 25 False 18446744073709551615 0\ +1.84467440737096e+19} {a02 26 False 18446744073709551616 0\ +1.84467440737096e+19} {a03 0 False -9223372036854775809 0 -9223372036854775808}\ +{a03 1 True -9223372036854775808 0 -9223372036854775808} {a03 2 True\ +-2147483649 0 -2147483649} {a03 3 True -2147483648 0 -2147483648} {a03 4 True\ +-32769 0 -32769} {a03 5 True -32768 0 -32768} {a03 6 True -129 0 -129} {a03 7\ +True -128 0 -128} {a03 8 True -1 0 -1} {a03 9 True 0 0 0} {a03 10 True 1 0 1}\ +{a03 11 True 127 0 127} {a03 12 True 128 0 128} {a03 13 True 255 0 255} {a03 14\ +True 256 0 256} {a03 15 True 32767 0 32767} {a03 16 True 32768 0 32768} {a03 17\ +True 65535 0 65535} {a03 18 True 65536 0 65536} {a03 19 True 2147483647 0\ +2147483647} {a03 20 True 2147483648 0 2147483648} {a03 21 True 4294967295 0\ +4294967295} {a03 22 True 4294967296 0 4294967296} {a03 23 True\ +9223372036854775807 0 9223372036854775807} {a03 24 False 9223372036854775808 0\ +-9223372036854775808} {a03 25 False 18446744073709551615 0\ +-9223372036854775808} {a03 26 False 18446744073709551616 0\ +-9223372036854775808} {a04 0 False -9223372036854775809 0 9223372036854775808}\ +{a04 1 False -9223372036854775808 0 9223372036854775808} {a04 2 False\ +-2147483649 0 18446744071562067967} {a04 3 False -2147483648 0\ +18446744071562067968} {a04 4 False -32769 0 18446744073709518847} {a04 5 False\ +-32768 0 18446744073709518848} {a04 6 False -129 0 18446744073709551487} {a04 7\ +False -128 0 18446744073709551488} {a04 8 False -1 0 18446744073709551615} {a04\ +9 True 0 0 0} {a04 10 True 1 0 1} {a04 11 True 127 0 127} {a04 12 True 128 0\ +128} {a04 13 True 255 0 255} {a04 14 True 256 0 256} {a04 15 True 32767 0\ +32767} {a04 16 True 32768 0 32768} {a04 17 True 65535 0 65535} {a04 18 True\ +65536 0 65536} {a04 19 True 2147483647 0 2147483647} {a04 20 True 2147483648 0\ +2147483648} {a04 21 True 4294967295 0 4294967295} {a04 22 True 4294967296 0\ +4294967296} {a04 23 True 9223372036854775807 0 9223372036854775807} {a04 24\ +True 9223372036854775808 0 9223372036854775808} {a04 25 False\ +18446744073709551615 0 9223372036854775808} {a04 26 False 18446744073709551616\ +0 9223372036854775808} {a05 0 False -9223372036854775809 0\ +-9223372036854775808} {a05 1 True -9223372036854775808 0 -9223372036854775808}\ +{a05 2 True -2147483649 0 -2147483649} {a05 3 True -2147483648 0 -2147483648}\ +{a05 4 True -32769 0 -32769} {a05 5 True -32768 0 -32768} {a05 6 True -129 0\ +-129} {a05 7 True -128 0 -128} {a05 8 True -1 0 -1} {a05 9 True 0 0 0} {a05 10\ +True 1 0 1} {a05 11 True 127 0 127} {a05 12 True 128 0 128} {a05 13 True 255 0\ +255} {a05 14 True 256 0 256} {a05 15 True 32767 0 32767} {a05 16 True 32768 0\ +32768} {a05 17 True 65535 0 65535} {a05 18 True 65536 0 65536} {a05 19 True\ +2147483647 0 2147483647} {a05 20 True 2147483648 0 2147483648} {a05 21 True\ +4294967295 0 4294967295} {a05 22 True 4294967296 0 4294967296} {a05 23 True\ +9223372036854775807 0 9223372036854775807} {a05 24 False 9223372036854775808 0\ +-9223372036854775808} {a05 25 False 18446744073709551615 0\ +-9223372036854775808} {a05 26 False 18446744073709551616 0\ +-9223372036854775808} {a06 0 False -9223372036854775809 0 -9223372036854775808}\ +{a06 1 True -9223372036854775808 0 -9223372036854775808} {a06 2 True\ +-2147483649 0 -2147483649} {a06 3 True -2147483648 0 -2147483648} {a06 4 True\ +-32769 0 -32769} {a06 5 True -32768 0 -32768} {a06 6 True -129 0 -129} {a06 7\ +True -128 0 -128} {a06 8 True -1 0 -1} {a06 9 True 0 0 0} {a06 10 True 1 0 1}\ +{a06 11 True 127 0 127} {a06 12 True 128 0 128} {a06 13 True 255 0 255} {a06 14\ +True 256 0 256} {a06 15 True 32767 0 32767} {a06 16 True 32768 0 32768} {a06 17\ +True 65535 0 65535} {a06 18 True 65536 0 65536} {a06 19 True 2147483647 0\ +2147483647} {a06 20 True 2147483648 0 2147483648} {a06 21 True 4294967295 0\ +4294967295} {a06 22 True 4294967296 0 4294967296} {a06 23 True\ +9223372036854775807 0 9223372036854775807} {a06 24 False 9223372036854775808 0\ +-9223372036854775808} {a06 25 False 18446744073709551615 0\ +-9223372036854775808} {a06 26 False 18446744073709551616 0\ +-9223372036854775808} {a07 0 False -9223372036854775809 0 0} {a07 1 False\ +-9223372036854775808 0 0} {a07 2 False -2147483649 0 2147483647} {a07 3 True\ +-2147483648 0 -2147483648} {a07 4 True -32769 0 -32769} {a07 5 True -32768 0\ +-32768} {a07 6 True -129 0 -129} {a07 7 True -128 0 -128} {a07 8 True -1 0 -1}\ +{a07 9 True 0 0 0} {a07 10 True 1 0 1} {a07 11 True 127 0 127} {a07 12 True 128\ +0 128} {a07 13 True 255 0 255} {a07 14 True 256 0 256} {a07 15 True 32767 0\ +32767} {a07 16 True 32768 0 32768} {a07 17 True 65535 0 65535} {a07 18 True\ +65536 0 65536} {a07 19 True 2147483647 0 2147483647} {a07 20 False 2147483648 0\ +-2147483648} {a07 21 False 4294967295 0 -1} {a07 22 False 4294967296 0 0} {a07\ +23 False 9223372036854775807 0 -1} {a07 24 False 9223372036854775808 0 0} {a07\ +25 False 18446744073709551615 0 0} {a07 26 False 18446744073709551616 0 0} {a08\ +0 False -9223372036854775809 0 0} {a08 1 False -9223372036854775808 0 0} {a08 2\ +False -2147483649 0 -1} {a08 3 False -2147483648 0 0} {a08 4 False -32769 0 -1}\ +{a08 5 False -32768 0 0} {a08 6 False -129 0 127} {a08 7 True -128 0 -128} {a08\ +8 True -1 0 -1} {a08 9 True 0 0 0} {a08 10 True 1 0 1} {a08 11 True 127 0 127}\ +{a08 12 False 128 0 -128} {a08 13 False 255 0 -1} {a08 14 False 256 0 0} {a08\ +15 False 32767 0 -1} {a08 16 False 32768 0 0} {a08 17 False 65535 0 -1} {a08 18\ +False 65536 0 0} {a08 19 False 2147483647 0 -1} {a08 20 False 2147483648 0 0}\ +{a08 21 False 4294967295 0 -1} {a08 22 False 4294967296 0 0} {a08 23 False\ +9223372036854775807 0 -1} {a08 24 False 9223372036854775808 0 0} {a08 25 False\ +18446744073709551615 0 0} {a08 26 False 18446744073709551616 0 0} {a09 0 False\ +-9223372036854775809 0 0} {a09 1 False -9223372036854775808 0 0} {a09 2 False\ +-2147483649 0 -1} {a09 3 False -2147483648 0 0} {a09 4 False -32769 0 32767}\ +{a09 5 True -32768 0 -32768} {a09 6 True -129 0 -129} {a09 7 True -128 0 -128}\ +{a09 8 True -1 0 -1} {a09 9 True 0 0 0} {a09 10 True 1 0 1} {a09 11 True 127 0\ +127} {a09 12 True 128 0 128} {a09 13 True 255 0 255} {a09 14 True 256 0 256}\ +{a09 15 True 32767 0 32767} {a09 16 False 32768 0 -32768} {a09 17 False 65535 0\ +-1} {a09 18 False 65536 0 0} {a09 19 False 2147483647 0 -1} {a09 20 False\ +2147483648 0 0} {a09 21 False 4294967295 0 -1} {a09 22 False 4294967296 0 0}\ +{a09 23 False 9223372036854775807 0 -1} {a09 24 False 9223372036854775808 0 0}\ +{a09 25 False 18446744073709551615 0 0} {a09 26 False 18446744073709551616 0 0}\ +{a10 0 False -9223372036854775809 0 0} {a10 1 False -9223372036854775808 0 0}\ +{a10 2 False -2147483649 0 2147483647} {a10 3 True -2147483648 0 -2147483648}\ +{a10 4 True -32769 0 -32769} {a10 5 True -32768 0 -32768} {a10 6 True -129 0\ +-129} {a10 7 True -128 0 -128} {a10 8 True -1 0 -1} {a10 9 True 0 0 0} {a10 10\ +True 1 0 1} {a10 11 True 127 0 127} {a10 12 True 128 0 128} {a10 13 True 255 0\ +255} {a10 14 True 256 0 256} {a10 15 True 32767 0 32767} {a10 16 True 32768 0\ +32768} {a10 17 True 65535 0 65535} {a10 18 True 65536 0 65536} {a10 19 True\ +2147483647 0 2147483647} {a10 20 False 2147483648 0 -2147483648} {a10 21 False\ +4294967295 0 -1} {a10 22 False 4294967296 0 0} {a10 23 False\ +9223372036854775807 0 -1} {a10 24 False 9223372036854775808 0 0} {a10 25 False\ +18446744073709551615 0 0} {a10 26 False 18446744073709551616 0 0} {a11 0 False\ +-9223372036854775809 0 -9223372036854775808} {a11 1 True -9223372036854775808 0\ +-9223372036854775808} {a11 2 True -2147483649 0 -2147483649} {a11 3 True\ +-2147483648 0 -2147483648} {a11 4 True -32769 0 -32769} {a11 5 True -32768 0\ +-32768} {a11 6 True -129 0 -129} {a11 7 True -128 0 -128} {a11 8 True -1 0 -1}\ +{a11 9 True 0 0 0} {a11 10 True 1 0 1} {a11 11 True 127 0 127} {a11 12 True 128\ +0 128} {a11 13 True 255 0 255} {a11 14 True 256 0 256} {a11 15 True 32767 0\ +32767} {a11 16 True 32768 0 32768} {a11 17 True 65535 0 65535} {a11 18 True\ +65536 0 65536} {a11 19 True 2147483647 0 2147483647} {a11 20 True 2147483648 0\ +2147483648} {a11 21 True 4294967295 0 4294967295} {a11 22 True 4294967296 0\ +4294967296} {a11 23 True 9223372036854775807 0 9223372036854775807} {a11 24\ +False 9223372036854775808 0 -9223372036854775808} {a11 25 False\ +18446744073709551615 0 -9223372036854775808} {a11 26 False 18446744073709551616\ +0 -9223372036854775808} {a12 0 False -9223372036854775809 0\ +-9223372036854775808} {a12 1 True -9223372036854775808 0 -9223372036854775808}\ +{a12 2 True -2147483649 0 -2147483649} {a12 3 True -2147483648 0 -2147483648}\ +{a12 4 True -32769 0 -32769} {a12 5 True -32768 0 -32768} {a12 6 True -129 0\ +-129} {a12 7 True -128 0 -128} {a12 8 True -1 0 -1} {a12 9 True 0 0 0} {a12 10\ +True 1 0 1} {a12 11 True 127 0 127} {a12 12 True 128 0 128} {a12 13 True 255 0\ +255} {a12 14 True 256 0 256} {a12 15 True 32767 0 32767} {a12 16 True 32768 0\ +32768} {a12 17 True 65535 0 65535} {a12 18 True 65536 0 65536} {a12 19 True\ +2147483647 0 2147483647} {a12 20 True 2147483648 0 2147483648} {a12 21 True\ +4294967295 0 4294967295} {a12 22 True 4294967296 0 4294967296} {a12 23 True\ +9223372036854775807 0 9223372036854775807} {a12 24 False 9223372036854775808 0\ +-9223372036854775808} {a12 25 False 18446744073709551615 0\ +-9223372036854775808} {a12 26 False 18446744073709551616 0\ +-9223372036854775808} {a13 0 False -9223372036854775809 0 0} {a13 1 False\ +-9223372036854775808 0 0} {a13 2 False -2147483649 0 -1} {a13 3 False\ +-2147483648 0 0} {a13 4 False -32769 0 -1} {a13 5 False -32768 0 0} {a13 6\ +False -129 0 127} {a13 7 True -128 0 -128} {a13 8 True -1 0 -1} {a13 9 True 0 0\ +0} {a13 10 True 1 0 1} {a13 11 True 127 0 127} {a13 12 False 128 0 -128} {a13\ +13 False 255 0 -1} {a13 14 False 256 0 0} {a13 15 False 32767 0 -1} {a13 16\ +False 32768 0 0} {a13 17 False 65535 0 -1} {a13 18 False 65536 0 0} {a13 19\ +False 2147483647 0 -1} {a13 20 False 2147483648 0 0} {a13 21 False 4294967295 0\ +-1} {a13 22 False 4294967296 0 0} {a13 23 False 9223372036854775807 0 -1} {a13\ +24 False 9223372036854775808 0 0} {a13 25 False 18446744073709551615 0 0} {a13\ +26 False 18446744073709551616 0 0} {a14 0 False -9223372036854775809 0 0} {a14\ +1 False -9223372036854775808 0 0} {a14 2 False -2147483649 0 -1} {a14 3 False\ +-2147483648 0 0} {a14 4 False -32769 0 32767} {a14 5 True -32768 0 -32768} {a14\ +6 True -129 0 -129} {a14 7 True -128 0 -128} {a14 8 True -1 0 -1} {a14 9 True 0\ +0 0} {a14 10 True 1 0 1} {a14 11 True 127 0 127} {a14 12 True 128 0 128} {a14\ +13 True 255 0 255} {a14 14 True 256 0 256} {a14 15 True 32767 0 32767} {a14 16\ +False 32768 0 -32768} {a14 17 False 65535 0 -1} {a14 18 False 65536 0 0} {a14\ +19 False 2147483647 0 -1} {a14 20 False 2147483648 0 0} {a14 21 False\ +4294967295 0 -1} {a14 22 False 4294967296 0 0} {a14 23 False\ +9223372036854775807 0 -1} {a14 24 False 9223372036854775808 0 0} {a14 25 False\ +18446744073709551615 0 0} {a14 26 False 18446744073709551616 0 0} {a15 0 False\ +-9223372036854775809 0 0} {a15 1 False -9223372036854775808 0 0} {a15 2 False\ +-2147483649 0 2147483647} {a15 3 True -2147483648 0 -2147483648} {a15 4 True\ +-32769 0 -32769} {a15 5 True -32768 0 -32768} {a15 6 True -129 0 -129} {a15 7\ +True -128 0 -128} {a15 8 True -1 0 -1} {a15 9 True 0 0 0} {a15 10 True 1 0 1}\ +{a15 11 True 127 0 127} {a15 12 True 128 0 128} {a15 13 True 255 0 255} {a15 14\ +True 256 0 256} {a15 15 True 32767 0 32767} {a15 16 True 32768 0 32768} {a15 17\ +True 65535 0 65535} {a15 18 True 65536 0 65536} {a15 19 True 2147483647 0\ +2147483647} {a15 20 False 2147483648 0 -2147483648} {a15 21 False 4294967295 0\ +-1} {a15 22 False 4294967296 0 0} {a15 23 False 9223372036854775807 0 -1} {a15\ +24 False 9223372036854775808 0 0} {a15 25 False 18446744073709551615 0 0} {a15\ +26 False 18446744073709551616 0 0} {a16 0 False -9223372036854775809 0\ +-9223372036854775808} {a16 1 True -9223372036854775808 0 -9223372036854775808}\ +{a16 2 True -2147483649 0 -2147483649} {a16 3 True -2147483648 0 -2147483648}\ +{a16 4 True -32769 0 -32769} {a16 5 True -32768 0 -32768} {a16 6 True -129 0\ +-129} {a16 7 True -128 0 -128} {a16 8 True -1 0 -1} {a16 9 True 0 0 0} {a16 10\ +True 1 0 1} {a16 11 True 127 0 127} {a16 12 True 128 0 128} {a16 13 True 255 0\ +255} {a16 14 True 256 0 256} {a16 15 True 32767 0 32767} {a16 16 True 32768 0\ +32768} {a16 17 True 65535 0 65535} {a16 18 True 65536 0 65536} {a16 19 True\ +2147483647 0 2147483647} {a16 20 True 2147483648 0 2147483648} {a16 21 True\ +4294967295 0 4294967295} {a16 22 True 4294967296 0 4294967296} {a16 23 True\ +9223372036854775807 0 9223372036854775807} {a16 24 False 9223372036854775808 0\ +-9223372036854775808} {a16 25 False 18446744073709551615 0\ +-9223372036854775808} {a16 26 False 18446744073709551616 0\ +-9223372036854775808} {a17 0 False -9223372036854775809 0 -9223372036854775808}\ +{a17 1 True -9223372036854775808 0 -9223372036854775808} {a17 2 True\ +-2147483649 0 -2147483649} {a17 3 True -2147483648 0 -2147483648} {a17 4 True\ +-32769 0 -32769} {a17 5 True -32768 0 -32768} {a17 6 True -129 0 -129} {a17 7\ +True -128 0 -128} {a17 8 True -1 0 -1} {a17 9 True 0 0 0} {a17 10 True 1 0 1}\ +{a17 11 True 127 0 127} {a17 12 True 128 0 128} {a17 13 True 255 0 255} {a17 14\ +True 256 0 256} {a17 15 True 32767 0 32767} {a17 16 True 32768 0 32768} {a17 17\ +True 65535 0 65535} {a17 18 True 65536 0 65536} {a17 19 True 2147483647 0\ +2147483647} {a17 20 True 2147483648 0 2147483648} {a17 21 True 4294967295 0\ +4294967295} {a17 22 True 4294967296 0 4294967296} {a17 23 True\ +9223372036854775807 0 9223372036854775807} {a17 24 False 9223372036854775808 0\ +-9223372036854775808} {a17 25 False 18446744073709551615 0\ +-9223372036854775808} {a17 26 False 18446744073709551616 0\ +-9223372036854775808} {a18 0 False -9223372036854775809 0 0} {a18 1 False\ +-9223372036854775808 0 0} {a18 2 False -2147483649 0 -1} {a18 3 False\ +-2147483648 0 0} {a18 4 False -32769 0 32767} {a18 5 True -32768 0 -32768} {a18\ +6 True -129 0 -129} {a18 7 True -128 0 -128} {a18 8 True -1 0 -1} {a18 9 True 0\ +0 0} {a18 10 True 1 0 1} {a18 11 True 127 0 127} {a18 12 True 128 0 128} {a18\ +13 True 255 0 255} {a18 14 True 256 0 256} {a18 15 True 32767 0 32767} {a18 16\ +False 32768 0 -32768} {a18 17 False 65535 0 -1} {a18 18 False 65536 0 0} {a18\ +19 False 2147483647 0 -1} {a18 20 False 2147483648 0 0} {a18 21 False\ +4294967295 0 -1} {a18 22 False 4294967296 0 0} {a18 23 False\ +9223372036854775807 0 -1} {a18 24 False 9223372036854775808 0 0} {a18 25 False\ +18446744073709551615 0 0} {a18 26 False 18446744073709551616 0 0} {a19 0 False\ +-9223372036854775809 0 0} {a19 1 False -9223372036854775808 0 0} {a19 2 False\ +-2147483649 0 65535} {a19 3 False -2147483648 0 0} {a19 4 False -32769 0 32767}\ +{a19 5 False -32768 0 32768} {a19 6 False -129 0 65407} {a19 7 False -128 0\ +65408} {a19 8 False -1 0 65535} {a19 9 True 0 0 0} {a19 10 True 1 0 1} {a19 11\ +True 127 0 127} {a19 12 True 128 0 128} {a19 13 True 255 0 255} {a19 14 True\ +256 0 256} {a19 15 True 32767 0 32767} {a19 16 True 32768 0 32768} {a19 17 True\ +65535 0 65535} {a19 18 False 65536 0 0} {a19 19 False 2147483647 0 65535} {a19\ +20 False 2147483648 0 0} {a19 21 False 4294967295 0 65535} {a19 22 False\ +4294967296 0 0} {a19 23 False 9223372036854775807 0 65535} {a19 24 False\ +9223372036854775808 0 0} {a19 25 False 18446744073709551615 0 0} {a19 26 False\ +18446744073709551616 0 0} {a20 0 False -9223372036854775809 0 0} {a20 1 False\ +-9223372036854775808 0 0} {a20 2 False -2147483649 0 255} {a20 3 False\ +-2147483648 0 0} {a20 4 False -32769 0 255} {a20 5 False -32768 0 0} {a20 6\ +False -129 0 127} {a20 7 False -128 0 128} {a20 8 False -1 0 255} {a20 9 True 0\ +0 0} {a20 10 True 1 0 1} {a20 11 True 127 0 127} {a20 12 True 128 0 128} {a20\ +13 True 255 0 255} {a20 14 False 256 0 0} {a20 15 False 32767 0 255} {a20 16\ +False 32768 0 0} {a20 17 False 65535 0 255} {a20 18 False 65536 0 0} {a20 19\ +False 2147483647 0 255} {a20 20 False 2147483648 0 0} {a20 21 False 4294967295\ +0 255} {a20 22 False 4294967296 0 0} {a20 23 False 9223372036854775807 0 255}\ +{a20 24 False 9223372036854775808 0 0} {a20 25 False 18446744073709551615 0 0}\ +{a20 26 False 18446744073709551616 0 0} {a21 0 False -9223372036854775809 0 0}\ +{a21 1 False -9223372036854775808 0 0} {a21 2 False -2147483649 0 -1} {a21 3\ +False -2147483648 0 0} {a21 4 False -32769 0 -1} {a21 5 False -32768 0 0} {a21\ +6 False -129 0 127} {a21 7 True -128 0 -128} {a21 8 True -1 0 -1} {a21 9 True 0\ +0 0} {a21 10 True 1 0 1} {a21 11 True 127 0 127} {a21 12 False 128 0 -128} {a21\ +13 False 255 0 -1} {a21 14 False 256 0 0} {a21 15 False 32767 0 -1} {a21 16\ +False 32768 0 0} {a21 17 False 65535 0 -1} {a21 18 False 65536 0 0} {a21 19\ +False 2147483647 0 -1} {a21 20 False 2147483648 0 0} {a21 21 False 4294967295 0\ +-1} {a21 22 False 4294967296 0 0} {a21 23 False 9223372036854775807 0 -1} {a21\ +24 False 9223372036854775808 0 0} {a21 25 False 18446744073709551615 0 0} {a21\ +26 False 18446744073709551616 0 0} {a22 0 False -9223372036854775809 0 0} {a22\ +1 False -9223372036854775808 0 0} {a22 2 False -2147483649 0 2147483647} {a22 3\ +False -2147483648 0 2147483648} {a22 4 False -32769 0 4294934527} {a22 5 False\ +-32768 0 4294934528} {a22 6 False -129 0 4294967167} {a22 7 False -128 0\ +4294967168} {a22 8 False -1 0 4294967295} {a22 9 True 0 0 0} {a22 10 True 1 0\ +1} {a22 11 True 127 0 127} {a22 12 True 128 0 128} {a22 13 True 255 0 255} {a22\ +14 True 256 0 256} {a22 15 True 32767 0 32767} {a22 16 True 32768 0 32768} {a22\ +17 True 65535 0 65535} {a22 18 True 65536 0 65536} {a22 19 True 2147483647 0\ +2147483647} {a22 20 True 2147483648 0 2147483648} {a22 21 True 4294967295 0\ +4294967295} {a22 22 False 4294967296 0 0} {a22 23 False 9223372036854775807 0\ +4294967295} {a22 24 False 9223372036854775808 0 0} {a22 25 False\ +18446744073709551615 0 0} {a22 26 False 18446744073709551616 0 0} {a23 0 False\ +-9223372036854775809 0 0} {a23 1 False -9223372036854775808 0 0} {a23 2 False\ +-2147483649 0 255} {a23 3 False -2147483648 0 0} {a23 4 False -32769 0 255}\ +{a23 5 False -32768 0 0} {a23 6 False -129 0 127} {a23 7 False -128 0 128} {a23\ +8 False -1 0 255} {a23 9 True 0 0 0} {a23 10 True 1 0 1} {a23 11 True 127 0\ +127} {a23 12 True 128 0 128} {a23 13 True 255 0 255} {a23 14 False 256 0 0}\ +{a23 15 False 32767 0 255} {a23 16 False 32768 0 0} {a23 17 False 65535 0 255}\ +{a23 18 False 65536 0 0} {a23 19 False 2147483647 0 255} {a23 20 False\ +2147483648 0 0} {a23 21 False 4294967295 0 255} {a23 22 False 4294967296 0 0}\ +{a23 23 False 9223372036854775807 0 255} {a23 24 False 9223372036854775808 0 0}\ +{a23 25 False 18446744073709551615 0 0} {a23 26 False 18446744073709551616 0 0}\ +{a24 0 False -9223372036854775809 0 0} {a24 1 False -9223372036854775808 0 0}\ +{a24 2 False -2147483649 0 65535} {a24 3 False -2147483648 0 0} {a24 4 False\ +-32769 0 32767} {a24 5 False -32768 0 32768} {a24 6 False -129 0 65407} {a24 7\ +False -128 0 65408} {a24 8 False -1 0 65535} {a24 9 True 0 0 0} {a24 10 True 1\ +0 1} {a24 11 True 127 0 127} {a24 12 True 128 0 128} {a24 13 True 255 0 255}\ +{a24 14 True 256 0 256} {a24 15 True 32767 0 32767} {a24 16 True 32768 0 32768}\ +{a24 17 True 65535 0 65535} {a24 18 False 65536 0 0} {a24 19 False 2147483647 0\ +65535} {a24 20 False 2147483648 0 0} {a24 21 False 4294967295 0 65535} {a24 22\ +False 4294967296 0 0} {a24 23 False 9223372036854775807 0 65535} {a24 24 False\ +9223372036854775808 0 0} {a24 25 False 18446744073709551615 0 0} {a24 26 False\ +18446744073709551616 0 0} {a25 0 False -9223372036854775809 0 0} {a25 1 False\ +-9223372036854775808 0 0} {a25 2 False -2147483649 0 2147483647} {a25 3 False\ +-2147483648 0 2147483648} {a25 4 False -32769 0 4294934527} {a25 5 False -32768\ +0 4294934528} {a25 6 False -129 0 4294967167} {a25 7 False -128 0 4294967168}\ +{a25 8 False -1 0 4294967295} {a25 9 True 0 0 0} {a25 10 True 1 0 1} {a25 11\ +True 127 0 127} {a25 12 True 128 0 128} {a25 13 True 255 0 255} {a25 14 True\ +256 0 256} {a25 15 True 32767 0 32767} {a25 16 True 32768 0 32768} {a25 17 True\ +65535 0 65535} {a25 18 True 65536 0 65536} {a25 19 True 2147483647 0\ +2147483647} {a25 20 True 2147483648 0 2147483648} {a25 21 True 4294967295 0\ +4294967295} {a25 22 False 4294967296 0 0} {a25 23 False 9223372036854775807 0\ +4294967295} {a25 24 False 9223372036854775808 0 0} {a25 25 False\ +18446744073709551615 0 0} {a25 26 False 18446744073709551616 0 0} {a26 0 False\ +-9223372036854775809 0 9223372036854775808} {a26 1 False -9223372036854775808 0\ +9223372036854775808} {a26 2 False -2147483649 0 18446744071562067967} {a26 3\ +False -2147483648 0 18446744071562067968} {a26 4 False -32769 0\ +18446744073709518847} {a26 5 False -32768 0 18446744073709518848} {a26 6 False\ +-129 0 18446744073709551487} {a26 7 False -128 0 18446744073709551488} {a26 8\ +False -1 0 18446744073709551615} {a26 9 True 0 0 0} {a26 10 True 1 0 1} {a26 11\ +True 127 0 127} {a26 12 True 128 0 128} {a26 13 True 255 0 255} {a26 14 True\ +256 0 256} {a26 15 True 32767 0 32767} {a26 16 True 32768 0 32768} {a26 17 True\ +65535 0 65535} {a26 18 True 65536 0 65536} {a26 19 True 2147483647 0\ +2147483647} {a26 20 True 2147483648 0 2147483648} {a26 21 True 4294967295 0\ +4294967295} {a26 22 True 4294967296 0 4294967296} {a26 23 True\ +9223372036854775807 0 9223372036854775807} {a26 24 True 9223372036854775808 0\ +9223372036854775808} {a26 25 False 18446744073709551615 0 9223372036854775808}\ +{a26 26 False 18446744073709551616 0 9223372036854775808} {a27 0 False\ +-9223372036854775809 0 9223372036854775808} {a27 1 False -9223372036854775808 0\ +9223372036854775808} {a27 2 False -2147483649 0 18446744071562067967} {a27 3\ +False -2147483648 0 18446744071562067968} {a27 4 False -32769 0\ +18446744073709518847} {a27 5 False -32768 0 18446744073709518848} {a27 6 False\ +-129 0 18446744073709551487} {a27 7 False -128 0 18446744073709551488} {a27 8\ +False -1 0 18446744073709551615} {a27 9 True 0 0 0} {a27 10 True 1 0 1} {a27 11\ +True 127 0 127} {a27 12 True 128 0 128} {a27 13 True 255 0 255} {a27 14 True\ +256 0 256} {a27 15 True 32767 0 32767} {a27 16 True 32768 0 32768} {a27 17 True\ +65535 0 65535} {a27 18 True 65536 0 65536} {a27 19 True 2147483647 0\ +2147483647} {a27 20 True 2147483648 0 2147483648} {a27 21 True 4294967295 0\ +4294967295} {a27 22 True 4294967296 0 4294967296} {a27 23 True\ +9223372036854775807 0 9223372036854775807} {a27 24 True 9223372036854775808 0\ +9223372036854775808} {a27 25 False 18446744073709551615 0 9223372036854775808}\ +{a27 26 False 18446744073709551616 0 9223372036854775808} {a28 0 False\ +-9223372036854775809 0 9223372036854775808} {a28 1 False -9223372036854775808 0\ +9223372036854775808} {a28 2 False -2147483649 0 18446744071562067967} {a28 3\ +False -2147483648 0 18446744071562067968} {a28 4 False -32769 0\ +18446744073709518847} {a28 5 False -32768 0 18446744073709518848} {a28 6 False\ +-129 0 18446744073709551487} {a28 7 False -128 0 18446744073709551488} {a28 8\ +False -1 0 18446744073709551615} {a28 9 True 0 0 0} {a28 10 True 1 0 1} {a28 11\ +True 127 0 127} {a28 12 True 128 0 128} {a28 13 True 255 0 255} {a28 14 True\ +256 0 256} {a28 15 True 32767 0 32767} {a28 16 True 32768 0 32768} {a28 17 True\ +65535 0 65535} {a28 18 True 65536 0 65536} {a28 19 True 2147483647 0\ +2147483647} {a28 20 True 2147483648 0 2147483648} {a28 21 True 4294967295 0\ +4294967295} {a28 22 True 4294967296 0 4294967296} {a28 23 True\ +9223372036854775807 0 9223372036854775807} {a28 24 True 9223372036854775808 0\ +9223372036854775808} {a28 25 False 18446744073709551615 0 9223372036854775808}\ +{a28 26 False 18446744073709551616 0 9223372036854775808} {a29 0 False\ +-9223372036854775809 0 0} {a29 1 False -9223372036854775808 0 0} {a29 2 False\ +-2147483649 0 255} {a29 3 False -2147483648 0 0} {a29 4 False -32769 0 255}\ +{a29 5 False -32768 0 0} {a29 6 False -129 0 127} {a29 7 False -128 0 128} {a29\ +8 False -1 0 255} {a29 9 True 0 0 0} {a29 10 True 1 0 1} {a29 11 True 127 0\ +127} {a29 12 True 128 0 128} {a29 13 True 255 0 255} {a29 14 False 256 0 0}\ +{a29 15 False 32767 0 255} {a29 16 False 32768 0 0} {a29 17 False 65535 0 255}\ +{a29 18 False 65536 0 0} {a29 19 False 2147483647 0 255} {a29 20 False\ +2147483648 0 0} {a29 21 False 4294967295 0 255} {a29 22 False 4294967296 0 0}\ +{a29 23 False 9223372036854775807 0 255} {a29 24 False 9223372036854775808 0 0}\ +{a29 25 False 18446744073709551615 0 0} {a29 26 False 18446744073709551616 0 0}\ +{a30 0 False -9223372036854775809 0 0} {a30 1 False -9223372036854775808 0 0}\ +{a30 2 False -2147483649 0 65535} {a30 3 False -2147483648 0 0} {a30 4 False\ +-32769 0 32767} {a30 5 False -32768 0 32768} {a30 6 False -129 0 65407} {a30 7\ +False -128 0 65408} {a30 8 False -1 0 65535} {a30 9 True 0 0 0} {a30 10 True 1\ +0 1} {a30 11 True 127 0 127} {a30 12 True 128 0 128} {a30 13 True 255 0 255}\ +{a30 14 True 256 0 256} {a30 15 True 32767 0 32767} {a30 16 True 32768 0 32768}\ +{a30 17 True 65535 0 65535} {a30 18 False 65536 0 0} {a30 19 False 2147483647 0\ +65535} {a30 20 False 2147483648 0 0} {a30 21 False 4294967295 0 65535} {a30 22\ +False 4294967296 0 0} {a30 23 False 9223372036854775807 0 65535} {a30 24 False\ +9223372036854775808 0 0} {a30 25 False 18446744073709551615 0 0} {a30 26 False\ +18446744073709551616 0 0} {a31 0 False -9223372036854775809 0 0} {a31 1 False\ +-9223372036854775808 0 0} {a31 2 False -2147483649 0 2147483647} {a31 3 False\ +-2147483648 0 2147483648} {a31 4 False -32769 0 4294934527} {a31 5 False -32768\ +0 4294934528} {a31 6 False -129 0 4294967167} {a31 7 False -128 0 4294967168}\ +{a31 8 False -1 0 4294967295} {a31 9 True 0 0 0} {a31 10 True 1 0 1} {a31 11\ +True 127 0 127} {a31 12 True 128 0 128} {a31 13 True 255 0 255} {a31 14 True\ +256 0 256} {a31 15 True 32767 0 32767} {a31 16 True 32768 0 32768} {a31 17 True\ +65535 0 65535} {a31 18 True 65536 0 65536} {a31 19 True 2147483647 0\ +2147483647} {a31 20 True 2147483648 0 2147483648} {a31 21 True 4294967295 0\ +4294967295} {a31 22 False 4294967296 0 0} {a31 23 False 9223372036854775807 0\ +4294967295} {a31 24 False 9223372036854775808 0 0} {a31 25 False\ +18446744073709551615 0 0} {a31 26 False 18446744073709551616 0 0} {a32 0 False\ +-9223372036854775809 0 9223372036854775808} {a32 1 False -9223372036854775808 0\ +9223372036854775808} {a32 2 False -2147483649 0 18446744071562067967} {a32 3\ +False -2147483648 0 18446744071562067968} {a32 4 False -32769 0\ +18446744073709518847} {a32 5 False -32768 0 18446744073709518848} {a32 6 False\ +-129 0 18446744073709551487} {a32 7 False -128 0 18446744073709551488} {a32 8\ +False -1 0 18446744073709551615} {a32 9 True 0 0 0} {a32 10 True 1 0 1} {a32 11\ +True 127 0 127} {a32 12 True 128 0 128} {a32 13 True 255 0 255} {a32 14 True\ +256 0 256} {a32 15 True 32767 0 32767} {a32 16 True 32768 0 32768} {a32 17 True\ +65535 0 65535} {a32 18 True 65536 0 65536} {a32 19 True 2147483647 0\ +2147483647} {a32 20 True 2147483648 0 2147483648} {a32 21 True 4294967295 0\ +4294967295} {a32 22 True 4294967296 0 4294967296} {a32 23 True\ +9223372036854775807 0 9223372036854775807} {a32 24 True 9223372036854775808 0\ +9223372036854775808} {a32 25 False 18446744073709551615 0 9223372036854775808}\ +{a32 26 False 18446744073709551616 0 9223372036854775808}}} + +############################################################################### + +runTest {test tkt-c010fa6584-1.2 {UInt32 parameter} -setup { + setupDb [set fileName tkt-c010fa6584-1.2.db] +} -body { + sql execute $db "CREATE TABLE t1(x UINT32);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 UInt32 0xFFFFFFFF] + + sql execute -execute scalar $db "SELECT x FROM t1;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{4294967295}} + +############################################################################### + +runTest {test tkt-c010fa6584-1.3 {UInt32 parameter (Int64)} -setup { + setupDb [set fileName tkt-c010fa6584-1.3.db] +} -body { + sql execute $db "CREATE TABLE t1(x INTEGER);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 UInt32 0xFFFFFFFF] + + sql execute -execute scalar $db "SELECT x FROM t1;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{-1}} + +############################################################################### + +runTest {test tkt-c010fa6584-1.4 {UInt32 parameter (Int64) w/flag} -setup { + setupDb [set fileName tkt-c010fa6584-1.4.db] "" "" "" BindUInt32AsInt64 +} -body { + sql execute $db "CREATE TABLE t1(x INTEGER);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 UInt32 0xFFFFFFFF] + + sql execute -execute scalar $db "SELECT x FROM t1;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{4294967295}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue ADDED Tests/tkt-e06c4caff3.eagle Index: Tests/tkt-e06c4caff3.eagle ================================================================== --- /dev/null +++ Tests/tkt-e06c4caff3.eagle @@ -0,0 +1,99 @@ +############################################################################### +# +# tkt-e06c4caff3.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require Eagle.Library +package require Eagle.Test + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-e06c4caff3-1.1 {NaN to NULL constraint failure} -setup { + setupDb [set fileName tkt-e06c4caff3-1.1.db] +} -body { + sql execute $db "CREATE TABLE t1(x REAL NOT NULL);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 Double [set NaN [object invoke Double NaN]]] +} -cleanup { + cleanupDb $fileName + + unset -nocomplain NaN db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ +-returnCodes 1 -match regexp -result [string map [list \n \r\n]\ +{^System\.Data\.SQLite\.SQLiteException \(0x80004005\): constraint failed +t1\.x may not be NULL +.*$}]} + +############################################################################### + +runTest {test tkt-e06c4caff3-1.2 {NaN to NULL} -setup { + setupDb [set fileName tkt-e06c4caff3-1.2.db] +} -body { + sql execute $db "CREATE TABLE t1(x REAL);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 Double [set NaN [object invoke Double NaN]]] + + sql execute -execute reader -format list $db "SELECT x FROM t1;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain NaN db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ +-result {}} + +############################################################################### + +runTest {test tkt-e06c4caff3-1.3 {NaN w/BindAllAsText} -setup { + setupDb [set fileName tkt-e06c4caff3-1.3.db] "" "" "" BindAllAsText +} -body { + sql execute $db "CREATE TABLE t1(x REAL NOT NULL);" + + list [sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 Double [set NaN [object invoke Double NaN]]]] \ + [sql execute -execute reader -format list $db "SELECT x FROM t1;"] +} -cleanup { + cleanupDb $fileName + + unset -nocomplain NaN db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{1 0}} + +############################################################################### + +runTest {test tkt-e06c4caff3-1.4 {NaN w/BindAllAsText & GetAllAsText} -setup { + setupDb [set fileName tkt-e06c4caff3-1.4.db] "" "" "" BindAndGetAllAsText +} -body { + sql execute $db "CREATE TABLE t1(x REAL NOT NULL);" + + list [sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 Double [set NaN [object invoke Double NaN]]]] \ + [sql execute -execute reader -format list $db "SELECT x FROM t1;"] +} -cleanup { + cleanupDb $fileName + + unset -nocomplain NaN db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +{1 NaN}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/version.eagle ================================================================== --- Tests/version.eagle +++ Tests/version.eagle @@ -27,11 +27,11 @@ # numbers must be manually kept synchronized with the version numbers for # the source code files, the built binaries, and the release packages. # set version(major) 1 set version(minor) 0 -set version(build) 84; # NOTE: Incremented with each release. +set version(build) 85; # NOTE: Incremented with each release. set version(revision) 0 ############################################################################### # ********************* END VOLATILE VERSION INFORMATION ********************** ############################################################################### @@ -175,10 +175,12 @@ [appendArgs[string map [list . \\.] $version(full)] \ ] \ [appendArgs[string map [list . \\.] $version(full)] \ ] \ [appendArgs[string map [list . \\.] $version(full)] \ + ] \ + [appendArgs[string map [list . \\.] $version(full)] \ ] \ [appendArgs Version= [string map [list . \\.] $version(full)] ,] \ [appendArgs "\; [format %03d $version(build)] "\;] \ [appendArgs AssemblyVersion\\(\" [string map [list . \\.] \ $version(full)] \"\\)] \ @@ -241,10 +243,11 @@ $version(full)] \"\\)]] set fileNames [list \ readme.htm \ SQLite.nuspec \ + SQLite.Beta.nuspec \ SQLite.MSIL.nuspec \ SQLite.x64.nuspec \ SQLite.x86.nuspec \ [file join Doc Extra dbfactorysupport.html] \ [file join Doc Extra welcome.html] \ Index: readme.htm ================================================================== --- readme.htm +++ readme.htm @@ -3,12 +3,12 @@ADO.NET SQLite Data Provider
-Version 1.0.84.0 January 9, 2013
-Using SQLite 3.7.15.2
+Version 1.0.85.0 March XX, 2013 (release scheduled)
+Using SQLite 3.7.16
Originally written by Robert Simpson
Released to the public domain, use at your own risk!
Official provider website: http://system.data.sqlite.org/
Legacy versions: http://sqlite.phxsoftware.com/
@@ -49,13 +49,13 @@ support both binary and cleartext password types.- Visual Studio design-time Support, works with all versions of Visual Studio - 2005/2008/2010. You can add a SQLite database to the Servers list, design - queries with the Query Designer, drag-and-drop tables onto a Typed DataSet, - etc. + 2005/2008/2010/2012. You can add a SQLite database to the Servers list, + design queries with the Query Designer, drag-and-drop tables onto a Typed + DataSet, etc.
+
Due to Visual Studio licensing restrictions, the Express Editions can no longer be supported. @@ -144,11 +144,11 @@ <configuration> <system.data> <DbProviderFactories> <remove invariant="System.Data.SQLite" /> <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" - type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.84.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" /> + type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.85.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" /> </DbProviderFactories> </system.data> </configuration>@@ -184,10 +184,35 @@
Version History
++ 1.0.85.0 - March XX, 2013 (release scheduled) +
+-
+
- Updated to SQLite 3.7.16. +
- Properly handle embedded NUL characters in parameter and column values. Fix for [3567020edf]. +
- Make use of the sqlite3_prepare_v2 function when applicable. +
- Check for a valid row in the SQLiteDataReader.GetValue method. +
- Implement processor architecture detection when running on the .NET Compact Framework (via P/Invoke). +
- Support automated testing when running on the .NET Compact Framework 2.0. +
- Skip checking loaded assemblies for types tagged with the SQLiteFunction attribute when the No_SQLiteFunctions environment variable is set. Pursuant to [e4c8121f7b]. +
- Add HexPassword connection string property to work around the inability to include a literal semicolon in a connection string property value. Pursuant to [1c456ae75f]. +
- Add static Execute method to the SQLiteCommand class. +
- Support custom connection pool implementations by adding the ISQLiteConnectionPool interface, the static SQLiteConnection.ConnectionPool property, and the static CreateHandle method in addition to modifying the SQLiteConnectionPool class. Pursuant to [393d954be0]. +
- Add public constructor to the SQLiteDataAdapter class that allows passing the parseViaFramework parameter to the SQLiteConnection constructor. +
- When built with the CHECK_STATE compile-time option, skip throwing exceptions from the SQLiteDataReader class when the object is being disposed. +
- Support automatic value conversions for columns with a declared type of BIGUINT, INTEGER8, INTEGER16, INTEGER32, INTEGER64, SMALLUINT, TINYSINT, UNSIGNEDINTEGER, UNSIGNEDINTEGER8, UNSIGNEDINTEGER16, UNSIGNEDINTEGER32, UNSIGNEDINTEGER64, INT8, INT16, INT32, INT64, UINT, UINT8, UINT16, UINT32, UINT64, or ULONG. +
- Add BindUInt32AsInt64 connection flag to force binding of UInt32 values as Int64 instead. Pursuant to [c010fa6584]. +
- Add BindAllAsText and GetAllAsText connection flags to force binding and returning of all values as text. +
- Remove AUTOINCREMENT from the column type name map. ** Potentially Incompatible Change ** +
- Avoid throwing overflow exceptions from the SQLite3.GetValue method for integral column types. Partial fix for [c010fa6584]. ** Potentially Incompatible Change ** +
- Use the legacy connection closing algorithm when built with the INTEROP_LEGACY_CLOSE compile-time option. +
- Support using the directory containing the primary managed-only assembly as the basis for native library pre-loading. +
- Still further enhancements to the build and test automation. +
1.0.84.0 - January 9, 2013
- Updated to SQLite 3.7.15.2. Index: test/AssemblyInfo.cs ================================================================== --- test/AssemblyInfo.cs +++ test/AssemblyInfo.cs @@ -36,7 +36,7 @@ // Major Version // Minor Version // Build Number // Revision // -[assembly: AssemblyVersion("1.0.84.0")] -[assembly: AssemblyFileVersion("1.0.84.0")] +[assembly: AssemblyVersion("1.0.85.0")] +[assembly: AssemblyFileVersion("1.0.85.0")] Index: test/app.config ================================================================== --- test/app.config +++ test/app.config @@ -1,8 +1,8 @@
- <root>\readme.htm
- <root>\SQLite.nuspec +
- <root>\SQLite.Beta.nuspec
- <root>\SQLite.MSIL.nuspec
- <root>\SQLite.x86.nuspec
- <root>\SQLite.x64.nuspec
- <root>\Doc\Extra\dbfactorysupport.html
- <root>\Doc\Extra\welcome.html Index: www/downloads.wiki ================================================================== --- www/downloads.wiki +++ www/downloads.wiki @@ -549,11 +549,11 @@
- + - + - + - + This setup package features the mixed-mode assembly and will install all the necessary runtime components and dependencies for the x86 version of the System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 - SP1 runtime for x86 is included. The .NET Framework 4.5 is required. + RTM runtime for x86 is included. The .NET Framework 4.5 is required.
This setup package is capable of installing the design-time components for Visual Studio 2012.
(sha1: 6954e6ee073a5965fbac99408e485bf68c1d35cf) @@ -569,11 +569,11 @@This setup package will install all the necessary runtime components and dependencies for the x86 version of the System.Data.SQLite 1.0.84.0 - (3.7.15.2) package. The Visual C++ 2012 SP1 runtime for x86 is included. + (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x86 is included. The .NET Framework 4.5 is required. @@ -594,11 +594,11 @@
(sha1: 6406842a3d916f2977f75ab5c6975debc232ce76)This setup package features the mixed-mode assembly and will install all the necessary runtime components and dependencies for the x64 version of the System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 - SP1 runtime for x64 is included. The .NET Framework 4.5 is required. + RTM runtime for x64 is included. The .NET Framework 4.5 is required. @@ -611,11 +611,11 @@
(sha1: 4ee62f74e45fd40ccaecbfe685fb6d08792c0cde)This setup package will install all the necessary runtime components and dependencies for the x64 version of the System.Data.SQLite 1.0.84.0 - (3.7.15.2) package. The Visual C++ 2012 SP1 runtime for x64 is included. + (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x64 is included. The .NET Framework 4.5 is required. @@ -881,11 +881,11 @@
(sha1: 0e24f71a7f2bcdb74bbd7afd807dafeafc64a81e)This binary package features the mixed-mode assembly and contains all the binaries for the x86 version of the System.Data.SQLite 1.0.84.0 - (3.7.15.2) package. The Visual C++ 2012 SP1 runtime for x86 and the .NET + (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x86 and the .NET Framework 4.5 are required. @@ -898,11 +898,11 @@ (1.70 MiB)
(sha1: 0dc8d3533375f8589c85d29d3270912666ef61e4)This binary package contains all the binaries for the x86 version of the - System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 SP1 + System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x86 and the .NET Framework 4.5 are required. @@ -922,11 +922,11 @@
(sha1: 1d88fdb25802d6b6ad34d7fa2da2da7003a49e53)This binary package features the mixed-mode assembly and contains all the binaries for the x64 version of the System.Data.SQLite 1.0.84.0 - (3.7.15.2) package. The Visual C++ 2012 SP1 runtime for x64 and the .NET + (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x64 and the .NET Framework 4.5 are required. @@ -939,11 +939,11 @@ (1.75 MiB)
(sha1: 33e865f0214fef3def4de4f18462832f9788ade4)This binary package contains all the binaries for the x64 version of the - System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 SP1 + System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x64 and the .NET Framework 4.5 are required. @@ -1215,11 +1215,11 @@
(sha1: a3970b48c78ecbf9fb92553d52f2af5cbfe01e25)This binary package features the mixed-mode assembly and contains all the binaries for the x86 version of the System.Data.SQLite 1.0.84.0 - (3.7.15.2) package. The Visual C++ 2012 SP1 runtime for x86 is statically + (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x86 is statically linked. The .NET Framework 4.5 is required. @@ -1232,11 +1232,11 @@ (2.03 MiB)
(sha1: 24a13024db3ef732cde9f128b43dcb0a9802dcdd)This binary package contains all the binaries for the x86 version of the - System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 SP1 + System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x86 is statically linked. The .NET Framework 4.5 is required. @@ -1257,11 +1257,11 @@
(sha1: 40f7633bbe527802f13cb653dc13354d871e4bb7)This binary package features the mixed-mode assembly and contains all the binaries for the x64 version of the System.Data.SQLite 1.0.84.0 - (3.7.15.2) package. The Visual C++ 2012 SP1 runtime for x64 is statically + (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x64 is statically linked. The .NET Framework 4.5 is required. @@ -1274,11 +1274,11 @@ (2.01 MiB)
(sha1: b41daf0cd84393651a8a68a6149dbd0193cdd838)This binary package contains all the binaries for the x64 version of the - System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 SP1 + System.Data.SQLite 1.0.84.0 (3.7.15.2) package. The Visual C++ 2012 RTM runtime for x64 is statically linked. The .NET Framework 4.5 is required. Index: www/faq.wiki ================================================================== --- www/faq.wiki +++ www/faq.wiki @@ -102,10 +102,16 @@ inside Visual Studio, why do I get a DllNotFoundException or a BadImageFormatException (for "sqlite3.dll" or "SQLite.Interop.dll") when trying to run or debug the application?
(sha1: 3516de01e430fbcda5625b8b52b76efd0a260875)
+- + Is this behavior a bug? -OR- Is there a quick way to + view the various lists of tickets for the System.Data.SQLite project? + +
@@ -484,5 +490,35 @@ "Win32" for 32-bit Windows and "x64" for 64-bit Windows). Therefore, it is good practice to double-check the selected build platform against the operating system prior to attempting to run a managed project in the solution.
+ +
+ ++ (21) Is this behavior a bug? -OR- Is there a quick way to view the various + lists of tickets for the System.Data.SQLite project? +
+ ++ If any behavior is observed that appears to be a bug, it may be reported via + the sqlite-users public + mailing list or by filing a ticket. Prior + to taking one of these steps, it is always prudent to check the various lists + of previously filed tickets to see if the behavior being observed has already + been reported and possibly fixed in a subsequent release. +
-
+
- + The list of tickets actively being worked on. + +
- + The list of tickets currently open. + +
- + The list of tickets fixed and then closed. + +
- + The list of tickets closed without needing a fix. + +
News Version History ++ 1.0.85.0 - March XX, 2013 (release scheduled) +
+-
+
- Updated to [http://www.sqlite.org/src/info/66d5f2b767|SQLite 3.7.16]. +
- Properly handle embedded NUL characters in parameter and column values. Fix for [3567020edf]. +
- Make use of the sqlite3_prepare_v2 function when applicable. +
- Check for a valid row in the SQLiteDataReader.GetValue method. +
- Implement processor architecture detection when running on the .NET Compact Framework (via P/Invoke). +
- Support automated testing when running on the .NET Compact Framework 2.0. +
- Skip checking loaded assemblies for types tagged with the SQLiteFunction attribute when the No_SQLiteFunctions environment variable is set. Pursuant to [e4c8121f7b]. +
- Add HexPassword connection string property to work around the inability to include a literal semicolon in a connection string property value. Pursuant to [1c456ae75f]. +
- Add static Execute method to the SQLiteCommand class. +
- Support custom connection pool implementations by adding the ISQLiteConnectionPool interface, the static SQLiteConnection.ConnectionPool property, and the static CreateHandle method in addition to modifying the SQLiteConnectionPool class. Pursuant to [393d954be0]. +
- Add public constructor to the SQLiteDataAdapter class that allows passing the parseViaFramework parameter to the SQLiteConnection constructor. +
- When built with the CHECK_STATE compile-time option, skip throwing exceptions from the SQLiteDataReader class when the object is being disposed. +
- Support automatic value conversions for columns with a declared type of BIGUINT, INTEGER8, INTEGER16, INTEGER32, INTEGER64, SMALLUINT, TINYSINT, UNSIGNEDINTEGER, UNSIGNEDINTEGER8, UNSIGNEDINTEGER16, UNSIGNEDINTEGER32, UNSIGNEDINTEGER64, INT8, INT16, INT32, INT64, UINT, UINT8, UINT16, UINT32, UINT64, or ULONG. +
- Add BindUInt32AsInt64 connection flag to force binding of UInt32 values as Int64 instead. Pursuant to [c010fa6584]. +
- Add BindAllAsText and GetAllAsText connection flags to force binding and returning of all values as text. +
- Remove AUTOINCREMENT from the column type name map. ** Potentially Incompatible Change ** +
- Avoid throwing overflow exceptions from the SQLite3.GetValue method for integral column types. Partial fix for [c010fa6584]. ** Potentially Incompatible Change ** +
- Use the legacy connection closing algorithm when built with the INTEROP_LEGACY_CLOSE compile-time option. +
- Support using the directory containing the primary managed-only assembly as the basis for native library pre-loading. +
- Still further enhancements to the build and test automation. +
1.0.84.0 - January 9, 2013
-
-
- Updated to SQLite 3.7.15.2. +
- Updated to [http://www.sqlite.org/releaselog/3_7_15_2.html|SQLite 3.7.15.2].
- Explicitly dispose of all SQLiteCommand objects managed by the DbDataAdapter class. Fix for [6434e23a0f].
- Add Cancel method to the SQLiteConnection class to interrupt a long running query.
- Improve thread safety of the SQLiteLog.LogMessage method.
Index: www/source.wiki ================================================================== --- www/source.wiki +++ www/source.wiki @@ -2,31 +2,31 @@
Follow these steps to obtain the latest (i.e. unreleased) source code for the System.Data.SQLite project. To obtain the latest officially released source code instead, refer to the [./downloads.wiki | downloads page]. Unless - otherwise noted, all steps need to be done in the order specified. + otherwise noted, all steps need to be performed in the order specified.
Assumptions & Prerequisites
- The string "<root>" represents the directory where you - would like a local working copy of source tree (a.k.a. the working check-out - directory) for the System.Data.SQLite project to reside (this should be a - completely empty directory). + The string "<root>" represents the directory where the + local working copy of the source tree (a.k.a. the working check-out directory) + for the System.Data.SQLite project is to reside. This should be a completely + empty directory.
The string "<repositories>" represents the directory - where you would like the local clone of the repository for the - System.Data.SQLite project (and potentially other projects) to reside. + where the local clone of the repository for the System.Data.SQLite project + (and potentially other projects) is to reside.
-Obtain & Install Fossil
+Obtain & Install Fossil Itself
The [http://www.fossil-scm.org | Fossil] open-source [http://en.wikipedia.org/wiki/Distributed_revision_control | distributed version control] system is a computer program that must be installed on your machine before you use it. @@ -53,15 +53,15 @@
- Enter the following command to create a complete clone (i.e. local copy) of the entire source code repository for the System.Data.SQLite project, including the entire check-in history: - fossil [http://www.fossil-scm.org/index.html/help/clone | clone] http://system.data.sqlite.org/ <repositories>\sds.fossil + fossil [http://www.fossil-scm.org/index.html/help/clone | clone] http://system.data.sqlite.org/ <repositories>/sds.fossil
- - The repository itself uses an + Please note that the repository itself uses an [http://www.fossil-scm.org/index.html/doc/trunk/www/fileformat.wiki | enduring file format] stored in a single [http://www.fossil-scm.org/index.html/doc/trunk/www/tech_overview.wiki | SQLite database file] with a particular schema.
@@ -86,11 +86,11 @@- Enter the following command to create a local working copy of all the files that are currently part of the System.Data.SQLite project: - fossil [http://www.fossil-scm.org/index.html/help/open | open] <repositories>\sds.fossil + fossil [http://www.fossil-scm.org/index.html/help/open | open] <repositories>/sds.fossil
- The local source tree should now be ready for use as described in the [./build.wiki | build procedures] and/or [./test.wiki | test procedures]. @@ -98,9 +98,9 @@
- In the future, to update the local working copy with the latest changes from the official System.Data.SQLite repository (i.e. instead of having to re-clone the entire thing), enter the following command from the same - directory where a working copy is located: + directory where the working copy is located: fossil [http://www.fossil-scm.org/index.html/help/update | update]
- Visual Studio design-time Support, works with all versions of Visual Studio - 2005/2008/2010. You can add a SQLite database to the Servers list, design - queries with the Query Designer, drag-and-drop tables onto a Typed DataSet, - etc. + 2005/2008/2010/2012. You can add a SQLite database to the Servers list, + design queries with the Query Designer, drag-and-drop tables onto a Typed + DataSet, etc.
Distributing the Binaries (Compact Framework)
Both the System.Data.SQLite.DLL and SQLite.Interop.XXX.DLL files must be deployed on the Compact Framework. The XXX is the build number of - the System.Data.SQLite library (e.g. "084"). The + the System.Data.SQLite library (e.g. "085"). The SQLite.Interop.XXX.DLL file is a fully native assembly compiled for the ARM processor, and System.Data.SQLite is the fully-managed Compact Framework assembly.