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..3500d50a96cb6 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); } @@ -365,6 +377,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); } @@ -632,6 +658,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; @@ -679,6 +708,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); @@ -992,17 +1024,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); @@ -1086,17 +1124,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) { @@ -1191,17 +1235,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); @@ -1272,17 +1322,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) { @@ -3540,6 +3596,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(); @@ -3549,10 +3608,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); @@ -3568,17 +3634,31 @@ 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 + } else { + link->synced = 1; } +#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); } /* }}} */ @@ -3616,6 +3696,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 +3710,41 @@ 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) { + link->synced = 0; + 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 +3789,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 +3803,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 +3861,16 @@ PHP_FUNCTION(pg_send_query_params) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + link->synced = 0; + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -3782,6 +3904,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 +3918,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 +3953,16 @@ PHP_FUNCTION(pg_send_prepare) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + link->synced = 0; + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -3855,6 +3998,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 +4012,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 +4072,16 @@ PHP_FUNCTION(pg_send_execute) } if (is_non_blocking) { - ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + if (is_pipeline_mode) { + link->synced = 0; + ret = 0; + } else { +#endif + ret = PQflush(pgsql); +#ifdef LIBPQ_HAS_PIPELINING + } +#endif } else { /* Wait to finish sending buffer */ while ((ret = PQflush(pgsql))) { @@ -3958,6 +4122,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) { @@ -5907,4 +6077,103 @@ PHP_FUNCTION(pg_select) } /* }}} */ +#ifdef LIBPQ_HAS_PIPELINING +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(); + } + + pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(pgsql_handle); + + PQsetnonblocking(pgsql_handle->conn, 1); + + 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(); + } + + pgsql_handle = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(pgsql_handle); + + PQsetnonblocking(pgsql_handle->conn, 0); + + ret = PQexitPipelineMode(pgsql_handle->conn); + if (ret) { + pgsql_handle->synced = 1; + } + + RETURN_BOOL(ret); +} + +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; + int ret; + + 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); + + ret = PQpipelineSync(pgsql_handle->conn); + if (ret) { + pgsql_handle->synced = 1; + } + + RETURN_BOOL(ret); +} + +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..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: a37b9df0c3b172d1160b1a7ef953cbd5a0a811b6 */ + * 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) @@ -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/php_pgsql.h b/ext/pgsql/php_pgsql.h index b9a5fec11d541..fd400c66c1824 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 new file mode 100644 index 0000000000000..d7154f912b9a8 --- /dev/null +++ b/ext/pgsql/tests/pg_pipeline_sync.phpt @@ -0,0 +1,284 @@ +--TEST-- +PostgreSQL pipeline mode +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + +--EXPECT-- +OK