从 PDO 预准备语句中获取原始 SQL 查询字符串

2022-08-30 06:46:57

有没有办法在预准备语句上调用 PDOStatement::execute() 时执行原始 SQL 字符串?出于调试目的,这将非常有用。


答案 1

我假设你的意思是你想要最终的SQL查询,并将参数值插值到其中。我知道这对调试很有用,但这不是预准备语句的工作方式。参数不会与客户端上的预准备语句组合,因此 PDO 永远无权访问与其参数组合的查询字符串。

执行 prepare() 时,SQL 语句将发送到数据库服务器,执行() 时将单独发送参数。MySQL的常规查询日志确实显示了最终的SQL,并在执行()后插入了值。以下是我的一般查询日志的摘录。我从mysql CLI运行查询,而不是从PDO运行查询,但原理是相同的。

081016 16:51:28 2 Query       prepare s1 from 'select * from foo where i = ?'
                2 Prepare     [2] select * from foo where i = ?
081016 16:51:39 2 Query       set @a =1
081016 16:51:47 2 Query       execute s1 using @a
                2 Execute     [2] select * from foo where i = 1

如果设置 PDO 属性 PDO::ATTR_EMULATE_PREPARES,也可以获得所需的内容。在此模式下,PDO 将参数插入 SQL 查询,并在执行() 时发送整个查询。这不是一个真正的预准备查询。通过在 execute() 之前将变量插入 SQL 字符串,您将规避预准备查询的好处。


来自@afilina的评论:

否,文本 SQL 查询在执行期间不会与参数组合。因此,PDO没有任何东西可以向您展示。

在内部,如果使用 PDO::ATTR_EMULATE_PREPARES,PDO 将创建 SQL 查询的副本,并在执行准备和执行之前将参数值插入其中。但 PDO 不会公开此修改后的 SQL 查询。

PDOStatement 对象具有属性$queryString,但该属性仅在 PDOStatement 的构造函数中设置,并且在使用参数重写查询时不会更新。

对于 PDO 来说,要求他们公开重写的查询将是一个合理的功能请求。但即使这样也不会给你“完整”查询,除非你使用PDO::ATTR_EMULATE_PREPARES。

这就是为什么我展示了使用MySQL服务器的常规查询日志的上述解决方法的原因,因为在这种情况下,即使是带有参数占位符的准备好的查询也会在服务器上重写,并将参数值填充到查询字符串中。但这仅在日志记录期间完成,而不是在查询执行期间完成。


答案 2
/**
 * Replaces any parameter placeholders in a query with the value of that
 * parameter. Useful for debugging. Assumes anonymous parameters from 
 * $params are are in the same order as specified in $query
 *
 * @param string $query The sql query with parameter placeholders
 * @param array $params The array of substitution parameters
 * @return string The interpolated query
 */
public static function interpolateQuery($query, $params) {
    $keys = array();

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
        } else {
            $keys[] = '/[?]/';
        }
    }

    $query = preg_replace($keys, $params, $query, 1, $count);

    #trigger_error('replaced '.$count.' keys');

    return $query;
}

推荐