I attached the patch as a single file.

There are currently no tests written for the new methods.

I'll provide them if needed to integrate the patch in the pdo_pgsql driver.

Denis


----- Messaggio originale -----
> Da: "Denis Gasparin" <denis.gaspa...@edistar.com>
> A: "Ilia Alshanetsky" <i...@prohost.org>
> Cc: internals@lists.php.net
> Inviato: Lunedì, 24 maggio 2010 21:45:30
> Oggetto: Re: [PHP-DEV] [PATCH] New PDO methods for PostgreSQL driver

> I'll provide the patches in a single file as soon as possible.
> 
> Actually all the methods are wrappers against the native PostgreSQL
> commands (connection status, copy to/from).
> 
> I needed to develop them as methods because it is not possible to get
> the same results with a sql statement (in particular for connection
> status).
> 
> It was not possible also for COPY TO/FROM because of the way
> PostgreSQL handle them ( special functions were developed also for the
> old pgsql driver).
> 
> We are currently using them in production and we needed them in order
> to avoid additional connections to database (one with the old driver
> and one with PDO).
> 
> Denis
> 
> 
> ----- Messaggio originale -----
> > Da: "Ilia Alshanetsky" <i...@prohost.org>
> > A: "Denis Gasparin" <denis.gaspa...@edistar.com>
> > Cc: internals@lists.php.net
> > Inviato: Lunedì, 24 maggio 2010 19:54:46
> > Oggetto: Re: [PHP-DEV] [PATCH] New PDO methods for PostgreSQL driver
> 
> > Denis,
> >
> >
> > Could you merge the patches into a single for easier code review.
> > Also, the copy to/from file seems like it would just be a wrapper
> > against the native COPY PostgreSQL command, is there really a need
> > to provide a method for it?
> >
> >
> > On Mon, May 24, 2010 at 4:57 AM, Denis Gasparin <
> > denis.gaspa...@edistar.com > wrote:
> >
> >
> > Hi.
> >
> > I developed some patches for PDO/Postgresql driver in order to add
> > some useful methods that were available in the original pgsql
> > driver.
> >
> > The attached patches apply on 5.3.2 and svn 5.3.x.
> > If needed, i have patches also for 5.2.x.
> >
> > Please comment and tell me improvements or tips.
> >
> > Thank you in advance,
> >
> > Denis Gasparin
> >
> > Documentation of the added methods follows:
> >
> > pgsqlIsInTransaction()
> >
> > It uses the native Postgresql functions to check transaction
> > status..
> > It returns one of the following status codes:
> > * PDO::PGSQL_TRANSACTION_IDLE: connection in idle status
> > * PDO::PGSQL_TRANSACTION_ACTIVE: connection is executing a command
> > * PDO::PGSQL_TRANSACTION_INTRANS: connection is idle in a valid
> > transaction block
> > * PDO::PGSQL_TRANSACTION_INERROR: connection is idle, in a failed
> > transaction block
> > * PDO::PGSQL_TRANSACTION_UNKNOWN: connection is in a bad status
> >
> >
> >
> > pgsqlCopyFromArray($table,Array $data,$delimiter,$null, Array
> > $fields)
> >
> > It uses the native Postgresql copy construct to append $data to
> > $table. It returns boolean.
> > Parameters: * (mandatory) $table: table to append data to
> > * (mandatory) $data: Array of rows with data in table field order
> > (or as specified in the $fields array). Fields must be separated by
> > $delimiter or by
> > postgresql standard \t)
> > * $delimiter: alternative delimiter to use in place of the standard
> > postgres delimiter ("\t")
> > * $null: alternative string to use as null value. Default is "\N"
> > * $fields: array with table fields that are specified in $data
> > parameter
> >
> >
> >
> > pgsqlCopyFromFile($table,$filename,$delimiter,$null,$fields)
> >
> > It uses the native Postgresql copy construct to append $filename
> > contents to $table.
> > It returns boolean.
> > Parameters: * (mandatory) $table: table to append data to.
> > * (mandatory) $filename: file with contents to append to $table. See
> > Postgresql documentation for the format.
> > * $delimiter: alternative delimiter to use in place of the standard
> > postgres delimiter ("\t")
> > * $null: alternative string to use as null value. Default is "\N"
> > * $fields: array with table fields that are specified in $filename
> > file
> >
> > pgsqlCopyToArray($table,$delimiter,$null,$fields)
> >
> > It uses the native Postgresql copy construct to retrieve $table
> > contents and store them to an array.
> > It returns an array of rows or false in case of problems.
> > The format of the rows into the array is indicated in the
> > $delimiter, $null and $fields parameters.
> > Parameters: * (mandatory) $table: table to retrieve data from.
> > * $delimiter: alternative delimiter to use in place of the standard
> > postgres delimiter ("\t")
> > * $null: alternative string to use as null value. Default is "\N"
> > * $fields: array with table fields to include in the row of the
> > array.
> >
> >
> > pgsqlCopyToFile($table,$filename,$delimiter,$null,$fields)
> >
> >
> > It uses the native Postgresql copy construct to retrieve $table
> > contents and store them into a file.
> > It returns boolean.
> > The format of the rows stored into the file is indicated in the
> > $delimiter, $null and $fields parameters.
> > Parameters: * (mandatory) $table: table to retrieve data from.
> > * (mandatory) $filename: file where to store the contents of the
> > table * $delimiter: alternative delimiter to use in place of the
> > standard postgres delimiter ("\t")
> > * $null: alternative string to use as null value. Default is "\N"
> > * $fields: array with table fields to include in the row of the
> > array.
> >
> > -- PHP Internals - PHP Runtime Development Mailing List
> > To unsubscribe, visit: http://www.php.net/unsub.php
> 
> -- PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
diff -u php-src-5.3/ext/pdo_pgsql/pdo_pgsql.c php-src-5.3.patched/ext/pdo_pgsql/pdo_pgsql.c
--- php-src-5.3/ext/pdo_pgsql/pdo_pgsql.c	2010-05-24 22:22:27.833522150 +0200
+++ php-src-5.3.patched/ext/pdo_pgsql/pdo_pgsql.c	2010-05-24 10:20:30.674038241 +0200
@@ -29,6 +29,7 @@
 #include "pdo/php_pdo_driver.h"
 #include "php_pdo_pgsql.h"
 #include "php_pdo_pgsql_int.h"
+#include "ext/standard/php_smart_str.h"
 
 #ifdef HAVE_PG_CONFIG_H
 #undef PACKAGE_BUGREPORT
@@ -79,6 +80,16 @@
 ZEND_GET_MODULE(pdo_pgsql)
 #endif
 
+
+static void php_pdo_pgsql_list_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) {
+	char * str = (char *)rsrc->ptr;
+	pefree(str,1);
+}
+
+
+static int le_pdo_pgsql_descriptor;
+
+
 /* true global environment */
 
 /* {{{ PHP_MINIT_FUNCTION
@@ -86,7 +97,13 @@
 PHP_MINIT_FUNCTION(pdo_pgsql)
 {
 	REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT", PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT);
+	le_pdo_pgsql_descriptor = zend_register_list_destructors_ex(NULL, php_pdo_pgsql_list_dtor, PHP_PDO_PGSQL_RES_NAME, module_number);
 	php_pdo_register_driver(&pdo_pgsql_driver);
+    REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_IDLE", (long)PGSQL_TRANSACTION_IDLE);
+    REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_ACTIVE", (long)PGSQL_TRANSACTION_ACTIVE);
+    REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INTRANS", (long)PGSQL_TRANSACTION_INTRANS);
+    REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (long)PGSQL_TRANSACTION_INERROR);
+    REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (long)PGSQL_TRANSACTION_UNKNOWN);
 	return SUCCESS;
 }
 /* }}} */
@@ -134,6 +151,91 @@
 }
 /* }}} */
 
+
+int _pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
+{
+	pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+	PGresult *result;
+	char *q=NULL;
+ 	ExecStatusType status;
+    smart_str str = {0};
+    zend_rsrc_list_entry *field_type;
+    char *ret=NULL;
+	zval *resource_val=NULL;
+
+	HashTable *list = &EG(persistent_list);
+
+	if (!S->result) {
+		return FAILURE;
+	}
+	
+	if (colno >= stmt->column_count) {
+		return FAILURE;
+	}
+	
+	array_init(return_value);
+	add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type);
+
+    /* try to lookup the type in the resource list */
+    smart_str_appends(&str, "pgsql_oid_");
+    smart_str_append_unsigned(&str,  S->cols[colno].pgsql_type);
+    smart_str_0(&str);
+
+    if (zend_hash_find(list,str.c,str.len+1,(void **) &field_type)==SUCCESS) {
+        ret = estrdup((char *)field_type->ptr);
+    } else { /* hash all oid's */
+        int i,num_rows;
+        int oid_offset,name_offset;
+        char *tmp_oid, *end_ptr, *tmp_name,*type_name;
+        zend_rsrc_list_entry new_oid_entry;
+
+        if ((result = PQexec(S->H->server,"select oid,typname from pg_type")) == NULL || PQresultStatus(result) != PGRES_TUPLES_OK) {
+            if (result) {
+                PQclear(result);
+            }
+            smart_str_free(&str);
+            return 0;
+        }
+        num_rows = PQntuples(result);
+        oid_offset = PQfnumber(result,"oid");
+        name_offset = PQfnumber(result,"typname");
+
+        for (i=0; i<num_rows; i++) {
+            if ((tmp_oid = PQgetvalue(result,i,oid_offset))==NULL) {
+                continue;
+            }
+
+            str.len = 0;
+            smart_str_appends(&str, "pgsql_oid_");
+            smart_str_appends(&str, tmp_oid);
+            smart_str_0(&str);
+
+            if ((tmp_name = PQgetvalue(result,i,name_offset))==NULL) {
+                continue;
+            }
+            type_name = pemalloc(strlen(tmp_name)+1,1);
+            memcpy(type_name,tmp_name,strlen(tmp_name)+1);
+            //type_name = pestrdup(tmp_name);
+            ZEND_REGISTER_RESOURCE(resource_val, type_name,le_pdo_pgsql_descriptor);
+		
+            new_oid_entry.type = le_pdo_pgsql_descriptor;
+            new_oid_entry.ptr = type_name;
+            zend_hash_update(list,str.c,str.len+1,(void *) &new_oid_entry, sizeof(zend_rsrc_list_entry), NULL);
+            if (!ret && strtoul(tmp_oid, &end_ptr, 10)==S->cols[colno].pgsql_type) {
+                ret = estrdup(tmp_name);
+            }
+        }
+        PQclear(result);
+    }
+
+    smart_str_free(&str);
+
+	add_assoc_string(return_value, "native_type", ret, 1);	
+	return 1;
+
+}
+
+
 /*
  * Local variables:
  * tab-width: 4
diff -u php-src-5.3/ext/pdo_pgsql/pgsql_driver.c php-src-5.3.patched/ext/pdo_pgsql/pgsql_driver.c
--- php-src-5.3/ext/pdo_pgsql/pgsql_driver.c	2010-05-24 22:22:27.833522150 +0200
+++ php-src-5.3.patched/ext/pdo_pgsql/pgsql_driver.c	2010-05-24 10:20:27.323869345 +0200
@@ -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,510 @@
 	return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
 }
 
+/* {{{ proto string PDO::pgsqlIsInTransaction()
+   Returns the status of a transaction according to PDO::PGSQL_TRANSACTION_* constants */
+static PHP_METHOD(PDO, pgsqlIsInTransaction)
+{
+	pdo_dbh_t *dbh;
+	pdo_pgsql_db_handle *H;
+
+	dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+	PDO_CONSTRUCT_CHECK;
+
+	H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+	RETURN_LONG(PQtransactionStatus(H->server));
+}
+/* }}} */
+
+/* {{{ 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;
+    zval **tmp;
+    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
+    int  table_name_len, pg_delim_len, pg_null_as_len, pg_fields_len;
+    char *query, *fields;
+    HashPosition pos;
+    int id = -1;
+    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 (!pg_delim) {
+        pg_delim = "\t";
+    }
+    if (pg_null_as_len<=0) {
+        pg_null_as = safe_estrdup("\\\\N");
+    }
+
+    if(pg_fields_len>0) {
+        spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, *pg_delim, pg_null_as);
+    }
+    else {
+        spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
+    }
+
+    // Obtain db Handler
+    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+    PDO_CONSTRUCT_CHECK;
+
+    H = (pdo_pgsql_db_handle *)dbh->driver_data;
+    // H->server is the db handle for libPQ
+
+
+    while ((pgsql_result = PQgetResult(H->server))) {
+        PQclear(pgsql_result);
+    }
+    pgsql_result = PQexec(H->server, query);
+
+    if (pg_null_as_len<=0) {
+        efree(pg_null_as);
+    }
+    efree(query);
+
+    if (pgsql_result) {
+        status = PQresultStatus(pgsql_result);
+    } else {
+        status = (ExecStatusType) PQstatus(H->server);
+    }
+
+    switch (status) {
+        case PGRES_COPY_IN:
+            if (pgsql_result) {
+                int command_failed = 0;
+                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) {
+                    convert_to_string_ex(tmp);
+                    query = (char *)emalloc(Z_STRLEN_PP(tmp) + 2);
+                    strlcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp) + 2);
+                    if(Z_STRLEN_PP(tmp) > 0 && *(query + Z_STRLEN_PP(tmp) - 1) != '\n') {
+                        strlcat(query, "\n", Z_STRLEN_PP(tmp) + 2);
+                    }
+                    if (PQputCopyData(H->server, query, strlen(query)) != 1) {
+                        efree(query);
+                        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
+                        RETURN_FALSE;
+                    }
+                    efree(query);
+                    zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
+                }
+                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);
+                }
+                if (command_failed) {
+                    RETURN_FALSE;
+                }
+            } else {
+                PQclear(pgsql_result);
+                RETURN_FALSE;
+            }
+            RETURN_TRUE;
+            break;
+        default:
+            PQclear(pgsql_result);
+            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+            RETURN_FALSE;
+            break;
+    }
+}
+/* }}} */
+
+/* {{{ 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, pg_null_as_len, pg_fields_len;
+    char *query, *fields;
+    PGresult *pgsql_result;
+    ExecStatusType status;
+    zval *zcontext = NULL;
+
+    php_stream_context *context = NULL;
+
+
+    size_t line_len = 0;
+    php_stream *stream;
+    char *buf = NULL;
+
+    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;
+    }
+
+    if (!pg_delim) {
+        pg_delim = "\t";
+    }
+    if (pg_null_as_len<=0) {
+        pg_null_as = safe_estrdup("\\\\N");
+    }
+
+    if(pg_fields_len>0) {
+        spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, *pg_delim, pg_null_as);
+    }
+    else {
+        spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
+    }
+
+    // Obtain db Handler
+    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+    PDO_CONSTRUCT_CHECK;
+
+    H = (pdo_pgsql_db_handle *)dbh->driver_data;
+    // H->server is the db handle for libPQ
+
+
+    // Check filename
+    context = php_stream_context_from_zval(zcontext, 0);
+
+    stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context);
+    if (!stream) {
+		pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
+        RETURN_FALSE;
+    }
+
+
+
+    while ((pgsql_result = PQgetResult(H->server))) {
+        PQclear(pgsql_result);
+    }
+    pgsql_result = PQexec(H->server, query);
+
+    if (pg_null_as_len<=0) {
+        efree(pg_null_as);
+    }
+    efree(query);
+
+    if (pgsql_result) {
+        status = PQresultStatus(pgsql_result);
+    } else {
+        status = (ExecStatusType) PQstatus(H->server);
+    }
+
+    switch (status) {
+        case PGRES_COPY_IN:
+            if (pgsql_result) {
+                int command_failed = 0;
+                PQclear(pgsql_result);
+                buf = php_stream_get_line(stream, NULL, 0, &line_len);
+				while(buf != NULL) {
+                    if (PQputCopyData(H->server, buf, strlen(buf)) != 1) {
+                        efree(buf);
+                        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
+			    		php_stream_close(stream);
+                        RETURN_FALSE;
+                    }
+                    efree(buf);
+                    buf = php_stream_get_line(stream, NULL, 0, &line_len);
+                } 
+
+                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);
+                }
+                if (command_failed) {
+                    RETURN_FALSE;
+                }
+            } else {
+                PQclear(pgsql_result);
+                RETURN_FALSE;
+            }
+            RETURN_TRUE;
+            break;
+        default:
+            PQclear(pgsql_result);
+            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+			php_stream_close(stream);
+            RETURN_FALSE;
+            break;
+    }
+}
+/* }}} */
+
+
+/* {{{ 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, pg_null_as_len, pg_fields_len, filename_len;
+    char *query;
+    int id = -1;
+
+    PGresult *pgsql_result;
+    ExecStatusType status;
+    int copydone = 0;
+    char *csv = (char *)NULL;
+    int ret, bytes_written;
+
+    zval *zcontext = NULL;
+    php_stream *stream;
+
+    php_stream_context *context = NULL;
+
+
+    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;
+    }
+
+
+    if (!pg_delim) {
+        pg_delim = "\t";
+    }
+
+    if (pg_null_as_len <= 0) {
+        pg_null_as = safe_estrdup("\\\\N");
+    }
+
+    if(pg_fields_len>0) {
+        spprintf(&query, 0, "COPY %s (%s) TO STDOUT DELIMITERS '%c' WITH NULL AS '%s'", table_name, pg_fields, *pg_delim, pg_null_as);
+    }
+    else {
+        spprintf(&query, 0, "COPY %s TO STDOUT DELIMITERS '%c' WITH NULL AS '%s'", table_name, *pg_delim, pg_null_as);
+    }
+
+    // Obtain db Handler
+    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+    PDO_CONSTRUCT_CHECK;
+
+    H = (pdo_pgsql_db_handle *)dbh->driver_data;
+    // H->server is the db handle for libPQ
+
+    // Check and open filename for writing
+    context = php_stream_context_from_zval(zcontext, 0);
+
+    stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, 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);
+    }
+    pgsql_result = PQexec(H->server, query);
+
+
+
+    if (pg_null_as_len <= 0) {
+        efree(pg_null_as);
+    }
+
+    efree(query);
+
+    if (pgsql_result) {
+        status = PQresultStatus(pgsql_result);
+    } else {
+        status = (ExecStatusType) PQstatus(H->server);
+    }
+
+
+    switch (status) {
+        case PGRES_COPY_OUT:
+            if (pgsql_result) {
+                PQclear(pgsql_result);
+                array_init(return_value);
+
+                while (!copydone)
+                {
+                    ret = PQgetCopyData(H->server, &csv, 0);
+                    switch (ret) {
+                        case -1:
+                            copydone = 1;
+                            break;
+                        case 0:
+                        case -2:
+                            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
+                            php_stream_close(stream);
+                            RETURN_FALSE;
+                            break;
+                        default:
+                            bytes_written = php_stream_write(stream, csv, ret);
+                            if(bytes_written != ret) {
+                                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
+                                PQfreemem(csv);
+                                php_stream_close(stream);
+                                RETURN_FALSE;
+                            }
+                            PQfreemem(csv);
+                            break;
+                    }
+                }
+                php_stream_close(stream);
+
+                while ((pgsql_result = PQgetResult(H->server))) {
+                    PQclear(pgsql_result);
+                }
+                RETURN_TRUE;
+            } else {
+                php_stream_close(stream);
+                PQclear(pgsql_result);
+                RETURN_FALSE;
+            }
+            break;
+        default:
+            PQclear(pgsql_result);
+            php_stream_close(stream);
+            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+            RETURN_FALSE;
+            break;
+    }
+}
+/* }}} */
+
+
+
+/* {{{ 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, pg_null_as_len, pg_fields_len;
+    char *query;
+    int id = -1;
+
+    PGresult *pgsql_result;
+    ExecStatusType status;
+    int copydone = 0;
+    char *csv = (char *)NULL;
+    int ret;
+
+    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;
+    }
+
+
+    if (!pg_delim) {
+        pg_delim = "\t";
+    }
+
+    if (pg_null_as_len <= 0) {
+        pg_null_as = safe_estrdup("\\\\N");
+    }
+
+    if(pg_fields_len>0) {
+        spprintf(&query, 0, "COPY %s (%s) TO STDOUT DELIMITERS '%c' WITH NULL AS '%s'", table_name, pg_fields, *pg_delim, pg_null_as);
+    }
+    else {
+        spprintf(&query, 0, "COPY %s TO STDOUT DELIMITERS '%c' WITH NULL AS '%s'", table_name, *pg_delim, pg_null_as);
+    }
+
+    // Obtain db Handler
+    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+    PDO_CONSTRUCT_CHECK;
+
+    H = (pdo_pgsql_db_handle *)dbh->driver_data;
+    // H->server is the db handle for libPQ
+
+
+
+    while ((pgsql_result = PQgetResult(H->server))) {
+        PQclear(pgsql_result);
+    }
+    pgsql_result = PQexec(H->server, query);
+
+
+
+	if (pg_null_as_len <= 0) {
+    	efree(pg_null_as);
+	}
+
+    efree(query);
+
+    if (pgsql_result) {
+        status = PQresultStatus(pgsql_result);
+    } else {
+        status = (ExecStatusType) PQstatus(H->server);
+    }
+
+
+    switch (status) {
+        case PGRES_COPY_OUT:
+            if (pgsql_result) {
+                PQclear(pgsql_result);
+                array_init(return_value);
+
+                while (!copydone)
+                {
+                    ret = PQgetCopyData(H->server, &csv, 0);
+                    switch (ret) {
+                        case -1:
+                            copydone = 1;
+                            break;
+                        case 0:
+                        case -2:
+                            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
+                            RETURN_FALSE;
+                            break;
+                        default:
+                            add_next_index_string(return_value, csv, 1);
+                            PQfreemem(csv);
+                            break;
+                    }
+                }
+                while ((pgsql_result = PQgetResult(H->server))) {
+                    PQclear(pgsql_result);
+                }
+            } else {
+                PQclear(pgsql_result);
+                RETURN_FALSE;
+            }
+            break;
+        default:
+            PQclear(pgsql_result);
+            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+            RETURN_FALSE;
+            break;
+    }
+}
+/* }}} */
+
+
 /* {{{ 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 +1113,11 @@
 	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, pgsqlIsInTransaction, 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 -u php-src-5.3/ext/pdo_pgsql/pgsql_statement.c php-src-5.3.patched/ext/pdo_pgsql/pgsql_statement.c
--- php-src-5.3/ext/pdo_pgsql/pgsql_statement.c	2010-05-24 22:22:27.843522641 +0200
+++ php-src-5.3.patched/ext/pdo_pgsql/pgsql_statement.c	2010-05-24 10:20:41.464582224 +0200
@@ -556,47 +556,9 @@
 	return 1;
 }
 
-static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
-{
-	pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
-	PGresult *res;
-	char *q=NULL;
-	ExecStatusType status;
-	
-	if (!S->result) {
-		return FAILURE;
-	}
-	
-	if (colno >= stmt->column_count) {
-		return FAILURE;
-	}
-	
-	array_init(return_value);
-	add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type);
-
-	/* Fetch metadata from Postgres system catalogue */
-	spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%d", S->cols[colno].pgsql_type);
-	res = PQexec(S->H->server, q);
-	efree(q);
-	
-	status = PQresultStatus(res);
-	
-	if (status != PGRES_TUPLES_OK) {
-		/* Failed to get system catalogue, but return success
-		 * with the data we have collected so far
-		 */
-		goto done;
-	}
 
-	/* We want exactly one row returned */
-	if (1 != PQntuples(res)) {
-		goto done;
-	}
-
-	add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0), 1);
-done:
-	PQclear(res);		
-	return 1;
+static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC) {
+	return _pgsql_stmt_get_column_meta(stmt, colno, return_value );
 }
 
 static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
diff -u php-src-5.3/ext/pdo_pgsql/php_pdo_pgsql.h php-src-5.3.patched/ext/pdo_pgsql/php_pdo_pgsql.h
--- php-src-5.3/ext/pdo_pgsql/php_pdo_pgsql.h	2010-05-24 22:22:27.833522150 +0200
+++ php-src-5.3.patched/ext/pdo_pgsql/php_pdo_pgsql.h	2010-05-24 10:20:38.404427954 +0200
@@ -30,6 +30,8 @@
 #include "TSRM.h"
 #endif
 
+#define PHP_PDO_PGSQL_RES_NAME "PHP PDO RES NAME"
+
 PHP_MINIT_FUNCTION(pdo_pgsql);
 PHP_MSHUTDOWN_FUNCTION(pdo_pgsql);
 PHP_RINIT_FUNCTION(pdo_pgsql);
diff -u php-src-5.3/ext/pdo_pgsql/php_pdo_pgsql_int.h php-src-5.3.patched/ext/pdo_pgsql/php_pdo_pgsql_int.h
--- php-src-5.3/ext/pdo_pgsql/php_pdo_pgsql_int.h	2010-05-24 22:22:27.833522150 +0200
+++ php-src-5.3.patched/ext/pdo_pgsql/php_pdo_pgsql_int.h	2010-05-24 10:20:34.674239905 +0200
@@ -87,6 +87,8 @@
 #define pdo_pgsql_error(d,e,z)	_pdo_pgsql_error(d, NULL, e, z, __FILE__, __LINE__ TSRMLS_CC)
 #define pdo_pgsql_error_stmt(s,e,z)	_pdo_pgsql_error(s->dbh, s, e, z, __FILE__, __LINE__ TSRMLS_CC)
 
+extern int _pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC);
+
 extern struct pdo_stmt_methods pgsql_stmt_methods;
 
 #define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE)
@@ -95,6 +97,14 @@
 	PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT = PDO_ATTR_DRIVER_SPECIFIC,
 };
 
+enum pdo_pgsql_specific_constants {
+	PGSQL_TRANSACTION_IDLE = PQTRANS_IDLE,
+	PGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE,
+	PGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS,
+	PGSQL_TRANSACTION_INERROR = PQTRANS_INERROR,
+	PGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN
+};
+
 struct pdo_pgsql_lob_self {
 	pdo_dbh_t *dbh;
 	PGconn *conn;

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to