-
Notifications
You must be signed in to change notification settings - Fork 3
fix(engine): seek_key must honor SET DELETED ON #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Admnwk
wants to merge
2
commits into
FiveTechSoft:main
Choose a base branch
from
Admnwk:fix/seek-honor-set-deleted
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1039,6 +1039,33 @@ Table::seek_key(const std::string& key, bool soft, bool last) { | |
| return false; | ||
| } | ||
| bool exact = r.value().hit == drivers::SeekHit::Exact; | ||
| // SET DELETED ON: a seek must not report a deleted row as found. | ||
| // goto_top/goto_bottom/skip already honor show_deleted() but seek_key | ||
| // did not (the B+tree match landed on a deleted row and was returned | ||
| // as found). Skip forward over deleted records at the landing, then | ||
| // re-derive `exact` from the row we actually land on (Clipper/DBFCDX: | ||
| // if the only matching rows are deleted, Found() is .F. and the cursor | ||
| // sits on the next live record or Eof). | ||
| if (!openads::abi::show_deleted()) { | ||
| std::string del_key = key; | ||
| del_key.resize(idx->key_length(), ' '); | ||
| while (r.value().positioned) { | ||
| if (auto ld = load_record_(r.value().recno); !ld) return ld.error(); | ||
| if (!is_deleted()) break; | ||
| r = idx->next(); | ||
| if (!r) return r.error(); | ||
| } | ||
| if (!r.value().positioned) { | ||
| state_ = (driver_->record_count() == 0) ? State::Limbo | ||
| : State::Eof; | ||
| recno_ = 0; | ||
| last_seek_found_ = false; | ||
| return false; | ||
| } | ||
| std::string ck = idx->current_key(); | ||
| ck.resize(del_key.size(), ' '); | ||
| exact = (ck == del_key); | ||
| } | ||
|
Comment on lines
+1049
to
+1068
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The manual padding and resizing of if (!openads::abi::show_deleted()) {
std::string del_key = key;
del_key.resize(idx->key_length(), ' ');
while (r.value().positioned) {
if (auto ld = load_record_(r.value().recno); !ld) return ld.error();
if (!is_deleted()) break;
r = idx->next();
if (!r) return r.error();
}
if (!r.value().positioned) {
state_ = (driver_->record_count() == 0) ? State::Limbo
: State::Eof;
recno_ = 0;
last_seek_found_ = false;
return false;
}
std::string ck = idx->current_key();
ck.resize(del_key.size(), ' ');
exact = (ck == del_key);
} |
||
| // DESCEND order treats the FIRST match in walk direction as | ||
| // the LAST entry in the equal-key group when sorted ASC. Walk | ||
| // duplicates regardless of `last` flag. | ||
|
|
@@ -1050,19 +1077,14 @@ Table::seek_key(const std::string& key, bool soft, bool last) { | |
| // the last matching entry; load_record_ syncs the table buffer. | ||
| if (walk_to_last && exact) { | ||
| std::string padded_key = key; | ||
| if (padded_key.size() < idx->key_length()) | ||
| padded_key.append(idx->key_length() - padded_key.size(), ' '); | ||
| if (padded_key.size() > idx->key_length()) | ||
| padded_key.resize(idx->key_length()); | ||
| padded_key.resize(idx->key_length(), ' '); | ||
| std::uint32_t last_recno = r.value().recno; | ||
| while (true) { | ||
| auto step = idx->next(); | ||
| if (!step || !step.value().positioned) break; | ||
| std::string ck = idx->current_key(); | ||
| if (ck.size() < padded_key.size()) | ||
| ck.append(padded_key.size() - ck.size(), ' '); | ||
| if (std::memcmp(ck.data(), padded_key.data(), | ||
| padded_key.size()) != 0) { | ||
| ck.resize(padded_key.size(), ' '); | ||
| if (ck != padded_key) { | ||
| // Past the equal-key run — step back one to leave | ||
| // cursor on the last matching entry. | ||
| (void)idx->prev(); | ||
|
|
@@ -1072,6 +1094,29 @@ Table::seek_key(const std::string& key, bool soft, bool last) { | |
| } | ||
| auto load = load_record_(last_recno); | ||
| if (!load) return load.error(); | ||
| if (!openads::abi::show_deleted()) { | ||
| while (is_deleted()) { | ||
| auto step = idx->prev(); | ||
| if (!step || !step.value().positioned) { | ||
| state_ = (driver_->record_count() == 0) ? State::Limbo | ||
| : State::Eof; | ||
| recno_ = 0; | ||
| last_seek_found_ = false; | ||
| return false; | ||
| } | ||
| std::string ck = idx->current_key(); | ||
| ck.resize(padded_key.size(), ' '); | ||
| if (ck != padded_key) { | ||
| state_ = (driver_->record_count() == 0) ? State::Limbo | ||
| : State::Eof; | ||
| recno_ = 0; | ||
| last_seek_found_ = false; | ||
| return false; | ||
| } | ||
| if (auto ld = load_record_(step.value().recno); !ld) | ||
| return ld.error(); | ||
| } | ||
| } | ||
| last_seek_found_ = true; | ||
| return true; | ||
| } | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
SET DELETED ONis active, if the seek lands on an exact match andwalk_to_lastis true (which is always the case for descending indexes or whenlastis true), the code walks forward to the last matching key without checking if those records are deleted. If the last matching record is deleted, the seek will land on it and report it as found, violatingSET DELETED ON.To fix this, after the
walk_to_lastloop finishes and loads the record, ifSET DELETED ONis active and the loaded record is deleted, we should walk backward usingidx->prev()until we find a non-deleted record. Since we started at a known active matching record, we are guaranteed to find one.Here is how the
walk_to_lastblock (lines 1083-1109) should be updated: