Prisma含IN的原生SQL参数化查询报错,求正确实现方式
Prisma原生SQL参数化查询IN条件的正确实现
Prisma原生SQL完全支持参数化查询,你遇到的42601语法错误(PostgreSQL语法错误),核心原因是IN子句的数组参数没有正确展开为多个占位符——Prisma不会自动把数组参数拆分成IN ($2, $3)这种格式,直接传数组会被当作单个参数,导致SQL语法错误。
正确实现步骤
下面以你的getWarmTransferStatusReport解析器为例,给出完整的参数化实现方案:
- 预处理过滤参数与占位符
先把status、userEmail这类IN条件的数组参数,转换成对应的占位符列表和扁平化的参数数组,避免SQL注入同时解决语法问题:
import { Prisma } from '@prisma/client'; export async function getWarmTransferStatusReport(parent, args, context) { const { page = 1, pageSize = 10, sortBy = 'createdAt', sortDir = 'ASC', status = [], userEmail = [] } = args; const params: any[] = []; const whereClauses: string[] = []; let paramIndex = 1; // 处理status IN条件 if (status.length > 0) { const placeholders = status.map(() => `$${paramIndex++}`).join(', '); whereClauses.push(`status IN (${placeholders})`); params.push(...status); } // 处理userEmail IN条件 if (userEmail.length > 0) { const placeholders = userEmail.map(() => `$${paramIndex++}`).join(', '); whereClauses.push(`user_email IN (${placeholders})`); params.push(...userEmail); } // 处理分页 const offset = (page - 1) * pageSize; params.push(pageSize, offset); const limitPlaceholder = `$${paramIndex++}`; const offsetPlaceholder = `$${paramIndex}`; // 处理排序(限制合法字段,防止SQL注入) const allowedSortFields = ['createdAt', 'status', 'user_email']; const safeSortBy = allowedSortFields.includes(sortBy) ? sortBy : 'createdAt'; const safeSortDir = ['ASC', 'DESC'].includes(sortDir.toUpperCase()) ? sortDir.toUpperCase() : 'ASC'; // 构建完整SQL const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(' AND ')}` : ''; const sql = ` SELECT id, status, user_email, created_at, updated_at FROM warm_transfers ${whereClause} ORDER BY ${safeSortBy} ${safeSortDir} LIMIT ${limitPlaceholder} OFFSET ${offsetPlaceholder} `; // 执行参数化查询 const results = await context.prisma.$queryRaw(Prisma.sql`${sql}`, ...params); return results; }
- 使用Prisma.join简化占位符生成
如果你想用Prisma.join工具,可以把占位符生成逻辑替换为:
// 处理status IN条件 if (status.length > 0) { const placeholders = Prisma.join(status.map((_, idx) => Prisma.raw(`$${paramIndex + idx}`))); whereClauses.push(`status IN (${placeholders})`); params.push(...status); paramIndex += status.length; }
本质和手动生成占位符一致,只是用Prisma的工具类来拼接。
期望生成的最终SQL示例
当传入status: ['PENDING', 'COMPLETED']、userEmail: ['user1@example.com']、page=1、pageSize=10时,最终执行的参数化SQL会是:
SELECT id, status, user_email, created_at, updated_at FROM warm_transfers WHERE status IN ($1, $2) AND user_email IN ($3) ORDER BY createdAt ASC LIMIT $4 OFFSET $5
对应的参数数组为:['PENDING', 'COMPLETED', 'user1@example.com', 10, 0]
关键注意事项
- 禁止直接拼接用户输入:所有用户传入的参数(包括排序字段、过滤值)都必须通过参数化传递,或者做合法性校验(如排序字段限制在允许列表内),防止SQL注入。
- 参数顺序必须严格对应:占位符的编号
$1、$2要和params数组的顺序完全一致,否则会出现参数不匹配的错误。
内容的提问来源于stack exchange,提问作者Igor Shmukler
相关产品推荐
相关产品推荐

