Skip to content

Commit 7ea9e87

Browse files
author
Ilia Alshanetsky
committed
Added support for copy to/from array/file for pdo_pgsql extension.
# original patch by Denis Gasparin
1 parent 408d786 commit 7ea9e87

File tree

3 files changed

+882
-0
lines changed

3 files changed

+882
-0
lines changed

ext/pdo_pgsql/pgsql_driver.c

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "ext/standard/info.h"
3030
#include "pdo/php_pdo.h"
3131
#include "pdo/php_pdo_driver.h"
32+
#include "ext/standard/file.h"
3233

3334
#undef PACKAGE_BUGREPORT
3435
#undef PACKAGE_NAME
@@ -496,6 +497,368 @@ static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
496497
return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
497498
}
498499

500+
/* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
501+
Returns true if the copy worked fine or false if error */
502+
static PHP_METHOD(PDO, pgsqlCopyFromArray)
503+
{
504+
pdo_dbh_t *dbh;
505+
pdo_pgsql_db_handle *H;
506+
507+
zval *pg_rows;
508+
509+
char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
510+
int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
511+
char *query;
512+
513+
PGresult *pgsql_result;
514+
ExecStatusType status;
515+
516+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss",
517+
&table_name, &table_name_len, &pg_rows,
518+
&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
519+
return;
520+
}
521+
522+
if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
523+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array");
524+
RETURN_FALSE;
525+
}
526+
527+
dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
528+
PDO_CONSTRUCT_CHECK;
529+
530+
if (pg_fields) {
531+
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"));
532+
} else {
533+
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"));
534+
}
535+
536+
// Obtain db Handle
537+
H = (pdo_pgsql_db_handle *)dbh->driver_data;
538+
539+
while ((pgsql_result = PQgetResult(H->server))) {
540+
PQclear(pgsql_result);
541+
}
542+
pgsql_result = PQexec(H->server, query);
543+
544+
efree(query);
545+
query = NULL;
546+
547+
if (pgsql_result) {
548+
status = PQresultStatus(pgsql_result);
549+
} else {
550+
status = (ExecStatusType) PQstatus(H->server);
551+
}
552+
553+
if (status == PGRES_COPY_IN && pgsql_result) {
554+
int command_failed = 0;
555+
int buffer_len = 0;
556+
zval **tmp;
557+
HashPosition pos;
558+
559+
PQclear(pgsql_result);
560+
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos);
561+
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
562+
int query_len;
563+
convert_to_string_ex(tmp);
564+
565+
if (buffer_len < Z_STRLEN_PP(tmp)) {
566+
buffer_len = Z_STRLEN_PP(tmp);
567+
query = erealloc(query, buffer_len + 2); /* room for \n\0 */
568+
}
569+
memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
570+
query_len = Z_STRLEN_PP(tmp);
571+
if (query[query_len - 1] != '\n') {
572+
query[query_len++] = '\n';
573+
}
574+
query[query_len] = '\0';
575+
if (PQputCopyData(H->server, query, query_len) != 1) {
576+
efree(query);
577+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
578+
RETURN_FALSE;
579+
}
580+
zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
581+
}
582+
if (query) {
583+
efree(query);
584+
}
585+
586+
if (PQputCopyEnd(H->server, NULL) != 1) {
587+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
588+
RETURN_FALSE;
589+
}
590+
591+
while ((pgsql_result = PQgetResult(H->server))) {
592+
if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
593+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
594+
command_failed = 1;
595+
}
596+
PQclear(pgsql_result);
597+
}
598+
599+
RETURN_BOOL(!command_failed);
600+
} else {
601+
PQclear(pgsql_result);
602+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
603+
RETURN_FALSE;
604+
}
605+
}
606+
/* }}} */
607+
608+
/* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
609+
Returns true if the copy worked fine or false if error */
610+
static PHP_METHOD(PDO, pgsqlCopyFromFile)
611+
{
612+
pdo_dbh_t *dbh;
613+
pdo_pgsql_db_handle *H;
614+
615+
char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
616+
int table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
617+
char *query;
618+
PGresult *pgsql_result;
619+
ExecStatusType status;
620+
php_stream *stream;
621+
622+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss",
623+
&table_name, &table_name_len, &filename, &filename_len,
624+
&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
625+
return;
626+
}
627+
628+
// Obtain db Handler
629+
dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
630+
PDO_CONSTRUCT_CHECK;
631+
632+
stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
633+
if (!stream) {
634+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
635+
RETURN_FALSE;
636+
}
637+
638+
if (pg_fields) {
639+
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"));
640+
} else {
641+
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"));
642+
}
643+
644+
H = (pdo_pgsql_db_handle *)dbh->driver_data;
645+
646+
while ((pgsql_result = PQgetResult(H->server))) {
647+
PQclear(pgsql_result);
648+
}
649+
pgsql_result = PQexec(H->server, query);
650+
651+
efree(query);
652+
653+
if (pgsql_result) {
654+
status = PQresultStatus(pgsql_result);
655+
} else {
656+
status = (ExecStatusType) PQstatus(H->server);
657+
}
658+
659+
if (status == PGRES_COPY_IN && pgsql_result) {
660+
char *buf;
661+
int command_failed = 0;
662+
size_t line_len = 0;
663+
664+
PQclear(pgsql_result);
665+
while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
666+
if (PQputCopyData(H->server, buf, line_len) != 1) {
667+
efree(buf);
668+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
669+
php_stream_close(stream);
670+
RETURN_FALSE;
671+
}
672+
efree(buf);
673+
}
674+
php_stream_close(stream);
675+
676+
if (PQputCopyEnd(H->server, NULL) != 1) {
677+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
678+
RETURN_FALSE;
679+
}
680+
681+
while ((pgsql_result = PQgetResult(H->server))) {
682+
if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
683+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
684+
command_failed = 1;
685+
}
686+
PQclear(pgsql_result);
687+
}
688+
689+
RETURN_BOOL(!command_failed);
690+
} else {
691+
PQclear(pgsql_result);
692+
php_stream_close(stream);
693+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
694+
RETURN_FALSE;
695+
}
696+
}
697+
/* }}} */
698+
699+
700+
/* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]])
701+
Returns true if the copy worked fine or false if error */
702+
static PHP_METHOD(PDO, pgsqlCopyToFile)
703+
{
704+
pdo_dbh_t *dbh;
705+
pdo_pgsql_db_handle *H;
706+
707+
char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
708+
int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
709+
char *query;
710+
711+
PGresult *pgsql_result;
712+
ExecStatusType status;
713+
714+
php_stream *stream;
715+
716+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss",
717+
&table_name, &table_name_len, &filename, &filename_len,
718+
&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
719+
return;
720+
}
721+
722+
dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
723+
PDO_CONSTRUCT_CHECK;
724+
725+
H = (pdo_pgsql_db_handle *)dbh->driver_data;
726+
727+
stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
728+
if (!stream) {
729+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
730+
RETURN_FALSE;
731+
}
732+
733+
while ((pgsql_result = PQgetResult(H->server))) {
734+
PQclear(pgsql_result);
735+
}
736+
737+
if (pg_fields) {
738+
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"));
739+
} else {
740+
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"));
741+
}
742+
pgsql_result = PQexec(H->server, query);
743+
efree(query);
744+
745+
if (pgsql_result) {
746+
status = PQresultStatus(pgsql_result);
747+
} else {
748+
status = (ExecStatusType) PQstatus(H->server);
749+
}
750+
751+
if (status == PGRES_COPY_OUT && pgsql_result) {
752+
PQclear(pgsql_result);
753+
while (1) {
754+
char *csv = NULL;
755+
int ret = PQgetCopyData(H->server, &csv, 0);
756+
757+
if (ret == -1) {
758+
break; /* done */
759+
} else if (ret > 0) {
760+
if (php_stream_write(stream, csv, ret) != ret) {
761+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
762+
PQfreemem(csv);
763+
php_stream_close(stream);
764+
RETURN_FALSE;
765+
} else {
766+
PQfreemem(csv);
767+
}
768+
} else {
769+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
770+
php_stream_close(stream);
771+
RETURN_FALSE;
772+
}
773+
}
774+
php_stream_close(stream);
775+
776+
while ((pgsql_result = PQgetResult(H->server))) {
777+
PQclear(pgsql_result);
778+
}
779+
RETURN_TRUE;
780+
} else {
781+
php_stream_close(stream);
782+
PQclear(pgsql_result);
783+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
784+
RETURN_FALSE;
785+
}
786+
}
787+
/* }}} */
788+
789+
/* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
790+
Returns true if the copy worked fine or false if error */
791+
static PHP_METHOD(PDO, pgsqlCopyToArray)
792+
{
793+
pdo_dbh_t *dbh;
794+
pdo_pgsql_db_handle *H;
795+
796+
char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
797+
int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
798+
char *query;
799+
800+
PGresult *pgsql_result;
801+
ExecStatusType status;
802+
803+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss",
804+
&table_name, &table_name_len,
805+
&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
806+
return;
807+
}
808+
809+
dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
810+
PDO_CONSTRUCT_CHECK;
811+
812+
H = (pdo_pgsql_db_handle *)dbh->driver_data;
813+
814+
while ((pgsql_result = PQgetResult(H->server))) {
815+
PQclear(pgsql_result);
816+
}
817+
818+
if (pg_fields) {
819+
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"));
820+
} else {
821+
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"));
822+
}
823+
pgsql_result = PQexec(H->server, query);
824+
efree(query);
825+
826+
if (pgsql_result) {
827+
status = PQresultStatus(pgsql_result);
828+
} else {
829+
status = (ExecStatusType) PQstatus(H->server);
830+
}
831+
832+
if (status == PGRES_COPY_OUT && pgsql_result) {
833+
PQclear(pgsql_result);
834+
array_init(return_value);
835+
836+
while (1) {
837+
char *csv = NULL;
838+
int ret = PQgetCopyData(H->server, &csv, 0);
839+
if (ret == -1) {
840+
break; /* copy done */
841+
} else if (ret > 0) {
842+
add_next_index_stringl(return_value, csv, ret, 1);
843+
PQfreemem(csv);
844+
} else {
845+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
846+
RETURN_FALSE;
847+
}
848+
}
849+
850+
while ((pgsql_result = PQgetResult(H->server))) {
851+
PQclear(pgsql_result);
852+
}
853+
} else {
854+
PQclear(pgsql_result);
855+
pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
856+
RETURN_FALSE;
857+
}
858+
}
859+
/* }}} */
860+
861+
499862
/* {{{ proto string PDO::pgsqlLOBCreate()
500863
Creates a new large object, returning its identifier. Must be called inside a transaction. */
501864
static PHP_METHOD(PDO, pgsqlLOBCreate)
@@ -608,6 +971,10 @@ static const zend_function_entry dbh_methods[] = {
608971
PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
609972
PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
610973
PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
974+
PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC)
975+
PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC)
976+
PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC)
977+
PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC)
611978
{NULL, NULL, NULL}
612979
};
613980

0 commit comments

Comments
 (0)