Skip to content

Commit 5075240

Browse files
AllenJBcmb69
authored andcommitted
Change the default PDO error mode to exceptions
According to <https://www.php.net/manual/en/pdo.error-handling.php>.
1 parent 8ffbd46 commit 5075240

15 files changed

+72
-6
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ PHP NEWS
9393
. Don't ignore invalid escape sequences. (sjon)
9494

9595
- PDO:
96+
. Changed default PDO error mode to exceptions. (AllenJB)
9697
. Fixed bug #77849 (Disable cloning of PDO handle/connection objects).
9798
(camporter)
9899

UPGRADING

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ PHP 8.0 UPGRADE NOTES
291291
now ignored.
292292

293293
- PDO:
294+
. The default error handling mode has been changed from "silent" to
295+
"exceptions". See https://www.php.net/manual/en/pdo.error-handling.php
296+
for details of behavior changes and how to explicitly set this attribute.
297+
RFC: https://wiki.php.net/rfc/pdo_default_errmode
294298
. The method PDOStatement::setFetchMode() now accepts the following signature:
295299

296300
PDOStatement::setFetchMode($mode, $classname, $params)

ext/pdo/pdo_dbh.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ PHP_METHOD(PDO, __construct)
340340
}
341341

342342
dbh->auto_commit = pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1);
343+
dbh->error_mode = pdo_attr_lval(options, PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);
343344

344345
if (!dbh->data_source || (username && !dbh->username) || (password && !dbh->password)) {
345346
php_error_docref(NULL, E_ERROR, "Out of memory");

ext/pdo/tests/bug_44159.phpt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ try {
1212
--FILE--
1313
<?php
1414
$pdo = new PDO("sqlite:".__DIR__."/foo.db");
15+
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
1516

1617
$attrs = array(PDO::ATTR_STATEMENT_CLASS, PDO::ATTR_STRINGIFY_FETCHES, PDO::NULL_TO_STRING);
1718

@@ -26,15 +27,23 @@ foreach ($attrs as $attr) {
2627
?>
2728
--EXPECTF--
2829
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
30+
31+
Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d
2932
bool(false)
3033

3134
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
35+
36+
Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d
3237
bool(false)
3338

3439
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
40+
41+
Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d
3542
bool(false)
3643

3744
Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error: attribute value must be an integer in %s on line %d
45+
46+
Warning: PDO::setAttribute(): SQLSTATE[HY000]: General error in %s on line %d
3847
bool(false)
3948
bool(true)
4049
bool(true)

ext/pdo/tests/pdo_test.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class PDOTest {
3737
if (!$db) {
3838
die("Could not create PDO object (DSN=$dsn, user=$user)\n");
3939
}
40+
// Ignore errors about non-existant tables
41+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
4042

4143
// clean up any crufty test tables we might have left behind
4244
// on a previous run

ext/pdo_mysql/tests/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,11 @@ PDO_MYSQL_TEST_CHARSET
3131
NOTE: if any of `PDO_MYSQL_TEST_[HOST|DB|SOCKET|ENGINE|CHARSET]` is part of
3232
`PDO_MYSQL_TEST_DSN`, the values must match. That is, for example, for
3333
`PDO_MYSQL_TEST_DSN = mysql:dbname=test` you MUST set `PDO_MYSQL_TEST_DB=test`.
34+
35+
## MySQL User Permissions
36+
37+
The MySQL user used to run the tests must have full permissions on the test
38+
database, plus the following additional permissions:
39+
40+
* SUPER: Required to [create functions if binary logging is enabled](https://dev.mysql.com/doc/refman/8.0/en/stored-programs-logging.html#sa38412929)
41+
* SELECT permissions on performance_schema.session_connect_attrs

ext/pdo_mysql/tests/pdo_mysql___construct.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ MySQLPDOTest::skip();
150150
$dsn = MySQLPDOTest::getDSN(array('dbname' => $db), 'dbname=' . $invalid_db);
151151
try { $db = @new PDO($dsn, $user, $pass); assert(false); printf("%s\n", $dsn); } catch (PDOException $e) {
152152
$tmp = $e->getMessage();
153-
if (!stristr($tmp, '42000') && !stristr($tmp, '1049'))
153+
// 1044 may occur here if running tests using a custom user that does not have access to all databases
154+
if (!stristr($tmp, '42000') && !stristr($tmp, '1049') && !stristr($tmp, '1044'))
154155
printf("[022] Cannot find proper error codes: %s\n", $tmp);
155156
}
156157

ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ MySQLPDOTest::skip();
1818

1919
try {
2020
$db = new PDO($dsn, $user, $pass, array($option => $value));
21+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
2122
if (!is_object($db) || ($value !== ($tmp = @$db->getAttribute($option))))
2223
printf("[%03d] Expecting '%s'/%s got '%s'/%s' for options '%s'\n",
2324
$offset,
@@ -81,6 +82,7 @@ MySQLPDOTest::skip();
8182
printf("[003] [TODO][CHANGEREQUEST] Please, lets not ignore invalid options and bail out!\n");
8283

8384
$db = new PDO($dsn, $user, $pass);
85+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
8486
foreach ($valid_options as $option => $name) {
8587
/* TODO getAttribute() is pretty poor in supporting the options, suppress errors */
8688
$tmp = @$db->getAttribute($option);
@@ -155,10 +157,11 @@ MySQLPDOTest::skip();
155157
set_option_and_check(34, PDO::MYSQL_ATTR_DIRECT_QUERY, 0, 'PDO::MYSQL_ATTR_DIRECT_QUERY');
156158

157159
} catch (PDOException $e) {
158-
printf("[001] %s, [%s] %s\n",
160+
printf("[001] %s, [%s] %s Line: %s\n",
159161
$e->getMessage(),
160162
(is_object($db)) ? $db->errorCode() : 'n/a',
161-
(is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a');
163+
(is_object($db)) ? implode(' ', $db->errorInfo()) : 'n/a',
164+
$e->getLine());
162165
}
163166

164167
print "done!";

ext/pdo_mysql/tests/pdo_mysql_attr_multi_statements.phpt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ error_reporting=E_ALL
1919

2020
$table = sprintf("test_%s", md5(mt_rand(0, PHP_INT_MAX)));
2121
$db = new PDO($dsn, $user, $pass);
22+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
2223
$db->exec(sprintf('DROP TABLE IF EXISTS %s', $table));
2324
$create = sprintf('CREATE TABLE %s(id INT)', $table);
2425
$db->exec($create);
@@ -35,6 +36,7 @@ error_reporting=E_ALL
3536

3637
// New connection, does not allow multiple statements.
3738
$db = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => false));
39+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
3840
$stmt = $db->query(sprintf('SELECT * FROM %s; INSERT INTO %s(id) VALUES (3)', $table, $table));
3941
var_dump($stmt);
4042
$info = $db->errorInfo();
@@ -49,7 +51,7 @@ error_reporting=E_ALL
4951
$db->exec(sprintf('DROP TABLE IF EXISTS %s', $table));
5052
print "done!";
5153
?>
52-
--EXPECT--
54+
--EXPECTF--
5355
string(5) "00000"
5456
array(2) {
5557
[0]=>
@@ -70,6 +72,8 @@ array(1) {
7072
string(1) "1"
7173
}
7274
}
75+
76+
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
7377
bool(false)
7478
string(5) "42000"
7579
array(2) {

ext/pdo_mysql/tests/pdo_mysql_exec.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ MySQLPDOTest::skip();
176176
<?php
177177
require __DIR__ . '/mysql_pdo_test.inc';
178178
$db = MySQLPDOTest::factory();
179+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
179180
@$db->exec('DROP TABLE IF EXISTS test');
180181
?>
181182
--EXPECTF--

ext/pdo_mysql/tests/pdo_mysql_multi_stmt_nextrowset.phpt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,14 @@ if (!MySQLPDOTest::isPDOMySQLnd())
5050
$user = PDO_MYSQL_TEST_USER;
5151
$pass = PDO_MYSQL_TEST_PASS;
5252
$db = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => $multi));
53+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
5354
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
5455
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
5556
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
5657
test_proc($db);
5758

5859
$db = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => $multi));
60+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
5961
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
6062
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 0);
6163
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
@@ -86,7 +88,7 @@ if (!MySQLPDOTest::isPDOMySQLnd())
8688
require __DIR__ . '/mysql_pdo_test.inc';
8789
MySQLPDOTest::dropTestTable();
8890
?>
89-
--EXPECT--
91+
--EXPECTF--
9092
Native PS...
9193

9294
Testing with PDO::MYSQL_ATTR_MULTI_STATEMENTS set to false
@@ -172,6 +174,8 @@ array(3) {
172174
}
173175
}
174176
bool(false)
177+
178+
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
175179
string(5) "42000"
176180

177181
Testing with PDO::MYSQL_ATTR_MULTI_STATEMENTS set to true

ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ MySQLPDOTest::skip();
1818

1919
$db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
2020
$db2 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
21+
$db1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
22+
$db2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
2123
$db1->exec('SET @pdo_persistent_connection=1');
2224
$stmt = $db2->query('SELECT @pdo_persistent_connection as _pers');
2325
$tmp = $stmt->fetch(PDO::FETCH_ASSOC);
@@ -37,6 +39,7 @@ MySQLPDOTest::skip();
3739

3840
$db1 = NULL; /* should be equal to closing to my understanding */
3941
$db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
42+
$db1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
4043
$stmt = $db1->query('SELECT CONNECTION_ID() as _con1');
4144
$tmp = $stmt->fetch(PDO::FETCH_ASSOC);
4245
$con1 = $tmp['_con1'];
@@ -60,11 +63,13 @@ MySQLPDOTest::skip();
6063
}
6164

6265
$db1 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => false));
66+
$db1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
6367
$stmt = $db1->query('SELECT CONNECTION_ID() as _con1');
6468
$tmp = $stmt->fetch(PDO::FETCH_ASSOC);
6569
$con1 = $tmp['_con1'];
6670

6771
@$db2 = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
72+
$db2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
6873
$stmt = $db2->query('SELECT CONNECTION_ID() as _con2');
6974
$tmp = $stmt->fetch(PDO::FETCH_ASSOC);
7075
$con2 = $tmp['_con2'];

ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ if (!extension_loaded('pdo_sqlite')) print 'skip not loaded';
88
<?php
99

1010
$db = new PDO('sqlite::memory:');
11+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
12+
1113
$db->exec('CREATE TABLE testing (id INTEGER , name VARCHAR)');
1214
$db->exec('INSERT INTO testing VALUES(1, "php")');
1315
$db->exec('INSERT INTO testing VALUES(2, "")');
@@ -91,18 +93,28 @@ array(2) {
9193
}
9294

9395
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: function 'nothing' not found or invalid function name in %s on line %d
96+
97+
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
9498
bool(false)
9599

96100
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: function '' not found or invalid function name in %s on line %d
101+
102+
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
97103
bool(false)
98104

99105
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: no array or string given in %s on line %d
106+
107+
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
100108
bool(false)
101109

102110
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: no array or string given in %s on line %d
111+
112+
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
103113
bool(false)
104114

105115
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: class 'PDOStatement' does not have a method 'foo' in %s on line %d
116+
117+
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
106118
bool(false)
107119
array(2) {
108120
[0]=>
@@ -118,10 +130,16 @@ array(2) {
118130
}
119131

120132
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: non-static method bar::test2() cannot be called statically in %s on line %d
133+
134+
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
121135
bool(false)
122136

123137
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: non-static method bar::test3() cannot be called statically in %s on line %d
138+
139+
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
124140
bool(false)
125141

126142
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: class 'bar' does not have a method 'inexistent' in %s on line %d
143+
144+
Warning: PDOStatement::fetchAll(): SQLSTATE[HY000]: General error in %s on line %d
127145
bool(false)

ext/pdo_sqlite/tests/pdo_sqlite_extendederror_attr.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ PDO_sqlite: Testing PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES
77

88
echo "Creating new PDO" . PHP_EOL;
99
$db = new PDO('sqlite::memory:');
10+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
1011

1112
$db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
1213

@@ -23,6 +24,7 @@ echo sprintf("Second Error Info: SQLSTATE Error Code: (%s), Driver Specific Erro
2324

2425
echo "Creating new PDO with Extended Result Codes turned on" . PHP_EOL;
2526
$db = new PDO('sqlite::memory:', '', '', [PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES => TRUE]);
27+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
2628

2729
$db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
2830

ext/pdo_sqlite/tests/pdo_sqlite_transaction.phpt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ PDO_sqlite: Testing transaction
66
<?php
77

88
$db = new PDO('sqlite::memory:');
9+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
910

1011
$db->beginTransaction();
1112

@@ -24,5 +25,7 @@ var_dump($r->rowCount());
2425
$db->query('DROP TABLE foobar');
2526

2627
?>
27-
--EXPECT--
28+
--EXPECTF--
2829
int(0)
30+
31+
Warning: PDO::query(): SQLSTATE[HY000]: General error: 6 database table is locked in %s on line %d

0 commit comments

Comments
 (0)