PDO не выбрасывает исключение с несвязанными параметрами (и без переменных в запросе)

так что я понятия не имею, что здесь происходит

$link = new PDO('pgsql:dbname=' . $name . ';host=' . $host, $user, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $stmt = $link->prepare("SELECT s.*, d.invalid_column FROM students s ORDER BY s.student_id");
    $stmt->execute(array(1));
}
catch (PDOException $e) {
    print $e->getMessage();
}

когда я запускаю этот небольшой пример кода, я ожидаю, что будет создано исключение (как d.invalid_column не является реальным столбцом, и я передаю параметры, которые не могут быть связаны), но единственное, что происходит, - это то, что execute возвращает false и ничего больше. Кроме того,$stmt->errorInfo() пусто, а код 00000 что затрудняет добавление правильного исключения за пределы чего-то супер общего с ничем другим для журналов помогите мне в отслеживании ошибок, когда некоторые конечные пользователи сообщают об ошибке.

если я добавлю один'?"где-то к запросу брошено правильное выполнение (это d.invalid_column не является допустимым столбцом), даже если я добавляю больше параметров, которые ни к чему не привязываются.

Итак, способы заставить этот запрос правильно ошибиться:
1) Избавиться от всех параметров
2) Добавить '? на запрос

это просто ошибка в PDO или что?

изменить: Установки, которые будут бросать исключение (недопустимый столбец):

    $stmt = $link->prepare("SELECT s.*, d.invalid_column, ? FROM students s ORDER BY s.student_id");
    $stmt->execute(array(1));

    $stmt = $link->prepare("SELECT s.*, d.invalid_column, ? FROM students s ORDER BY s.student_id");
    $stmt->execute(array(1,2,3));

    $stmt = $link->prepare("SELECT s.*, d.invalid_column, ? FROM students s ORDER BY s.student_id");
    $stmt->execute();

    $stmt = $link->prepare("SELECT s.*, d.invalid_column FROM students s ORDER BY s.student_id");
    $stmt->execute();

это только тогда, когда у меня нет ? в моем запросе и передать что-то execute() что вещи просто терпят неудачу молча и без объяснения от PDO.

1 ответов


это поведение воспроизводимо с текущим PHP (5.6.13), и запрос даже не отправляется на сервер.

ваш случай описан в док as:

вы не можете связать больше значений, чем указано; если существует больше ключей в input_parameters, чем в SQL, указанном в PDO:: prepare (), затем оператор завершится ошибкой, и будет выдана ошибка.

ожидается значение 0, задано значение 1, и утверждение не выполняется,false возвращается. Пока работает, как документировано.

вы можете возразить, что "выдается ошибка " означало бы, что когда ERRMODE_EXCEPTION включено, будет создано исключение. Это аргумент, но не очевидно, что разработчики PDO согласятся с ним.

обновление:

почему SQLCode не установлен?

глядя на исходный код PDO, в частности static PHP_METHOD(PDOStatement, execute) что ручками PDO:: execute (), вы можете видеть, что все ошибки обрабатываются макросом: PDO_HANDLE_STMT_ERR()

#define PDO_HANDLE_STMT_ERR()   if (strcmp(stmt->error_code, PDO_ERR_NONE)) { pdo_handle_error(stmt->dbh, stmt TSRMLS_CC); }

дело в том, что при передаче связанного параметра, когда PDO не ожидал none, запрос никогда не попадает в SQL engine, поэтому SQL engine никогда не имеет возможности сообщить об ошибке, сопровождаемой SQLSTATE

PDO сам по себе не создает подделку SQLSTATE самостоятельно, по крайней мере, нет в этом случае, так чтоstmt->error_code остается PDO_ERR_NONE что это "00000".

это понятно, что вы предпочли бы исключение, но тогда вы должны предложить это https://bugs.php.net

то же самое с MySQL ?

да, поведение корня такое же, за исключением того, что с драйвером MySQL prepare отправляется немедленно в SQL engine, поэтому, если это неверно из-за плохого столбца, он терпит неудачу раньше и с реальной ошибкой SQL. С другой стороны, драйвер PgSQL имеет другая реализация, которая заставляет его отложить серверную сторону prepare. Это конкретное поведение подробно обсуждается в драйвер PHP Postgres PDO не поддерживает подготовленный оператор?

во всяком случае, вот случай с MySQL, который демонстрирует мое объяснение, то есть:

  • запрос ожидает 0 параметр, 1 задается
  • $stmt->execute возвращает false
  • никаких исключений не возникает
  • PDO:: код ошибки 00000

код:

$link = new PDO('mysql:dbname=' . $name . ';host=' . $host, $user, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $stmt = $link->prepare("SELECT 1");
    $rc=$stmt->execute(array(1));
   if ($rc===false)
    echo "query failed, errorCode=", $link->errorCode(), "\n";
   else
    echo "query succeeded, errorCode=", $link->errorCode(), "\n";
}
catch (PDOException $e) {
    print "A PDOException has occurred";
    print $e->getMessage();
}

результат:

ошибка запроса, errorCode=00000

что происходит под капотом, что prepare отправляется на сервер и успешно, но execute шаг отменяется PDO из-за несоответствия параметров.

вот случай, который отличается тем, что запрос ссылается на несуществующий столбец. Я добавляю печать, чтобы показать, что $stmt->execute даже не называется, Как исключения $stmt->prepare

код:

$link = new PDO('mysql:dbname=' . $name . ';host=' . $host, $user, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $stmt = $link->prepare("SELECT nonexisting");
    echo "Executing query\n";
    $rc=$stmt->execute(array(1));
   if ($rc===false)
    echo "query failed, errorCode=", $link->errorCode(), "\n";
   else
    echo "query succeeded, errorCode=", $link->errorCode(), "\n";
}
catch (PDOException $e) {
  print "A PDOException has occurred";
    print $e->getMessage();
}

результат:

A PDOException has occurredSQLSTATE[42S22]: столбец не найден: 1054 Неизвестный столбец "несуществующий" в "списке полей"

обратите внимание, как шаг "выполнение запроса" никогда не происходит, потому что это prepare это не удается, на стороне сервера.

вывод

  • при отправке запроса сервер, будь то в prepare () или execute (), и это сервер, который генерирует ошибку, тогда мы можем ожидать, что будет вызвано исключение PDOException.

  • когда запрос не отправляется на сервер для выполнения шага, то PDO execute () может завершиться ошибкой (возвращает false), но исключение не создается и errorCode() остается 00000