Fix infinite loop in takeInternal with undefined values - #1198
Merged
Merged
Conversation
…lues (issue #1186) This test demonstrates the infinite loop bug that occurs when calling take() on a BTreeIndex containing items with undefined indexed values. The bug is in takeInternal() where nextHigherPair(undefined) returns the minimum pair [undefined, undefined], then key is set to pair[0] (undefined), causing the same pair to be returned infinitely since the while condition (pair !== undefined) is always true for arrays. https://claude.ai/code/session_01RKBKXMoKVe1hSXGy3VVEFo
… from the start/end. Also introduce a sentinel such that we never store undefined as a key in the btree.
🦋 Changeset detectedLatest commit: 57b91d5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
More templates
@tanstack/angular-db
@tanstack/db
@tanstack/db-ivm
@tanstack/electric-db-collection
@tanstack/offline-transactions
@tanstack/powersync-db-collection
@tanstack/query-db-collection
@tanstack/react-db
@tanstack/rxdb-db-collection
@tanstack/solid-db
@tanstack/svelte-db
@tanstack/trailbase-db-collection
@tanstack/vue-db
commit: |
Contributor
|
Size Change: +391 B (+0.43%) Total Size: 91.3 kB
ℹ️ View Unchanged
|
Contributor
|
Size Change: 0 B Total Size: 3.7 kB ℹ️ View Unchanged
|
…ue because normalizeValue is also used in other places.
Merged
Contributor
|
🎉 This PR has been released! Thank you for your contribution! |
4 tasks
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Fix: Infinite loop in
BTreeIndex.takeInternalwhen indexed values areundefined(fixes #1186)Problem
When a collection contains items where an indexed field evaluates to
undefined, and the query uses both.orderBy()and.limit(), theBTreeIndex.takeInternalmethod enters an infinite loop.Root Cause:
The underlying BTree implementation uses
undefinedas a special parameter value meaning "start from the beginning" (fornextHigherPair) or "start from the end" (fornextLowerPair). This creates an ambiguity when the actual indexed value isundefined:takeInternalcallsnextHigherPair(undefined)to get the first elementundefined(the actual indexed value)takeInternalsetskey = pair[0]which isundefinednextHigherPair(undefined)Solution
1. New explicit methods for starting from beginning/end
Added two new methods to clearly distinguish between "start from a value" and "start from the beginning/end":
takeFromStart(n, filterFn?)- Returns the first n items from the beginningtakeReversedFromEnd(n, filterFn?)- Returns the last n items from the endThe existing
take(n, from, filterFn?)andtakeReversed(n, from, filterFn?)methods now require afromvalue (which can beundefinedas an actual indexed value).2. Sentinel value for
undefinedSince the BTree cannot store
undefinedas a key (it has special meaning), we normalizeundefinedvalues to a sentinel string__TS_DB_BTREE_UNDEFINED_VALUE__:undefinedis converted to the sentinelundefinedparameter) and "the key is the undefined value" (sentinel string)3. Comparison function wrapper
The BTree's comparison function is wrapped to convert the sentinel back to
undefinedbefore comparison. This ensures that the sentinel compares exactly asundefinedwould (respecting nulls-first/nulls-last ordering).Changes
comparison.ts: AddedUNDEFINED_SENTINELconstant,normalizeValueForBTreenow convertsundefinedto sentinel, addeddenormalizeUndefinedfunctionbtree-index.ts: AddedtakeFromStartandtakeReversedFromEndmethods, wrapped comparator to denormalize sentinel before comparisonbase-index.ts: Updated interface and abstract class with new method signaturesreverse-index.ts: Added new methods that delegate appropriatelysubscription.tsandchange-events.tsto use the new methods where appropriate