Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions src/jrd/recsrc/IndexTableScan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,16 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const
}

index_desc* const idx = (index_desc*) ((SCHAR*) impure + m_offset);
const bool descending = (idx->idx_flags & idx_descending);

// find the last fetched position from the index
const USHORT pageSpaceID = m_relation->getPages(tdbb)->rel_pg_space_id;
win window(pageSpaceID, impure->irsb_nav_page);

const IndexRetrieval* const retrieval = m_index->retrieval;
const USHORT flags = retrieval->irb_generic & (irb_descending | irb_partial | irb_starting);
const bool ignoreNulls =
(retrieval->irb_generic & irb_ignore_null_value_key) && (idx->idx_count == 1);

do
{
Expand Down Expand Up @@ -252,6 +255,14 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const
continue;
}

// If we're walking in a descending index and we need to ignore NULLs
// then stop at the first NULL we see (only for single segment!)
if (descending && ignoreNulls && node.prefix == 0 &&
node.length >= 1 && node.data[0] == 255)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accordingly to the compress():
// Further a NULL state is always returned as 1 byte 0xFF (descending index).
So, why check for >= 1, not == 1 ?
And, for consistency, please use 0xFF, not 255.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a copy-paste from btr.cpp :) where such a check is used twice. I don't mind changing it, but I'd suggest this to be done in btr.cpp too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also wondering whether we really should check for (node.prefix == 0 && node.length == 1 && node.data[0] == 0xFF) -- i.e. the first NULL encountered -- or better for (node.prefix + node.length == 1 && node.data[0] == 0xFF) -- i.e. any NULL encountered, as more reliable. Is it theoretically possible that we somehow jump to the bucket in the middle of the NULL duplicates chain and start scanning from there, thus skipping the first NULL node?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also wondering whether we really should check for
(node.prefix == 0 && node.length == 1 && node.data[0] == 0xFF) -- i.e. the first NULL encountered -- or better
for
(node.prefix + node.length == 1 && node.data[0] == 0xFF) -- i.e. any NULL encountered, as more reliable.

Second way is not correct: when node.length == 0 we should not access node.data[0] at all.
Instead, we could look at key contents, if necessary.

Is it theoretically possible that we somehow jump to the bucket in the middle of the NULL duplicates chain and start scanning from there, thus skipping the first NULL node?

The position is based on the last key processed, so, we should be safe.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a copy-paste from btr.cpp :) where such a check is used twice. I don't mind changing it, but I'd suggest this to be done in btr.cpp too.

Ok, then, let it be this way

{
break;
}

// Build the current key value from the prefix and current node data.
memcpy(key.key_data + node.prefix, node.data, node.length);
key.key_length = node.length + node.prefix;
Expand Down Expand Up @@ -297,12 +308,16 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const
break;
}

// skip this record if:
// 1) there is an inversion tree for this index and this record
const bool skipNullKey = (ignoreNulls && !descending && !key.key_length);

// Skip this record if:
// 1) NULL key is found and we were asked to ignore them, or
// 2) there is an inversion tree for this index and this record
// is not in the bitmap for the inversion, or
// 2) the record has already been visited
// 3) the record has already been visited

if ((!(impure->irsb_flags & irsb_mustread) &&
if (skipNullKey ||
(!(impure->irsb_flags & irsb_mustread) &&
(!impure->irsb_nav_bitmap ||
!RecordBitmap::test(*impure->irsb_nav_bitmap, number.getValue()))) ||
RecordBitmap::test(impure->irsb_nav_records_visited, number.getValue()))
Expand Down
Loading