.count and .findMany generated where clause not matching
Description and expected behavior I'm attempting to implement a generic paginated function which returns the total, next cursor, and data. I'm finding that, because of my RLS settings for this model, the data returned by .findMany does not match the count returned from .count, using exactly the same provided 'where'.
I would expect the where clause generated by Zenstack to be the same for both - especially considering manually inputing the generated .findMany clause into an un-enhanced prisma client .count works as intended.
Screenshots I've logged the different where clauses that are generated by Zenstack by combining my client provided where clause along with the auto-generated RLS where clause. To be claer, the findMany where clause is the one that is functioning as expected.
findMany:
{"status":{"in":["friend","pending"]},"OR":[{"requesterId":"pcHL3YTMhy6wNgEzIruoY"},{"recipientId":"pcHL3YTMhy6wNgEzIruoY"}],"AND":[{"OR":[{"AND":[{"AND":[{"NOT":{"deletedAt":{"not":{"equals":null}}}},{"NOT":{"OR":[{"requester":{"deletedAt":{"not":{"equals":null}}}},{"AND":[{"requester":{"banned":{"not":{"equals":false}}}},{"requester":{"banned":{"not":{"equals":null}}}}]}]}},{"NOT":{"OR":[{"recipient":{"deletedAt":{"not":{"equals":null}}}},{"AND":[{"recipient":{"banned":{"not":{"equals":false}}}},{"recipient":{"banned":{"not":{"equals":null}}}}]}]}}]},{"OR":[{"status":{"equals":"friend"}},{"OR":[{"requester":{"is":{"id":"pcHL3YTMhy6wNgEzIruoY"}}},{"recipient":{"is":{"id":"pcHL3YTMhy6wNgEzIruoY"}}}]}]}]}]},{"AND":[{"requester":{"OR":[{"AND":[{"AND":[{"NOT":{"deletedAt":{"not":{"equals":null}}}},{"NOT":{"sentBlocks":{"some":{"recipient":{"is":{"id":"pcHL3YTMhy6wNgEzIruoY"}}}}}},{"NOT":{"AND":[{"banned":{"not":{"equals":false}}},{"banned":{"not":{"equals":null}}}]}}]}]}]}},{"recipient":{"OR":[{"AND":[{"AND":[{"NOT":{"deletedAt":{"not":{"equals":null}}}},{"NOT":{"sentBlocks":{"some":{"recipient":{"is":{"id":"pcHL3YTMhy6wNgEzIruoY"}}}}}},{"NOT":{"AND":[{"banned":{"not":{"equals":false}}},{"banned":{"not":{"equals":null}}}]}}]}]}]}}]}]}
count:
{"AND":[{"status":{"in":["friend","pending"]},"OR":[{"requesterId":"pcHL3YTMhy6wNgEzIruoY"},{"recipientId":"pcHL3YTMhy6wNgEzIruoY"}]},{"OR":[{"AND":[{"OR":[{"AND":[{"AND":[{"NOT":{"deletedAt":{"not":{"equals":null}}}},{"NOT":{"OR":[{"requester":{"deletedAt":{"not":{"equals":null}}}},{"AND":[{"requester":{"banned":{"not":{"equals":false}}}},{"requester":{"banned":{"not":{"equals":null}}}}]}]}},{"NOT":{"OR":[{"recipient":{"deletedAt":{"not":{"equals":null}}}},{"AND":[{"recipient":{"banned":{"not":{"equals":false}}}},{"recipient":{"banned":{"not":{"equals":null}}}}]}]}}]},{"OR":[{"status":{"equals":"friend"}},{"OR":[{"requester":{"is":{"id":"pcHL3YTMhy6wNgEzIruoY"}}},{"recipient":{"is":{"id":"pcHL3YTMhy6wNgEzIruoY"}}}]}]}]}]}]}]}]}
Models:
abstract model BaseWithUsersRelationship extends Base {
requesterId String @map("requester_id")
recipientId String @map("recipient_id")
requester User @relation("RelationshipsSent", fields: [requesterId], references: [id], onDelete: Cascade)
recipient User @relation("RelationshipsReceived", fields: [recipientId], references: [id], onDelete: Cascade)
// deny all access if either user is deleted or banned
@@deny('all', requester.deletedAt != null || (requester.banned != false && requester.banned != null))
@@deny('all', recipient.deletedAt != null || (recipient.banned != false && recipient.banned != null))
// deny all access if either user is blocked
@@deny('all', requester.sentBlocks?[recipient == this.recipient] || recipient.sentBlocks?[recipient == this.requester])
@@unique([requesterId, recipientId], map: "relationship_unique_idx")
@@index([requesterId, recipientId], map: "relationship_requester_reverse_idx")
@@index([recipientId, requesterId], map: "relationship_recipient_reverse_idx")
}
model UserRelationship extends BaseWithUsersRelationship {
status UserRelationshipStatus
// only auth users can create
@@allow('create', auth() != null)
// allow all users to see it, if they are friends
@@allow('read', status == friend)
// only the requester/recipient can read/update
@@allow('read,update', (auth() == requester || auth() == recipient))
@@map("user_relationship")
}
model User extends Base {
name String
email String @deny('read', auth().id != this.id) @deny('read', auth() != this)
emailVerified Boolean @map("email_verified") @deny('read', auth() != this)
role String? @deny('read', true)
banned Boolean? @deny("all", true)
banReason String? @map("ban_reason") @deny('read', true)
banExpires DateTime? @map("ban_expires") @deny('read', true)
apiKeys Apikey[]
chatMemberships ChatMember[]
chatMessages ChatMessage[]
sentBlocks UserBlock[] @relation("BlocksSent")
receivedBlocks UserBlock[] @relation("BlocksReceived")
sentRelationships UserRelationship[] @relation("RelationshipsSent")
receivedRelationships UserRelationship[] @relation("RelationshipsReceived")
authSessions AuthSession[]
authAccounts AuthAccount[]
// users visible to everyone
@@allow('read', true)
// only authenticated user can update this
@@allow('all', auth() == this)
// deny read access to users blocked by this user
@@deny('read', sentBlocks?[auth() == recipient])
// deny all access if banned
@@deny('all', banned != false && banned != null)
@@unique([email])
@@map("user")
@@schema("auth")
}
Environment (please complete the following information):
- ZenStack version: 2.15.1
- Prisma version: 6.10.0
- Database type: Postgresql
Additional context By manually putting in the findMany where clause into a non-enhanced prisma client .count on this model, it does return the same total that I'm expecting.