MySQL 4.1才引入PREPARE/EXECUTE机制,5.0完善支持;4.0及更早版本不支持参数化查询,需升级至5.1+或手动过滤输入。

旧版 MySQL 5.0 之前不支持 PREPARE 语句
MySQL 在 4.1 版本才引入 PREPARE/EXECUTE 机制,而 5.0 才完善客户端协议对参数化查询的支持。如果你用的是 MySQL 4.0 或更早版本(比如某些嵌入式设备或遗留系统),mysql_real_query() 无法解析问号占位符,直接报错 Unknown prepared statement handler 或语法错误。
实操建议:
- 确认版本:
SELECT VERSION();,若返回4.1.22或更低,基本可判定不支持标准参数化 - 不要强行在应用层拼接
mysql_stmt_init()—— 驱动会静默降级为字符串拼接,失去防护意义 - 升级到 MySQL 5.1+ 是唯一可靠解法;若无法升级,必须手动实现输入过滤(如
mysql_real_escape_string())并严格限制字段类型和长度
SQL Server 2000 的 sp_executesql 不兼容命名参数
SQL Server 2000 支持参数化,但 sp_executesql 要求所有参数必须按声明顺序传入,不接受命名绑定(比如 Python pyodbc 默认的 @name 绑定会失败)。常见错误是 Invalid parameter 1 specified 或执行结果为空。
实操建议:
- 改用位置参数:把
"SELECT * FROM users WHERE id = @id"改成"SELECT * FROM users WHERE id = ?",并确保execute()传参顺序与问号顺序一致 - 避免使用
adodbapi等老驱动,它们默认走命名绑定;推荐用pymssql2.1+(已强制位置模式)或升级到 SQL Server 2005+ - 注意
datetime类型在 2000 中精度仅到 3ms,若参数含毫秒级时间,可能被截断导致查不到数据
PostgreSQL 7.4 以下无二进制协议参数化支持
PostgreSQL 从 7.4 开始通过 PQprepare() + PQexecPrepared() 提供完整参数化能力;7.3 及更早只支持文本协议下的简单变量替换(类似 libpq 的 PQexecParams() 实际是客户端拼接),无法防御二进制数据中的特殊字节(如 \x00、\xFF)引发的截断或注入。
实操建议:
- 检查协议版本:
SHOW server_version;,若为7.3.22,需升级至 7.4+ 或切换到外部预处理方案(如用pgbouncer做连接池层重写) - 不要依赖
psycopg1的cursor.execute("...", dict)—— 它在旧服务端下仍会转成字符串拼接 - 若必须维持旧环境,对二进制字段(如
bytea)一律 base64 编码后作为文本传入,并在 SQL 中用decode(?, 'base64')
Oracle 9i 的 OCIStmtPrepare() 对绑定变量数量有限制
Oracle 9.2(即 9i R2)最大只支持 65535 个绑定变量,且当 SQL 中有大量 IN (?, ?, ..., ?) 时,驱动(如 cx_Oracle 5.x)可能在准备阶段就报 ORA-01000: maximum open cursors exceeded,并非真的游标泄漏,而是绑定结构体超限。
实操建议:
- 拆分大 IN 查询:每 1000 个值一批,用
UNION ALL合并结果,避免单语句绑定超限 - 禁用自动绑定缓存:
connection.autocommit = True并显式调用cursor.close(),防止 OCI 句柄堆积 - 升级到 Oracle 10g+ 可解除该限制,且
cx_Oracle 7+对大参数列表做了内部 chunk 处理
旧版数据库的参数化不是“开了就安全”,而是“开了但没真正生效”。最危险的情况是开发误以为用了 execute("SELECT * FROM t WHERE x = ?", [val]) 就万事大吉,其实底层驱动悄悄 fallback 到了字符串拼接——这种无声失效,比明着报错更难排查。


















