使用PDO支持服务器端预处理语句?

6

假设有以下内容

DB()->prepare("SELECT * FROM mysql.general_log WHERE user_host LIKE ?");

$statement->execute( array('%console%') );

foreach($statement as $record){
    var_dump($record);
}

general_log 的内容是:

*************************** 1. row ***************************
event_time: 2011-04-20 14:27:59
user_host: REDACTED[REDACTED] @ REDACTED [192.168.56.101]
thread_id: 30
server_id: 0
command_type: Connect
argument: REDACTED@REDACTED on REDACTED
*************************** 2. row ***************************
event_time: 2011-04-20 14:27:59
user_host: REDACTED[REDACTED] @ REDACTED [192.168.56.101]
thread_id: 30
server_id: 0
command_type: Query
argument: SELECT * FROM mysql.general_log WHERE user_host LIKE '%console%'

我正在使用一个混乱的框架(没有单元测试,没有文档,没有规律可循),所以是否可能有人在某个地方明确禁用了MySQL准备语句,强制PDO使用模拟模式……或者这是预期的行为?

PHP版本为PHP Version 5.2.10-2ubuntu6, MySQL PDO驱动程序客户端库版本为5.1.41。

更新: PDO()是使用以下属性构造的。

PDO::ATTR_PERSISTENT => false
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true

我仔细阅读了PDO文档,但不幸的是,似乎没有提到类似这个相似问题中提到的标志。


也许我错过了什么,但你似乎忘了告诉我们实际出了什么问题。如果你担心模拟模式,请检查 PDO::ATTR_EMULATE_PREPARES -- 它很难找到。 - Charles
1
非常抱歉,问题在于PDO实际上没有调用MySQL并准备语句,它似乎自动使用仿真模式。我通过观察我的开发服务器的general_log和通过WireShark跟踪通信来确认了这一点。 - David
@Charles 如果你想要一些积分,可以将PDO::ATTR_EMULATE_PREPARES设置为false作为解决方案。这样就可以使用MySQL/服务器端准备而不是模拟层了。 - David
Wireshark的使用是一项高难度的研究,做得好!我已经将属性发布为答案,并添加了一些重要的注释,这可能会让您在特定条件下重新考虑关闭仿真。 - Charles
@Charles,strace和Wireshark是我的好朋友...它们在最奇怪的情况下非常有用,而且我想确保MySQL没有被指示准备语句。 - David
2个回答

12

一些PDO驱动程序不支持本地预处理语句,因此PDO执行预处理语句的仿真。它还允许您手动启用此仿真。

检查PDO::ATTR_EMULATE_PREPARES属性。当前的PDO手册中对其文档描述不够清晰。我所说的不够清晰,是指该属性仅出现在网站评论中,而不是手册本身。

通常情况下您希望尽可能使用本地预处理语句。在MySQL的情况下,如果您正在利用查询缓存,则实际上可能需要在PDO中禁用本地预处理语句!MySQL手册提供了更多信息,但简短的版本是,在5.1.17之前的版本中,不会将准备好的语句通过查询缓存运行,并且随后的版本仅在某些特定(但常见)条件下使用查询缓存。

(有人建议完全关闭查询缓存。使用大缓存大小可能会严重影响性能。)


很高兴你加了最后一行,当我戴上DBA的帽子时,我首先要做的事情就是禁用查询缓存。通常有更好的地方可以利用内存。 - David
顺便提一下,查询缓存已经被移除了:自MySQL 5.7.20起,查询缓存已被弃用,并在MySQL 8.0中被移除。https://dev.mysql.com/doc/refman/5.7/en/query-cache.html - mikep

5

默认情况下,PDO_MYSQL模拟预处理语句。 要使用本机服务器端准备的语句,应该明确设置。

$PDO->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接