fix(db-d1-sqlite): avoid bound parameter limit when querying relationships and inserting rows (#14099)
Fixes https://discord.com/channels/967097582721572934/1422639568808841329/1425037080051978261 This PR avoids bound parameters usage for `IN` and `NOT_IN` querying on `id` to respect the D1 limit https://developers.cloudflare.com/d1/platform/limits/ And also batches inserts when inserting arrays/blocks/hasMany relationships etc. This is needed because we can't avoid using bound parameters there, but still want to respect the 100 variables limit per query.
This commit is contained in:
99
test/database/sqlite-bound-parameters-limit.int.spec.ts
Normal file
99
test/database/sqlite-bound-parameters-limit.int.spec.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
/* eslint-disable jest/require-top-level-describe */
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const describeSqlite = process.env.PAYLOAD_DATABASE?.startsWith('sqlite') ? describe : describe.skip
|
||||
|
||||
let payload: Payload
|
||||
|
||||
describeSqlite('database - sqlite bound parameters limit', () => {
|
||||
beforeAll(async () => {
|
||||
;({ payload } = await initPayloadInt(dirname))
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await payload.destroy()
|
||||
})
|
||||
|
||||
it('should not use bound parameters for where querying on ID with IN if limitedBoundParameters: true', async () => {
|
||||
const defaultExecute = payload.db.drizzle.$client.execute.bind(payload.db.drizzle.$client)
|
||||
|
||||
// Limit bounds parameters length
|
||||
payload.db.drizzle.$client.execute = async function execute(...args) {
|
||||
const res = await defaultExecute(...args)
|
||||
const [{ args: boundParameters }] = args as [{ args: any[] }]
|
||||
|
||||
// eslint-disable-next-line jest/no-conditional-in-test
|
||||
if (boundParameters.length > 100) {
|
||||
throw new Error('Exceeded limit of bound parameters!')
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
payload.db.limitedBoundParameters = false
|
||||
|
||||
const IN = Array.from({ length: 300 }, (_, i) => i)
|
||||
|
||||
// Should fail here because too the length exceeds the limit
|
||||
await expect(
|
||||
payload.find({
|
||||
collection: 'simple',
|
||||
pagination: false,
|
||||
where: { id: { in: IN } },
|
||||
}),
|
||||
).rejects.toBeTruthy()
|
||||
|
||||
// Should fail here because too the length exceeds the limit
|
||||
await expect(
|
||||
payload.find({
|
||||
collection: 'simple',
|
||||
pagination: false,
|
||||
where: { id: { not_in: IN } },
|
||||
}),
|
||||
).rejects.toBeTruthy()
|
||||
|
||||
payload.db.limitedBoundParameters = true
|
||||
|
||||
// Should not fail because limitedBoundParameters: true
|
||||
await expect(
|
||||
payload.find({
|
||||
collection: 'simple',
|
||||
pagination: false,
|
||||
where: { id: { in: IN } },
|
||||
}),
|
||||
).resolves.toBeTruthy()
|
||||
|
||||
// Should not fail because limitedBoundParameters: true
|
||||
await expect(
|
||||
payload.find({
|
||||
collection: 'simple',
|
||||
pagination: false,
|
||||
where: { id: { not_in: IN } },
|
||||
}),
|
||||
).resolves.toBeTruthy()
|
||||
|
||||
// Verify that "in" still works properly
|
||||
|
||||
const docs = await Promise.all(
|
||||
Array.from({ length: 300 }, () => payload.create({ collection: 'simple', data: {} })),
|
||||
)
|
||||
|
||||
const res = await payload.find({
|
||||
collection: 'simple',
|
||||
pagination: false,
|
||||
where: { id: { in: docs.map((e) => e.id) } },
|
||||
})
|
||||
|
||||
expect(res.totalDocs).toBe(300)
|
||||
for (const docInRes of res.docs) {
|
||||
expect(docs.some((doc) => doc.id === docInRes.id)).toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user