Index: SQLite.Interop/src/win/interop.c ================================================================== --- SQLite.Interop/src/win/interop.c +++ SQLite.Interop/src/win/interop.c @@ -389,15 +389,10 @@ pModule->xSavepoint = xSavepoint; pModule->xRelease = xRelease; pModule->xRollbackTo = xRollbackTo; return sqlite3_create_disposable_module(db, zName, pModule, pClientData, xDestroyModule); } - -SQLITE_API void WINAPI sqlite3_dispose_module_interop(void *pModule) -{ - sqlite3_dispose_module(pModule); -} #endif SQLITE_API int WINAPI sqlite3_bind_double_interop(sqlite3_stmt *stmt, int iCol, double *val) { return sqlite3_bind_double(stmt,iCol,*val); Index: System.Data.SQLite/SQLite3.cs ================================================================== --- System.Data.SQLite/SQLite3.cs +++ System.Data.SQLite/SQLite3.cs @@ -159,28 +159,11 @@ ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// #if INTEROP_VIRTUAL_TABLE - // - // NOTE: If any modules were created, attempt to dispose of - // them now. This code is designed to avoid throwing - // exceptions unless the Dispose method of the module - // itself throws an exception. - // - if (_modules != null) - { - foreach (KeyValuePair pair in _modules) - { - SQLiteModule module = pair.Value; - - if (module == null) - continue; - - module.Dispose(); - } - } + DisposeModules(); #endif Close(false); /* Disposing, cannot throw. */ disposed = true; @@ -190,10 +173,40 @@ { base.Dispose(disposing); } } #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + +#if INTEROP_VIRTUAL_TABLE + /// + /// This method attempts to dispose of all the derived + /// object instances currently associated with the native database connection. + /// + private void DisposeModules() + { + // + // NOTE: If any modules were created, attempt to dispose of + // them now. This code is designed to avoid throwing + // exceptions unless the Dispose method of the module + // itself throws an exception. + // + if (_modules != null) + { + foreach (KeyValuePair pair in _modules) + { + SQLiteModule module = pair.Value; + + if (module == null) + continue; + + module.Dispose(); + } + } + } +#endif /////////////////////////////////////////////////////////////////////////////////////////////// // It isn't necessary to cleanup any functions we've registered. If the connection // goes to the pool and is resurrected later, re-registered functions will overwrite the @@ -211,10 +224,14 @@ if (_usePool) { if (SQLiteBase.ResetConnection(_sql, _sql, canThrow)) { +#if INTEROP_VIRTUAL_TABLE + DisposeModules(); +#endif + SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion); #if !NET_COMPACT_20 && TRACE_CONNECTION Trace.WriteLine(String.Format("Close (Pool) Success: {0}", _sql)); #endif @@ -1709,61 +1726,20 @@ throw new SQLiteException("connection has an invalid handle"); SetLoadExtension(true); LoadExtension(UnsafeNativeMethods.SQLITE_DLL, "sqlite3_vtshim_init"); - IntPtr pName = IntPtr.Zero; - - try - { - pName = SQLiteString.Utf8IntPtrFromString(module.Name); - - UnsafeNativeMethods.sqlite3_module nativeModule = - module.CreateNativeModule(); - -#if !PLATFORM_COMPACTFRAMEWORK - if (UnsafeNativeMethods.sqlite3_create_disposable_module( - _sql, pName, ref nativeModule, IntPtr.Zero, - null) != IntPtr.Zero) -#elif !SQLITE_STANDARD - if (UnsafeNativeMethods.sqlite3_create_disposable_module_interop( - _sql, pName, module.CreateNativeModuleInterop(), - nativeModule.iVersion, nativeModule.xCreate, - nativeModule.xConnect, nativeModule.xBestIndex, - nativeModule.xDisconnect, nativeModule.xDestroy, - nativeModule.xOpen, nativeModule.xClose, - nativeModule.xFilter, nativeModule.xNext, - nativeModule.xEof, nativeModule.xColumn, - nativeModule.xRowId, nativeModule.xUpdate, - nativeModule.xBegin, nativeModule.xSync, - nativeModule.xCommit, nativeModule.xRollback, - nativeModule.xFindFunction, nativeModule.xRename, - nativeModule.xSavepoint, nativeModule.xRelease, - nativeModule.xRollbackTo, IntPtr.Zero, null) != IntPtr.Zero) -#else - throw new NotImplementedException(); -#endif -#if !PLATFORM_COMPACTFRAMEWORK || !SQLITE_STANDARD - { - if (_modules == null) - _modules = new Dictionary(); - - _modules.Add(module.Name, module); - } - else - { - throw new SQLiteException(GetLastError()); - } -#endif - } - finally - { - if (pName != IntPtr.Zero) - { - SQLiteMemory.Free(pName); - pName = IntPtr.Zero; - } + if (module.CreateDisposableModule(_sql)) + { + if (_modules == null) + _modules = new Dictionary(); + + _modules.Add(module.Name, module); + } + else + { + throw new SQLiteException(GetLastError()); } } /// /// Calls the native SQLite core library in order to cleanup the resources Index: System.Data.SQLite/SQLiteModule.cs ================================================================== --- System.Data.SQLite/SQLiteModule.cs +++ System.Data.SQLite/SQLiteModule.cs @@ -1102,50 +1102,55 @@ int offset = 0; int nConstraint = SQLiteMarshal.ReadInt32(pIndex, offset); - offset += sizeof(int); + offset += SQLiteMarshal.SizeOfStructInt(); IntPtr pConstraint = SQLiteMarshal.ReadIntPtr(pIndex, offset); offset += IntPtr.Size; int nOrderBy = SQLiteMarshal.ReadInt32(pIndex, offset); - offset += sizeof(int); + offset += SQLiteMarshal.SizeOfStructInt(); IntPtr pOrderBy = SQLiteMarshal.ReadIntPtr(pIndex, offset); index = new SQLiteIndex(nConstraint, nOrderBy); - int sizeOfConstraintType = Marshal.SizeOf(typeof( - UnsafeNativeMethods.sqlite3_index_constraint)); + Type indexConstraintType = typeof( + UnsafeNativeMethods.sqlite3_index_constraint); + + int sizeOfConstraintType = Marshal.SizeOf(indexConstraintType); for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++) { + IntPtr pOffset = SQLiteMarshal.IntPtrForOffset( + pConstraint, iConstraint * sizeOfConstraintType); + UnsafeNativeMethods.sqlite3_index_constraint constraint = - new UnsafeNativeMethods.sqlite3_index_constraint(); - - Marshal.PtrToStructure(SQLiteMarshal.IntPtrForOffset( - pConstraint, iConstraint * sizeOfConstraintType), - constraint); + (UnsafeNativeMethods.sqlite3_index_constraint) + Marshal.PtrToStructure(pOffset, indexConstraintType); index.Inputs.Constraints[iConstraint] = new SQLiteIndexConstraint(constraint); } - int sizeOfOrderByType = Marshal.SizeOf(typeof( - UnsafeNativeMethods.sqlite3_index_orderby)); + Type indexOrderByType = typeof( + UnsafeNativeMethods.sqlite3_index_orderby); + + int sizeOfOrderByType = Marshal.SizeOf(indexOrderByType); for (int iOrderBy = 0; iOrderBy < nOrderBy; iOrderBy++) { + IntPtr pOffset = SQLiteMarshal.IntPtrForOffset( + pOrderBy, iOrderBy * sizeOfOrderByType); + UnsafeNativeMethods.sqlite3_index_orderby orderBy = - new UnsafeNativeMethods.sqlite3_index_orderby(); - - Marshal.PtrToStructure(SQLiteMarshal.IntPtrForOffset( - pOrderBy, iOrderBy * sizeOfOrderByType), orderBy); + (UnsafeNativeMethods.sqlite3_index_orderby) + Marshal.PtrToStructure(pOffset, indexOrderByType); index.Inputs.OrderBys[iOrderBy] = new SQLiteIndexOrderBy(orderBy); } } @@ -1189,11 +1194,12 @@ return; if (nConstraint != index.Outputs.ConstraintUsages.Length) return; - offset += sizeof(int) + IntPtr.Size + sizeof(int) + IntPtr.Size; + offset += SQLiteMarshal.SizeOfStructInt() + IntPtr.Size + + SQLiteMarshal.SizeOfStructInt() + IntPtr.Size; IntPtr pConstraintUsage = SQLiteMarshal.ReadIntPtr(pIndex, offset); int sizeOfConstraintUsageType = Marshal.SizeOf(typeof( UnsafeNativeMethods.sqlite3_index_constraint_usage)); @@ -1206,21 +1212,18 @@ Marshal.StructureToPtr( constraintUsage, SQLiteMarshal.IntPtrForOffset( pConstraintUsage, iConstraint * sizeOfConstraintUsageType), false); - - index.Outputs.ConstraintUsages[iConstraint] = - new SQLiteIndexConstraintUsage(constraintUsage); } offset += IntPtr.Size; SQLiteMarshal.WriteInt32(pIndex, offset, index.Outputs.IndexNumber); - offset += sizeof(int); + offset += SQLiteMarshal.SizeOfStructInt(); SQLiteMarshal.WriteIntPtr(pIndex, offset, SQLiteString.Utf8IntPtrFromString(index.Outputs.IndexString)); offset += IntPtr.Size; @@ -1229,16 +1232,16 @@ // NOTE: We just allocated the IndexString field; therefore, we // need to set the NeedToFreeIndexString field to non-zero. // SQLiteMarshal.WriteInt32(pIndex, offset, 1); - offset += sizeof(int); + offset += SQLiteMarshal.SizeOfStructInt(); SQLiteMarshal.WriteInt32(pIndex, offset, index.Outputs.OrderByConsumed); - offset += sizeof(int); + offset += SQLiteMarshal.SizeOfStructInt(); SQLiteMarshal.WriteDouble(pIndex, offset, index.Outputs.EstimatedCost); } #endregion @@ -4105,10 +4108,25 @@ int offset ) { return new IntPtr(pointer.ToInt64() + offset); } + + /////////////////////////////////////////////////////////////////////// + + /// + /// Determines the size of a when it resides + /// inside of a native structure. + /// + /// + /// The size of the type, in bytes, when it + /// resides inside a native structure. + /// + public static int SizeOfStructInt() + { + return IntPtr.Size; + } #endregion /////////////////////////////////////////////////////////////////////// #region Marshal Read Helper Methods @@ -5164,10 +5182,19 @@ /// private UnsafeNativeMethods.sqlite3_module nativeModule; /////////////////////////////////////////////////////////////////////// + /// + /// This field is used to store a pointer to the native sqlite3_module + /// structure returned by the sqlite3_create_disposable_module + /// function. + /// + private IntPtr disposableModule; + + /////////////////////////////////////////////////////////////////////// + #if PLATFORM_COMPACTFRAMEWORK /// /// This field is used to hold the block of native memory that contains /// the native sqlite3_module structure associated with this object /// instance when running on the .NET Compact Framework. @@ -5227,10 +5254,80 @@ #endregion /////////////////////////////////////////////////////////////////////// #region Internal Methods + /// + /// Calls the native SQLite core library in order to create a new + /// disposable module containing the implementation of a virtual table. + /// + /// + /// The native database connection pointer to use. + /// + /// + /// Non-zero upon success. + /// + internal bool CreateDisposableModule( + IntPtr pDb + ) + { + if (disposableModule != IntPtr.Zero) + return true; + + IntPtr pName = IntPtr.Zero; + + try + { + pName = SQLiteString.Utf8IntPtrFromString(name); + + UnsafeNativeMethods.sqlite3_module nativeModule = + AllocateNativeModule(); + +#if !PLATFORM_COMPACTFRAMEWORK + disposableModule = + UnsafeNativeMethods.sqlite3_create_disposable_module( + pDb, pName, ref nativeModule, IntPtr.Zero, null); + + return (disposableModule != IntPtr.Zero); +#elif !SQLITE_STANDARD + + + disposableModule = + UnsafeNativeMethods.sqlite3_create_disposable_module_interop( + pDb, pName, AllocateNativeModuleInterop(), + nativeModule.iVersion, nativeModule.xCreate, + nativeModule.xConnect, nativeModule.xBestIndex, + nativeModule.xDisconnect, nativeModule.xDestroy, + nativeModule.xOpen, nativeModule.xClose, + nativeModule.xFilter, nativeModule.xNext, + nativeModule.xEof, nativeModule.xColumn, + nativeModule.xRowId, nativeModule.xUpdate, + nativeModule.xBegin, nativeModule.xSync, + nativeModule.xCommit, nativeModule.xRollback, + nativeModule.xFindFunction, nativeModule.xRename, + nativeModule.xSavepoint, nativeModule.xRelease, + nativeModule.xRollbackTo, IntPtr.Zero, null); + + return (disposableModule != IntPtr.Zero); +#else + throw new NotImplementedException(); +#endif + } + finally + { + if (pName != IntPtr.Zero) + { + SQLiteMemory.Free(pName); + pName = IntPtr.Zero; + } + } + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Private Methods /// /// Creates and returns the native sqlite_module structure using the /// configured (or default) /// interface implementation. /// @@ -5237,13 +5334,13 @@ /// /// The native sqlite_module structure using the configured (or /// default) interface /// implementation. /// - internal UnsafeNativeMethods.sqlite3_module CreateNativeModule() + private UnsafeNativeMethods.sqlite3_module AllocateNativeModule() { - return CreateNativeModule(GetNativeModuleImpl()); + return AllocateNativeModule(GetNativeModuleImpl()); } /////////////////////////////////////////////////////////////////////// #if PLATFORM_COMPACTFRAMEWORK @@ -5253,11 +5350,11 @@ /// object instance when running on the .NET Compact Framework. /// /// /// The native pointer to the native sqlite3_module structure. /// - internal IntPtr CreateNativeModuleInterop() + private IntPtr AllocateNativeModuleInterop() { if (pNativeModule == IntPtr.Zero) { // // HACK: No easy way to determine the size of the native @@ -5267,24 +5364,22 @@ // // There is one integer member. // There are 22 function pointer members. // pNativeModule = SQLiteMemory.Allocate( - sizeof(int) + (22 * IntPtr.Size)); + SQLiteMarshal.SizeOfStructInt() + (22 * IntPtr.Size)); if (pNativeModule == IntPtr.Zero) throw new OutOfMemoryException("sqlite3_module"); } return pNativeModule; } #endif - #endregion /////////////////////////////////////////////////////////////////////// - #region Private Methods /// /// Creates and returns the native sqlite_module structure using the /// specified interface /// implementation. /// @@ -5294,11 +5389,11 @@ /// /// /// The native sqlite_module structure using the specified /// interface implementation. /// - private UnsafeNativeMethods.sqlite3_module CreateNativeModule( + private UnsafeNativeMethods.sqlite3_module AllocateNativeModule( ISQLiteNativeModule module ) { nativeModule = new UnsafeNativeMethods.sqlite3_module(); nativeModule.iVersion = DefaultModuleVersion; @@ -5664,11 +5759,11 @@ // condition back to the caller; therefore, use the // logging facility instead. // try { - if (LogExceptions) + if (LogExceptionsNoThrow) { /* throw */ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION, String.Format(CultureInfo.CurrentCulture, "Caught exception in \"{0}\" method: {1}", @@ -5734,11 +5829,11 @@ } if (pVtab == IntPtr.Zero) return false; - int offset = IntPtr.Size + sizeof(int); + int offset = IntPtr.Size + SQLiteMarshal.SizeOfStructInt(); IntPtr pError = SQLiteMarshal.ReadIntPtr(pVtab, offset); if (pError != IntPtr.Zero) { SQLiteMemory.Free(pError); pError = IntPtr.Zero; @@ -5979,11 +6074,11 @@ offset += IntPtr.Size; SQLiteMarshal.WriteInt32(pVtab, offset, 0); - offset += sizeof(int); + offset += SQLiteMarshal.SizeOfStructInt(); SQLiteMarshal.WriteIntPtr(pVtab, offset, IntPtr.Zero); } /////////////////////////////////////////////////////////////////////// @@ -6418,10 +6513,42 @@ } #endregion /////////////////////////////////////////////////////////////////////// + #region Error Handling Properties + private bool logErrors; + /// + /// Returns or sets a boolean value indicating whether virtual table + /// errors should be logged using the class. + /// + protected virtual bool LogErrorsNoThrow + { + get { return logErrors; } + set { logErrors = value; } + } + + /////////////////////////////////////////////////////////////////////// + + private bool logExceptions; + /// + /// Returns or sets a boolean value indicating whether exceptions + /// caught in the + /// method, + /// method, and the + /// method should be logged using the + /// class. + /// + protected virtual bool LogExceptionsNoThrow + { + get { return logExceptions; } + set { logExceptions = value; } + } + #endregion + + /////////////////////////////////////////////////////////////////////// + #region Error Handling Helper Methods /// /// Arranges for the specified error message to be placed into the /// zErrMsg field of a sqlite3_vtab derived structure, freeing the /// existing error message, if any. @@ -6438,11 +6565,11 @@ protected virtual bool SetTableError( IntPtr pVtab, string error ) { - return SetTableError(this, pVtab, LogErrors, error); + return SetTableError(this, pVtab, LogErrorsNoThrow, error); } /////////////////////////////////////////////////////////////////////// /// @@ -6463,11 +6590,11 @@ protected virtual bool SetTableError( SQLiteVirtualTable table, string error ) { - return SetTableError(this, table, LogErrors, error); + return SetTableError(this, table, LogErrorsNoThrow, error); } /////////////////////////////////////////////////////////////////////// /// @@ -6488,11 +6615,11 @@ protected virtual bool SetCursorError( SQLiteVirtualTableCursor cursor, string error ) { - return SetCursorError(this, cursor, LogErrors, error); + return SetCursorError(this, cursor, LogErrorsNoThrow, error); } #endregion /////////////////////////////////////////////////////////////////////// @@ -6544,24 +6671,22 @@ #endregion /////////////////////////////////////////////////////////////////////// #region Public Properties - private bool logErrors; /// /// Returns or sets a boolean value indicating whether virtual table /// errors should be logged using the class. /// public virtual bool LogErrors { - get { CheckDisposed(); return logErrors; } - set { CheckDisposed(); logErrors = value; } + get { CheckDisposed(); return LogErrorsNoThrow; } + set { CheckDisposed(); LogErrorsNoThrow = value; } } /////////////////////////////////////////////////////////////////////// - private bool logExceptions; /// /// Returns or sets a boolean value indicating whether exceptions /// caught in the /// method, /// method, and the @@ -6568,12 +6693,12 @@ /// method should be logged using the /// class. /// public virtual bool LogExceptions { - get { CheckDisposed(); return logExceptions; } - set { CheckDisposed(); logExceptions = value; } + get { CheckDisposed(); return LogExceptionsNoThrow; } + set { CheckDisposed(); LogExceptionsNoThrow = value; } } #endregion /////////////////////////////////////////////////////////////////////// @@ -8032,35 +8157,18 @@ // release unmanaged resources here... ////////////////////////////////////// try { -#if !PLATFORM_COMPACTFRAMEWORK - UnsafeNativeMethods.sqlite3_dispose_module( - ref nativeModule); -#elif !SQLITE_STANDARD - if (pNativeModule != IntPtr.Zero) - { - try - { - UnsafeNativeMethods.sqlite3_dispose_module_interop( - pNativeModule); - } - finally - { - SQLiteMemory.Free(pNativeModule); - } - } -#else - throw new NotImplementedException(); -#endif + UnsafeNativeMethods.sqlite3_dispose_module(disposableModule); + disposableModule = IntPtr.Zero; } catch (Exception e) { try { - if (LogExceptions) + if (LogExceptionsNoThrow) { SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION, String.Format(CultureInfo.CurrentCulture, "Caught exception in \"Dispose\" method: {0}", e)); /* throw */ @@ -8069,10 +8177,20 @@ catch { // do nothing. } } +#if PLATFORM_COMPACTFRAMEWORK + finally + { + if (pNativeModule == IntPtr.Zero) + { + SQLiteMemory.Free(pNativeModule); + pNativeModule = IntPtr.Zero; + } + } +#endif disposed = true; } } #endregion Index: System.Data.SQLite/UnsafeNativeMethods.cs ================================================================== --- System.Data.SQLite/UnsafeNativeMethods.cs +++ System.Data.SQLite/UnsafeNativeMethods.cs @@ -1605,11 +1605,11 @@ #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else [DllImport(SQLITE_DLL)] #endif - internal static extern void sqlite3_dispose_module(ref sqlite3_module pModule); + internal static extern void sqlite3_dispose_module(IntPtr pModule); #endif #endregion /////////////////////////////////////////////////////////////////////////// @@ -1659,13 +1659,10 @@ xNext xNext, xEof xEof, xColumn xColumn, xRowId xRowId, xUpdate xUpdate, xBegin xBegin, xSync xSync, xCommit xCommit, xRollback xRollback, xFindFunction xFindFunction, xRename xRename, xSavepoint xSavepoint, xRelease xRelease, xRollbackTo xRollbackTo, IntPtr pClientData, xDestroyModule xDestroyModule); - - [DllImport(SQLITE_DLL)] - internal static extern void sqlite3_dispose_module_interop(IntPtr pModule); #endif // PLATFORM_COMPACTFRAMEWORK && !SQLITE_STANDARD #endregion ///////////////////////////////////////////////////////////////////////////