mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 10:40:38 +01:00
Simplified the code and reworked the NULL validation logic.
This commit is contained in:
parent
f2fcd26b7e
commit
af97c12801
184
src/jrd/btr.cpp
184
src/jrd/btr.cpp
@ -183,7 +183,7 @@ static UCHAR* find_node_start_point(btree_page*, temporary_key*, UCHAR*, USHORT*
|
||||
static UCHAR* find_area_start_point(btree_page*, const temporary_key*, UCHAR*,
|
||||
USHORT*, bool, bool, RecordNumber = NO_VALUE);
|
||||
|
||||
static ULONG find_page(btree_page*, const temporary_key*, UCHAR, RecordNumber = NO_VALUE,
|
||||
static ULONG find_page(btree_page*, const temporary_key*, const index_desc*, RecordNumber = NO_VALUE,
|
||||
bool = false);
|
||||
|
||||
static contents garbage_collect(thread_db*, WIN*, ULONG);
|
||||
@ -865,7 +865,7 @@ btree_page* BTR_find_page(thread_db* tdbb,
|
||||
while (true)
|
||||
{
|
||||
const temporary_key* tkey = ignoreNulls ? &firstNotNullKey : lower;
|
||||
const ULONG number = find_page(page, tkey, idx->idx_flags,
|
||||
const ULONG number = find_page(page, tkey, idx,
|
||||
NO_VALUE, (retrieval->irb_generic & (irb_starting | irb_partial)));
|
||||
if (number != END_BUCKET)
|
||||
{
|
||||
@ -1057,7 +1057,7 @@ void BTR_insert(thread_db* tdbb, WIN* root_window, index_insertion* insertion)
|
||||
|
||||
|
||||
idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* idx,
|
||||
temporary_key* key, idx_null_state* null_state, const bool fuzzy, USHORT count)
|
||||
temporary_key* key, const bool fuzzy, USHORT count)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -1077,8 +1077,6 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id
|
||||
temp.key_length = 0;
|
||||
DSC desc;
|
||||
DSC* desc_ptr;
|
||||
//SSHORT stuff_count;
|
||||
int missing_unique_segments = 0;
|
||||
|
||||
SET_TDBB(tdbb);
|
||||
const Database* dbb = tdbb->getDatabase();
|
||||
@ -1086,8 +1084,13 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id
|
||||
|
||||
idx_e result = idx_e_ok;
|
||||
index_desc::idx_repeat* tail = idx->idx_rpt;
|
||||
key->key_flags = key_all_nulls;
|
||||
key->key_null_segment = 0;
|
||||
key->key_flags = 0;
|
||||
key->key_nulls = 0;
|
||||
|
||||
const bool descending = (idx->idx_flags & idx_descending);
|
||||
|
||||
if (!count)
|
||||
count = idx->idx_count;
|
||||
|
||||
try {
|
||||
|
||||
@ -1126,17 +1129,12 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id
|
||||
}
|
||||
}
|
||||
|
||||
if (isNull && (idx->idx_flags & idx_unique)) {
|
||||
missing_unique_segments++;
|
||||
}
|
||||
if (isNull)
|
||||
key->key_nulls = 1;
|
||||
|
||||
key->key_flags |= key_empty;
|
||||
|
||||
if (!isNull)
|
||||
key->key_flags &= ~key_all_nulls;
|
||||
|
||||
compress(tdbb, desc_ptr, key, tail->idx_itype, isNull,
|
||||
(idx->idx_flags & idx_descending), keyType);
|
||||
compress(tdbb, desc_ptr, key, tail->idx_itype, isNull, descending, keyType);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1145,23 +1143,18 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id
|
||||
temp.key_flags |= key_empty;
|
||||
for (USHORT n = 0; n < count; n++, tail++)
|
||||
{
|
||||
for (; stuff_count; --stuff_count) {
|
||||
for (; stuff_count; --stuff_count)
|
||||
*p++ = 0;
|
||||
}
|
||||
|
||||
desc_ptr = &desc;
|
||||
// In order to "map a null to a default" value (in EVL_field()),
|
||||
// the relation block is referenced.
|
||||
// Reference: Bug 10116, 10424
|
||||
const bool isNull = !EVL_field(relation, record, tail->idx_field, desc_ptr);
|
||||
if (isNull && (idx->idx_flags & idx_unique))
|
||||
{
|
||||
if (missing_unique_segments++ == 0) {
|
||||
key->key_null_segment = n;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNull)
|
||||
if (isNull)
|
||||
key->key_nulls |= 1 << n;
|
||||
else
|
||||
{
|
||||
if (!(relation->rel_flags & REL_system) && // UNICODE_FSS_HACK
|
||||
desc_ptr->dsc_dtype == dtype_text)
|
||||
@ -1169,12 +1162,9 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id
|
||||
// That's necessary for NO-PAD collations.
|
||||
INTL_adjust_text_descriptor(tdbb, desc_ptr);
|
||||
}
|
||||
|
||||
key->key_flags &= ~key_all_nulls;
|
||||
}
|
||||
|
||||
compress(tdbb, desc_ptr, &temp, tail->idx_itype, isNull,
|
||||
(idx->idx_flags & idx_descending), keyType);
|
||||
compress(tdbb, desc_ptr, &temp, tail->idx_itype, isNull, descending, keyType);
|
||||
|
||||
if (temp.key_length)
|
||||
{
|
||||
@ -1196,25 +1186,18 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id
|
||||
stuff_count = STUFF_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
key->key_length = (p - key->key_data);
|
||||
if (temp.key_flags & key_empty) {
|
||||
|
||||
if (temp.key_flags & key_empty)
|
||||
key->key_flags |= key_empty;
|
||||
}
|
||||
}
|
||||
|
||||
if (key->key_length >= dbb->getMaxIndexKeyLength()) {
|
||||
if (key->key_length >= dbb->getMaxIndexKeyLength())
|
||||
result = idx_e_keytoobig;
|
||||
}
|
||||
|
||||
if (idx->idx_flags & idx_descending) {
|
||||
if (descending)
|
||||
BTR_complement_key(key);
|
||||
}
|
||||
|
||||
if (null_state)
|
||||
{
|
||||
*null_state = !missing_unique_segments ? idx_nulls_none :
|
||||
(missing_unique_segments == idx->idx_count) ? idx_nulls_all : idx_nulls_some;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@ -1227,12 +1210,6 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id
|
||||
}
|
||||
}
|
||||
|
||||
idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* idx,
|
||||
temporary_key* key, idx_null_state* null_state, const bool fuzzy)
|
||||
{
|
||||
return BTR_key(tdbb, relation, record, idx, key, null_state, fuzzy, idx->idx_count);
|
||||
}
|
||||
|
||||
|
||||
USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx)
|
||||
{
|
||||
@ -1421,28 +1398,27 @@ idx_e BTR_make_key(thread_db* tdbb,
|
||||
|
||||
idx_e result = idx_e_ok;
|
||||
|
||||
key->key_flags = key_all_nulls;
|
||||
key->key_null_segment = 0;
|
||||
key->key_flags = 0;
|
||||
key->key_nulls = 0;
|
||||
|
||||
const bool descending = (idx->idx_flags & idx_descending);
|
||||
|
||||
const index_desc::idx_repeat* tail = idx->idx_rpt;
|
||||
|
||||
const USHORT keyType = fuzzy ?
|
||||
INTL_KEY_PARTIAL :
|
||||
((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT);
|
||||
INTL_KEY_PARTIAL : ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT);
|
||||
|
||||
// If the index is a single segment index, don't sweat the compound
|
||||
// stuff.
|
||||
// If the index is a single segment index, don't sweat the compound stuff
|
||||
if (idx->idx_count == 1)
|
||||
{
|
||||
bool isNull;
|
||||
const dsc* desc = eval(tdbb, *exprs, &temp_desc, &isNull);
|
||||
key->key_flags |= key_empty;
|
||||
|
||||
if (!isNull)
|
||||
key->key_flags &= ~key_all_nulls;
|
||||
if (isNull)
|
||||
key->key_nulls = 1;
|
||||
|
||||
compress(tdbb, desc, key, tail->idx_itype, isNull,
|
||||
(idx->idx_flags & idx_descending), keyType);
|
||||
compress(tdbb, desc, key, tail->idx_itype, isNull, descending, keyType);
|
||||
|
||||
if (fuzzy && (key->key_flags & key_empty))
|
||||
key->key_length = 0;
|
||||
@ -1456,18 +1432,16 @@ idx_e BTR_make_key(thread_db* tdbb,
|
||||
USHORT n = 0;
|
||||
for (; n < count; n++, tail++)
|
||||
{
|
||||
for (; stuff_count; --stuff_count) {
|
||||
for (; stuff_count; --stuff_count)
|
||||
*p++ = 0;
|
||||
}
|
||||
|
||||
bool isNull;
|
||||
const dsc* desc = eval(tdbb, *exprs++, &temp_desc, &isNull);
|
||||
if (!isNull) {
|
||||
key->key_flags &= ~key_all_nulls;
|
||||
}
|
||||
|
||||
compress(tdbb, desc, &temp, tail->idx_itype, isNull,
|
||||
(idx->idx_flags & idx_descending),
|
||||
if (isNull)
|
||||
key->key_nulls |= 1 << n;
|
||||
|
||||
compress(tdbb, desc, &temp, tail->idx_itype, isNull, descending,
|
||||
(n == count - 1 ?
|
||||
keyType : ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)));
|
||||
|
||||
@ -1513,14 +1487,14 @@ idx_e BTR_make_key(thread_db* tdbb,
|
||||
if (key->key_length >= dbb->getMaxIndexKeyLength())
|
||||
result = idx_e_keytoobig;
|
||||
|
||||
if (idx->idx_flags & idx_descending)
|
||||
if (descending)
|
||||
BTR_complement_key(key);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void BTR_make_null_key(thread_db* tdbb, index_desc* idx, temporary_key* key)
|
||||
void BTR_make_null_key(thread_db* tdbb, const index_desc* idx, temporary_key* key)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
@ -1547,19 +1521,21 @@ void BTR_make_null_key(thread_db* tdbb, index_desc* idx, temporary_key* key)
|
||||
temp.key_length = 0;
|
||||
|
||||
SET_TDBB(tdbb);
|
||||
//const Database* dbb = tdbb->getDatabase();
|
||||
|
||||
fb_assert(idx != NULL);
|
||||
fb_assert(key != NULL);
|
||||
|
||||
key->key_flags = key_all_nulls;
|
||||
key->key_flags = 0;
|
||||
key->key_nulls = (1 << idx->idx_count) - 1;
|
||||
|
||||
index_desc::idx_repeat* tail = idx->idx_rpt;
|
||||
const bool descending = (idx->idx_flags & idx_descending);
|
||||
|
||||
// If the index is a single segment index, don't sweat the compound
|
||||
// stuff.
|
||||
if ((idx->idx_count == 1) || (idx->idx_flags & idx_expressn)) {
|
||||
compress(tdbb, &null_desc, key, tail->idx_itype, true, (idx->idx_flags & idx_descending), false);
|
||||
const index_desc::idx_repeat* tail = idx->idx_rpt;
|
||||
|
||||
// If the index is a single segment index, don't sweat the compound stuff
|
||||
if ((idx->idx_count == 1) || (idx->idx_flags & idx_expressn))
|
||||
{
|
||||
compress(tdbb, &null_desc, key, tail->idx_itype, true, descending, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1567,14 +1543,13 @@ void BTR_make_null_key(thread_db* tdbb, index_desc* idx, temporary_key* key)
|
||||
UCHAR* p = key->key_data;
|
||||
SSHORT stuff_count = 0;
|
||||
temp.key_flags |= key_empty;
|
||||
|
||||
for (USHORT n = 0; n < idx->idx_count; n++, tail++)
|
||||
{
|
||||
for (; stuff_count; --stuff_count) {
|
||||
for (; stuff_count; --stuff_count)
|
||||
*p++ = 0;
|
||||
}
|
||||
|
||||
compress(tdbb, &null_desc, &temp, tail->idx_itype, true,
|
||||
(idx->idx_flags & idx_descending), false);
|
||||
compress(tdbb, &null_desc, &temp, tail->idx_itype, true, descending, false);
|
||||
|
||||
if (temp.key_length)
|
||||
{
|
||||
@ -1596,15 +1571,15 @@ void BTR_make_null_key(thread_db* tdbb, index_desc* idx, temporary_key* key)
|
||||
stuff_count = STUFF_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
key->key_length = (p - key->key_data);
|
||||
if (temp.key_flags & key_empty) {
|
||||
|
||||
if (temp.key_flags & key_empty)
|
||||
key->key_flags |= key_empty;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx->idx_flags & idx_descending) {
|
||||
if (descending)
|
||||
BTR_complement_key(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2205,7 +2180,7 @@ static ULONG add_node(thread_db* tdbb,
|
||||
ULONG page;
|
||||
while (true)
|
||||
{
|
||||
page = find_page(bucket, insertion->iib_key, insertion->iib_descriptor->idx_flags,
|
||||
page = find_page(bucket, insertion->iib_key, insertion->iib_descriptor,
|
||||
insertion->iib_number);
|
||||
if (page != END_BUCKET) {
|
||||
break;
|
||||
@ -2331,7 +2306,7 @@ static void compress(thread_db* tdbb,
|
||||
|
||||
if (isNull)
|
||||
{
|
||||
UCHAR pad = 0;
|
||||
const UCHAR pad = 0;
|
||||
key->key_flags &= ~key_empty;
|
||||
// AB: NULL should be threated as lowest value possible.
|
||||
// Therefore don't complement pad when we have an ascending index.
|
||||
@ -4192,7 +4167,7 @@ static UCHAR* find_area_start_point(btree_page* bucket, const temporary_key* key
|
||||
|
||||
|
||||
static ULONG find_page(btree_page* bucket, const temporary_key* key,
|
||||
UCHAR idx_flags, RecordNumber find_record_number,
|
||||
const index_desc* idx, RecordNumber find_record_number,
|
||||
bool retrieval)
|
||||
{
|
||||
/**************************************
|
||||
@ -4212,15 +4187,16 @@ static ULONG find_page(btree_page* bucket, const temporary_key* key,
|
||||
|
||||
const bool leafPage = (bucket->btr_level == 0);
|
||||
bool firstPass = true;
|
||||
const bool descending = (idx_flags & idx_descending);
|
||||
const UCHAR* const endPointer = (UCHAR*)bucket + bucket->btr_length;
|
||||
const bool validateDuplicates =
|
||||
((idx_flags & idx_unique) && !(key->key_flags & key_all_nulls)) ||
|
||||
(idx_flags & idx_primary);
|
||||
const bool descending = (idx->idx_flags & idx_descending);
|
||||
const bool primary = (idx->idx_flags & idx_primary);
|
||||
const bool unique = (idx->idx_flags & idx_unique);
|
||||
const bool key_all_nulls = (key->key_nulls == (1 << idx->idx_count) - 1);
|
||||
const bool validateDuplicates = (unique && !key_all_nulls) || primary;
|
||||
|
||||
if (validateDuplicates) {
|
||||
if (validateDuplicates)
|
||||
find_record_number = NO_VALUE;
|
||||
}
|
||||
|
||||
const UCHAR* const endPointer = (UCHAR*) bucket + bucket->btr_length;
|
||||
|
||||
USHORT prefix = 0; // last computed prefix against processed node
|
||||
|
||||
@ -5020,11 +4996,13 @@ static ULONG insert_node(thread_db* tdbb,
|
||||
btree_page* bucket = (btree_page*) window->win_buffer;
|
||||
temporary_key* key = insertion->iib_key;
|
||||
|
||||
const bool unique = (insertion->iib_descriptor->idx_flags & idx_unique);
|
||||
const bool primary = (insertion->iib_descriptor->idx_flags & idx_primary);
|
||||
const index_desc* const idx = insertion->iib_descriptor;
|
||||
const bool unique = (idx->idx_flags & idx_unique);
|
||||
const bool primary = (idx->idx_flags & idx_primary);
|
||||
const bool key_all_nulls = (key->key_nulls == (1 << idx->idx_count) - 1);
|
||||
const bool leafPage = (bucket->btr_level == 0);
|
||||
// hvlad: don't check unique index if key has only null values
|
||||
const bool validateDuplicates = (unique && !(key->key_flags & key_all_nulls)) || primary;
|
||||
const bool validateDuplicates = (unique && !key_all_nulls) || primary;
|
||||
USHORT prefix = 0;
|
||||
RecordNumber newRecordNumber;
|
||||
if (leafPage) {
|
||||
@ -5035,7 +5013,7 @@ static ULONG insert_node(thread_db* tdbb,
|
||||
}
|
||||
// For checking on duplicate nodes we should find the first matching key.
|
||||
UCHAR* pointer = find_node_start_point(bucket, key, 0, &prefix,
|
||||
insertion->iib_descriptor->idx_flags & idx_descending,
|
||||
idx->idx_flags & idx_descending,
|
||||
false, true, validateDuplicates ? NO_VALUE : newRecordNumber);
|
||||
if (!pointer) {
|
||||
return NO_VALUE_PAGE;
|
||||
@ -5605,10 +5583,7 @@ static ULONG insert_node(thread_db* tdbb,
|
||||
// place (in order of record numbers).
|
||||
|
||||
temporary_key nullKey;
|
||||
nullKey.key_length = 0;
|
||||
nullKey.key_flags = 0;
|
||||
nullKey.key_null_segment = 0;
|
||||
BTR_make_null_key(tdbb, insertion->iib_descriptor, &nullKey);
|
||||
BTR_make_null_key(tdbb, idx, &nullKey);
|
||||
|
||||
if (new_key->key_length == nullKey.key_length &&
|
||||
memcmp(new_key->key_data, nullKey.key_data, nullKey.key_length) == 0)
|
||||
@ -5720,7 +5695,7 @@ static contents remove_node(thread_db* tdbb, index_insertion* insertion, WIN* wi
|
||||
|
||||
while (true)
|
||||
{
|
||||
const ULONG number = find_page(page, insertion->iib_key, idx->idx_flags, insertion->iib_number);
|
||||
const ULONG number = find_page(page, insertion->iib_key, idx, insertion->iib_number);
|
||||
|
||||
// we should always find the node, but let's make sure
|
||||
if (number == END_LEVEL)
|
||||
@ -5783,16 +5758,17 @@ static contents remove_leaf_node(thread_db* tdbb, index_insertion* insertion, WI
|
||||
btree_page* page = (btree_page*) window->win_buffer;
|
||||
temporary_key* key = insertion->iib_key;
|
||||
|
||||
const UCHAR idx_flags = insertion->iib_descriptor->idx_flags;
|
||||
const bool validateDuplicates =
|
||||
((idx_flags & idx_unique) && !(key->key_flags & key_all_nulls)) ||
|
||||
(idx_flags & idx_primary);
|
||||
const index_desc* const idx = insertion->iib_descriptor;
|
||||
const bool primary = (idx->idx_flags & idx_primary);
|
||||
const bool unique = (idx->idx_flags & idx_unique);
|
||||
const bool key_all_nulls = (key->key_nulls == (1 << idx->idx_count) - 1);
|
||||
const bool validateDuplicates = (unique && !key_all_nulls) || primary;
|
||||
|
||||
// Look for the first node with the value to be removed.
|
||||
UCHAR* pointer;
|
||||
USHORT prefix;
|
||||
while (!(pointer = find_node_start_point(page, key, 0, &prefix,
|
||||
insertion->iib_descriptor->idx_flags & idx_descending,
|
||||
idx->idx_flags & idx_descending,
|
||||
false, false,
|
||||
validateDuplicates ? NO_VALUE : insertion->iib_number)))
|
||||
{
|
||||
|
@ -48,12 +48,6 @@ struct temporary_key;
|
||||
class jrd_tra;
|
||||
class BtrPageGCLock;
|
||||
|
||||
enum idx_null_state {
|
||||
idx_nulls_none,
|
||||
idx_nulls_some,
|
||||
idx_nulls_all
|
||||
};
|
||||
|
||||
// Index descriptor block -- used to hold info from index root page
|
||||
|
||||
struct index_desc
|
||||
@ -145,7 +139,6 @@ struct index_insertion
|
||||
// these flags are for the key_flags
|
||||
|
||||
const int key_empty = 1; // Key contains empty data / empty string
|
||||
const int key_all_nulls = 2; // All key fields are nulls
|
||||
|
||||
// Temporary key block
|
||||
|
||||
@ -154,16 +147,8 @@ struct temporary_key
|
||||
USHORT key_length;
|
||||
UCHAR key_data[MAX_KEY + 1];
|
||||
UCHAR key_flags;
|
||||
USHORT key_null_segment; // index of first encountered null segment.
|
||||
// Evaluated in BTR_key only and used in IDX_create_index for better
|
||||
// error diagnostics
|
||||
|
||||
// AB: I don't see the use of multiplying with 2 anymore.
|
||||
//UCHAR key_data[MAX_KEY * 2];
|
||||
// This needs to be on a SHORT boundary.
|
||||
// This is because key_data is complemented as
|
||||
// (SSHORT *) if value is negative.
|
||||
// See compress() (JRD/btr.cpp) for more details
|
||||
USHORT key_nulls; // bitmap of encountered null segments,
|
||||
// USHORT is enought to store MAX_INDEX_SEGMENTS bits
|
||||
};
|
||||
|
||||
|
||||
|
@ -33,7 +33,6 @@ USHORT BTR_all(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::IndexDescAlloc**, Jrd::Relat
|
||||
void BTR_complement_key(Jrd::temporary_key*);
|
||||
void BTR_create(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*, USHORT, Firebird::AutoPtr<Jrd::Sort>&, Jrd::SelectivityList&);
|
||||
bool BTR_delete_index(Jrd::thread_db*, Jrd::win*, USHORT);
|
||||
//USHORT BTR_delete_node(Jrd::thread_db*, Ods::btree_page*, USHORT);
|
||||
bool BTR_description(Jrd::thread_db*, Jrd::jrd_rel*, Ods::index_root_page*, Jrd::index_desc*, USHORT);
|
||||
DSC* BTR_eval_expression(Jrd::thread_db*, Jrd::index_desc*, Jrd::Record*, bool&);
|
||||
void BTR_evaluate(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::RecordBitmap**, Jrd::RecordBitmap*);
|
||||
@ -42,15 +41,13 @@ Ods::btree_page* BTR_find_page(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd:
|
||||
Jrd::temporary_key*, Jrd::temporary_key*);
|
||||
void BTR_insert(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*);
|
||||
Jrd::idx_e BTR_key(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::Record*, Jrd::index_desc*, Jrd::temporary_key*,
|
||||
Jrd::idx_null_state*, const bool, USHORT);
|
||||
Jrd::idx_e BTR_key(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::Record*, Jrd::index_desc*, Jrd::temporary_key*,
|
||||
Jrd::idx_null_state*, const bool);
|
||||
const bool, USHORT = 0);
|
||||
USHORT BTR_key_length(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*);
|
||||
Ods::btree_page* BTR_left_handoff(Jrd::thread_db*, Jrd::win*, Ods::btree_page*, SSHORT);
|
||||
USHORT BTR_lookup(Jrd::thread_db*, Jrd::jrd_rel*, USHORT, Jrd::index_desc*, Jrd::RelationPages*);
|
||||
Jrd::idx_e BTR_make_key(Jrd::thread_db*, USHORT, const Jrd::ValueExprNode* const*, const Jrd::index_desc*,
|
||||
Jrd::temporary_key*, bool);
|
||||
void BTR_make_null_key(Jrd::thread_db*, Jrd::index_desc*, Jrd::temporary_key*);
|
||||
void BTR_make_null_key(Jrd::thread_db*, const Jrd::index_desc*, Jrd::temporary_key*);
|
||||
bool BTR_next_index(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::jrd_tra*, Jrd::index_desc*, Jrd::win*);
|
||||
void BTR_remove(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*);
|
||||
void BTR_reserve_slot(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::jrd_tra*, Jrd::index_desc*);
|
||||
|
111
src/jrd/idx.cpp
111
src/jrd/idx.cpp
@ -89,6 +89,20 @@ static bool key_equal(const temporary_key*, const temporary_key*);
|
||||
static void release_index_block(thread_db*, IndexBlock*);
|
||||
static void signal_index_deletion(thread_db*, jrd_rel*, USHORT);
|
||||
|
||||
inline USHORT getNullSegment(const temporary_key& key)
|
||||
{
|
||||
USHORT nulls = key.key_nulls;
|
||||
|
||||
for (USHORT i = 0; nulls; i++)
|
||||
{
|
||||
if (nulls & 1)
|
||||
return i;
|
||||
|
||||
nulls >>= 1;
|
||||
}
|
||||
|
||||
return MAX_USHORT;
|
||||
}
|
||||
|
||||
void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_rel* relation)
|
||||
{
|
||||
@ -263,6 +277,7 @@ void IDX_create_index(thread_db* tdbb,
|
||||
|
||||
const bool isDescending = (idx->idx_flags & idx_descending);
|
||||
const bool isPrimary = (idx->idx_flags & idx_primary);
|
||||
const bool isForeign = (idx->idx_flags & idx_foreign);
|
||||
|
||||
// hvlad: in ODS11 empty string and NULL values can have the same binary
|
||||
// representation in index keys. BTR can distinguish it by the key_length
|
||||
@ -289,8 +304,6 @@ void IDX_create_index(thread_db* tdbb,
|
||||
ifl_data.ifl_duplicates = 0;
|
||||
ifl_data.ifl_key_length = key_length;
|
||||
|
||||
bool key_is_null = false;
|
||||
|
||||
sort_key_def key_desc[2];
|
||||
// Key sort description
|
||||
key_desc[0].skd_dtype = SKD_bytes;
|
||||
@ -314,7 +327,7 @@ void IDX_create_index(thread_db* tdbb,
|
||||
|
||||
jrd_rel* partner_relation = NULL;
|
||||
USHORT partner_index_id = 0;
|
||||
if (idx->idx_flags & idx_foreign)
|
||||
if (isForeign)
|
||||
{
|
||||
if (!MET_lookup_partner(tdbb, relation, idx, index_name)) {
|
||||
BUGCHECK(173); // msg 173 referenced index description not found
|
||||
@ -372,52 +385,28 @@ void IDX_create_index(thread_db* tdbb,
|
||||
{
|
||||
Record* record = stack.pop();
|
||||
|
||||
// If foreign key index is being defined, make sure foreign
|
||||
// key definition will not be violated
|
||||
|
||||
if (idx->idx_flags & idx_foreign)
|
||||
{
|
||||
idx_null_state null_state;
|
||||
// find out if there is a null segment by faking uniqueness --
|
||||
// if there is one, don't bother to check the primary key
|
||||
|
||||
if (!(idx->idx_flags & idx_unique))
|
||||
{
|
||||
idx->idx_flags |= idx_unique;
|
||||
result = BTR_key(tdbb, relation, record, idx, &key, &null_state, false);
|
||||
idx->idx_flags &= ~idx_unique;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = BTR_key(tdbb, relation, record, idx, &key, &null_state, false);
|
||||
}
|
||||
|
||||
if (result == idx_e_ok && null_state == idx_nulls_none)
|
||||
{
|
||||
result = check_partner_index(tdbb, relation, record, transaction, idx,
|
||||
partner_relation, partner_index_id);
|
||||
}
|
||||
}
|
||||
result = BTR_key(tdbb, relation, record, idx, &key, false);
|
||||
|
||||
if (result == idx_e_ok)
|
||||
{
|
||||
idx_null_state null_state;
|
||||
result = BTR_key(tdbb, relation, record, idx, &key, &null_state, false);
|
||||
|
||||
if (result == idx_e_ok)
|
||||
if (isPrimary && key.key_nulls != 0)
|
||||
{
|
||||
if (isPrimary && null_state != idx_nulls_none)
|
||||
{
|
||||
fb_assert(key.key_null_segment < idx->idx_count);
|
||||
const USHORT key_null_segment = getNullSegment(key);
|
||||
fb_assert(key_null_segment < idx->idx_count);
|
||||
const USHORT bad_id = idx->idx_rpt[key_null_segment].idx_field;
|
||||
const jrd_fld *bad_fld = MET_get_field(relation, bad_id);
|
||||
|
||||
const USHORT bad_id = idx->idx_rpt[key.key_null_segment].idx_field;
|
||||
const jrd_fld *bad_fld = MET_get_field(relation, bad_id);
|
||||
ERR_post(Arg::Gds(isc_not_valid) << Arg::Str(bad_fld->fld_name) <<
|
||||
Arg::Str(NULL_STRING_MARK));
|
||||
}
|
||||
|
||||
ERR_post(Arg::Gds(isc_not_valid) << Arg::Str(bad_fld->fld_name) <<
|
||||
Arg::Str(NULL_STRING_MARK));
|
||||
}
|
||||
// If foreign key index is being defined, make sure foreign
|
||||
// key definition will not be violated
|
||||
|
||||
key_is_null = (null_state == idx_nulls_all);
|
||||
if (isForeign && key.key_nulls == 0)
|
||||
{
|
||||
result = check_partner_index(tdbb, relation, record, transaction, idx,
|
||||
partner_relation, partner_index_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -466,19 +455,23 @@ void IDX_create_index(thread_db* tdbb,
|
||||
|
||||
USHORT l = key.key_length;
|
||||
|
||||
if (nullIndLen) {
|
||||
if (nullIndLen)
|
||||
*p++ = (key.key_length == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
if (l > 0)
|
||||
{
|
||||
memcpy(p, key.key_data, l);
|
||||
p += l;
|
||||
}
|
||||
|
||||
if ( (l = key_length - nullIndLen - key.key_length) )
|
||||
{
|
||||
memset(p, pad, l);
|
||||
p += l;
|
||||
}
|
||||
|
||||
const bool key_is_null = (key.key_nulls == (1 << idx->idx_count) - 1);
|
||||
|
||||
index_sort_record* isr = (index_sort_record*) p;
|
||||
isr->isr_record_number = primary.rpb_number.getValue();
|
||||
isr->isr_key_length = key.key_length;
|
||||
@ -717,7 +710,7 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going,
|
||||
{
|
||||
Record* rec1 = stack1.object();
|
||||
|
||||
idx_e result = BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, 0, false);
|
||||
idx_e result = BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, false);
|
||||
if (result != idx_e_ok)
|
||||
{
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
@ -731,7 +724,7 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going,
|
||||
{
|
||||
Record* rec2 = stack2.object();
|
||||
|
||||
result = BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2, 0, false);
|
||||
result = BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2, false);
|
||||
if (result != idx_e_ok)
|
||||
{
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
@ -751,7 +744,7 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going,
|
||||
{
|
||||
Record* rec3 = stack3.object();
|
||||
|
||||
result = BTR_key(tdbb, rpb->rpb_relation, rec3, &idx, &key2, 0, false);
|
||||
result = BTR_key(tdbb, rpb->rpb_relation, rec3, &idx, &key2, false);
|
||||
if (result != idx_e_ok)
|
||||
{
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
@ -821,9 +814,9 @@ idx_e IDX_modify(thread_db* tdbb,
|
||||
*bad_index = idx.idx_id;
|
||||
*bad_relation = new_rpb->rpb_relation;
|
||||
if ((error_code =
|
||||
BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, &idx, &key1, 0, false)) ||
|
||||
BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, &idx, &key1, false)) ||
|
||||
(error_code =
|
||||
BTR_key(tdbb, org_rpb->rpb_relation, org_rpb->rpb_record, &idx, &key2, 0, false)))
|
||||
BTR_key(tdbb, org_rpb->rpb_relation, org_rpb->rpb_record, &idx, &key2, false)))
|
||||
{
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
break;
|
||||
@ -892,9 +885,9 @@ idx_e IDX_modify_check_constraints(thread_db* tdbb,
|
||||
*bad_index = idx.idx_id;
|
||||
*bad_relation = new_rpb->rpb_relation;
|
||||
if ((error_code =
|
||||
BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, &idx, &key1, 0, false)) ||
|
||||
BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, &idx, &key1, false)) ||
|
||||
(error_code =
|
||||
BTR_key(tdbb, org_rpb->rpb_relation, org_rpb->rpb_record, &idx, &key2, 0, false)))
|
||||
BTR_key(tdbb, org_rpb->rpb_relation, org_rpb->rpb_record, &idx, &key2, false)))
|
||||
{
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
break;
|
||||
@ -972,7 +965,7 @@ idx_e IDX_store(thread_db* tdbb, record_param* rpb,
|
||||
{
|
||||
*bad_index = idx.idx_id;
|
||||
*bad_relation = rpb->rpb_relation;
|
||||
if ( (error_code = BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key, 0, false)) )
|
||||
if ( (error_code = BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key, false)) )
|
||||
{
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
break;
|
||||
@ -1276,7 +1269,7 @@ static idx_e check_partner_index(thread_db* tdbb,
|
||||
// tmpIndex.idx_flags |= idx_unique;
|
||||
tmpIndex.idx_flags = (tmpIndex.idx_flags & ~idx_unique) | (partner_idx.idx_flags & idx_unique);
|
||||
temporary_key key;
|
||||
result = BTR_key(tdbb, relation, record, &tmpIndex, &key, 0, starting, segment);
|
||||
result = BTR_key(tdbb, relation, record, &tmpIndex, &key, starting, segment);
|
||||
CCH_RELEASE(tdbb, &window);
|
||||
|
||||
// now check for current duplicates
|
||||
@ -1469,17 +1462,13 @@ static idx_e insert_key(thread_db* tdbb,
|
||||
// check for an insert into the corresponding primary key index
|
||||
if (idx->idx_flags & idx_foreign)
|
||||
{
|
||||
// find out if there is a null segment by faking uniqueness --
|
||||
// if there is one, don't bother to check the primary key
|
||||
|
||||
idx->idx_flags |= idx_unique;
|
||||
// Find out if there is a null segment. If there is one,
|
||||
// don't bother to check the primary key.
|
||||
CCH_FETCH(tdbb, window_ptr, LCK_read, pag_root);
|
||||
temporary_key key;
|
||||
idx_null_state null_state;
|
||||
result = BTR_key(tdbb, relation, record, idx, &key, &null_state, false);
|
||||
result = BTR_key(tdbb, relation, record, idx, &key, false);
|
||||
CCH_RELEASE(tdbb, window_ptr);
|
||||
idx->idx_flags &= ~idx_unique;
|
||||
if (result == idx_e_ok && null_state == idx_nulls_none)
|
||||
if (result == idx_e_ok && key.key_nulls == 0)
|
||||
{
|
||||
result = check_foreign_key(tdbb, record, insertion->iib_relation,
|
||||
transaction, idx, bad_relation, bad_index);
|
||||
|
@ -211,7 +211,7 @@ bool IndexTableScan::getRecord(thread_db* tdbb) const
|
||||
temporary_key value;
|
||||
|
||||
const idx_e result =
|
||||
BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, idx, &value, 0, false);
|
||||
BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, idx, &value, false);
|
||||
|
||||
if (result != idx_e_ok)
|
||||
ERR_duplicate_error(result, rpb->rpb_relation, idx->idx_id);
|
||||
|
Loading…
Reference in New Issue
Block a user