SaveBlobToFile (WIP)

This commit is contained in:
Paul Reeves 2023-02-03 11:43:15 +01:00
parent 8aac65cc81
commit 1ad24d3f71
3 changed files with 116 additions and 40 deletions

View File

@ -70,6 +70,14 @@
#include <limits>
#ifdef _WIN32
#include <windows.h>
#define SYSERROR() GetLastError()
#else
#include <errno.h>
#define SYSERROR() errno
#endif
using namespace Firebird;
@ -243,8 +251,7 @@ FB_UDR_EXECUTE_FUNCTION
throw Firebird::FbException(status, statusVector);
}
catch (...) {
// This generates an unrecognised C++ exception, which is insufficient.
throw std::runtime_error("Error writing stream to BLOB.");
throw std::runtime_error("Error writing file to BLOB.");
}
}
@ -267,7 +274,7 @@ engine udr;
FB_UDR_BEGIN_FUNCTION(MFK_SaveBlobToFile)
//BEGIN
FB_UDR_MESSAGE(InMessage,
(FB_CHAR(8191), afilename)
(FB_VARCHAR(8191), afilename)
(FB_BLOB, ablob)
);
@ -277,7 +284,8 @@ FB_UDR_MESSAGE(OutMessage,
AutoRelease<IAttachment> att;
AutoRelease<ITransaction> tra;
AutoRelease<IBlob> Blob;
AutoRelease<IBlob> blob;
ISC_STATUS_ARRAY statusVector = {0};
FB_UDR_EXECUTE_FUNCTION
@ -292,47 +300,68 @@ FB_UDR_EXECUTE_FUNCTION
* DOC NOTE - We do not test for existence of the file here!
*/
std::fstream File ( in->afilename.str , std::fstream::out | std::fstream::binary | std::fstream::app );
if (! File.is_open()) {
out->result = -2;
std::ofstream FileWriter ( in->afilename.str , std::ofstream::binary );
if (! FileWriter.is_open()) {
out->resultNull = FB_TRUE;
// Convert system error to a negative value and assign to result
out->result = ( SYSERROR() * -1 );
// important to set this to false here otherwise the error code will
// be hidden and NULL returned to the firebird engine. This is because
// the test for out->resultNull has precedence.
if ( out->result < 0 ) {
out->resultNull = FB_FALSE;
}
return;
}
Blob.reset(att->openBlob(status, tra, &in->ablob, 0, nullptr));
if (Blob == nullptr) {
att.reset(context->getAttachment(status));
tra.reset(context->getTransaction(status));
blob.reset(att->openBlob(status, tra, &in->ablob, 0, nullptr));
try {
if (blob == nullptr) {
out->resultNull = FB_TRUE;
out->result = -1;
return;
}
std::vector<char> Buffer (MaxSegmentSize, 0);
std::vector<std::uint8_t> Buffer (MaxSegmentSize, 0);
unsigned BytesRead = 0;
for (bool Eof = false; !Eof; )
{
switch (Blob->getSegment( status, MaxSegmentSize, Buffer.data(), &BytesRead))
switch ( blob->getSegment ( status, MaxSegmentSize, Buffer.data(), &BytesRead))
{
case IStatus::RESULT_OK:
case IStatus::RESULT_SEGMENT:
{
File.write( Buffer.data(), Buffer.size() );
FileWriter.write( (char *) Buffer.data(), Buffer.size() );
out->result += BytesRead;
continue;
}
default:
{
Blob->close( status ); // will close interface
Blob.release();
blob->close( status ); // will close interface
blob->release();
Eof = true;
break;
}
}
}
// If we have got this far then we have written the blob.
// out->resultNULL ***must*** be set to false or else the function will return NULL
out->resultNull = FB_FALSE;
Buffer.clear();
File.close();
FileWriter.close();
}
catch ( const FbException& error )
{
throw Firebird::FbException(status, statusVector);
}
catch (...) {
throw std::runtime_error("Error writing BLOB to file.");
}
}

View File

@ -20,15 +20,33 @@ execute procedure load_blob(3);
execute procedure load_blob(4);
commit;
select cast ( description as varchar(32) ) as DESCRIPTION
select cast ( description as varchar(16) ) as DESCRIPTION
, file_type
, cast (left(source_file,32) as varchar(32)) as source_file
, source_status
-- , source_bytes D_BIGINT
-- , target_file D_PATH
-- , target_status D_STATUS
-- , target_bytes D_BIGINT
-- , the_blob D_BLOB
, cast (left(target_file,32) as varchar(32)) as target_file
, target_status
, target_bytes
from test_blobs;
commit;
set term ^;
shell
mkdir --verbose /tmp/testudrkit
ls /tmp/testudrkit
^
set term ;^
execute procedure save_blob(1, '/tmp/testudrkit/fb.conf' );
execute procedure save_blob(2, '/tmp/testudrkit/fb.log' );
commit;
select cast ( description as varchar(16) ) as DESCRIPTION
, file_type
, cast (left(source_file,32) as varchar(32)) as source_file
, source_status
, cast (left(target_file,32) as varchar(32)) as target_file
, target_status
, cast (target_bytes as varchar(16) ) as target_bytes
from test_blobs;
commit;

View File

@ -85,10 +85,39 @@ begin
EXCEPTION E_BLOB_EXCEPTION 'Error updating test_blobs';
end
end ^
set term ;^
set term ^;
create or alter procedure save_blob ( a_test_blob_id D_ID, atarget_file D_PATH ) sql security definer
as
declare theblob D_BLOB;
declare result D_BIGINT;
declare status D_STATUS;
begin
select the_blob from test_blobs where test_blobs_id = :a_test_blob_id into :theblob;
select SaveBlobToFile( :atarget_file, :theblob ) from rdb$database into :result;
if ( result >= 0 ) then
status = 'W';
else
status = 'F';
update test_blobs
set target_file = :atarget_file
, target_bytes = :result
, target_status = :status
where test_blobs_id = :a_test_blob_id;
when any do begin
EXCEPTION E_BLOB_EXCEPTION 'Error updating test_blobs';
end
end ^
set term ;^