Skip to content

fix(orm): parenthesize an inlined computed field expression - #2796

Open
evgenovalov wants to merge 1 commit into
zenstackhq:devfrom
evgenovalov:fix/parenthesize-inlined-computed-field
Open

fix(orm): parenthesize an inlined computed field expression#2796
evgenovalov wants to merge 1 commit into
zenstackhq:devfrom
evgenovalov:fix/parenthesize-inlined-computed-field

Conversation

@evgenovalov

@evgenovalov evgenovalov commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #2795

Motivation

A computed field is inlined into larger expressions — a boolean filter renders it as <expr> = $n. If the implementation's top-level node is a bare comparison, the operator precedence leaks into the surrounding query:

computedFields: {
    post: {
        isMine: (eb) => eb('authorId', '=', 1),
    },
},
await db.post.findMany({ where: { isMine: true } });
error: syntax error at or near "="
-- before
select "Post"."id" as "id", "authorId" = $1 as "isMine" from "public"."Post" where "authorId" = $2 = $3
-- after
select "Post"."id" as "id", ("authorId" = $1) as "isMine" from "public"."Post" where ("authorId" = $2) = $3

Postgres rejects the chained = outright. SQLite and MySQL parse it left-associatively as (a = $2) = $3, which happens to be the intended semantics, so the bug is invisible there — it surfaced only on the Postgres CI matrix of #2789.

Changes

  • BaseCrudDialect.fieldRef() wraps the implementation's expression in eb.parens() when inlining a computed field.
  • e2e test in computed-fields.test.ts (runs on every provider in the matrix): a bare-comparison field asserted through where: { isMine: true } / { isMine: false } / NOT, plus an eb.or-based field as a no-regression guard.

The wrap is unconditional because Kysely skips it for an expression that is already parenthesized, so eb.or / eb.and and subquery implementations compile byte-for-byte as before; only bare comparisons, unary operations and raw fragments change. Verified against the compiled SQL for each shape.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved filtering for computed fields that use comparisons or combined logical expressions.
    • Ensured computed-field expressions are grouped correctly when used in larger queries.
    • Preserved support for true, false, NOT, and OR filtering behavior.

A computed field is inlined into larger expressions — a boolean filter renders it as
`<expr> = $n` — so an implementation whose top-level node is a bare comparison produced
a chained `"authorId" = $2 = $3`. Postgres rejects that as a syntax error; sqlite and
mysql only parse it correctly by accident of left associativity.

Wrap the implementation's expression in parens so its operator precedence stays
contained. Kysely doesn't double-wrap an already parenthesized expression, so `eb.or`
/`eb.and`-based and subquery implementations compile unchanged.

Fixes zenstackhq#2795

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ffab305a-d87a-4cc2-b397-c646bf99b6dc

📥 Commits

Reviewing files that changed from the base of the PR and between 797b5bc and c67c8a2.

📒 Files selected for processing (2)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • tests/e2e/orm/client-api/computed-fields.test.ts

📝 Walkthrough

Walkthrough

The ORM now parenthesizes computed-field expressions before embedding them in larger SQL expressions. End-to-end tests cover direct, negated, and logical boolean filters while confirming computed-field reads.

Changes

Computed field filtering

Layer / File(s) Summary
Parenthesized computed expressions and boolean filter coverage
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/computed-fields.test.ts
fieldRef now wraps computed-field expressions in parentheses and continues forwarding query-time arguments. End-to-end tests cover direct, NOT, and OR boolean filters and computed-field reads.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: ymc9, jiashengguo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary fix: parenthesizing inlined computed field expressions.
Linked Issues check ✅ Passed The implementation and tests satisfy issue #2795 by parenthesizing computed expressions and covering boolean, NOT, and OR filters.
Out of Scope Changes check ✅ Passed All changes support issue #2795 and remain limited to the dialect fix and related end-to-end regression tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/orm/src/client/crud/dialects/base-dialect.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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.

Computed field expression is not parenthesized when inlined, breaking boolean filters on Postgres

1 participant