mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 07:23: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:
parent
b8752f4248
commit
59029fb3e7
@ -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;
|
||||
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);
|
||||
|
||||
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
|
||||
*/
|
||||
const bool flag =
|
||||
EVL_field(relation_1, rpb.rpb_record, field_id, &desc1);
|
||||
flag_cur = EVL_field(relation_1, rpb.rpb_record, field_id, &desc1);
|
||||
}
|
||||
|
||||
field_id = record_idx->idx_rpt[i].idx_field;
|
||||
const bool flag_2 = EVL_field(relation_2, record, field_id, &desc2);
|
||||
const bool notEqualCur = !hasCurValues ||
|
||||
hasCurValues && ( (flag_cur != flag_idx) || (MOV_compare(&desc1, &desc2) != 0) );
|
||||
|
||||
if (flag != flag_2 || 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;
|
||||
}
|
||||
all_nulls = all_nulls && !flag && !flag_2;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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,6 +1955,54 @@ bool VIO_get_current(
|
||||
return true;
|
||||
|
||||
case tra_active:
|
||||
// 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:
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user