Commit f0236f11ba7 for php.net
commit f0236f11ba77cf372f1709493988bf37f10c09cd
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Sat Jun 20 22:00:28 2026 -0400
pdo_pgsql: preserve the pending exception when a COPY row fails to convert
pgsqlCopyFromArray() feeds each row through try_convert_to_string(). A
non-stringable row throws a TypeError, but both the array and iterator
branches then called pdo_pgsql_error() and recorded a fabricated
PGRES_FATAL_ERROR, leaving PDO::errorInfo() reporting "HY000" for what is
a client-side type error. Return through the pending exception instead of
overwriting the driver error state.
Closes GH-22384
diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c
index db640f2a1b5..54b2e25f72f 100644
--- a/ext/pdo_pgsql/pgsql_driver.c
+++ b/ext/pdo_pgsql/pgsql_driver.c
@@ -720,6 +720,9 @@ void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS)
if (Z_TYPE_P(pg_rows) == IS_ARRAY) {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {
if (!_pdo_pgsql_send_copy_data(H, tmp)) {
+ if (EG(exception)) {
+ RETURN_THROWS();
+ }
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
PDO_HANDLE_DBH_ERR();
RETURN_FALSE;
@@ -742,6 +745,9 @@ void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS)
tmp = iter->funcs->get_current_data(iter);
if (!_pdo_pgsql_send_copy_data(H, tmp)) {
zend_iterator_dtor(iter);
+ if (EG(exception)) {
+ RETURN_THROWS();
+ }
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
PDO_HANDLE_DBH_ERR();
RETURN_FALSE;
diff --git a/ext/pdo_pgsql/tests/copy_from_array_non_stringable.phpt b/ext/pdo_pgsql/tests/copy_from_array_non_stringable.phpt
new file mode 100644
index 00000000000..65edb30d667
--- /dev/null
+++ b/ext/pdo_pgsql/tests/copy_from_array_non_stringable.phpt
@@ -0,0 +1,49 @@
+--TEST--
+PDO PgSQL pgsqlCopyFromArray()/copyFromArray() with a non-stringable row throws without polluting errorInfo
+--EXTENSIONS--
+pdo_pgsql
+--SKIPIF--
+<?php
+require __DIR__ . '/config.inc';
+require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(__DIR__ . '/common.phpt');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+
+$db->exec('CREATE TABLE test_copy_non_stringable (v text)');
+
+try {
+ $db->pgsqlCopyFromArray('test_copy_non_stringable', [new stdClass()]);
+} catch (\Error $e) {
+ echo $e->getMessage(), PHP_EOL;
+}
+
+var_dump($db->errorInfo()[0]);
+
+$pgsql = PDOTest::test_factory(__DIR__ . '/common.phpt', Pdo\Pgsql::class, true);
+$pgsql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+
+try {
+ $pgsql->copyFromArray('test_copy_non_stringable', [new stdClass()]);
+} catch (\Error $e) {
+ echo $e->getMessage(), PHP_EOL;
+}
+
+var_dump($pgsql->errorInfo()[0]);
+?>
+--CLEAN--
+<?php
+require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(__DIR__ . '/common.phpt');
+$db->query('DROP TABLE IF EXISTS test_copy_non_stringable CASCADE');
+?>
+--EXPECTF--
+Deprecated: Method PDO::pgsqlCopyFromArray() is deprecated since 8.5, use Pdo\Pgsql::copyFromArray() instead in %s on line %d
+Object of class stdClass could not be converted to string
+string(5) "00000"
+Object of class stdClass could not be converted to string
+string(5) "00000"