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:
parent
d64f31b504
commit
1922f1f8cd
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user