/// <summary> /// Compares two blob fields that reside in the two databases on which the blob reader writer object /// was opened (you need to use the dual databases constructor in order to use this method). /// </summary> /// <param name="tableName">The name of the table that contains the blob field</param> /// <param name="columnName">The name of the column that contains the blob field</param> /// <param name="rowid1">The row id in the first database where the blob field is located.</param> /// <param name="rowid2">The row id in the second database where the blob field is located.</param> /// <param name="comparer">An optional delegate to get progress notifications and be allowed to /// cancel the comparison process.</param> /// <returns>TRUE if the two blobs are the same, FALSE if they are different.</returns> public bool CompareBlobs(string tableName, string columnName, long rowid1, long rowid2, BlobProgressHandler comparer) { if (_disposed) { throw new ObjectDisposedException("Object was disposed."); } if (_conn2 == IntPtr.Zero) { throw new InvalidOperationException("You need to use a dual database constructor"); } tableName = SQLiteParser.Utils.Chop(tableName); columnName = SQLiteParser.Utils.Chop(columnName); try { byte[] bbytes1 = null; byte[] bbytes2 = null; _errorCode = (SQLiteErrorCode)sqlite3_blob_open(_conn1, "main", tableName, columnName, rowid1, 0, ref _blob1); if (_errorCode > SQLiteErrorCode.Ok) { bbytes1 = GetFieldBytes(_conn1, tableName, columnName, rowid1); } _errorCode = (SQLiteErrorCode)sqlite3_blob_open(_conn2, "main", tableName, columnName, rowid2, 0, ref _blob2); if (_errorCode > SQLiteErrorCode.Ok) { bbytes2 = GetFieldBytes(_conn2, tableName, columnName, rowid2); } if (bbytes1 != null && bbytes2 != null) { if (bbytes1.Length != bbytes2.Length) { return(false); } for (int i = 0; i < bbytes1.Length; i++) { if (bbytes1[i] != bbytes2[i]) { return(false); } } return(true); } int count1; if (bbytes1 != null) { count1 = bbytes1.Length; } else { count1 = sqlite3_blob_bytes(_blob1); } int count2; if (bbytes2 != null) { count2 = bbytes2.Length; } else { count2 = sqlite3_blob_bytes(_blob2); } if (count1 != count2) { return(false); } int count = count1; int offset = 0; while (count > 0) { int toread = (count > PAGESIZE ? PAGESIZE : count); if (bbytes1 == null) { _errorCode = (SQLiteErrorCode)sqlite3_blob_read(_blob1, _buffer1, toread, offset); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "sqlite3_blob_read failed"); } } if (bbytes2 == null) { _errorCode = (SQLiteErrorCode)sqlite3_blob_read(_blob2, _buffer2, toread, offset); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "sqlite3_blob_read failed"); } } if (bbytes1 == null && bbytes2 == null) { for (int i = 0; i < toread; i++) { if (_buffer1[i] != _buffer2[i]) { return(false); } } } else if (bbytes1 != null) { for (int i = 0; i < bbytes1.Length; i++) { if (bbytes1[i] != _buffer2[i]) { return(false); } } } else { for (int i = 0; i < bbytes2.Length; i++) { if (bbytes2[i] != _buffer1[i]) { return(false); } } } if (comparer != null) { bool cancel = false; comparer(offset + toread, count1, ref cancel); if (cancel) { throw new UserCancellationException(); } } offset += toread; count -= toread; } // while // Close the blob handles if (bbytes1 == null) { sqlite3_blob_close(_blob1); _blob1 = IntPtr.Zero; } if (bbytes2 == null) { sqlite3_blob_close(_blob2); _blob2 = IntPtr.Zero; } return(true); } finally { if (_blob1 != IntPtr.Zero) { sqlite3_blob_close(_blob1); _blob1 = IntPtr.Zero; } if (_blob2 != IntPtr.Zero) { sqlite3_blob_close(_blob2); _blob2 = IntPtr.Zero; } } // finally }
/// <summary> /// Copy a BLOB field from the source row to the target row /// </summary> /// <param name="targetdb">The path to the target row database</param> /// <param name="tableName">The name of the table where the row resides</param> /// <param name="columnName">The name of the BLOB column</param> /// <param name="fromRowId">The ID of the source row</param> /// <param name="toRowId">The ID of the target row</param> /// <param name="handler">An optional progress notifications handler</param> public void CopyBlob(string targetdb, string tableName, string columnName, long fromRowId, long toRowId, BlobProgressHandler handler) { if (_disposed) { throw new ObjectDisposedException("Object was disposed."); } if (_conn2 != IntPtr.Zero) { throw new InvalidOperationException("You need to use a non-dual database constructor"); } // Open conncetion to the target database as read-write _errorCode = (SQLiteErrorCode)sqlite3_open(targetdb, ref _conn2); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "can't open file: " + targetdb); } try { // Open source BLOB handle and compute its size _errorCode = (SQLiteErrorCode)sqlite3_blob_open(_conn1, "main", tableName, columnName, fromRowId, 0, ref _blob1); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "failed to open BLOB handle"); } int count1 = sqlite3_blob_bytes(_blob1); bool needsResizing = false; // Open target BLOB handle and check if it needs to be resized first _errorCode = (SQLiteErrorCode)sqlite3_blob_open(_conn2, "main", tableName, columnName, toRowId, 1, ref _blob2); if (_errorCode > SQLiteErrorCode.Ok) { needsResizing = true; } else { int count2 = sqlite3_blob_bytes(_blob2); if (count1 != count2) { needsResizing = true; } } // else // If the target BLOB needs resizing do it now. if (needsResizing) { // We'll need to resize the target BLOB field IntPtr stmt = IntPtr.Zero; IntPtr tail = IntPtr.Zero; // Close the BLOB handle if necessary if (_blob2 != IntPtr.Zero) { _errorCode = (SQLiteErrorCode)sqlite3_blob_close(_blob2); _blob2 = IntPtr.Zero; if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "can't close BLOB handle"); } } // Prepare SQL statement for zeroing the BLOB field in the correct size for the BLOB // length we want to write to it. _errorCode = (SQLiteErrorCode)sqlite3_prepare_v2(_conn2, "UPDATE " + SQLiteParser.Utils.QuoteIfNeeded(tableName) + " SET " + SQLiteParser.Utils.QuoteIfNeeded(columnName) + " = @blob WHERE RowID = @rowid", -1, ref stmt, ref tail); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "sqlite3_prepare_v2 failed"); } try { // Bind the BLOB parameter _errorCode = (SQLiteErrorCode)sqlite3_bind_zeroblob(stmt, 1, count1); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "failed to bind zero-blob"); } // Bind the RowID parameter _errorCode = (SQLiteErrorCode)sqlite3_bind_int64(stmt, 2, toRowId); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "failed to bind rowid"); } // Execute the prepared statement _errorCode = (SQLiteErrorCode)sqlite3_step(stmt); if (_errorCode > SQLiteErrorCode.Ok && _errorCode < SQLiteErrorCode.Row) { throw new SQLiteException(_errorCode, "failed to execute zeroblob command"); } } finally { // Finalize the prepared statement (we don't need it for more executions). sqlite3_finalize(stmt); } // catch // Re-open the target BLOB field _errorCode = (SQLiteErrorCode)sqlite3_blob_open(_conn2, "main", tableName, columnName, toRowId, 1, ref _blob2); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "failed to re-open target BLOB handle"); } } // if int count = count1; int offset = 0; while (count > 0) { int toread = (count > PAGESIZE ? PAGESIZE : count); // Read the BLOB data from the source database _errorCode = (SQLiteErrorCode)sqlite3_blob_read(_blob1, _buffer1, toread, offset); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "sqlite3_blob_read failed"); } // Write it to the target database _errorCode = (SQLiteErrorCode)sqlite3_blob_write(_blob2, _buffer1, toread, offset); if (_errorCode > SQLiteErrorCode.Ok) { throw new SQLiteException(_errorCode, "sqlite3_blob_read failed"); } offset += toread; count -= toread; if (handler != null) { bool cancel = false; handler(offset, count1, ref cancel); if (cancel) { throw new UserCancellationException(); } } } // while // Close the blob handles sqlite3_blob_close(_blob1); _blob1 = IntPtr.Zero; sqlite3_blob_close(_blob2); _blob2 = IntPtr.Zero; // Close the target database connection sqlite3_close(_conn2); _conn2 = IntPtr.Zero; } finally { if (_blob1 != IntPtr.Zero) { sqlite3_blob_close(_blob1); _blob1 = IntPtr.Zero; } if (_blob2 != IntPtr.Zero) { sqlite3_blob_close(_blob2); _blob2 = IntPtr.Zero; } if (_conn2 != IntPtr.Zero) { sqlite3_close(_conn2); _conn2 = IntPtr.Zero; } } // finally }