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

Fixed bug CORE-6252 : UNIQUE CONSTRAINT violation

This commit is contained in:
hvlad 2020-02-23 22:31:05 +02:00
parent d64f31b504
commit 1922f1f8cd
3 changed files with 40 additions and 28 deletions

View File

@ -3364,6 +3364,7 @@ static ULONG fast_load(thread_db* tdbb,
bool error = false;
FB_UINT64 count = 0;
FB_UINT64 duplicates = 0;
const bool unique = (idx->idx_flags & idx_unique);
const bool descending = (idx->idx_flags & idx_descending);
const ULONG segments = idx->idx_count;
@ -3437,6 +3438,13 @@ static ULONG fast_load(thread_db* tdbb,
IndexNode tempNode;
// Detect the case when set of duplicate keys contains more then one key
// from primary record version. It breaks the unique constraint and must
// be rejected. Note, it is not always could be detected while sorting.
// Set to true when primary record version is found in current set of
// duplicate keys.
bool primarySeen = false;
while (!error)
{
// Get the next record in sorted order.
@ -3444,7 +3452,7 @@ static ULONG fast_load(thread_db* tdbb,
UCHAR* record;
scb->get(tdbb, reinterpret_cast<ULONG**>(&record));
if (!record)
if (!record || creation.duplicates)
break;
index_sort_record* isr = (index_sort_record*) (record + key_length);
@ -3656,8 +3664,21 @@ static ULONG fast_load(thread_db* tdbb,
// check if this is a duplicate node
duplicate = (!newNode.length && prefix == leafKey->key_length);
const bool isPrimary = !(isr->isr_flags & ISR_secondary);
if (duplicate && (count > 1))
{
++duplicates;
if (unique && primarySeen && isPrimary && !(isr->isr_flags & ISR_null))
{
creation.duplicates++;
creation.dup_recno = isr->isr_record_number;
}
if (isPrimary)
primarySeen = true;
}
else
primarySeen = isPrimary;
// Update the length of the page.
bucket->btr_length = pointer - (UCHAR*) bucket;

View File

@ -254,6 +254,8 @@ struct IndexCreation
jrd_tra* transaction;
USHORT key_length;
Firebird::AutoPtr<Sort> sort;
SINT64 dup_recno;
SLONG duplicates;
};
// Class used to report any index related errors

View File

@ -70,14 +70,6 @@ using namespace Jrd;
using namespace Ods;
using namespace Firebird;
// Data to be passed to index fast load duplicates routine
struct index_fast_load
{
SINT64 ifl_dup_recno;
SLONG ifl_duplicates;
USHORT ifl_key_length;
};
static idx_e check_duplicates(thread_db*, Record*, index_desc*, index_insertion*, jrd_rel*);
static idx_e check_foreign_key(thread_db*, Record*, jrd_rel*, jrd_tra*, index_desc*, IndexErrorContext&);
@ -301,6 +293,8 @@ void IDX_create_index(thread_db* tdbb,
creation.relation = relation;
creation.transaction = transaction;
creation.key_length = key_length;
creation.dup_recno = -1;
creation.duplicates = 0;
BTR_reserve_slot(tdbb, creation);
@ -310,11 +304,6 @@ void IDX_create_index(thread_db* tdbb,
RecordStack stack;
const UCHAR pad = isDescending ? -1 : 0;
index_fast_load ifl_data;
ifl_data.ifl_dup_recno = -1;
ifl_data.ifl_duplicates = 0;
ifl_data.ifl_key_length = key_length;
sort_key_def key_desc[2];
// Key sort description
key_desc[0].setSkdLength(SKD_bytes, key_length);
@ -328,7 +317,7 @@ void IDX_create_index(thread_db* tdbb,
key_desc[1].skd_vary_offset = 0;
FPTR_REJECT_DUP_CALLBACK callback = (idx->idx_flags & idx_unique) ? duplicate_key : NULL;
void* callback_arg = (idx->idx_flags & idx_unique) ? &ifl_data : NULL;
void* callback_arg = (idx->idx_flags & idx_unique) ? &creation : NULL;
Sort* const scb = FB_NEW_POOL(transaction->tra_sorts.getPool())
Sort(dbb, &transaction->tra_sorts, key_length + sizeof(index_sort_record),
@ -465,7 +454,7 @@ void IDX_create_index(thread_db* tdbb,
// try to catch duplicates early
if (ifl_data.ifl_duplicates > 0)
if (creation.duplicates > 0)
{
do {
if (record != gc_record)
@ -502,7 +491,7 @@ void IDX_create_index(thread_db* tdbb,
delete record;
}
if (ifl_data.ifl_duplicates > 0)
if (creation.duplicates > 0)
break;
if (--tdbb->tdbb_quantum < 0)
@ -514,20 +503,20 @@ void IDX_create_index(thread_db* tdbb,
if (primary.getWindow(tdbb).win_flags & WIN_large_scan)
--relation->rel_scan_count;
if (!ifl_data.ifl_duplicates)
if (!creation.duplicates)
scb->sort(tdbb);
// ASF: We have a callback accessing ifl_data, so don't join above and below if's.
// ASF: We have a callback accessing "creation", so don't join above and below if's.
if (!ifl_data.ifl_duplicates)
if (!creation.duplicates)
BTR_create(tdbb, creation, selectivity);
if (ifl_data.ifl_duplicates > 0)
if (creation.duplicates > 0)
{
AutoPtr<Record> error_record;
primary.rpb_record = NULL;
fb_assert(ifl_data.ifl_dup_recno >= 0);
primary.rpb_number.setValue(ifl_data.ifl_dup_recno);
fb_assert(creation.dup_recno >= 0);
primary.rpb_number.setValue(creation.dup_recno);
if (DPM_get(tdbb, &primary, LCK_read))
{
@ -1440,15 +1429,15 @@ static bool duplicate_key(const UCHAR* record1, const UCHAR* record2, void* ifl_
* bump a counter.
*
**************************************/
index_fast_load* ifl_data = static_cast<index_fast_load*>(ifl_void);
const index_sort_record* rec1 = (index_sort_record*) (record1 + ifl_data->ifl_key_length);
const index_sort_record* rec2 = (index_sort_record*) (record2 + ifl_data->ifl_key_length);
IndexCreation* ifl_data = static_cast<IndexCreation*>(ifl_void);
const index_sort_record* rec1 = (index_sort_record*) (record1 + ifl_data->key_length);
const index_sort_record* rec2 = (index_sort_record*) (record2 + ifl_data->key_length);
if (!(rec1->isr_flags & (ISR_secondary | ISR_null)) &&
!(rec2->isr_flags & (ISR_secondary | ISR_null)))
{
if (!ifl_data->ifl_duplicates++)
ifl_data->ifl_dup_recno = rec2->isr_record_number;
if (!ifl_data->duplicates++)
ifl_data->dup_recno = rec2->isr_record_number;
}
return false;