Create policy compares stored enum array with text[] candidate input (PostgreSQL 42883)
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 54/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- postgresql, typescript
- Lĩnh vực
- authorization, databases
Hướng nghiên cứu
Start with the create-policy preflight shown in the issue and inspect how the virtual create row gets its enum-array type; the reported SQL casts the candidate to text[]. Run the supplied schema.zmodel and repro.ts against PostgreSQL to confirm the 42883 error. Done when a matching array passes policy, a different array is rejected, and equality preserves array order and multiplicity.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Create-policy equality between a stored enum array and a candidate create array produces a PostgreSQL enum[] = text[] comparison in ZenStack 3.9.7. A matching list should pass the policy; it instead raises PostgreSQL 42883. The create-input virtual row casts the enum field to text[].
This appears distinct from #2818: the enum below has no mappings, and ordinary enum-array reads/writes work. The failure is in the create-policy preflight comparison.
Environment
- @zenstackhq/cli, orm, plugin-policy and schema: 3.9.7
- PostgreSQL 16 (pgvector/pgvector:pg16); the application regression also reproduces against PGlite 0.4.1
- Kysely 0.29.6, pg 8.13.1+, Node.js 22.23.3, pnpm 10.18.2
Reproduction
Save this as schema.zmodel, set DATABASE_URL to a PostgreSQL connection, and run pnpm exec zen generate:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum Permission {
VIEW
EDIT
}
model Anchor {
id Int @id
approvals Approval[]
roles Role[]
}
model Approval {
id Int @id
anchorId Int
anchor Anchor @relation(fields: [anchorId], references: [id])
permissions Permission[]
}
model Role {
id Int @id
anchorId Int
anchor Anchor @relation(fields: [anchorId], references: [id])
permissions Permission[]
@@allow('read', true)
@@allow('create', anchor.approvals?[permissions == this.permissions])
}
plugin policy {
provider = "@zenstackhq/plugin-policy"
}
The following repro.ts creates and drops its own isolated database. Set TEST_POSTGRES_URL to an admin connection with CREATEDB, then run pnpm exec tsx repro.ts (with "type": "module" in package.json). The DDL is included to avoid relying on migration state.
import { randomUUID } from 'node:crypto';
import { ZenStackClient } from '@zenstackhq/orm';
import { PolicyPlugin } from '@zenstackhq/plugin-policy';
import { PostgresDialect } from 'kysely';
import { Pool } from 'pg';
import { schema } from './schema';
const admin = new Pool({ connectionString: process.env.TEST_POSTGRES_URL, max: 1 });
const name = `enum_array_repro_${randomUUID().replaceAll('-', '')}`;
await admin.query(`CREATE DATABASE "${name}"`);
const url = new URL(process.env.TEST_POSTGRES_URL!);
url.pathname = `/${name}`;
const pool = new Pool({ connectionString: url.toString(), max: 1 });
try {
await pool.query(`
CREATE TYPE "Permission" AS ENUM ('VIEW', 'EDIT');
CREATE TABLE "Anchor" (id integer PRIMARY KEY);
CREATE TABLE "Approval" (id integer PRIMARY KEY, "anchorId" integer REFERENCES "Anchor"(id), permissions "Permission"[] NOT NULL);
CREATE TABLE "Role" (id integer PRIMARY KEY, "anchorId" integer REFERENCES "Anchor"(id), permissions "Permission"[] NOT NULL);
`);
const raw = new ZenStackClient(schema, { dialect: new PostgresDialect({ pool }) });
await raw.anchor.create({ data: { id: 1 } });
await raw.approval.create({ data: { id: 1, anchorId: 1, permissions: ['VIEW'] } });
const db = raw.$use(new PolicyPlugin());
try {
await db.role.create({ data: { id: 1, anchorId: 1, permissions: ['VIEW'] } });
console.log('Unexpected success');
process.exitCode = 1;
} catch (error: any) {
console.log(JSON.stringify({ reason: error.reason, code: error.dbErrorCode, message: error.dbErrorMessage, sql: error.sql, sqlParams: error.sqlParams }, null, 2));
if (error.dbErrorCode !== '42883') process.exitCode = 1;
}
} finally {
await pool.end();
await admin.query(`DROP DATABASE "${name}" WITH (FORCE)`);
await admin.end();
}
Observed result
{
"reason": "db-query-error",
"code": "42883",
"message": "operator does not exist: \"Permission\"[] = text[]",
"sql": "select exists (select 1 as \"_\" from (select cast(\"$values\".\"column1\" as integer) as \"id\", cast(\"$values\".\"column2\" as integer) as \"anchorId\", cast(\"$values\".\"column3\" as text[]) as \"permissions\" from (VALUES ($1, $2, cast(ARRAY[$3] as text[]))) as \"$values\") as \"Role\" where (select exists (select 1 as \"_\" from \"public\".\"Approval\" as \"$$t2\" where \"$$t1\".\"id\" = \"$$t2\".\"anchorId\" and \"$$t2\".\"permissions\" = \"Role\".\"permissions\") as \"_\" from \"public\".\"Anchor\" as \"$$t1\" where \"Role\".\"anchorId\" = \"$$t1\".\"id\")) as \"$condition\"",
"sqlParams": [
1,
1,
"VIEW"
]
}
Expected result
The matching ['VIEW'] create succeeds; a different array is rejected by policy. The candidate enum array should retain the database enum-array type, or both operands should be coerced consistently without losing array order/multiplicity semantics.
Current workaround
A custom policy-function expression converts the stored array and validated candidate values to JSONB and compares them exactly. Expanding the enum into membership/non-membership pairs also avoids the type error, but compares sets rather than preserving order and duplicate entries. Neither workaround changes the underlying enum-array equality limitation.
- Ngôn ngữ chính
- TypeScript
- Star
- 2.9k
- Fork
- 157
- Merge trung bình
- 11 giờ 42 phút
- Pull request đã merge (30 ngày)
- 20
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của zenstackhq/zenstack
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
zenstackhq/zenstack#2873 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
runtime
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
zenstackhq/zenstack#2868 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
zenstackhq/zenstack#2694 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 68/100
zenstackhq/zenstack#2659 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
zenstackhq/zenstack#2542 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của zenstackhq/zenstack
Issue tương tự
-
[bug] diagnostics.dumpBody:Buffer 形态请求(透传 lane)跳过 dumps/ 落盘,仅留 raw/-unknown-Có thể đã có người làm @ranxianglei đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
ranxianglei/billion-context#2421 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
platformatic/mcp#216 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
pending triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
nuxt/test-utils#1842 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
MoonshotAI/kimi-code#4146 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
farbenmeer/tapi#531 ·