Skip to content

Commit b02b8a4

Browse files
committed
fix: fix DATE cursor comparison
1 parent f8e742a commit b02b8a4

4 files changed

Lines changed: 102 additions & 1 deletion

File tree

packages/client-engine-runtime/src/interpreter/render-query.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,17 @@ export function evaluateArg(arg: unknown, scope: ScopeBindings, generators: Gene
4848
if (found === undefined) {
4949
throw new Error(`Missing value for query variable ${arg.prisma__value.name}`)
5050
}
51-
arg = found
51+
if (arg.prisma__value.type === 'DateTime' && typeof found === 'string') {
52+
// Convert input datetime strings to Date objects. This is done to prevent issues that
53+
// arise when query input values end up being directly compared to values retrieved from
54+
// the database. One example of this is a query containing a DateTime cursor value being
55+
// used against a DATE MySQL column. The pagination logic doesn't have parameter type
56+
// information, therefore it ends up comparing the two datetimes as strings and would yield
57+
// false even if the two date datetime strings represent the same Date.
58+
arg = new Date(found)
59+
} else {
60+
arg = found
61+
}
5262
} else if (isPrismaValueGenerator(arg)) {
5363
const { name, args } = arg.prisma__value
5464
const generator = generators[name]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { defineMatrix } from '../../_utils/defineMatrix'
2+
import { sqlProviders } from '../../_utils/providers'
3+
4+
export default defineMatrix(() => [sqlProviders])
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import testMatrix from '../_matrix'
2+
3+
export default testMatrix.setupSchema(({ provider }) => {
4+
return /* Prisma */ `
5+
generator client {
6+
provider = "prisma-client-js"
7+
}
8+
9+
datasource db {
10+
provider = "${provider}"
11+
}
12+
13+
model Event {
14+
appId Int
15+
createdAt DateTime @db.Date
16+
value Int
17+
18+
@@id([appId, createdAt])
19+
}
20+
`
21+
})
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import testMatrix from './_matrix'
2+
// @ts-ignore
3+
import type { Event, PrismaClient } from './generated/prisma/client'
4+
5+
declare let prisma: PrismaClient
6+
7+
testMatrix.setupTestSuite(
8+
() => {
9+
test('retrieves a cursor against a DATE column', async () => {
10+
const rows: Event[] = []
11+
for (let day = 1; day <= 10; day++) {
12+
rows.push({
13+
appId: 1,
14+
createdAt: new Date(`2025-01-${String(day).padStart(2, '0')}`),
15+
value: day * 100,
16+
})
17+
}
18+
await prisma.event.createMany({ data: rows })
19+
20+
const firstThree = await prisma.event.findMany({
21+
where: { appId: 1 },
22+
orderBy: { createdAt: 'asc' },
23+
take: 3,
24+
})
25+
const cursorRow = firstThree[2] // 2025-01-03
26+
27+
// BUG: returns 0 rows
28+
const withCursor = await prisma.event.findMany({
29+
where: { appId: 1 },
30+
cursor: {
31+
appId_createdAt: {
32+
appId: cursorRow.appId,
33+
createdAt: cursorRow.createdAt,
34+
},
35+
},
36+
orderBy: { createdAt: 'asc' },
37+
take: 3,
38+
skip: 1,
39+
})
40+
41+
expect(withCursor).toEqual([
42+
{
43+
appId: 1,
44+
createdAt: new Date('2025-01-04T00:00:00.000Z'),
45+
value: 400,
46+
},
47+
{
48+
appId: 1,
49+
createdAt: new Date('2025-01-05T00:00:00.000Z'),
50+
value: 500,
51+
},
52+
{
53+
appId: 1,
54+
createdAt: new Date('2025-01-06T00:00:00.000Z'),
55+
value: 600,
56+
},
57+
])
58+
})
59+
},
60+
{
61+
optOut: {
62+
from: ['mongodb'],
63+
reason: 'MongoDB does not support DATE columns',
64+
},
65+
},
66+
)

0 commit comments

Comments
 (0)