mbeccati Sat Mar 28 02:34:02 2009 UTC Removed files: /php-src/ext/pdo_pgsql package.xml
Modified files: /php-src/ext/pdo_pgsql package2.xml config.m4 config.w32 pdo_pgsql.c pgsql_driver.c pgsql_statement.c php_pdo_pgsql.h php_pdo_pgsql_int.h /php-src/ext/pdo_pgsql/tests bug46274.phpt bug46274_2.phpt common.phpt Log: MFB: - Updated pdo_pgsql from PHP_5_3
http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/package2.xml?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/pdo_pgsql/package2.xml diff -u /dev/null php-src/ext/pdo_pgsql/package2.xml:1.2 --- /dev/null Sat Mar 28 02:34:02 2009 +++ php-src/ext/pdo_pgsql/package2.xml Sat Mar 28 02:34:02 2009 @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package packagerversion="1.4.2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 +http://pear.php.net/dtd/tasks-1.0.xsd +http://pear.php.net/dtd/package-2.0 +http://pear.php.net/dtd/package-2.0.xsd"> + <name>PDO_PGSQL</name> + <channel>pecl.php.net</channel> + <summary>PostgreSQL driver for PDO</summary> + <description>This extension provides an PostgreSQL driver for PDO. + </description> + <lead> + <name>Edin Kadribasic</name> + <user>edink</user> + <email>ed...@php.net</email> + <active>yes</active> + </lead> + <lead> + <name>Ilia Alshanetsky</name> + <user>iliaa</user> + <email>il...@php.net</email> + <active>yes</active> + </lead> + <lead> + <name>Wez Furlong</name> + <user>wez</user> + <email>w...@php.net</email> + <active>yes</active> + </lead> + <date>2006-05-01</date> + <version> + <release>1.0.2</release> + <api>1.0.2</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.php.net/license">PHP</license> + <notes> +This PECL release corresponds to PHP 5.1.3. + +- Fixed bug #36727 (segfault in pdo_pgsql bindValue() when no parameters are + defined). (Tony) +- Fixed bug #36382 (PDO/PgSQL's getColumnMeta() crashes). (Derick) +- Fixed bug #36176 (PDO_PGSQL - PDO::exec() does not return number of rows + affected by the operation). (Ilia) +- Fixed prepared statement name conflict handling in PDO_PGSQL. (Thies, Ilia) +- repackage with package2.xml +- Added PDO::pgsqlLOBCreate(), PDO::pgsqlLOBOpen() and PDO::pgsqlLOBUnlink(). + +You require PostgreSQL client libraries installed on the machine where you +intend to build and/or use this package. + +If you are running on windows, you can download the binary from here: +http://pecl4win.php.net/ext.php/php_pdo_pgsql.dll + </notes> + <contents> + <dir name="/"> + <file name="config.m4" role="src" /> + <file name="config.w32" role="src" /> + <file name="CREDITS" role="doc" /> + <file name="pdo_pgsql.c" role="src" /> + <file name="pgsql_driver.c" role="src" /> + <file name="pgsql_statement.c" role="src" /> + <file name="php_pdo_pgsql.h" role="src" /> + <file name="php_pdo_pgsql_int.h" role="src" /> + </dir> <!-- / --> + </contents> + <dependencies> + <required> + <php> + <min>5.0.3</min> + </php> + <pearinstaller> + <min>1.4.0</min> + </pearinstaller> + <package> + <name>pdo</name> + <channel>pecl.php.net</channel> + <min>1.0.3</min> + <providesextension>PDO</providesextension> + </package> + </required> + </dependencies> + <providesextension>PDO_PGSQL</providesextension> + <extsrcrelease /> +</package> http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/config.m4?r1=1.20&r2=1.21&diff_format=u Index: php-src/ext/pdo_pgsql/config.m4 diff -u php-src/ext/pdo_pgsql/config.m4:1.20 php-src/ext/pdo_pgsql/config.m4:1.21 --- php-src/ext/pdo_pgsql/config.m4:1.20 Fri Jul 25 13:38:41 2008 +++ php-src/ext/pdo_pgsql/config.m4 Sat Mar 28 02:34:02 2009 @@ -1,4 +1,4 @@ -dnl $Id: config.m4,v 1.20 2008/07/25 13:38:41 jani Exp $ +dnl $Id: config.m4,v 1.21 2009/03/28 02:34:02 mbeccati Exp $ dnl config.m4 for extension pdo_pgsql dnl vim:et:sw=2:ts=2: http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/config.w32?r1=1.8&r2=1.9&diff_format=u Index: php-src/ext/pdo_pgsql/config.w32 diff -u php-src/ext/pdo_pgsql/config.w32:1.8 php-src/ext/pdo_pgsql/config.w32:1.9 --- php-src/ext/pdo_pgsql/config.w32:1.8 Wed Oct 11 11:00:37 2006 +++ php-src/ext/pdo_pgsql/config.w32 Sat Mar 28 02:34:02 2009 @@ -1,11 +1,11 @@ -// $Id: config.w32,v 1.8 2006/10/11 11:00:37 edink Exp $ +// $Id: config.w32,v 1.9 2009/03/28 02:34:02 mbeccati Exp $ // vim:ft=javascript ARG_WITH("pdo-pgsql", "PostgreSQL support for PDO", "no"); if (PHP_PDO_PGSQL != "no") { if (CHECK_LIB("libpq.lib", "pdo_pgsql", PHP_PDO_PGSQL) && - CHECK_HEADER_ADD_INCLUDE("libpq-fe.h", "CFLAGS_PDO_PGSQL", PHP_PDO_PGSQL + ";" + PHP_PHP_BUILD + "\\include\\pgsql")) { + CHECK_HEADER_ADD_INCLUDE("libpq-fe.h", "CFLAGS_PDO_PGSQL", PHP_PDO_PGSQL + ";" + PHP_PHP_BUILD + "\\include\\pgsql;" + PHP_PHP_BUILD + "\\include\\libpq;")) { EXTENSION("pdo_pgsql", "pdo_pgsql.c pgsql_driver.c pgsql_statement.c"); if (CHECK_HEADER_ADD_INCLUDE("pg_config.h", "CFLAGS_PDO_PGSQL", PHP_PDO_PGSQL + ";" + PHP_PHP_BUILD + "\\include\\pgsql")) { @@ -14,8 +14,8 @@ AC_DEFINE('HAVE_PDO_PGSQL', 1, 'Have PostgreSQL library'); ADD_FLAG('CFLAGS_PDO_PGSQL', "/D HAVE_PQPARAMETERSTATUS=1 /D HAVE_PQPROTOCOLVERSION=1 /D HAVE_PGTRANSACTIONSTATUS=1 /D HAVE_PQUNESCAPEBYTEA=1 /D HAVE_PQRESULTERRORFIELD=1 /D HAVE_PQESCAPE_CONN=1 /D HAVE_PQESCAPE_BYTEA_CONN=1"); + ADD_EXTENSION_DEP('pdo_pgsql', 'pdo'); } else { WARNING("pdo_pgsql not enabled; libraries and headers not found"); } - ADD_EXTENSION_DEP('pdo_pgsql', 'pdo'); } http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/pdo_pgsql.c?r1=1.19&r2=1.20&diff_format=u Index: php-src/ext/pdo_pgsql/pdo_pgsql.c diff -u php-src/ext/pdo_pgsql/pdo_pgsql.c:1.19 php-src/ext/pdo_pgsql/pdo_pgsql.c:1.20 --- php-src/ext/pdo_pgsql/pdo_pgsql.c:1.19 Tue Mar 10 23:39:31 2009 +++ php-src/ext/pdo_pgsql/pdo_pgsql.c Sat Mar 28 02:34:02 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_pgsql.c,v 1.19 2009/03/10 23:39:31 helly Exp $ */ +/* $Id: pdo_pgsql.c,v 1.20 2009/03/28 02:34:02 mbeccati Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -70,7 +70,7 @@ PHP_RINIT(pdo_pgsql), PHP_RSHUTDOWN(pdo_pgsql), PHP_MINFO(pdo_pgsql), - "0.9", + "1.0.2", STANDARD_MODULE_PROPERTIES }; /* }}} */ @@ -86,7 +86,7 @@ PHP_MINIT_FUNCTION(pdo_pgsql) { php_pdo_register_driver(&pdo_pgsql_driver); - REGISTER_PDO_CONST_LONG("PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT", PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT", PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT); return SUCCESS; } /* }}} */ @@ -128,7 +128,7 @@ php_info_print_table_row(2, "PostgreSQL(libpq) Version", PG_VERSION); #endif php_info_print_table_row(2, "Module version", pdo_pgsql_module_entry.version); - php_info_print_table_row(2, "Revision", " $Id: pdo_pgsql.c,v 1.19 2009/03/10 23:39:31 helly Exp $ "); + php_info_print_table_row(2, "Revision", " $Id: pdo_pgsql.c,v 1.20 2009/03/28 02:34:02 mbeccati Exp $ "); php_info_print_table_end(); } http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/pgsql_driver.c?r1=1.70&r2=1.71&diff_format=u Index: php-src/ext/pdo_pgsql/pgsql_driver.c diff -u php-src/ext/pdo_pgsql/pgsql_driver.c:1.70 php-src/ext/pdo_pgsql/pgsql_driver.c:1.71 --- php-src/ext/pdo_pgsql/pgsql_driver.c:1.70 Tue Mar 10 23:39:31 2009 +++ php-src/ext/pdo_pgsql/pgsql_driver.c Sat Mar 28 02:34:02 2009 @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pgsql_driver.c,v 1.70 2009/03/10 23:39:31 helly Exp $ */ +/* $Id: pgsql_driver.c,v 1.71 2009/03/28 02:34:02 mbeccati Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -115,6 +115,81 @@ } /* }}} */ +/* {{{ pdo_pgsql_create_lob_stream */ +static size_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_write(self->conn, self->lfd, (char*)buf, count); +} + +static size_t pgsql_lob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + return lo_read(self->conn, self->lfd, buf, count); +} + +static int pgsql_lob_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + pdo_dbh_t *dbh = self->dbh; + + if (close_handle) { + lo_close(self->conn, self->lfd); + } + efree(self); + php_pdo_dbh_delref(dbh TSRMLS_CC); + return 0; +} + +static int pgsql_lob_flush(php_stream *stream TSRMLS_DC) +{ + return 0; +} + +static int pgsql_lob_seek(php_stream *stream, off_t offset, int whence, + off_t *newoffset TSRMLS_DC) +{ + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + int pos = lo_lseek(self->conn, self->lfd, offset, whence); + *newoffset = pos; + return pos >= 0 ? 0 : -1; +} + +php_stream_ops pdo_pgsql_lob_stream_ops = { + pgsql_lob_write, + pgsql_lob_read, + pgsql_lob_close, + pgsql_lob_flush, + "pdo_pgsql lob stream", + pgsql_lob_seek, + NULL, + NULL, + NULL +}; + +php_stream *pdo_pgsql_create_lob_stream(pdo_dbh_t *dbh, int lfd, Oid oid TSRMLS_DC) +{ + php_stream *stm; + struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + self->dbh = dbh; + self->lfd = lfd; + self->oid = oid; + self->conn = H->server; + + stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); + + if (stm) { + php_pdo_dbh_addref(dbh TSRMLS_CC); + return stm; + } + + efree(self); + return NULL; +} +/* }}} */ + static int pgsql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ { pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; @@ -140,11 +215,10 @@ pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); int scrollable; #if HAVE_PQPREPARE - PGresult *res; int ret; char *nsql = NULL; int nsql_len = 0; - ExecStatusType status; + int emulate = 0; #endif S->H = H; @@ -163,8 +237,18 @@ } #if HAVE_PQPREPARE - if (!driver_options || pdo_attr_lval(driver_options, - PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, 0 TSRMLS_CC) == 0) { + + if (driver_options) { + if (pdo_attr_lval(driver_options, + PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, 0 TSRMLS_CC) == 1) { + emulate = 1; + } else if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, + 0 TSRMLS_CC) == 1) { + emulate = 1; + } + } + + if (!emulate && PQprotocolVersion(H->server) > 2) { stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; stmt->named_rewrite_template = "$%d"; ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC); @@ -179,41 +263,15 @@ } spprintf(&S->stmt_name, 0, "pdo_pgsql_stmt_%08x", (unsigned int)stmt); - res = PQprepare(H->server, S->stmt_name, sql, 0, NULL); + /* that's all for now; we'll defer the actual prepare until the first execute call */ + if (nsql) { - efree(nsql); - } - if (!res) { - pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); - return 0; + S->query = nsql; + } else { + S->query = estrdup(sql); } - /* check if the connection is using protocol version 2.0. - * if that is the reason that the prepare failed, we want to fall - * through and let PDO emulate it for us */ - status = PQresultStatus(res); - switch (status) { - case PGRES_COMMAND_OK: - case PGRES_TUPLES_OK: - /* it worked */ - PQclear(res); - return 1; - - case PGRES_BAD_RESPONSE: - /* server is probably too old; fall through and let - * PDO emulate it */ - efree(S->stmt_name); - S->stmt_name = NULL; - PQclear(res); - break; - - default: - /* protocol 3.0 and above; hard error */ - pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); - PQclear(res); - return 0; - } - /* fall through */ + return 1; } #endif @@ -418,6 +476,17 @@ return 1; } +/* {{{ */ +static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (PQstatus(H->server) == CONNECTION_BAD) { + PQreset(H->server); + } + return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; +} +/* }}} */ + static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh TSRMLS_DC) { pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; @@ -450,6 +519,131 @@ return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC); } +/* {{{ proto string PDO::pgsqlLOBCreate() + Creates a new large object, returning its identifier. Must be called inside a transaction. */ +static PHP_METHOD(PDO, pgsqlLOBCreate) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid lfd; + + dbh = zend_object_store_get_object(getThis() TSRMLS_CC); + PDO_CONSTRUCT_CHECK; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + lfd = lo_creat(H->server, INV_READ|INV_WRITE); + + if (lfd != InvalidOid) { + char *buf; + spprintf(&buf, 0, "%lu", (long) lfd); + RETURN_STRING(buf, 0); + } + + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb']) + Opens an existing large object stream. Must be called inside a transaction. */ +static PHP_METHOD(PDO, pgsqlLOBOpen) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + int lfd; + char *oidstr; + int oidstrlen; + char *modestr = "rb"; + int modestrlen; + int mode = INV_READ; + char *end_ptr; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", + &oidstr, &oidstrlen, &modestr, &modestrlen)) { + RETURN_FALSE; + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + if (strpbrk(modestr, "+w")) { + mode = INV_READ|INV_WRITE; + } + + dbh = zend_object_store_get_object(getThis() TSRMLS_CC); + PDO_CONSTRUCT_CHECK; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + lfd = lo_open(H->server, oid, mode); + + if (lfd >= 0) { + php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC); + if (stream) { + php_stream_to_zval(stream, return_value); + return; + } + } else { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); + } + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto bool PDO::pgsqlLOBUnlink(string oid) + Deletes the large object identified by oid. Must be called inside a transaction. */ +static PHP_METHOD(PDO, pgsqlLOBUnlink) +{ + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + Oid oid; + char *oidstr, *end_ptr; + int oidlen; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", + &oidstr, &oidlen)) { + RETURN_FALSE; + } + + oid = (Oid)strtoul(oidstr, &end_ptr, 10); + if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { + RETURN_FALSE; + } + + dbh = zend_object_store_get_object(getThis() TSRMLS_CC); + PDO_CONSTRUCT_CHECK; + + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if (1 == lo_unlink(H->server, oid)) { + RETURN_TRUE; + } + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); + RETURN_FALSE; +} +/* }}} */ + + +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) + {NULL, NULL, NULL} +}; + +static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC) +{ + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return dbh_methods; + default: + return NULL; + } +} + static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC) { return 0; @@ -467,8 +661,8 @@ pdo_pgsql_last_insert_id, pdo_pgsql_fetch_error_func, pdo_pgsql_get_attribute, - NULL, /* check_liveness */ - NULL /* get_driver_methods */ + pdo_pgsql_check_liveness, /* check_liveness */ + pdo_pgsql_get_driver_methods /* get_driver_methods */ }; static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */ @@ -476,6 +670,7 @@ pdo_pgsql_db_handle *H; int ret = 0; char *conn_str, *p, *e; + long connect_timeout = 30; H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); dbh->driver_data = H; @@ -492,23 +687,25 @@ *p = ' '; } + if (driver_options) { + connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC); + } + /* support both full connection string & connection string + login and/or password */ if (dbh->username && dbh->password) { - spprintf(&conn_str, 0, "%s user=%s password=%s", dbh->data_source, dbh->username, dbh->password); + spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout); } else if (dbh->username) { - spprintf(&conn_str, 0, "%s user=%s", dbh->data_source, dbh->username); + spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout); } else if (dbh->password) { - spprintf(&conn_str, 0, "%s password=%s", dbh->data_source, dbh->password); + spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout); } else { - conn_str = (char *) dbh->data_source; + spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout); } H->server = PQconnectdb(conn_str); - - if (conn_str != dbh->data_source) { - efree(conn_str); - } - + + efree(conn_str); + if (PQstatus(H->server) != CONNECTION_OK) { pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); goto cleanup; http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/pgsql_statement.c?r1=1.50&r2=1.51&diff_format=u Index: php-src/ext/pdo_pgsql/pgsql_statement.c diff -u php-src/ext/pdo_pgsql/pgsql_statement.c:1.50 php-src/ext/pdo_pgsql/pgsql_statement.c:1.51 --- php-src/ext/pdo_pgsql/pgsql_statement.c:1.50 Tue Mar 10 23:39:31 2009 +++ php-src/ext/pdo_pgsql/pgsql_statement.c Sat Mar 28 02:34:02 2009 @@ -12,11 +12,13 @@ | obtain it through the world-wide-web, please send a note to | | lice...@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Edin Kadribasic <ed...@emini.dk> | + | Authors: Edin Kadribasic <ed...@emini.dk> | + | Ilia Alshanestsky <i...@prohost.org> | + | Wez Furlong <w...@php.net> | +----------------------------------------------------------------------+ */ -/* $Id: pgsql_statement.c,v 1.50 2009/03/10 23:39:31 helly Exp $ */ +/* $Id: pgsql_statement.c,v 1.51 2009/03/28 02:34:02 mbeccati Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -29,6 +31,9 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_pgsql.h" #include "php_pdo_pgsql_int.h" +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif /* from postgresql/src/include/catalog/pg_type.h */ #define BOOLOID 16 @@ -36,9 +41,9 @@ #define INT8OID 20 #define INT2OID 21 #define INT4OID 23 +#define TEXTOID 25 #define OIDOID 26 - static int pgsql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) { pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; @@ -51,6 +56,18 @@ #if HAVE_PQPREPARE if (S->stmt_name) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + if (S->is_prepared) { + spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, q); + efree(q); + if (res) { + PQclear(res); + } + } efree(S->stmt_name); S->stmt_name = NULL; } @@ -66,7 +83,14 @@ efree(S->param_formats); S->param_formats = NULL; } - + if (S->param_types) { + efree(S->param_types); + S->param_types = NULL; + } + if (S->query) { + efree(S->query); + S->query = NULL; + } #endif if (S->cursor_name) { @@ -97,12 +121,10 @@ pdo_pgsql_db_handle *H = S->H; ExecStatusType status; - if (stmt->executed) { - /* ensure that we free any previous unfetched results */ - if(S->result) { - PQclear(S->result); - S->result = NULL; - } + /* ensure that we free any previous unfetched results */ + if(S->result) { + PQclear(S->result); + S->result = NULL; } S->current_row = 0; @@ -111,13 +133,52 @@ if (S->stmt_name) { /* using a prepared statement */ + if (!S->is_prepared) { +stmt_retry: + /* we deferred the prepare until now, because we didn't + * know anything about the parameter types; now we do */ + S->result = PQprepare(H->server, S->stmt_name, S->query, + stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, + S->param_types); + status = PQresultStatus(S->result); + switch (status) { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + /* it worked */ + S->is_prepared = 1; + PQclear(S->result); + break; + default: { + char *sqlstate = pdo_pgsql_sqlstate(S->result); + /* 42P05 means that the prepared statement already existed. this can happen if you use + * a connection pooling software line pgpool which doesn't close the db-connection once + * php disconnects. if php dies (no chanche to run RSHUTDOWN) during execution it has no + * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we + * deallocate it and retry ONCE (thies 2005.12.15) + */ + if (!strcmp(sqlstate, "42P05")) { + char buf[100]; /* stmt_name == "pdo_pgsql_cursor_%08x" */ + PGresult *res; + snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, buf); + if (res) { + PQclear(res); + } + goto stmt_retry; + } else { + pdo_pgsql_error_stmt(stmt, status, sqlstate); + return 0; + } + } + } + } S->result = PQexecPrepared(H->server, S->stmt_name, stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, (const char**)S->param_values, S->param_lengths, - NULL, + S->param_formats, 0); } else #endif @@ -159,7 +220,13 @@ if (S->stmt_name && param->is_param) { switch (event_type) { - case PDO_PARAM_EVT_ALLOC: + case PDO_PARAM_EVT_FREE: + if (param->driver_data) { + efree(param->driver_data); + } + break; + + case PDO_PARAM_EVT_NORMALIZE: /* decode name from $1, $2 into 0, 1 etc. */ if (param->name) { if (param->name[0] == '$') { @@ -178,6 +245,13 @@ } break; + case PDO_PARAM_EVT_ALLOC: + case PDO_PARAM_EVT_EXEC_POST: + case PDO_PARAM_EVT_FETCH_PRE: + case PDO_PARAM_EVT_FETCH_POST: + /* work is handled by EVT_NORMALIZE */ + return 1; + case PDO_PARAM_EVT_EXEC_PRE: if (!stmt->bound_param_map) { return 0; @@ -192,9 +266,54 @@ S->param_formats = ecalloc( zend_hash_num_elements(stmt->bound_param_map), sizeof(int)); - + S->param_types = ecalloc( + zend_hash_num_elements(stmt->bound_param_map), + sizeof(Oid)); } if (param->paramno >= 0) { + if (param->paramno > zend_hash_num_elements(stmt->bound_param_map)) { + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + return 0; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && + Z_TYPE_P(param->parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_from_zval_no_verify(stm, ¶m->parameter); + if (stm) { + if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; + pdo_pgsql_bound_param *P = param->driver_data; + + if (P == NULL) { + P = ecalloc(1, sizeof(*P)); + param->driver_data = P; + } + P->oid = htonl(self->oid); + S->param_values[param->paramno] = (char*)&P->oid; + S->param_lengths[param->paramno] = sizeof(P->oid); + S->param_formats[param->paramno] = 1; + S->param_types[param->paramno] = OIDOID; + return 1; + } else { + int len; + + SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); + Z_TYPE_P(param->parameter) = IS_STRING; + + if ((len = php_stream_copy_to_mem(stm, &Z_STRVAL_P(param->parameter), PHP_STREAM_COPY_ALL, 0)) > 0) { + Z_STRLEN_P(param->parameter) = len; + } else { + ZVAL_EMPTY_STRING(param->parameter); + } + } + } else { + /* expected a stream resource */ + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + return 0; + } + } + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || Z_TYPE_P(param->parameter) == IS_NULL) { S->param_values[param->paramno] = NULL; @@ -202,13 +321,20 @@ } else if (Z_TYPE_P(param->parameter) == IS_BOOL) { S->param_values[param->paramno] = Z_BVAL_P(param->parameter) ? "t" : "f"; S->param_lengths[param->paramno] = 1; - S->param_formats[param->paramno] = 1; + S->param_formats[param->paramno] = 0; } else { SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); convert_to_string(param->parameter); S->param_values[param->paramno] = Z_STRVAL_P(param->parameter); S->param_lengths[param->paramno] = Z_STRLEN_P(param->parameter); + S->param_formats[param->paramno] = 0; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + S->param_types[param->paramno] = 0; S->param_formats[param->paramno] = 1; + } else { + S->param_types[param->paramno] = 0; } } break; @@ -263,6 +389,7 @@ { pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; struct pdo_column_data *cols = stmt->columns; + struct pdo_bound_param_data *param; if (!S->result) { return 0; @@ -279,10 +406,25 @@ case BOOLOID: cols[colno].param_type = PDO_PARAM_BOOL; break; + + case OIDOID: + /* did the user bind the column as a LOB ? */ + if (stmt->bound_columns && ( + SUCCESS == zend_hash_index_find(stmt->bound_columns, + colno, (void**)¶m) || + SUCCESS == zend_hash_find(stmt->bound_columns, + cols[colno].name, cols[colno].namelen, + (void**)¶m))) { + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + cols[colno].param_type = PDO_PARAM_LOB; + break; + } + } + cols[colno].param_type = PDO_PARAM_INT; + break; case INT2OID: case INT4OID: - case OIDOID: cols[colno].param_type = PDO_PARAM_INT; break; @@ -442,14 +584,31 @@ break; case PDO_PARAM_LOB: - *ptr = php_pdo_pgsql_unescape_bytea(*ptr, &tmp_len); - *len = tmp_len; - *caller_frees = 1; + if (S->cols[colno].pgsql_type == OIDOID) { + /* ooo, a real large object */ + char *end_ptr; + Oid oid = (Oid)strtoul(*ptr, &end_ptr, 10); + int loid = lo_open(S->H->server, oid, INV_READ); + if (loid >= 0) { + *ptr = (char*)pdo_pgsql_create_lob_stream(stmt->dbh, loid, oid TSRMLS_CC); + *len = 0; + return *ptr ? 1 : 0; + } + *ptr = NULL; + *len = 0; + return 0; + } else { + *ptr = php_pdo_pgsql_unescape_bytea(*ptr, &tmp_len); + *len = tmp_len; + *caller_frees = 1; + } break; case PDO_PARAM_NULL: case PDO_PARAM_STR: case PDO_PARAM_STMT: case PDO_PARAM_INPUT_OUTPUT: + case PDO_PARAM_ZVAL: + default: break; } } @@ -486,27 +645,23 @@ /* Failed to get system catalogue, but return success * with the data we have collected so far */ - PQclear(res); - return 1; + goto done; } /* We want exactly one row returned */ if (1 != PQntuples(res)) { - PQclear(res); - return 1; + goto done; } add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0), 1); - +done: PQclear(res); return 1; } static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) { -#if HAVE_PQPREPARE return 1; -#endif } struct pdo_stmt_methods pgsql_stmt_methods = { http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/php_pdo_pgsql.h?r1=1.9&r2=1.10&diff_format=u Index: php-src/ext/pdo_pgsql/php_pdo_pgsql.h diff -u php-src/ext/pdo_pgsql/php_pdo_pgsql.h:1.9 php-src/ext/pdo_pgsql/php_pdo_pgsql.h:1.10 --- php-src/ext/pdo_pgsql/php_pdo_pgsql.h:1.9 Tue Mar 10 23:39:31 2009 +++ php-src/ext/pdo_pgsql/php_pdo_pgsql.h Sat Mar 28 02:34:02 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_pdo_pgsql.h,v 1.9 2009/03/10 23:39:31 helly Exp $ */ +/* $Id: php_pdo_pgsql.h,v 1.10 2009/03/28 02:34:02 mbeccati Exp $ */ #ifndef PHP_PDO_PGSQL_H #define PHP_PDO_PGSQL_H http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/php_pdo_pgsql_int.h?r1=1.18&r2=1.19&diff_format=u Index: php-src/ext/pdo_pgsql/php_pdo_pgsql_int.h diff -u php-src/ext/pdo_pgsql/php_pdo_pgsql_int.h:1.18 php-src/ext/pdo_pgsql/php_pdo_pgsql_int.h:1.19 --- php-src/ext/pdo_pgsql/php_pdo_pgsql_int.h:1.18 Tue Mar 10 23:39:31 2009 +++ php-src/ext/pdo_pgsql/php_pdo_pgsql_int.h Sat Mar 28 02:34:02 2009 @@ -12,16 +12,19 @@ | obtain it through the world-wide-web, please send a note to | | lice...@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Edin Kadribasic <ed...@emini.dk> | + | Authors: Edin Kadribasic <ed...@emini.dk> | + | Ilia Alshanestsky <i...@prohost.org> | + | Wez Furlong <w...@php.net> | +----------------------------------------------------------------------+ */ -/* $Id: php_pdo_pgsql_int.h,v 1.18 2009/03/10 23:39:31 helly Exp $ */ +/* $Id: php_pdo_pgsql_int.h,v 1.19 2009/03/28 02:34:02 mbeccati Exp $ */ #ifndef PHP_PDO_PGSQL_INT_H #define PHP_PDO_PGSQL_INT_H #include <libpq-fe.h> +#include <libpq/libpq-fs.h> #include <php.h> #define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE "08006" @@ -57,17 +60,17 @@ char *cursor_name; #if HAVE_PQPREPARE char *stmt_name; + char *query; char **param_values; int *param_lengths; int *param_formats; + Oid *param_types; + zend_bool is_prepared; #endif } pdo_pgsql_stmt; typedef struct { - char *repr; - long repr_len; - int pgsql_type; - void *thing; /* for LOBS, REFCURSORS etc. */ + Oid oid; } pdo_pgsql_bound_param; extern pdo_driver_t pdo_pgsql_driver; @@ -88,6 +91,17 @@ PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT = PDO_ATTR_DRIVER_SPECIFIC, }; +struct pdo_pgsql_lob_self { + pdo_dbh_t *dbh; + PGconn *conn; + int lfd; + Oid oid; +}; + + +php_stream *pdo_pgsql_create_lob_stream(pdo_dbh_t *stmt, int lfd, Oid oid TSRMLS_DC); +extern php_stream_ops pdo_pgsql_lob_stream_ops; + #endif /* PHP_PDO_PGSQL_INT_H */ /* http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/tests/bug46274.phpt?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/pdo_pgsql/tests/bug46274.phpt diff -u /dev/null php-src/ext/pdo_pgsql/tests/bug46274.phpt:1.2 --- /dev/null Sat Mar 28 02:34:02 2009 +++ php-src/ext/pdo_pgsql/tests/bug46274.phpt Sat Mar 28 02:34:02 2009 @@ -0,0 +1,85 @@ +--TEST-- +Bug #46274 (pdo_pgsql - Segfault when using PDO::ATTR_STRINGIFY_FETCHES and blob) +--SKIPIF-- +<?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_STRINGIFY_FETCHES, true); + +$db->query('CREATE TABLE test_one_blob (id SERIAL NOT NULL, blob1 BYTEA)'); + +$stmt = $db->prepare("INSERT INTO test_one_blob (blob1) VALUES (:foo)"); + +$data = 'foo'; +$blob = fopen('php://memory', 'a'); +fwrite($blob, $data); +rewind($blob); + +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$blob = ''; +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$data = ''; +$blob = fopen('php://memory', 'a'); +fwrite($blob, $data); +rewind($blob); + +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$blob = NULL; +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$res = $db->query("SELECT blob1 from test_one_blob"); +// Resource +var_dump($res->fetch()); + +// Empty string +var_dump($res->fetch()); + +// Empty string +var_dump($res->fetch()); + +// NULL +var_dump($res->fetch()); + +$db->query('DROP TABLE test_one_blob'); + +?> +--EXPECTF-- +array(2) { + ["blob1"]=> + string(3) "foo" + [0]=> + string(3) "foo" +} +array(2) { + ["blob1"]=> + string(0) "" + [0]=> + string(0) "" +} +array(2) { + ["blob1"]=> + string(0) "" + [0]=> + string(0) "" +} +array(2) { + ["blob1"]=> + NULL + [0]=> + NULL +} http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/tests/bug46274_2.phpt?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/pdo_pgsql/tests/bug46274_2.phpt diff -u /dev/null php-src/ext/pdo_pgsql/tests/bug46274_2.phpt:1.2 --- /dev/null Sat Mar 28 02:34:02 2009 +++ php-src/ext/pdo_pgsql/tests/bug46274_2.phpt Sat Mar 28 02:34:02 2009 @@ -0,0 +1,87 @@ +--TEST-- +Bug #46274 (pdo_pgsql - Segfault when using PDO::ATTR_STRINGIFY_FETCHES and blob) +--SKIPIF-- +<?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_STRINGIFY_FETCHES, false); + +$db->query('CREATE TABLE test_one_blob (id SERIAL NOT NULL, blob1 BYTEA)'); + +$stmt = $db->prepare("INSERT INTO test_one_blob (blob1) VALUES (:foo)"); + +$data = 'foo'; +$blob = fopen('php://memory', 'a'); +fwrite($blob, $data); +rewind($blob); + +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$blob = ''; +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$data = ''; +$blob = fopen('php://memory', 'a'); +fwrite($blob, $data); +rewind($blob); + +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$blob = NULL; +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$res = $db->query("SELECT blob1 from test_one_blob"); +// Resource +var_dump($x = $res->fetch()); +var_dump(fread($x['blob1'], 10)); + +// Empty string +var_dump($res->fetch()); + +// Empty string +var_dump($res->fetch()); + +// NULL +var_dump($res->fetch()); + +$db->query('DROP TABLE test_one_blob'); + +?> +--EXPECTF-- +array(2) { + ["blob1"]=> + resource(%d) of type (stream) + [0]=> + resource(%d) of type (stream) +} +string(3) "foo" +array(2) { + ["blob1"]=> + string(0) "" + [0]=> + string(0) "" +} +array(2) { + ["blob1"]=> + string(0) "" + [0]=> + string(0) "" +} +array(2) { + ["blob1"]=> + NULL + [0]=> + NULL +} http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_pgsql/tests/common.phpt?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/pdo_pgsql/tests/common.phpt diff -u php-src/ext/pdo_pgsql/tests/common.phpt:1.1 php-src/ext/pdo_pgsql/tests/common.phpt:1.2 --- php-src/ext/pdo_pgsql/tests/common.phpt:1.1 Thu Jul 7 15:20:06 2005 +++ php-src/ext/pdo_pgsql/tests/common.phpt Sat Mar 28 02:34:02 2009 @@ -5,6 +5,7 @@ if (!extension_loaded('pdo_pgsql')) print 'skip'; ?> --REDIRECTTEST-- # magic auto-configuration +# Also update config.inc if you make changes here... $config = array( 'TESTS' => 'ext/pdo/tests'
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php