diff --git a/NEWS b/NEWS index eaf78f515ee..42168212aa5 100644 --- a/NEWS +++ b/NEWS @@ -195,6 +195,8 @@ PHP NEWS . Added pg_result_memory_size to get the query result memory usage. (KentarouTakeda) . Added pg_change_password to alter an user's password. (David Carlier) + . Added pg_put_copy_data/pg_put_copy_end to send COPY commands and signal + the end of the COPY. (David Carlier) - Phar: . Fixed bug GH-12532 (PharData created from zip has incorrect timestamp). diff --git a/UPGRADING b/UPGRADING index 58f46d9d2ee..3cee5b9bc4b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -541,6 +541,8 @@ PHP 8.4 UPGRADE NOTES - PGSQL: . Added pg_change_password to alter a given user's password. It handles transparently the password encryption from the database settings. + . Added pg_put_copy_data to send COPY commands and pg_put_copy_end to send + end-of-data to the server. - Sodium: . Added the sodium_crypto_aead_aegis128l_*() and sodium_crypto_aead_aegis256l_*() diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index e363f705723..bf5b2fd8338 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -6123,3 +6123,45 @@ PHP_FUNCTION(pg_change_password) } #endif + +PHP_FUNCTION(pg_put_copy_data) +{ + zval *pgsql_link; + pgsql_link_handle *link; + zend_string *cmd; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pgsql_link, pgsql_link_ce) + Z_PARAM_STR(cmd) + ZEND_PARSE_PARAMETERS_END(); + + link = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(link); + + // PQputCopyData accepts empty buffers as well + + RETURN_LONG((zend_long)PQputCopyData(link->conn, ZSTR_VAL(cmd), ZSTR_LEN(cmd))); +} + +PHP_FUNCTION(pg_put_copy_end) +{ + zval *pgsql_link; + pgsql_link_handle *link; + zend_string *error; + char *err = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(pgsql_link, pgsql_link_ce) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(error) + ZEND_PARSE_PARAMETERS_END(); + + link = Z_PGSQL_LINK_P(pgsql_link); + CHECK_PGSQL_LINK(link); + + if (error != NULL && ZSTR_LEN(error) != 0) { + err = ZSTR_VAL(error); + } + + RETURN_LONG((zend_long)PQputCopyEnd(link->conn, err)); +} diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index 2afcf7fbc23..0bdcb7d34b4 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -949,6 +949,9 @@ namespace { #endif function pg_change_password(PgSql\Connection $connection, string $user, #[\SensitiveParameter] string $password): bool {} + + function pg_put_copy_data(PgSql\Connection $connection, string $cmd): int {} + function pg_put_copy_end(PgSql\Connection $connection, string $error = null): int {} } namespace PgSql { diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 04ac324cb90..bf85e752de0 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: c5cb23b6536c1908d3dcc804f5fa176323f4db07 */ + * Stub hash: b3645285a405c83fec3ae55195ed577cec5f5356 */ 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) @@ -471,6 +471,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_change_password, 0, 3, _IS_BO ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_put_copy_data, 0, 2, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, connection, PgSql\\Connection, 0) + ZEND_ARG_TYPE_INFO(0, cmd, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_put_copy_end, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, connection, PgSql\\Connection, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, error, IS_STRING, 0, "null") +ZEND_END_ARG_INFO() + ZEND_FUNCTION(pg_connect); ZEND_FUNCTION(pg_pconnect); ZEND_FUNCTION(pg_connect_poll); @@ -569,6 +579,8 @@ ZEND_FUNCTION(pg_set_error_context_visibility); ZEND_FUNCTION(pg_result_memory_size); #endif ZEND_FUNCTION(pg_change_password); +ZEND_FUNCTION(pg_put_copy_data); +ZEND_FUNCTION(pg_put_copy_end); static const zend_function_entry ext_functions[] = { ZEND_FE(pg_connect, arginfo_pg_connect) @@ -692,6 +704,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(pg_result_memory_size, arginfo_pg_result_memory_size) #endif ZEND_FE(pg_change_password, arginfo_pg_change_password) + ZEND_FE(pg_put_copy_data, arginfo_pg_put_copy_data) + ZEND_FE(pg_put_copy_end, arginfo_pg_put_copy_end) ZEND_FE_END }; diff --git a/ext/pgsql/tests/pg_put_copy.phpt b/ext/pgsql/tests/pg_put_copy.phpt new file mode 100644 index 00000000000..ef259e4ec9d --- /dev/null +++ b/ext/pgsql/tests/pg_put_copy.phpt @@ -0,0 +1,89 @@ +--TEST-- +PostgreSQL pg_put_copy_data/pg_end_copy_end +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +int(1) +int(1) +int(1) +int(1) +object(PgSql\Result)#2 (0) { +} +array(2) { + [0]=> + array(2) { + ["id"]=> + string(1) "0" + ["val"]=> + string(6) "FooBar" + } + [1]=> + array(2) { + ["id"]=> + string(1) "1" + ["val"]=> + string(6) "BarFoo" + } +} +int(-1) +int(-1) +string(39) "no COPY in progress +no COPY in progress" +int(1) +int(1) +object(PgSql\Result)#2 (0) { +} +array(2) { + [0]=> + array(2) { + ["id"]=> + string(1) "0" + ["val"]=> + string(6) "FooBar" + } + [1]=> + array(2) { + ["id"]=> + string(1) "1" + ["val"]=> + string(6) "BarFoo" + } +}