Jone Word wrote: >There's an incorrect processing of BLOB field with BindDataSet, which results >in incorrect posting of this field into DB using TDataSetProvider & >TDISQLite** objects. This happens when BLOB data contains null (#0) >characters. BLOB is being >treated as a string! You are right, I did think of the implicit string conversions in my code. Unfortunately, the BLOB must be treated as a string, because this is what TBlobField.Old/NewValue returns. However, I did not pass it to the DB on a byte level and the #0 escaped my test suite. Many thanks for the catch! >I spent hours and hours finding why my BLOB object is always being truncated. I had also spend hours on the blob issue, and looked at it again just now. I still believe that there is an intricate problem in how TClientDataSet passes BLOBs to PSUpdateRecord (see source code comments). However, I took up your suggestion and modified the BindDataSet procedure using a dirty TDataSet cracker trick (below). There is also an acknowledged bug in Provider.pas: D2005, D2006, and D2007 execute PSUpdateRecord twice. This results in double database inserts. There is no workaround. See http://qc.codegear.com/wc/qcmain.aspx?d=26092 for disucsion and fix. I would be glad if you could test my new code and report if you meet any problems. If you do, I would also appreciate if you could send a test case which escaped my notice. I'd like to include the changes in the next version of DISQLite3, which I hope to release in just a few days. And yes: It will also include the new life-backup API! Ralf //------------------------------------------------------------------------------ type TDataSetCracker = class(TDataSet); { Binds TDataSet to a prepared statement. Only available fields will be bound - unavailable parameters stay untouched. } procedure BindDataSet(const AStmt: sqlite3_stmt; const ADataSet: TDataSet); var f: TField; i: Integer; Old: Boolean; p: PAnsiChar; SaveState: TDataSetState; Value: Variant; ValueBlob: RawByteString; begin for i := 1 to sqlite3_bind_parameter_count(AStmt) do begin p := sqlite3_bind_parameter_name(AStmt, i); // Only named parameters starting with ':'. if Assigned(p) and (p^ = ':') then begin Inc(p); Old := IsOld(p); if Old then Inc(p, 4); f := ADataSet.FindField(sqlite3_decode_utf8(p)); if Assigned(f) then if f.IsBlob then begin { Special blob handling necessary because all Values returned from TBlobField are Variants of type AnsiString. Additional TBlobField problem: There seems to be no way to resolve modifications to blob fields reliably. TBlobField.OldValue always returns an empty variant, TBlobField.NewValue an empty string for unmodified blobs. In addition, D2005 and later truncate strings with #0 chars during string -> variant conversation. We looked hard for a solution, but it seems best not to use TBlobFields in WHERE clauses with PSUpdateRecord. } if Old then SaveState := TDataSetCracker(ADataSet).SetTempState(dsOldValue) else SaveState := TDataSetCracker(ADataSet).SetTempState(dsNewValue); try ValueBlob := f.AsString; finally TDataSetCracker(ADataSet).restorestate(SaveState); end; sqlite3_check_stmt(sqlite3_bind_blob( AStmt, i, PAnsiChar(ValueBlob), Length(ValueBlob), sqlite3_Destroy_AnsiString), AStmt); Pointer(ValueBlob) := nil; end else begin if Old then Value := f.OldValue else begin Value := f.NewValue; if VarIsEmpty(Value) then Value := f.OldValue; end; if not VarIsEmpty(Value) then sqlite3_check_stmt(sqlite3_bind_variant( AStmt, i, Value), AStmt); end; end; end; end; //------------------------------------------------------------------------------ > So I've corrected the code so now it works OK. The following is what I did: > >OLD code (DISQLite3DataSet.pas): >-------------------------- > if f.IsBlob then > sqlite3_check_stmt(sqlite3_bind_str(AStmt, i, Utf8String(Value)), AStmt); >-------------------------- > >NEW code: >------------------------- > SValue: String; >.... > if f.IsBlob then > begin > if Old then > SValue := tblobfield(f).OldValue else > SValue := tblobfield(f).NewValue; > sqlite3_check_stmt(sqlite3_bind_blob(AStmt, i, > PAnsiChar(svalue), tblobfield(f).blobsize, SQLITE_TRANSIENT), AStmt); > end >---------------------------- _______________________________________________ Delphi Inspiration mailing list yunqa@xxxxxxxxxxxxx //www.freelists.org/list/yunqa