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

More precise check for primary key\unique\foreign key constraints. Code for expression indexes will be commited soon.

This commit is contained in:
hvlad 2004-10-18 14:46:41 +00:00
parent b8752f4248
commit 59029fb3e7
3 changed files with 115 additions and 21 deletions

View File

@ -901,21 +901,30 @@ static IDX_E check_duplicates(
IDX_E result = idx_e_ok;
index_desc* insertion_idx = insertion->iib_descriptor;
record_param rpb;
record_param rpb, old_rpb;
rpb.rpb_relation = insertion->iib_relation;
rpb.rpb_record = NULL;
// rpb.rpb_window.win_flags = 0; redundant.
old_rpb.rpb_relation = insertion->iib_relation;
old_rpb.rpb_record = NULL;
jrd_rel* relation_1 = insertion->iib_relation;
RecordBitmap::Accessor accessor(insertion->iib_duplicates);
if (accessor.getFirst())
do {
bool hasOldValues;
const bool isFK = (record_idx->idx_flags & idx_foreign) != 0;
rpb.rpb_number.setValue(accessor.current());
if (rpb.rpb_number != insertion->iib_number
&& VIO_get_current(tdbb, &rpb, insertion->iib_transaction,
&& VIO_get_current(tdbb, &old_rpb, &rpb, insertion->iib_transaction,
tdbb->getDefaultPool(),
(record_idx->idx_flags & idx_foreign) != 0))
isFK,
hasOldValues) )
{
// dimitr: we shouldn't ignore status exceptions which take place
// inside the lock manager. Namely, they are: isc_deadlock,
@ -931,7 +940,7 @@ static IDX_E check_duplicates(
// P.S. I think the check for a status vector should be enough,
// but for sure let's keep the old one as well.
// 2003.05.27
/*
const bool lock_error =
(tdbb->tdbb_status_vector[1] == isc_deadlock ||
tdbb->tdbb_status_vector[1] == isc_lock_conflict ||
@ -942,6 +951,12 @@ static IDX_E check_duplicates(
result = idx_e_duplicate;
break;
}
*/
const bool hasCurValues = !(rpb.rpb_flags & rpb_deleted);
if (!hasCurValues && !hasOldValues) {
result = idx_e_duplicate;
break;
}
/* check the values of the fields in the record being inserted with the
record retrieved -- for unique indexes the insertion index and the
@ -1010,22 +1025,46 @@ static IDX_E check_duplicates(
{
bool all_nulls = true;
USHORT i;
for (i = 0; i < insertion_idx->idx_count; i++) {
USHORT field_id = insertion_idx->idx_rpt[i].idx_field;
/* In order to "map a null to a default" value (in EVL_field()),
* the relation block is referenced.
* Reference: Bug 10116, 10424
*/
const bool flag =
EVL_field(relation_1, rpb.rpb_record, field_id, &desc1);
for (i = 0; i < insertion_idx->idx_count; i++)
{
bool flag_cur = false;
USHORT field_id = record_idx->idx_rpt[i].idx_field;
const bool flag_idx = EVL_field(relation_2, record, field_id, &desc2);
field_id = record_idx->idx_rpt[i].idx_field;
const bool flag_2 = EVL_field(relation_2, record, field_id, &desc2);
if (flag != flag_2 || MOV_compare(&desc1, &desc2) != 0) {
break;
if (hasCurValues)
{
field_id = insertion_idx->idx_rpt[i].idx_field;
/* In order to "map a null to a default" value (in EVL_field()),
* the relation block is referenced.
* Reference: Bug 10116, 10424
*/
flag_cur = EVL_field(relation_1, rpb.rpb_record, field_id, &desc1);
}
all_nulls = all_nulls && !flag && !flag_2;
const bool notEqualCur = !hasCurValues ||
hasCurValues && ( (flag_cur != flag_idx) || (MOV_compare(&desc1, &desc2) != 0) );
if((isFK || !hasOldValues) && notEqualCur)
break;
if (hasOldValues)
{
field_id = insertion_idx->idx_rpt[i].idx_field;
const bool flag_old = EVL_field(relation_1, old_rpb.rpb_record, field_id, &desc1);
const bool notEqualOld = (flag_old != flag_idx || MOV_compare(&desc1, &desc2) != 0);
if(isFK) {
if (notEqualCur || notEqualOld)
break;
}
else {
if (notEqualCur && notEqualOld)
break;
}
}
all_nulls = all_nulls && !flag_cur && !flag_idx;
}
if (i >= insertion_idx->idx_count && !all_nulls) {
@ -1039,6 +1078,9 @@ static IDX_E check_duplicates(
if (rpb.rpb_record)
delete rpb.rpb_record;
if (old_rpb.rpb_record)
delete old_rpb.rpb_record;
return result;
}

View File

@ -1769,10 +1769,12 @@ bool VIO_get(thread_db* tdbb, record_param* rpb, RecordSource* rsb, jrd_tra* tra
bool VIO_get_current(
thread_db* tdbb,
record_param* old_rpb,
record_param* rpb,
jrd_tra* transaction,
JrdMemoryPool* pool,
bool foreign_key)
bool foreign_key,
bool &has_old_values)
{
/**************************************
*
@ -1803,6 +1805,8 @@ bool VIO_get_current(
}
#endif
has_old_values = false;
while (true) {
/* If the record doesn't exist, no problem. */
@ -1951,7 +1955,55 @@ bool VIO_get_current(
return true;
case tra_active:
return !foreign_key;
// 1. if record just inserted
// than FK can't reference it but PK must check it's new value
// 2. if record just deleted
// than FK can't reference it but PK must check it's old value
// 3. if record just modified
// than FK can reference it if old key values are equal to new
// key values and equal FK values
// PK is ok if PK values are not equal to old and not equal to
// new values
if (!rpb->rpb_b_page)
return !foreign_key;
else if (old_rpb)
{
Record* data = rpb->rpb_prior;
*old_rpb = *rpb;
old_rpb->rpb_record = NULL;
if(!DPM_fetch(tdbb, old_rpb, LCK_read))
{
return false; // record deleted
}
// if record was changed between reads
// start all over again
if (old_rpb->rpb_b_page != rpb->rpb_b_page ||
old_rpb->rpb_b_line != rpb->rpb_b_line ||
old_rpb->rpb_f_page != rpb->rpb_f_page ||
old_rpb->rpb_f_line != rpb->rpb_f_line ||
old_rpb->rpb_flags != rpb->rpb_flags)
{
continue;
}
if (!old_rpb->rpb_b_page)
return !foreign_key;
old_rpb->rpb_prior = (old_rpb->rpb_flags & rpb_delta) ? data : NULL;
if(!DPM_fetch_back(tdbb, old_rpb, LCK_read, 1))
{
return !foreign_key;
}
VIO_data(tdbb, old_rpb, pool);
has_old_values = true;
return true;
}
else
return !foreign_key;
case tra_dead:
if (transaction->tra_attachment->att_flags & ATT_no_cleanup) {

View File

@ -51,7 +51,7 @@ void VIO_fini(Jrd::thread_db*);
bool VIO_garbage_collect(Jrd::thread_db*, Jrd::record_param*, const Jrd::jrd_tra*);
Jrd::Record* VIO_gc_record(Jrd::thread_db*, Jrd::jrd_rel*);
bool VIO_get(Jrd::thread_db*, Jrd::record_param*, Jrd::RecordSource*, Jrd::jrd_tra*, JrdMemoryPool*);
bool VIO_get_current(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*, JrdMemoryPool*, bool);
bool VIO_get_current(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*, Jrd::jrd_tra*, JrdMemoryPool*, bool, bool&);
#ifdef GARBAGE_THREAD
void VIO_init(Jrd::thread_db*);
#endif