Web

Получение необработанной строки запроса SQL из подготовленных операторов PDO

Есть ли способ выполнить необработанную строку SQL при вызове PDOStatement::execute() для подготовленного оператора? Это было бы чрезвычайно полезно для целей отладки.

 

Ответ 1

Я предполагаю, что вы имеете в виду, что вам нужен окончательный SQL-запрос с интерполированными в него значениями параметров. Я понимаю, что это было бы полезно для отладки, но это не совсем то, как работают подготовленные операторы. Параметры не объединяются с подготовленным оператором на стороне клиента, поэтому PDO никогда не должен иметь доступ к строке запроса вместе с параметрами. SQL-запрос отправляется на сервер базы данных при выполнении prepare(), а параметры отправляются отдельно при выполнении execute(). Общий журнал запросов MySQL показывает окончательный SQL с интерполированными значениями после выполнения функции execute(). Ниже приведена выдержка из моего общего журнала запросов. Я выполнял запросы из 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 перед выполнением execute(). Текстовый SQL-запрос не объединяется с параметрами во время выполнения. Поэтому PDO не сможет вам ничего показать. Внутренне, если вы используете PDO::ATTR_EMULATE_PREPARES, PDO делает копию SQL-запроса и интерполирует в него значения параметров перед выполнением prepare и execute. Но PDO не раскрывает этот измененный SQL-запрос. Объект PDOStatement имеет свойство $queryString, но оно устанавливается только в конструкторе PDOStatement и не обновляется, когда запрос переписывается с параметрами.

 Было бы разумным запросить у PDO возможность раскрывать переписанный запрос. Но даже это не даст вам «полный» запрос, если вы не используете PDO::ATTR_EMULATE_PREPARES. Вот почему я показал выше обходной путь использования общего журнала запросов сервера MySQL, потому что в этом случае даже подготовленный запрос с заполнителями параметров переписывается на сервере, а значения параметров подставляются в строку запроса. Но это происходит только во время ведения журнала, а не во время выполнения запроса.

 

Ответ 2

/**

 * Заменяет любые заполнители параметров в запросе на значение этого

 * параметра. Полезно для отладки. Предполагает, что анонимные параметры из 

 * $params расположены в том же порядке, что и в $query.

 *

 * @param string $query sql-запрос параметров.

 * @param array $params Массив параметров подстановки

 * @return string Интерполированный запрос

 */

public static function interpolateQuery($query, $params) {

    $keys = array();

    # построить регулярное выражение для каждого параметра

    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;

}

 

Ответ 3

Я модифицировал метод из предыдущего ответа, чтобы включить обработку вывода массивов для операторов типа WHERE IN (?).Также добавил проверку на значение NULL и продублировал $params, чтобы фактические значения $param не изменялись.

/**

 * Заменяет любые заполнители параметров в запросе на значение этого

 * параметра. Полезно для отладки. Предполагает, что анонимные параметры из 

 * $params расположены в том же порядке, что и в $query.

 *

 * @param string $query sql-запрос параметров.

 * @param array $params Массив параметров подстановки

 * @return string Интерполированный запрос

 */public function interpolateQuery($query, $params) {

    $keys    = array();

    $values = $params;

    # построить регулярное выражение для каждого параметра

    foreach ($params as $key => $value) {

        if (is_string($key)) {

            $keys[] = '/:'.$key.'/';

        } else {

            $keys[] = '/[?]/';

        }

        if (is_string($value))

            $values[$key] = "'" . $value . "'";

        if (is_array($value))

            $values[$key] = "'" . implode("','", $value) . "'";

        if (is_null($value))

            $values[$key] = 'NULL';

    }

    $query = preg_replace($keys, $values, $query);

    return $query;

}

 

Ответ 4

Решение состоит в том, чтобы корректно поместить ошибку в запрос и вывести сообщение об ошибке:

// Соединение с базой данных

$co = new PDO('mysql:dbname=myDB;host=localhost','root','');

// разрешаем выводить ошибки при каждом их появлении

$co->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// создаем наш подготовленный отчет

$stmt = $co->prepare("ELECT * FROM Person WHERE age=:age"); //I removed the 'S' of 'SELECT'

$stmt->bindValue(':age','18',PDO::PARAM_STR);

try {

    $stmt->execute();

} catch (PDOException $e) {

    echo $e->getMessage();

}

Стандартный вывод:

SQLSTATE[42000]: Syntax error or access violation: [...] near 'ELECT * FROM Person WHERE age=18' at line 1

Важно отметить, что здесь выводятся только первые 80 символов запроса.

 

Схожие статьи

Web

Как удалить index.php из пути codeigniter

Web-технологии: курс для начинающих веб-дизайнеров и разработчиков
Web

Web-технологии: курс для начинающих веб-дизайнеров и разработчиков

Web

Как выполнить цикл по массиву в php с форматированным html оформлением?

Web

Определение размера удаленного файла без загрузки файла