]> granicus.if.org Git - php/commitdiff
Change the default PDO error mode to exceptions
authorAllenJB <github@allenjb.me.uk>
Mon, 4 May 2020 18:24:06 +0000 (20:24 +0200)
committerChristoph M. Becker <cmbecker69@gmx.de>
Mon, 4 May 2020 20:51:47 +0000 (22:51 +0200)
According to <https://www.php.net/manual/en/pdo.error-handling.php>.

15 files changed:
NEWS
UPGRADING
ext/pdo/pdo_dbh.c
ext/pdo/tests/bug_44159.phpt
ext/pdo/tests/pdo_test.inc
ext/pdo_mysql/tests/README.md
ext/pdo_mysql/tests/pdo_mysql___construct.phpt
ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt
ext/pdo_mysql/tests/pdo_mysql_attr_multi_statements.phpt
ext/pdo_mysql/tests/pdo_mysql_exec.phpt
ext/pdo_mysql/tests/pdo_mysql_multi_stmt_nextrowset.phpt
ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt
ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt
ext/pdo_sqlite/tests/pdo_sqlite_extendederror_attr.phpt
ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt

diff --git a/NEWS b/NEWS
index b8cb7be1e9e26aad86e0961cf89930783bc555f0..0a5b9bfee2e04b6d7c5fcff6ec47a93997a092d6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -93,6 +93,7 @@ PHP                                                                        NEWS
   . Don't ignore invalid escape sequences. (sjon)
 
 - PDO:
+  . Changed default PDO error mode to exceptions. (AllenJB)
   . Fixed bug #77849 (Disable cloning of PDO handle/connection objects).
     (camporter)
 
index dc97eb6cc4e1bfaa104b2632588f16ae1609884f..668b3dd59b6dc2fc46290d084ca9927de7f81368 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -291,6 +291,10 @@ PHP 8.0 UPGRADE NOTES
     now ignored.
 
 - PDO:
+  . The default error handling mode has been changed from "silent" to
+   "exceptions". See https://www.php.net/manual/en/pdo.error-handling.php
+    for details of behavior changes and how to explicitly set this attribute.
+    RFC: https://wiki.php.net/rfc/pdo_default_errmode
   . The method PDOStatement::setFetchMode() now accepts the following signature:
 
         PDOStatement::setFetchMode($mode, $classname, $params)
index 4ff5da3e13b045435f42d6a5c0d3d1146964e001..769fe25f9d83296508805390e0492f0cc0630adc 100644 (file)
@@ -340,6 +340,7 @@ PHP_METHOD(PDO, __construct)
        }
 
        dbh->auto_commit = pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1);
+       dbh->error_mode = pdo_attr_lval(options, PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);
 
        if (!dbh->data_source || (username && !dbh->username) || (password && !dbh->password)) {
                php_error_docref(NULL, E_ERROR, "Out of memory");
index bd7aca0e93f6f7e706db522e82d315a39db15fda..5f23f910eb6ecc2601a0a78f9cd104709fdd61d1 100644 (file)
@@ -12,6 +12,7 @@ try {
 --FILE--
 <?php
 $pdo = new PDO("sqlite:".__DIR__."/foo.db");
+$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
 
 $attrs = array(PDO::ATTR_STATEMENT_CLASS, PDO::ATTR_STRINGIFY_FETCHES, PDO::NULL_TO_STRING);
 
@@ -26,15 +27,23 @@ foreach ($attrs as $attr) {
 ?>
 --EXPECTF--
 Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error: PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); the classname must be a string specifying an existing class in %s on line %d
+
+Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error: PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); the classname must be a string specifying an existing class in %s on line %d
+
+Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error: PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); the classname must be a string specifying an existing class in %s on line %d
+
+Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error: attribute value must be an integer in %s on line %d
+
+Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 bool(true)
 bool(true)
index 2238123d40a0153cc2b6aa146cca00df401b78fd..aa1d1eadcb1cf48f5856671c484415c1f7b6918e 100644 (file)
@@ -37,6 +37,8 @@ class PDOTest {
         if (!$db) {
             die("Could not create PDO object (DSN=$dsn, user=$user)\n");
         }
+        // Ignore errors about non-existant tables
+        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
 
         // clean up any crufty test tables we might have left behind
         // on a previous run
index afdac4189a168ead0e59ae8a73783151dcf60af3..dd3b93b592a18f348c21e2503dab347b8048572b 100644 (file)
@@ -31,3 +31,11 @@ PDO_MYSQL_TEST_CHARSET
 NOTE: if any of `PDO_MYSQL_TEST_[HOST|DB|SOCKET|ENGINE|CHARSET]` is part of
 `PDO_MYSQL_TEST_DSN`, the values must match. That is, for example, for
 `PDO_MYSQL_TEST_DSN = mysql:dbname=test` you MUST set `PDO_MYSQL_TEST_DB=test`.
+
+## MySQL User Permissions
+
+The MySQL user used to run the tests must have full permissions on the test
+database, plus the following additional permissions:
+
+* SUPER: Required to [create functions if binary logging is enabled](https://dev.mysql.com/doc/refman/8.0/en/stored-programs-logging.html#sa38412929)
+* SELECT permissions on performance_schema.session_connect_attrs
index 7412402125d1f47da72df780f633ff62bb66f73f..bff3fcbe5a602dd7d247a48ac3c3336aa6a96f9a 100644 (file)
@@ -150,7 +150,8 @@ MySQLPDOTest::skip();
             $dsn = MySQLPDOTest::getDSN(array('dbname' => $db), 'dbname=' . $invalid_db);
             try { $db = @new PDO($dsn, $user, $pass); assert(false); printf("%s\n", $dsn); } catch (PDOException $e) {
                 $tmp = $e->getMessage();
-                if (!stristr($tmp, '42000') && !stristr($tmp, '1049'))
+                // 1044 may occur here if running tests using a custom user that does not have access to all databases
+                if (!stristr($tmp, '42000') && !stristr($tmp, '1049') && !stristr($tmp, '1044'))
                     printf("[022] Cannot find proper error codes: %s\n", $tmp);
             }
 
index ac5f7f6911f39aac3ec999702473c36ce36c48e1..efbf3c51c812cb0790f0fc587d9911149135e73f 100644 (file)
@@ -18,6 +18,7 @@ MySQLPDOTest::skip();
 
         try {
             $db = new PDO($dsn, $user, $pass, array($option => $value));
+            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
             if (!is_object($db) || ($value !== ($tmp = @$db->getAttribute($option))))
                 printf("[%03d] Expecting '%s'/%s got '%s'/%s' for options '%s'\n",
                     $offset,
@@ -81,6 +82,7 @@ MySQLPDOTest::skip();
             printf("[003] [TODO][CHANGEREQUEST] Please, lets not ignore invalid options and bail out!\n");
 
         $db = new PDO($dsn, $user, $pass);
+        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
         foreach ($valid_options as $option => $name) {
             /* TODO getAttribute() is pretty poor in supporting the options, suppress errors */
             $tmp = @$db->getAttribute($option);
@@ -155,10 +157,11 @@ MySQLPDOTest::skip();
         set_option_and_check(34, PDO::MYSQL_ATTR_DIRECT_QUERY, 0, 'PDO::MYSQL_ATTR_DIRECT_QUERY');
 
     } catch (PDOException $e) {
-        printf("[001] %s, [%s] %s\n",
+        printf("[001] %s, [%s] %s Line: %s\n",
             $e->getMessage(),
             (is_object($db)) ? $db->errorCode() : 'n/a',
-            (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a');
+            (is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a',
+            $e->getLine());
     }
 
     print "done!";
index 65b5e1bdc9273d715683e9e37aff79462c0f6ab9..62de8ad1ca37b63f76ada7ccb271e31df3b2056f 100644 (file)
@@ -19,6 +19,7 @@ error_reporting=E_ALL
 
     $table = sprintf("test_%s", md5(mt_rand(0, PHP_INT_MAX)));
     $db = new PDO($dsn, $user, $pass);
+    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
     $db->exec(sprintf('DROP TABLE IF EXISTS %s', $table));
     $create = sprintf('CREATE TABLE %s(id INT)', $table);
     $db->exec($create);
@@ -35,6 +36,7 @@ error_reporting=E_ALL
 
     // New connection, does not allow multiple statements.
     $db = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => false));
+    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
     $stmt = $db->query(sprintf('SELECT * FROM %s; INSERT INTO %s(id) VALUES (3)', $table, $table));
     var_dump($stmt);
     $info = $db->errorInfo();
@@ -49,7 +51,7 @@ error_reporting=E_ALL
     $db->exec(sprintf('DROP TABLE IF EXISTS %s', $table));
     print "done!";
 ?>
---EXPECT--
+--EXPECTF--
 string(5) "00000"
 array(2) {
   [0]=>
@@ -70,6 +72,8 @@ array(1) {
     string(1) "1"
   }
 }
+
+Warning: PDO::query(): SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO %s(id) VALUES (3)' at line 1 in %s on line %d
 bool(false)
 string(5) "42000"
 array(2) {
index c6f3d54ddd6dea8b0d687bf41121c4e13abc768b..a670be737f69100971d54e5aab33170c18c9206d 100644 (file)
@@ -176,6 +176,7 @@ MySQLPDOTest::skip();
 <?php
 require __DIR__ . '/mysql_pdo_test.inc';
 $db = MySQLPDOTest::factory();
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
 @$db->exec('DROP TABLE IF EXISTS test');
 ?>
 --EXPECTF--
index 74ab7858d4bfe0426c3c0128df8d94572134aafb..368f588cfde0b5135e518739e36a9101495f0966 100644 (file)
@@ -50,12 +50,14 @@ if (!MySQLPDOTest::isPDOMySQLnd())
             $user = PDO_MYSQL_TEST_USER;
             $pass = PDO_MYSQL_TEST_PASS;
             $db = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => $multi));
+            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
             $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
             $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
             $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
             test_proc($db);
 
             $db = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => $multi));
+            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
             $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
             $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 0);
             $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
@@ -86,7 +88,7 @@ if (!MySQLPDOTest::isPDOMySQLnd())
 require __DIR__ . '/mysql_pdo_test.inc';
 MySQLPDOTest::dropTestTable();
 ?>
---EXPECT--
+--EXPECTF--
 Native PS...
 
 Testing with PDO::MYSQL_ATTR_MULTI_STATEMENTS set to false
@@ -172,6 +174,8 @@ array(3) {
   }
 }
 bool(false)
+
+Warning: PDO::query(): SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO test (id, label) VALUES (99, 'x')' at line 1 in %s on line %d
 string(5) "42000"
 
 Testing with PDO::MYSQL_ATTR_MULTI_STATEMENTS set to true
index 4d95c73cdc7b366849113a06d74a161eacc206da..51e5b7b44af9ce067e973269b40a5504f92b6c60 100644 (file)
@@ -18,6 +18,8 @@ MySQLPDOTest::skip();
 
         $db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
         $db2 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
+        $db1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+        $db2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
         $db1->exec('SET @pdo_persistent_connection=1');
         $stmt = $db2->query('SELECT @pdo_persistent_connection as _pers');
         $tmp = $stmt->fetch(PDO::FETCH_ASSOC);
@@ -37,6 +39,7 @@ MySQLPDOTest::skip();
 
         $db1 = NULL; /* should be equal to closing to my understanding */
         $db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
+        $db1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
         $stmt = $db1->query('SELECT CONNECTION_ID() as _con1');
         $tmp = $stmt->fetch(PDO::FETCH_ASSOC);
         $con1 = $tmp['_con1'];
@@ -60,11 +63,13 @@ MySQLPDOTest::skip();
         }
 
         $db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => false));
+        $db1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
         $stmt = $db1->query('SELECT CONNECTION_ID() as _con1');
         $tmp = $stmt->fetch(PDO::FETCH_ASSOC);
         $con1 = $tmp['_con1'];
 
         @$db2 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
+        $db2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
         $stmt = $db2->query('SELECT CONNECTION_ID() as _con2');
         $tmp = $stmt->fetch(PDO::FETCH_ASSOC);
         $con2 = $tmp['_con2'];
index 525e29b953e15c25eebf50de16e093391c7eefb2..4463dbb3ecdf46c2ab3df70be941243acc4b4201 100644 (file)
@@ -8,6 +8,8 @@ if (!extension_loaded('pdo_sqlite')) print 'skip not loaded';
 <?php
 
 $db = new PDO('sqlite::memory:');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
+
 $db->exec('CREATE TABLE testing (id INTEGER , name VARCHAR)');
 $db->exec('INSERT INTO testing VALUES(1, "php")');
 $db->exec('INSERT INTO testing VALUES(2, "")');
@@ -91,18 +93,28 @@ array(2) {
 }
 
 Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: function 'nothing' not found or invalid function name in %s on line %d
+
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: function '' not found or invalid function name in %s on line %d
+
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: no array or string given in %s on line %d
+
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: no array or string given in %s on line %d
+
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: class 'PDOStatement' does not have a method 'foo' in %s on line %d
+
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 array(2) {
   [0]=>
@@ -118,10 +130,16 @@ array(2) {
 }
 
 Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: non-static method bar::test2() cannot be called statically in %s on line %d
+
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: non-static method bar::test3() cannot be called statically in %s on line %d
+
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
 
 Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: class 'bar' does not have a method 'inexistent' in %s on line %d
+
+Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
 bool(false)
index 50b939b6eb09e4caa0680af36317c33082e56bb0..5ea4a2def15fbcc83a87252af3048ca82d444416 100644 (file)
@@ -7,6 +7,7 @@ PDO_sqlite: Testing PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES
 
 echo "Creating new PDO" . PHP_EOL;
 $db = new PDO('sqlite::memory:');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
 
 $db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
 
@@ -23,6 +24,7 @@ echo sprintf("Second Error Info: SQLSTATE Error Code: (%s), Driver Specific Erro
 
 echo "Creating new PDO with Extended Result Codes turned on" . PHP_EOL;
 $db = new PDO('sqlite::memory:', '', '', [PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES => TRUE]);
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
 
 $db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
 
index 493fff78d8589653438dbc72984ec6310e23ef8e..478121e721f5349b32d8a47834e46d9e29122002 100644 (file)
@@ -6,6 +6,7 @@ PDO_sqlite: Testing transaction
 <?php
 
 $db = new PDO('sqlite::memory:');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
 
 $db->beginTransaction();
 
@@ -24,5 +25,7 @@ var_dump($r->rowCount());
 $db->query('DROP TABLE foobar');
 
 ?>
---EXPECT--
+--EXPECTF--
 int(0)
+
+Warning: PDO::query(): SQLSTATE[HY000]: General error: 6 database table is locked in %s on line %d