From d9bbcdf684cce2290fc06b85c2360128473b2bc4 Mon Sep 17 00:00:00 2001 From: Dmitriy Degtyaryov Date: Fri, 10 Nov 2023 11:26:02 +0500 Subject: [PATCH 1/8] Fix GH-9344: pgsql pipeline mode proposal. Adding pg_send_flush_request. Fix freeze after next execute pg_send_* on PQgetResult in _php_pgsql_link_has_results. Set nonblocking for pipelining mode. No flush client buffer in pg_send_* for pipelining mode. --- UPGRADING | 1 + ext/pgsql/pgsql.c | 171 ++++++++++++++++++++------ ext/pgsql/pgsql.stub.php | 1 + ext/pgsql/pgsql_arginfo.h | 10 ++ ext/pgsql/tests/pg_pipeline_sync.phpt | 57 +++++++-- 5 files changed, 191 insertions(+), 49 deletions(-) diff --git a/UPGRADING b/UPGRADING index b1600aa0dd550..2ba242d811e2a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -432,6 +432,7 @@ PHP 8.3 UPGRADE NOTES in error messages (with libpq >= 9.6). . Added pg_enter_pipeline_mode(). . Added pg_exit_pipeline_mode(). + . Added pg_send_flush_request(). . Added pg_pipeline_sync(). . Added pg_pipeline_status(). diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 31f15cbe6acab..165342edcc51c 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3595,6 +3595,9 @@ PHP_FUNCTION(pg_send_query) char *query; size_t len; PGconn *pgsql; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif int is_non_blocking; int ret; @@ -3606,23 +3609,40 @@ PHP_FUNCTION(pg_send_query) CHECK_PGSQL_LINK(link); pgsql = link->conn; - is_non_blocking = PQisnonblocking(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (is_pipeline_mode) { + is_non_blocking = 1; + } else { +#endif + is_non_blocking = PQisnonblocking(pgsql); - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } - if (_php_pgsql_link_has_results(pgsql)) { - php_error_docref(NULL, E_NOTICE, - "There are results on this connection. Call pg_get_result() until it returns FALSE"); + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif if (is_non_blocking) { if (!PQsendQuery(pgsql, query)) { RETURN_FALSE; } - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { if (!PQsendQuery(pgsql, query)) { if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { @@ -3667,6 +3687,9 @@ PHP_FUNCTION(pg_send_query_params) char *query; size_t query_len; PGconn *pgsql; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif int is_non_blocking; int ret; @@ -3678,17 +3701,26 @@ PHP_FUNCTION(pg_send_query_params) CHECK_PGSQL_LINK(link); pgsql = link->conn; - is_non_blocking = PQisnonblocking(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (is_pipeline_mode) { + is_non_blocking = 1; + } else { +#endif + is_non_blocking = PQisnonblocking(pgsql); - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } - if (_php_pgsql_link_has_results(pgsql)) { - php_error_docref(NULL, E_NOTICE, - "There are results on this connection. Call pg_get_result() until it returns FALSE"); + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)); if (num_params > 0) { @@ -3727,7 +3759,15 @@ PHP_FUNCTION(pg_send_query_params) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -3761,6 +3801,9 @@ PHP_FUNCTION(pg_send_prepare) char *query, *stmtname; size_t stmtname_len, query_len; PGconn *pgsql; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif int is_non_blocking; int ret; @@ -3772,17 +3815,26 @@ PHP_FUNCTION(pg_send_prepare) CHECK_PGSQL_LINK(link); pgsql = link->conn; - is_non_blocking = PQisnonblocking(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (is_pipeline_mode) { + is_non_blocking = 1; + } else { +#endif + is_non_blocking = PQisnonblocking(pgsql); - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } - if (_php_pgsql_link_has_results(pgsql)) { - php_error_docref(NULL, E_NOTICE, - "There are results on this connection. Call pg_get_result() until it returns FALSE"); + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) { if (is_non_blocking) { @@ -3798,7 +3850,15 @@ PHP_FUNCTION(pg_send_prepare) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -3834,6 +3894,9 @@ PHP_FUNCTION(pg_send_execute) char *stmtname; size_t stmtname_len; PGconn *pgsql; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif int is_non_blocking; int ret; @@ -3845,17 +3908,26 @@ PHP_FUNCTION(pg_send_execute) CHECK_PGSQL_LINK(link); pgsql = link->conn; - is_non_blocking = PQisnonblocking(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (is_pipeline_mode) { + is_non_blocking = 1; + } else { +#endif + is_non_blocking = PQisnonblocking(pgsql); - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } - if (_php_pgsql_link_has_results(pgsql)) { - php_error_docref(NULL, E_NOTICE, - "There are results on this connection. Call pg_get_result() until it returns FALSE"); + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)); if (num_params > 0) { @@ -3896,7 +3968,15 @@ PHP_FUNCTION(pg_send_execute) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -5891,6 +5971,8 @@ PHP_FUNCTION(pg_enter_pipeline_mode) pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(pgsql_handle); + PQsetnonblocking(pgsql_handle->conn, 1); + RETURN_BOOL(PQenterPipelineMode(pgsql_handle->conn)); } @@ -5906,9 +5988,26 @@ PHP_FUNCTION(pg_exit_pipeline_mode) pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(pgsql_handle); + PQsetnonblocking(pgsql_handle->conn, 0); + RETURN_BOOL(PQexitPipelineMode(pgsql_handle->conn)); } +PHP_FUNCTION(pg_send_flush_request) +{ + zval *pgsql_link; + pgsql_link_handle *pgsql_handle; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { + RETURN_THROWS(); + } + + pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(pgsql_handle); + + RETURN_BOOL(PQsendFlushRequest(pgsql_handle->conn)); +} + PHP_FUNCTION(pg_pipeline_sync) { zval *pgsql_link; diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index 7ce709d17e2d8..082f72fa00d69 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -966,6 +966,7 @@ function pg_select(PgSql\Connection $connection, string $table_name, array $cond #ifdef LIBPQ_HAS_PIPELINING function pg_enter_pipeline_mode(PgSql\Connection $connection): bool {} function pg_exit_pipeline_mode(PgSql\Connection $connection): bool {} + function pg_send_flush_request(PgSql\Connection $connection): bool {} function pg_pipeline_sync(PgSql\Connection $connection): bool {} function pg_pipeline_status(PgSql\Connection $connection): int {} #endif diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 6f035f19246cf..b34aa6f77fe15 100644 --- a/ext/pgsql/pgsql_arginfo.h +++ b/ext/pgsql/pgsql_arginfo.h @@ -462,6 +462,10 @@ ZEND_END_ARG_INFO() #define arginfo_pg_exit_pipeline_mode arginfo_pg_enter_pipeline_mode #endif +#if defined(LIBPQ_HAS_PIPELINING) +#define arginfo_pg_send_flush_request arginfo_pg_enter_pipeline_mode +#endif + #if defined(LIBPQ_HAS_PIPELINING) #define arginfo_pg_pipeline_sync arginfo_pg_enter_pipeline_mode #endif @@ -578,6 +582,9 @@ ZEND_FUNCTION(pg_enter_pipeline_mode); ZEND_FUNCTION(pg_exit_pipeline_mode); #endif #if defined(LIBPQ_HAS_PIPELINING) +ZEND_FUNCTION(pg_send_flush_request); +#endif +#if defined(LIBPQ_HAS_PIPELINING) ZEND_FUNCTION(pg_pipeline_sync); #endif #if defined(LIBPQ_HAS_PIPELINING) @@ -709,6 +716,9 @@ static const zend_function_entry ext_functions[] = { #if defined(LIBPQ_HAS_PIPELINING) ZEND_FE(pg_exit_pipeline_mode, arginfo_pg_exit_pipeline_mode) #endif +#if defined(LIBPQ_HAS_PIPELINING) + ZEND_FE(pg_send_flush_request, arginfo_pg_pipeline_sync) +#endif #if defined(LIBPQ_HAS_PIPELINING) ZEND_FE(pg_pipeline_sync, arginfo_pg_pipeline_sync) #endif diff --git a/ext/pgsql/tests/pg_pipeline_sync.phpt b/ext/pgsql/tests/pg_pipeline_sync.phpt index 38366a025483b..f72c8cd87940f 100644 --- a/ext/pgsql/tests/pg_pipeline_sync.phpt +++ b/ext/pgsql/tests/pg_pipeline_sync.phpt @@ -50,6 +50,26 @@ if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interv die('pg_send_query_params failed'); } +if (!pg_flush($db)) { + die('pg_flush failed'); +} + +for ($i = 2; $i < 50; ++$i) { + if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array($i))) { + die('pg_send_query_params failed'); + } +} + +if (!pg_send_flush_request($db)) { + die('pg_send_flush_request failed'); +} + +for ($i = 50; $i < 99; ++$i) { + if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array($i))) { + die('pg_send_query_params failed'); + } +} + if (!pg_pipeline_sync($db)) { die('pg_pipeline_sync failed'); } @@ -58,26 +78,37 @@ if (pg_pipeline_status($db) !== PGSQL_PIPELINE_ON) { die('pg_pipeline_status failed'); } -if (!($result = pg_get_result($db))) { - die('pg_get_result'); +if (!($stream = pg_socket($db))) { + die('pg_socket'); } -if (pg_result_status($result) !== PGSQL_TUPLES_OK) { - die('pg_result_status failed'); +if (pg_connection_busy($db)) { + $read = [$stream]; $write = $ex = []; + while (!stream_select($read, $write, $ex, null, null)) { } } -if (pg_num_rows($result) == -1) { - die('pg_num_rows failed'); -} +for ($i = 1; $i < 99; ++$i) { + if (!($result = pg_get_result($db))) { + die('pg_get_result'); + } -if (!pg_fetch_row($result, null)) { - die('pg_fetch_row failed'); -} + if (pg_result_status($result) !== PGSQL_TUPLES_OK) { + die('pg_result_status failed'); + } -pg_free_result($result); + if (pg_num_rows($result) == -1) { + die('pg_num_rows failed'); + } -if (pg_get_result($db) !== false) { - die('pg_get_result failed'); + if (!($row = pg_fetch_row($result, null))) { + die('pg_fetch_row failed'); + } + + pg_free_result($result); + + if (pg_get_result($db) !== false) { + die('pg_get_result failed'); + } } if (($result = pg_get_result($db)) !== false) { From 9402d490937457ec44e6a7a21de4089ba5a9cb19 Mon Sep 17 00:00:00 2001 From: ddv Date: Fri, 10 Nov 2023 14:26:54 +0500 Subject: [PATCH 2/8] Fix phpGH-9344: pgsql pipeline mode proposal. Fix pgsql_arginfo.h --- ext/pgsql/pgsql_arginfo.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index b34aa6f77fe15..53627f2f73ef6 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: 0d6ef9904082180bf9205d61c2f45c0752be8f7b */ + * Stub hash: 25badfac7b1d807202b80fd544e6db234fd726fd */ 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) @@ -717,7 +717,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(pg_exit_pipeline_mode, arginfo_pg_exit_pipeline_mode) #endif #if defined(LIBPQ_HAS_PIPELINING) - ZEND_FE(pg_send_flush_request, arginfo_pg_pipeline_sync) + ZEND_FE(pg_send_flush_request, arginfo_pg_send_flush_request) #endif #if defined(LIBPQ_HAS_PIPELINING) ZEND_FE(pg_pipeline_sync, arginfo_pg_pipeline_sync) From 070e80d5eddd63aa6c1c0da4869f67ee9a1942e8 Mon Sep 17 00:00:00 2001 From: ddv Date: Mon, 20 Nov 2023 11:30:17 +0500 Subject: [PATCH 3/8] Fix phpGH-9344: pgsql pipeline mode proposal. Fix freeze pg_cancel_query. In pipeline mode it should be possible to receive part of the results. (cherry picked from commit 72f3efe36d2824b4e3fb25b5ef4a58311d094aaf) --- ext/pgsql/pgsql.c | 36 +++++++++--- ext/pgsql/tests/pg_pipeline_sync.phpt | 81 +++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 165342edcc51c..df749c2b87455 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3519,6 +3519,9 @@ static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type) pgsql_link_handle *link; PGconn *pgsql; PGresult *pgsql_result; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { RETURN_THROWS(); @@ -3528,10 +3531,17 @@ static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type) CHECK_PGSQL_LINK(link); pgsql = link->conn; - if (PQsetnonblocking(pgsql, 1)) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (!is_pipeline_mode) { +#endif + if (PQsetnonblocking(pgsql, 1)) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif switch(entry_type) { case PHP_PG_ASYNC_IS_BUSY: PQconsumeInput(pgsql); @@ -3547,17 +3557,29 @@ static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type) if (rc < 0) { zend_error(E_WARNING, "cannot cancel the query: %s", err); } - while ((pgsql_result = PQgetResult(pgsql))) { - PQclear(pgsql_result); +#ifdef LIBPQ_HAS_PIPELINING + if (!is_pipeline_mode) { +#endif + while ((pgsql_result = PQgetResult(pgsql))) { + PQclear(pgsql_result); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif PQfreeCancel(c); break; } EMPTY_SWITCH_DEFAULT_CASE() } - if (PQsetnonblocking(pgsql, 0)) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode"); +#ifdef LIBPQ_HAS_PIPELINING + if (!is_pipeline_mode) { +#endif + if (PQsetnonblocking(pgsql, 0)) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif convert_to_boolean(return_value); } /* }}} */ diff --git a/ext/pgsql/tests/pg_pipeline_sync.phpt b/ext/pgsql/tests/pg_pipeline_sync.phpt index f72c8cd87940f..831ff88dfe1b6 100644 --- a/ext/pgsql/tests/pg_pipeline_sync.phpt +++ b/ext/pgsql/tests/pg_pipeline_sync.phpt @@ -117,6 +117,87 @@ if (($result = pg_get_result($db)) !== false) { } } +for ($i = 99; $i < 199; ++$i) { + if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time, pg_sleep(0.001)", array($i))) { + die('pg_send_query_params failed'); + } +} + +if (!pg_pipeline_sync($db)) { + die('pg_pipeline_sync failed'); +} + +usleep(10000); + +pg_cancel_query($db); + +if (pg_pipeline_status($db) !== PGSQL_PIPELINE_ON) { + die('pg_pipeline_status failed'); +} + +if (pg_connection_busy($db)) { + $read = [$stream]; $write = $ex = []; + while (!stream_select($read, $write, $ex, null, null)) { } +} + +$canceled_count = 0; +for ($i = 99; $i < 199; ++$i) { + if (!($result = pg_get_result($db))) { + die('pg_get_result'); + } + + $result_status = pg_result_status($result); + if ($result_status === PGSQL_FATAL_ERROR) { + if (pg_connection_status($db) !== PGSQL_CONNECTION_OK) { + die('pg_cancel_query failed'); + } + if (strpos(pg_last_error($db), 'canceling statement') === false) { + die('pg_cancel_query failed'); + } + pg_free_result($result); + if ($result = pg_get_result($db)) { + die('pg_get_result'); + } + continue; + } + if ($result_status === 11/*PGSQL_STATUS_PIPELINE_ABORTED*/) { + ++$canceled_count; + pg_free_result($result); + if ($result = pg_get_result($db)) { + die('pg_get_result'); + } + continue; + } + if ($result_status !== PGSQL_TUPLES_OK) { + die('pg_result_status failed'); + } + + if (pg_num_rows($result) == -1) { + die('pg_num_rows failed'); + } + + if (!($row = pg_fetch_row($result, null))) { + die('pg_fetch_row failed'); + } + + pg_free_result($result); + + if (pg_get_result($db) !== false) { + die('pg_get_result failed'); + } +} + +if ($canceled_count < 1) { + die('pg_cancel_query failed'); +} + +if (($result = pg_get_result($db)) !== false) { + if (pg_result_status($result) !== PGSQL_PIPELINE_SYNC) { + die('pg_result_status failed'); + } +} + + if (!pg_exit_pipeline_mode($db)) { die('pg_exit_pipeline_mode failed'); } From 5f7ed3280cd1bb8d8a06954653642dc81d60b7be Mon Sep 17 00:00:00 2001 From: ddv Date: Mon, 20 Nov 2023 14:37:43 +0500 Subject: [PATCH 4/8] Fix phpGH-9344: pgsql pipeline mode proposal. Prevent freeze pg_get_result when not sent sync message in pipelining mode. Fix freeze pg_close for connection in pipelining mode. Fix freeze on rollback transactions for persistent connection in pipelining mode. Fix freeze on shutdown. (cherry picked from commit 1a775ceac9673f6131c8a42b46d0b9d657636067) --- ext/pgsql/pgsql.c | 68 +++++++++++++++++++++++++-- ext/pgsql/php_pgsql.h | 3 ++ ext/pgsql/tests/pg_pipeline_sync.phpt | 48 ++++++++++++++++++- 3 files changed, 114 insertions(+), 5 deletions(-) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index df749c2b87455..2868e33b65ccc 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -174,6 +174,18 @@ static void pgsql_link_free(pgsql_link_handle *link) { PGresult *res; +#ifdef LIBPQ_HAS_PIPELINING + if (!link->synced) { + PGcancel *c; + char err[256]; + + c = PQgetCancel(link->conn); + PQcancel(c, err, sizeof(err)); + PQfreeCancel(c); + PQpipelineSync(link->conn); + } +#endif + while ((res = PQgetResult(link->conn))) { PQclear(res); } @@ -359,6 +371,20 @@ static int _rollback_transactions(zval *el) return -1; } + if (PQtransactionStatus(link) != PQTRANS_IDLE) { + PGcancel *c; + char err[256]; + + c = PQgetCancel(link); + PQcancel(c, err, sizeof(err)); + PQfreeCancel(c); +#ifdef LIBPQ_HAS_PIPELINING + if (PQpipelineSync(link)) { + PQexitPipelineMode(link); + } +#endif + } + while ((res = PQgetResult(link))) { PQclear(res); } @@ -626,6 +652,9 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) link->hash = zend_string_copy(str.s); link->notices = NULL; link->persistent = 1; +#ifdef LIBPQ_HAS_PIPELINING + link->synced = 1; +#endif } else { /* Non persistent connection */ zval *index_ptr; @@ -673,6 +702,9 @@ static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) link->hash = zend_string_copy(str.s); link->notices = NULL; link->persistent = 0; +#ifdef LIBPQ_HAS_PIPELINING + link->synced = 1; +#endif /* add it to the hash */ zend_hash_update(&PGG(connections), str.s, return_value); @@ -3564,6 +3596,8 @@ static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type) PQclear(pgsql_result); } #ifdef LIBPQ_HAS_PIPELINING + } else { + link->synced = 1; } #endif PQfreeCancel(c); @@ -3658,6 +3692,7 @@ PHP_FUNCTION(pg_send_query) } #ifdef LIBPQ_HAS_PIPELINING if (is_pipeline_mode) { + link->synced = 0; ret = 0; } else { #endif @@ -3783,6 +3818,7 @@ PHP_FUNCTION(pg_send_query_params) if (is_non_blocking) { #ifdef LIBPQ_HAS_PIPELINING if (is_pipeline_mode) { + link->synced = 0; ret = 0; } else { #endif @@ -3874,6 +3910,7 @@ PHP_FUNCTION(pg_send_prepare) if (is_non_blocking) { #ifdef LIBPQ_HAS_PIPELINING if (is_pipeline_mode) { + link->synced = 0; ret = 0; } else { #endif @@ -3992,6 +4029,7 @@ PHP_FUNCTION(pg_send_execute) if (is_non_blocking) { #ifdef LIBPQ_HAS_PIPELINING if (is_pipeline_mode) { + link->synced = 0; ret = 0; } else { #endif @@ -4039,6 +4077,12 @@ PHP_FUNCTION(pg_get_result) link = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(link); pgsql = link->conn; +#ifdef LIBPQ_HAS_PIPELINING + if (!link->synced) { + php_error_docref(NULL, E_NOTICE, + "The connection is in pipeline mode. A synchronization message must be sent before results can be received. Call pg_pipeline_sync()"); + } +#endif pgsql_result = PQgetResult(pgsql); if (!pgsql_result) { @@ -5985,6 +6029,7 @@ PHP_FUNCTION(pg_enter_pipeline_mode) { zval *pgsql_link; pgsql_link_handle *pgsql_handle; + int ret; if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { RETURN_THROWS(); @@ -5995,13 +6040,19 @@ PHP_FUNCTION(pg_enter_pipeline_mode) PQsetnonblocking(pgsql_handle->conn, 1); - RETURN_BOOL(PQenterPipelineMode(pgsql_handle->conn)); + ret = PQenterPipelineMode(pgsql_handle->conn); + if (ret) { + pgsql_handle->synced = 0; + } + + RETURN_BOOL(ret); } PHP_FUNCTION(pg_exit_pipeline_mode) { zval *pgsql_link; pgsql_link_handle *pgsql_handle; + int ret; if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { RETURN_THROWS(); @@ -6012,7 +6063,12 @@ PHP_FUNCTION(pg_exit_pipeline_mode) PQsetnonblocking(pgsql_handle->conn, 0); - RETURN_BOOL(PQexitPipelineMode(pgsql_handle->conn)); + ret = PQexitPipelineMode(pgsql_handle->conn); + if (ret) { + pgsql_handle->synced = 1; + } + + RETURN_BOOL(ret); } PHP_FUNCTION(pg_send_flush_request) @@ -6034,6 +6090,7 @@ PHP_FUNCTION(pg_pipeline_sync) { zval *pgsql_link; pgsql_link_handle *pgsql_handle; + int ret; if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { RETURN_THROWS(); @@ -6042,7 +6099,12 @@ PHP_FUNCTION(pg_pipeline_sync) pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(pgsql_handle); - RETURN_BOOL(PQpipelineSync(pgsql_handle->conn)); + ret = PQpipelineSync(pgsql_handle->conn); + if (ret) { + pgsql_handle->synced = 1; + } + + RETURN_BOOL(ret); } PHP_FUNCTION(pg_pipeline_status) diff --git a/ext/pgsql/php_pgsql.h b/ext/pgsql/php_pgsql.h index 5286ccec636b4..6fc2bc9b6dda5 100644 --- a/ext/pgsql/php_pgsql.h +++ b/ext/pgsql/php_pgsql.h @@ -148,6 +148,9 @@ typedef struct pgsql_link_handle { zend_string *hash; HashTable *notices; bool persistent; +#ifdef LIBPQ_HAS_PIPELINING + bool synced; +#endif zend_object std; } pgsql_link_handle; diff --git a/ext/pgsql/tests/pg_pipeline_sync.phpt b/ext/pgsql/tests/pg_pipeline_sync.phpt index 831ff88dfe1b6..db3fcf54bf133 100644 --- a/ext/pgsql/tests/pg_pipeline_sync.phpt +++ b/ext/pgsql/tests/pg_pipeline_sync.phpt @@ -197,15 +197,59 @@ if (($result = pg_get_result($db)) !== false) { } } - if (!pg_exit_pipeline_mode($db)) { die('pg_exit_pipeline_mode failed'); } -echo "OK"; +if (!pg_enter_pipeline_mode($db)) { + die('pg_exit_pipeline_mode failed'); +} + +if (!pg_send_query_params($db, "create table if not exists __test__pg_pipeline_sync__test__(f1 integer, f2 character varying)", array())) { + die('pg_send_query_params failed'); +} + +if (pg_exit_pipeline_mode($db)) { + die('pg_exit_pipeline_mode failed'); +} pg_close($db); +if (!$db = pg_connect($conn_str)) { + die("pg_connect() error"); +} + +if (!pg_send_query_params($db, "select * from __test__pg_pipeline_sync__test__", array())) { + die('pg_send_query_params failed'); +} + +if (!($stream = pg_socket($db))) { + die('pg_socket'); +} + +if (pg_connection_busy($db)) { + $read = [$stream]; $write = $ex = []; + while (!stream_select($read, $write, $ex, null, null)) { } +} + +if (!($result = pg_get_result($db))) { + die('pg_get_result'); +} + +if (pg_result_status($result) !== PGSQL_FATAL_ERROR) { + die('pg_result_status failed'); +} + +pg_free_result($result); + +if (pg_get_result($db) !== false) { + die('pg_get_result failed'); +} + +pg_close($db); + +echo "OK"; + ?> --EXPECT-- OK From e8b987d709812c4174066567132fdb772b08106a Mon Sep 17 00:00:00 2001 From: ddv Date: Mon, 20 Nov 2023 16:21:44 +0500 Subject: [PATCH 5/8] Fix phpGH-9344: pgsql pipeline mode proposal. Fix freeze pg_query/pg_query_params/pg_prepare/pg_execute --- ext/pgsql/pgsql.c | 104 ++++++++++++++++---------- ext/pgsql/tests/pg_pipeline_sync.phpt | 19 +++++ 2 files changed, 83 insertions(+), 40 deletions(-) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 2868e33b65ccc..f96e6ad745c25 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -1020,17 +1020,23 @@ PHP_FUNCTION(pg_query) pgsql = link->conn; - if (PQsetnonblocking(pgsql, 0)) { - php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode"); - RETURN_FALSE; - } - while ((pgsql_result = PQgetResult(pgsql))) { - PQclear(pgsql_result); - leftover = 1; - } - if (leftover) { - php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first"); +#ifdef LIBPQ_HAS_PIPELINING + if (PQpipelineStatus(pgsql) == PQ_PIPELINE_OFF) { +#endif + if (PQsetnonblocking(pgsql, 0)) { + php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode"); + RETURN_FALSE; + } + while ((pgsql_result = PQgetResult(pgsql))) { + PQclear(pgsql_result); + leftover = 1; + } + if (leftover) { + php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif pgsql_result = PQexec(pgsql, query); if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { PQclear(pgsql_result); @@ -1114,17 +1120,23 @@ PHP_FUNCTION(pg_query_params) pgsql = link->conn; - if (PQsetnonblocking(pgsql, 0)) { - php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode"); - RETURN_FALSE; - } - while ((pgsql_result = PQgetResult(pgsql))) { - PQclear(pgsql_result); - leftover = 1; - } - if (leftover) { - php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first"); +#ifdef LIBPQ_HAS_PIPELINING + if (PQpipelineStatus(pgsql) == PQ_PIPELINE_OFF) { +#endif + if (PQsetnonblocking(pgsql, 0)) { + php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode"); + RETURN_FALSE; + } + while ((pgsql_result = PQgetResult(pgsql))) { + PQclear(pgsql_result); + leftover = 1; + } + if (leftover) { + php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)); if (num_params > 0) { @@ -1219,17 +1231,23 @@ PHP_FUNCTION(pg_prepare) pgsql = link->conn; - if (PQsetnonblocking(pgsql, 0)) { - php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode"); - RETURN_FALSE; - } - while ((pgsql_result = PQgetResult(pgsql))) { - PQclear(pgsql_result); - leftover = 1; - } - if (leftover) { - php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first"); +#ifdef LIBPQ_HAS_PIPELINING + if (PQpipelineStatus(pgsql) == PQ_PIPELINE_OFF) { +#endif + if (PQsetnonblocking(pgsql, 0)) { + php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode"); + RETURN_FALSE; + } + while ((pgsql_result = PQgetResult(pgsql))) { + PQclear(pgsql_result); + leftover = 1; + } + if (leftover) { + php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL); if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { PQclear(pgsql_result); @@ -1300,17 +1318,23 @@ PHP_FUNCTION(pg_execute) pgsql = link->conn; - if (PQsetnonblocking(pgsql, 0)) { - php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode"); - RETURN_FALSE; - } - while ((pgsql_result = PQgetResult(pgsql))) { - PQclear(pgsql_result); - leftover = 1; - } - if (leftover) { - php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first"); +#ifdef LIBPQ_HAS_PIPELINING + if (PQpipelineStatus(pgsql) == PQ_PIPELINE_OFF) { +#endif + if (PQsetnonblocking(pgsql, 0)) { + php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode"); + RETURN_FALSE; + } + while ((pgsql_result = PQgetResult(pgsql))) { + PQclear(pgsql_result); + leftover = 1; + } + if (leftover) { + php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)); if (num_params > 0) { diff --git a/ext/pgsql/tests/pg_pipeline_sync.phpt b/ext/pgsql/tests/pg_pipeline_sync.phpt index db3fcf54bf133..ec54ab4b03cfd 100644 --- a/ext/pgsql/tests/pg_pipeline_sync.phpt +++ b/ext/pgsql/tests/pg_pipeline_sync.phpt @@ -248,6 +248,25 @@ if (pg_get_result($db) !== false) { pg_close($db); +if (!$db = pg_connect($conn_str)) { + die("pg_connect() error"); +} + +if (!pg_enter_pipeline_mode($db)) { + die('pg_enter_pipeline_mode'); +} + +if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array(199))) { + die('pg_send_query_params failed'); +} + +@pg_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array(200)); +if (strpos(pg_last_error($db), 'synchronous command execution functions are not allowed in pipeline mode') === false) { + die('pg_query_params failed'); +} + +pg_close($db); + echo "OK"; ?> From 61c121970501318ae84183c570f1b7782887b94b Mon Sep 17 00:00:00 2001 From: ddv Date: Tue, 23 Jan 2024 16:03:51 +0500 Subject: [PATCH 6/8] Revert "Revert incomplete PG pipeline addition" This reverts commit 1e66e6ae --- UPGRADING | 13 ++ ext/pgsql/pgsql.c | 233 ++++++++++++++++++++++---- ext/pgsql/pgsql.stub.php | 31 ++++ ext/pgsql/pgsql_arginfo.h | 68 +++++++- ext/pgsql/tests/pg_pipeline_sync.phpt | 130 ++++++++++++++ 5 files changed, 438 insertions(+), 37 deletions(-) create mode 100644 ext/pgsql/tests/pg_pipeline_sync.phpt diff --git a/UPGRADING b/UPGRADING index f94e6a3cc67d2..334514649d78e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -357,6 +357,13 @@ PDO_SQLITE: . Added mb_trim, mb_ltrim and mb_rtrim functions. RFC: https://wiki.php.net/rfc/mb_trim +- PGSQL: + . Added pg_enter_pipeline_mode(). + . Added pg_exit_pipeline_mode(). + . Added pg_send_flush_request(). + . Added pg_pipeline_sync(). + . Added pg_pipeline_status(). + - Opcache: . If JIT is enabled, PHP will now exit with a fatal error on startup in case of JIT startup initialization issues. @@ -428,6 +435,12 @@ PDO_SQLITE: . PHP_ROUND_TOWARD_ZERO. . PHP_ROUND_AWAY_FROM_ZERO. +- PGSQL: + . PGSQL_PIPELINE_SYNC + . PGSQL_PIPELINE_ON + . PGSQL_PIPELINE_OFF + . PGSQL_PIPELINE_ABORTED + - Sockets: . SO_EXCLUSIVEADDRUSE (Windows only). diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index c393f3c4a1801..aa864549422d6 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3616,6 +3616,9 @@ PHP_FUNCTION(pg_send_query) char *query; size_t len; PGconn *pgsql; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif int is_non_blocking; int ret; @@ -3627,23 +3630,40 @@ PHP_FUNCTION(pg_send_query) CHECK_PGSQL_LINK(link); pgsql = link->conn; - is_non_blocking = PQisnonblocking(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (is_pipeline_mode) { + is_non_blocking = 1; + } else { +#endif + is_non_blocking = PQisnonblocking(pgsql); - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } - if (_php_pgsql_link_has_results(pgsql)) { - php_error_docref(NULL, E_NOTICE, - "There are results on this connection. Call pg_get_result() until it returns FALSE"); + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif if (is_non_blocking) { if (!PQsendQuery(pgsql, query)) { RETURN_FALSE; } - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { if (!PQsendQuery(pgsql, query)) { if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { @@ -3688,6 +3708,9 @@ PHP_FUNCTION(pg_send_query_params) char *query; size_t query_len; PGconn *pgsql; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif int is_non_blocking; int ret; @@ -3699,17 +3722,26 @@ PHP_FUNCTION(pg_send_query_params) CHECK_PGSQL_LINK(link); pgsql = link->conn; - is_non_blocking = PQisnonblocking(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (is_pipeline_mode) { + is_non_blocking = 1; + } else { +#endif + is_non_blocking = PQisnonblocking(pgsql); - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } - if (_php_pgsql_link_has_results(pgsql)) { - php_error_docref(NULL, E_NOTICE, - "There are results on this connection. Call pg_get_result() until it returns FALSE"); + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)); if (num_params > 0) { @@ -3748,7 +3780,15 @@ PHP_FUNCTION(pg_send_query_params) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -3782,6 +3822,9 @@ PHP_FUNCTION(pg_send_prepare) char *query, *stmtname; size_t stmtname_len, query_len; PGconn *pgsql; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif int is_non_blocking; int ret; @@ -3793,17 +3836,26 @@ PHP_FUNCTION(pg_send_prepare) CHECK_PGSQL_LINK(link); pgsql = link->conn; - is_non_blocking = PQisnonblocking(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (is_pipeline_mode) { + is_non_blocking = 1; + } else { +#endif + is_non_blocking = PQisnonblocking(pgsql); - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } - if (_php_pgsql_link_has_results(pgsql)) { - php_error_docref(NULL, E_NOTICE, - "There are results on this connection. Call pg_get_result() until it returns FALSE"); + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) { if (is_non_blocking) { @@ -3819,7 +3871,15 @@ PHP_FUNCTION(pg_send_prepare) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -3855,6 +3915,9 @@ PHP_FUNCTION(pg_send_execute) char *stmtname; size_t stmtname_len; PGconn *pgsql; +#ifdef LIBPQ_HAS_PIPELINING + bool is_pipeline_mode; +#endif int is_non_blocking; int ret; @@ -3866,17 +3929,26 @@ PHP_FUNCTION(pg_send_execute) CHECK_PGSQL_LINK(link); pgsql = link->conn; - is_non_blocking = PQisnonblocking(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + is_pipeline_mode = (PQpipelineStatus(pgsql) == PQ_PIPELINE_ON); + if (is_pipeline_mode) { + is_non_blocking = 1; + } else { +#endif + is_non_blocking = PQisnonblocking(pgsql); - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } + if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { + php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode"); + RETURN_FALSE; + } - if (_php_pgsql_link_has_results(pgsql)) { - php_error_docref(NULL, E_NOTICE, - "There are results on this connection. Call pg_get_result() until it returns FALSE"); + if (_php_pgsql_link_has_results(pgsql)) { + php_error_docref(NULL, E_NOTICE, + "There are results on this connection. Call pg_get_result() until it returns FALSE"); + } +#ifdef LIBPQ_HAS_PIPELINING } +#endif num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)); if (num_params > 0) { @@ -3917,7 +3989,15 @@ PHP_FUNCTION(pg_send_execute) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -5907,4 +5987,85 @@ PHP_FUNCTION(pg_select) } /* }}} */ +#ifdef LIBPQ_HAS_PIPELINING +PHP_FUNCTION(pg_enter_pipeline_mode) +{ + zval *pgsql_link; + pgsql_link_handle *pgsql_handle; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { + RETURN_THROWS(); + } + + pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(pgsql_handle); + + PQsetnonblocking(pgsql_handle->conn, 1); + + RETURN_BOOL(PQenterPipelineMode(pgsql_handle->conn)); +} + +PHP_FUNCTION(pg_exit_pipeline_mode) +{ + zval *pgsql_link; + pgsql_link_handle *pgsql_handle; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { + RETURN_THROWS(); + } + + pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(pgsql_handle); + + PQsetnonblocking(pgsql_handle->conn, 0); + + RETURN_BOOL(PQexitPipelineMode(pgsql_handle->conn)); +} + +PHP_FUNCTION(pg_send_flush_request) +{ + zval *pgsql_link; + pgsql_link_handle *pgsql_handle; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { + RETURN_THROWS(); + } + + pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(pgsql_handle); + + RETURN_BOOL(PQsendFlushRequest(pgsql_handle->conn)); +} + +PHP_FUNCTION(pg_pipeline_sync) +{ + zval *pgsql_link; + pgsql_link_handle *pgsql_handle; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { + RETURN_THROWS(); + } + + pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(pgsql_handle); + + RETURN_BOOL(PQpipelineSync(pgsql_handle->conn)); +} + +PHP_FUNCTION(pg_pipeline_status) +{ + zval *pgsql_link; + pgsql_link_handle *pgsql_handle; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) { + RETURN_THROWS(); + } + + pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(pgsql_handle); + + RETURN_LONG(PQpipelineStatus(pgsql_handle->conn)); +} +#endif + #endif diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index b706959d89437..83ba55323e286 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -440,6 +440,29 @@ const PGSQL_TRACE_REGRESS_MODE = UNKNOWN; #endif +#ifdef LIBPQ_HAS_PIPELINING + /** + * @var int + * @cvalue PGRES_PIPELINE_SYNC + */ + const PGSQL_PIPELINE_SYNC = UNKNOWN; + /** + * @var int + * @cvalue PQ_PIPELINE_ON + */ + const PGSQL_PIPELINE_ON = UNKNOWN; + /** + * @var int + * @cvalue PQ_PIPELINE_OFF + */ + const PGSQL_PIPELINE_OFF = UNKNOWN; + /** + * @var int + * @cvalue PQ_PIPELINE_ABORTED + */ + const PGSQL_PIPELINE_ABORTED = UNKNOWN; +#endif + #ifdef HAVE_PG_CONTEXT_VISIBILITY /* For pg_set_error_context_visibility() */ @@ -940,6 +963,14 @@ function pg_delete(PgSql\Connection $connection, string $table_name, array $cond */ function pg_select(PgSql\Connection $connection, string $table_name, array $conditions = [], int $flags = PGSQL_DML_EXEC, int $mode = PGSQL_ASSOC): array|string|false {} +#ifdef LIBPQ_HAS_PIPELINING + function pg_enter_pipeline_mode(PgSql\Connection $connection): bool {} + function pg_exit_pipeline_mode(PgSql\Connection $connection): bool {} + function pg_send_flush_request(PgSql\Connection $connection): bool {} + function pg_pipeline_sync(PgSql\Connection $connection): bool {} + function pg_pipeline_status(PgSql\Connection $connection): int {} +#endif + #ifdef HAVE_PG_CONTEXT_VISIBILITY function pg_set_error_context_visibility(PgSql\Connection $connection, int $visibility): int {} #endif diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 8e1fa79466732..364dd9b1df5f2 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: a37b9df0c3b172d1160b1a7ef953cbd5a0a811b6 */ + * Stub hash: 25badfac7b1d807202b80fd544e6db234fd726fd */ 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) @@ -452,6 +452,30 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_pg_select, 0, 2, MAY_BE_ARRAY|MA ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "PGSQL_ASSOC") ZEND_END_ARG_INFO() +#if defined(LIBPQ_HAS_PIPELINING) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_enter_pipeline_mode, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, connection, PgSql\\Connection, 0) +ZEND_END_ARG_INFO() +#endif + +#if defined(LIBPQ_HAS_PIPELINING) +#define arginfo_pg_exit_pipeline_mode arginfo_pg_enter_pipeline_mode +#endif + +#if defined(LIBPQ_HAS_PIPELINING) +#define arginfo_pg_send_flush_request arginfo_pg_enter_pipeline_mode +#endif + +#if defined(LIBPQ_HAS_PIPELINING) +#define arginfo_pg_pipeline_sync arginfo_pg_enter_pipeline_mode +#endif + +#if defined(LIBPQ_HAS_PIPELINING) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_pipeline_status, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, connection, PgSql\\Connection, 0) +ZEND_END_ARG_INFO() +#endif + #if defined(HAVE_PG_CONTEXT_VISIBILITY) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_set_error_context_visibility, 0, 2, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, connection, PgSql\\Connection, 0) @@ -551,6 +575,21 @@ ZEND_FUNCTION(pg_insert); ZEND_FUNCTION(pg_update); ZEND_FUNCTION(pg_delete); ZEND_FUNCTION(pg_select); +#if defined(LIBPQ_HAS_PIPELINING) +ZEND_FUNCTION(pg_enter_pipeline_mode); +#endif +#if defined(LIBPQ_HAS_PIPELINING) +ZEND_FUNCTION(pg_exit_pipeline_mode); +#endif +#if defined(LIBPQ_HAS_PIPELINING) +ZEND_FUNCTION(pg_send_flush_request); +#endif +#if defined(LIBPQ_HAS_PIPELINING) +ZEND_FUNCTION(pg_pipeline_sync); +#endif +#if defined(LIBPQ_HAS_PIPELINING) +ZEND_FUNCTION(pg_pipeline_status); +#endif #if defined(HAVE_PG_CONTEXT_VISIBILITY) ZEND_FUNCTION(pg_set_error_context_visibility); #endif @@ -671,6 +710,21 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(pg_update, arginfo_pg_update) ZEND_FE(pg_delete, arginfo_pg_delete) ZEND_FE(pg_select, arginfo_pg_select) +#if defined(LIBPQ_HAS_PIPELINING) + ZEND_FE(pg_enter_pipeline_mode, arginfo_pg_enter_pipeline_mode) +#endif +#if defined(LIBPQ_HAS_PIPELINING) + ZEND_FE(pg_exit_pipeline_mode, arginfo_pg_exit_pipeline_mode) +#endif +#if defined(LIBPQ_HAS_PIPELINING) + ZEND_FE(pg_send_flush_request, arginfo_pg_send_flush_request) +#endif +#if defined(LIBPQ_HAS_PIPELINING) + ZEND_FE(pg_pipeline_sync, arginfo_pg_pipeline_sync) +#endif +#if defined(LIBPQ_HAS_PIPELINING) + ZEND_FE(pg_pipeline_status, arginfo_pg_pipeline_status) +#endif #if defined(HAVE_PG_CONTEXT_VISIBILITY) ZEND_FE(pg_set_error_context_visibility, arginfo_pg_set_error_context_visibility) #endif @@ -794,6 +848,18 @@ static void register_pgsql_symbols(int module_number) #if defined(PQTRACE_REGRESS_MODE) REGISTER_LONG_CONSTANT("PGSQL_TRACE_REGRESS_MODE", PQTRACE_REGRESS_MODE, CONST_PERSISTENT); #endif +#if defined(LIBPQ_HAS_PIPELINING) + REGISTER_LONG_CONSTANT("PGSQL_PIPELINE_SYNC", PGRES_PIPELINE_SYNC, CONST_PERSISTENT); +#endif +#if defined(LIBPQ_HAS_PIPELINING) + REGISTER_LONG_CONSTANT("PGSQL_PIPELINE_ON", PQ_PIPELINE_ON, CONST_PERSISTENT); +#endif +#if defined(LIBPQ_HAS_PIPELINING) + REGISTER_LONG_CONSTANT("PGSQL_PIPELINE_OFF", PQ_PIPELINE_OFF, CONST_PERSISTENT); +#endif +#if defined(LIBPQ_HAS_PIPELINING) + REGISTER_LONG_CONSTANT("PGSQL_PIPELINE_ABORTED", PQ_PIPELINE_ABORTED, CONST_PERSISTENT); +#endif #if defined(HAVE_PG_CONTEXT_VISIBILITY) REGISTER_LONG_CONSTANT("PGSQL_SHOW_CONTEXT_NEVER", PQSHOW_CONTEXT_NEVER, CONST_PERSISTENT); #endif diff --git a/ext/pgsql/tests/pg_pipeline_sync.phpt b/ext/pgsql/tests/pg_pipeline_sync.phpt new file mode 100644 index 0000000000000..f72c8cd87940f --- /dev/null +++ b/ext/pgsql/tests/pg_pipeline_sync.phpt @@ -0,0 +1,130 @@ +--TEST-- +PostgreSQL pipeline mode +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + +--EXPECT-- +OK From 287c3dd89b7cd0adde5fae59046d486b14db51aa Mon Sep 17 00:00:00 2001 From: ddv Date: Tue, 23 Jan 2024 16:43:05 +0500 Subject: [PATCH 7/8] Fix phpGH-9344: pgsql pipeline mode proposal. fix stub. fix phpt include. --- ext/pgsql/pgsql_arginfo.h | 2 +- ext/pgsql/tests/pg_pipeline_sync.phpt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 364dd9b1df5f2..f7d7cc727b193 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: 25badfac7b1d807202b80fd544e6db234fd726fd */ + * Stub hash: b52f6b94faabab12457bf0d8ec035295aaacbfba */ 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) diff --git a/ext/pgsql/tests/pg_pipeline_sync.phpt b/ext/pgsql/tests/pg_pipeline_sync.phpt index ec54ab4b03cfd..e8b2281cd614a 100644 --- a/ext/pgsql/tests/pg_pipeline_sync.phpt +++ b/ext/pgsql/tests/pg_pipeline_sync.phpt @@ -4,7 +4,7 @@ PostgreSQL pipeline mode pgsql --SKIPIF-- Date: Thu, 25 Jan 2024 10:14:04 +0500 Subject: [PATCH 8/8] Fix phpGH-9344: pgsql pipeline mode proposal. show in test detail PostgreSQL error. --- ext/pgsql/tests/pg_pipeline_sync.phpt | 106 ++++++++++++++------------ 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/ext/pgsql/tests/pg_pipeline_sync.phpt b/ext/pgsql/tests/pg_pipeline_sync.phpt index e8b2281cd614a..d7154f912b9a8 100644 --- a/ext/pgsql/tests/pg_pipeline_sync.phpt +++ b/ext/pgsql/tests/pg_pipeline_sync.phpt @@ -15,14 +15,24 @@ if (!defined('PGSQL_PIPELINE_SYNC') || !function_exists('pg_send_query_params')) include('inc/config.inc'); include('inc/nonblocking.inc'); +$db = false; + +function failure($method) { + $backtrace = debug_backtrace(); + $line = $backtrace[0]['line'] + 12; + global $db; + $error = $db ? pg_last_error($db) : 'unknown error'; + die("{$method} failed on line {$line}: {$error}"); +} + if (!$db = pg_connect($conn_str, PGSQL_CONNECT_ASYNC)) { - die("pg_connect() error"); + failure('pg_connect'); } elseif (pg_connection_status($db) === PGSQL_CONNECTION_BAD) { - die("pg_connect() error"); + failure('pg_connect'); } elseif ($db_socket = pg_socket($db)) { stream_set_blocking($db_socket, FALSE); } else { - die("pg_socket() error"); + failure('pg_socket'); } while (true) { @@ -34,52 +44,52 @@ while (true) { nb_is_writable($db_socket); break; case PGSQL_POLLING_FAILED: - die("async connection failed"); + failure('async connection failed'); case PGSQL_POLLING_OK: break 2; default: - die("unknown poll status"); + failure("unknown poll status {$status}"); } } if (!pg_enter_pipeline_mode($db)) { - die('pg_enter_pipeline_mode{}'); + failure('pg_enter_pipeline_mode'); } if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array(1))) { - die('pg_send_query_params failed'); + failure('pg_send_query_params'); } if (!pg_flush($db)) { - die('pg_flush failed'); + failure('pg_flush'); } for ($i = 2; $i < 50; ++$i) { if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array($i))) { - die('pg_send_query_params failed'); + failure('pg_send_query_params'); } } if (!pg_send_flush_request($db)) { - die('pg_send_flush_request failed'); + failure('pg_send_flush_request'); } for ($i = 50; $i < 99; ++$i) { if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array($i))) { - die('pg_send_query_params failed'); + failure('pg_send_query_params'); } } if (!pg_pipeline_sync($db)) { - die('pg_pipeline_sync failed'); + failure('pg_pipeline_sync'); } if (pg_pipeline_status($db) !== PGSQL_PIPELINE_ON) { - die('pg_pipeline_status failed'); + failure('pg_pipeline_status'); } if (!($stream = pg_socket($db))) { - die('pg_socket'); + failure('pg_socket'); } if (pg_connection_busy($db)) { @@ -89,42 +99,42 @@ if (pg_connection_busy($db)) { for ($i = 1; $i < 99; ++$i) { if (!($result = pg_get_result($db))) { - die('pg_get_result'); + failure('pg_get_result'); } if (pg_result_status($result) !== PGSQL_TUPLES_OK) { - die('pg_result_status failed'); + failure('pg_result_status'); } if (pg_num_rows($result) == -1) { - die('pg_num_rows failed'); + failure('pg_num_rows'); } if (!($row = pg_fetch_row($result, null))) { - die('pg_fetch_row failed'); + failure('pg_fetch_row'); } pg_free_result($result); if (pg_get_result($db) !== false) { - die('pg_get_result failed'); + failure('pg_get_result'); } } if (($result = pg_get_result($db)) !== false) { if (pg_result_status($result) !== PGSQL_PIPELINE_SYNC) { - die('pg_result_status failed'); + failure('pg_result_status'); } } for ($i = 99; $i < 199; ++$i) { if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time, pg_sleep(0.001)", array($i))) { - die('pg_send_query_params failed'); + failure('pg_send_query_params'); } } if (!pg_pipeline_sync($db)) { - die('pg_pipeline_sync failed'); + failure('pg_pipeline_sync'); } usleep(10000); @@ -132,7 +142,7 @@ usleep(10000); pg_cancel_query($db); if (pg_pipeline_status($db) !== PGSQL_PIPELINE_ON) { - die('pg_pipeline_status failed'); + failure('pg_pipeline_status'); } if (pg_connection_busy($db)) { @@ -143,20 +153,20 @@ if (pg_connection_busy($db)) { $canceled_count = 0; for ($i = 99; $i < 199; ++$i) { if (!($result = pg_get_result($db))) { - die('pg_get_result'); + failure('pg_get_result'); } $result_status = pg_result_status($result); if ($result_status === PGSQL_FATAL_ERROR) { if (pg_connection_status($db) !== PGSQL_CONNECTION_OK) { - die('pg_cancel_query failed'); + failure('pg_cancel_query'); } if (strpos(pg_last_error($db), 'canceling statement') === false) { - die('pg_cancel_query failed'); + failure('pg_cancel_query'); } pg_free_result($result); if ($result = pg_get_result($db)) { - die('pg_get_result'); + failure('pg_get_result'); } continue; } @@ -164,67 +174,67 @@ for ($i = 99; $i < 199; ++$i) { ++$canceled_count; pg_free_result($result); if ($result = pg_get_result($db)) { - die('pg_get_result'); + failure('pg_get_result'); } continue; } if ($result_status !== PGSQL_TUPLES_OK) { - die('pg_result_status failed'); + failure('pg_result_status'); } if (pg_num_rows($result) == -1) { - die('pg_num_rows failed'); + failure('pg_num_rows'); } if (!($row = pg_fetch_row($result, null))) { - die('pg_fetch_row failed'); + failure('pg_fetch_row'); } pg_free_result($result); if (pg_get_result($db) !== false) { - die('pg_get_result failed'); + failure('pg_get_result'); } } if ($canceled_count < 1) { - die('pg_cancel_query failed'); + failure('pg_cancel_query'); } if (($result = pg_get_result($db)) !== false) { if (pg_result_status($result) !== PGSQL_PIPELINE_SYNC) { - die('pg_result_status failed'); + failure('pg_result_status'); } } if (!pg_exit_pipeline_mode($db)) { - die('pg_exit_pipeline_mode failed'); + failure('pg_exit_pipeline_mode'); } if (!pg_enter_pipeline_mode($db)) { - die('pg_exit_pipeline_mode failed'); + failure('pg_exit_pipeline_mode'); } if (!pg_send_query_params($db, "create table if not exists __test__pg_pipeline_sync__test__(f1 integer, f2 character varying)", array())) { - die('pg_send_query_params failed'); + failure('pg_send_query_params'); } if (pg_exit_pipeline_mode($db)) { - die('pg_exit_pipeline_mode failed'); + failure('pg_exit_pipeline_mode'); } pg_close($db); if (!$db = pg_connect($conn_str)) { - die("pg_connect() error"); + failure('pg_connect'); } if (!pg_send_query_params($db, "select * from __test__pg_pipeline_sync__test__", array())) { - die('pg_send_query_params failed'); + failure('pg_send_query_params'); } if (!($stream = pg_socket($db))) { - die('pg_socket'); + failure('pg_socket'); } if (pg_connection_busy($db)) { @@ -233,36 +243,36 @@ if (pg_connection_busy($db)) { } if (!($result = pg_get_result($db))) { - die('pg_get_result'); + failure('pg_get_result'); } if (pg_result_status($result) !== PGSQL_FATAL_ERROR) { - die('pg_result_status failed'); + failure('pg_result_status'); } pg_free_result($result); if (pg_get_result($db) !== false) { - die('pg_get_result failed'); + failure('pg_get_result'); } pg_close($db); if (!$db = pg_connect($conn_str)) { - die("pg_connect() error"); + failure('pg_connect'); } if (!pg_enter_pipeline_mode($db)) { - die('pg_enter_pipeline_mode'); + failure('pg_enter_pipeline_mode'); } if (!pg_send_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array(199))) { - die('pg_send_query_params failed'); + failure('pg_send_query_params'); } @pg_query_params($db, "select $1 as index, now() + ($1||' day')::interval as time", array(200)); if (strpos(pg_last_error($db), 'synchronous command execution functions are not allowed in pipeline mode') === false) { - die('pg_query_params failed'); + failure('pg_query_params'); } pg_close($db);