|
@@ -85,6 +85,7 @@ type
|
|
procedure AddFieldDefs(cursor: TSQLCursor;FieldDefs : TfieldDefs); override;
|
|
procedure AddFieldDefs(cursor: TSQLCursor;FieldDefs : TfieldDefs); override;
|
|
function Fetch(cursor : TSQLCursor) : boolean; override;
|
|
function Fetch(cursor : TSQLCursor) : boolean; override;
|
|
function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
|
|
function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
|
|
|
|
+ function GetBlobSize(blobHandle : TIsc_Blob_Handle) : LongInt;
|
|
function GetTransactionHandle(trans : TSQLHandle): pointer; override;
|
|
function GetTransactionHandle(trans : TSQLHandle): pointer; override;
|
|
function Commit(trans : TSQLHandle) : boolean; override;
|
|
function Commit(trans : TSQLHandle) : boolean; override;
|
|
function RollBack(trans : TSQLHandle) : boolean; override;
|
|
function RollBack(trans : TSQLHandle) : boolean; override;
|
|
@@ -786,7 +787,7 @@ var ParNr,SQLVarNr : integer;
|
|
with cursor as TIBCursor do
|
|
with cursor as TIBCursor do
|
|
begin
|
|
begin
|
|
TransactionHandle := aTransation.Handle;
|
|
TransactionHandle := aTransation.Handle;
|
|
- blobhandle := nil;
|
|
|
|
|
|
+ blobhandle := FB_API_NULLHANDLE;
|
|
if isc_create_blob(@FStatus[0], @FSQLDatabaseHandle, @TransactionHandle, @blobHandle, @blobId) <> 0 then
|
|
if isc_create_blob(@FStatus[0], @FSQLDatabaseHandle, @TransactionHandle, @blobHandle, @blobId) <> 0 then
|
|
CheckError('TIBConnection.CreateBlobStream', FStatus);
|
|
CheckError('TIBConnection.CreateBlobStream', FStatus);
|
|
|
|
|
|
@@ -1246,6 +1247,22 @@ begin
|
|
Move(Dbl, Buffer^, 8);
|
|
Move(Dbl, Buffer^, 8);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TIBConnection.GetBlobSize(blobHandle: TIsc_Blob_Handle): LongInt;
|
|
|
|
+var
|
|
|
|
+ iscInfoBlobTotalLength : byte;
|
|
|
|
+ blobInfo : array[0..50] of byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ iscInfoBlobTotalLength:=isc_info_blob_total_length;
|
|
|
|
+ if isc_blob_info(@Fstatus[0], @blobHandle, sizeof(iscInfoBlobTotalLength), pchar(@iscInfoBlobTotalLength), sizeof(blobInfo) - 2, pchar(@blobInfo[0])) <> 0 then
|
|
|
|
+ CheckError('isc_blob_info', FStatus);
|
|
|
|
+ if blobInfo[0] = iscInfoBlobTotalLength then
|
|
|
|
+ begin
|
|
|
|
+ result := isc_vax_integer(pchar(@blobInfo[3]), isc_vax_integer(pchar(@blobInfo[1]), 2));
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ CheckError('isc_blob_info', FStatus);
|
|
|
|
+end;
|
|
|
|
|
|
procedure TIBConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction);
|
|
procedure TIBConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction);
|
|
const
|
|
const
|
|
@@ -1255,6 +1272,7 @@ var
|
|
blobHandle : Isc_blob_Handle;
|
|
blobHandle : Isc_blob_Handle;
|
|
blobSegment : pointer;
|
|
blobSegment : pointer;
|
|
blobSegLen : word;
|
|
blobSegLen : word;
|
|
|
|
+ blobSize: LongInt;
|
|
TransactionHandle : pointer;
|
|
TransactionHandle : pointer;
|
|
blobId : PISC_QUAD;
|
|
blobId : PISC_QUAD;
|
|
ptr : Pointer;
|
|
ptr : Pointer;
|
|
@@ -1262,34 +1280,40 @@ begin
|
|
blobId := PISC_QUAD(@(ABlobBuf^.ConnBlobBuffer));
|
|
blobId := PISC_QUAD(@(ABlobBuf^.ConnBlobBuffer));
|
|
|
|
|
|
TransactionHandle := Atransaction.Handle;
|
|
TransactionHandle := Atransaction.Handle;
|
|
- blobHandle := nil;
|
|
|
|
|
|
+ blobHandle := FB_API_NULLHANDLE;
|
|
|
|
|
|
if isc_open_blob(@FStatus[0], @FSQLDatabaseHandle, @TransactionHandle, @blobHandle, blobId) <> 0 then
|
|
if isc_open_blob(@FStatus[0], @FSQLDatabaseHandle, @TransactionHandle, @blobHandle, blobId) <> 0 then
|
|
CheckError('TIBConnection.CreateBlobStream', FStatus);
|
|
CheckError('TIBConnection.CreateBlobStream', FStatus);
|
|
|
|
|
|
|
|
+ blobSize := GetBlobSize(blobHandle);
|
|
|
|
+
|
|
//For performance, read as much as we can, regardless of any segment size set in database.
|
|
//For performance, read as much as we can, regardless of any segment size set in database.
|
|
blobSegment := AllocMem(MAXBLOBSEGMENTSIZE);
|
|
blobSegment := AllocMem(MAXBLOBSEGMENTSIZE);
|
|
|
|
|
|
with ABlobBuf^.BlobBuffer^ do
|
|
with ABlobBuf^.BlobBuffer^ do
|
|
begin
|
|
begin
|
|
Size := 0;
|
|
Size := 0;
|
|
- while (isc_get_segment(@FStatus[0], @blobHandle, @blobSegLen, MAXBLOBSEGMENTSIZE, blobSegment) = 0) do
|
|
|
|
|
|
+ // Test for Size is a workaround for Win64 Firebird embedded crashing in isc_get_segment when entire blob is read.
|
|
|
|
+ while (Size < blobSize) and (isc_get_segment(@FStatus[0], @blobHandle, @blobSegLen, MAXBLOBSEGMENTSIZE, blobSegment) = 0) do
|
|
begin
|
|
begin
|
|
ReAllocMem(Buffer,Size+blobSegLen);
|
|
ReAllocMem(Buffer,Size+blobSegLen);
|
|
ptr := Buffer+Size;
|
|
ptr := Buffer+Size;
|
|
move(blobSegment^,ptr^,blobSegLen);
|
|
move(blobSegment^,ptr^,blobSegLen);
|
|
inc(Size,blobSegLen);
|
|
inc(Size,blobSegLen);
|
|
end;
|
|
end;
|
|
- end;
|
|
|
|
- freemem(blobSegment);
|
|
|
|
|
|
|
|
- if FStatus[1] = isc_segstr_eof then
|
|
|
|
- begin
|
|
|
|
- if isc_close_blob(@FStatus[0], @blobHandle) <> 0 then
|
|
|
|
- CheckError('TIBConnection.CreateBlobStream isc_close_blob', FStatus);
|
|
|
|
- end
|
|
|
|
- else
|
|
|
|
- CheckError('TIBConnection.CreateBlobStream isc_get_segment', FStatus);
|
|
|
|
|
|
+ freemem(blobSegment);
|
|
|
|
+
|
|
|
|
+ // Throwing the proper error on failure is more important than closing the blob:
|
|
|
|
+ // Test for Size is another workaround.
|
|
|
|
+ if (Size = blobSize) or (FStatus[1] = isc_segstr_eof) then
|
|
|
|
+ begin
|
|
|
|
+ if isc_close_blob(@FStatus[0], @blobHandle) <> 0 then
|
|
|
|
+ CheckError('TIBConnection.CreateBlobStream isc_close_blob', FStatus);
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ CheckError('TIBConnection.CreateBlobStream isc_get_segment', FStatus);
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TIBConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
|
|
function TIBConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
|