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> #include <limits>
#ifdef _WIN32
#include <windows.h>
#define SYSERROR() GetLastError()
#else
#include <errno.h>
#define SYSERROR() errno
#endif
using namespace Firebird; using namespace Firebird;
@ -243,8 +251,7 @@ FB_UDR_EXECUTE_FUNCTION
throw Firebird::FbException(status, statusVector); throw Firebird::FbException(status, statusVector);
} }
catch (...) { catch (...) {
// This generates an unrecognised C++ exception, which is insufficient. throw std::runtime_error("Error writing file to BLOB.");
throw std::runtime_error("Error writing stream to BLOB.");
} }
} }
@ -267,7 +274,7 @@ engine udr;
FB_UDR_BEGIN_FUNCTION(MFK_SaveBlobToFile) FB_UDR_BEGIN_FUNCTION(MFK_SaveBlobToFile)
//BEGIN //BEGIN
FB_UDR_MESSAGE(InMessage, FB_UDR_MESSAGE(InMessage,
(FB_CHAR(8191), afilename) (FB_VARCHAR(8191), afilename)
(FB_BLOB, ablob) (FB_BLOB, ablob)
); );
@ -277,7 +284,8 @@ FB_UDR_MESSAGE(OutMessage,
AutoRelease<IAttachment> att; AutoRelease<IAttachment> att;
AutoRelease<ITransaction> tra; AutoRelease<ITransaction> tra;
AutoRelease<IBlob> Blob; AutoRelease<IBlob> blob;
ISC_STATUS_ARRAY statusVector = {0};
FB_UDR_EXECUTE_FUNCTION FB_UDR_EXECUTE_FUNCTION
@ -292,47 +300,68 @@ FB_UDR_EXECUTE_FUNCTION
* DOC NOTE - We do not test for existence of the file here! * 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 ); std::ofstream FileWriter ( in->afilename.str , std::ofstream::binary );
if (! File.is_open()) { if (! FileWriter.is_open()) {
out->result = -2; 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; return;
} }
Blob.reset(att->openBlob(status, tra, &in->ablob, 0, nullptr)); att.reset(context->getAttachment(status));
if (Blob == nullptr) { tra.reset(context->getTransaction(status));
out->result = -1; blob.reset(att->openBlob(status, tra, &in->ablob, 0, nullptr));
return; 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;
unsigned BytesRead = 0; for (bool Eof = false; !Eof; )
for (bool Eof = false; !Eof; )
{
switch (Blob->getSegment( status, MaxSegmentSize, Buffer.data(), &BytesRead))
{ {
case IStatus::RESULT_OK: switch ( blob->getSegment ( status, MaxSegmentSize, Buffer.data(), &BytesRead))
case IStatus::RESULT_SEGMENT:
{ {
File.write( Buffer.data(), Buffer.size() ); case IStatus::RESULT_OK:
out->result += BytesRead; case IStatus::RESULT_SEGMENT:
continue; {
} FileWriter.write( (char *) Buffer.data(), Buffer.size() );
default: out->result += BytesRead;
{ continue;
Blob->close( status ); // will close interface }
Blob.release(); default:
Eof = true; {
break; 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();
FileWriter.close();
}
catch ( const FbException& error )
{
throw Firebird::FbException(status, statusVector);
}
catch (...) {
throw std::runtime_error("Error writing BLOB to file.");
} }
Buffer.clear();
File.close();
} }

View File

@ -20,15 +20,33 @@ execute procedure load_blob(3);
execute procedure load_blob(4); execute procedure load_blob(4);
commit; commit;
select cast ( description as varchar(32) ) as DESCRIPTION select cast ( description as varchar(16) ) as DESCRIPTION
, file_type , file_type
, cast (left(source_file,32) as varchar(32)) as source_file , cast (left(source_file,32) as varchar(32)) as source_file
, source_status , source_status
-- , source_bytes D_BIGINT , cast (left(target_file,32) as varchar(32)) as target_file
-- , target_file D_PATH , target_status
-- , target_status D_STATUS , target_bytes
-- , target_bytes D_BIGINT
-- , the_blob D_BLOB
from test_blobs; from test_blobs;
commit; 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'; EXCEPTION E_BLOB_EXCEPTION 'Error updating test_blobs';
end 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 ^ end ^
set term ;^ set term ;^