Repository navigation
fix(policy): compare before() enum fields as text on PostgreSQL - #2869
Conversation
The post-update `$before` VALUES table casts enum fields to text, so a rule like `before().state != state` produced `text <> "State"`, which PostgreSQL rejects for native enum columns. Cast both operands to text when one side is a `before()` enum field. Fixes zenstackhq#2727 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
…n collection predicates Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ymc9
left a comment
There was a problem hiding this comment.
Hey @theluckystrike , thanks for making the fix. Changes look good overall. I've made a small additional fix to use context.thisType to check for before(). access, which always points to the model being updated.
|
Thanks @ymc9 for reviewing and adding the context.thisType check for before(). I'm glad the fix is merged.
Thank you,
Michael Lip
Founder of https://zovo.one, 108 Chrome extensions with 150,000+ users
Also building https://makeroll.com and https://ml0x.com, open-source guardrails for Claude Code agents
Open source has to win, and small fixes like this one are my part of it
GitHub Sponsors keeps these fixes coming, https://github.com/sponsors/theluckystrike
|
Fixes #2727
What broke. On PostgreSQL with a native enum column, a post-update rule that compares
before().fieldwith the current field fails withoperator does not exist: text <> "State"instead of evaluating the rule.Root cause. The
$beforetable for post-update checks is built bybuildValuesTableSelect, and the PostgreSQL dialect casts every enum field there totext(packages/orm/src/client/crud/dialects/postgresql.ts:503-505, used at:657). The current row's column keeps its native enum type, and_binaryinpackages/plugins/policy/src/expression-transformer.ts:325-337compares the two as they are. PostgreSQL has no operator betweentextand a user enum type.Fix. In
_binary, when either operand is abefore()access to an enum field, both operands go throughdialect.castTextbeforebuildComparison. Theinbranch already does the same for enum fields. Comparisons that don't involvebefore()enums are unchanged, so normal enum filters keep using the column as is.How I tested it.
tests/regression/test/issue-2727.test.tsruns on PostgreSQL withusePrismaPush: true.devit fails withoperator does not exist: text <> "State". With the fix the denied transition is rejected by policy and the allowed one goes through.@mapon the enum values and@@mapon the enum. It passes with the fix (not added as a test).tests/e2eorm/policyon PostgreSQL 16 and SQLite gives the same results with and without the change. A few tests fail locally on both (for example thenow()tests on PostgreSQL), so that's my environment.pnpm lintinpackages/plugins/policyandtsc --noEmitintests/regressionare clean.Summary by CodeRabbit
before(), so comparisons correctly distinguish allowed and rejected state changes.