Skip to content

Commit c9426de

Browse files
author
Artyom Abakumov
committed
Add ability to use BLB_LSEEK with BLB_put_segment and BLB_put_data
1 parent dfb5435 commit c9426de

7 files changed

Lines changed: 1836 additions & 121 deletions

File tree

src/include/firebird/impl/msg/jrd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,4 +1020,4 @@ FB_IMPL_MSG(JRD, 1017, dsql_agg_non_agg_context, -104, "42", "000", "Aggregate f
10201020
FB_IMPL_MSG(JRD, 1018, dsql_agg_param_not_accum, -204, "42", "000", "Aggregate function input parameters may be referenced only in ON ACCUMULATE DO")
10211021
FB_IMPL_MSG(JRD, 1019, dsql_agg_exit_group, -204, "42", "000", "EXIT is not allowed in ON GROUP DO section of aggregate function")
10221022
FB_IMPL_MSG(JRD, 1020, dsql_agg_return, -204, "42", "000", "RETURN is not allowed in ON START DO, ON ACCUMULATE DO or ON FINISH DO sections of aggregate function; use EXIT instead")
1023-
FB_IMPL_MSG(JRD, 1021, blob_out_of_length_write, -204, "42", "000", "Cannot write to blob. Position @1 is out of length @2")
1023+
FB_IMPL_MSG(JRD, 1021, blob_out_of_length_write, -204, "42", "000", "Cannot write to blob. Position @1 is out of blob length @2")

src/jrd/blb.cpp

Lines changed: 123 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ static blb* store_array(thread_db*, jrd_tra*, bid*);
9292

9393
namespace {
9494

95-
class ReplaceDataHelper
95+
class DataModifyHelper
9696
{
9797
public:
98-
ReplaceDataHelper(thread_db* tdbb, const vcl* blb_pages, const offset_t position, const void* buffer, const ULONG length) :
98+
DataModifyHelper(thread_db* tdbb, const vcl* blb_pages, const offset_t position, const void* buffer, const ULONG length) :
9999
m_newData(buffer), m_newLength(length),
100100
m_level1Pages(*blb_pages),
101101
m_pageDataLength(tdbb->getDatabase()->dbb_page_size - BLP_SIZE)
@@ -1645,6 +1645,21 @@ void blb::BLB_put_data(thread_db* tdbb, const UCHAR* buffer, SLONG length)
16451645
SET_TDBB(tdbb);
16461646
const BLOB_PTR* p = buffer;
16471647

1648+
// BLB_put_segment will remove the flag after teh first call so replace the data here
1649+
if (blb_flags & BLB_seek)
1650+
{
1651+
if (!(blb_flags & BLB_temporary) || (blb_flags & BLB_closed))
1652+
ERR_post(Arg::Gds(isc_cannot_update_old_blob));
1653+
1654+
blb_flags &= ~BLB_seek;
1655+
1656+
// Modify part inside existing data
1657+
if (modifyDataMoveBuffer(tdbb, blb_seek, p, length))
1658+
return;
1659+
1660+
// Continue and append the rest
1661+
}
1662+
16481663
while (length > 0)
16491664
{
16501665
// ASF: the comment below was copied from BLB_get_data
@@ -1679,6 +1694,17 @@ void blb::BLB_put_segment(thread_db* tdbb, const void* seg, USHORT segment_lengt
16791694
if (!(blb_flags & BLB_temporary) || (blb_flags & BLB_closed))
16801695
ERR_post(Arg::Gds(isc_cannot_update_old_blob));
16811696

1697+
if (blb_flags & BLB_seek)
1698+
{
1699+
blb_flags &= ~BLB_seek;
1700+
1701+
// Modify part inside existing data
1702+
if (modifyDataMoveBuffer(tdbb, blb_seek, segment, segment_length))
1703+
return;
1704+
1705+
// Continue and append the rest
1706+
}
1707+
16821708
if (blb_filter)
16831709
{
16841710
BLF_put_segment(tdbb, &blb_filter, segment_length, segment);
@@ -2016,6 +2042,95 @@ void blb::scalar(thread_db* tdbb,
20162042
blob->BLB_close(tdbb);
20172043
}
20182044

2045+
void blb::modifyExistingData(thread_db* tdbb, offset_t position, const void* buffer, const ULONG length)
2046+
{
2047+
fb_assert ((blb_flags & BLB_temporary) && !(blb_flags & BLB_closed));
2048+
2049+
const offset_t end = position + length;
2050+
fb_assert(end <= blb_length);
2051+
2052+
if (blb_level == 0)
2053+
{
2054+
blob_page* page = (blob_page*) getBuffer();
2055+
memcpy(reinterpret_cast<char*>(page->blp_page) + position, buffer, length);
2056+
return;
2057+
}
2058+
2059+
DataModifyHelper helper(tdbb, blb_pages, position, buffer, length);
2060+
blob_page* page = nullptr;
2061+
2062+
WIN window(blb_pg_space_id, -1);
2063+
if (blb_flags & BLB_large_scan)
2064+
{
2065+
window.win_flags = WIN_large_scan;
2066+
window.win_scans = 1;
2067+
}
2068+
2069+
auto releasePage = [&tdbb, &window](const bool mark)
2070+
{
2071+
if (mark)
2072+
CCH_MARK(tdbb, &window);
2073+
2074+
if (window.win_flags & WIN_large_scan)
2075+
CCH_RELEASE_TAIL(tdbb, &window);
2076+
else
2077+
CCH_RELEASE(tdbb, &window);
2078+
};
2079+
2080+
// Level 1 blobs are much easier -- page number is in vector.
2081+
if (blb_level == 1)
2082+
{
2083+
while (helper.needWrite())
2084+
{
2085+
if (!helper.hasPages()) // The last data is in the blb_buffer
2086+
{
2087+
page = reinterpret_cast<blob_page*>(getBuffer());
2088+
helper.replaceInPage(page);
2089+
fb_assert(helper.getWrittenLength() == length);
2090+
return;
2091+
}
2092+
2093+
// Level 1 page constains data
2094+
window.win_page = helper.getNextLevel1PageId();
2095+
page = reinterpret_cast<blob_page*>(CCH_FETCH(tdbb, &window, LCK_write, pag_blob));
2096+
helper.replaceInPage(page);
2097+
releasePage(true);
2098+
}
2099+
}
2100+
else
2101+
{
2102+
auto level2page = helper.setLevel2(blb_pointers);
2103+
while (helper.needWrite())
2104+
{
2105+
if (!helper.hasPages()) // The last data is in the blb_buffer
2106+
{
2107+
helper.replaceInPage(page);
2108+
fb_assert(helper.getWrittenLength() == length);
2109+
return;
2110+
}
2111+
2112+
// Level 1 page contains pointers
2113+
window.win_page = helper.getNextLevel1PageId();
2114+
page = reinterpret_cast<blob_page*>(CCH_FETCH(tdbb, &window, LCK_write, pag_blob));
2115+
2116+
// Level 2 pages contain data
2117+
const auto numberOfPagess = page->blp_length / sizeof(page->blp_page);
2118+
for (FB_SIZE_T i = level2page; i < numberOfPagess && helper.needWrite(); ++i)
2119+
{
2120+
auto level2Page = reinterpret_cast<blob_page*>(CCH_HANDOFF(tdbb, &window,
2121+
page->blp_page[i],
2122+
LCK_write, pag_blob));
2123+
2124+
helper.replaceInPage(level2Page);
2125+
CCH_MARK(tdbb, &window);
2126+
}
2127+
releasePage(false);
2128+
2129+
level2page = 0; // Offset only for the first pages
2130+
}
2131+
}
2132+
fb_assert(helper.getWrittenLength() == length);
2133+
}
20192134

20202135
static ArrayField* alloc_array(jrd_tra* transaction, Ods::InternalArrayDesc* proto_desc)
20212136
{
@@ -3106,102 +3221,14 @@ void blb::BLB_cancel()
31063221
BLB_cancel(JRD_get_thread_data());
31073222
}
31083223

3109-
void blb::BLB_write(thread_db* tdbb, offset_t position, const void* buffer, const ULONG length)
3224+
void blb::BLB_write(thread_db* tdbb, offset_t position, const void* buffer, ULONG length)
31103225
{
31113226
if (!(blb_flags & BLB_temporary) || (blb_flags & BLB_closed))
3112-
ERR_post(Arg::Gds(isc_cannot_update_old_blob));
3113-
3114-
if (position > blb_length)
3115-
ERR_post(Arg::Gds(isc_blob_out_of_length_write) << Arg::Int64(position) << Arg::Int64(blb_length));
3116-
3117-
const offset_t end = position + length;
3118-
if (end > blb_length)
3119-
{
3120-
const offset_t middle = blb_length - position;
3121-
BLB_write(tdbb, position, buffer, middle); // Replace
3122-
BLB_put_segment(tdbb, (const UCHAR*)buffer + middle, length - middle); // Append
3123-
return;
3124-
}
3125-
3126-
if (blb_level == 0)
3127-
{
3128-
blob_page* page = (blob_page*) getBuffer();
3129-
memcpy(reinterpret_cast<char*>(page->blp_page) + position, buffer, length);
3130-
return;
3131-
}
3132-
3133-
ReplaceDataHelper helper(tdbb, blb_pages, position, buffer, length);
3134-
blob_page* page = nullptr;
3135-
3136-
WIN window(blb_pg_space_id, -1);
3137-
if (blb_flags & BLB_large_scan)
3138-
{
3139-
window.win_flags = WIN_large_scan;
3140-
window.win_scans = 1;
3141-
}
3142-
3143-
auto releasePage = [&tdbb, &window](const bool mark)
3144-
{
3145-
if (mark)
3146-
CCH_MARK(tdbb, &window);
3147-
3148-
if (window.win_flags & WIN_large_scan)
3149-
CCH_RELEASE_TAIL(tdbb, &window);
3150-
else
3151-
CCH_RELEASE(tdbb, &window);
3152-
};
3153-
3154-
// Level 1 blobs are much easier -- page number is in vector.
3155-
if (blb_level == 1)
3156-
{
3157-
while (helper.needWrite())
3158-
{
3159-
if (!helper.hasPages()) // The last data is in the blb_buffer
3160-
{
3161-
page = reinterpret_cast<blob_page*>(getBuffer());
3162-
helper.replaceInPage(page);
3163-
fb_assert(helper.getWrittenLength() == length);
3164-
return;
3165-
}
3227+
ERR_post(Arg::Gds(isc_cannot_update_old_blob)); // Cannot update existing blob
31663228

3167-
// Level 1 page constains data
3168-
window.win_page = helper.getNextLevel1PageId();
3169-
page = reinterpret_cast<blob_page*>(CCH_FETCH(tdbb, &window, LCK_write, pag_blob));
3170-
helper.replaceInPage(page);
3171-
releasePage(true);
3172-
}
3173-
}
3174-
else
3175-
{
3176-
auto level2page = helper.setLevel2(blb_pointers);
3177-
while (helper.needWrite())
3178-
{
3179-
if (!helper.hasPages()) // The last data is in the blb_buffer
3180-
{
3181-
helper.replaceInPage(page);
3182-
fb_assert(helper.getWrittenLength() == length);
3183-
return;
3184-
}
3185-
3186-
// Level 1 page contains pointers
3187-
window.win_page = helper.getNextLevel1PageId();
3188-
page = reinterpret_cast<blob_page*>(CCH_FETCH(tdbb, &window, LCK_write, pag_blob));
3229+
// Modify part inside existing data
3230+
if (modifyDataMoveBuffer(tdbb, position, buffer, length))
3231+
return; // Only modify, exit
31893232

3190-
// Level 2 pages contain data
3191-
const auto numberOfPagess = page->blp_length / sizeof(page->blp_page);
3192-
for (FB_SIZE_T i = level2page; i < numberOfPagess && helper.needWrite(); ++i)
3193-
{
3194-
auto level2Page = reinterpret_cast<blob_page*>(CCH_HANDOFF(tdbb, &window,
3195-
page->blp_page[i],
3196-
LCK_write, pag_blob));
3197-
3198-
helper.replaceInPage(level2Page);
3199-
CCH_MARK(tdbb, &window);
3200-
}
3201-
releasePage(false);
3202-
3203-
level2page = 0; // Offset only for the first pages
3204-
}
3205-
}
3206-
fb_assert(helper.getWrittenLength() == length);
3233+
BLB_put_segment(tdbb, buffer, length); // Append
32073234
}

src/jrd/blb.h

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "../common/classes/ImplementHelper.h"
4141
#include "../common/dsc.h"
4242
#include "../jrd/Resources.h"
43+
#include "err_proto.h"
4344

4445
namespace Ods
4546
{
@@ -136,7 +137,7 @@ class blb : public pool_alloc<type_blb>
136137
// Write data at any position in a temporally (new) blob
137138
// The position of the new buffer must start inside the blob range, but its length may extend beyond it
138139
// Existing data will be overwritten
139-
void BLB_write(thread_db* tdbb, offset_t position, const void* buffer, const ULONG length);
140+
void BLB_write(thread_db* tdbb, offset_t position, const void* buffer, ULONG length);
140141

141142
private:
142143
static blb* allocate_blob(thread_db*, jrd_tra*);
@@ -147,6 +148,41 @@ class blb : public pool_alloc<type_blb>
147148
void insert_page(thread_db*);
148149
void destroy(const bool purge_flag);
149150

151+
// Modify only existing data. Throw error on side violation
152+
void modifyExistingData(thread_db* tdbb, offset_t position, const void* buffer, const ULONG length);
153+
154+
// Modify existing data
155+
// Output:
156+
// true: the input range is only inside the blob data
157+
// false: the input range is extends beyond existing data. Modify `buffer` and `length` to return only non-written data
158+
template<class BufferType, class SizeType>
159+
requires((std::is_same_v<BufferType, void> || std::is_same_v<BufferType, UCHAR>) && std::is_integral_v<SizeType>)
160+
bool modifyDataMoveBuffer(thread_db* tdbb, const offset_t position, const BufferType*& buffer, SizeType& length)
161+
{
162+
if (position > blb_length)
163+
{
164+
ERR_post(Firebird::Arg::Gds(isc_blob_out_of_length_write) <<
165+
Firebird::Arg::Int64(position) << Firebird::Arg::Int64(blb_length));
166+
}
167+
168+
const offset_t end = position + length;
169+
if (end <= blb_length)
170+
{
171+
// Range is inside the current data, replace and report that no extra actions are requeued
172+
modifyExistingData(tdbb, position, buffer, length);
173+
return true;
174+
}
175+
176+
// Part inside existing data
177+
const offset_t middle = blb_length - position;
178+
modifyExistingData(tdbb, position, buffer, middle);
179+
180+
// Return only part to append
181+
buffer = reinterpret_cast<const BufferType*>(reinterpret_cast<const UCHAR*>(buffer) + middle); // Move pointer
182+
length -= middle;
183+
return false;
184+
}
185+
150186
FB_SIZE_T blb_temp_size = 0; // size stored in transaction temp space
151187
offset_t blb_temp_offset = 0; // offset in transaction temp space
152188
Attachment* blb_attachment = nullptr; // database attachment

0 commit comments

Comments
 (0)