mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 07:23:03 +01:00
Fixed CORE-4383: Index and BLOBs garbage collection doesn't work for update_in_place().
This commit is contained in:
parent
01813f7c55
commit
f1d60b9596
@ -164,6 +164,7 @@ public:
|
||||
const UCHAR REC_same_tx = 1; // record inserted/updated and deleted by same tx
|
||||
const UCHAR REC_gc_active = 2; // relation garbage collect record block in use
|
||||
const UCHAR REC_new_version = 4; // savepoint created new record version and deleted it
|
||||
const UCHAR REC_undo_active = 8; // record block in use for undo purposes
|
||||
|
||||
// save record_param block
|
||||
|
||||
|
@ -102,6 +102,9 @@ const char* const TRA_UNDO_SPACE = "fb_undo_";
|
||||
|
||||
class jrd_tra : public pool_alloc<type_tra>
|
||||
{
|
||||
static const size_t MAX_UNDO_RECORDS = 2;
|
||||
typedef Firebird::HalfStaticArray<Record*, MAX_UNDO_RECORDS> UndoRecordList;
|
||||
|
||||
public:
|
||||
enum wait_t {
|
||||
tra_no_wait,
|
||||
@ -129,7 +132,7 @@ public:
|
||||
tra_sorts(*p),
|
||||
tra_blob_space(NULL),
|
||||
tra_undo_space(NULL),
|
||||
tra_undo_record(NULL),
|
||||
tra_undo_records(*p),
|
||||
tra_user_management(NULL),
|
||||
tra_autonomous_pool(NULL),
|
||||
tra_autonomous_cnt(0)
|
||||
@ -220,7 +223,7 @@ private:
|
||||
TempSpace* tra_blob_space; // temp blob storage
|
||||
TempSpace* tra_undo_space; // undo log storage
|
||||
|
||||
Record* tra_undo_record; // temporary record used for the undo purposes
|
||||
UndoRecordList tra_undo_records; // temporary records used for the undo purposes
|
||||
UserManagement* tra_user_management;
|
||||
MemoryPool* tra_autonomous_pool;
|
||||
USHORT tra_autonomous_cnt;
|
||||
@ -258,17 +261,47 @@ public:
|
||||
return tra_undo_space;
|
||||
}
|
||||
|
||||
Record* getUndoRecord(USHORT length)
|
||||
Record* getUndoRecord(USHORT length, SINT64 number, const Format* format, UCHAR flags)
|
||||
{
|
||||
if (!tra_undo_record || tra_undo_record->rec_length < length)
|
||||
Record** recordPtr = NULL;
|
||||
|
||||
for (Record** iter = tra_undo_records.begin(); iter != tra_undo_records.end(); ++iter)
|
||||
{
|
||||
delete tra_undo_record;
|
||||
tra_undo_record = FB_NEW_RPT(*tra_pool, length) Record(*tra_pool);
|
||||
fb_assert(*iter);
|
||||
|
||||
if (!((*iter)->rec_flags & REC_undo_active))
|
||||
{
|
||||
recordPtr = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memset(tra_undo_record, 0, sizeof(Record) + length);
|
||||
Record* record = NULL;
|
||||
|
||||
return tra_undo_record;
|
||||
if (recordPtr)
|
||||
{
|
||||
record = *recordPtr;
|
||||
|
||||
if (record->rec_length < length)
|
||||
{
|
||||
delete record;
|
||||
*recordPtr = record = FB_NEW_RPT(*tra_pool, length) Record(*tra_pool);
|
||||
}
|
||||
else
|
||||
memset(record, 0, sizeof(Record) + length);
|
||||
}
|
||||
else
|
||||
{
|
||||
fb_assert(tra_undo_records.getCount() < MAX_UNDO_RECORDS);
|
||||
record = FB_NEW_RPT(*tra_pool, length) Record(*tra_pool);
|
||||
tra_undo_records.add(record);
|
||||
}
|
||||
|
||||
record->rec_number.setValue(number);
|
||||
record->rec_length = length;
|
||||
record->rec_format = format;
|
||||
record->rec_flags = (flags | REC_undo_active);
|
||||
return record;
|
||||
}
|
||||
|
||||
UserManagement* getUserManagement();
|
||||
@ -466,16 +499,10 @@ public:
|
||||
{
|
||||
flags |= newFlags;
|
||||
|
||||
Record* const record = transaction->getUndoRecord(length);
|
||||
record->rec_number.setValue(number);
|
||||
record->rec_flags = flags;
|
||||
record->rec_length = length;
|
||||
record->rec_format = format;
|
||||
Record* const record = transaction->getUndoRecord(length, number, format, flags);
|
||||
|
||||
if (length)
|
||||
{
|
||||
transaction->getUndoSpace()->read(offset, record->rec_data, length);
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
@ -526,6 +553,36 @@ inline void Savepoint::deleteActions(VerbAction* list)
|
||||
}
|
||||
};
|
||||
|
||||
class AutoUndoRecord
|
||||
{
|
||||
public:
|
||||
AutoUndoRecord(jrd_tra* transaction, UndoItem* undo, USHORT flags = 0)
|
||||
: m_record(undo ? undo->setupRecord(transaction, flags) : NULL)
|
||||
{}
|
||||
|
||||
~AutoUndoRecord()
|
||||
{
|
||||
if (m_record)
|
||||
{
|
||||
fb_assert(m_record->rec_flags & REC_undo_active);
|
||||
m_record->rec_flags &= ~REC_undo_active;
|
||||
}
|
||||
}
|
||||
|
||||
operator Record*()
|
||||
{
|
||||
return m_record;
|
||||
}
|
||||
|
||||
Record* operator->()
|
||||
{
|
||||
return m_record;
|
||||
}
|
||||
|
||||
private:
|
||||
Record* const m_record;
|
||||
};
|
||||
|
||||
} //namespace Jrd
|
||||
|
||||
#endif // JRD_TRA_H
|
||||
|
@ -3180,7 +3180,7 @@ void VIO_verb_cleanup(thread_db* tdbb, jrd_tra* transaction)
|
||||
}
|
||||
else
|
||||
{
|
||||
Record* const record = action->vct_undo->current().setupRecord(transaction);
|
||||
AutoUndoRecord record(transaction, &action->vct_undo->current());
|
||||
const bool same_tx = (record->rec_flags & REC_same_tx) != 0;
|
||||
|
||||
// Have we done BOTH an update and delete to this record
|
||||
@ -3209,6 +3209,7 @@ void VIO_verb_cleanup(thread_db* tdbb, jrd_tra* transaction)
|
||||
VIO_backout(tdbb, &rpb, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
if (record->rec_length != 0)
|
||||
{
|
||||
Record* dead_record = rpb.rpb_record;
|
||||
@ -3256,7 +3257,8 @@ void VIO_verb_cleanup(thread_db* tdbb, jrd_tra* transaction)
|
||||
BUGCHECK(186); // msg 186 record disappeared
|
||||
}
|
||||
CCH_RELEASE(tdbb, &rpb.getWindow(tdbb));
|
||||
Record* const record = action->vct_undo->current().setupRecord(transaction);
|
||||
|
||||
AutoUndoRecord record(transaction, &action->vct_undo->current());
|
||||
const bool same_tx = (record->rec_flags & REC_same_tx) != 0;
|
||||
const bool new_ver = (record->rec_flags & REC_new_version) != 0;
|
||||
if (record->rec_length != 0)
|
||||
@ -5354,19 +5356,17 @@ static void verb_post(thread_db* tdbb,
|
||||
VerbAction* action;
|
||||
for (action = transaction->tra_save_point->sav_verb_actions; action; action = action->vct_next)
|
||||
{
|
||||
if (action->vct_relation == rpb->rpb_relation) {
|
||||
if (action->vct_relation == rpb->rpb_relation)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!action)
|
||||
{
|
||||
if ( (action = transaction->tra_save_point->sav_verb_free) ) {
|
||||
if ( (action = transaction->tra_save_point->sav_verb_free) )
|
||||
transaction->tra_save_point->sav_verb_free = action->vct_next;
|
||||
}
|
||||
else {
|
||||
else
|
||||
action = FB_NEW(*tdbb->getDefaultPool()) VerbAction();
|
||||
}
|
||||
|
||||
action->vct_next = transaction->tra_save_point->sav_verb_actions;
|
||||
transaction->tra_save_point->sav_verb_actions = action;
|
||||
action->vct_relation = rpb->rpb_relation;
|
||||
@ -5380,9 +5380,9 @@ static void verb_post(thread_db* tdbb,
|
||||
// An update-in-place is being posted to this savepoint, and this
|
||||
// savepoint hasn't seen this record before.
|
||||
|
||||
if (!action->vct_undo) {
|
||||
if (!action->vct_undo)
|
||||
action->vct_undo = new UndoItemTree(tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
const UCHAR flags = same_tx ? REC_same_tx : 0;
|
||||
action->vct_undo->add(UndoItem(transaction, rpb->rpb_number, old_data, flags));
|
||||
}
|
||||
@ -5391,33 +5391,33 @@ static void verb_post(thread_db* tdbb,
|
||||
// An insert/update followed by a delete is posted to this savepoint,
|
||||
// and this savepoint hasn't seen this record before.
|
||||
|
||||
if (!action->vct_undo) {
|
||||
if (!action->vct_undo)
|
||||
action->vct_undo = new UndoItemTree(tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
const UCHAR flags = REC_same_tx | (new_ver ? REC_new_version : 0);
|
||||
action->vct_undo->add(UndoItem(rpb->rpb_number, flags));
|
||||
}
|
||||
}
|
||||
else if (same_tx)
|
||||
{
|
||||
Record* undo = NULL;
|
||||
if (action->vct_undo && action->vct_undo->locate(rpb->rpb_number.getValue()))
|
||||
{
|
||||
const bool found = (action->vct_undo && action->vct_undo->locate(rpb->rpb_number.getValue()));
|
||||
|
||||
// An insert/update followed by a delete is posted to this savepoint,
|
||||
// and this savepoint has already undo for this record.
|
||||
undo = action->vct_undo->current().setupRecord(transaction, REC_same_tx);
|
||||
}
|
||||
else
|
||||
AutoUndoRecord undo(transaction, found ? &action->vct_undo->current() : NULL, REC_same_tx);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
// An insert/update followed by a delete is posted to this savepoint,
|
||||
// and this savepoint has seen this record before but it doesn't have undo data.
|
||||
|
||||
if (!action->vct_undo) {
|
||||
if (!action->vct_undo)
|
||||
action->vct_undo = new UndoItemTree(tdbb->getDefaultPool());
|
||||
}
|
||||
|
||||
const UCHAR flags = REC_same_tx | REC_new_version;
|
||||
action->vct_undo->add(UndoItem(rpb->rpb_number, flags));
|
||||
}
|
||||
|
||||
if (old_data)
|
||||
{
|
||||
// The passed old_data will not be used. Thus, garbage collect.
|
||||
@ -5427,15 +5427,14 @@ static void verb_post(thread_db* tdbb,
|
||||
}
|
||||
else if (old_data)
|
||||
{
|
||||
const bool found = (action->vct_undo && action->vct_undo->locate(rpb->rpb_number.getValue()));
|
||||
|
||||
// We are posting an update-in-place, but the current savepoint has
|
||||
// already undo data for this record. The old_data will not be used,
|
||||
// so make sure we garbage collect before we lose track of the
|
||||
// in-place-updated record.
|
||||
|
||||
Record* undo = NULL;
|
||||
if (action->vct_undo && action->vct_undo->locate(rpb->rpb_number.getValue())) {
|
||||
undo = action->vct_undo->current().setupRecord(transaction);
|
||||
}
|
||||
AutoUndoRecord undo(transaction, found ? &action->vct_undo->current() : NULL);
|
||||
|
||||
garbage_collect_idx(tdbb, rpb, /*new_rpb,*/ old_data, undo);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user