From 1da352c367cef4327942fdba5cc360aea977fbe6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 16 Jun 2024 13:01:36 +0100 Subject: [PATCH] ext/pgsql: adding pg_close_stmt. up to postgresql 17, when done with a prepared statement, we could release it with DEALLOCATE sql command which is fine ; until we want to implement a cache solution based on statement ids. Since PostgreSQL 17, PQclosePrepared uses internally the `close` protocol allowing to reuse the statement name while still freeing it. Since the close protocol implementation had been added on libpq within this release, no way to reimplement it. close GH-14584 --- NEWS | 4 ++++ UPGRADING | 5 +++++ ext/pgsql/config.m4 | 3 +++ ext/pgsql/pgsql.c | 36 ++++++++++++++++++++++++++++++ ext/pgsql/pgsql.stub.php | 3 +++ ext/pgsql/pgsql_arginfo.h | 15 ++++++++++++- ext/pgsql/tests/pg_close_stmt.phpt | 33 +++++++++++++++++++++++++++ 7 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 ext/pgsql/tests/pg_close_stmt.phpt diff --git a/NEWS b/NEWS index 6a55e0b8bc0..9aac5a1d2f6 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ PHP NEWS Pdo\Pgsql::prepare(…, [ PDO::ATTR_PREFETCH => 0 ]) make fetch() lazy instead of storing the whole result set in memory (Guillaume Outters) +- PGSQL: + . Added pg_close_stmt to close a prepared statement while allowing + its name to be reused. (David Carlier) + - Random: . Moves from /dev/urandom usage to arc4random_buf on Haiku. (David Carlier) diff --git a/UPGRADING b/UPGRADING index 0ed96761743..0ac78adaeee 100644 --- a/UPGRADING +++ b/UPGRADING @@ -59,6 +59,11 @@ PHP 8.5 UPGRADE NOTES 6. New Functions ======================================== +- PGSQL@ + . pg_close_stmt offers an alternative way to close a prepared + statement from the DEALLOCATE sql command in that we can reuse + its name afterwards. + ======================================== 7. New Classes and Interfaces ======================================== diff --git a/ext/pgsql/config.m4 b/ext/pgsql/config.m4 index 8718208e850..b8952521c30 100644 --- a/ext/pgsql/config.m4 +++ b/ext/pgsql/config.m4 @@ -33,6 +33,9 @@ if test "$PHP_PGSQL" != "no"; then [Define to 1 if libpq has the 'PQsetChunkedRowsMode' function (PostgreSQL 17 or later).])],, [$PGSQL_LIBS]) + PHP_CHECK_LIBRARY([pq], [PQclosePrepared], + [AC_DEFINE([HAVE_PG_CLOSE_STMT], [1], [PostgreSQL 17 or later])],, + [$PGSQL_LIBS]) old_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $PGSQL_CFLAGS" diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index af95c81d803..09ed9d75623 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -6256,3 +6256,39 @@ PHP_FUNCTION(pg_set_chunked_rows_size) RETURN_BOOL(PQsetChunkedRowsMode(link->conn, (int)size) == 1); } #endif + +#if defined(HAVE_PG_CLOSE_STMT) +PHP_FUNCTION(pg_close_stmt) +{ + zval *pgsql_link; + pgsql_link_handle *link; + PGresult *pgsql_result; + zend_string *stmt; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pgsql_link, pgsql_link_ce) + Z_PARAM_STR(stmt) + ZEND_PARSE_PARAMETERS_END(); + + if (ZSTR_LEN(stmt) == 0) { + zend_argument_value_error(2, "cannot be empty"); + RETURN_THROWS(); + } + + link = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(link); + + pgsql_result = PQclosePrepared(link->conn, ZSTR_VAL(stmt)); + + if (PQresultStatus(pgsql_result) != PGRES_COMMAND_OK) { + RETURN_FALSE; + } else { + pgsql_result_handle *pg_handle; + object_init_ex(return_value, pgsql_result_ce); + pg_handle = Z_PGSQL_RESULT_P(return_value); + pg_handle->conn = link->conn; + pg_handle->result = pgsql_result; + pg_handle->row = 0; + } +} +#endif diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index f507de2e062..10ce60df9cd 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -970,6 +970,9 @@ namespace { #ifdef HAVE_PG_SET_CHUNKED_ROWS_SIZE function pg_set_chunked_rows_size(Pgsql\Connection $connection, int $size): bool {} #endif +#ifdef HAVE_PG_CLOSE_STMT + function pg_close_stmt(Pgsql\Connection $connection, string $statement_name): Pgsql\Result|false {} +#endif } namespace PgSql { diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 182dea8d221..778698a2875 100644 --- a/ext/pgsql/pgsql_arginfo.h +++ b/ext/pgsql/pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0b89a48c27c6682542312391f10a3ab8fb719ef8 */ + * Stub hash: 1f0141abe7cf476c305b074e31ce69a48b6eee21 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_pg_connect, 0, 1, PgSql\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, connection_string, IS_STRING, 0) @@ -495,6 +495,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_set_chunked_rows_size, 0, 2, ZEND_END_ARG_INFO() #endif +#if defined(HAVE_PG_CLOSE_STMT) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_pg_close_stmt, 0, 2, Pgsql\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, connection, Pgsql\\Connection, 0) + ZEND_ARG_TYPE_INFO(0, statement_name, IS_STRING, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_FUNCTION(pg_connect); ZEND_FUNCTION(pg_pconnect); ZEND_FUNCTION(pg_connect_poll); @@ -598,6 +605,9 @@ ZEND_FUNCTION(pg_socket_poll); #if defined(HAVE_PG_SET_CHUNKED_ROWS_SIZE) ZEND_FUNCTION(pg_set_chunked_rows_size); #endif +#if defined(HAVE_PG_CLOSE_STMT) +ZEND_FUNCTION(pg_close_stmt); +#endif static const zend_function_entry ext_functions[] = { ZEND_FE(pg_connect, arginfo_pg_connect) @@ -725,6 +735,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(pg_socket_poll, arginfo_pg_socket_poll) #if defined(HAVE_PG_SET_CHUNKED_ROWS_SIZE) ZEND_FE(pg_set_chunked_rows_size, arginfo_pg_set_chunked_rows_size) +#endif +#if defined(HAVE_PG_CLOSE_STMT) + ZEND_FE(pg_close_stmt, arginfo_pg_close_stmt) #endif ZEND_FE_END }; diff --git a/ext/pgsql/tests/pg_close_stmt.phpt b/ext/pgsql/tests/pg_close_stmt.phpt new file mode 100644 index 00000000000..e93108c1e7a --- /dev/null +++ b/ext/pgsql/tests/pg_close_stmt.phpt @@ -0,0 +1,33 @@ +--TEST-- +PostgreSQL pg_close_stmt +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true)