diff options
author | SVN Migration <svn@php.net> | 2008-04-16 09:30:17 +0000 |
---|---|---|
committer | SVN Migration <svn@php.net> | 2008-04-16 09:30:17 +0000 |
commit | 64ed05bf8ed66726a318df16b7933cc1c6eb5eb4 (patch) | |
tree | 8cd954134f73919ddaa17f37021f3440c67abdf1 /ext/pdo_pgsql | |
parent | 985f1c2c600b41c79e4f630ddeb026c2a3e22a64 (diff) | |
download | php-git-RELEASE_1_0_2.tar.gz |
This commit was manufactured by cvs2svn to create tag 'RELEASE_1_0_2'.RELEASE_1_0_2
Diffstat (limited to 'ext/pdo_pgsql')
-rw-r--r-- | ext/pdo_pgsql/CREDITS | 2 | ||||
-rw-r--r-- | ext/pdo_pgsql/config.m4 | 131 | ||||
-rw-r--r-- | ext/pdo_pgsql/config.w32 | 22 | ||||
-rw-r--r-- | ext/pdo_pgsql/package2.xml | 87 | ||||
-rw-r--r-- | ext/pdo_pgsql/pdo_pgsql.c | 139 | ||||
-rw-r--r-- | ext/pdo_pgsql/pgsql_driver.c | 711 | ||||
-rw-r--r-- | ext/pdo_pgsql/pgsql_statement.c | 664 | ||||
-rw-r--r-- | ext/pdo_pgsql/php_pdo_pgsql.h | 55 | ||||
-rw-r--r-- | ext/pdo_pgsql/php_pdo_pgsql_int.h | 114 |
9 files changed, 1925 insertions, 0 deletions
diff --git a/ext/pdo_pgsql/CREDITS b/ext/pdo_pgsql/CREDITS new file mode 100644 index 0000000000..bbb7b94428 --- /dev/null +++ b/ext/pdo_pgsql/CREDITS @@ -0,0 +1,2 @@ +PostgreSQL driver for PDO +Edin Kadribasic, Ilia Alshanetsky diff --git a/ext/pdo_pgsql/config.m4 b/ext/pdo_pgsql/config.m4 new file mode 100644 index 0000000000..9f356db64a --- /dev/null +++ b/ext/pdo_pgsql/config.m4 @@ -0,0 +1,131 @@ +dnl +dnl $Id$ +dnl + +if test "$PHP_PDO" != "no"; then + +AC_DEFUN([PHP_PGSQL_CHECK_FUNCTIONS],[ +]) + +PHP_ARG_WITH(pdo-pgsql,for PostgreSQL support for PDO, +[ --with-pdo-pgsql[=DIR] PDO: PostgreSQL support. DIR is the PostgreSQL base + install directory or the path to pg_config]) + +if test "$PHP_PDO_PGSQL" != "no"; then + PHP_EXPAND_PATH($PGSQL_INCLUDE, PGSQL_INCLUDE) + + AC_MSG_CHECKING(for pg_config) + for i in $PHP_PDO_PGSQL $PHP_PDO_PGSQL/bin /usr/local/pgsql/bin /usr/local/bin /usr/bin ""; do + if test -x $i/pg_config; then + PG_CONFIG="$i/pg_config" + break; + fi + done + + if test -n "$PG_CONFIG"; then + AC_MSG_RESULT([$PG_CONFIG]) + PGSQL_INCLUDE=`$PG_CONFIG --includedir` + PGSQL_LIBDIR=`$PG_CONFIG --libdir` + AC_DEFINE(HAVE_PG_CONFIG_H,1,[Whether to have pg_config.h]) + else + AC_MSG_RESULT(not found) + if test "$PHP_PDO_PGSQL" = "yes"; then + PGSQL_SEARCH_PATHS="/usr /usr/local /usr/local/pgsql" + else + PGSQL_SEARCH_PATHS=$PHP_PDO_PGSQL + fi + + for i in $PGSQL_SEARCH_PATHS; do + for j in include include/pgsql include/postgres include/postgresql ""; do + if test -r "$i/$j/libpq-fe.h"; then + PGSQL_INC_BASE=$i + PGSQL_INCLUDE=$i/$j + if test -r "$i/$j/pg_config.h"; then + AC_DEFINE(HAVE_PG_CONFIG_H,1,[Whether to have pg_config.h]) + fi + fi + done + + for j in lib lib/pgsql lib/postgres lib/postgresql ""; do + if test -f "$i/$j/libpq.so" || test -f "$i/$j/libpq.a"; then + PGSQL_LIBDIR=$i/$j + fi + done + done + fi + + if test -z "$PGSQL_INCLUDE"; then + AC_MSG_ERROR(Cannot find libpq-fe.h. Please specify correct PostgreSQL installation path) + fi + + if test -z "$PGSQL_LIBDIR"; then + AC_MSG_ERROR(Cannot find libpq.so. Please specify correct PostgreSQL installation path) + fi + + if test -z "$PGSQL_INCLUDE" -a -z "$PGSQL_LIBDIR" ; then + AC_MSG_ERROR([Unable to find libpq anywhere under $withval]) + fi + + AC_DEFINE(HAVE_PDO_PGSQL,1,[Whether to build PostgreSQL for PDO support or not]) + + AC_MSG_CHECKING([for openssl dependencies]) + if grep -q openssl $PGSQL_INCLUDE/libpq-fe.h ; then + AC_MSG_RESULT([yes]) + if pkg-config openssl ; then + PDO_PGSQL_CFLAGS="`pkg-config openssl --cflags`" + fi + else + AC_MSG_RESULT([no]) + fi + + old_LIBS=$LIBS + old_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -L$PGSQL_LIBDIR" + AC_CHECK_LIB(pq, PQescapeString,AC_DEFINE(HAVE_PQESCAPE,1,[PostgreSQL 7.2.0 or later])) + AC_CHECK_LIB(pq, PQsetnonblocking,AC_DEFINE(HAVE_PQSETNONBLOCKING,1,[PostgreSQL 7.0.x or later])) + AC_CHECK_LIB(pq, PQcmdTuples,AC_DEFINE(HAVE_PQCMDTUPLES,1,[Broken libpq under windows])) + AC_CHECK_LIB(pq, PQoidValue,AC_DEFINE(HAVE_PQOIDVALUE,1,[Older PostgreSQL])) + AC_CHECK_LIB(pq, PQclientEncoding,AC_DEFINE(HAVE_PQCLIENTENCODING,1,[PostgreSQL 7.0.x or later])) + AC_CHECK_LIB(pq, PQparameterStatus,AC_DEFINE(HAVE_PQPARAMETERSTATUS,1,[PostgreSQL 7.4 or later])) + AC_CHECK_LIB(pq, PQprotocolVersion,AC_DEFINE(HAVE_PQPROTOCOLVERSION,1,[PostgreSQL 7.4 or later])) + AC_CHECK_LIB(pq, PQtransactionStatus,AC_DEFINE(HAVE_PGTRANSACTIONSTATUS,1,[PostgreSQL 7.4 or later])) + AC_CHECK_LIB(pq, PQunescapeBytea,AC_DEFINE(HAVE_PQUNESCAPEBYTEA,1,[PostgreSQL 7.4 or later])) + AC_CHECK_LIB(pq, PQExecParams,AC_DEFINE(HAVE_PQEXECPARAMS,1,[PostgreSQL 7.4 or later])) + AC_CHECK_LIB(pq, PQresultErrorField,AC_DEFINE(HAVE_PQRESULTERRORFIELD,1,[PostgreSQL 7.4 or later])) + AC_CHECK_LIB(pq, pg_encoding_to_char,AC_DEFINE(HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT,1,[Whether libpq is compiled with --enable-multibyte])) + + AC_CHECK_LIB(pq, PQprepare,AC_DEFINE(HAVE_PQPREPARE,1,[prepared statements])) + + LIBS=$old_LIBS + LDFLAGS=$old_LDFLAGS + + PHP_ADD_LIBRARY_WITH_PATH(pq, $PGSQL_LIBDIR, PDO_PGSQL_SHARED_LIBADD) + PHP_SUBST(PDO_PGSQL_SHARED_LIBADD) + + PHP_ADD_INCLUDE($PGSQL_INCLUDE) + + ifdef([PHP_CHECK_PDO_INCLUDES], + [ + PHP_CHECK_PDO_INCLUDES + ],[ + AC_MSG_CHECKING([for PDO includes]) + if test -f $abs_srcdir/include/php/ext/pdo/php_pdo_driver.h; then + pdo_inc_path=$abs_srcdir/ext + elif test -f $abs_srcdir/ext/pdo/php_pdo_driver.h; then + pdo_inc_path=$abs_srcdir/ext + elif test -f $prefix/include/php/ext/pdo/php_pdo_driver.h; then + pdo_inc_path=$prefix/include/php/ext + else + AC_MSG_ERROR([Cannot find php_pdo_driver.h.]) + fi + AC_MSG_RESULT($pdo_inc_path) + ]) + + PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c, $ext_shared,,-I$pdo_inc_path $PDO_PGSQL_CFLAGS) + ifdef([PHP_ADD_EXTENSION_DEP], + [ + PHP_ADD_EXTENSION_DEP(pdo_pgsql, pdo) + ]) +fi + +fi diff --git a/ext/pdo_pgsql/config.w32 b/ext/pdo_pgsql/config.w32 new file mode 100644 index 0000000000..3c74c9bf00 --- /dev/null +++ b/ext/pdo_pgsql/config.w32 @@ -0,0 +1,22 @@ +// $Id$ +// 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")) { + 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")) { + ADD_FLAG('CFLAGS_PDO_PGSQL', "/D HAVE_PG_CONFIG_H"); + } + + ADD_FLAG('CFLAGS_PDO_PGSQL', "/I ..\\pecl"); + 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"); + } else { + WARNING("pdo_pgsql not enabled; libraries and headers not found"); + } + ADD_EXTENSION_DEP('pdo_pgsql', 'pdo'); +} diff --git a/ext/pdo_pgsql/package2.xml b/ext/pdo_pgsql/package2.xml new file mode 100644 index 0000000000..e397958a7d --- /dev/null +++ b/ext/pdo_pgsql/package2.xml @@ -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>edink@php.net</email> + <active>yes</active> + </lead> + <lead> + <name>Ilia Alshanetsky</name> + <user>iliaa</user> + <email>iliaa@php.net</email> + <active>yes</active> + </lead> + <lead> + <name>Wez Furlong</name> + <user>wez</user> + <email>wez@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> diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c new file mode 100644 index 0000000000..d3730afb90 --- /dev/null +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -0,0 +1,139 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Edin Kadribasic <edink@emini.dk> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "php_pdo_pgsql.h" +#include "php_pdo_pgsql_int.h" + +#ifdef HAVE_PG_CONFIG_H +#include <pg_config.h> +#endif + +/* {{{ pdo_pgsql_functions[] */ +zend_function_entry pdo_pgsql_functions[] = { + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ pdo_sqlite_deps + */ +#if ZEND_MODULE_API_NO >= 20050922 +static zend_module_dep pdo_pgsql_deps[] = { + ZEND_MOD_REQUIRED("pdo") + {NULL, NULL, NULL} +}; +#endif +/* }}} */ + +/* {{{ pdo_pgsql_module_entry */ +zend_module_entry pdo_pgsql_module_entry = { +#if ZEND_MODULE_API_NO >= 20050922 + STANDARD_MODULE_HEADER_EX, NULL, + pdo_pgsql_deps, +#else + STANDARD_MODULE_HEADER, +#endif + "pdo_pgsql", + pdo_pgsql_functions, + PHP_MINIT(pdo_pgsql), + PHP_MSHUTDOWN(pdo_pgsql), + PHP_RINIT(pdo_pgsql), + PHP_RSHUTDOWN(pdo_pgsql), + PHP_MINFO(pdo_pgsql), + "1.0.2", + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_PDO_PGSQL +ZEND_GET_MODULE(pdo_pgsql) +#endif + +/* true global environment */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(pdo_pgsql) +{ + php_pdo_register_driver(&pdo_pgsql_driver); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT", PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(pdo_pgsql) +{ + php_pdo_unregister_driver(&pdo_pgsql_driver); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION + */ +PHP_RINIT_FUNCTION(pdo_pgsql) +{ + /* php_pdo_register_driver(&pdo_pgsql_driver); */ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(pdo_pgsql) +{ + /* php_pdo_unregister_driver(&pdo_pgsql_driver); */ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(pdo_pgsql) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "PDO Driver for PostgreSQL", "enabled"); +#ifdef HAVE_PG_CONFIG_H + 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$ "); + + php_info_print_table_end(); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c new file mode 100644 index 0000000000..7b1561506a --- /dev/null +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -0,0 +1,711 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Edin Kadribasic <edink@emini.dk> | + | Ilia Alshanestsky <ilia@prohost.org> | + | Wez Furlong <wez@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" + +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#include "pg_config.h" /* needed for PG_VERSION */ +#include "php_pdo_pgsql.h" +#include "php_pdo_pgsql_int.h" +#include "zend_exceptions.h" + +static char * _pdo_pgsql_trim_message(const char *message, int persistent) +{ + register int i = strlen(message)-1; + char *tmp; + + if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') { + --i; + } + while (i>0 && (message[i] == '\r' || message[i] == '\n')) { + --i; + } + ++i; + tmp = pemalloc(i + 1, persistent); + memcpy(tmp, message, i); + tmp[i] = '\0'; + + return tmp; +} + +int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *file, int line TSRMLS_DC) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_pgsql_error_info *einfo = &H->einfo; + char *errmsg = PQerrorMessage(H->server); + + einfo->errcode = errcode; + einfo->file = file; + einfo->line = line; + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + + if (sqlstate == NULL) { + strcpy(*pdo_err, "HY000"); + } + else { + strcpy(*pdo_err, sqlstate); + } + + if (errmsg) { + einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); + } + + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "SQLSTATE[%s] [%d] %s", + *pdo_err, einfo->errcode, einfo->errmsg); + } + + return errcode; +} +/* }}} */ + +static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */ +{ +/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */ +} +/* }}} */ + +static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) /* {{{ */ +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg, 1); + } + + return 1; +} +/* }}} */ + +/* {{{ 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; + if (H) { + if (H->server) { + PQfinish(H->server); + H->server = NULL; + } + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } + return 0; +} +/* }}} */ + +static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); + int scrollable; +#if HAVE_PQPREPARE + int ret; + char *nsql = NULL; + int nsql_len = 0; + int emulate = 0; +#endif + + S->H = H; + stmt->driver_data = S; + stmt->methods = &pgsql_stmt_methods; + + scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, + PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL; + + if (scrollable) { + /* TODO: check how scrollable cursors related to prepared statements */ + spprintf(&S->cursor_name, 0, "pdo_pgsql_cursor_%08x", (unsigned int) stmt); + } + +#if HAVE_PQPREPARE + + 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); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + return 0; + } + + spprintf(&S->stmt_name, 0, "pdo_pgsql_stmt_%08x", (unsigned int)stmt); + /* that's all for now; we'll defer the actual prepare until the first execute call */ + + if (nsql) { + S->query = nsql; + } else { + S->query = estrdup(sql); + } + + return 1; + } +#endif + + stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; + return 1; +} + +static long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + PGresult *res; + long ret = 1; + ExecStatusType qs; + + if (!(res = PQexec(H->server, sql))) { + /* fatal error */ + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + return -1; + } + qs = PQresultStatus(res); + if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { + pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); + PQclear(res); + return -1; + } + H->pgoid = PQoidValue(res); +#if HAVE_PQCMDTUPLES + ret = atol(PQcmdTuples(res)); +#endif + PQclear(res); + + return ret; +} + +static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC) +{ + unsigned char *escaped; + + switch (paramtype) { + case PDO_PARAM_LOB: + /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ + escaped = PQescapeBytea(unquoted, unquotedlen, quotedlen); + *quotedlen += 1; + *quoted = emalloc(*quotedlen + 1); + memcpy((*quoted)+1, escaped, *quotedlen-2); + (*quoted)[0] = '\''; + (*quoted)[*quotedlen-1] = '\''; + (*quoted)[*quotedlen] = '\0'; + free(escaped); + break; + default: + *quoted = emalloc(2*unquotedlen + 3); + (*quoted)[0] = '\''; + *quotedlen = PQescapeString(*quoted + 1, unquoted, unquotedlen); + (*quoted)[*quotedlen + 1] = '\''; + (*quoted)[*quotedlen + 2] = '\0'; + *quotedlen += 2; + } + return 1; +} + +static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, unsigned int *len TSRMLS_DC) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + char *id = NULL; + + if (name == NULL) { + if (H->pgoid == InvalidOid) { + return NULL; + } + *len = spprintf(&id, 0, "%ld", (long) H->pgoid); + } else { + PGresult *res; + ExecStatusType status; +#ifdef HAVE_PQEXECPARAMS + const char *q[1]; + q[0] = name; + res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); +#else + char *name_escaped, *q; + size_t l = strlen(name); + + name_escaped = safe_emalloc(l, 2, 1); + PQescapeString(name_escaped, name, l); + spprintf(&q, 0, "SELECT CURRVAL('%s')", name_escaped); + res = PQexec(H->server, q); + efree(name_escaped); + efree(q); +#endif + status = PQresultStatus(res); + + if (res && (status == PGRES_TUPLES_OK)) { + id = estrdup((char *)PQgetvalue(res, 0, 0)); + *len = PQgetlength(res, 0, 0); + } else { + pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); + } + + if (res) { + PQclear(res); + } + } + return id; +} + +static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC) +{ + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + ZVAL_STRING(return_value, PG_VERSION, 1); + break; + + case PDO_ATTR_SERVER_VERSION: +#ifdef HAVE_PQPROTOCOLVERSION + if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */ + ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"), 1); + } else /* emulate above via a query */ +#endif + { + PGresult *res = PQexec(H->server, "SELECT VERSION()"); + if (res && PQresultStatus(res) == PGRES_TUPLES_OK) { + ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0), 1); + } + + if (res) { + PQclear(res); + } + } + break; + + case PDO_ATTR_CONNECTION_STATUS: + switch (PQstatus(H->server)) { + case CONNECTION_STARTED: + ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1, 1); + break; + + case CONNECTION_MADE: + case CONNECTION_OK: + ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1, 1); + break; + + case CONNECTION_AWAITING_RESPONSE: + ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1, 1); + break; + + case CONNECTION_AUTH_OK: + ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1, 1); + break; +#ifdef CONNECTION_SSL_STARTUP + case CONNECTION_SSL_STARTUP: + ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1, 1); + break; +#endif + case CONNECTION_SETENV: + ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1, 1); + break; + + case CONNECTION_BAD: + default: + ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1, 1); + break; + } + break; + + case PDO_ATTR_SERVER_INFO: { + int spid = PQbackendPID(H->server); + char *tmp; +#ifdef HAVE_PQPROTOCOLVERSION + spprintf(&tmp, 0, + "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", + spid, + (char*)PQparameterStatus(H->server, "client_encoding"), + (char*)PQparameterStatus(H->server, "is_superuser"), + (char*)PQparameterStatus(H->server, "session_authorization"), + (char*)PQparameterStatus(H->server, "DateStyle")); +#else + spprintf(&tmp, 0, "PID: %d", spid); +#endif + ZVAL_STRING(return_value, tmp, 0); + } + break; + + default: + return 0; + } + + return 1; +} + +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; + PGresult *res; + int ret = 1; + + res = PQexec(H->server, cmd); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + ret = 0; + } + + PQclear(res); + return ret; +} + +static int pgsql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC) +{ + return pdo_pgsql_transaction_cmd("BEGIN", dbh TSRMLS_CC); +} + +static int pgsql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC) +{ + return pdo_pgsql_transaction_cmd("COMMIT", dbh TSRMLS_CC); +} + +static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC) +{ + 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 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 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 struct pdo_dbh_methods pgsql_methods = { + pgsql_handle_closer, + pgsql_handle_preparer, + pgsql_handle_doer, + pgsql_handle_quoter, + pgsql_handle_begin, + pgsql_handle_commit, + pgsql_handle_rollback, + NULL, /* set_attr */ + pdo_pgsql_last_insert_id, + pdo_pgsql_fetch_error_func, + pdo_pgsql_get_attribute, + NULL, /* 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) /* {{{ */ +{ + pdo_pgsql_db_handle *H; + int ret = 0; + char *conn_str, *p, *e; + + H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); + dbh->driver_data = H; + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + + /* PostgreSQL wants params in the connect string to be separated by spaces, + * if the PDO standard semicolons are used, we convert them to spaces + */ + e = (char *) dbh->data_source + strlen(dbh->data_source); + p = (char *) dbh->data_source; + while ((p = memchr(p, ';', (e - p)))) { + *p = ' '; + } + + /* support both full connection string & connection string + login and/or password */ + if (!dbh->username || !dbh->password) { + conn_str = (char *) dbh->data_source; + } else if (dbh->username && dbh->password) { + spprintf(&conn_str, 0, "%s user=%s password=%s", dbh->data_source, dbh->username, dbh->password); + } else if (dbh->username) { + spprintf(&conn_str, 0, "%s user=%s", dbh->data_source, dbh->username); + } else { + spprintf(&conn_str, 0, "%s password=%s", dbh->data_source, dbh->password); + } + + H->server = PQconnectdb(conn_str); + + if (conn_str != dbh->data_source) { + efree(conn_str); + } + + if (PQstatus(H->server) != CONNECTION_OK) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); + goto cleanup; + } + + PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); + + H->attached = 1; + H->pgoid = -1; + + dbh->methods = &pgsql_methods; + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &pgsql_methods; + if (!ret) { + pgsql_handle_closer(dbh TSRMLS_CC); + } + + return ret; +} +/* }}} */ + +pdo_driver_t pdo_pgsql_driver = { + PDO_DRIVER_HEADER(pgsql), + pdo_pgsql_handle_factory +}; + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c new file mode 100644 index 0000000000..d83b86ad78 --- /dev/null +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -0,0 +1,664 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Edin Kadribasic <edink@emini.dk> | + | Ilia Alshanestsky <ilia@prohost.org> | + | Wez Furlong <wez@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#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 +#define BYTEAOID 17 +#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; + + if (S->result) { + /* free the resource */ + PQclear(S->result); + S->result = NULL; + } + +#if HAVE_PQPREPARE + if (S->stmt_name) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + 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; + } + if (S->param_lengths) { + efree(S->param_lengths); + S->param_lengths = NULL; + } + if (S->param_values) { + efree(S->param_values); + S->param_values = NULL; + } + if (S->param_formats) { + 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) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "CLOSE %s", S->cursor_name); + res = PQexec(H->server, q); + efree(q); + if (res) PQclear(res); + efree(S->cursor_name); + S->cursor_name = NULL; + } + + if(S->cols) { + efree(S->cols); + S->cols = NULL; + } + efree(S); + stmt->driver_data = NULL; + return 1; +} + +static int pgsql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + pdo_pgsql_db_handle *H = S->H; + ExecStatusType status; + + /* ensure that we free any previous unfetched results */ + if(S->result) { + PQclear(S->result); + S->result = NULL; + } + + S->current_row = 0; + +#if HAVE_PQPREPARE + 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, + S->param_formats, + 0); + } else +#endif + if (S->cursor_name) { + char *q = NULL; + spprintf(&q, 0, "DECLARE %s CURSOR FOR %s", S->cursor_name, stmt->active_query_string); + S->result = PQexec(H->server, q); + efree(q); + } else { + S->result = PQexec(H->server, stmt->active_query_string); + } + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + if(!stmt->executed) { + stmt->column_count = (int) PQnfields(S->result); + S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); + } + + if (status == PGRES_COMMAND_OK) { + stmt->row_count = (long)atoi(PQcmdTuples(S->result)); + H->pgoid = PQoidValue(S->result); + } else { + stmt->row_count = (long)PQntuples(S->result); + } + + return 1; +} + +static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, + enum pdo_param_event event_type TSRMLS_DC) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; +#if HAVE_PQPREPARE + if (S->stmt_name && param->is_param) { + switch (event_type) { + 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] == '$') { + param->paramno = atoi(param->name + 1); + } else { + /* resolve parameter name to rewritten name */ + char *nameptr; + if (stmt->bound_param_map && SUCCESS == zend_hash_find(stmt->bound_param_map, + param->name, param->namelen + 1, (void**)&nameptr)) { + param->paramno = atoi(nameptr + 1) - 1; + } else { + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY093"); + return 0; + } + } + } + break; + + case PDO_PARAM_EVT_ALLOC: + /* work is handled by EVT_NORMALIZE */ + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + if (!S->param_values) { + S->param_values = ecalloc( + zend_hash_num_elements(stmt->bound_params), + sizeof(char*)); + S->param_lengths = ecalloc( + zend_hash_num_elements(stmt->bound_params), + sizeof(int)); + S->param_formats = ecalloc( + zend_hash_num_elements(stmt->bound_params), + sizeof(int)); + S->param_types = ecalloc( + zend_hash_num_elements(stmt->bound_params), + sizeof(Oid)); + } + if (param->paramno >= 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 { + SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); + Z_TYPE_P(param->parameter) = IS_STRING; + Z_STRLEN_P(param->parameter) = php_stream_copy_to_mem(stm, + &Z_STRVAL_P(param->parameter), PHP_STREAM_COPY_ALL, 0); + } + } 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; + S->param_lengths[param->paramno] = 0; + } 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] = 0; + } else { + 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; + } + } +#endif + return 1; +} + +static int pgsql_stmt_fetch(pdo_stmt_t *stmt, + enum pdo_fetch_orientation ori, long offset TSRMLS_DC) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->cursor_name) { + char *ori_str; + char *q = NULL; + ExecStatusType status; + + switch (ori) { + case PDO_FETCH_ORI_NEXT: ori_str = "FORWARD"; break; + case PDO_FETCH_ORI_PRIOR: ori_str = "BACKWARD"; break; + case PDO_FETCH_ORI_REL: ori_str = "RELATIVE"; break; + default: + return 0; + } + + spprintf(&q, 0, "FETCH %s %ld FROM %s", ori_str, offset, S->cursor_name); + S->result = PQexec(S->H->server, q); + status = PQresultStatus(S->result); + + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); + return 0; + } + + S->current_row = 1; + return 1; + + } else { + if (S->current_row < stmt->row_count) { + S->current_row++; + return 1; + } else { + return 0; + } + } +} + +static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) +{ + 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; + } + + cols[colno].name = estrdup(PQfname(S->result, colno)); + cols[colno].namelen = strlen(cols[colno].name); + cols[colno].maxlen = PQfsize(S->result, colno); + cols[colno].precision = PQfmod(S->result, colno); + S->cols[colno].pgsql_type = PQftype(S->result, colno); + + switch(S->cols[colno].pgsql_type) { + + 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: + cols[colno].param_type = PDO_PARAM_INT; + break; + + case INT8OID: + if (sizeof(long)>=8) { + cols[colno].param_type = PDO_PARAM_INT; + } else { + cols[colno].param_type = PDO_PARAM_STR; + } + break; + + case BYTEAOID: + cols[colno].param_type = PDO_PARAM_LOB; + break; + + default: + cols[colno].param_type = PDO_PARAM_STR; + } + + return 1; +} + +/* PQunescapeBytea() from PostgreSQL 7.3 to provide bytea unescape feature to 7.2 users. + Renamed to php_pdo_pgsql_unescape_bytea() */ +/* + * PQunescapeBytea - converts the null terminated string representation + * of a bytea, strtext, into binary, filling a buffer. It returns a + * pointer to the buffer which is NULL on error, and the size of the + * buffer in retbuflen. The pointer may subsequently be used as an + * argument to the function free(3). It is the reverse of PQescapeBytea. + * + * The following transformations are reversed: + * '\0' == ASCII 0 == \000 + * '\'' == ASCII 39 == \' + * '\\' == ASCII 92 == \\ + * + * States: + * 0 normal 0->1->2->3->4 + * 1 \ 1->5 + * 2 \0 1->6 + * 3 \00 + * 4 \000 + * 5 \' + * 6 \\ + */ +static unsigned char *php_pdo_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen) +{ + size_t buflen; + unsigned char *buffer, + *sp, + *bp; + unsigned int state = 0; + + if (strtext == NULL) + return NULL; + buflen = strlen(strtext); /* will shrink, also we discover if + * strtext */ + buffer = (unsigned char *) emalloc(buflen); /* isn't NULL terminated */ + for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++) + { + switch (state) + { + case 0: + if (*sp == '\\') + state = 1; + *bp = *sp; + break; + case 1: + if (*sp == '\'') /* state=5 */ + { /* replace \' with 39 */ + bp--; + *bp = '\''; + buflen--; + state = 0; + } + else if (*sp == '\\') /* state=6 */ + { /* replace \\ with 92 */ + bp--; + *bp = '\\'; + buflen--; + state = 0; + } + else + { + if (isdigit(*sp)) + state = 2; + else + state = 0; + *bp = *sp; + } + break; + case 2: + if (isdigit(*sp)) + state = 3; + else + state = 0; + *bp = *sp; + break; + case 3: + if (isdigit(*sp)) /* state=4 */ + { + unsigned char *start, *end, buf[4]; /* 000 + '\0' */ + + bp -= 3; + memcpy(buf, sp-2, 3); + buf[3] = '\0'; + start = buf; + *bp = (unsigned char)strtoul(start, (char **)&end, 8); + buflen -= 3; + state = 0; + } + else + { + *bp = *sp; + state = 0; + } + break; + } + } + buffer = erealloc(buffer, buflen+1); + buffer[buflen] = '\0'; + + *retbuflen = buflen; + return buffer; +} + +static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + struct pdo_column_data *cols = stmt->columns; + size_t tmp_len; + + if (!S->result) { + return 0; + } + + /* We have already increased count by 1 in pgsql_stmt_fetch() */ + if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ + *ptr = NULL; + *len = 0; + } else { + *ptr = PQgetvalue(S->result, S->current_row - 1, colno); + *len = PQgetlength(S->result, S->current_row - 1, colno); + + switch(cols[colno].param_type) { + + case PDO_PARAM_INT: + S->cols[colno].intval = atol(*ptr); + *ptr = (char *) &(S->cols[colno].intval); + *len = sizeof(long); + break; + + case PDO_PARAM_BOOL: + S->cols[colno].boolval = **ptr == 't' ? 1: 0; + *ptr = (char *) &(S->cols[colno].boolval); + *len = sizeof(zend_bool); + break; + + case PDO_PARAM_LOB: + 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: + break; + } + } + + 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 + */ + PQclear(res); + return 1; + } + + /* We want exactly one row returned */ + if (1 != PQntuples(res)) { + PQclear(res); + return 1; + } + + add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0), 1); + + PQclear(res); + return 1; +} + +static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) +{ + return 1; +} + +struct pdo_stmt_methods pgsql_stmt_methods = { + pgsql_stmt_dtor, + pgsql_stmt_execute, + pgsql_stmt_fetch, + pgsql_stmt_describe, + pgsql_stmt_get_col, + pgsql_stmt_param_hook, + NULL, /* set_attr */ + NULL, /* get_attr */ + pgsql_stmt_get_column_meta, + NULL, /* next_rowset */ + pdo_pgsql_stmt_cursor_closer +}; + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/pdo_pgsql/php_pdo_pgsql.h b/ext/pdo_pgsql/php_pdo_pgsql.h new file mode 100644 index 0000000000..4096566870 --- /dev/null +++ b/ext/pdo_pgsql/php_pdo_pgsql.h @@ -0,0 +1,55 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Edin Kadribasic <edink@emini.dk> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_PDO_PGSQL_H +#define PHP_PDO_PGSQL_H + +#include <libpq-fe.h> + +extern zend_module_entry pdo_pgsql_module_entry; +#define phpext_pdo_pgsql_ptr &pdo_pgsql_module_entry + +#ifdef PHP_WIN32 +#define PHP_PDO_PGSQL_API __declspec(dllexport) +#else +#define PHP_PDO_PGSQL_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +PHP_MINIT_FUNCTION(pdo_pgsql); +PHP_MSHUTDOWN_FUNCTION(pdo_pgsql); +PHP_RINIT_FUNCTION(pdo_pgsql); +PHP_RSHUTDOWN_FUNCTION(pdo_pgsql); +PHP_MINFO_FUNCTION(pdo_pgsql); + +#endif /* PHP_PDO_PGSQL_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h new file mode 100644 index 0000000000..7cdd340988 --- /dev/null +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -0,0 +1,114 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Edin Kadribasic <edink@emini.dk> | + | Ilia Alshanestsky <ilia@prohost.org> | + | Wez Furlong <wez@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#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" + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_pgsql_error_info; + +/* stuff we use in a pgsql database handle */ +typedef struct { + PGconn *server; + unsigned attached:1; + unsigned _reserved:31; + pdo_pgsql_error_info einfo; + Oid pgoid; +} pdo_pgsql_db_handle; + +typedef struct { + char *def; + Oid pgsql_type; + long intval; + zend_bool boolval; +} pdo_pgsql_column; + +typedef struct { + pdo_pgsql_db_handle *H; + PGresult *result; + int current_row; + pdo_pgsql_column *cols; + 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 { + Oid oid; +} pdo_pgsql_bound_param; + +extern pdo_driver_t pdo_pgsql_driver; + +extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *file, int line TSRMLS_DC); +#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 struct pdo_stmt_methods pgsql_stmt_methods; + +#ifdef HAVE_PQRESULTERRORFIELD +#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) +#else +#define pdo_pgsql_sqlstate(r) (const char *)NULL +#endif + +enum { + 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 */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ |