Skip to content

feat: support raw SQL expression cursors (v0.7.0)#36

Merged
xantiagoma merged 2 commits intomainfrom
feat/sql-cursor-support
May 1, 2026
Merged

feat: support raw SQL expression cursors (v0.7.0)#36
xantiagoma merged 2 commits intomainfrom
feat/sql-cursor-support

Conversation

@xantiagoma
Copy link
Copy Markdown
Owner

@xantiagoma xantiagoma commented May 1, 2026

Summary

  • Add SQLCursor type alongside TableCursorCursor is now a discriminated union of both, both exported as named types
  • Make CursorConfig<C> generic so cursor types are fully inferred at call sites without manual annotations
  • cursor.orderBy and cursor.where() branch on "sql" in cursor at runtime, supporting both table columns and SQL expressions
  • cursor.relations.orderBy returns () => SQL[] when any cursor uses sql (auto-inferred via ConfigHasSqlCursor<C> conditional type), Record<string, "asc"|"desc"> otherwise — no manual type annotation needed
  • cursor.relations.where() builds { RAW: () => SQL } entries for SQL cursors alongside plain field conditions for table cursors
  • Compat fixtures (drizzle-v0, drizzle-v1) extended with SQL expression cursor pagination tests using rank::text || '-' || upper(firstName)
  • README documents SQL cursor usage, RQB v2 behaviour, and the table-alias limitation for relations.where() with SQL expressions

Test plan

  • pnpm exec vitest run — 193 tests pass
  • pnpm exec tsc --noEmit — no type errors
  • pnpm run test:compat — both drizzle-v0 and drizzle-v1 fixtures pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for SQL-expression-based cursors. Users can now define cursor entries using Drizzle SQL expressions for enhanced pagination flexibility alongside traditional table-column cursors.
  • Documentation

    • Updated README with SQL cursor documentation, including usage examples, type references, and best practices for query builders and pagination.
  • Chores

    • Bumped version to 0.7.0.

xantiagoma and others added 2 commits May 1, 2026 01:41
Allow Drizzle `sql` expressions (not just table columns) as cursor
values, enabling pagination by computed fields like upper(name),
concatenated strings, or any DB-evaluable expression.

- `Cursor` is now a discriminated union of `TableCursor` (schema) and
  `SQLCursor` (sql) — both exported as named types
- `CursorConfig<C>` is generic so call-site types are inferred without
  manual annotations
- `generateCursor` uses `<C extends CursorConfig>` so TypeScript infers
  the exact config literal; no column-type unification issues
- `cursor.orderBy` and `cursor.where()` branch on `"sql" in cursor` at
  runtime for both table and SQL cursors
- `cursor.relations.orderBy` returns `() => SQL[]` when any cursor uses
  `sql`, and `Record<string, "asc"|"desc">` otherwise — resolved via
  `ConfigHasSqlCursor<C>` conditional type with no manual annotations
- `cursor.relations.where()` builds `{ RAW: () => SQL }` entries for SQL
  cursors alongside plain field conditions for table cursors
- Compat fixtures (drizzle-v0, drizzle-v1) extended with `items` table
  and SQL expression cursor pagination tests
- README documents SQL cursor usage, RQB v2 behaviour, and the
  table-alias limitation for `relations.where()` with SQL expressions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6af8259a-13f8-410b-b6ce-8218f1b6b576

📥 Commits

Reviewing files that changed from the base of the PR and between ad4b8a0 and d036bdf.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • README.md
  • package.json
  • src/generateCursor.ts
  • src/types.ts
  • test-fixtures/drizzle-v0/test.ts
  • test-fixtures/drizzle-v1/test.ts
  • test/generateCursor.test.ts
  • test/relations-helper.test.ts

📝 Walkthrough

Walkthrough

This PR introduces SQL-expression-based cursors as a new feature. The type system is updated to define cursors as a union of table-based (TableCursor) and SQL-expression-based (SQLCursor) variants, making CursorConfig generic to improve type inference. The generateCursor function is enhanced with conditional type logic and runtime branching to handle both cursor types, generating appropriate SQL expressions and predicate structures. Documentation and package version are updated to reflect the 0.7.0 release.

Changes

Cohort / File(s) Summary
Documentation & Metadata
CHANGELOG.md, README.md, package.json
Version bump to 0.7.0 with changelog entry. README expanded with SQL-expression cursor support guide, typed examples, and notes on conditional orderBy/where behavior.
Type System
src/types.ts
Cursor redefined as union of TableCursor and SQLCursor (with sql: SQL field). CursorConfig made generic (CursorConfig<C extends Cursor>). RelationsWhere extended with optional RAW predicate returning SQL.
Core Implementation
src/generateCursor.ts
Function now generic over cursor config type. Runtime logic branches per cursor: sql cursors use asc/desc(cursor.sql) and generate RAW SQL comparisons; table cursors continue with column-based logic. relations.orderBy conditionally returns callback (() => SQL[]) or record based on presence of SQL cursors.
Test Fixtures
test-fixtures/drizzle-v0/test.ts, test-fixtures/drizzle-v1/test.ts
Added items table and SQL-expression cursor test flows. Validates pagination using derived SQL sort keys (rank::text || '-' || ...) alongside primary id cursors across query builder and v1 paths.
Test Coverage
test/generateCursor.test.ts, test/relations-helper.test.ts
Extensive Vitest coverage for SQL cursor ordering, predicate generation, token serialization, and RAW clause behavior. Relations-helper contract tests verify callback-based orderBy and mixed SQL/schema cursor comparisons.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 Behold, the SQL cursor hops,
A union dance where logic stops—
Generic types and branching flows,
SQL expressions steal the shows!
Pagination leaps both old and new, 🎉

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sql-cursor-support

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@xantiagoma xantiagoma merged commit 627b6ee into main May 1, 2026
4 of 5 checks passed
@xantiagoma xantiagoma deleted the feat/sql-cursor-support branch May 1, 2026 06:44
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.

1 participant