8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 17:23:03 +01:00

RDB$BLOB_UTIL system package. (#281)

* RDB$BLOB_UTIL system package.

* Do not checkout from engine when calling system packages.

* Remove usage of Attachment::SyncGuard in RDB$BLOB_UTIL.

* Fix Windows build.

* Fix RDB$BLOB_UTIL.SEEK.

* Fix crash.

* Rework changing routines and names for better fit after creation of BLOB_APPEND.

* Add RDB$BLOB_UTIL.IS_WRITABLE function.

* Misc.

* Fix documentation.

* Re-add and use RDB$BLOB_UTIL_HANDLE domain.

* Rename domain RDB$LONG_NUMBER to RDB$INTEGER.
This commit is contained in:
Adriano dos Santos Fernandes 2022-12-16 06:53:47 -03:00 committed by GitHub
parent 4c78ac43eb
commit 73c1ab807a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 672 additions and 6 deletions

View File

@ -58,6 +58,7 @@
<ClCompile Include="..\..\..\src\jrd\Attachment.cpp" />
<ClCompile Include="..\..\..\src\jrd\blb.cpp" />
<ClCompile Include="..\..\..\src\jrd\blob_filter.cpp" />
<ClCompile Include="..\..\..\src\jrd\BlobUtil.cpp" />
<ClCompile Include="..\..\..\src\jrd\btn.cpp" />
<ClCompile Include="..\..\..\src\jrd\btr.cpp" />
<ClCompile Include="..\..\..\src\jrd\builtin.cpp" />
@ -220,6 +221,7 @@
<ClInclude Include="..\..\..\src\jrd\blb_proto.h" />
<ClInclude Include="..\..\..\src\jrd\blf_proto.h" />
<ClInclude Include="..\..\..\src\jrd\blob_filter.h" />
<ClInclude Include="..\..\..\src\jrd\BlobUtil.h" />
<ClInclude Include="..\..\..\src\jrd\blp.h" />
<ClInclude Include="..\..\..\src\jrd\blr.h" />
<ClInclude Include="..\..\..\src\jrd\btn.h" />

View File

@ -216,6 +216,9 @@
<ClCompile Include="..\..\..\src\jrd\blob_filter.cpp">
<Filter>JRD files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\jrd\BlobUtil.cpp">
<Filter>JRD files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\jrd\btn.cpp">
<Filter>JRD files</Filter>
</ClCompile>
@ -680,6 +683,9 @@
<ClInclude Include="..\..\..\src\jrd\blob_filter.h">
<Filter>Header files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\jrd\BlobUtil.h">
<Filter>Header files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\jrd\blp.h">
<Filter>Header files</Filter>
</ClInclude>

View File

@ -0,0 +1,199 @@
# `RDB$BLOB_UTIL` package (FB 5.0)
This package exists to manipulate BLOBs in a way that standard Firebird functions, like `BLOB_APPEND` and `SUBSTRING` cannot do it or is very slow.
These routines operates on binary data directly, even for text BLOBs.
## Function `NEW_BLOB`
`RDB$BLOB_UTIL.NEW_BLOB` is used to create a new BLOB. It returns a BLOB suitable for data appending, like `BLOB_APPEND` does.
The advantage over `BLOB_APPEND` is that it's possible to set custom `SEGMENTED` and `TEMP_STORAGE` options.
`BLOB_APPEND` always creates BLOB in temporary storage. That may not be the best approach if the created BLOB is going to be stored in a permanent table, as it will require copy.
Returned BLOB from this function, even when `TEMP_STORAGE = FALSE` may be used with `BLOB_APPEND` for appending data.
Input parameter:
- `SEGMENTED` type `BOOLEAN NOT NULL`
- `TEMP_STORAGE` type `BOOLEAN NOT NULL`
Return type: `BLOB NOT NULL`.
## Function `OPEN_BLOB`
`RDB$BLOB_UTIL.OPEN_BLOB` is used to open an existing BLOB for read. It returns a handle (an integer bound to the transaction) suitable for use with others functions of this package, like `SEEK`, `READ_DATA` and `CLOSE_HANDLE`.
Input parameter:
- `BLOB` type `BLOB NOT NULL`
Return type: `INTEGER NOT NULL`.
## Function `IS_WRITABLE`
`RDB$BLOB_UTIL.IS_WRITABLE` returns `TRUE` when BLOB is suitable for data appending without copying using `BLOB_APPEND`.
Input parameter:
- `BLOB` type `BLOB NOT NULL`
Return type: `BOOLEAN NOT NULL`.
## Function `READ_DATA`
`RDB$BLOB_UTIL.READ_DATA` is used to read chunks of data of a BLOB handle opened with `RDB$BLOB_UTIL.OPEN_BLOB`. When the BLOB is fully read and there is no more data, it returns `NULL`.
If `LENGTH` is passed with a positive number, it returns a VARBINARY with its maximum length.
If `LENGTH` is `NULL` it returns just a segment of the BLOB with a maximum length of 32765.
Input parameters:
- `HANDLE` type `INTEGER NOT NULL`
- `LENGTH` type `INTEGER`
Return type: `VARBINARY(32767)`.
## Function `SEEK`
`RDB$BLOB_UTIL.SEEK` is used to set the position for the next `READ_DATA`. It returns the new position.
`MODE` may be 0 (from the start), 1 (from current position) or 2 (from end).
When `MODE` is 2, `OFFSET` should be zero or negative.
Input parameter:
- `HANDLE` type `INTEGER NOT NULL`
- `MODE` type `INTEGER NOT NULL`
- `OFFSET` type `INTEGER NOT NULL`
Return type: `INTEGER NOT NULL`.
## Procedure `CANCEL_BLOB`
`RDB$BLOB_UTIL.CANCEL_BLOB` is used to immediately release a temporary BLOB, like one created with `BLOB_APPEND`.
Note that if the same BLOB is used after cancel, using the same variable or another one with the same BLOB id reference, invalid blob id error will be raised.
## Procedure `CLOSE_HANDLE`
`RDB$BLOB_UTIL.CLOSE_HANDLE` is used to close a BLOB handle opened with `RDB$BLOB_UTIL.OPEN_BLOB`.
Not closed handles are closed automatically only in the transaction end.
Input parameter:
- `HANDLE` type `INTEGER NOT NULL`
# Examples
- Example 1: Create a BLOB in temporary space and return it in `EXECUTE BLOCK`:
```
execute block returns (b blob)
as
begin
-- Create a BLOB handle in the temporary space.
b = rdb$blob_util.new_blob(false, true);
-- Add chunks of data.
b = blob_append(b, '12345');
b = blob_append(b, '67');
suspend;
end
```
- Example 2: Open a BLOB and return chunks of it with `EXECUTE BLOCK`:
```
execute block returns (s varchar(10))
as
declare b blob = '1234567';
declare bhandle integer;
begin
-- Open the BLOB and get a BLOB handle.
bhandle = rdb$blob_util.open_blob(b);
-- Get chunks of data as string and return.
s = rdb$blob_util.read_data(bhandle, 3);
suspend;
s = rdb$blob_util.read_data(bhandle, 3);
suspend;
s = rdb$blob_util.read_data(bhandle, 3);
suspend;
-- Here EOF is found, so it returns NULL.
s = rdb$blob_util.read_data(bhandle, 3);
suspend;
-- Close the BLOB handle.
execute procedure rdb$blob_util.close_handle(bhandle);
end
```
- Example 3: Seek in a blob.
```
set term !;
execute block returns (s varchar(10))
as
declare b blob;
declare bhandle integer;
begin
-- Create a stream BLOB handle.
b = rdb$blob_util.new_blob(false, true);
-- Add data.
b = blob_append(b, '0123456789');
-- Open the BLOB.
bhandle = rdb$blob_util.open_blob(b);
-- Seek to 5 since the start.
rdb$blob_util.seek(bhandle, 0, 5);
s = rdb$blob_util.read_data(bhandle, 3);
suspend;
-- Seek to 2 since the start.
rdb$blob_util.seek(bhandle, 0, 2);
s = rdb$blob_util.read_data(bhandle, 3);
suspend;
-- Advance 2.
rdb$blob_util.seek(bhandle, 1, 2);
s = rdb$blob_util.read_data(bhandle, 3);
suspend;
-- Seek to -1 since the end.
rdb$blob_util.seek(bhandle, 2, -1);
s = rdb$blob_util.read_data(bhandle, 3);
suspend;
end!
set term ;!
```
- Example 4: Check if blobs are writable:
```
create table t(b blob);
set term !;
execute block returns (bool boolean)
as
declare b blob;
begin
b = blob_append(null, 'writable');
bool = rdb$blob_util.is_writable(b);
suspend;
insert into t (b) values ('not writable') returning b into b;
bool = rdb$blob_util.is_writable(b);
suspend;
end!
set term ;!
```

View File

@ -961,3 +961,5 @@ FB_IMPL_MSG(JRD, 959, quoted_str_miss, -901, "22", "024", "Missing terminating q
FB_IMPL_MSG(JRD, 960, wrong_shmem_ver, -902, "08", "006", "@1: inconsistent shared memory type/version; found @2, expected @3")
FB_IMPL_MSG(JRD, 961, wrong_shmem_bitness, -902, "08", "006", "@1-bit engine can't open database already opened by @2-bit engine")
FB_IMPL_MSG(JRD, 962, wrong_proc_plan, -281, "HY", "000", "Procedures cannot specify access type other than NATURAL in the plan")
FB_IMPL_MSG(JRD, 963, invalid_blob_util_handle, -402, "42", "000", "Invalid RDB$BLOB_UTIL handle")
FB_IMPL_MSG(JRD, 964, bad_temp_blob_id, -402, "42", "000", "Invalid temporary BLOB ID")

View File

@ -5302,6 +5302,8 @@ const
isc_wrong_shmem_ver = 335545280;
isc_wrong_shmem_bitness = 335545281;
isc_wrong_proc_plan = 335545282;
isc_invalid_blob_util_handle = 335545283;
isc_bad_temp_blob_id = 335545284;
isc_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;
isc_gfix_incmp_sw = 335740932;

298
src/jrd/BlobUtil.cpp Normal file
View File

@ -0,0 +1,298 @@
/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Adriano dos Santos Fernandes
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2020 Adriano dos Santos Fernandes <adrianosf@gmail.com>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "../jrd/BlobUtil.h"
#include "../jrd/blb.h"
#include "../jrd/tra.h"
using namespace Jrd;
using namespace Firebird;
namespace
{
blb* getBlobFromHandle(thread_db* tdbb, ISC_INT64 handle)
{
const auto transaction = tdbb->getTransaction();
blb* blob;
if (transaction->tra_blob_util_map.get(handle, blob))
return blob;
else
status_exception::raise(Arg::Gds(isc_invalid_blob_util_handle));
}
BlobIndex* getTempBlobIndexFromId(thread_db* tdbb, const bid& blobId)
{
if (blobId.bid_internal.bid_relation_id)
return nullptr;
const auto transaction = tdbb->getTransaction();
if (!transaction->tra_blobs->locate(blobId.bid_temp_id()))
status_exception::raise(Arg::Gds(isc_bad_segstr_id));
const auto blobIndex = &transaction->tra_blobs->current();
fb_assert(blobIndex->bli_blob_object);
return blobIndex;
}
}
namespace Jrd {
//--------------------------------------
IExternalResultSet* BlobUtilPackage::cancelBlobProcedure(ThrowStatusExceptionWrapper* status,
IExternalContext* context, const BlobMessage::Type* in, void*)
{
const auto tdbb = JRD_get_thread_data();
const auto transaction = tdbb->getTransaction();
const auto blobId = *(bid*) &in->blob;
if (const auto blobIdx = getTempBlobIndexFromId(tdbb, blobId))
{
if (blobIdx->bli_materialized)
status_exception::raise(Arg::Gds(isc_bad_segstr_id));
const auto blob = blobIdx->bli_blob_object;
blob->BLB_cancel(tdbb);
return nullptr;
}
else
status_exception::raise(Arg::Gds(isc_bad_temp_blob_id));
}
IExternalResultSet* BlobUtilPackage::closeHandleProcedure(ThrowStatusExceptionWrapper* status,
IExternalContext* context, const HandleMessage::Type* in, void*)
{
const auto tdbb = JRD_get_thread_data();
const auto transaction = tdbb->getTransaction();
const auto blob = getBlobFromHandle(tdbb, in->handle);
transaction->tra_blob_util_map.remove(in->handle);
blob->BLB_close(tdbb);
return nullptr;
}
void BlobUtilPackage::isWritableFunction(ThrowStatusExceptionWrapper* status,
IExternalContext* context, const BlobMessage::Type* in, BooleanMessage::Type* out)
{
const auto tdbb = JRD_get_thread_data();
const auto transaction = tdbb->getTransaction();
const auto blobId = *(bid*) &in->blob;
out->booleanNull = FB_FALSE;
if (const auto blobIdx = getTempBlobIndexFromId(tdbb, blobId))
{
if (!blobIdx->bli_materialized && (blobIdx->bli_blob_object->blb_flags & BLB_close_on_read))
{
out->boolean = FB_TRUE;
return;
}
}
out->boolean = FB_FALSE;
}
void BlobUtilPackage::newBlobFunction(ThrowStatusExceptionWrapper* status,
IExternalContext* context, const NewBlobInput::Type* in, BlobMessage::Type* out)
{
thread_db* tdbb = JRD_get_thread_data();
const auto transaction = tdbb->getTransaction();
const UCHAR bpb[] = {
isc_bpb_version1,
isc_bpb_type, 1, UCHAR(in->segmented ? isc_bpb_type_segmented : isc_bpb_type_stream),
isc_bpb_storage, 1, UCHAR(in->tempStorage ? isc_bpb_storage_temp : isc_bpb_storage_main)
};
bid id;
blb* blob = blb::create2(tdbb, transaction, &id, sizeof(bpb), bpb);
blob->blb_flags |= BLB_close_on_read;
out->blobNull = FB_FALSE;
out->blob.gds_quad_low = (ULONG) blob->getTempId();
out->blob.gds_quad_high = ((FB_UINT64) blob->getTempId()) >> 32;
}
void BlobUtilPackage::openBlobFunction(ThrowStatusExceptionWrapper* status,
IExternalContext* context, const BlobMessage::Type* in, HandleMessage::Type* out)
{
const auto tdbb = JRD_get_thread_data();
const auto transaction = tdbb->getTransaction();
const auto blobId = *(bid*) &in->blob;
const auto blob = blb::open(tdbb, transaction, &blobId);
transaction->tra_blob_util_map.put(++transaction->tra_blob_util_next, blob);
out->handleNull = FB_FALSE;
out->handle = transaction->tra_blob_util_next;
}
void BlobUtilPackage::seekFunction(ThrowStatusExceptionWrapper* status,
IExternalContext* context, const SeekInput::Type* in, SeekOutput::Type* out)
{
const auto tdbb = JRD_get_thread_data();
const auto transaction = tdbb->getTransaction();
const auto blob = getBlobFromHandle(tdbb, in->handle);
if (!(in->mode >= 0 && in->mode <= 2))
status_exception::raise(Arg::Gds(isc_random) << "Seek mode must be 0 (START), 1 (CURRENT) or 2 (END)");
if (in->mode == 2 && in->offset > 0) // 2 == from END
{
status_exception::raise(
Arg::Gds(isc_random) <<
"Argument OFFSET for RDB$BLOB_UTIL must be zero or negative when argument MODE is 2");
}
out->offsetNull = FB_FALSE;
out->offset = blob->BLB_lseek(in->mode, in->offset);
}
void BlobUtilPackage::readDataFunction(ThrowStatusExceptionWrapper* status,
IExternalContext* context, const ReadDataInput::Type* in, BinaryMessage::Type* out)
{
if (!in->lengthNull && in->length <= 0)
status_exception::raise(Arg::Gds(isc_random) << "Length must be NULL or greater than 0");
const auto tdbb = JRD_get_thread_data();
const auto transaction = tdbb->getTransaction();
const auto blob = getBlobFromHandle(tdbb, in->handle);
if (in->lengthNull)
out->data.length = blob->BLB_get_segment(tdbb, (UCHAR*) out->data.str, sizeof(out->data.str));
else
{
out->data.length = blob->BLB_get_data(tdbb, (UCHAR*) out->data.str,
MIN(in->length, sizeof(out->data.str)), false);
}
out->dataNull = out->data.length == 0 && (blob->blb_flags & BLB_eof) ? FB_TRUE : FB_FALSE;
}
//--------------------------------------
BlobUtilPackage::BlobUtilPackage(Firebird::MemoryPool& pool)
: SystemPackage(
pool,
"RDB$BLOB_UTIL",
ODS_13_1,
// procedures
{
SystemProcedure(
pool,
"CANCEL_BLOB",
SystemProcedureFactory<BlobMessage, VoidMessage, cancelBlobProcedure>(),
prc_executable,
// input parameters
{
{"BLOB", fld_blob, false}
},
// output parameters
{}
),
SystemProcedure(
pool,
"CLOSE_HANDLE",
SystemProcedureFactory<HandleMessage, VoidMessage, closeHandleProcedure>(),
prc_executable,
// input parameters
{
{"HANDLE", fld_butil_handle, false},
},
// output parameters
{}
)
},
// functions
{
SystemFunction(
pool,
"IS_WRITABLE",
SystemFunctionFactory<BlobMessage, BooleanMessage, isWritableFunction>(),
// parameters
{
{"BLOB", fld_blob, false}
},
{fld_bool, false}
),
SystemFunction(
pool,
"NEW_BLOB",
SystemFunctionFactory<NewBlobInput, BlobMessage, newBlobFunction>(),
// parameters
{
{"SEGMENTED", fld_bool, false},
{"TEMP_STORAGE", fld_bool, false}
},
{fld_blob, false}
),
SystemFunction(
pool,
"OPEN_BLOB",
SystemFunctionFactory<BlobMessage, HandleMessage, openBlobFunction>(),
// parameters
{
{"BLOB", fld_blob, false}
},
{fld_butil_handle, false}
),
SystemFunction(
pool,
"SEEK",
SystemFunctionFactory<SeekInput, SeekOutput, seekFunction>(),
// parameters
{
{"HANDLE", fld_butil_handle, false},
{"MODE", fld_integer, false},
{"OFFSET", fld_integer, false}
},
{fld_integer, false}
),
SystemFunction(
pool,
"READ_DATA",
SystemFunctionFactory<ReadDataInput, BinaryMessage, readDataFunction>(),
// parameters
{
{"HANDLE", fld_butil_handle, false},
{"LENGTH", fld_integer, true}
},
{fld_varybinary_max, true}
)
}
)
{
}
} // namespace Jrd

127
src/jrd/BlobUtil.h Normal file
View File

@ -0,0 +1,127 @@
/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Adriano dos Santos Fernandes
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2020 Adriano dos Santos Fernandes <adrianosf@gmail.com>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#ifndef JRD_BLOB_UTIL_H
#define JRD_BLOB_UTIL_H
#include "firebird.h"
#include "firebird/Message.h"
#include "../common/classes/fb_string.h"
#include "../common/classes/ImplementHelper.h"
#include "../common/status.h"
#include "../jrd/SystemPackages.h"
namespace Jrd {
class BlobUtilPackage : public SystemPackage
{
public:
BlobUtilPackage(Firebird::MemoryPool& pool);
private:
FB_MESSAGE(BinaryMessage, Firebird::ThrowStatusExceptionWrapper,
(FB_INTL_VARCHAR(MAX_VARY_COLUMN_SIZE, 0), data)
);
FB_MESSAGE(BlobMessage, Firebird::ThrowStatusExceptionWrapper,
(FB_BLOB, blob)
);
FB_MESSAGE(HandleMessage, Firebird::ThrowStatusExceptionWrapper,
(FB_INTEGER, handle)
);
FB_MESSAGE(BooleanMessage, Firebird::ThrowStatusExceptionWrapper,
(FB_BOOLEAN, boolean)
);
//----------
static Firebird::IExternalResultSet* cancelBlobProcedure(Firebird::ThrowStatusExceptionWrapper* status,
Firebird::IExternalContext* context, const BlobMessage::Type* in, void* out);
//----------
static Firebird::IExternalResultSet* closeHandleProcedure(Firebird::ThrowStatusExceptionWrapper* status,
Firebird::IExternalContext* context, const HandleMessage::Type* in, void* out);
//----------
static void isWritableFunction(Firebird::ThrowStatusExceptionWrapper* status,
Firebird::IExternalContext* context,
const BlobMessage::Type* in, BooleanMessage::Type* out);
//----------
FB_MESSAGE(NewBlobInput, Firebird::ThrowStatusExceptionWrapper,
(FB_BOOLEAN, segmented)
(FB_BOOLEAN, tempStorage)
);
static void newBlobFunction(Firebird::ThrowStatusExceptionWrapper* status,
Firebird::IExternalContext* context,
const NewBlobInput::Type* in, BlobMessage::Type* out);
//----------
static void openBlobFunction(Firebird::ThrowStatusExceptionWrapper* status,
Firebird::IExternalContext* context,
const BlobMessage::Type* in, HandleMessage::Type* out);
//----------
FB_MESSAGE(SeekInput, Firebird::ThrowStatusExceptionWrapper,
(FB_INTEGER, handle)
(FB_INTEGER, mode)
(FB_INTEGER, offset)
);
FB_MESSAGE(SeekOutput, Firebird::ThrowStatusExceptionWrapper,
(FB_INTEGER, offset)
);
static void seekFunction(Firebird::ThrowStatusExceptionWrapper* status,
Firebird::IExternalContext* context, const SeekInput::Type* in, SeekOutput::Type* out);
//----------
FB_MESSAGE(ReadDataInput, Firebird::ThrowStatusExceptionWrapper,
(FB_INTEGER, handle)
(FB_INTEGER, length)
);
static void readDataFunction(Firebird::ThrowStatusExceptionWrapper* status,
Firebird::IExternalContext* context,
const ReadDataInput::Type* in, BinaryMessage::Type* out);
//----------
static void makeBlobFunction(Firebird::ThrowStatusExceptionWrapper* status,
Firebird::IExternalContext* context,
const HandleMessage::Type* in, BlobMessage::Type* out);
};
} // namespace
#endif // JRD_BLOB_UTIL_H

View File

@ -22,6 +22,7 @@
#include "firebird.h"
#include "../jrd/SystemPackages.h"
#include "../jrd/BlobUtil.h"
#include "../jrd/TimeZone.h"
#include "../jrd/ProfilerManager.h"
@ -38,6 +39,7 @@ namespace
{
list->add(TimeZonePackage(pool));
list->add(ProfilerPackage(pool));
list->add(BlobUtilPackage(pool));
}
static InitInstance<SystemPackagesInit> INSTANCE;

View File

@ -227,3 +227,8 @@
FIELD(fld_short_description, nam_short_description, dtype_varying, 255 * METADATA_BYTES_PER_CHAR, dsc_text_type_metadata, NULL , true)
FIELD(fld_seconds_interval, nam_seconds_interval, dtype_long, sizeof(SLONG) , 0 , NULL , true)
FIELD(fld_prof_ses_id , nam_prof_ses_id , dtype_int64 , sizeof(SINT64) , 0 , NULL , true)
FIELD(fld_butil_handle , nam_butil_handle , dtype_long , sizeof(SLONG) , 0 , NULL , true)
FIELD(fld_blob , nam_blob , dtype_blob , BLOB_SIZE , isc_blob_untyped , NULL , true)
FIELD(fld_varybinary_max, nam_varbinary_max , dtype_varying , MAX_VARY_COLUMN_SIZE , 0 , NULL , true)
FIELD(fld_integer , nam_integer , dtype_long , sizeof(SLONG) , 0 , NULL , true)

View File

@ -1147,10 +1147,12 @@ namespace Jrd {
}
}
EngineCheckout(Attachment* att, const char* from, bool optional = false)
EngineCheckout(Attachment* att, const char* from, Type type = REQUIRED)
: m_tdbb(nullptr), m_from(from)
{
fb_assert(optional || att);
if (type != AVOID)
{
fb_assert(type == UNNECESSARY || att);
if (att && att->att_use_count)
{
@ -1158,6 +1160,7 @@ namespace Jrd {
m_ref->getSync()->leave();
}
}
}
~EngineCheckout()
{

View File

@ -466,3 +466,8 @@ NAME("MON$COMPILED_STATEMENT_ID", nam_mon_cmp_stmt_id)
NAME("RDB$SHORT_DESCRIPTION", nam_short_description)
NAME("RDB$SECONDS_INTERVAL", nam_seconds_interval)
NAME("RDB$PROFILE_SESSION_ID", nam_prof_ses_id)
NAME("RDB$BLOB_UTIL_HANDLE", nam_butil_handle)
NAME("RDB$BLOB", nam_blob)
NAME("RDB$VARBINARY_MAX", nam_varbinary_max)
NAME("RDB$INTEGER", nam_integer)

View File

@ -1210,6 +1210,17 @@ void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction, Jrd::TraceTr
if (!transaction->tra_outer)
{
for (auto& item : transaction->tra_blob_util_map)
{
auto blb = item.second;
// Let temporary blobs be cancelled in the block below.
if (!(blb->blb_flags & BLB_temporary))
blb->BLB_close(tdbb);
}
transaction->tra_blob_util_map.clear();
if (transaction->tra_blobs->getFirst())
{
while (true)

View File

@ -145,6 +145,7 @@ struct CallerName
};
typedef Firebird::GenericMap<Firebird::Pair<Firebird::NonPooled<SINT64, ULONG> > > ReplBlobMap;
typedef Firebird::GenericMap<Firebird::Pair<Firebird::NonPooled<SLONG, blb*> > > BlobUtilMap;
const int DEFAULT_LOCK_TIMEOUT = -1; // infinite
const char* const TRA_BLOB_SPACE = "fb_blob_";
@ -174,6 +175,7 @@ public:
tra_blobs(outer ? outer->tra_blobs : &tra_blobs_tree),
tra_fetched_blobs(p),
tra_repl_blobs(*p),
tra_blob_util_map(*p),
tra_arrays(NULL),
tra_deferred_job(NULL),
tra_resources(*p),
@ -269,6 +271,7 @@ public:
BlobIndexTree* tra_blobs; // pointer to actual list of active blobs
FetchedBlobIdTree tra_fetched_blobs; // list of fetched blobs
ReplBlobMap tra_repl_blobs; // map of blob IDs replicated in this transaction
BlobUtilMap tra_blob_util_map; // map of blob IDs for RDB$BLOB_UTIL package
ArrayField* tra_arrays; // Linked list of active arrays
Lock* tra_lock; // lock for transaction
Lock* tra_alter_db_lock; // lock for ALTER DATABASE statement(s)
@ -298,6 +301,7 @@ public:
SnapshotHandle tra_snapshot_handle;
CommitNumber tra_snapshot_number;
SortOwner tra_sorts;
SLONG tra_blob_util_next = 1;
EDS::Transaction *tra_ext_common;
//Transaction *tra_ext_two_phase;