-
Notifications
You must be signed in to change notification settings - Fork 162
Fix infinite loop in takeInternal with undefined values
#1198
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
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: |
|
Size Change: +391 B (+0.43%) Total Size: 91.3 kB
ℹ️ View Unchanged
|
|
Size Change: 0 B Total Size: 3.7 kB ℹ️ View Unchanged
|
…ue because normalizeValue is also used in other places.
KyleAMathews
left a comment
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.
![]()
|
🎉 This PR has been released! Thank you for your contribution! |
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