Skip to content

Policy plugin SQL generation bug for complex computed fields in SQLite #2666

@lombo75

Description

@lombo75

Description and expected behavior
I think there is a bug or limitation in the ZenStack policy plugin’s SQL rewriting for computed fields—especially with complex subqueries or correlated subqueries in SQLite.

The computed field SQL for [total], [subTotal] etc., is valid on its own.
When you use the policy-wrapped client ([db] with [PolicyPlugin]), ZenStack rewrites the query to enforce row-level security.
This rewriting sometimes produces malformed SQL (e.g., stray or mismatched braces, or invalid subquery placement) that SQLite cannot parse, resulting in errors like:
SqliteError: unrecognized token: "}"
Evidence:

The same computed field works with the raw client ([rawDb])) but fails with the policy-wrapped client.
The error is not a TypeScript or schema error, but a runtime SQL parse error after ZenStack’s policy transformation.

total: (qb, { modelAlias }) =>
sqlCOALESCE(( SELECT SUM((("WorkItem"."budgetedPrice" * "WorkItem"."quantity") * (1 + COALESCE("WorkItem"."profit", 0) / 100.0)) - COALESCE("WorkItem"."adjustment", 0)) FROM "WorkItem" INNER JOIN "WorkOrder" ON "WorkItem"."workOrderId" = "WorkOrder"."id" WHERE "WorkOrder"."projectId" = ${sql.ref(${modelAlias}.projectId)} ), 0),

subTotal: (qb, { modelAlias }) =>
sql( COALESCE(( SELECT SUM((("WorkItem"."budgetedPrice" * "WorkItem"."quantity") * (1 + COALESCE("WorkItem"."profit", 0) / 100.0)) - COALESCE("WorkItem"."adjustment", 0)) FROM "WorkItem" INNER JOIN "WorkOrder" ON "WorkItem"."workOrderId" = "WorkOrder"."id" WHERE "WorkOrder"."projectId" = ${sql.ref(${modelAlias}.projectId)} ), 0) * (1 + COALESCE(${sql.ref(${modelAlias}.tax)}, 0) / 100.0) ) - COALESCE(${sql.ref(${modelAlias}.discount)}, 0),

Environment (please complete the following information):

  • ZenStack version: [e.g., 3..6.4]
  • Database type: [e.g. Sqlite]
  • Node.js/Bun version: [e.g., v25.8.2]
  • Package manager: [pnpm]

Additional context
Evidence:

The same computed field works with the raw client (rawDb but fails with the policy-wrapped client.
The error is not a TypeScript or schema error, but a runtime SQL parse error after ZenStack’s policy transformation.

What to do:

Report this to the ZenStack maintainers with a minimal reproduction (computed field, policy, and failing query).
As a workaround, avoid selecting these computed fields through the policy-wrapped client, or compute them in route code as you’ve done.

Summary:
This is not a bug in your SQL or schema, but a ZenStack policy plugin SQL generation bug for complex computed fields in SQLite.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions