From d6932d887dcef51a2225d2f59ad48eb9b6734e66 Mon Sep 17 00:00:00 2001 From: dimitr Date: Wed, 5 Mar 2003 11:23:08 +0000 Subject: [PATCH] Allowed NULLs in unique indices. --- src/jrd/btr.cpp | 14 +++++-- src/jrd/btr_proto.h | 2 +- src/jrd/err.cpp | 5 --- src/jrd/err_proto.h | 1 - src/jrd/idx.cpp | 91 +++++++++++++++++++++++++++------------------ src/jrd/nav.cpp | 5 ++- 6 files changed, 68 insertions(+), 50 deletions(-) diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 7f0f82b359..9ad1dfa7bb 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -24,7 +24,7 @@ * */ /* -$Id: btr.cpp,v 1.24 2003-02-20 06:57:44 tamlin Exp $ +$Id: btr.cpp,v 1.25 2003-03-05 11:23:08 dimitr Exp $ */ #include "firebird.h" @@ -929,7 +929,7 @@ void BTR_insert(TDBB tdbb, WIN * root_window, IIB * insertion) } -IDX_E BTR_key(TDBB tdbb, JRD_REL relation, REC record, IDX * idx, KEY * key) +IDX_E BTR_key(TDBB tdbb, JRD_REL relation, REC record, IDX * idx, KEY * key, bool * null_unique) { /************************************** * @@ -952,6 +952,7 @@ IDX_E BTR_key(TDBB tdbb, JRD_REL relation, REC record, IDX * idx, KEY * key) IDX_E result; idx::idx_repeat * tail; int not_missing; + int missing_unique_segments = 0; result = idx_e_ok; tail = idx->idx_rpt; @@ -995,7 +996,7 @@ IDX_E BTR_key(TDBB tdbb, JRD_REL relation, REC record, IDX * idx, KEY * key) } if (!not_missing && (idx->idx_flags & idx_unique)) - result = idx_e_nullunique; + missing_unique_segments++; compress(tdbb, desc_ptr, key, tail->idx_itype, (USHORT) ((not_missing) ? FALSE : TRUE), @@ -1021,7 +1022,7 @@ IDX_E BTR_key(TDBB tdbb, JRD_REL relation, REC record, IDX * idx, KEY * key) not_missing = EVL_field(relation, record, tail->idx_field, desc_ptr); if (!not_missing && (idx->idx_flags & idx_unique)) - result = idx_e_nullunique; + missing_unique_segments++; compress(tdbb, desc_ptr, &temp, tail->idx_itype, (USHORT) ((not_missing) ? FALSE : TRUE), @@ -1051,6 +1052,11 @@ IDX_E BTR_key(TDBB tdbb, JRD_REL relation, REC record, IDX * idx, KEY * key) if (idx->idx_flags & idx_descending) complement_key(key); + if (null_unique) { + // dimitr: TRUE, if all segments of the unique index are NULL + *null_unique = (missing_unique_segments == idx->idx_count); + } + return result; } // try diff --git a/src/jrd/btr_proto.h b/src/jrd/btr_proto.h index 344ea45650..1395eabab2 100644 --- a/src/jrd/btr_proto.h +++ b/src/jrd/btr_proto.h @@ -48,7 +48,7 @@ extern struct btr *BTR_find_page(struct tdbb *, struct irb *, struct win *, extern SLONG BTR_get_quad(const SCHAR*); extern void BTR_insert(struct tdbb *, struct win *, struct iib *); extern enum idx_e BTR_key(struct tdbb *, struct jrd_rel *, struct rec *, - struct idx *, struct key *); + struct idx *, struct key *, bool *); extern USHORT BTR_key_length(struct jrd_rel *, struct idx *); extern struct btn *BTR_last_node(struct btr *, struct exp *, struct btx **); extern struct btr *BTR_left_handoff(struct tdbb *, struct win *, struct btr *, diff --git a/src/jrd/err.cpp b/src/jrd/err.cpp index f37795cea6..e2676a044c 100644 --- a/src/jrd/err.cpp +++ b/src/jrd/err.cpp @@ -199,11 +199,6 @@ void DLL_EXPORT ERR_duplicate_error(IDX_E code, gds_arg_string, ERR_cstring(index_name), 0); break; - case idx_e_nullunique: - ERR_post(gds_no_dup, gds_arg_string, ERR_cstring(index_name), - gds_arg_gds, gds_nullsegkey, 0); - break; - case idx_e_conversion: ERR_punt(); break; diff --git a/src/jrd/err_proto.h b/src/jrd/err_proto.h index b675354865..e6b96d83ab 100644 --- a/src/jrd/err_proto.h +++ b/src/jrd/err_proto.h @@ -35,7 +35,6 @@ typedef enum idx_e { idx_e_ok = 0, idx_e_duplicate, idx_e_keytoobig, - idx_e_nullunique, idx_e_conversion, idx_e_foreign } IDX_E; diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index 58622cc5b0..bebea19ab2 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -19,6 +19,14 @@ * * All Rights Reserved. * Contributor(s): ______________________________________. + * + * 2003.03.04 Dmitry Yemanov: Added support for NULLs in unique indices. + * Done in two stages: + * 1. Restored old behaviour of having only _one_ + * NULL key allowed (i.e. two NULLs are considered + * duplicates). idx_e_nullunique error was removed. + * 2. Changed algorithms in IDX_create_index() and + * check_duplicates() to ignore NULL key duplicates. */ #include "firebird.h" @@ -227,6 +235,8 @@ void IDX_create_index( ifl_data.ifl_duplicates = 0; ifl_data.ifl_key_length = key_length; + bool null_duplicates = false; + key_desc.skd_dtype = SKD_bytes; key_desc.skd_flags = SKD_ascending; key_desc.skd_length = key_length; @@ -299,6 +309,7 @@ void IDX_create_index( while (stack) { record = (REC) LLS_POP(&stack); + bool null_unique = false; /* If foreign key index is being defined, make sure foreign key definition will not be violated */ @@ -309,22 +320,29 @@ void IDX_create_index( if (!(idx->idx_flags & idx_unique)) { idx->idx_flags |= idx_unique; - result = BTR_key(tdbb, relation, record, idx, &key); + result = BTR_key(tdbb, relation, record, idx, &key, &null_unique); idx->idx_flags &= ~idx_unique; } - if (result == idx_e_nullunique) - result = idx_e_ok; - else + if (!null_unique) { result = check_partner_index(tdbb, relation, record, transaction, idx, partner_relation, partner_index_id); + } } - if (result != idx_e_ok || - BTR_key(tdbb, relation, record, idx, - &key) == idx_e_nullunique) { + if (result == idx_e_ok) { + BTR_key(tdbb, relation, record, idx, &key, &null_unique); + if (null_unique) { + // first null key is not a duplicate + if (null_duplicates) + ifl_data.ifl_duplicates--; + else + null_duplicates = true; + } + } + else { do { if (record != gc_record) delete record; @@ -333,14 +351,8 @@ void IDX_create_index( gc_record->rec_flags &= ~REC_gc_active; if (primary.rpb_window.win_flags & WIN_large_scan) --relation->rel_scan_count; - if (result != idx_e_ok) - ERR_duplicate_error(result, partner_relation, - partner_index_id); - else - ERR_post(gds_no_dup, gds_arg_string, - ERR_cstring(reinterpret_cast < - char *>(index_name)), gds_arg_gds, - gds_nullsegkey, 0); + ERR_duplicate_error(result, partner_relation, + partner_index_id); } #ifdef IGNORE_NULL_IDX_KEY @@ -376,7 +388,7 @@ void IDX_create_index( /* try to catch duplicates early */ - if ((idx->idx_flags & idx_unique) && ifl_data.ifl_duplicates) { + if ((idx->idx_flags & idx_unique) && ifl_data.ifl_duplicates > 0) { do { if (record != gc_record) delete record; @@ -441,7 +453,7 @@ void IDX_create_index( ERR_punt(); } - if ((idx->idx_flags & idx_unique) && ifl_data.ifl_duplicates) { + if ((idx->idx_flags & idx_unique) && ifl_data.ifl_duplicates > 0) { SORT_fini(sort_handle, tdbb->tdbb_attachment); ERR_post(gds_no_dup, gds_arg_string, ERR_cstring(reinterpret_cast < char *>(index_name)), 0); @@ -449,7 +461,7 @@ void IDX_create_index( BTR_create(tdbb, relation, idx, key_length, sort_handle, selectivity); - if ((idx->idx_flags & idx_unique) && ifl_data.ifl_duplicates) { + if ((idx->idx_flags & idx_unique) && ifl_data.ifl_duplicates > 0) { SORT_fini(sort_handle, tdbb->tdbb_attachment); ERR_post(gds_no_dup, gds_arg_string, ERR_cstring(reinterpret_cast < char *>(index_name)), 0); @@ -642,13 +654,13 @@ void IDX_garbage_collect(TDBB tdbb, RPB * rpb, LLS going, LLS staying) if (BTR_description(rpb->rpb_relation, root, &idx, i)) { for (stack1 = going; stack1; stack1 = stack1->lls_next) { rec1 = (REC) stack1->lls_object; - BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1); + BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, 0); /* Make sure the index doesn't exist in any record remaining */ for (stack2 = staying; stack2; stack2 = stack2->lls_next) { rec2 = (REC) stack2->lls_object; - BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2); + BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2, 0); if (key_equal(&key1, &key2)) break; } @@ -708,12 +720,12 @@ IDX_E IDX_modify(TDBB tdbb, *bad_relation = new_rpb->rpb_relation; if ( (error_code = BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, &idx, - &key1)) ) { + &key1, 0)) ) { CCH_RELEASE(tdbb, &window); break; } BTR_key(tdbb, org_rpb->rpb_relation, org_rpb->rpb_record, &idx, - &key2); + &key2, 0); if (!key_equal(&key1, &key2)) { if (( error_code = insert_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, @@ -778,10 +790,10 @@ IDX_E IDX_modify_check_constraints(TDBB tdbb, if ( (error_code = BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, &idx, - &key1)) + &key1, 0)) || (error_code = BTR_key(tdbb, org_rpb->rpb_relation, org_rpb->rpb_record, - &idx, &key2))) { + &idx, &key2, 0))) { CCH_RELEASE(tdbb, &window); break; } @@ -860,7 +872,7 @@ IDX_E IDX_store(TDBB tdbb, *bad_index = idx.idx_id; *bad_relation = rpb->rpb_relation; if ( (error_code = - BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key)) ) { + BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key, 0)) ) { CCH_RELEASE(tdbb, &window); break; } @@ -936,11 +948,12 @@ static IDX_E check_duplicates( field_id = record_idx->idx_rpt[i].idx_field; flag_2 = EVL_field(relation_2, record, field_id, &desc2); - if (flag != flag_2 || MOV_compare(&desc1, &desc2) != 0) + // dimitr: stop if the fields are not-null and equal + if (flag && flag_2 && MOV_compare(&desc1, &desc2) == 0) break; } - if (i >= insertion_idx->idx_count) { + if (i < insertion_idx->idx_count) { result = idx_e_duplicate; break; } @@ -1073,7 +1086,7 @@ JRD_REL partner_relation, SSHORT index_id) /* get the key in the original index */ - result = BTR_key(tdbb, relation, record, idx, &key); + result = BTR_key(tdbb, relation, record, idx, &key, 0); CCH_RELEASE(tdbb, &window); /* now check for current duplicates */ @@ -1139,7 +1152,9 @@ static BOOLEAN duplicate_key(UCHAR * record1, UCHAR * record2, IFL ifl_data) rec2 = (ISR) (record2 + ifl_data->ifl_key_length); if (!(rec1->isr_flags & ISR_secondary) && - !(rec2->isr_flags & ISR_secondary)) ++ifl_data->ifl_duplicates; + !(rec2->isr_flags & ISR_secondary)) { + ++ifl_data->ifl_duplicates; + } return FALSE; } @@ -1226,7 +1241,9 @@ static IDX_E insert_key( REC record, JRD_TRA transaction, WIN * window_ptr, -IIB * insertion, JRD_REL * bad_relation, USHORT * bad_index) + IIB * insertion, + JRD_REL * bad_relation, + USHORT * bad_index) { /************************************** * @@ -1272,17 +1289,17 @@ IIB * insertion, JRD_REL * bad_relation, USHORT * bad_index) /* find out if there is a null segment by faking uniqueness -- if there is one, don't bother to check the primary key */ + bool null_unique; idx->idx_flags |= idx_unique; CCH_FETCH(tdbb, window_ptr, LCK_read, pag_root); - result = BTR_key(tdbb, relation, record, idx, &key); + result = BTR_key(tdbb, relation, record, idx, &key, &null_unique); CCH_RELEASE(tdbb, window_ptr); idx->idx_flags &= ~idx_unique; - if (result == idx_e_nullunique) - return idx_e_ok; - - result = - check_foreign_key(tdbb, record, insertion->iib_relation, - transaction, idx, bad_relation, bad_index); + if (!null_unique) { + result = + check_foreign_key(tdbb, record, insertion->iib_relation, + transaction, idx, bad_relation, bad_index); + } } return result; diff --git a/src/jrd/nav.cpp b/src/jrd/nav.cpp index 2776452023..d6454d6156 100644 --- a/src/jrd/nav.cpp +++ b/src/jrd/nav.cpp @@ -830,7 +830,7 @@ BOOLEAN NAV_reset_position(RSB rsb, RPB * new_rpb) /* find the key value of the new position, and set the stream to it */ BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, idx, - &key_value); + &key_value, 0); if (!find_record(rsb, RSE_get_first, &key_value, idx->idx_count, /* XXX */ 0)) return FALSE; @@ -1657,7 +1657,8 @@ static BOOLEAN get_record( reinterpret_cast < struct idx *>((SCHAR *) impure + (SLONG) rsb->rsb_arg[RSB_NAV_idx_offset]), - &value); + &value, + 0); if (compare_keys(idx, key->key_data, key->key_length, &value, FALSE)) { result = FALSE; } else {