summaryrefslogtreecommitdiff
path: root/ext/pdo_pgsql
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-03-14 05:42:27 +0000
committer <>2013-04-03 16:25:08 +0000
commitc4dd7a1a684490673e25aaf4fabec5df138854c4 (patch)
tree4d57c44caae4480efff02b90b9be86f44bf25409 /ext/pdo_pgsql
downloadphp2-c4dd7a1a684490673e25aaf4fabec5df138854c4.tar.gz
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/pdo_pgsql')
-rw-r--r--ext/pdo_pgsql/CREDITS2
-rw-r--r--ext/pdo_pgsql/config.m4129
-rw-r--r--ext/pdo_pgsql/config.w3224
-rw-r--r--ext/pdo_pgsql/package2.xml87
-rw-r--r--ext/pdo_pgsql/pdo_pgsql.c132
-rw-r--r--ext/pdo_pgsql/pgsql_driver.c1116
-rw-r--r--ext/pdo_pgsql/pgsql_statement.c640
-rw-r--r--ext/pdo_pgsql/php_pdo_pgsql.h47
-rw-r--r--ext/pdo_pgsql/php_pdo_pgsql_int.h125
-rw-r--r--ext/pdo_pgsql/tests/bug36727.phpt24
-rw-r--r--ext/pdo_pgsql/tests/bug43925.phpt59
-rw-r--r--ext/pdo_pgsql/tests/bug46274.phpt85
-rw-r--r--ext/pdo_pgsql/tests/bug46274_2.phpt91
-rw-r--r--ext/pdo_pgsql/tests/bug48764.phpt134
-rw-r--r--ext/pdo_pgsql/tests/bug61267.phpt22
-rw-r--r--ext/pdo_pgsql/tests/bug62593.phpt51
-rw-r--r--ext/pdo_pgsql/tests/bug_33876.phpt155
-rw-r--r--ext/pdo_pgsql/tests/bug_49985.phpt35
-rw-r--r--ext/pdo_pgsql/tests/common.phpt24
-rw-r--r--ext/pdo_pgsql/tests/config.inc14
-rw-r--r--ext/pdo_pgsql/tests/copy_from.phpt386
-rw-r--r--ext/pdo_pgsql/tests/copy_to.phpt129
-rw-r--r--ext/pdo_pgsql/tests/is_in_transaction.phpt66
-rw-r--r--ext/pdo_pgsql/tests/large_objects.phpt93
24 files changed, 3670 insertions, 0 deletions
diff --git a/ext/pdo_pgsql/CREDITS b/ext/pdo_pgsql/CREDITS
new file mode 100644
index 0000000..bbb7b94
--- /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 0000000..f9254a8
--- /dev/null
+++ b/ext/pdo_pgsql/config.m4
@@ -0,0 +1,129 @@
+dnl $Id$
+dnl config.m4 for extension pdo_pgsql
+dnl vim:et:sw=2:ts=2:
+
+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
+
+ if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then
+ AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.])
+ fi
+
+ 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 $PHP_LIBDIR $PHP_LIBDIR/pgsql $PHP_LIBDIR/postgres $PHP_LIBDIR/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 $PGSQL_SEARCH_PATHS])
+ fi
+
+ AC_DEFINE(HAVE_PDO_PGSQL,1,[Whether to build PostgreSQL for PDO support or not])
+
+ AC_MSG_CHECKING([for openssl dependencies])
+ grep openssl $PGSQL_INCLUDE/libpq-fe.h >/dev/null 2>&1
+ if test $? -eq 0 ; then
+ AC_MSG_RESULT([yes])
+ dnl First try to find pkg-config
+ AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+ if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists openssl; then
+ PDO_PGSQL_CFLAGS=`$PKG_CONFIG openssl --cflags`
+ fi
+ else
+ AC_MSG_RESULT([no])
+ fi
+
+ old_LIBS=$LIBS
+ old_LDFLAGS=$LDFLAGS
+ LDFLAGS="-L$PGSQL_LIBDIR $LDFLAGS"
+ AC_CHECK_LIB(pq, PQparameterStatus,AC_DEFINE(HAVE_PQPARAMETERSTATUS,1,[PostgreSQL 7.4 or later]), [
+ echo "Unable to build the PDO PostgreSQL driver: libpq 7.4+ is required"
+ exit 1
+ ])
+
+ AC_CHECK_LIB(pq, PQprepare,AC_DEFINE(HAVE_PQPREPARE,1,[PostgreSQL 8.0 or later]))
+ AC_CHECK_LIB(pq, PQescapeStringConn, AC_DEFINE(HAVE_PQESCAPE_CONN,1,[PostgreSQL 8.1.4 or later]))
+ AC_CHECK_LIB(pq, PQescapeByteaConn, AC_DEFINE(HAVE_PQESCAPE_BYTEA_CONN,1,[PostgreSQL 8.1.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]))
+
+
+ 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_cv_inc_path=$abs_srcdir/ext
+ elif test -f $abs_srcdir/ext/pdo/php_pdo_driver.h; then
+ pdo_cv_inc_path=$abs_srcdir/ext
+ elif test -f $prefix/include/php/ext/pdo/php_pdo_driver.h; then
+ pdo_cv_inc_path=$prefix/include/php/ext
+ else
+ AC_MSG_ERROR([Cannot find php_pdo_driver.h.])
+ fi
+ AC_MSG_RESULT($pdo_cv_inc_path)
+ ])
+
+ PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c, $ext_shared,,-I$pdo_cv_inc_path $PDO_PGSQL_CFLAGS)
+ ifdef([PHP_ADD_EXTENSION_DEP],
+ [
+ PHP_ADD_EXTENSION_DEP(pdo_pgsql, pdo)
+ ])
+fi
diff --git a/ext/pdo_pgsql/config.w32 b/ext/pdo_pgsql/config.w32
new file mode 100644
index 0000000..223780e
--- /dev/null
+++ b/ext/pdo_pgsql/config.w32
@@ -0,0 +1,24 @@
+// $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;" + 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")) {
+ ADD_FLAG('CFLAGS_PDO_PGSQL', "/D HAVE_PG_CONFIG_H");
+ }
+
+ AC_DEFINE('HAVE_PDO_PGSQL', 1, 'Have PostgreSQL library');
+ AC_DEFINE('HAVE_PQESCAPE_BYTEA_CONN', 1, 'Have PQescapeByteaConn');
+ AC_DEFINE('HAVE_PQESCAPE_CONN', 1, 'Have PQescapeConn');
+ AC_DEFINE('HAVE_PQPREPARE', 1, 'Have PQprepare');
+ 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");
+ }
+}
diff --git a/ext/pdo_pgsql/package2.xml b/ext/pdo_pgsql/package2.xml
new file mode 100644
index 0000000..e397958
--- /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 0000000..7c37a60
--- /dev/null
+++ b/ext/pdo_pgsql/pdo_pgsql.c
@@ -0,0 +1,132 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 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
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include <pg_config.h>
+#endif
+
+/* {{{ pdo_pgsql_functions[] */
+const zend_function_entry pdo_pgsql_functions[] = {
+ {NULL, NULL, NULL}
+};
+/* }}} */
+
+/* {{{ pdo_sqlite_deps
+ */
+#if ZEND_MODULE_API_NO >= 20050922
+static const zend_module_dep pdo_pgsql_deps[] = {
+ ZEND_MOD_REQUIRED("pdo")
+ ZEND_MOD_END
+};
+#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),
+ NULL,
+ NULL,
+ 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)
+{
+ REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT", PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT);
+ 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);
+
+ php_pdo_register_driver(&pdo_pgsql_driver);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_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 0000000..a54fccd
--- /dev/null
+++ b/ext/pdo_pgsql/pgsql_driver.c
@@ -0,0 +1,1116 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 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 "ext/standard/file.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(), einfo->errcode 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) {
+ if (S->cursor_name) {
+ efree(S->cursor_name);
+ }
+ spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
+#if HAVE_PQPREPARE
+ emulate = 1;
+#endif
+ }
+
+#if HAVE_PQPREPARE
+ else if (driver_options) {
+ if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, H->disable_native_prepares TSRMLS_CC) == 1 ||
+ pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares TSRMLS_CC) == 1) {
+ emulate = 1;
+ }
+ } else {
+ emulate = H->disable_native_prepares || H->emulate_prepares;
+ }
+
+ 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_stmt_%08x", ++H->stmt_counter);
+ /* 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);
+ ret = (qs == PGRES_COMMAND_OK) ? atol(PQcmdTuples(res)) : 0L;
+ 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;
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+ size_t tmp_len;
+
+ switch (paramtype) {
+ case PDO_PARAM_LOB:
+ /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
+#ifdef HAVE_PQESCAPE_BYTEA_CONN
+ escaped = PQescapeByteaConn(H->server, unquoted, unquotedlen, &tmp_len);
+#else
+ escaped = PQescapeBytea(unquoted, unquotedlen, &tmp_len);
+#endif
+ *quotedlen = (int)tmp_len + 1;
+ *quoted = emalloc(*quotedlen + 1);
+ memcpy((*quoted)+1, escaped, *quotedlen-2);
+ (*quoted)[0] = '\'';
+ (*quoted)[*quotedlen-1] = '\'';
+ (*quoted)[*quotedlen] = '\0';
+ PQfreemem(escaped);
+ break;
+ default:
+ *quoted = safe_emalloc(2, unquotedlen, 3);
+ (*quoted)[0] = '\'';
+#ifndef HAVE_PQESCAPE_CONN
+ *quotedlen = PQescapeString(*quoted + 1, unquoted, unquotedlen);
+#else
+ *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL);
+#endif
+ (*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;
+ const char *q[1];
+ q[0] = name;
+ res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
+ 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:
+ 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 */
+ {
+ 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;
+ 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"));
+ ZVAL_STRING(return_value, tmp, 0);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ 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;
+ PGresult *res;
+ int ret = 1;
+
+ res = PQexec(H->server, cmd);
+
+ if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+ pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
+ 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);
+}
+
+static int pgsql_handle_in_transaction(pdo_dbh_t *dbh TSRMLS_DC)
+{
+ pdo_pgsql_db_handle *H;
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ return 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;
+
+ char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
+ int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
+ char *query;
+
+ 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 (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array");
+ RETURN_FALSE;
+ }
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ if (pg_fields) {
+ spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ } else {
+ spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ }
+
+ /* Obtain db Handle */
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+ pgsql_result = PQexec(H->server, query);
+
+ efree(query);
+ query = NULL;
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(H->server);
+ }
+
+ if (status == PGRES_COPY_IN && pgsql_result) {
+ int command_failed = 0;
+ int buffer_len = 0;
+ zval **tmp;
+ HashPosition pos;
+
+ 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) {
+ int query_len;
+ convert_to_string_ex(tmp);
+
+ if (buffer_len < Z_STRLEN_PP(tmp)) {
+ buffer_len = Z_STRLEN_PP(tmp);
+ query = erealloc(query, buffer_len + 2); /* room for \n\0 */
+ }
+ memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
+ query_len = Z_STRLEN_PP(tmp);
+ if (query[query_len - 1] != '\n') {
+ query[query_len++] = '\n';
+ }
+ query[query_len] = '\0';
+ if (PQputCopyData(H->server, query, query_len) != 1) {
+ efree(query);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
+ RETURN_FALSE;
+ }
+ zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
+ }
+ if (query) {
+ efree(query);
+ }
+
+ 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);
+ }
+
+ RETURN_BOOL(!command_failed);
+ } else {
+ PQclear(pgsql_result);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ 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 = 0, pg_null_as_len = 0, pg_fields_len;
+ char *query;
+ PGresult *pgsql_result;
+ ExecStatusType status;
+ php_stream *stream;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|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;
+ }
+
+ /* Obtain db Handler */
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
+ if (!stream) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
+ RETURN_FALSE;
+ }
+
+ if (pg_fields) {
+ spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ } else {
+ spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ }
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+ pgsql_result = PQexec(H->server, query);
+
+ efree(query);
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(H->server);
+ }
+
+ if (status == PGRES_COPY_IN && pgsql_result) {
+ char *buf;
+ int command_failed = 0;
+ size_t line_len = 0;
+
+ PQclear(pgsql_result);
+ while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
+ if (PQputCopyData(H->server, buf, line_len) != 1) {
+ efree(buf);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
+ php_stream_close(stream);
+ RETURN_FALSE;
+ }
+ 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);
+ }
+
+ RETURN_BOOL(!command_failed);
+ } else {
+ PQclear(pgsql_result);
+ php_stream_close(stream);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+
+/* {{{ 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 = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
+ char *query;
+
+ PGresult *pgsql_result;
+ ExecStatusType status;
+
+ php_stream *stream;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|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;
+ }
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_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);
+ }
+
+ if (pg_fields) {
+ spprintf(&query, 0, "COPY %s (%s) TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ } else {
+ spprintf(&query, 0, "COPY %s TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ }
+ pgsql_result = PQexec(H->server, query);
+ efree(query);
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(H->server);
+ }
+
+ if (status == PGRES_COPY_OUT && pgsql_result) {
+ PQclear(pgsql_result);
+ while (1) {
+ char *csv = NULL;
+ int ret = PQgetCopyData(H->server, &csv, 0);
+
+ if (ret == -1) {
+ break; /* done */
+ } else if (ret > 0) {
+ if (php_stream_write(stream, csv, ret) != ret) {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
+ PQfreemem(csv);
+ php_stream_close(stream);
+ RETURN_FALSE;
+ } else {
+ PQfreemem(csv);
+ }
+ } else {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
+ php_stream_close(stream);
+ RETURN_FALSE;
+ }
+ }
+ php_stream_close(stream);
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+ RETURN_TRUE;
+ } else {
+ php_stream_close(stream);
+ PQclear(pgsql_result);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ 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 = 0, pg_null_as_len = 0, pg_fields_len;
+ char *query;
+
+ PGresult *pgsql_result;
+ ExecStatusType status;
+
+ 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;
+ }
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+
+ if (pg_fields) {
+ spprintf(&query, 0, "COPY %s (%s) TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ } else {
+ spprintf(&query, 0, "COPY %s TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
+ }
+ pgsql_result = PQexec(H->server, query);
+ efree(query);
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(H->server);
+ }
+
+ if (status == PGRES_COPY_OUT && pgsql_result) {
+ PQclear(pgsql_result);
+ array_init(return_value);
+
+ while (1) {
+ char *csv = NULL;
+ int ret = PQgetCopyData(H->server, &csv, 0);
+ if (ret == -1) {
+ break; /* copy done */
+ } else if (ret > 0) {
+ add_next_index_stringl(return_value, csv, ret, 1);
+ PQfreemem(csv);
+ } else {
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
+ RETURN_FALSE;
+ }
+ }
+
+ while ((pgsql_result = PQgetResult(H->server))) {
+ PQclear(pgsql_result);
+ }
+ } else {
+ PQclear(pgsql_result);
+ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+
+/* {{{ 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)
+ 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)
+ PHP_FE_END
+};
+
+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)
+{
+ pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
+
+ switch (attr) {
+#if HAVE_PQPREPARE
+ case PDO_ATTR_EMULATE_PREPARES:
+ H->emulate_prepares = Z_LVAL_P(val);
+ return 1;
+ case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
+ H->disable_native_prepares = Z_LVAL_P(val);
+ return 1;
+#endif
+
+ default:
+ return 0;
+ }
+}
+
+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,
+ pdo_pgsql_set_attr,
+ pdo_pgsql_last_insert_id,
+ pdo_pgsql_fetch_error_func,
+ pdo_pgsql_get_attribute,
+ pdo_pgsql_check_liveness, /* check_liveness */
+ pdo_pgsql_get_driver_methods, /* get_driver_methods */
+ NULL,
+ pgsql_handle_in_transaction,
+};
+
+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;
+ long connect_timeout = 30;
+
+ 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 = ' ';
+ }
+
+ 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 connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout);
+ } else if (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 connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout);
+ } else {
+ spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout);
+ }
+
+ H->server = PQconnectdb(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;
+ }
+
+ 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 0000000..d58eebf
--- /dev/null
+++ b/ext/pdo_pgsql/pgsql_statement.c
@@ -0,0 +1,640 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 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;
+
+ 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;
+ }
+ 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 (S->cursor_name) {
+ char *q = NULL;
+
+ if (S->is_prepared) {
+ spprintf(&q, 0, "CLOSE %s", S->cursor_name);
+ S->result = PQexec(H->server, q);
+ efree(q);
+ }
+
+ spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, stmt->active_query_string);
+ S->result = PQexec(H->server, q);
+ efree(q);
+
+ /* check if declare failed */
+ 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;
+ }
+
+ /* the cursor was declared correctly */
+ S->is_prepared = 1;
+
+ /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */
+ spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name);
+ S->result = PQexec(H->server, q);
+ efree(q);
+ } else
+#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 chance 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 (sqlstate && !strcmp(sqlstate, "42P05")) {
+ char buf[100]; /* stmt_name == "pdo_crsr_%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
+ {
+ 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) {
+ 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)
+{
+#if HAVE_PQPREPARE
+ pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
+
+ 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_raise_impl_error(stmt->dbh, stmt, "HY093", param->name TSRMLS_CC);
+ return 0;
+ }
+ }
+ }
+ 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;
+ }
+ if (!S->param_values) {
+ S->param_values = ecalloc(
+ zend_hash_num_elements(stmt->bound_param_map),
+ sizeof(char*));
+ S->param_lengths = ecalloc(
+ zend_hash_num_elements(stmt->bound_param_map),
+ sizeof(int));
+ 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, &param->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(&param->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;
+ 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 {
+ SEPARATE_ZVAL_IF_NOT_REF(&param->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;
+ }
+ } else {
+#endif
+ if (param->is_param) {
+ /* We need to manually convert to a pg native boolean value */
+ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&
+ ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) {
+ SEPARATE_ZVAL(&param->parameter);
+ param->param_type = PDO_PARAM_STR;
+ ZVAL_STRINGL(param->parameter, Z_BVAL_P(param->parameter) ? "t" : "f", 1, 1);
+ }
+ }
+#if HAVE_PQPREPARE
+ }
+#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 = NULL;
+ char *q = NULL;
+ ExecStatusType status;
+
+ switch (ori) {
+ case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break;
+ case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break;
+ case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break;
+ case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break;
+ case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE %ld", offset); break;
+ case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE %ld", offset); break;
+ default:
+ return 0;
+ }
+
+ spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name);
+ efree(ori_str);
+ S->result = PQexec(S->H->server, q);
+ efree(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;
+ }
+
+ if (PQntuples(S->result)) {
+ S->current_row = 1;
+ return 1;
+ } else {
+ return 0;
+ }
+ } 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**)&param) ||
+ SUCCESS == zend_hash_find(stmt->bound_columns,
+ cols[colno].name, cols[colno].namelen,
+ (void**)&param))) {
+ 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;
+}
+
+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 {
+ char *tmp_ptr = PQunescapeBytea(*ptr, &tmp_len);
+ if (!tmp_ptr) {
+ /* PQunescapeBytea returned an error */
+ *len = 0;
+ return 0;
+ }
+ if (!tmp_len) {
+ /* Empty string, return as empty stream */
+ *ptr = (char *)php_stream_memory_open(TEMP_STREAM_READONLY, "", 0);
+ PQfreemem(tmp_ptr);
+ *len = 0;
+ } else {
+ *ptr = estrndup(tmp_ptr, tmp_len);
+ PQfreemem(tmp_ptr);
+ *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;
+ }
+ }
+
+ 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 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 0000000..b7ea262
--- /dev/null
+++ b/ext/pdo_pgsql/php_pdo_pgsql.h
@@ -0,0 +1,47 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 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 ZTS
+#include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(pdo_pgsql);
+PHP_MSHUTDOWN_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 0000000..02a6717
--- /dev/null
+++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h
@@ -0,0 +1,125 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 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;
+#if HAVE_PQPREPARE
+ /* The following two variables have the same purpose. Unfortunately we need
+ to keep track of two different attributes having the same effect.
+ It might be worth to deprecate the driver specific one soon. */
+ int emulate_prepares;
+ int disable_native_prepares;
+#endif
+ unsigned int stmt_counter;
+} 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;
+#endif
+ zend_bool is_prepared;
+} 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;
+
+#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE)
+
+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;
+};
+
+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
+};
+
+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
+ */
diff --git a/ext/pdo_pgsql/tests/bug36727.phpt b/ext/pdo_pgsql/tests/bug36727.phpt
new file mode 100644
index 0000000..34d14db
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug36727.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #36727 (segfault in bindValue() when no parameters are defined)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded');
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+require dirname(__FILE__) . '/config.inc';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+require dirname(__FILE__) . '/config.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+$stmt = $db->prepare('SELECT * FROM child');
+var_dump($stmt->bindValue(':test', 1, PDO::PARAM_INT));
+
+echo "Done\n";
+?>
+--EXPECTF--
+Warning: PDOStatement::bindValue(): SQLSTATE[HY093]: Invalid parameter number: :test in %sbug36727.php on line %d
+bool(false)
+Done \ No newline at end of file
diff --git a/ext/pdo_pgsql/tests/bug43925.phpt b/ext/pdo_pgsql/tests/bug43925.phpt
new file mode 100644
index 0000000..e22ffba
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug43925.phpt
@@ -0,0 +1,59 @@
+--TEST--
+Bug #43925 (Incorrect argument counter in prepared statements with pgsql)
+--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';
+$dbh = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+@$dbh->query('DROP TABLE nodes');
+
+$dbh->query('
+CREATE TABLE nodes
+(
+ id integer NOT NULL PRIMARY KEY
+ , root integer NOT NULL
+ , lft integer NOT NULL
+ , rgt integer NOT NULL
+);');
+
+$dbh->query('INSERT INTO nodes (id, root, lft, rgt) VALUES (1, 1, 1, 6);');
+$dbh->query('INSERT INTO nodes (id, root, lft, rgt) VALUES (2, 1, 2, 3);');
+$dbh->query('INSERT INTO nodes (id, root, lft, rgt) VALUES (3, 1, 4, 5);');
+
+
+$stmt = $dbh->prepare('
+ SELECT *
+ FROM nodes
+ WHERE (:rootId > 0 OR lft > :left OR rgt > :left)
+ AND (root = :rootId OR root = :left)
+ AND (1 > :left OR 1 < :left OR 1 = :left)
+ AND (:x > 0 OR :x < 10 OR :x > 100)
+ OR :y = 1 OR :left = 1
+');
+
+$stmt->bindValue('left', 1, PDO::PARAM_INT);
+$stmt->bindValue('rootId', 3, PDO::PARAM_INT);
+$stmt->bindValue('x', 5, PDO::PARAM_INT);
+$stmt->bindValue('y', 50, PDO::PARAM_INT);
+
+$stmt->execute();
+
+foreach ($stmt->fetchAll() as $row) {
+ print implode(' - ', $row);
+ print "\n";
+}
+
+$dbh->query('DROP TABLE nodes');
+
+?>
+--EXPECTF--
+1 - 1 - 1 - 1 - 1 - 1 - 6 - 6
+2 - 2 - 1 - 1 - 2 - 2 - 3 - 3
+3 - 3 - 1 - 1 - 4 - 4 - 5 - 5
diff --git a/ext/pdo_pgsql/tests/bug46274.phpt b/ext/pdo_pgsql/tests/bug46274.phpt
new file mode 100644
index 0000000..c34839a
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug46274.phpt
@@ -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
+}
diff --git a/ext/pdo_pgsql/tests/bug46274_2.phpt b/ext/pdo_pgsql/tests/bug46274_2.phpt
new file mode 100644
index 0000000..eb675af
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug46274_2.phpt
@@ -0,0 +1,91 @@
+--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));
+
+// Resource
+var_dump($res->fetch());
+var_dump(fread($x['blob1'], 10));
+
+// Resource
+var_dump($res->fetch());
+var_dump(fread($x['blob1'], 10));
+
+// 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"]=>
+ resource(%d) of type (stream)
+ [0]=>
+ resource(%d) of type (stream)
+}
+string(0) ""
+array(2) {
+ ["blob1"]=>
+ resource(%d) of type (stream)
+ [0]=>
+ resource(%d) of type (stream)
+}
+string(0) ""
+array(2) {
+ ["blob1"]=>
+ NULL
+ [0]=>
+ NULL
+}
diff --git a/ext/pdo_pgsql/tests/bug48764.phpt b/ext/pdo_pgsql/tests/bug48764.phpt
new file mode 100644
index 0000000..67e8f39
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug48764.phpt
@@ -0,0 +1,134 @@
+--TEST--
+Bug #48764 (PDO_pgsql::query always uses implicit prepared statements if v3 proto available)
+--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();
+
+$db = PDOTest::factory();
+
+$client_version = $db->getAttribute(PDO::ATTR_CLIENT_VERSION);
+$server_version = $db->getAttribute(PDO::ATTR_SERVER_VERSION);
+
+if (version_compare($server_version, '7.4', '<') || version_compare($client_version, '7.4', '<')) {
+ die('skip');
+}
+
+?>
+--FILE--
+<?php
+require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+echo "Test 1\n";
+bug($db);
+
+echo "Test 2\n";
+bug($db, array(PDO::ATTR_EMULATE_PREPARES => 0));
+bug($db, array(PDO::ATTR_EMULATE_PREPARES => 1));
+
+echo "Test 3\n";
+bug($db, array(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => 0));
+bug($db, array(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => 1));
+
+echo "Test 4\n";
+$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);
+bug($db);
+$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
+bug($db);
+
+echo "Test 5\n";
+$db->setAttribute(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, 1);
+bug($db);
+$db->setAttribute(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, 0);
+bug($db);
+
+
+putenv('PDOTEST_ATTR='.serialize(array(
+ PDO::ATTR_EMULATE_PREPARES => 1,
+)));
+$db = PDOTest::factory('PDO', false);
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+echo "Test 6\n";
+bug($db);
+bug($db, array(PDO::ATTR_EMULATE_PREPARES => 0));
+bug($db, array(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => 0));
+
+
+putenv('PDOTEST_ATTR='.serialize(array(
+ PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => 1,
+)));
+
+$db = PDOTest::factory('PDO', false);
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+echo "Test 7\n";
+bug($db);
+bug($db, array(PDO::ATTR_EMULATE_PREPARES => 0));
+bug($db, array(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => 0));
+
+
+putenv('PDOTEST_ATTR='.serialize(array(
+ PDO::ATTR_EMULATE_PREPARES => 1,
+ PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => 1,
+)));
+
+$db = PDOTest::factory('PDO', false);
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+echo "Test 8\n";
+bug($db);
+bug($db, array(PDO::ATTR_EMULATE_PREPARES => 0));
+bug($db, array(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => 0));
+bug($db, array(
+ PDO::ATTR_EMULATE_PREPARES => 0,
+ PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => 0,
+));
+
+
+putenv('PDOTEST_ATTR');
+
+
+function bug($db, $options = array()) {
+ try {
+ $stmt = $db->prepare("SELECT ?", $options);
+ $stmt->execute(array(1));
+ echo "OK\n";
+ } catch (PDOException $e) {
+ // Indetermined data type when using native prepared statements
+ echo $e->getCode()."\n";
+ }
+}
+
+--EXPECT--
+Test 1
+42P18
+Test 2
+42P18
+OK
+Test 3
+42P18
+OK
+Test 4
+OK
+42P18
+Test 5
+OK
+42P18
+Test 6
+OK
+42P18
+OK
+Test 7
+OK
+OK
+42P18
+Test 8
+OK
+OK
+OK
+42P18
diff --git a/ext/pdo_pgsql/tests/bug61267.phpt b/ext/pdo_pgsql/tests/bug61267.phpt
new file mode 100644
index 0000000..4214cd9
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug61267.phpt
@@ -0,0 +1,22 @@
+--TEST--
+PDO::exec() returns 0 when the statement is a SELECT.
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded');
+require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+require_once dirname(__FILE__) . '/config.inc';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
+require_once dirname(__FILE__) . '/config.inc';
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
+
+$res = $db->exec('SELECT * from generate_series(1, 42);');
+var_dump($res);
+echo "Done\n";
+?>
+--EXPECTF--
+int(0)
+Done
diff --git a/ext/pdo_pgsql/tests/bug62593.phpt b/ext/pdo_pgsql/tests/bug62593.phpt
new file mode 100644
index 0000000..e3ebf46
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug62593.phpt
@@ -0,0 +1,51 @@
+--TEST--
+PDO PgSQL Bug #62593 (Emulate prepares behave strangely with PARAM_BOOL)
+--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_EMULATE_PREPARES, true);
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+$errors = array();
+
+$value = true;
+$query = $db->prepare('SELECT :foo IS FALSE as val_is_false');
+$query->bindValue(':foo', $value, PDO::PARAM_BOOL);
+$query->execute();
+$errors[] = $query->errorInfo();
+var_dump($value);
+
+$query->bindValue(':foo', 0, PDO::PARAM_BOOL);
+$query->execute();
+$errors[] = $query->errorInfo();
+
+// Verify bindParam maintains reference and only passes when execute is called
+$value = true;
+$query->bindParam(':foo', $value, PDO::PARAM_BOOL);
+$value = false;
+$query->execute();
+$errors[] = $query->errorInfo();
+var_dump($value);
+
+$expect = 'No errors found';
+
+foreach ($errors as $error)
+{
+ if (strpos('Invalid text representation', $error[2]) !== false)
+ {
+ $expect = 'Invalid boolean found';
+ }
+}
+echo $expect;
+?>
+--EXPECTF--
+bool(true)
+bool(false)
+No errors found
diff --git a/ext/pdo_pgsql/tests/bug_33876.phpt b/ext/pdo_pgsql/tests/bug_33876.phpt
new file mode 100644
index 0000000..48618e1
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug_33876.phpt
@@ -0,0 +1,155 @@
+--TEST--
+PDO PgSQL Bug #33876 (PDO misquotes/miscasts bool(false))
+--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_ERRMODE, PDO::ERRMODE_SILENT);
+
+$db->exec("SET LC_MESSAGES='C'");
+$db->exec('CREATE TABLE test (foo varchar(5) NOT NULL, bar bool NOT NULL)');
+$db->exec("INSERT INTO test VALUES('false','f')");
+$db->exec("INSERT INTO test VALUES('true', 't')");
+
+$res = $db->prepare('SELECT foo from test where bar = ?');
+
+# this is the portable approach to binding a bool
+$res->bindValue(1, false, PDO::PARAM_BOOL);
+if (!$res->execute())
+ print_r($res->errorInfo());
+else
+ print_r($res->fetchAll(PDO::FETCH_ASSOC));
+
+# this is the portable approach to binding a bool
+$res->bindValue(1, true, PDO::PARAM_BOOL);
+if (!$res->execute())
+ print_r($res->errorInfo());
+else
+ print_r($res->fetchAll(PDO::FETCH_ASSOC));
+
+
+# true gets cast to string (because the implied default is string)
+# true-as-string is 1, so this "works"
+if (!$res->execute(array(true)))
+ print_r($res->errorInfo());
+else
+ print_r($res->fetchAll(PDO::FETCH_ASSOC));
+
+# Expected to fail; unless told otherwise, PDO assumes string inputs
+# false -> "" as string, which pgsql doesn't like
+if (!$res->execute(array(false)))
+ print_r($res->errorInfo());
+else
+ print_r($res->fetchAll(PDO::FETCH_ASSOC));
+
+# And now using emulator prepares
+echo "EMUL\n";
+
+
+$res = $db->prepare('SELECT foo from test where bar = ?', array(
+ PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => true));
+
+# this is the portable approach to binding a bool
+$res->bindValue(1, false, PDO::PARAM_BOOL);
+if (!$res->execute())
+ print_r($res->errorInfo());
+else
+ print_r($res->fetchAll(PDO::FETCH_ASSOC));
+
+# this is the portable approach to binding a bool
+$res->bindValue(1, true, PDO::PARAM_BOOL);
+if (!$res->execute())
+ print_r($res->errorInfo());
+else
+ print_r($res->fetchAll(PDO::FETCH_ASSOC));
+
+
+# true gets cast to string (because the implied default is string)
+# true-as-string is 1, so this "works"
+if (!$res->execute(array(true)))
+ print_r($res->errorInfo());
+else
+ print_r($res->fetchAll(PDO::FETCH_ASSOC));
+
+# Expected to fail; unless told otherwise, PDO assumes string inputs
+# false -> "" as string, which pgsql doesn't like
+if (!$res->execute(array(false))) {
+ $err = $res->errorInfo();
+ // Strip additional lines ouputted by recent PgSQL versions
+ $err[2] = trim(current(explode("\n", $err[2])));
+ print_r($err);
+} else {
+ print_r($res->fetchAll(PDO::FETCH_ASSOC));
+}
+
+
+
+--EXPECT--
+Array
+(
+ [0] => Array
+ (
+ [foo] => false
+ )
+
+)
+Array
+(
+ [0] => Array
+ (
+ [foo] => true
+ )
+
+)
+Array
+(
+ [0] => Array
+ (
+ [foo] => true
+ )
+
+)
+Array
+(
+ [0] => 22P02
+ [1] => 7
+ [2] => ERROR: invalid input syntax for type boolean: ""
+)
+EMUL
+Array
+(
+ [0] => Array
+ (
+ [foo] => false
+ )
+
+)
+Array
+(
+ [0] => Array
+ (
+ [foo] => true
+ )
+
+)
+Array
+(
+ [0] => Array
+ (
+ [foo] => true
+ )
+
+)
+Array
+(
+ [0] => 22P02
+ [1] => 7
+ [2] => ERROR: invalid input syntax for type boolean: ""
+)
diff --git a/ext/pdo_pgsql/tests/bug_49985.phpt b/ext/pdo_pgsql/tests/bug_49985.phpt
new file mode 100644
index 0000000..26dcfc6
--- /dev/null
+++ b/ext/pdo_pgsql/tests/bug_49985.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Bug #49985 (pdo_pgsql prepare() re-use previous aborted transaction)
+--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_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+$db->exec("CREATE TABLE test (a int PRIMARY KEY)");
+
+for ($i = 0; $i < 3; $i++) {
+ try {
+ $db->beginTransaction();
+ $stmt = $db->prepare("INSERT INTO test (a) VALUES (?)");
+ var_dump($stmt->execute(array(1)));
+ $db->commit();
+ } catch (Exception $e) {
+ echo trim(current(explode("\n", $e->getMessage())))."\n";
+ $db->rollback();
+ }
+}
+
+?>
+--EXPECTF--
+bool(true)
+SQLSTATE[23505]: %s"test_pkey"
+SQLSTATE[23505]: %s"test_pkey"
+
diff --git a/ext/pdo_pgsql/tests/common.phpt b/ext/pdo_pgsql/tests/common.phpt
new file mode 100644
index 0000000..2ecc202
--- /dev/null
+++ b/ext/pdo_pgsql/tests/common.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Postgres
+--SKIPIF--
+<?php # vim:ft=php
+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'
+);
+
+if (false !== getenv('PDO_PGSQL_TEST_DSN')) {
+ # user set them from their shell
+ $config['ENV']['PDOTEST_DSN'] = getenv('PDO_PGSQL_TEST_DSN');
+ if (false !== getenv('PDO_PGSQL_TEST_ATTR')) {
+ $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_PGSQL_TEST_ATTR');
+ }
+} else {
+ $config['ENV']['PDOTEST_DSN'] = 'pgsql:host=localhost port=5432 dbname=test user=root password=';
+}
+
+return $config;
diff --git a/ext/pdo_pgsql/tests/config.inc b/ext/pdo_pgsql/tests/config.inc
new file mode 100644
index 0000000..6d616cd
--- /dev/null
+++ b/ext/pdo_pgsql/tests/config.inc
@@ -0,0 +1,14 @@
+<?php # vim:se ft=php:
+if (false !== getenv('PDO_PGSQL_TEST_DSN')) {
+ # user set them from their shell
+ $config['ENV']['PDOTEST_DSN'] = getenv('PDO_PGSQL_TEST_DSN');
+ if (false !== getenv('PDO_PGSQL_TEST_ATTR')) {
+ $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_PGSQL_TEST_ATTR');
+ }
+} else {
+ $config['ENV']['PDOTEST_DSN'] = 'pgsql:host=localhost port=5432 dbname=test user=root password=';
+}
+
+foreach ($config['ENV'] as $k => $v) {
+ putenv("$k=$v");
+}
diff --git a/ext/pdo_pgsql/tests/copy_from.phpt b/ext/pdo_pgsql/tests/copy_from.phpt
new file mode 100644
index 0000000..2858905
--- /dev/null
+++ b/ext/pdo_pgsql/tests/copy_from.phpt
@@ -0,0 +1,386 @@
+--TEST--
+PDO PgSQL pgsqlCopyFromArray and pgsqlCopyFromFile
+--SKIPIF--
+<?php # vim:se ft=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_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
+
+$db->exec('CREATE TABLE test (a integer not null primary key, b text, c integer)');
+
+try {
+
+echo "Preparing test file and array for CopyFrom tests\n";
+
+$tableRows = array();
+$tableRowsWithDifferentNullValues = array();
+
+for($i=0;$i<3;$i++) {
+ $firstParameter = $i;
+ $secondParameter = "test insert {$i}";
+ $tableRows[] = "{$firstParameter}\t{$secondParameter}\t\\N";
+ $tableRowsWithDifferentNullValues[] = "{$firstParameter};{$secondParameter};NULL";
+ $tableRowsWithDifferentNullValuesAndSelectedFields[] = "{$firstParameter};NULL";
+}
+$filename = 'test_pgsqlCopyFromFile.csv';
+$filenameWithDifferentNullValues = 'test_pgsqlCopyFromFileWithDifferentNullValues.csv';
+$filenameWithDifferentNullValuesAndSelectedFields = 'test_pgsqlCopyFromFileWithDifferentNullValuesAndSelectedFields.csv';
+
+file_put_contents($filename, implode("\n",$tableRows));
+file_put_contents($filenameWithDifferentNullValues, implode("\n",$tableRowsWithDifferentNullValues));
+file_put_contents($filenameWithDifferentNullValuesAndSelectedFields, implode("\n",$tableRowsWithDifferentNullValuesAndSelectedFields));
+
+echo "Testing pgsqlCopyFromArray() with default parameters\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromArray('test',$tableRows));
+
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+ var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromArray() with different field separator and not null indicator\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromArray('test',$tableRowsWithDifferentNullValues,";","NULL"));
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+ var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromArray() with only selected fields\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromArray('test',$tableRowsWithDifferentNullValuesAndSelectedFields,";","NULL",'a,c'));
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+ var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromArray() with error\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromArray('test_error',$tableRowsWithDifferentNullValuesAndSelectedFields,";","NULL",'a,c'));
+$db->rollback();
+
+
+echo "Testing pgsqlCopyFromFile() with default parameters\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromFile('test',$filename));
+
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+ var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromFile() with different field separator and not null indicator\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromFile('test',$filenameWithDifferentNullValues,";","NULL"));
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+ var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromFile() with only selected fields\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromFile('test',$filenameWithDifferentNullValuesAndSelectedFields,";","NULL",'a,c'));
+$stmt = $db->query("select * from test");
+foreach($stmt as $r) {
+ var_dump($r);
+}
+$db->rollback();
+
+echo "Testing pgsqlCopyFromFile() with error\n";
+$db->beginTransaction();
+var_dump($db->pgsqlCopyFromFile('test_error',$filenameWithDifferentNullValuesAndSelectedFields,";","NULL",'a,c'));
+$db->rollback();
+
+} catch (Exception $e) {
+ /* catch exceptions so that we can show the relative error */
+ echo "Exception! at line ", $e->getLine(), "\n";
+ var_dump($e->getMessage());
+}
+if(isset($filename)) {
+ @unlink($filename);
+}
+?>
+--EXPECT--
+Preparing test file and array for CopyFrom tests
+Testing pgsqlCopyFromArray() with default parameters
+bool(true)
+array(6) {
+ ["a"]=>
+ int(0)
+ [0]=>
+ int(0)
+ ["b"]=>
+ string(13) "test insert 0"
+ [1]=>
+ string(13) "test insert 0"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(1)
+ [0]=>
+ int(1)
+ ["b"]=>
+ string(13) "test insert 1"
+ [1]=>
+ string(13) "test insert 1"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(2)
+ [0]=>
+ int(2)
+ ["b"]=>
+ string(13) "test insert 2"
+ [1]=>
+ string(13) "test insert 2"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+Testing pgsqlCopyFromArray() with different field separator and not null indicator
+bool(true)
+array(6) {
+ ["a"]=>
+ int(0)
+ [0]=>
+ int(0)
+ ["b"]=>
+ string(13) "test insert 0"
+ [1]=>
+ string(13) "test insert 0"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(1)
+ [0]=>
+ int(1)
+ ["b"]=>
+ string(13) "test insert 1"
+ [1]=>
+ string(13) "test insert 1"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(2)
+ [0]=>
+ int(2)
+ ["b"]=>
+ string(13) "test insert 2"
+ [1]=>
+ string(13) "test insert 2"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+Testing pgsqlCopyFromArray() with only selected fields
+bool(true)
+array(6) {
+ ["a"]=>
+ int(0)
+ [0]=>
+ int(0)
+ ["b"]=>
+ NULL
+ [1]=>
+ NULL
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(1)
+ [0]=>
+ int(1)
+ ["b"]=>
+ NULL
+ [1]=>
+ NULL
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(2)
+ [0]=>
+ int(2)
+ ["b"]=>
+ NULL
+ [1]=>
+ NULL
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+Testing pgsqlCopyFromArray() with error
+bool(false)
+Testing pgsqlCopyFromFile() with default parameters
+bool(true)
+array(6) {
+ ["a"]=>
+ int(0)
+ [0]=>
+ int(0)
+ ["b"]=>
+ string(13) "test insert 0"
+ [1]=>
+ string(13) "test insert 0"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(1)
+ [0]=>
+ int(1)
+ ["b"]=>
+ string(13) "test insert 1"
+ [1]=>
+ string(13) "test insert 1"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(2)
+ [0]=>
+ int(2)
+ ["b"]=>
+ string(13) "test insert 2"
+ [1]=>
+ string(13) "test insert 2"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+Testing pgsqlCopyFromFile() with different field separator and not null indicator
+bool(true)
+array(6) {
+ ["a"]=>
+ int(0)
+ [0]=>
+ int(0)
+ ["b"]=>
+ string(13) "test insert 0"
+ [1]=>
+ string(13) "test insert 0"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(1)
+ [0]=>
+ int(1)
+ ["b"]=>
+ string(13) "test insert 1"
+ [1]=>
+ string(13) "test insert 1"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(2)
+ [0]=>
+ int(2)
+ ["b"]=>
+ string(13) "test insert 2"
+ [1]=>
+ string(13) "test insert 2"
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+Testing pgsqlCopyFromFile() with only selected fields
+bool(true)
+array(6) {
+ ["a"]=>
+ int(0)
+ [0]=>
+ int(0)
+ ["b"]=>
+ NULL
+ [1]=>
+ NULL
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(1)
+ [0]=>
+ int(1)
+ ["b"]=>
+ NULL
+ [1]=>
+ NULL
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+array(6) {
+ ["a"]=>
+ int(2)
+ [0]=>
+ int(2)
+ ["b"]=>
+ NULL
+ [1]=>
+ NULL
+ ["c"]=>
+ NULL
+ [2]=>
+ NULL
+}
+Testing pgsqlCopyFromFile() with error
+bool(false) \ No newline at end of file
diff --git a/ext/pdo_pgsql/tests/copy_to.phpt b/ext/pdo_pgsql/tests/copy_to.phpt
new file mode 100644
index 0000000..1dc7d1d
--- /dev/null
+++ b/ext/pdo_pgsql/tests/copy_to.phpt
@@ -0,0 +1,129 @@
+--TEST--
+PDO PgSQL pgsqlCopyToArray and pgsqlCopyToFile
+--SKIPIF--
+<?php # vim:se ft=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_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
+
+$db->exec('CREATE TABLE test (a integer not null primary key, b text, c integer)');
+
+$db->beginTransaction();
+try {
+
+echo "Preparing test table for CopyTo tests\n";
+$stmt = $db->prepare("INSERT INTO test (a, b, c) values (?, ?, ?)");
+
+for($i=0;$i<3;$i++) {
+ $firstParameter = $i;
+ $secondParameter = "test insert {$i}";
+ $thirdParameter = NULL;
+ $stmt->bindValue(1, $firstParameter);
+ $stmt->bindValue(2, $secondParameter);
+ $stmt->bindValue(3, $thirdParameter);
+ $stmt->execute();
+}
+
+$db->commit();
+
+echo "Testing pgsqlCopyToArray() with default parameters\n";
+var_dump($db->pgsqlCopyToArray('test'));
+echo "Testing pgsqlCopyToArray() with different field separator and not null indicator\n";
+var_dump($db->pgsqlCopyToArray('test',";","NULL"));
+echo "Testing pgsqlCopyToArray() with only selected fields\n";
+var_dump($db->pgsqlCopyToArray('test',";","NULL",'a,c'));
+
+echo "Testing pgsqlCopyToArray() with error\n";
+var_dump($db->pgsqlCopyToArray('test_error'));
+
+
+echo "Testing pgsqlCopyToFile() with default parameters\n";
+
+$filename="test_pgsqlCopyToFile.csv";
+var_dump($db->pgsqlCopyToFile('test',$filename));
+echo file_get_contents($filename);
+echo "Testing pgsqlCopyToFile() with different field separator and not null indicator\n";
+var_dump($db->pgsqlCopyToFile('test',$filename,";","NULL"));
+echo file_get_contents($filename);
+echo "Testing pgsqlCopyToFile() with only selected fields\n";
+var_dump($db->pgsqlCopyToFile('test',$filename,";","NULL",'a,c'));
+echo file_get_contents($filename);
+
+echo "Testing pgsqlCopyToFile() with error\n";
+var_dump($db->pgsqlCopyToFile('test_error',$filename));
+
+
+} catch (Exception $e) {
+ /* catch exceptions so that we can show the relative error */
+ echo "Exception! at line ", $e->getLine(), "\n";
+ var_dump($e->getMessage());
+}
+if(isset($filename)) {
+ @unlink($filename);
+}
+?>
+--EXPECT--
+Preparing test table for CopyTo tests
+Testing pgsqlCopyToArray() with default parameters
+array(3) {
+ [0]=>
+ string(19) "0 test insert 0 \N
+"
+ [1]=>
+ string(19) "1 test insert 1 \N
+"
+ [2]=>
+ string(19) "2 test insert 2 \N
+"
+}
+Testing pgsqlCopyToArray() with different field separator and not null indicator
+array(3) {
+ [0]=>
+ string(21) "0;test insert 0;NULL
+"
+ [1]=>
+ string(21) "1;test insert 1;NULL
+"
+ [2]=>
+ string(21) "2;test insert 2;NULL
+"
+}
+Testing pgsqlCopyToArray() with only selected fields
+array(3) {
+ [0]=>
+ string(7) "0;NULL
+"
+ [1]=>
+ string(7) "1;NULL
+"
+ [2]=>
+ string(7) "2;NULL
+"
+}
+Testing pgsqlCopyToArray() with error
+bool(false)
+Testing pgsqlCopyToFile() with default parameters
+bool(true)
+0 test insert 0 \N
+1 test insert 1 \N
+2 test insert 2 \N
+Testing pgsqlCopyToFile() with different field separator and not null indicator
+bool(true)
+0;test insert 0;NULL
+1;test insert 1;NULL
+2;test insert 2;NULL
+Testing pgsqlCopyToFile() with only selected fields
+bool(true)
+0;NULL
+1;NULL
+2;NULL
+Testing pgsqlCopyToFile() with error
+bool(false) \ No newline at end of file
diff --git a/ext/pdo_pgsql/tests/is_in_transaction.phpt b/ext/pdo_pgsql/tests/is_in_transaction.phpt
new file mode 100644
index 0000000..99ff561
--- /dev/null
+++ b/ext/pdo_pgsql/tests/is_in_transaction.phpt
@@ -0,0 +1,66 @@
+--TEST--
+PDO PgSQL isInTransaction
+--SKIPIF--
+<?php # vim:se ft=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_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
+
+$db->exec('CREATE TABLE test (a integer not null primary key, b text)');
+
+$db->beginTransaction();
+try {
+echo "Test PDO::PGSQL_TRANSACTION_INTRANS\n";
+var_dump($db->inTransaction());
+
+$stmt = $db->prepare("INSERT INTO test (a, b) values (?, ?)");
+$stmt->bindValue(1, 1);
+$stmt->bindValue(2, "test insert");
+$stmt->execute();
+
+$db->commit();
+
+echo "Test PDO::PGSQL_TRANSACTION_IDLE\n";
+var_dump($db->inTransaction());
+
+$db->beginTransaction();
+
+try {
+$stmt = $db->prepare("INSERT INTO test (a, b) values (?, ?)");
+$stmt->bindValue(1, "error");
+$stmt->bindValue(2, "test insert");
+$stmt->execute();
+} catch (Exception $e) {
+ /* We catch the exception because the execute will give error and we must test the PDO::PGSQL_TRANSACTION_ERROR */
+ echo "Test PDO::PGSQL_TRANSACTION_INERROR\n";
+ var_dump($db->inTransaction());
+ $db->rollBack();
+}
+
+echo "Test PDO::PGSQL_TRANSACTION_IDLE\n";
+var_dump($db->inTransaction());
+
+} catch (Exception $e) {
+ /* catch exceptions so that we can show the relative error */
+ echo "Exception! at line ", $e->getLine(), "\n";
+ var_dump($e->getMessage());
+}
+
+?>
+--EXPECT--
+Test PDO::PGSQL_TRANSACTION_INTRANS
+int(2)
+Test PDO::PGSQL_TRANSACTION_IDLE
+int(0)
+Test PDO::PGSQL_TRANSACTION_INERROR
+int(3)
+Test PDO::PGSQL_TRANSACTION_IDLE
+int(0)
diff --git a/ext/pdo_pgsql/tests/large_objects.phpt b/ext/pdo_pgsql/tests/large_objects.phpt
new file mode 100644
index 0000000..316963a
--- /dev/null
+++ b/ext/pdo_pgsql/tests/large_objects.phpt
@@ -0,0 +1,93 @@
+--TEST--
+PDO PgSQL Large Objects
+--SKIPIF--
+<?php # vim:se ft=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_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
+
+$db->exec('CREATE TABLE test (blobid integer not null primary key, bloboid OID)');
+
+$db->beginTransaction();
+$oid = $db->pgsqlLOBCreate();
+try {
+$stm = $db->pgsqlLOBOpen($oid, 'w+b');
+fwrite($stm, "Hello dude\n");
+
+$stmt = $db->prepare("INSERT INTO test (blobid, bloboid) values (?, ?)");
+$stmt->bindValue(1, 1);
+/* bind as LOB; the oid from the pgsql stream will be inserted instead
+ * of the stream contents. Binding other streams will attempt to bind
+ * as bytea, and will most likely lead to an error.
+ * You can also just bind the $oid in as a string. */
+$stmt->bindParam(2, $stm, PDO::PARAM_LOB);
+$stmt->execute();
+$stm = null;
+
+/* Pull it out */
+$stmt = $db->prepare("SELECT * from test");
+$stmt->bindColumn('bloboid', $lob, PDO::PARAM_LOB);
+$stmt->execute();
+echo "Fetching:\n";
+while (($row = $stmt->fetch(PDO::FETCH_ASSOC))) {
+ var_dump($row['blobid']);
+ var_dump(stream_get_contents($lob));
+}
+echo "Fetched!\n";
+
+/* Try again, with late bind */
+$stmt = $db->prepare("SELECT * from test");
+$stmt->execute();
+$stmt->bindColumn('bloboid', $lob, PDO::PARAM_LOB);
+echo "Fetching late bind:\n";
+while (($row = $stmt->fetch(PDO::FETCH_ASSOC))) {
+ var_dump($row['blobid']);
+ var_dump(is_int($row['bloboid']));
+}
+echo "Fetched!\n";
+
+/* Try again, with NO bind */
+$stmt = $db->prepare("SELECT * from test");
+$stmt->execute();
+$stmt->bindColumn('bloboid', $lob, PDO::PARAM_LOB);
+echo "Fetching NO bind:\n";
+while (($row = $stmt->fetch(PDO::FETCH_ASSOC))) {
+ var_dump($row['blobid']);
+ var_dump(is_int($row['bloboid']));
+}
+echo "Fetched!\n";
+
+} catch (Exception $e) {
+ /* catch exceptions so that we can guarantee to clean
+ * up the LOB */
+ echo "Exception! at line ", $e->getLine(), "\n";
+ var_dump($e->getMessage());
+}
+
+/* Now to remove the large object from the database, so it doesn't
+ * linger and clutter up the storage */
+$db->pgsqlLOBUnlink($oid);
+
+?>
+--EXPECT--
+Fetching:
+int(1)
+string(11) "Hello dude
+"
+Fetched!
+Fetching late bind:
+int(1)
+bool(true)
+Fetched!
+Fetching NO bind:
+int(1)
+bool(true)
+Fetched!