queryRaw template not working with Jest
See original GitHub issueBug description
I’m running the following SQL query with queryRaw
:
const results = await prisma.$queryRaw`
select
posts.*,
${
requestingUserId
? Prisma.sql`max(case when l.profile_id = ${requestingUserId} then 1 else 0 end) as is_liked_by_user,`
: Prisma.empty
}
submitter.username
from posts
inner join user_profiles as submitter
on posts.profile_id = submitter.id
left join post_likes as l
on posts.id = l.post_id
where posts.show_id = ${showId}
group by posts.id, posts.parent_post_id, submitter.username;
`;
This works correctly with requestingUserId == undefined
when running in a normal node.js environment, but when I run the same query in the same code via Jest I get an error:
Invalid `prisma.queryRaw()` invocation:
Raw query failed. Code: `42601`. Message: `db error: ERROR: syntax error at or near "("`
I added some debug output to see the SQL and I notice it is trying to create a prepared statement in the select clause with what looks like the serialized JSON of the Prisma.empty
object itself when I run in Jest (but it is correctly just inserting empty when running in the node server).
Node.js SQL
sql:
select
posts.*,
sum(case when l.post_id is null then 0 else 1 end) as total_likes,
submitter.username
from posts
inner join user_profiles as submitter
on posts.profile_id = submitter.id
left join post_likes as l
on posts.id = l.post_id
where posts.show_id = $1
group by posts.id, posts.parent_post_id, submitter.username;
params: [550]
Jest SQL
sql:
select
posts.*,
$1
sum(case when l.post_id is null then 0 else 1 end) as total_likes,
submitter.username
from posts
inner join user_profiles as submitter
on posts.profile_id = submitter.id
left join post_likes as l
on posts.id = l.post_id
where posts.show_id = $2
group by posts.id, posts.parent_post_id, submitter.username;
params: [{"values":[],"strings":[""],"text":"","sql":""},550]
There’s not much difference between my jest setup and my node.js server. I’m using supertest
when running Jest to hit the API, so I was thinking maybe it’s serializing+deserializing the Prisma.empty
object to/from JSON and then failing some kind of instanceof
check? But, I don’t pass the query object between Jest/Express or anything like that - the code that creates the query and executes it is colocated in just the server (as you can see from my snippet above)…
How to reproduce
- Write a
queryRaw
statement that conditionally insertsPrisma.empty
into the query - Execute this statement in a Jest test
- See odd prepared statement attempting to pass a serialized version of the
Prisma.empty
object as a parameter to the query
Expected behavior
Similar to how it works when running directly via node, the Prisma.empty
portion should be omitted from the query
Prisma information
const results = await prisma.$queryRaw`
select
posts.*,
${
requestingUserId
? Prisma.sql`max(case when l.profile_id = ${requestingUserId} then 1 else 0 end) as is_liked_by_user,`
: Prisma.empty
}
submitter.username
from posts
inner join user_profiles as submitter
on posts.profile_id = submitter.id
left join post_likes as l
on posts.id = l.post_id
where posts.show_id = ${showId}
group by posts.id, posts.parent_post_id, submitter.username;
`;
Environment & setup
- OS: macOS 12.1
- Database: PostgreSQL
- Node.js version: 16.13.0
Prisma Version
prisma : 3.8.1
@prisma/client : 3.8.1
Current platform : darwin-arm64
Query Engine (Node-API) : libquery-engine 34df67547cf5598f5a6cd3eb45f14ee70c3fb86f (at node_modules/@prisma/engines/libquery_engine-darwin-arm64.dylib.node)
Migration Engine : migration-engine-cli 34df67547cf5598f5a6cd3eb45f14ee70c3fb86f (at node_modules/@prisma/engines/migration-engine-darwin-arm64)
Introspection Engine : introspection-core 34df67547cf5598f5a6cd3eb45f14ee70c3fb86f (at node_modules/@prisma/engines/introspection-engine-darwin-arm64)
Format Binary : prisma-fmt 34df67547cf5598f5a6cd3eb45f14ee70c3fb86f (at node_modules/@prisma/engines/prisma-fmt-darwin-arm64)
Default Engines Hash : 34df67547cf5598f5a6cd3eb45f14ee70c3fb86f
Studio : 0.452.0
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (3 by maintainers)
Top GitHub Comments
I was able to reproduce it using the repo, provided by @idolize. I think you also correctly pointed out that the issues with this is a combination of
instanceof
check and jest sandbox. Now, step by step reproduction of what happens.In your jest setup, you have a custom jest environment, which exposes
prisma
as a global. Jest loads environment file outside of its module sandbox, so anyimport '@prisma/client'
resolves to a raw unmodified prisma module. Let’s call this instance of module “environment prisma”Test files and all their imports do pass through sandbox. In your case, that’s
server.ts
, which importsPrisma.empty
from@prisma/client
. In that case, this is a different instance of the module - let’s call it “sandbox prisma” andPrisma.empty
, imported from it “sanbox empty”.In order to start your server, you need to pass it prisma instance. In your test this is environment prisma. To distingush between raw parameter and
Prisma.Empty
value, at some point a do a check,parameter instanceof EnvironmentPrisma.empty
. However, it receives and instance of “sanbox empty” instead and the check fails.Hope my explanation makes sense. Thank you for the great reproduction, @idolize.
While we are working on a fix, the workaround is to move
new PrismaClient()
creation into test file itself.Ultimately, this is not jest-specific issue, even though it is probably most common setup where it could be observed. Any kind of cross-module
Prisma.empty
usage will fail and we should probably move away frominstanceof
checks in this case. That specificinstanceof
lives insql-template-tag
module.@pantharshit00 Here is a repo I made with some simple instructions to reproduce the issue https://github.com/idolize/prisma-issue-11454