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" <[email protected]>
> A: "Ilia Alshanetsky" <[email protected]>
> Cc: [email protected]
> 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" <[email protected]>
> > A: "Denis Gasparin" <[email protected]>
> > Cc: [email protected]
> > 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 <
> > [email protected] > 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