PDO не генерирует исключение с несколькими запросами

Как я могу заставить PDO генерировать исключение при выполнении нескольких запросов?

Если я запускаю ошибочный sql сам по себе:

$execute($ddl_partial);
$execute($insert);

Затем я получаю ожидаемую ошибку:

Неустранимая ошибка PHP: Uncaught PDOException: SQLSTATE [42S22]: Столбец не найден: 207 [Microsoft] [Драйвер ODBC 13 для SQL Server] [SQL Server] Недопустимое имя столбца «другое». (SQLExecute[207] в /build/php7.0-41GaEn/php7.0-7.0.8/ext/pdo_odbc/odbc_stmt.c:260)

Однако, если я сначала запускаю хороший SQL, а затем плохой, он просто проглатывает ошибку, и все выглядит нормально. Но просмотр базы данных впоследствии подтверждает, что все запросы терпят неудачу после неверного утверждения.

$execute($ddl_full);
$execute($insert);

$execute($drop);
$execute($ddl_partial);
$execute($insert);

Я перебираю все возвращенные наборы строк с помощью while ($statement->nextRowset()), как продемонстрировано в этом ответе, но это печатается ровно 8 раз. :

array(4) {
  [0] =>
  string(5) "00000"
  [1] =>
  int(0)
  [2] =>
  string(24) " ((null)[0] at (null):0)"
  [3] =>
  string(0) ""
}

что складывается:

  • 1 - первая капля
  • 1 - полный ddl
  • 3 - первые вставки
  • 1 - второе падение
  • 1 - частичный ddl
  • 1 - первый оператор из вставки (второй ошибочен, поэтому третий никогда не выполняется)

Почему я не получаю сообщение об ошибке из неверного оператора?

<?php

$hostname = 'microsoftsql.example.com';
$database = 'mydb';
$username = 'user';
$password = 'P@55w0rd';
// https://www.microsoft.com/en-us/download/details.aspx?id=50419
$driver   = 'ODBC Driver 13 for SQL Server';

$pdo = new PDO("odbc:Driver=$driver;
    Server=$hostname;
    Database=$database",
    $username,
    $password
);
$pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$pdo->setAttribute( PDO::ATTR_EMULATE_PREPARES, false  );

$drop = "DROP TABLE testerino;";

$ddl_full = "
    CREATE TABLE testerino (
        value VARCHAR(10),
        other VARCHAR(10)
    );
";

$ddl_partial = "
    CREATE TABLE testerino (
        value VARCHAR(10)
    );
";

$insert = "
    INSERT INTO testerino (value)
    VALUES ('first');

    INSERT INTO testerino (value, other)
    VALUES ('second', 'another');

    INSERT INTO testerino (value)
    VALUES ('third');
";

$execute = function (String $sql) use ($pdo) {
    $pdo->beginTransaction();
    try {
        $statement = $pdo->prepare($sql);
        $statement->execute();
        do {
            /* https://bugs.php.net/bug.php?id=61613 */
            var_dump($statement->errorInfo());
        } while ($statement->nextRowset());
        $pdo->commit();
    } catch (PDOException $e) {
        $pdo->rollBack();
        throw $e;
    } finally {
        $statement->closeCursor();
    }
};

$execute($drop); // not needed first time
$execute($ddl_full);
$execute($insert);

$execute($drop);
$execute($ddl_partial);
$execute($insert);

Это большая проблема для меня, потому что исключение никогда не генерируется, поэтому транзакция не откатывается, и в итоге я вставляю только 1/3 записей. Мне нужно, чтобы было все или ничего.

Я использую Ubuntu Linux 16.04.1 с PHP 7.0.8-0ubuntu0.16.04.3 (cli) (NTS), подключаясь к Microsoft SQL Server 2012 (SP3) (KB3072779) — 11.0.6020.0 (X64), используя Драйвер Microsoft® ODBC 13 (предварительная версия) для SQL Server®


person Jeff Puckett    schedule 18.10.2016    source источник
comment
Как вы думаете, почему этот код вставит более 1 записи?   -  person david strachan    schedule 18.10.2016
comment
@davidstrachan мой запрос имеет 3 оператора вставки. При запуске с полным ddl он правильно вставляет все 3. При запуске с частичным выдает ошибку (как и должно) и откатывает транзакцию (записи не вставляются). Но при запуске обоих он вставляет 3, удаляет таблицу, вставляет 1 и завершает работу.   -  person Jeff Puckett    schedule 18.10.2016
comment
Затем опубликуйте фактический код.   -  person david strachan    schedule 18.10.2016
comment
@davidstrachan Извините, я не понимаю вашего комментария. Если вы посмотрите на опубликованное значение для $insert, то увидите, что оно содержит 3 оператора вставки.   -  person Jeff Puckett    schedule 18.10.2016
comment
@JeffPuckettII интересно, раньше не видел этой проблемы - но другие по-видимому есть. Я бы просто предложил добавить значения в массив, а затем перебрать их для выполнения, что приведет к отдельному выполнению для каждой вставки.   -  person JimL    schedule 18.10.2016
comment
@JimL да, спасибо, я действительно сталкивался с этими ответами во время этого тяжелого положения, но обратите внимание, что они разрешаются с помощью PDO::ERRMODE_EXCEPTION, который у меня есть, или путем повторения while ($statement->nextRowset()), что тоже не работает для меня. Кроме того, выполнение запросов в пакетном режиме необходимо для повышения производительности. Выполнение операторов по одному работает нормально, но для некоторых целей занимает слишком много времени.   -  person Jeff Puckett    schedule 18.10.2016
comment
Простой факт заключается в том, что драйверы Microsoft SQL для PHP никогда не были очень хороши просто потому, что они мало используются, поэтому ошибки в них остаются незамеченными, и никто не удосуживается исследовать их, когда они обнаружены и о них сообщают, опять же потому, что они не не получить много пользы. Я не знаю, поддерживает ли MSSQL ODBC, но если да, может быть, это лучший вариант?   -  person GordonM    schedule 22.02.2017