Commit 810a9e58dc0 for php.net

commit 810a9e58dc03a93351d2ff093676871a0cc3ea03
Merge: 67135cb97b5 f3607f147fe
Author: David Carlier <devnexen@gmail.com>
Date:   Sun Sep 6 21:00:54 2026 +0100

    Merge branch 'PHP-8.5'

    * PHP-8.5:
      ext/pdo_pgsql: Fix CURSOR_SCROLL statements closing a nonexistent cursor

    # Conflicts:
    #       ext/pdo_pgsql/config.m4
    #       ext/pdo_pgsql/pgsql_statement.c

diff --cc ext/pdo_pgsql/config.m4
index 406f08465e9,eb002e3f38a..b6e1ec6edb8
--- a/ext/pdo_pgsql/config.m4
+++ b/ext/pdo_pgsql/config.m4
@@@ -25,20 -25,12 +25,26 @@@ if test "$PHP_PDO_PGSQL" != "no"; the
        or later).])],,
      [$PGSQL_LIBS])

+   PHP_CHECK_LIBRARY([pq], [PQclosePortal],
+     [AC_DEFINE([HAVE_PQCLOSEPORTAL], [1],
+       [Define to 1 if libpq has the 'PQclosePortal' function (PostgreSQL 17
+       or later).])],,
+     [$PGSQL_LIBS])
+
 +  old_CFLAGS=$CFLAGS
 +  CFLAGS="$CFLAGS $PGSQL_CFLAGS"
 +
 +  AC_CHECK_DECL([PGRES_TUPLES_CHUNK],
 +    PHP_CHECK_LIBRARY([pq], [PQsetChunkedRowsMode],
 +      [AC_DEFINE([HAVE_PG_SET_CHUNKED_ROWS_SIZE], [1],
 +        [Define to 1 if libpq has the 'PQsetChunkedRowsMode' function (PostgreSQL
 +        17 or later).])],,
 +      [$PGSQL_LIBS]),,
 +      [#include <libpq-fe.h>]
 +  )
 +
 +  CFLAGS=$old_CFLAGS
 +
    PHP_CHECK_PDO_INCLUDES

    PHP_NEW_EXTENSION([pdo_pgsql],
diff --cc ext/pdo_pgsql/config.w32
index 87ad0a661b5,fcc60140d31..5739e3060ef
--- a/ext/pdo_pgsql/config.w32
+++ b/ext/pdo_pgsql/config.w32
@@@ -4,12 -4,15 +4,16 @@@ ARG_WITH("pdo-pgsql", "PostgreSQL suppo

  if (PHP_PDO_PGSQL != "no") {
  	if (CHECK_LIB("libpq.lib", "pdo_pgsql", PHP_PDO_PGSQL) &&
 -			CHECK_HEADER_ADD_INCLUDE("libpq-fe.h", "CFLAGS_PDO_PGSQL", PHP_PDO_PGSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\pgsql;" + PHP_PHP_BUILD + "\\include\\libpq;")) {
 +			CHECK_HEADER("libpq-fe.h", "CFLAGS_PDO_PGSQL", PHP_PDO_PGSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\pgsql;" + PHP_PHP_BUILD + "\\include\\libpq;")) {
  		EXTENSION("pdo_pgsql", "pdo_pgsql.c pgsql_driver.c pgsql_statement.c pgsql_sql_parser.c");

 +		AC_DEFINE('HAVE_PG_RESULT_MEMORY_SIZE', 1, "Define to 1 if libpq has the 'PQresultMemorySize' function (PostgreSQL 12 or later).");
  		AC_DEFINE('HAVE_PDO_PGSQL', 1, "Define to 1 if the PHP extension 'pdo_pgsql' is available.");

+ 		if (GREP_HEADER("libpq-fe.h", "PQclosePortal", PHP_PDO_PGSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\pgsql;" + PHP_PHP_BUILD + "\\include\\libpq;")) {
+ 			AC_DEFINE('HAVE_PQCLOSEPORTAL', 1, "Define to 1 if libpq has the 'PQclosePortal' function (PostgreSQL 17 or later).");
+ 		}
+
  		ADD_EXTENSION_DEP('pdo_pgsql', 'pdo');
  		ADD_MAKEFILE_FRAGMENT();
  	} else {
diff --cc ext/pdo_pgsql/pgsql_statement.c
index f6f87139958,b2af41a339b..2f072553493
--- a/ext/pdo_pgsql/pgsql_statement.c
+++ b/ext/pdo_pgsql/pgsql_statement.c
@@@ -58,21 -60,47 +58,62 @@@
  #define FIN_CLOSE   0x2
  #define FIN_ABORT   0x4

 +static bool pgsql_result_status_ok(ExecStatusType status)
 +{
 +	switch (status) {
 +		case PGRES_COMMAND_OK:
 +		case PGRES_TUPLES_OK:
 +		case PGRES_SINGLE_TUPLE:
 +#ifdef HAVE_PG_SET_CHUNKED_ROWS_SIZE
 +		case PGRES_TUPLES_CHUNK:
 +#endif
 +			return true;
 +		default:
 +			return false;
 +	}
 +}
 +
+ #ifndef HAVE_PQCLOSEPORTAL
+ static bool pdo_pgsql_try_cmd(const char *cmd, const char *ok_sqlstate, pdo_pgsql_db_handle *H)
+ {
+ 	bool result = false;
+ 	char *q = NULL;
+ 	PGresult *res = NULL;
+
+ 	PGTransactionStatusType status = PQtransactionStatus(H->server);
+
+ 	switch (status) {
+ 		case PQTRANS_ACTIVE:
+ 		case PQTRANS_INERROR:
+ 			break;
+ 		case PQTRANS_INTRANS: /* failure must not abort the caller's transaction */
+ 			/* PQexec does not run the statements following a failed one */
+ 			spprintf(&q, 0, "SAVEPOINT pdo_pgsql_savepoint; %s; RELEASE SAVEPOINT pdo_pgsql_savepoint;", cmd);
+ 			res = PQexec(H->server, q);
+
+ 			if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+ 				PQclear(PQexec(H->server, "ROLLBACK TO SAVEPOINT pdo_pgsql_savepoint; RELEASE SAVEPOINT pdo_pgsql_savepoint"));
+ 			}
+
+ 			break;
+ 		default:
+ 			res = PQexec(H->server, cmd);
+ 	}
+
+ 	if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ 		result = true;
+ 	} else if (res) {
+ 		const char *sqlstate = pdo_pgsql_sqlstate(res);
+
+ 		result = sqlstate && !strcmp(sqlstate, ok_sqlstate);
+ 	}
+
+ 	if (q) efree(q);
+ 	if (res) PQclear(res);
+
+ 	return result;
+ }
+ #endif


  static void pgsql_stmt_finish(pdo_pgsql_stmt *S, int fin_mode)
diff --cc ext/pdo_pgsql/php_pdo_pgsql_int.h
index a9a09a24b70,60fa28bbcb6..dc71d5ae7a5
--- a/ext/pdo_pgsql/php_pdo_pgsql_int.h
+++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h
@@@ -67,8 -68,8 +67,9 @@@ struct pdo_pgsql_stmt
  	int *param_formats;
  	Oid *param_types;
  	int                     current_row;
 +	zend_long chunk_size;
  	bool is_prepared;
+ 	bool is_cursor_declared;
  	bool is_unbuffered;
  	bool is_running_unbuffered;
  };