]> granicus.if.org Git - php/commitdiff
Added support for copy to/from array/file for pdo_pgsql extension.
authorIlia Alshanetsky <iliaa@php.net>
Thu, 10 Jun 2010 11:11:29 +0000 (11:11 +0000)
committerIlia Alshanetsky <iliaa@php.net>
Thu, 10 Jun 2010 11:11:29 +0000 (11:11 +0000)
# original patch by Denis Gasparin

NEWS
ext/pdo_pgsql/pgsql_driver.c
ext/pdo_pgsql/tests/copy_from.phpt [new file with mode: 0644]
ext/pdo_pgsql/tests/copy_to.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 38bfe84493e4ddd36eca76530cf4aa45ddc2cfa5..711e0a1e8f7b593bebc69a2f4d57613bd6461139 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,8 @@ PHP                                                                        NEWS
 - Added recent Windows versions to php_uname and fix undefined windows 
   version support. (Pierre)
 - Added Berkeley DB 5 support to the DBA extension (Johannes, Chris Jones)
+- Added support for copy to/from array/file for pdo_pgsql extension. 
+  (Denis Gasparin, Ilia)
 
 - Changed namespaced classes so that the ctor can only be named
   __construct now. (Stas)
index f2467e8a6634e3ea2920cbc0940cd3de6aacf8b4..a9ce7e603217722dcdfd90112437f5973c474735 100644 (file)
@@ -29,6 +29,7 @@
 #include "ext/standard/info.h"
 #include "pdo/php_pdo.h"
 #include "pdo/php_pdo_driver.h"
+#include "ext/standard/file.h"
 
 #undef PACKAGE_BUGREPORT
 #undef PACKAGE_NAME
@@ -496,6 +497,368 @@ static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
        return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
 }
 
+/* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
+   Returns true if the copy worked fine or false if error */
+static PHP_METHOD(PDO, pgsqlCopyFromArray)
+{
+       pdo_dbh_t *dbh;
+       pdo_pgsql_db_handle *H;
+
+       zval *pg_rows;
+
+       char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
+       int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
+       char *query;
+
+       PGresult *pgsql_result;
+       ExecStatusType status;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss",
+                                       &table_name, &table_name_len, &pg_rows,
+                                       &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
+               return;
+       }
+
+       if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array");
+               RETURN_FALSE;
+       }
+
+       dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+       PDO_CONSTRUCT_CHECK;
+
+       if (pg_fields) {
+               spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+       } else {
+               spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+       }
+
+       // Obtain db Handle
+       H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+       while ((pgsql_result = PQgetResult(H->server))) {
+               PQclear(pgsql_result);
+       }
+       pgsql_result = PQexec(H->server, query);
+
+       efree(query);
+       query = NULL;
+
+       if (pgsql_result) {
+               status = PQresultStatus(pgsql_result);
+       } else {
+               status = (ExecStatusType) PQstatus(H->server);
+       }
+
+       if (status == PGRES_COPY_IN && pgsql_result) {
+               int command_failed = 0;
+               int buffer_len = 0;
+               zval **tmp;
+               HashPosition pos;
+
+               PQclear(pgsql_result);
+               zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos);
+               while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
+                       int query_len;
+                       convert_to_string_ex(tmp);
+               
+                       if (buffer_len < Z_STRLEN_PP(tmp)) {
+                               buffer_len = Z_STRLEN_PP(tmp);
+                               query = erealloc(query, buffer_len + 2); /* room for \n\0 */
+                       }
+                       memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
+                       query_len = Z_STRLEN_PP(tmp);
+                       if (query[query_len - 1] != '\n') {
+                               query[query_len++] = '\n';
+                       }
+                       query[query_len] = '\0';
+                       if (PQputCopyData(H->server, query, query_len) != 1) {
+                               efree(query);
+                               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
+                               RETURN_FALSE;
+                       }
+                       zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
+                }
+               if (query) {
+                       efree(query);
+               }
+
+               if (PQputCopyEnd(H->server, NULL) != 1) {
+                       pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
+                       RETURN_FALSE;
+               }
+
+               while ((pgsql_result = PQgetResult(H->server))) {
+                       if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
+                               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+                               command_failed = 1;
+                       }
+                       PQclear(pgsql_result);
+               }
+
+               RETURN_BOOL(!command_failed);
+       } else {
+               PQclear(pgsql_result);
+               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+               RETURN_FALSE;
+       }
+}
+/* }}} */
+
+/* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
+   Returns true if the copy worked fine or false if error */
+static PHP_METHOD(PDO, pgsqlCopyFromFile)
+{
+       pdo_dbh_t *dbh;
+       pdo_pgsql_db_handle *H;
+
+       char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
+       int  table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
+       char *query;
+       PGresult *pgsql_result;
+       ExecStatusType status;
+       php_stream *stream;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss",
+                               &table_name, &table_name_len, &filename, &filename_len,
+                               &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
+               return;
+       }
+
+       // Obtain db Handler
+       dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+       PDO_CONSTRUCT_CHECK;
+
+       stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
+       if (!stream) {
+               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
+               RETURN_FALSE;
+       }
+
+       if (pg_fields) {
+               spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+       } else {
+               spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+       }
+
+       H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+       while ((pgsql_result = PQgetResult(H->server))) {
+               PQclear(pgsql_result);
+       }
+       pgsql_result = PQexec(H->server, query);
+
+       efree(query);
+
+       if (pgsql_result) {
+               status = PQresultStatus(pgsql_result);
+       } else {
+               status = (ExecStatusType) PQstatus(H->server);
+       }
+
+       if (status == PGRES_COPY_IN && pgsql_result) {
+               char *buf;
+               int command_failed = 0;
+               size_t line_len = 0;
+
+               PQclear(pgsql_result);
+               while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
+                       if (PQputCopyData(H->server, buf, line_len) != 1) {
+                               efree(buf);
+                               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
+                               php_stream_close(stream);
+                               RETURN_FALSE;
+                       }
+                       efree(buf);
+               }
+               php_stream_close(stream);
+
+               if (PQputCopyEnd(H->server, NULL) != 1) {
+                       pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
+                       RETURN_FALSE;
+               }
+
+               while ((pgsql_result = PQgetResult(H->server))) {
+                       if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
+                               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+                               command_failed = 1;
+                       }
+                       PQclear(pgsql_result);
+               }
+
+               RETURN_BOOL(!command_failed);
+       } else {
+               PQclear(pgsql_result);
+               php_stream_close(stream);
+               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+               RETURN_FALSE;
+       }
+}
+/* }}} */
+
+
+/* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]])
+   Returns true if the copy worked fine or false if error */
+static PHP_METHOD(PDO, pgsqlCopyToFile)
+{
+       pdo_dbh_t *dbh;
+       pdo_pgsql_db_handle *H;
+
+       char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
+       int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
+       char *query;
+
+       PGresult *pgsql_result;
+       ExecStatusType status;
+
+       php_stream *stream;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss",
+                                       &table_name, &table_name_len, &filename, &filename_len,
+                                       &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
+               return;
+       }
+
+       dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+       PDO_CONSTRUCT_CHECK;
+
+       H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+       stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
+       if (!stream) {
+               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
+               RETURN_FALSE;
+       }
+
+       while ((pgsql_result = PQgetResult(H->server))) {
+               PQclear(pgsql_result);
+       }
+
+       if (pg_fields) {
+               spprintf(&query, 0, "COPY %s (%s) TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+       } else {
+               spprintf(&query, 0, "COPY %s TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+       }
+       pgsql_result = PQexec(H->server, query);
+       efree(query);
+
+       if (pgsql_result) {
+               status = PQresultStatus(pgsql_result);
+       } else {
+               status = (ExecStatusType) PQstatus(H->server);
+       }
+
+       if (status == PGRES_COPY_OUT && pgsql_result) {
+               PQclear(pgsql_result);
+               while (1) {
+                       char *csv = NULL;
+                       int ret = PQgetCopyData(H->server, &csv, 0);
+
+                       if (ret == -1) {
+                               break; /* done */
+                       } else if (ret > 0) {
+                               if (php_stream_write(stream, csv, ret) != ret) {
+                                       pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
+                                       PQfreemem(csv);
+                                       php_stream_close(stream);
+                                       RETURN_FALSE;
+                               } else {
+                                       PQfreemem(csv);
+                               }
+                       } else {
+                               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
+                               php_stream_close(stream);
+                               RETURN_FALSE;
+                       }
+               }
+               php_stream_close(stream);
+
+               while ((pgsql_result = PQgetResult(H->server))) {
+                       PQclear(pgsql_result);
+               }
+               RETURN_TRUE;
+       } else {
+               php_stream_close(stream);
+               PQclear(pgsql_result);
+               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+               RETURN_FALSE;
+       }
+}
+/* }}} */
+
+/* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
+   Returns true if the copy worked fine or false if error */
+static PHP_METHOD(PDO, pgsqlCopyToArray)
+{
+       pdo_dbh_t *dbh;
+       pdo_pgsql_db_handle *H;
+
+       char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
+       int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
+       char *query;
+
+       PGresult *pgsql_result;
+       ExecStatusType status;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss",
+               &table_name, &table_name_len,
+               &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
+               return;
+       }
+
+       dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+       PDO_CONSTRUCT_CHECK;
+
+       H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+       while ((pgsql_result = PQgetResult(H->server))) {
+               PQclear(pgsql_result);
+       }
+
+       if (pg_fields) {
+               spprintf(&query, 0, "COPY %s (%s) TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+       } else {
+               spprintf(&query, 0, "COPY %s TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+       }
+       pgsql_result = PQexec(H->server, query);
+       efree(query);
+
+       if (pgsql_result) {
+               status = PQresultStatus(pgsql_result);
+       } else {
+               status = (ExecStatusType) PQstatus(H->server);
+       }
+
+       if (status == PGRES_COPY_OUT && pgsql_result) {
+               PQclear(pgsql_result);
+                array_init(return_value);
+
+               while (1) {
+                       char *csv = NULL;
+                       int ret = PQgetCopyData(H->server, &csv, 0);
+                       if (ret == -1) {
+                               break; /* copy done */
+                       } else if (ret > 0) { 
+                               add_next_index_stringl(return_value, csv, ret, 1);
+                               PQfreemem(csv);
+                       } else {
+                               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
+                               RETURN_FALSE;
+                       }
+               }
+
+               while ((pgsql_result = PQgetResult(H->server))) {
+                       PQclear(pgsql_result);
+               }
+       } else {
+               PQclear(pgsql_result);
+               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+               RETURN_FALSE;
+       }
+}
+/* }}} */
+
+
 /* {{{ proto string PDO::pgsqlLOBCreate()
    Creates a new large object, returning its identifier.  Must be called inside a transaction. */
 static PHP_METHOD(PDO, pgsqlLOBCreate)
@@ -608,6 +971,10 @@ static const zend_function_entry dbh_methods[] = {
        PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
        PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
        PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
 };
 
diff --git a/ext/pdo_pgsql/tests/copy_from.phpt b/ext/pdo_pgsql/tests/copy_from.phpt
new file mode 100644 (file)
index 0000000..2858905
--- /dev/null
@@ -0,0 +1,386 @@
+--TEST--
+PDO PgSQL pgsqlCopyFromArray and pgsqlCopyFromFile
+--SKIPIF--
+<?php # vim:se ft=php:
+if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded');
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
+
+$db->exec('CREATE TABLE test (a integer not null primary key, b text, c integer)');
+
+try {
+
+echo "Preparing test file and array for CopyFrom tests\n";
+
+$tableRows = array();
+$tableRowsWithDifferentNullValues = array();
+
+for($i=0;$i<3;$i++) {
+       $firstParameter = $i;
+       $secondParameter = "test insert {$i}";
+       $tableRows[] = "{$firstParameter}\t{$secondParameter}\t\\N";
+       $tableRowsWithDifferentNullValues[] = "{$firstParameter};{$secondParameter};NULL";
+       $tableRowsWithDifferentNullValuesAndSelectedFields[] = "{$firstParameter};NULL";
+}
+$filename = 'test_pgsqlCopyFromFile.csv';
+$filenameWithDifferentNullValues = 'test_pgsqlCopyFromFileWithDifferentNullValues.csv';
+$filenameWithDifferentNullValuesAndSelectedFields = 'test_pgsqlCopyFromFileWithDifferentNullValuesAndSelectedFields.csv';
+
+file_put_contents($filename, implode("\n",$tableRows));
+file_put_contents($filenameWithDifferentNullValues, implode("\n",$tableRowsWithDifferentNullValues));
+file_put_contents($filenameWithDifferentNullValuesAndSelectedFields, implode("\n",$tableRowsWithDifferentNullValuesAndSelectedFields));
+
+echo "Testing pgsqlCopyFromArray() with default parameters\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromArray('test',$tableRows));
+
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+       var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromArray() with different field separator and not null indicator\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromArray('test',$tableRowsWithDifferentNullValues,";","NULL"));
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+       var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromArray() with only selected fields\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromArray('test',$tableRowsWithDifferentNullValuesAndSelectedFields,";","NULL",'a,c'));
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+       var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromArray() with error\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromArray('test_error',$tableRowsWithDifferentNullValuesAndSelectedFields,";","NULL",'a,c'));
+$db->rollback();
+
+
+echo "Testing pgsqlCopyFromFile() with default parameters\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromFile('test',$filename));
+
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+       var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromFile() with different field separator and not null indicator\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromFile('test',$filenameWithDifferentNullValues,";","NULL"));
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+       var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromFile() with only selected fields\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromFile('test',$filenameWithDifferentNullValuesAndSelectedFields,";","NULL",'a,c'));
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+       var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromFile() with error\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromFile('test_error',$filenameWithDifferentNullValuesAndSelectedFields,";","NULL",'a,c'));
+$db->rollback();
+
+} catch (Exception $e) {
+       /* catch exceptions so that we can show the relative error */
+       echo "Exception! at line ", $e->getLine(), "\n";
+       var_dump($e->getMessage());
+}
+if(isset($filename)) {
+       @unlink($filename);
+}
+?>
+--EXPECT--
+Preparing test file and array for CopyFrom tests
+Testing pgsqlCopyFromArray() with default parameters
+bool(true)
+array(6) {
+  ["a"]=>
+  int(0)
+  [0]=>
+  int(0)
+  ["b"]=>
+  string(13) "test insert 0"
+  [1]=>
+  string(13) "test insert 0"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(1)
+  [0]=>
+  int(1)
+  ["b"]=>
+  string(13) "test insert 1"
+  [1]=>
+  string(13) "test insert 1"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(2)
+  [0]=>
+  int(2)
+  ["b"]=>
+  string(13) "test insert 2"
+  [1]=>
+  string(13) "test insert 2"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+Testing pgsqlCopyFromArray() with different field separator and not null indicator
+bool(true)
+array(6) {
+  ["a"]=>
+  int(0)
+  [0]=>
+  int(0)
+  ["b"]=>
+  string(13) "test insert 0"
+  [1]=>
+  string(13) "test insert 0"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(1)
+  [0]=>
+  int(1)
+  ["b"]=>
+  string(13) "test insert 1"
+  [1]=>
+  string(13) "test insert 1"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(2)
+  [0]=>
+  int(2)
+  ["b"]=>
+  string(13) "test insert 2"
+  [1]=>
+  string(13) "test insert 2"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+Testing pgsqlCopyFromArray() with only selected fields
+bool(true)
+array(6) {
+  ["a"]=>
+  int(0)
+  [0]=>
+  int(0)
+  ["b"]=>
+  NULL
+  [1]=>
+  NULL
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(1)
+  [0]=>
+  int(1)
+  ["b"]=>
+  NULL
+  [1]=>
+  NULL
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(2)
+  [0]=>
+  int(2)
+  ["b"]=>
+  NULL
+  [1]=>
+  NULL
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+Testing pgsqlCopyFromArray() with error
+bool(false)
+Testing pgsqlCopyFromFile() with default parameters
+bool(true)
+array(6) {
+  ["a"]=>
+  int(0)
+  [0]=>
+  int(0)
+  ["b"]=>
+  string(13) "test insert 0"
+  [1]=>
+  string(13) "test insert 0"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(1)
+  [0]=>
+  int(1)
+  ["b"]=>
+  string(13) "test insert 1"
+  [1]=>
+  string(13) "test insert 1"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(2)
+  [0]=>
+  int(2)
+  ["b"]=>
+  string(13) "test insert 2"
+  [1]=>
+  string(13) "test insert 2"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+Testing pgsqlCopyFromFile() with different field separator and not null indicator
+bool(true)
+array(6) {
+  ["a"]=>
+  int(0)
+  [0]=>
+  int(0)
+  ["b"]=>
+  string(13) "test insert 0"
+  [1]=>
+  string(13) "test insert 0"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(1)
+  [0]=>
+  int(1)
+  ["b"]=>
+  string(13) "test insert 1"
+  [1]=>
+  string(13) "test insert 1"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(2)
+  [0]=>
+  int(2)
+  ["b"]=>
+  string(13) "test insert 2"
+  [1]=>
+  string(13) "test insert 2"
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+Testing pgsqlCopyFromFile() with only selected fields
+bool(true)
+array(6) {
+  ["a"]=>
+  int(0)
+  [0]=>
+  int(0)
+  ["b"]=>
+  NULL
+  [1]=>
+  NULL
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(1)
+  [0]=>
+  int(1)
+  ["b"]=>
+  NULL
+  [1]=>
+  NULL
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+array(6) {
+  ["a"]=>
+  int(2)
+  [0]=>
+  int(2)
+  ["b"]=>
+  NULL
+  [1]=>
+  NULL
+  ["c"]=>
+  NULL
+  [2]=>
+  NULL
+}
+Testing pgsqlCopyFromFile() with error
+bool(false)
\ No newline at end of file
diff --git a/ext/pdo_pgsql/tests/copy_to.phpt b/ext/pdo_pgsql/tests/copy_to.phpt
new file mode 100644 (file)
index 0000000..1dc7d1d
--- /dev/null
@@ -0,0 +1,129 @@
+--TEST--
+PDO PgSQL pgsqlCopyToArray and pgsqlCopyToFile
+--SKIPIF--
+<?php # vim:se ft=php:
+if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded');
+require dirname(__FILE__) . '/config.inc';
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
+
+$db->exec('CREATE TABLE test (a integer not null primary key, b text, c integer)');
+
+$db->beginTransaction();
+try {
+
+echo "Preparing test table for CopyTo tests\n";
+$stmt = $db->prepare("INSERT INTO test (a, b, c) values (?, ?, ?)");
+
+for($i=0;$i<3;$i++) {
+       $firstParameter = $i;
+       $secondParameter = "test insert {$i}";
+       $thirdParameter = NULL;
+       $stmt->bindValue(1, $firstParameter);
+       $stmt->bindValue(2, $secondParameter);
+       $stmt->bindValue(3, $thirdParameter);
+       $stmt->execute();
+}
+
+$db->commit();
+
+echo "Testing pgsqlCopyToArray() with default parameters\n";
+var_dump($db->pgsqlCopyToArray('test'));
+echo "Testing pgsqlCopyToArray() with different field separator and not null indicator\n";
+var_dump($db->pgsqlCopyToArray('test',";","NULL"));
+echo "Testing pgsqlCopyToArray() with only selected fields\n";
+var_dump($db->pgsqlCopyToArray('test',";","NULL",'a,c'));
+
+echo "Testing pgsqlCopyToArray() with error\n";
+var_dump($db->pgsqlCopyToArray('test_error'));
+
+
+echo "Testing pgsqlCopyToFile() with default parameters\n";
+
+$filename="test_pgsqlCopyToFile.csv";
+var_dump($db->pgsqlCopyToFile('test',$filename));
+echo file_get_contents($filename);
+echo "Testing pgsqlCopyToFile() with different field separator and not null indicator\n";
+var_dump($db->pgsqlCopyToFile('test',$filename,";","NULL"));
+echo file_get_contents($filename);
+echo "Testing pgsqlCopyToFile() with only selected fields\n";
+var_dump($db->pgsqlCopyToFile('test',$filename,";","NULL",'a,c'));
+echo file_get_contents($filename);
+
+echo "Testing pgsqlCopyToFile() with error\n";
+var_dump($db->pgsqlCopyToFile('test_error',$filename));
+
+
+} catch (Exception $e) {
+       /* catch exceptions so that we can show the relative error */
+       echo "Exception! at line ", $e->getLine(), "\n";
+       var_dump($e->getMessage());
+}
+if(isset($filename)) {
+       @unlink($filename);
+}
+?>
+--EXPECT--
+Preparing test table for CopyTo tests
+Testing pgsqlCopyToArray() with default parameters
+array(3) {
+  [0]=>
+  string(19) "0        test insert 0   \N
+"
+  [1]=>
+  string(19) "1        test insert 1   \N
+"
+  [2]=>
+  string(19) "2        test insert 2   \N
+"
+}
+Testing pgsqlCopyToArray() with different field separator and not null indicator
+array(3) {
+  [0]=>
+  string(21) "0;test insert 0;NULL
+"
+  [1]=>
+  string(21) "1;test insert 1;NULL
+"
+  [2]=>
+  string(21) "2;test insert 2;NULL
+"
+}
+Testing pgsqlCopyToArray() with only selected fields
+array(3) {
+  [0]=>
+  string(7) "0;NULL
+"
+  [1]=>
+  string(7) "1;NULL
+"
+  [2]=>
+  string(7) "2;NULL
+"
+}
+Testing pgsqlCopyToArray() with error
+bool(false)
+Testing pgsqlCopyToFile() with default parameters
+bool(true)
+0      test insert 0   \N
+1      test insert 1   \N
+2      test insert 2   \N
+Testing pgsqlCopyToFile() with different field separator and not null indicator
+bool(true)
+0;test insert 0;NULL
+1;test insert 1;NULL
+2;test insert 2;NULL
+Testing pgsqlCopyToFile() with only selected fields
+bool(true)
+0;NULL
+1;NULL
+2;NULL
+Testing pgsqlCopyToFile() with error
+bool(false)
\ No newline at end of file