From 99f8c2f113e4675156654eb1b584bdb8203a02f3 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 14 Jun 2024 19:06:58 +0100 Subject: [PATCH] ext/pgsql: adding postgresql 17 new libpq wrapper call. pg_set_chunked_rows_mode to allow to fetch results in chunk of max N rows. --- ext/pgsql/config.m4 | 1 + ext/pgsql/pgsql.c | 38 +++++++++++--- ext/pgsql/pgsql.stub.php | 11 +++++ ext/pgsql/pgsql_arginfo.h | 18 ++++++- ext/pgsql/tests/pg_set_chunked_rows_size.phpt | 49 +++++++++++++++++++ 5 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 ext/pgsql/tests/pg_set_chunked_rows_size.phpt diff --git a/ext/pgsql/config.m4 b/ext/pgsql/config.m4 index b7a5ebb8110bb..46f52c8a12cb1 100644 --- a/ext/pgsql/config.m4 +++ b/ext/pgsql/config.m4 @@ -70,6 +70,7 @@ if test "$PHP_PGSQL" != "no"; then AC_CHECK_LIB(pq, PQresultMemorySize, AC_DEFINE(HAVE_PG_RESULT_MEMORY_SIZE,1,[PostgreSQL 12 or later])) AC_CHECK_LIB(pq, PQchangePassword, AC_DEFINE(HAVE_PG_CHANGE_PASSWORD,1,[PostgreSQL 17 or later])) AC_CHECK_LIB(pq, PQsocketPoll, AC_DEFINE(HAVE_PG_SOCKET_POLL,1,[PostgreSQL 17 or later])) + AC_CHECK_LIB(pq, PQsetChunkedRowsMode, AC_DEFINE(HAVE_PG_SET_CHUNKED_ROWS_SIZE,1,[PostgreSQL 17 or later])) dnl Available since PostgreSQL 12. AC_CACHE_CHECK([if PGVerbosity enum has PQERRORS_SQLSTATE], diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index a3ce194a29ec8..ee746c0d47c80 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -40,9 +40,7 @@ #include "php_globals.h" #include "zend_exceptions.h" #include "zend_attributes.h" -#if !defined(HAVE_PG_SOCKET_POLL) #include "php_network.h" -#endif #ifdef HAVE_PGSQL @@ -935,16 +933,16 @@ static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type array_init(return_value); res = PQexec(pgsql, "SHOW jit_provider"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { - add_assoc_null(return_value, "jit_provider"); + add_assoc_null(return_value, "jit_provider"); } else { - add_assoc_string(return_value, "jit_provider", PQgetvalue(res, 0, 0)); + add_assoc_string(return_value, "jit_provider", PQgetvalue(res, 0, 0)); } PQclear(res); res = PQexec(pgsql, "SHOW jit"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { - add_assoc_null(return_value, "jit"); + add_assoc_null(return_value, "jit"); } else { - add_assoc_string(return_value, "jit", PQgetvalue(res, 0, 0)); + add_assoc_string(return_value, "jit", PQgetvalue(res, 0, 0)); } PQclear(res); return; @@ -4346,7 +4344,7 @@ static int php_pgsql_fd_cast(php_stream *stream, int cast_as, void **ret) /* {{{ } if (ret) { - *(php_socket_t *)ret = fd_number; + *(php_socket_t *)ret = fd_number; } } return SUCCESS; @@ -6248,3 +6246,29 @@ PHP_FUNCTION(pg_socket_poll) RETURN_LONG((zend_long)PQsocketPoll(socket, (int)read, (int)write, (int)ts)); } + +#if defined(HAVE_PG_SET_CHUNKED_ROWS_SIZE) +PHP_FUNCTION(pg_set_chunked_rows_size) +{ + zval *pgsql_link; + pgsql_link_handle *link; + zend_long size; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pgsql_link, pgsql_link_ce) + Z_PARAM_LONG(size) + ZEND_PARSE_PARAMETERS_END(); + + if (size < 1 || size > INT_MAX) { + zend_argument_value_error(2, "must be between 1 and %d", INT_MAX); + RETURN_THROWS(); + } + + link = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(link); + + /** can still fail if it is not allowed e.g. already fetched results **/ + + RETURN_BOOL(PQsetChunkedRowsMode(link->conn, (int)size) == 1); +} +#endif diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index c58c842dafa0f..812980b823c88 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -245,6 +245,13 @@ * @cvalue PGRES_TUPLES_OK */ const PGSQL_TUPLES_OK = UNKNOWN; +#ifdef HAVE_PG_SET_CHUNKED_ROWS_SIZE + /** + * @var int + * @cvalue PGRES_TUPLES_CHUNK + */ + const PGSQL_TUPLES_CHUNK = UNKNOWN; +#endif /** * @var int * @cvalue PGRES_COPY_OUT @@ -963,6 +970,10 @@ function pg_put_copy_end(PgSql\Connection $connection, ?string $error = null): i * @param resource $socket */ 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 } namespace PgSql { diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 69a9606393eea..ec41bcaea94cb 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: c4b75f8f59a1c4f906cf89fb6aea66436a4eedc3 */ + * Stub hash: 8feb7d195444838a4c016910593d30dd76aafe89 */ 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) @@ -490,6 +490,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_socket_poll, 0, 3, IS_LONG, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() +#if defined(HAVE_PG_SET_CHUNKED_ROWS_SIZE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_set_chunked_rows_size, 0, 2, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, connection, Pgsql\\Connection, 0) + ZEND_ARG_TYPE_INFO(0, size, IS_LONG, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_FUNCTION(pg_connect); ZEND_FUNCTION(pg_pconnect); ZEND_FUNCTION(pg_connect_poll); @@ -592,6 +599,9 @@ ZEND_FUNCTION(pg_change_password); ZEND_FUNCTION(pg_put_copy_data); ZEND_FUNCTION(pg_put_copy_end); ZEND_FUNCTION(pg_socket_poll); +#if defined(HAVE_PG_SET_CHUNKED_ROWS_SIZE) +ZEND_FUNCTION(pg_set_chunked_rows_size); +#endif static const zend_function_entry ext_functions[] = { ZEND_FE(pg_connect, arginfo_pg_connect) @@ -719,6 +729,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(pg_put_copy_data, arginfo_pg_put_copy_data) ZEND_FE(pg_put_copy_end, arginfo_pg_put_copy_end) 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 ZEND_FE_END }; @@ -783,6 +796,9 @@ static void register_pgsql_symbols(int module_number) REGISTER_LONG_CONSTANT("PGSQL_EMPTY_QUERY", PGRES_EMPTY_QUERY, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_COMMAND_OK", PGRES_COMMAND_OK, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_TUPLES_OK", PGRES_TUPLES_OK, CONST_PERSISTENT); +#if defined(HAVE_PG_SET_CHUNKED_ROWS_SIZE) + REGISTER_LONG_CONSTANT("PGSQL_TUPLES_CHUNK", PGRES_TUPLES_CHUNK, CONST_PERSISTENT); +#endif REGISTER_LONG_CONSTANT("PGSQL_COPY_OUT", PGRES_COPY_OUT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_COPY_IN", PGRES_COPY_IN, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PGSQL_BAD_RESPONSE", PGRES_BAD_RESPONSE, CONST_PERSISTENT); diff --git a/ext/pgsql/tests/pg_set_chunked_rows_size.phpt b/ext/pgsql/tests/pg_set_chunked_rows_size.phpt new file mode 100644 index 0000000000000..946c455595e6e --- /dev/null +++ b/ext/pgsql/tests/pg_set_chunked_rows_size.phpt @@ -0,0 +1,49 @@ +--TEST-- +PostgreSQL pg_set_chunked_rows_size +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- +getMessage() . PHP_EOL; +} + +var_dump(pg_set_chunked_rows_size($conn, 1)); +$result = pg_get_result($conn); +var_dump(pg_result_status($result) === PGSQL_TUPLES_CHUNK); +var_dump(pg_num_rows($result)); +var_dump(pg_set_chunked_rows_size($conn, 10)); +?> +--CLEAN-- + +--EXPECTF-- +bool(true) +pg_set_chunked_rows_size(): Argument #2 ($size) must be between 1 and %d +bool(true) +bool(true) +int(1) +bool(false)