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
This commit is contained in:
David Carlier 2024-06-16 13:01:36 +01:00
parent 291eef285c
commit 1da352c367
No known key found for this signature in database
GPG key ID: 8486F847B4B94EF1
7 changed files with 98 additions and 1 deletions

4
NEWS
View file

@ -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)

View file

@ -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
========================================

View file

@ -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"

View file

@ -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

View file

@ -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 {

View file

@ -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
};

View file

@ -0,0 +1,33 @@
--TEST--
PostgreSQL pg_close_stmt
--EXTENSIONS--
pgsql
--SKIPIF--
<?php include("inc/skipif.inc");
if (!function_exists("pg_close_stmt")) die("skip pg_close_stmt unsupported");
?>
--FILE--
<?php
include('inc/config.inc');
$query = 'SELECT $1::text IS NULL;';
$params_null = [null];
$db = pg_connect($conn_str);
$res = pg_prepare($db, 'test', $query);
$res = pg_execute($db, 'test', $params_null);
$res = pg_close_stmt($db, 'test');
var_dump($res !== false);
var_dump(pg_result_status($res) === PGSQL_COMMAND_OK);
pg_prepare($db, 'test', $query);
$res = pg_execute($db, 'test', $params_null);
pg_free_result($res);
pg_close($db);
?>
--EXPECT--
bool(true)
bool(true)