Version History
-1.0.83.0 - December XX, 2012 (release scheduled)
+1.0.85.0 - March XX, 2013 (release scheduled)
+-
+
- Updated to SQLite 3.7.16. +
- 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 connection flag to force binding 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. +
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
- 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,112 @@ 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++) + result.AppendFormat("{0:x2}", array[index]); + + 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 +1889,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); @@ -1897,10 +2120,29 @@ throw new InvalidOperationException("Database connection not valid for getting last insert rowid."); return _sql.LastInsertRowId; } } + + ///. /// + /// This method causes any pending database operation to abort and return at + /// its earliest opportunity. This routine is typically called in response + /// to a user action such as pressing "Cancel" or Ctrl-C where the user wants + /// a long query operation to halt immediately. It is safe to call this + /// routine from any thread. However, it is not safe to call this routine + /// with a database connection that is closed or might close before this method + /// returns. + /// + public void Cancel() + { + CheckDisposed(); + + if (_sql == null) + throw new InvalidOperationException("Database connection not valid for query cancellation."); + + _sql.Cancel(); /* throw */ + } ////// Returns the number of rows changed by the last INSERT, UPDATE, or DELETE statement executed on /// this connection. /// @@ -2116,11 +2358,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. /// @@ -2146,11 +2388,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 - { - /// ///- /// 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 { @@ -170,11 +170,11 @@ nativestringlen++; } while (Marshal.ReadByte(nativestring, nativestringlen) != 0); } byte[] byteArray = new byte[nativestringlen]; - + Marshal.Copy(nativestring, byteArray, 0, nativestringlen); return _utf8.GetString(byteArray, 0, nativestringlen); } @@ -572,18 +572,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 +600,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 +632,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 +747,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 +760,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 +807,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 +923,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 +1077,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 +1121,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 +1201,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 +1286,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; @@ -82,16 +138,40 @@ { try { if (!disposed) { - //if (disposing) - //{ - // //////////////////////////////////// - // // dispose managed resources here... - // //////////////////////////////////// - //} + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + if (SelectCommand != null) + { + SelectCommand.Dispose(); + SelectCommand = null; + } + + if (InsertCommand != null) + { + InsertCommand.Dispose(); + InsertCommand = null; + } + + if (UpdateCommand != null) + { + UpdateCommand.Dispose(); + UpdateCommand = null; + } + + if (DeleteCommand != null) + { + DeleteCommand.Dispose(); + DeleteCommand = null; + } + } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// Index: System.Data.SQLite/SQLiteDataReader.cs ================================================================== --- System.Data.SQLite/SQLiteDataReader.cs +++ System.Data.SQLite/SQLiteDataReader.cs @@ -643,12 +643,12 @@ /// The name of the column to retrieve ///The int i of the column public override int GetOrdinal(string name) { CheckDisposed(); - CheckClosed(); - SQLiteCommand.Check(_command); + CheckClosed(); + if (_throwOnDisposed) SQLiteCommand.Check(_command); // // NOTE: First, check if the column name cache has been initialized yet. // If not, do it now. // @@ -851,12 +851,12 @@ } } internal DataTable GetSchemaTable(bool wantUniqueInfo, bool wantDefaultValue) { - CheckClosed(); - SQLiteCommand.Check(_command); + CheckClosed(); + 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 @@ -1218,12 +1218,12 @@ ///True if the command was successful and a new resultset is available, False otherwise. public override bool NextResult() { CheckDisposed(); - CheckClosed(); - SQLiteCommand.Check(_command); + CheckClosed(); + if (_throwOnDisposed) SQLiteCommand.Check(_command); SQLiteStatement stmt = null; int fieldCount; while (true) @@ -1344,12 +1344,12 @@ /// - Updated to SQLite 3.7.16. +
- 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 connection flag to force binding 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. +
- 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. +
- 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. 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.83.0")] -[assembly: AssemblyFileVersion("1.0.83.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 @@ -253,20 +253,109 @@
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. "083"). 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.
-Version 1.0.83.0 December XX, 2012 (release scheduled)
-Using SQLite 3.7.15.1
+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/
@@ -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.83.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>
@@ -185,11 +185,39 @@
Version History
- 1.0.83.0 - December XX, 2012 + 1.0.85.0 - March XX, 2013 (release scheduled) +
+-
+
+ 1.0.84.0 - January 9, 2013 +
+-
+
+ 1.0.83.0 - December 29, 2012