/******************************************************** * ADO.NET 2.0 Data Provider for SQLite Version 3.X * Written by Joe Mistachkin (joe@mistachkin.com) * * Released to the public domain, use at your own risk! ********************************************************/ using System.Collections; using System.Collections.Generic; using System.IO; using System.Globalization; using System.Runtime.InteropServices; namespace System.Data.SQLite { #region Session Extension Enumerations public enum SQLiteChangeSetConflictType { Data = 1, NotFound = 2, Conflict = 3, Constraint = 4, ForeignKey = 5 } /////////////////////////////////////////////////////////////////////////// public enum SQLiteChangeSetConflictResult { Omit = 0, Replace = 1, Abort = 2 } #endregion /////////////////////////////////////////////////////////////////////////// #region Session Extension Delegates public delegate bool SessionTableFilterCallback( object clientData, string name ); /////////////////////////////////////////////////////////////////////////// public delegate SQLiteChangeSetConflictResult SessionConflictCallback( object clientData, SQLiteChangeSetConflictType type, ISQLiteChangeSetMetadataItem item ); #endregion /////////////////////////////////////////////////////////////////////////// #region Session Extension Interfaces public interface ISQLiteChangeSet { ISQLiteChangeSet Invert(); ISQLiteChangeSet CombineWith(ISQLiteChangeSet changeSet); void Apply( SessionConflictCallback conflictCallback, object clientData ); void Apply( SessionConflictCallback conflictCallback, SessionTableFilterCallback tableFilterCallback, object clientData ); } /////////////////////////////////////////////////////////////////////////// public interface ISQLiteChangeGroup { void AddChangeSet(byte[] rawData); void AddChangeSet(Stream stream); void CreateChangeSet(ref byte[] rawData); void CreateChangeSet(Stream stream); } /////////////////////////////////////////////////////////////////////////// public interface ISQLiteChangeSetMetadataItem { string TableName { get; } int NumberOfColumns { get; } SQLiteAuthorizerActionCode OperationCode { get; } bool Indirect { get; } bool[] PrimaryKeyColumns { get; } int NumberOfForeignKeyConflicts { get; } SQLiteValue GetOldValue(int columnIndex); SQLiteValue GetNewValue(int columnIndex); SQLiteValue GetConflictValue(int columnIndex); } /////////////////////////////////////////////////////////////////////////// public interface ISQLiteSession { bool IsEnabled(); void SetToEnabled(); void SetToDisabled(); bool IsIndirect(); void SetToIndirect(); void SetToDirect(); bool IsEmpty(); void AttachTable(string name); void SetTableFilter( SessionTableFilterCallback callback, object clientData ); void CreateChangeSet(ref byte[] rawData); void CreateChangeSet(Stream stream); void CreatePatchSet(ref byte[] rawData); void CreatePatchSet(Stream stream); void LoadDifferencesFromTable( string fromDatabaseName, string tableName ); } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteChangeSetIterator Class internal abstract class SQLiteChangeSetIterator : IDisposable { #region Private Data private IntPtr iterator; #endregion /////////////////////////////////////////////////////////////////////// #region Protected Constructors protected SQLiteChangeSetIterator( IntPtr iterator ) { this.iterator = iterator; } #endregion /////////////////////////////////////////////////////////////////////// #region Private Methods internal void CheckHandle() { if (iterator == IntPtr.Zero) throw new InvalidOperationException("iterator is not open"); } /////////////////////////////////////////////////////////////////////// internal IntPtr GetHandle() { return iterator; } #endregion /////////////////////////////////////////////////////////////////////// #region Public Methods public bool Next() { CheckDisposed(); CheckHandle(); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_next( iterator); switch (rc) { case SQLiteErrorCode.Ok: { throw new SQLiteException(SQLiteErrorCode.Ok, "sqlite3changeset_next: unexpected result Ok"); } case SQLiteErrorCode.Row: { return true; } case SQLiteErrorCode.Done: { return false; } default: { throw new SQLiteException(rc, "sqlite3changeset_next"); } } } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteChangeSetIterator).Name); } #endif } /////////////////////////////////////////////////////////////////////// protected virtual void Dispose(bool disposing) { try { if (!disposed) { //if (disposing) //{ // //////////////////////////////////// // // dispose managed resources here... // //////////////////////////////////// //} ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// if (iterator != IntPtr.Zero) { UnsafeNativeMethods.sqlite3changeset_finalize( iterator); iterator = IntPtr.Zero; } } } finally { // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion /////////////////////////////////////////////////////////////////////// #region Destructor ~SQLiteChangeSetIterator() { Dispose(false); } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteMemoryChangeSetIterator Class internal sealed class SQLiteMemoryChangeSetIterator : SQLiteChangeSetIterator { #region Private Data private IntPtr pData; #endregion /////////////////////////////////////////////////////////////////////// #region Private Constructors private SQLiteMemoryChangeSetIterator( IntPtr pData, IntPtr iterator ) : base(iterator) { this.pData = pData; } #endregion /////////////////////////////////////////////////////////////////////// #region Static "Factory" Methods public static SQLiteMemoryChangeSetIterator Create( byte[] rawData ) { if (rawData == null) throw new ArgumentNullException("rawData"); SQLiteMemoryChangeSetIterator result = null; IntPtr pData = IntPtr.Zero; IntPtr iterator = IntPtr.Zero; try { int nData = 0; pData = SQLiteBytes.ToIntPtr(rawData, ref nData); if (pData == IntPtr.Zero) throw new SQLiteException(SQLiteErrorCode.NoMem, null); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_start( ref iterator, nData, pData); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_start"); result = new SQLiteMemoryChangeSetIterator(pData, iterator); } finally { if (result == null) { if (iterator != IntPtr.Zero) { UnsafeNativeMethods.sqlite3changeset_finalize( iterator); iterator = IntPtr.Zero; } if (pData != IntPtr.Zero) { SQLiteMemory.Free(pData); pData = IntPtr.Zero; } } } return result; } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteMemoryChangeSetIterator).Name); } #endif } /////////////////////////////////////////////////////////////////////// protected override void Dispose(bool disposing) { // // NOTE: Must dispose of the base class first (leaky abstraction) // because it contains the iterator handle, which must be // closed *prior* to freeing the underlying memory. // base.Dispose(disposing); try { if (!disposed) { //if (disposing) //{ // //////////////////////////////////// // // dispose managed resources here... // //////////////////////////////////// //} ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// if (pData != IntPtr.Zero) { SQLiteMemory.Free(pData); pData = IntPtr.Zero; } } } finally { // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteStreamChangeSetIterator Class internal sealed class SQLiteStreamChangeSetIterator : SQLiteChangeSetIterator { #region Private Constructors private SQLiteStreamChangeSetIterator( IntPtr iterator ) : base(iterator) { // do nothing. } #endregion /////////////////////////////////////////////////////////////////////// #region Static "Factory" Methods public static SQLiteStreamChangeSetIterator Create( Stream stream, SQLiteConnectionFlags flags ) { if (stream == null) throw new ArgumentNullException("stream"); SQLiteStreamChangeSetIterator result = null; IntPtr iterator = IntPtr.Zero; try { SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_start_strm( ref iterator, new SQLiteStreamAdapter(stream, flags).xInput, IntPtr.Zero); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_start_strm"); result = new SQLiteStreamChangeSetIterator(iterator); } finally { if (result == null) { if (iterator != IntPtr.Zero) { UnsafeNativeMethods.sqlite3changeset_finalize( iterator); iterator = IntPtr.Zero; } } } return result; } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteStreamChangeSetIterator).Name); } #endif } /////////////////////////////////////////////////////////////////////// protected override void Dispose(bool disposing) { try { if (!disposed) { //if (disposing) //{ // //////////////////////////////////// // // dispose managed resources here... // //////////////////////////////////// //} ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// } } finally { base.Dispose(disposing); // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteStreamAdapter Class internal sealed class SQLiteStreamAdapter : IDisposable { #region Private Data private Stream stream; private SQLiteConnectionFlags flags; #endregion /////////////////////////////////////////////////////////////////////// #region Public Constructors public SQLiteStreamAdapter( Stream stream, SQLiteConnectionFlags flags ) { this.stream = stream; this.flags = flags; } #endregion /////////////////////////////////////////////////////////////////////// #region Native Callback Methods public SQLiteErrorCode xInput( IntPtr context, IntPtr pData, ref int nData ) { try { if (stream == null) return SQLiteErrorCode.Misuse; if (nData > 0) { byte[] bytes = new byte[nData]; nData = stream.Read(bytes, 0, nData); Marshal.Copy(bytes, 0, pData, nData); } return SQLiteErrorCode.Ok; } catch (Exception e) { try { if ((flags & SQLiteConnectionFlags.LogCallbackException) == SQLiteConnectionFlags.LogCallbackException) { SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION, HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Caught exception in \"xInput\" method: {0}", e)); /* throw */ } } catch { // do nothing. } } return SQLiteErrorCode.IoErr_Read; } /////////////////////////////////////////////////////////////////////// public SQLiteErrorCode xOutput( IntPtr context, IntPtr pData, int nData ) { try { if (stream == null) return SQLiteErrorCode.Misuse; if (nData > 0) { byte[] bytes = new byte[nData]; Marshal.Copy(pData, bytes, 0, nData); stream.Write(bytes, 0, nData); } stream.Flush(); return SQLiteErrorCode.Ok; } catch (Exception e) { try { if ((flags & SQLiteConnectionFlags.LogCallbackException) == SQLiteConnectionFlags.LogCallbackException) { SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION, HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Caught exception in \"xOutput\" method: {0}", e)); /* throw */ } } catch { // do nothing. } } return SQLiteErrorCode.IoErr_Write; } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteStreamAdapter).Name); } #endif } /////////////////////////////////////////////////////////////////////// private /* protected virtual */ void Dispose(bool disposing) { try { if (!disposed) { if (disposing) { //////////////////////////////////// // dispose managed resources here... //////////////////////////////////// if (stream != null) stream = null; /* NOT OWNED */ } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// } } finally { // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion /////////////////////////////////////////////////////////////////////// #region Destructor ~SQLiteStreamAdapter() { Dispose(false); } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteSession Class public sealed class SQLiteSession : ISQLiteSession, IDisposable { #region Private Data private SQLiteConnectionHandle handle; private SQLiteConnectionFlags flags; private string databaseName; private IntPtr session; /////////////////////////////////////////////////////////////////////// private SessionTableFilterCallback tableFilterCallback; private object tableFilterClientData; #endregion /////////////////////////////////////////////////////////////////////// #region Private Constructors internal SQLiteSession( SQLiteConnectionHandle handle, SQLiteConnectionFlags flags, string databaseName ) { this.handle = handle; this.flags = flags; this.databaseName = databaseName; UnsafeNativeMethods.sqlite3session_create( handle, SQLiteString.GetUtf8BytesFromString(databaseName), ref session); } #endregion /////////////////////////////////////////////////////////////////////// #region Private Methods private void CheckHandle() { if (session == IntPtr.Zero) throw new InvalidOperationException("session is not open"); } /////////////////////////////////////////////////////////////////////// #region Native Callback Methods private int xFilter( IntPtr context, /* NOT USED */ byte[] tblName ) { return tableFilterCallback(tableFilterClientData, SQLiteString.GetStringFromUtf8Bytes(tblName)) ? 1 : 0; } #endregion #endregion /////////////////////////////////////////////////////////////////////// #region ISQLiteSession Members public bool IsEnabled() { CheckDisposed(); CheckHandle(); return UnsafeNativeMethods.sqlite3session_enable(session, -1) != 0; } /////////////////////////////////////////////////////////////////////// public void SetToEnabled() { CheckDisposed(); CheckHandle(); UnsafeNativeMethods.sqlite3session_enable(session, 1); } /////////////////////////////////////////////////////////////////////// public void SetToDisabled() { CheckDisposed(); CheckHandle(); UnsafeNativeMethods.sqlite3session_enable(session, 0); } /////////////////////////////////////////////////////////////////////// public bool IsIndirect() { CheckDisposed(); CheckHandle(); return UnsafeNativeMethods.sqlite3session_indirect(session, -1) != 0; } /////////////////////////////////////////////////////////////////////// public void SetToIndirect() { CheckDisposed(); CheckHandle(); UnsafeNativeMethods.sqlite3session_indirect(session, 1); } /////////////////////////////////////////////////////////////////////// public void SetToDirect() { CheckDisposed(); CheckHandle(); UnsafeNativeMethods.sqlite3session_indirect(session, 0); } /////////////////////////////////////////////////////////////////////// public bool IsEmpty() { CheckDisposed(); CheckHandle(); return UnsafeNativeMethods.sqlite3session_isempty(session) != 0; } /////////////////////////////////////////////////////////////////////// public void AttachTable(string name) { CheckDisposed(); CheckHandle(); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_attach( session, SQLiteString.GetUtf8BytesFromString(name)); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3session_attach"); } /////////////////////////////////////////////////////////////////////// public void SetTableFilter( SessionTableFilterCallback callback, object clientData ) { CheckDisposed(); CheckHandle(); this.tableFilterCallback = callback; this.tableFilterClientData = clientData; UnsafeNativeMethods.sqlite3session_table_filter( session, xFilter, IntPtr.Zero); } /////////////////////////////////////////////////////////////////////// public void CreateChangeSet( ref byte[] rawData ) { CheckDisposed(); CheckHandle(); IntPtr pData = IntPtr.Zero; try { int nData = 0; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_changeset( session, ref nData, ref pData); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3session_changeset"); rawData = SQLiteBytes.FromIntPtr(pData, nData); } finally { if (pData != IntPtr.Zero) { SQLiteMemory.Free(pData); pData = IntPtr.Zero; } } } /////////////////////////////////////////////////////////////////////// public void CreateChangeSet( Stream stream ) { CheckDisposed(); CheckHandle(); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_changeset_strm( session, new SQLiteStreamAdapter(stream, flags).xOutput, IntPtr.Zero); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3session_changeset_strm"); } /////////////////////////////////////////////////////////////////////// public void CreatePatchSet( ref byte[] rawData ) { CheckDisposed(); CheckHandle(); IntPtr pData = IntPtr.Zero; try { int nData = 0; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_patchset( session, ref nData, ref pData); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3session_patchset"); rawData = SQLiteBytes.FromIntPtr(pData, nData); } finally { if (pData != IntPtr.Zero) { SQLiteMemory.Free(pData); pData = IntPtr.Zero; } } } /////////////////////////////////////////////////////////////////////// public void CreatePatchSet( Stream stream ) { CheckDisposed(); CheckHandle(); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_patchset_strm( session, new SQLiteStreamAdapter(stream, flags).xOutput, IntPtr.Zero); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3session_patchset_strm"); } /////////////////////////////////////////////////////////////////////// public void LoadDifferencesFromTable( string fromDatabaseName, string tableName ) { CheckDisposed(); CheckHandle(); IntPtr pError = IntPtr.Zero; try { SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_diff( session, SQLiteString.GetUtf8BytesFromString(fromDatabaseName), SQLiteString.GetUtf8BytesFromString(tableName), ref pError); if (rc != SQLiteErrorCode.Ok) { string error = null; if (pError != IntPtr.Zero) { error = SQLiteString.StringFromUtf8IntPtr(pError); if (!String.IsNullOrEmpty(error)) { error = HelperMethods.StringFormat( CultureInfo.CurrentCulture, ": {0}", error); } } throw new SQLiteException(rc, HelperMethods.StringFormat( CultureInfo.CurrentCulture, "{0}{1}", "sqlite3session_diff", error)); } } finally { if (pError != IntPtr.Zero) { SQLiteMemory.Free(pError); pError = IntPtr.Zero; } } } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) throw new ObjectDisposedException(typeof(SQLiteSession).Name); #endif } /////////////////////////////////////////////////////////////////////// private /* protected virtual */ void Dispose(bool disposing) { try { if (!disposed) { //if (disposing) //{ // //////////////////////////////////// // // dispose managed resources here... // //////////////////////////////////// //} ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// if (session != IntPtr.Zero) { UnsafeNativeMethods.sqlite3session_delete(session); session = IntPtr.Zero; } } } finally { // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion /////////////////////////////////////////////////////////////////////// #region Destructor ~SQLiteSession() { Dispose(false); } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteMemoryChangeSet Class internal sealed class SQLiteMemoryChangeSet : ISQLiteChangeSet, IEnumerable, IDisposable { #region Private Data private byte[] rawData; private SQLiteConnectionHandle handle; private SQLiteConnectionFlags flags; #endregion /////////////////////////////////////////////////////////////////////// #region Private Constructors internal SQLiteMemoryChangeSet( byte[] rawData, SQLiteConnectionHandle handle, SQLiteConnectionFlags flags ) { this.rawData = rawData; this.handle= handle; this.flags = flags; } #endregion /////////////////////////////////////////////////////////////////////// #region Private Methods private void CheckRawData() { if (rawData == null) throw new InvalidOperationException("no change set data"); } #endregion /////////////////////////////////////////////////////////////////////// #region ISQLiteChangeSet Members public ISQLiteChangeSet Invert() { CheckDisposed(); CheckRawData(); IntPtr pInData = IntPtr.Zero; IntPtr pOutData = IntPtr.Zero; try { int nInData = 0; pInData = SQLiteBytes.ToIntPtr(rawData, ref nInData); int nOutData = 0; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_invert( nInData, pInData, ref nOutData, ref pOutData); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_invert"); byte[] newData = SQLiteBytes.FromIntPtr(pOutData, nOutData); return new SQLiteMemoryChangeSet(newData, handle, flags); } finally { if (pOutData != IntPtr.Zero) { SQLiteMemory.Free(pOutData); pOutData = IntPtr.Zero; } if (pInData != IntPtr.Zero) { SQLiteMemory.Free(pInData); pInData = IntPtr.Zero; } } } /////////////////////////////////////////////////////////////////////// public ISQLiteChangeSet CombineWith( ISQLiteChangeSet changeSet ) { CheckDisposed(); CheckRawData(); SQLiteMemoryChangeSet memoryChangeSet = changeSet as SQLiteMemoryChangeSet; if (memoryChangeSet == null) { throw new ArgumentException( "not a memory based change set", "changeSet"); } memoryChangeSet.CheckRawData(); IntPtr pInData1 = IntPtr.Zero; IntPtr pInData2 = IntPtr.Zero; IntPtr pOutData = IntPtr.Zero; try { int nInData1 = 0; pInData1 = SQLiteBytes.ToIntPtr(rawData, ref nInData1); int nInData2 = 0; pInData2 = SQLiteBytes.ToIntPtr( memoryChangeSet.rawData, ref nInData2); int nOutData = 0; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_concat( nInData1, pInData1, nInData2, pInData2, ref nOutData, ref pOutData); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_concat"); byte[] newData = SQLiteBytes.FromIntPtr(pOutData, nOutData); return new SQLiteMemoryChangeSet(newData, handle, flags); } finally { if (pOutData != IntPtr.Zero) { SQLiteMemory.Free(pOutData); pOutData = IntPtr.Zero; } if (pInData2 != IntPtr.Zero) { SQLiteMemory.Free(pInData2); pInData2 = IntPtr.Zero; } if (pInData1 != IntPtr.Zero) { SQLiteMemory.Free(pInData1); pInData1 = IntPtr.Zero; } } } /////////////////////////////////////////////////////////////////////// public void Apply( SessionConflictCallback conflictCallback, object clientData ) { CheckDisposed(); Apply(conflictCallback, null, clientData); } /////////////////////////////////////////////////////////////////////// public void Apply( SessionConflictCallback conflictCallback, SessionTableFilterCallback tableFilterCallback, object clientData ) { CheckDisposed(); CheckRawData(); if (conflictCallback == null) throw new ArgumentNullException("conflictCallback"); UnsafeNativeMethods.xSessionFilter xFilter = null; if (tableFilterCallback != null) { xFilter = new UnsafeNativeMethods.xSessionFilter( delegate(IntPtr context, byte[] tblName) { try { string name = SQLiteString.GetStringFromUtf8Bytes( tblName); return tableFilterCallback(clientData, name) ? 1 : 0; } catch (Exception e) { try { if ((flags & SQLiteConnectionFlags.LogCallbackException) == SQLiteConnectionFlags.LogCallbackException) { SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION, HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Caught exception in \"xFilter\" method: {0}", e)); /* throw */ } } catch { // do nothing. } } return 0; }); } UnsafeNativeMethods.xSessionConflict xConflict; xConflict = new UnsafeNativeMethods.xSessionConflict( delegate(IntPtr context, SQLiteChangeSetConflictType type, IntPtr iterator) { ISQLiteChangeSetMetadataItem item = null; try { return conflictCallback(clientData, type, item); } catch (Exception e) { try { if ((flags & SQLiteConnectionFlags.LogCallbackException) == SQLiteConnectionFlags.LogCallbackException) { SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION, HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Caught exception in \"xConflict\" method: {0}", e)); /* throw */ } } catch { // do nothing. } } return SQLiteChangeSetConflictResult.Abort; }); IntPtr pData = IntPtr.Zero; try { int nData = 0; pData = SQLiteBytes.ToIntPtr(rawData, ref nData); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_apply( handle, nData, pData, xFilter, xConflict, IntPtr.Zero); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_apply"); } finally { if (pData != IntPtr.Zero) { SQLiteMemory.Free(pData); pData = IntPtr.Zero; } } } #endregion /////////////////////////////////////////////////////////////////////// #region IEnumerable Members public IEnumerator GetEnumerator() { return new SQLiteMemoryChangeSetEnumerator(rawData); } #endregion /////////////////////////////////////////////////////////////////////// #region IEnumerable Members IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteMemoryChangeSet).Name); } #endif } /////////////////////////////////////////////////////////////////////// private /* protected virtual */ void Dispose(bool disposing) { try { if (!disposed) { if (disposing) { //////////////////////////////////// // dispose managed resources here... //////////////////////////////////// if (rawData != null) rawData = null; } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// } } finally { // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion /////////////////////////////////////////////////////////////////////// #region Destructor ~SQLiteMemoryChangeSet() { Dispose(false); } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteStreamChangeSet Class internal sealed class SQLiteStreamChangeSet : ISQLiteChangeSet, IEnumerable, IDisposable { #region Private Data private Stream inputStream; private Stream outputStream; private SQLiteConnectionHandle handle; private SQLiteConnectionFlags flags; #endregion /////////////////////////////////////////////////////////////////////// #region Private Constructors internal SQLiteStreamChangeSet( Stream inputStream, Stream outputStream, SQLiteConnectionHandle handle, SQLiteConnectionFlags flags ) { this.inputStream = inputStream; this.outputStream = outputStream; this.handle = handle; this.flags = flags; } #endregion /////////////////////////////////////////////////////////////////////// #region Private Methods private void CheckInputStream() { if (inputStream == null) { throw new InvalidOperationException( "input stream unavailable"); } } /////////////////////////////////////////////////////////////////////// private void CheckOutputStream() { if (outputStream == null) { throw new InvalidOperationException( "output stream unavailable"); } } #endregion /////////////////////////////////////////////////////////////////////// #region ISQLiteChangeSet Members public ISQLiteChangeSet Invert() { CheckDisposed(); CheckInputStream(); CheckOutputStream(); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_invert_strm( new SQLiteStreamAdapter(inputStream, flags).xInput, IntPtr.Zero, new SQLiteStreamAdapter(outputStream, flags).xOutput, IntPtr.Zero); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_invert_strm"); return null; } /////////////////////////////////////////////////////////////////////// public ISQLiteChangeSet CombineWith( ISQLiteChangeSet changeSet ) { CheckDisposed(); CheckInputStream(); CheckOutputStream(); SQLiteStreamChangeSet streamChangeSet = changeSet as SQLiteStreamChangeSet; if (streamChangeSet == null) { throw new ArgumentException( "not a stream based change set", "changeSet"); } streamChangeSet.CheckInputStream(); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_concat_strm( new SQLiteStreamAdapter(inputStream, flags).xInput, IntPtr.Zero, new SQLiteStreamAdapter(streamChangeSet.inputStream, streamChangeSet.flags).xInput, IntPtr.Zero, new SQLiteStreamAdapter(outputStream, flags).xOutput, IntPtr.Zero); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_concat_strm"); return null; } /////////////////////////////////////////////////////////////////////// public void Apply( SessionConflictCallback conflictCallback, object clientData ) { CheckDisposed(); Apply(conflictCallback, null, clientData); } /////////////////////////////////////////////////////////////////////// public void Apply( SessionConflictCallback conflictCallback, SessionTableFilterCallback tableFilterCallback, object clientData ) { CheckDisposed(); CheckInputStream(); UnsafeNativeMethods.xSessionFilter xFilter; xFilter = new UnsafeNativeMethods.xSessionFilter( delegate(IntPtr context, byte[] tblName) { try { string name = SQLiteString.GetStringFromUtf8Bytes( tblName); return tableFilterCallback(clientData, name) ? 1 : 0; } catch (Exception e) { try { if ((flags & SQLiteConnectionFlags.LogCallbackException) == SQLiteConnectionFlags.LogCallbackException) { SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION, HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Caught exception in \"xFilter\" method: {0}", e)); /* throw */ } } catch { // do nothing. } } return 0; }); UnsafeNativeMethods.xSessionConflict xConflict; xConflict = new UnsafeNativeMethods.xSessionConflict( delegate(IntPtr context, SQLiteChangeSetConflictType type, IntPtr iterator) { ISQLiteChangeSetMetadataItem item = null; try { return conflictCallback(clientData, type, item); } catch (Exception e) { try { if ((flags & SQLiteConnectionFlags.LogCallbackException) == SQLiteConnectionFlags.LogCallbackException) { SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION, HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Caught exception in \"xConflict\" method: {0}", e)); /* throw */ } } catch { // do nothing. } } return SQLiteChangeSetConflictResult.Abort; }); SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_apply_strm( handle, new SQLiteStreamAdapter(inputStream, flags).xInput, IntPtr.Zero, xFilter, xConflict, IntPtr.Zero); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_apply_strm"); } #endregion /////////////////////////////////////////////////////////////////////// #region IEnumerable Members public IEnumerator GetEnumerator() { return new SQLiteStreamChangeSetEnumerator(inputStream, flags); } #endregion /////////////////////////////////////////////////////////////////////// #region IEnumerable Members IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteStreamChangeSet).Name); } #endif } /////////////////////////////////////////////////////////////////////// private /* protected virtual */ void Dispose(bool disposing) { try { if (!disposed) { if (disposing) { //////////////////////////////////// // dispose managed resources here... //////////////////////////////////// if (outputStream != null) outputStream = null; /* NOT OWNED */ if (inputStream != null) inputStream = null; /* NOT OWNED */ } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// } } finally { // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion /////////////////////////////////////////////////////////////////////// #region Destructor ~SQLiteStreamChangeSet() { Dispose(false); } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteChangeSetEnumerator Class internal abstract class SQLiteChangeSetEnumerator : IEnumerator { #region Private Data private SQLiteChangeSetIterator iterator; #endregion /////////////////////////////////////////////////////////////////////// #region Public Constructors public SQLiteChangeSetEnumerator( SQLiteChangeSetIterator iterator ) { SetIterator(iterator); } #endregion /////////////////////////////////////////////////////////////////////// #region Private Methods private void CheckIterator() { if (iterator == null) throw new InvalidOperationException("iterator unavailable"); iterator.CheckHandle(); } /////////////////////////////////////////////////////////////////////// private void SetIterator( SQLiteChangeSetIterator iterator ) { this.iterator = iterator; } /////////////////////////////////////////////////////////////////////// private void CloseIterator() { if (iterator != null) { iterator.Dispose(); iterator = null; } } #endregion /////////////////////////////////////////////////////////////////////// #region Protected Methods protected void ResetIterator( SQLiteChangeSetIterator iterator ) { CloseIterator(); SetIterator(iterator); } #endregion /////////////////////////////////////////////////////////////////////// #region IEnumerator Members public ISQLiteChangeSetMetadataItem Current { get { CheckDisposed(); return new SQLiteChangeSetMetadataItem(iterator); } } #endregion /////////////////////////////////////////////////////////////////////// #region IEnumerator Members object Collections.IEnumerator.Current { get { CheckDisposed(); return Current; } } /////////////////////////////////////////////////////////////////////// public bool MoveNext() { CheckDisposed(); CheckIterator(); return iterator.Next(); } /////////////////////////////////////////////////////////////////////// public virtual void Reset() { CheckDisposed(); throw new NotImplementedException(); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteChangeSetEnumerator).Name); } #endif } /////////////////////////////////////////////////////////////////////// protected virtual void Dispose(bool disposing) { try { if (!disposed) { if (disposing) { //////////////////////////////////// // dispose managed resources here... //////////////////////////////////// CloseIterator(); } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// } } finally { // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion /////////////////////////////////////////////////////////////////////// #region Destructor ~SQLiteChangeSetEnumerator() { Dispose(false); } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteMemoryChangeSetEnumerator Class internal sealed class SQLiteMemoryChangeSetEnumerator : SQLiteChangeSetEnumerator { #region Private Data private byte[] rawData; #endregion /////////////////////////////////////////////////////////////////////// #region Public Constructors public SQLiteMemoryChangeSetEnumerator( byte[] rawData ) : base(SQLiteMemoryChangeSetIterator.Create(rawData)) { this.rawData = rawData; } #endregion /////////////////////////////////////////////////////////////////////// #region IEnumerator Overrides public override void Reset() { CheckDisposed(); ResetIterator(SQLiteMemoryChangeSetIterator.Create(rawData)); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteMemoryChangeSetEnumerator).Name); } #endif } /////////////////////////////////////////////////////////////////////// protected override void Dispose(bool disposing) { try { if (!disposed) { if (disposing) { //////////////////////////////////// // dispose managed resources here... //////////////////////////////////// } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// } } finally { base.Dispose(disposing); // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteStreamChangeSetEnumerator Class internal sealed class SQLiteStreamChangeSetEnumerator : SQLiteChangeSetEnumerator { #region Public Constructors public SQLiteStreamChangeSetEnumerator( Stream stream, SQLiteConnectionFlags flags ) : base(SQLiteStreamChangeSetIterator.Create(stream, flags)) { // do nothing. } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteStreamChangeSetEnumerator).Name); } #endif } /////////////////////////////////////////////////////////////////////// protected override void Dispose(bool disposing) { try { //if (!disposed) //{ // if (disposing) // { // //////////////////////////////////// // // dispose managed resources here... // //////////////////////////////////// // } // ////////////////////////////////////// // // release unmanaged resources here... // ////////////////////////////////////// //} } finally { base.Dispose(disposing); // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion } #endregion /////////////////////////////////////////////////////////////////////////// #region SQLiteChangeSetMetadataItem Class public sealed class SQLiteChangeSetMetadataItem : ISQLiteChangeSetMetadataItem, IDisposable { #region Private Data private SQLiteChangeSetIterator iterator; #endregion /////////////////////////////////////////////////////////////////////// #region Private Constructors internal SQLiteChangeSetMetadataItem( SQLiteChangeSetIterator iterator ) { this.iterator = iterator; } #endregion /////////////////////////////////////////////////////////////////////// #region Private Methods private void CheckIterator() { if (iterator == null) throw new InvalidOperationException("iterator unavailable"); iterator.CheckHandle(); } /////////////////////////////////////////////////////////////////////// private void PopulateOperationMetadata() { if ((tableName == null) || (numberOfColumns == null) || (operationCode == null) || (indirect == null)) { CheckIterator(); IntPtr pTblName = IntPtr.Zero; SQLiteAuthorizerActionCode op = SQLiteAuthorizerActionCode.None; int bIndirect = 0; int nColumns = 0; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_op( iterator.GetHandle(), ref pTblName, ref nColumns, ref op, ref bIndirect); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_op"); tableName = SQLiteString.StringFromUtf8IntPtr(pTblName); numberOfColumns = nColumns; operationCode = op; indirect = (bIndirect != 0); } } /////////////////////////////////////////////////////////////////////// private void PopulatePrimaryKeyColumns() { if (primaryKeyColumns == null) { CheckIterator(); IntPtr pPrimaryKeys = IntPtr.Zero; int nColumns = 0; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_pk( iterator.GetHandle(), ref pPrimaryKeys, ref nColumns); if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, "sqlite3changeset_pk"); byte[] bytes = SQLiteBytes.FromIntPtr(pPrimaryKeys, nColumns); if (bytes != null) { primaryKeyColumns = new bool[nColumns]; for (int index = 0; index < bytes.Length; index++) primaryKeyColumns[index] = (bytes[index] != 0); } } } /////////////////////////////////////////////////////////////////////// private void PopulateNumberOfForeignKeyConflicts() { if (numberOfForeignKeyConflicts == null) { CheckIterator(); int conflicts = 0; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_fk_conflicts( iterator.GetHandle(), ref conflicts); if (rc != SQLiteErrorCode.Ok) { throw new SQLiteException(rc, "sqlite3changeset_fk_conflicts"); } numberOfForeignKeyConflicts = conflicts; } } #endregion /////////////////////////////////////////////////////////////////////// #region ISQLiteChangeSetMetadataItem Members private string tableName; public string TableName { get { CheckDisposed(); PopulateOperationMetadata(); return tableName; } } /////////////////////////////////////////////////////////////////////// private int? numberOfColumns; public int NumberOfColumns { get { CheckDisposed(); PopulateOperationMetadata(); return (int)numberOfColumns; } } /////////////////////////////////////////////////////////////////////// private SQLiteAuthorizerActionCode? operationCode; public SQLiteAuthorizerActionCode OperationCode { get { CheckDisposed(); PopulateOperationMetadata(); return (SQLiteAuthorizerActionCode)operationCode; } } /////////////////////////////////////////////////////////////////////// private bool? indirect; public bool Indirect { get { CheckDisposed(); PopulateOperationMetadata(); return (bool)indirect; } } /////////////////////////////////////////////////////////////////////// private bool[] primaryKeyColumns; public bool[] PrimaryKeyColumns { get { CheckDisposed(); PopulatePrimaryKeyColumns(); return primaryKeyColumns; } } /////////////////////////////////////////////////////////////////////// private int? numberOfForeignKeyConflicts; public int NumberOfForeignKeyConflicts { get { CheckDisposed(); PopulateNumberOfForeignKeyConflicts(); return (int)numberOfForeignKeyConflicts; } } /////////////////////////////////////////////////////////////////////// public SQLiteValue GetOldValue( int columnIndex ) { CheckDisposed(); CheckIterator(); IntPtr pValue = IntPtr.Zero; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_old( iterator.GetHandle(), columnIndex, ref pValue); return SQLiteValue.FromIntPtr(pValue); } /////////////////////////////////////////////////////////////////////// public SQLiteValue GetNewValue(int columnIndex) { CheckDisposed(); CheckIterator(); IntPtr pValue = IntPtr.Zero; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_new( iterator.GetHandle(), columnIndex, ref pValue); return SQLiteValue.FromIntPtr(pValue); } /////////////////////////////////////////////////////////////////////// public SQLiteValue GetConflictValue(int columnIndex) { CheckDisposed(); CheckIterator(); IntPtr pValue = IntPtr.Zero; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_conflict( iterator.GetHandle(), columnIndex, ref pValue); return SQLiteValue.FromIntPtr(pValue); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) { throw new ObjectDisposedException( typeof(SQLiteChangeSetMetadataItem).Name); } #endif } /////////////////////////////////////////////////////////////////////// private /* protected virtual */ void Dispose(bool disposing) { try { if (!disposed) { if (disposing) { //////////////////////////////////// // dispose managed resources here... //////////////////////////////////// if (iterator != null) iterator = null; /* NOT OWNED */ } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// } } finally { // // NOTE: Everything should be fully disposed at this point. // disposed = true; } } #endregion /////////////////////////////////////////////////////////////////////// #region Destructor ~SQLiteChangeSetMetadataItem() { Dispose(false); } #endregion } #endregion }