zenstack icon indicating copy to clipboard operation
zenstack copied to clipboard

[BUG] - having delete and create in the same query starts first with create and only then with delete - ignoring the order if the object

Open Tsimopak opened this issue 7 months ago • 1 comments

Prisma had a feature some time ago that the order of Delete and Create in the same query should preserve - the same way the developer actually wrote it in code But it seems not to be the case when using enhanced db.

An example:

DB:

model Post {
    id Int @id @default(autoincrement())
    tickets TicketInPost[]
    @@allow('all', true)
}

model View {
  id Int @id @default(autoincrement())
  ability String
  tickets TicketInPost[]
  @@allow('all', true)
}

model TicketInPost {
  id Int @id @default(autoincrement())
  postId Int
  post Post @relation(fields: [postId], references: [id])
  viewId Int
  view View @relation(fields: [viewId], references: [id])
  @@allow('all', true)
}

QUERY:

import { PrismaClient } from '@prisma/client';
import { enhance } from '@zenstackhq/runtime';

const prisma = new PrismaClient();
const db = enhance(
  prisma,
  {},
  {
    kinds: ['delegate', 'policy'],
    logPrismaQuery: true,
  }
);

async function main() {
  const view = await db.view.create({ data: { ability: 'bla' }})
  const post = await db.post.create({ data: {} })
  const updatedPost = await db.post.update({
    where: {
      id: post.id
    },
    data: {
      tickets: {
        deleteMany: {},
        create: {
          viewId: view.id
        }
      }
    },
    include: {
      tickets: true
    }
  })
  console.log('created!', updatedPost)
}

main()

If I use db variable to create the models, I get in return:

created! { id: 4, tickets: [] }

if I use prisma variable to create the models, I get in return:

created! { id: 5, tickets: [ { id: 3, postId: 5, viewId: 3 } ] }

Because the order I chose was first delete and only then create. BTW If I would have chosen to do it in reverse, even when using prisma, I wouldn't see anything, so for example:

  const updatedPost = await prisma.post.update({
    where: {
      id: post.id
    },
    data: {
      tickets: {
        create: {
          viewId: view.id
        },
        deleteMany: {},
      }
    },
    include: {
      tickets: true
    }
  })

would lead to:

created! { id: 6, tickets: [] }

even when using prisma

INFO: "devDependencies": { "prisma": "6.6.x", "typescript": "^5.8.3", "zenstack": "2.14.0" }, "dependencies": { "@prisma/client": "6.6.x", "@zenstackhq/runtime": "2.14.0" }

Tsimopak avatar May 01 '25 14:05 Tsimopak

Thanks for filing it. The bug is a nasty one: when enforcing access policies, we have to execute some nested operations separately, which effectively breaks the order. Please make multiple calls for now. I'll see what's the best way to address it.

ymc9 avatar May 20 '25 02:05 ymc9