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

Allowed NULLs in unique indices.

This commit is contained in:
dimitr 2003-03-05 11:23:08 +00:00
parent 91233fb49b
commit d6932d887d
6 changed files with 68 additions and 50 deletions

View File

@ -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

View File

@ -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 *,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 {