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 6a55e0b8bc027..9aac5a1d2f671 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 0ed9676174381..0ac78adaeeec0 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 8718208e85061..b8952521c3020 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 af95c81d803af..09ed9d75623cc 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 f507de2e062a2..10ce60df9cdc8 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -970,6 +970,9 @@ function pg_socket_poll($socket, int $read, int $write, int $timeout = -1): int #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 182dea8d221a8..778698a287563 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 0000000000000..e93108c1e7a22 --- /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)