Skip to content

Fix GH-7996: Adding new parameter to pg_copy_from/pg_copy_to #14441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions ext/pgsql/pgsql.c
Original file line number Diff line number Diff line change
Expand Up @@ -3211,21 +3211,23 @@ PHP_FUNCTION(pg_copy_to)
pgsql_link_handle *link;
zend_string *table_name;
zend_string *pg_delimiter = NULL;
zend_string *pg_escape = NULL;
char *pg_null_as = NULL;
size_t pg_null_as_len = 0;
bool free_pg_null = false;
char *query;
PGconn *pgsql;
PGresult *pgsql_result;
ExecStatusType status;
char *csv = (char *)NULL;
char *csv = NULL;

ZEND_PARSE_PARAMETERS_START(2, 4)
ZEND_PARSE_PARAMETERS_START(2, 5)
Z_PARAM_OBJECT_OF_CLASS(pgsql_link, pgsql_link_ce)
Z_PARAM_PATH_STR(table_name)
Z_PARAM_OPTIONAL
Z_PARAM_STR(pg_delimiter)
Z_PARAM_STRING(pg_null_as, pg_null_as_len)
Z_PARAM_STR(pg_escape)
ZEND_PARSE_PARAMETERS_END();

link = Z_PGSQL_LINK_P(pgsql_link);
Expand All @@ -3242,8 +3244,14 @@ PHP_FUNCTION(pg_copy_to)
pg_null_as = estrdup("\\\\N");
free_pg_null = true;
}
if (!pg_escape) {
pg_escape = ZSTR_CHAR('"');
} else if (ZSTR_LEN(pg_escape) != 1) {
zend_argument_value_error(5, "must be one character");
RETURN_THROWS();
}

spprintf(&query, 0, "COPY %s TO STDOUT DELIMITER E'%c' NULL AS E'%s'", ZSTR_VAL(table_name), *ZSTR_VAL(pg_delimiter), pg_null_as);
spprintf(&query, 0, "COPY %s TO STDOUT CSV DELIMITER E'%c' ESCAPE E'%c' NULL AS E'%s'", ZSTR_VAL(table_name), *ZSTR_VAL(pg_delimiter), *ZSTR_VAL(pg_escape), pg_null_as);

while ((pgsql_result = PQgetResult(pgsql))) {
PQclear(pgsql_result);
Expand Down Expand Up @@ -3310,6 +3318,7 @@ PHP_FUNCTION(pg_copy_from)
zval *value;
zend_string *table_name;
zend_string *pg_delimiter = NULL;
zend_string *pg_escape = NULL;
char *pg_null_as = NULL;
size_t pg_null_as_len;
bool pg_null_as_free = false;
Expand All @@ -3318,13 +3327,14 @@ PHP_FUNCTION(pg_copy_from)
PGresult *pgsql_result;
ExecStatusType status;

ZEND_PARSE_PARAMETERS_START(3, 5)
ZEND_PARSE_PARAMETERS_START(3, 6)
Z_PARAM_OBJECT_OF_CLASS(pgsql_link, pgsql_link_ce)
Z_PARAM_PATH_STR(table_name)
Z_PARAM_ARRAY(pg_rows)
Z_PARAM_OPTIONAL
Z_PARAM_STR(pg_delimiter)
Z_PARAM_STRING(pg_null_as, pg_null_as_len)
Z_PARAM_STR(pg_escape)
ZEND_PARSE_PARAMETERS_END();

link = Z_PGSQL_LINK_P(pgsql_link);
Expand All @@ -3341,8 +3351,14 @@ PHP_FUNCTION(pg_copy_from)
pg_null_as = estrdup("\\\\N");
pg_null_as_free = true;
}
if (!pg_escape) {
pg_escape = ZSTR_CHAR('"');
} else if (ZSTR_LEN(pg_escape) != 1) {
zend_argument_value_error(6, "must be one character");
RETURN_THROWS();
}

spprintf(&query, 0, "COPY %s FROM STDIN DELIMITER E'%c' NULL AS E'%s'", ZSTR_VAL(table_name), *ZSTR_VAL(pg_delimiter), pg_null_as);
spprintf(&query, 0, "COPY %s FROM STDIN CSV DELIMITER E'%c' ESCAPE E'%c' NULL AS E'%s'", ZSTR_VAL(table_name), *ZSTR_VAL(pg_delimiter), *ZSTR_VAL(pg_escape), pg_null_as);
while ((pgsql_result = PQgetResult(pgsql))) {
PQclear(pgsql_result);
}
Expand Down
4 changes: 2 additions & 2 deletions ext/pgsql/pgsql.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -834,9 +834,9 @@ function pg_put_line($connection, string $query = UNKNOWN): bool {}
* @return array<int, string>|false
* @refcount 1
*/
function pg_copy_to(PgSql\Connection $connection, string $table_name, string $separator = "\t", string $null_as = "\\\\N"): array|false {}
function pg_copy_to(PgSql\Connection $connection, string $table_name, string $separator = "\t", string $null_as = "\\\\N", string $escape = "\""): array|false {}

function pg_copy_from(PgSql\Connection $connection, string $table_name, array $rows, string $separator = "\t", string $null_as = "\\\\N"): bool {}
function pg_copy_from(PgSql\Connection $connection, string $table_name, array $rows, string $separator = "\t", string $null_as = "\\\\N", string $escape = "\""): bool {}

/**
* @param PgSql\Connection|string $connection
Expand Down
4 changes: 3 additions & 1 deletion ext/pgsql/pgsql_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions ext/pgsql/tests/06copy_escape.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
PostgreSQL copy with escape settings
--EXTENSIONS--
pgsql
--SKIPIF--
<?php include("inc/skipif.inc"); ?>
--FILE--
<?php

include('inc/config.inc');
$table_name = "table_06copy";

$db = pg_connect($conn_str);
pg_query($db, "CREATE TABLE {$table_name} (num int, str text, comment text)");
pg_query($db, "INSERT INTO {$table_name} VALUES (0, '\"test A\"', '\"just testing\"')");
pg_query($db, "INSERT INTO {$table_name} VALUES (1, '\"test B\"', '\"test again\"')");

var_dump(pg_copy_to($db, $table_name, ";", "\\\\N", "%"));

pg_query($db, "DELETE FROM $table_name");

?>
--CLEAN--
<?php
include('inc/config.inc');
$table_name = "table_06copy";

$db = pg_connect($conn_str);
pg_query($db, "DROP TABLE IF EXISTS {$table_name}");
?>
--EXPECT--
array(2) {
[0]=>
string(34) "0;"%"test A%"";"%"just testing%""
"
[1]=>
string(33) "1;"%"test B%"";"%"test again%""
"
}
Loading