Create policy compares stored enum array with text[] candidate input (PostgreSQL 42883)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 54/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- postgresql, typescript
調査の方向性
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.
索引モデルが issue の本文から書いたものです。
説明
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.
- 主要言語
- TypeScript
- スター
- 2.9k
- フォーク
- 157
- 平均マージ
- 11時間 42分
- マージ済み PR(30日)
- 20
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
zenstackhq/zenstack のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
zenstackhq/zenstack#2873 ·
メンテナーはふだん 1 日以内に返信
-
runtime
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
zenstackhq/zenstack#2868 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
zenstackhq/zenstack#2694 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 68/100
zenstackhq/zenstack#2659 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
zenstackhq/zenstack#2542 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
zenstackhq/zenstack の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
cockpit-project/cockpit-machines#2835 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
cloudflare/kumo#866 ·
メンテナーはふだん 1 日以内に返信
-
area:connector bug
難易度 1/5 1時間未満 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
tester-army/e2e#1015 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
メンテナーはふだん 1 日以内に返信