Skip to content

fix: prevent double .where() from overwriting scope predicate#21

Merged
benvinegar merged 2 commits into
mainfrom
fix/double-where-overwrites-scope
Jun 26, 2026
Merged

fix: prevent double .where() from overwriting scope predicate#21
benvinegar merged 2 commits into
mainfrom
fix/double-where-overwrites-scope

Conversation

@benvinegar

Copy link
Copy Markdown
Member

Problem

Drizzle's where() overwrites config.where rather than ANDing. The scoped where() returned the raw Drizzle builder, so a second .where() call replaced the injected scope predicate entirely (Issue #19).

scopedDb
  .select()
  .from(projects)
  .where(eq(projects.workspaceId, "workspace-1"))  // injects scope
  .where(eq(projects.id, "p1"));                     // overwrites — scope gone
// Executes: WHERE projects.id = 'p1'  (no workspace_id)

The ScopedWhereBuilder type hid .where() from TS users, but at runtime the raw builder exposed it — reachable by JS users, as any casters, or any-typed variables.

Fix

Three changes, all returning facades instead of raw builders:

  1. Select: where() now returns a ScopedWhereBuilder facade (built from a Promise + limit/offset/orderBy methods) that does NOT expose .where() at runtime. limit/offset/orderBy delegate to the raw builder and return the same facade.

  2. Update: set().where() returns a thenable facade that hides .where() and other scope-unsafe builder methods.

  3. Delete: where() returns the same thenable facade.

The facades are proper Promises (via Object.assign(promise, methods)), so await and .then() work normally. The scope predicate cannot be overwritten because .where() simply doesn't exist on the returned objects.

Tests

Added 5 regression tests:

  • Double .where() on select is prevented (facade has no .where)
  • Scope predicate survives through .limit()/.offset()/.orderBy() chaining
  • Double .where() on update is prevented
  • Double .where() on delete is prevented
  • Update/delete results are awaitable as promises

Also updated the fake DB's where() to simulate Drizzle's overwrite behavior (returns an object with .where() that overwrites, not ANDs).

Validation

pnpm format:check  ✓
pnpm lint          ✓  (0 warnings, 0 errors)
pnpm typecheck     ✓
pnpm test          ✓  (40/40, +5 new tests)
pnpm coverage      ✓  (100% statements/branches/functions/lines)
pnpm build         ✓

Closes #19

@benvinegar benvinegar merged commit 5f89d8a into main Jun 26, 2026
4 checks passed
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.

Double .where() overwrites the injected scope predicate

1 participant