Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

@KyleAMathews KyleAMathews commented Oct 24, 2025

Summary

Fixes a Discord bug where using the same collection alias in both a parent query and subquery causes empty results or incorrect aggregation values.

Problem

When both parent and subquery use the same alias for a direct collection reference:

const locksAgg = q
  .from({ lock: c.locksCollection })
  .join({ vote: c.votesCollection }, ...)  // Uses "vote"

return q
  .from({ vote: c.votesCollection })  // Also uses "vote" directly
  .join({ lock: locksAgg }, ...)

Result:

  • Empty query results
  • Incorrect aggregation (values from individual rows instead of aggregates)

Root Cause: Both queries share the same input stream, causing interference.

Solution

Added validation that throws a clear DuplicateAliasInSubqueryError when this pattern is detected.

Implementation:

  • New error type with helpful message suggesting to rename the alias
  • collectDirectCollectionAliases(): Identifies conflicting aliases
  • validateSubqueryAliases(): Validates before compiling subqueries
  • Only validates DIRECT collection references (allows legitimate subquery wrapping)

User Fix:
Simply rename the alias in either the parent or subquery:

const locksAgg = q
  .from({ lock: c.locksCollection })
  .join({ v: c.votesCollection }, ...)  // Renamed "vote" to "v"

return q
  .from({ vote: c.votesCollection })  // No conflict
  .join({ lock: locksAgg }, ...)

Testing

  • Added discord-alias-bug.test.ts with comprehensive test coverage
  • Validates error is thrown for conflicting aliases
  • Verifies workaround works correctly

Related

Fixes Discord bug reported by JustTheSyme.

Investigated Discord bug report where using the same collection with the
same alias in both a subquery and main query causes:
1. Empty results when subquery has joins
2. Aggregation cross-leaking (wrong aggregated values)

ROOT CAUSE:
- Parent and subquery share the same input streams from allInputs when
  they use the same alias
- IVM stream sharing causes interference between query contexts
- Caching compounds the issue by reusing compilations with wrong bindings

ATTEMPTED FIXES (all unsuccessful):
- Input filtering: Doesn't solve the core issue of shared streams
- Fresh caching: Breaks existing caching tests and doesn't fix the bug
- Both approaches together: Still fails

CONCLUSION:
This requires a fundamental architectural change to support independent
input streams per query context. Current workaround: wrap query in an
extra from() layer to avoid alias conflicts.

See INVESTIGATION_SAME_COLLECTION_BUG.md for full details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@changeset-bot
Copy link

changeset-bot bot commented Oct 24, 2025

🦋 Changeset detected

Latest commit: 632ef93

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@tanstack/db Patch
@tanstack/angular-db Patch
@tanstack/electric-db-collection Patch
@tanstack/query-db-collection Patch
@tanstack/react-db Patch
@tanstack/rxdb-db-collection Patch
@tanstack/solid-db Patch
@tanstack/svelte-db Patch
@tanstack/trailbase-db-collection Patch
@tanstack/vue-db Patch
todos Patch
@tanstack/db-example-react-todo Patch

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

@KyleAMathews KyleAMathews marked this pull request as draft October 24, 2025 18:20
@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 24, 2025

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@719

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@719

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@719

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@719

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@719

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@719

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@719

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@719

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@719

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@719

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@719

commit: 632ef93

@github-actions
Copy link
Contributor

github-actions bot commented Oct 24, 2025

Size Change: +372 B (+0.44%)

Total Size: 84.7 kB

Filename Size Change
./packages/db/dist/esm/errors.js 3.6 kB +114 B (+3.27%)
./packages/db/dist/esm/index.js 1.64 kB +15 B (+0.92%)
./packages/db/dist/esm/query/compiler/index.js 2.45 kB +243 B (+11.02%) ⚠️
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.63 kB
./packages/db/dist/esm/collection/changes.js 1.01 kB
./packages/db/dist/esm/collection/events.js 413 B
./packages/db/dist/esm/collection/index.js 3.23 kB
./packages/db/dist/esm/collection/indexes.js 1.16 kB
./packages/db/dist/esm/collection/lifecycle.js 1.8 kB
./packages/db/dist/esm/collection/mutations.js 2.52 kB
./packages/db/dist/esm/collection/state.js 3.8 kB
./packages/db/dist/esm/collection/subscription.js 2.2 kB
./packages/db/dist/esm/collection/sync.js 2.2 kB
./packages/db/dist/esm/deferred.js 230 B
./packages/db/dist/esm/event-emitter.js 798 B
./packages/db/dist/esm/indexes/auto-index.js 794 B
./packages/db/dist/esm/indexes/base-index.js 835 B
./packages/db/dist/esm/indexes/btree-index.js 2 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.21 kB
./packages/db/dist/esm/indexes/reverse-index.js 577 B
./packages/db/dist/esm/local-only.js 967 B
./packages/db/dist/esm/local-storage.js 2.4 kB
./packages/db/dist/esm/optimistic-action.js 294 B
./packages/db/dist/esm/proxy.js 3.86 kB
./packages/db/dist/esm/query/builder/functions.js 615 B
./packages/db/dist/esm/query/builder/index.js 4.04 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 938 B
./packages/db/dist/esm/query/compiler/evaluators.js 1.55 kB
./packages/db/dist/esm/query/compiler/expressions.js 760 B
./packages/db/dist/esm/query/compiler/group-by.js 2.04 kB
./packages/db/dist/esm/query/compiler/joins.js 2.65 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.43 kB
./packages/db/dist/esm/query/compiler/select.js 1.28 kB
./packages/db/dist/esm/query/ir.js 785 B
./packages/db/dist/esm/query/live-query-collection.js 404 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.54 kB
./packages/db/dist/esm/query/live/collection-registry.js 233 B
./packages/db/dist/esm/query/live/collection-subscriber.js 2.11 kB
./packages/db/dist/esm/query/optimizer.js 3.26 kB
./packages/db/dist/esm/scheduler.js 1.29 kB
./packages/db/dist/esm/SortedMap.js 1.24 kB
./packages/db/dist/esm/transactions.js 3.05 kB
./packages/db/dist/esm/utils.js 1.01 kB
./packages/db/dist/esm/utils/browser-polyfills.js 365 B
./packages/db/dist/esm/utils/btree.js 6.01 kB
./packages/db/dist/esm/utils/comparison.js 754 B
./packages/db/dist/esm/utils/index-optimization.js 1.73 kB

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

github-actions bot commented Oct 24, 2025

Size Change: 0 B

Total Size: 2.89 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 168 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.41 kB
./packages/react-db/dist/esm/useLiveQuery.js 1.31 kB

compressed-size-action::react-db-package-size

Implements validation to prevent the Discord bug where using the same
alias for a collection in both parent query and subquery causes empty
results or incorrect aggregation values.

The issue occurs when both parent and subquery use the same alias to
reference a collection directly (e.g., both use `vote: votesCollection`).
This causes them to share the same input stream, leading to interference.

Solution: Add validation that throws a clear DuplicateAliasInSubqueryError
when this pattern is detected.

Implementation:
- New error: DuplicateAliasInSubqueryError with clear message
- collectDirectCollectionAliases(): Collects only direct CollectionRef aliases
- validateSubqueryAliases(): Checks for conflicts before compiling subqueries
- Only validates DIRECT collection references, not QueryRef (subquery outputs)

This allows legitimate patterns like:
  const sub = q.from({ issue: collection })
  q.from({ issue: sub })  // OK - different scopes

But prevents problematic patterns like:
  const sub = q.from({ x: coll }).join({ vote: voteColl }, ...)
  q.from({ vote: voteColl }).join({ x: sub }, ...)  // Error!

Users should rename the alias in either parent or subquery to fix.

Fixes Discord bug reported by JustTheSyme.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Cleaned up INVESTIGATION_SAME_COLLECTION_BUG.md to focus on solution
- Added PR_UPDATE.md with suggested title and body for PR #719
- Added changeset for the alias validation fix

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@KyleAMathews KyleAMathews changed the title [Will be closed]: Investigate Discord Bug Report fix: validate against duplicate collection aliases in subqueries Oct 24, 2025
@KyleAMathews KyleAMathews marked this pull request as ready for review October 24, 2025 19:16
Removed investigation and PR update docs that were used during development.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Moved the validation for duplicate collection aliases in subqueries to run
before query optimization rather than during compilation. This prevents false
positives from optimizer-generated subqueries while still catching user-written
duplicate aliases.

The optimizer can legitimately create internal subqueries that reuse aliases
(e.g., for predicate pushdown), but users should not be allowed to write
queries with conflicting aliases between parent and subquery.

Changes:
- Added validateQueryStructure() in compiler/index.ts that recursively validates
  the entire query tree before optimization
- Removed validation logic from processFrom() and processJoinSource() in joins.ts
- Fixed TypeScript type errors in discord-alias-bug.test.ts

All tests pass (1403 tests, 0 type errors).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants