From f0236f11ba77cf372f1709493988bf37f10c09cd Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sat, 20 Jun 2026 22:00:28 -0400 Subject: [PATCH] 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 --- ext/pdo_pgsql/pgsql_driver.c | 6 +++ .../tests/copy_from_array_non_stringable.phpt | 49 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 ext/pdo_pgsql/tests/copy_from_array_non_stringable.phpt diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index db640f2a1b53..54b2e25f72f6 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 000000000000..65edb30d6671 --- /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-- + +--FILE-- +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-- +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"