当字段名称和值包含单引号时 PDO 插入失败 (42000/1064)
PDO insert fails when field name and value contains single quote (42000/1064)
当 MySQL 准备语句的 PDO 具有:
- 参数(
v = ''
)
- 名称中包含单引号的字段 (
f'f
)
VALUES
部分参数后跟一个值
单引号 (''
)
然后失败并出现异常:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ':v,'')' at line 1
一些要重现的代码:
function query($sql) {
$pdo = new PDO('mysql:host=...', 'u', 'p', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]);
$pdo->prepare($sql)->execute(['v' => '']);
}
query("INSERT INTO `t` (`f'f`,`f`) VALUES ('',:v)"); // ok
query("INSERT INTO `t` (`f`,`f'f`) VALUES ('',:v)"); // ok
query("INSERT INTO `t` (`f'f`,`f`) VALUES (:v,'')"); // exception
query("INSERT INTO `t` (`f`,`f'f`) VALUES (:v,'')"); // exception
query("INSERT INTO `t` (`f'f`,`f`) VALUES (null,:v)"); // ok
query("INSERT INTO `t` (`f`,`f'f`) VALUES (null,:v)"); // ok
query("INSERT INTO `t` (`f'f`,`f`) VALUES (:v,null)"); // ok
query("INSERT INTO `t` (`f`,`f'f`) VALUES (:v,null)"); // ok
PHP版本5.5.9-1ubuntu4.24,mysqlnd 5.0.11
这是一个错误还是我遗漏了什么?
更新:
已尝试 PHP 版本 7.1.15-1,mysqlnd 5.0.12:无变化。
尝试使用位置参数:没有变化。
引用的列名对于 MySQL 不是问题。我可以使用 MySQL 客户端成功测试(根本没有 PHP):
mysql> set @v = '';
mysql> set @sql = "INSERT INTO `t` (`f'f`,`f`) VALUES (?,'')";
mysql> create table t (f int, `f'f` int);
mysql> prepare stmt from @sql;
mysql> execute stmt using @v;
Query OK, 1 row affected, 2 warnings (0.01 sec)
mysql> select * from t;
+------+------+
| f | f'f |
+------+------+
| 0 | 0 |
+------+------+
错误来自 MySQL 并且它看到命名参数占位符 :v
的事实是一个问题。 MySQL 本身不支持命名参数占位符,因此 PDO 应该用 ?
.
替换位置参数
因此,如果存在错误,则在 PDO 中。我怀疑 PDO 对列名中的 single-quote 感到困惑,因为它试图解析参数占位符的 SQL 字符串。
我建议您在使用 PDO 时不要使用包含 single-quote 的列名。
PHP 5.5 已经很老了,已经 out of support for almost two years(我在 2018 年 3 月写这篇文章)。这些天,应该至少使用 PHP 7.1.
我建议您使用 PHP 7.1 重复测试,看看 PDO 中的错误是否已修复。也可以尝试使用位置参数(使用 ?
占位符)进行测试,看看是否可以解决问题。
当 MySQL 准备语句的 PDO 具有:
- 参数(
v = ''
) - 名称中包含单引号的字段 (
f'f
) VALUES
部分参数后跟一个值 单引号 (''
)
然后失败并出现异常:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ':v,'')' at line 1
一些要重现的代码:
function query($sql) {
$pdo = new PDO('mysql:host=...', 'u', 'p', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]);
$pdo->prepare($sql)->execute(['v' => '']);
}
query("INSERT INTO `t` (`f'f`,`f`) VALUES ('',:v)"); // ok
query("INSERT INTO `t` (`f`,`f'f`) VALUES ('',:v)"); // ok
query("INSERT INTO `t` (`f'f`,`f`) VALUES (:v,'')"); // exception
query("INSERT INTO `t` (`f`,`f'f`) VALUES (:v,'')"); // exception
query("INSERT INTO `t` (`f'f`,`f`) VALUES (null,:v)"); // ok
query("INSERT INTO `t` (`f`,`f'f`) VALUES (null,:v)"); // ok
query("INSERT INTO `t` (`f'f`,`f`) VALUES (:v,null)"); // ok
query("INSERT INTO `t` (`f`,`f'f`) VALUES (:v,null)"); // ok
PHP版本5.5.9-1ubuntu4.24,mysqlnd 5.0.11
这是一个错误还是我遗漏了什么?
更新:
已尝试 PHP 版本 7.1.15-1,mysqlnd 5.0.12:无变化。
尝试使用位置参数:没有变化。
引用的列名对于 MySQL 不是问题。我可以使用 MySQL 客户端成功测试(根本没有 PHP):
mysql> set @v = '';
mysql> set @sql = "INSERT INTO `t` (`f'f`,`f`) VALUES (?,'')";
mysql> create table t (f int, `f'f` int);
mysql> prepare stmt from @sql;
mysql> execute stmt using @v;
Query OK, 1 row affected, 2 warnings (0.01 sec)
mysql> select * from t;
+------+------+
| f | f'f |
+------+------+
| 0 | 0 |
+------+------+
错误来自 MySQL 并且它看到命名参数占位符 :v
的事实是一个问题。 MySQL 本身不支持命名参数占位符,因此 PDO 应该用 ?
.
因此,如果存在错误,则在 PDO 中。我怀疑 PDO 对列名中的 single-quote 感到困惑,因为它试图解析参数占位符的 SQL 字符串。
我建议您在使用 PDO 时不要使用包含 single-quote 的列名。
PHP 5.5 已经很老了,已经 out of support for almost two years(我在 2018 年 3 月写这篇文章)。这些天,应该至少使用 PHP 7.1.
我建议您使用 PHP 7.1 重复测试,看看 PDO 中的错误是否已修复。也可以尝试使用位置参数(使用 ?
占位符)进行测试,看看是否可以解决问题。