summaryrefslogtreecommitdiff
path: root/ext/pgsql
diff options
context:
space:
mode:
Diffstat (limited to 'ext/pgsql')
-rw-r--r--ext/pgsql/CREDITS2
-rw-r--r--ext/pgsql/README235
-rw-r--r--ext/pgsql/config.m4109
-rw-r--r--ext/pgsql/config.w3215
-rw-r--r--ext/pgsql/mysql_users.php82
-rw-r--r--ext/pgsql/pgsql.c6484
-rw-r--r--ext/pgsql/pgsql.dsp167
-rw-r--r--ext/pgsql/pgsql.mak170
-rw-r--r--ext/pgsql/php_pgsql.h309
-rw-r--r--ext/pgsql/tests/01createdb.phpt28
-rw-r--r--ext/pgsql/tests/02connection.phpt59
-rw-r--r--ext/pgsql/tests/03sync_query.phpt73
-rw-r--r--ext/pgsql/tests/04async_query.phpt65
-rw-r--r--ext/pgsql/tests/05large_object.phpt82
-rw-r--r--ext/pgsql/tests/06copy.phpt22
-rw-r--r--ext/pgsql/tests/07optional.phpt24
-rw-r--r--ext/pgsql/tests/08escape.phpt95
-rw-r--r--ext/pgsql/tests/09notice.phpt38
-rw-r--r--ext/pgsql/tests/10pg_convert.phpt29
-rw-r--r--ext/pgsql/tests/10pg_convert_85.phpt29
-rw-r--r--ext/pgsql/tests/11pg_meta_data.phpt64
-rw-r--r--ext/pgsql/tests/12pg_insert.phpt24
-rw-r--r--ext/pgsql/tests/12pg_insert_85.phpt24
-rw-r--r--ext/pgsql/tests/13pg_select.phpt37
-rw-r--r--ext/pgsql/tests/13pg_select_85.phpt37
-rw-r--r--ext/pgsql/tests/14pg_update.phpt25
-rw-r--r--ext/pgsql/tests/14pg_update_85.phpt25
-rw-r--r--ext/pgsql/tests/15pg_delete.phpt23
-rw-r--r--ext/pgsql/tests/16pg_result_status.phpt19
-rw-r--r--ext/pgsql/tests/17result.phpt67
-rw-r--r--ext/pgsql/tests/18pg_escape_bytea.phpt28
-rw-r--r--ext/pgsql/tests/19pg_ping.phpt15
-rw-r--r--ext/pgsql/tests/20pg_get_pid.phpt17
-rw-r--r--ext/pgsql/tests/21pg_get_notify.phpt20
-rw-r--r--ext/pgsql/tests/22pg_fetch_object.phpt37
-rw-r--r--ext/pgsql/tests/23sync_query_params.phpt59
-rw-r--r--ext/pgsql/tests/24sync_query_prepared.phpt65
-rw-r--r--ext/pgsql/tests/25async_query_params.phpt70
-rw-r--r--ext/pgsql/tests/26async_query_prepared.phpt106
-rw-r--r--ext/pgsql/tests/27large_object_oid.phpt47
-rw-r--r--ext/pgsql/tests/28large_object_import_oid.phpt48
-rw-r--r--ext/pgsql/tests/80_bug14383.phpt52
-rw-r--r--ext/pgsql/tests/80_bug24499.phpt66
-rw-r--r--ext/pgsql/tests/80_bug27597.phpt60
-rw-r--r--ext/pgsql/tests/80_bug32223.phpt58
-rw-r--r--ext/pgsql/tests/80_bug32223b.phpt60
-rw-r--r--ext/pgsql/tests/80_bug36625.phpt49
-rw-r--r--ext/pgsql/tests/80_bug39971.phpt30
-rw-r--r--ext/pgsql/tests/80_bug42783.phpt34
-rw-r--r--ext/pgsql/tests/98old_api.phpt33
-rw-r--r--ext/pgsql/tests/9999dropdb.phpt18
-rw-r--r--ext/pgsql/tests/README16
-rw-r--r--ext/pgsql/tests/bug37100.phpt46
-rw-r--r--ext/pgsql/tests/bug37100_85.phpt46
-rw-r--r--ext/pgsql/tests/bug47199.phpt67
-rw-r--r--ext/pgsql/tests/bug60244.phpt57
-rw-r--r--ext/pgsql/tests/config.inc12
-rw-r--r--ext/pgsql/tests/lcmess.inc21
-rw-r--r--ext/pgsql/tests/pg_delete_001.phpt86
-rw-r--r--ext/pgsql/tests/pg_insert_001.phpt40
-rw-r--r--ext/pgsql/tests/pg_meta_data_001.phpt92
-rw-r--r--ext/pgsql/tests/pg_select_001.phpt63
-rw-r--r--ext/pgsql/tests/pg_update_001.phpt51
-rw-r--r--ext/pgsql/tests/php.gifbin0 -> 3872 bytes
-rw-r--r--ext/pgsql/tests/skipif.inc32
65 files changed, 10063 insertions, 0 deletions
diff --git a/ext/pgsql/CREDITS b/ext/pgsql/CREDITS
new file mode 100644
index 0000000..7930bc8
--- /dev/null
+++ b/ext/pgsql/CREDITS
@@ -0,0 +1,2 @@
+PostgreSQL
+Jouni Ahto, Zeev Suraski, Yasuo Ohgaki, Chris Kings-Lynne
diff --git a/ext/pgsql/README b/ext/pgsql/README
new file mode 100644
index 0000000..2b4c412
--- /dev/null
+++ b/ext/pgsql/README
@@ -0,0 +1,235 @@
+==== About This Module ===
+PostgreSQL module provides access to PostgreSQL server from
+PHP script. This module uses PostgreSQL C client lib called libpq.
+It is important that you use libpq that is later than backend
+(PostgreSQL Server) version. Otherwise, you may experience
+strange problems.
+
+Please send e-mail to yohgaki@php.net if you have comments for
+pgsql module. I appreciate your feedback.
+
+==== API Change ===
+Older PHP than 4.2.0, pg_loimport()/pg_loexport() connection
+parameter as last parameter, not like other functions. From 4.2.0,
+connection parameter became 1st parameter. Old syntax is preserved,
+but it will raise NOTICE error message.
+
+pg_connect()/pg_pconnect() has obsolete multi parameter syntax.
+This syntax will be deleted in 4.3.0 or later.
+
+Omitting connectin parameter is NOT recommended. Connection
+parameter may be required for future PHP version. Specify connection
+always if you don't want to rewrite code when it is changed.
+
+==== Function Name Change ====
+Function names are going to be changed to confirm coding
+standard. MySQL module has been done this already. Function names will
+be changed as follows.
+
+pg_errormessage -> pg_error_message
+pg_cmdtuples -> pg_affected_rows
+pg_fieldnum -> pg_field_num
+and so on. Except pg_cmdtuples, under scores '_' will be added to
+names.
+
+Older names will become aliases of new functions for backward
+compatibility.
+
+Manual will be updated when this change is commited to CVS source.
+
+==== Configure Option Notes ====
+You cannot specify PostgreSQL source directly to build PostgreSQL
+module with specific version. You need to install PostgreSQL
+somewhere in your system to build PHP with PostgreSQL support.
+
+==== Note For PostgreSQL 7.2 ====
+I've tested upto 7.2.2.
+
+==== TODO List ===
+Make pg_convert() smater.
+ - Better regex
+ - User defiend type support
+Support async connection.
+
+==== Experimental Functions =====
+
+WARNING: API/behavior may be changed without notice.
+
+Async query can improve application performance
+*significantly*. Please test and report any failure to
+yohgaki@php.net
+
+There are some cases that async functions blocks process. Even if
+process was blocked, functions work as expected. (except it blocks
+process) These are cases that process is blocked. Refer to libpq
+manual for details. Followings are common cases that async functions
+are blocked.
+
+ - If libpq is compile with USE_SSL, some async functions are
+ blocked.
+ - If libpq under Win32 is *NOT* compiled with
+ WIN32_NON_BLOCKING_CONNECTIONS, non-blocking connection will block.
+
+Async function may also block if you have not retrive result and
+send or execute query. If there is result left on connection,
+pg_send_query() will block until last query is completed.
+
+Garbages are cleaned when resource is cleaned up. There is no need to
+clean up query result if it is not needed.
+
+Please refer to libpq manual or source for details.
+These functions are *NOT* supposed to be documented, yet.
+API may be changed.
+
+NOTE: These functions are added in PHP 4.2.0 unless they are mentioned.
+
+-------------------------------------------------------------------
+bool pg_send_query(resource connection, string query)
+
+Sends async query to backend. Result may be retrieved with
+pg_get_result(). It does not accept multiple query, but it accepts
+multiple queries at once. Each result may be retrieved separately by
+pg_get_result().
+
+--------------------------------------------------------------------
+bool pg_cancel_query(resource connection)
+
+Cancels currently executing async query already sent to PostgreSQL
+server. This function is useful when user request time consuming query
+to server. It cannot cancel query executed by pg_exec(), since
+pg_exec() is a blocking function.
+
+--------------------------------------------------------------------
+resource pg_get_result(resource conn)
+
+Gets pgsql query result resource. Returned value can be fed to
+pg_result()/pg_fetch_*(). pg_get_result() may block if result is not
+ready to be retrived. Use pg_is_busy() to check result is ready to be
+retrieved or not. If multiple query is sent to backend, it may be
+retrieved one by one using pg_get_result(). If there is no result left
+in connection, it returns false.
+
+--------------------------------------------------------------------
+bool pg_connection_busy(resource connection)
+
+Returns connections is executing query or not.
+
+--------------------------------------------------------------------
+int pg_connection_status(resource connection)
+
+Gets connection status. It returns PGSQL_CONNECTION_OK or
+PGSQL_CONNECTION_BAD.
+
+--------------------------------------------------------------------
+bool pg_connection_reset(resource connection)
+
+Resets communication port to Postgresql server using the same
+connection parameter. It's useful for error recovery.
+
+--------------------------------------------------------------------
+string pg_result_error(resource result)
+
+Get error message associated with result
+
+--------------------------------------------------------------------
+int pg_result_status(resource result)
+
+Get status of query result
+
+--------------------------------------------------------------------
+
+
+Copy functions
+
+--------------------------------------------------------------------
+mixed pg_copy_to(int connection_id, string table_name,
+ [, string delim [, string null_as]])
+
+nt pg_copy_from(int connection_id, string table_name, array rows
+ [, string delim [, string null_as]])
+
+--------------------------------------------------------------------
+
+Utility functions
+
+--------------------------------------------------------------------
+string pg_escape_string(string data)
+Escape string or binary for SQL statemen (7.2 or later)
+
+
+string pg_escape_bytea(string data)
+Escape string or binary for SQL statement (7.2 or later)
+
+--------------------------------------------------------------------
+
+Large Object Functions
+
+--------------------------------------------------------------------
+int pg_lo_tell(resource large_object)
+Returns current position of large object
+
+--------------------------------------------------------------------
+bool pg_lo_lseek(resource large_object, int offset[, int whence])
+Seeks position of large object
+
+--------------------------------------------------------------------
+
+Notice message function
+
+--------------------------------------------------------------------
+
+string pg_last_notice(resource connection)
+Returns the last notice set by the backend
+
+This function is fully implemed in only in current CVS version.
+PHP 4.3.0 supposed to included fully implemented version.
+
+NOTE: Added in PHP 4.0.6, but there is bug in notice message handling
+in PHP 4.0.6. Do no use 4.0.6 with pgsql module!!
+
+--------------------------------------------------------------------
+
+Utility functions (for PHP 4.3.0)
+
+--------------------------------------------------------------------
+array pg_metadata(resource db, string table)
+ Get metadata
+
+--------------------------------------------------------------------
+array pg_convert(resource db, string table, array values)
+ Check and convert values for PostgreSQL SQL statement
+
+--------------------------------------------------------------------
+bool pg_insert(resource db, string table, array values[, bool convert[, bool async]])
+ Insert values (filed=>value) to table
+
+--------------------------------------------------------------------
+bool pg_update(resource db, string table, array fields, array ids[, bool convert[, bool async]])
+ Update table using values (field=>value) and ids (id=>value)
+
+--------------------------------------------------------------------
+bool pg_delete(resource db, string table, array ids[, bool convert[, bool async]])
+ Delete records has ids (id=>value)
+
+--------------------------------------------------------------------
+array pg_select(resource db, string table, array ids[, bool convert])
+ Select records that has ids (id=>value)
+
+--------------------------------------------------------------------
+array pg_get_notify([resource db[, notify]])
+ Get notify message on the connection
+
+--------------------------------------------------------------------
+string pg_unescape_bytea(string bytea_data)
+ Unescape bytea field data
+
+--------------------------------------------------------------------
+bool pg_ping(resource db)
+ ping database connection and try to reset connection if it's
+ broken
+
+-------------------------------------------------------------------
+
+Again, experimental functions are subject to be changed without
+notice.
+
diff --git a/ext/pgsql/config.m4 b/ext/pgsql/config.m4
new file mode 100644
index 0000000..bddb77a
--- /dev/null
+++ b/ext/pgsql/config.m4
@@ -0,0 +1,109 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(pgsql,for PostgreSQL support,
+[ --with-pgsql[=DIR] Include PostgreSQL support. DIR is the PostgreSQL
+ base install directory or the path to pg_config])
+
+if test "$PHP_PGSQL" != "no"; then
+ PHP_EXPAND_PATH($PGSQL_INCLUDE, PGSQL_INCLUDE)
+
+ AC_MSG_CHECKING(for pg_config)
+ for i in $PHP_PGSQL $PHP_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`
+ if test -r "$PGSQL_INCLUDE/pg_config.h"; then
+ AC_DEFINE(HAVE_PG_CONFIG_H,1,[Whether to have pg_config.h])
+ fi
+ else
+ AC_MSG_RESULT(not found)
+ if test "$PHP_PGSQL" = "yes"; then
+ PGSQL_SEARCH_PATHS="/usr /usr/local /usr/local/pgsql"
+ else
+ PGSQL_SEARCH_PATHS=$PHP_PGSQL
+ fi
+
+ for i in $PGSQL_SEARCH_PATHS; do
+ for j in include include/pgsql include/postgres include/postgresql ""; do
+ if test -r "$i/$j/libpq-fe.h"; then
+ PGSQL_INC_BASE=$i
+ PGSQL_INCLUDE=$i/$j
+ if test -r "$i/$j/pg_config.h"; then
+ AC_DEFINE(HAVE_PG_CONFIG_H,1,[Whether to have pg_config.h])
+ fi
+ fi
+ done
+
+ for j in lib $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_PGSQL,1,[Whether to build PostgreSQL support or not])
+ old_LIBS=$LIBS
+ old_LDFLAGS=$LDFLAGS
+ LDFLAGS="-L$PGSQL_LIBDIR $LDFLAGS"
+ AC_CHECK_LIB(pq, PQescapeString,AC_DEFINE(HAVE_PQESCAPE,1,[PostgreSQL 7.2.0 or later]))
+ AC_CHECK_LIB(pq, PQunescapeBytea,AC_DEFINE(HAVE_PQUNESCAPEBYTEA,1,[PostgreSQL 7.3.0 or later]))
+ AC_CHECK_LIB(pq, PQsetnonblocking,AC_DEFINE(HAVE_PQSETNONBLOCKING,1,[PostgreSQL 7.0.x or later]))
+ AC_CHECK_LIB(pq, PQcmdTuples,AC_DEFINE(HAVE_PQCMDTUPLES,1,[Broken libpq under windows]))
+ AC_CHECK_LIB(pq, PQoidValue,AC_DEFINE(HAVE_PQOIDVALUE,1,[Older PostgreSQL]))
+ AC_CHECK_LIB(pq, PQclientEncoding,AC_DEFINE(HAVE_PQCLIENTENCODING,1,[PostgreSQL 7.0.x or later]))
+ AC_CHECK_LIB(pq, PQparameterStatus,AC_DEFINE(HAVE_PQPARAMETERSTATUS,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQprotocolVersion,AC_DEFINE(HAVE_PQPROTOCOLVERSION,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQtransactionStatus,AC_DEFINE(HAVE_PGTRANSACTIONSTATUS,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQexecParams,AC_DEFINE(HAVE_PQEXECPARAMS,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQprepare,AC_DEFINE(HAVE_PQPREPARE,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQexecPrepared,AC_DEFINE(HAVE_PQEXECPREPARED,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQresultErrorField,AC_DEFINE(HAVE_PQRESULTERRORFIELD,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQsendQueryParams,AC_DEFINE(HAVE_PQSENDQUERYPARAMS,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQsendPrepare,AC_DEFINE(HAVE_PQSENDPREPARE,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQsendQueryPrepared,AC_DEFINE(HAVE_PQSENDQUERYPREPARED,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQputCopyData,AC_DEFINE(HAVE_PQPUTCOPYDATA,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQputCopyEnd,AC_DEFINE(HAVE_PQPUTCOPYEND,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQgetCopyData,AC_DEFINE(HAVE_PQGETCOPYDATA,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQfreemem,AC_DEFINE(HAVE_PQFREEMEM,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQsetErrorVerbosity,AC_DEFINE(HAVE_PQSETERRORVERBOSITY,1,[PostgreSQL 7.4 or later]))
+ AC_CHECK_LIB(pq, PQftable,AC_DEFINE(HAVE_PQFTABLE,1,[PostgreSQL 7.4 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]))
+ AC_CHECK_LIB(pq, lo_create, AC_DEFINE(HAVE_PG_LO_CREATE,1,[PostgreSQL 8.1 or later]))
+ AC_CHECK_LIB(pq, lo_import_with_oid, AC_DEFINE(HAVE_PG_LO_IMPORT_WITH_OID,1,[PostgreSQL 8.4 or later]))
+ AC_CHECK_LIB(pq, PQescapeLiteral, AC_DEFINE(HAVE_PQESCAPELITERAL,1,[PostgreSQL 9.0 or later]))
+ LIBS=$old_LIBS
+ LDFLAGS=$old_LDFLAGS
+
+ PHP_ADD_LIBRARY_WITH_PATH(pq, $PGSQL_LIBDIR, PGSQL_SHARED_LIBADD)
+ PHP_SUBST(PGSQL_SHARED_LIBADD)
+
+ PHP_ADD_INCLUDE($PGSQL_INCLUDE)
+
+ PHP_NEW_EXTENSION(pgsql, pgsql.c, $ext_shared)
+fi
+
+
diff --git a/ext/pgsql/config.w32 b/ext/pgsql/config.w32
new file mode 100644
index 0000000..0a577e9
--- /dev/null
+++ b/ext/pgsql/config.w32
@@ -0,0 +1,15 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_WITH("pgsql", "PostgreSQL support", "no");
+
+if (PHP_PGSQL != "no") {
+ if (CHECK_LIB("libpq.lib", "pgsql", PHP_PGSQL) &&
+ CHECK_HEADER_ADD_INCLUDE("libpq-fe.h", "CFLAGS_PGSQL", PHP_PGSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\pgsql;" + PHP_PHP_BUILD + "\\include\\libpq;" + PHP_PGSQL)) {
+ EXTENSION("pgsql", "pgsql.c");
+ AC_DEFINE('HAVE_PGSQL', 1, 'Have PostgreSQL library');
+ ADD_FLAG("CFLAGS_PGSQL", "/D HAVE_PG_CONFIG_H /D PGSQL_EXPORTS /D HAVE_PQSETNONBLOCKING /D HAVE_PQCMDTUPLES /D HAVE_PQCLIENTENCODING /D HAVE_PQESCAPE /D HAVE_PQPARAMETERSTATUS /D HAVE_PGTRANSACTIONSTATUS /D HAVE_PQEXECPARAMS /D HAVE_PQPREPARE /D HAVE_PQEXECPREPARED /D HAVE_PQRESULTERRORFIELD /D HAVE_PQSENDQUERYPARAMS /D HAVE_PQSENDPREPARE /D HAVE_PQSENDQUERYPREPARED /D HAVE_PQPUTCOPYDATA /D HAVE_PQPUTCOPYEND /D HAVE_PQGETCOPYDATA /D HAVE_PQSETERRORVERBOSITY /D HAVE_PQUNESCAPEBYTEA /D HAVE_PQFTABLE /D HAVE_PQESCAPE_CONN /D HAVE_PQESCAPE_BYTEA_CONN /D HAVE_PQFREEMEM /D HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT /D HAVE_PQPROTOCOLVERSION /D HAVE_PG_LO_CREATE");
+ } else {
+ WARNING("pgsql not enabled; libraries and headers not found");
+ }
+}
diff --git a/ext/pgsql/mysql_users.php b/ext/pgsql/mysql_users.php
new file mode 100644
index 0000000..65a2c33
--- /dev/null
+++ b/ext/pgsql/mysql_users.php
@@ -0,0 +1,82 @@
+<?php
+/*
+ * File: mysql_users.php
+ * Author: Yasuo Ohgaki <yohgaki@php.net>
+ *
+ * This file contains example user defined functions that does
+ * similar to MySQL functions. They can be implemented as module
+ * functions, but there won't be many users need them.
+ *
+ * Requires: PostgreSQL 7.2.x
+ */
+
+/*
+ * mysql_list_dbs()
+ *
+ * This function should be needed, since PostgreSQL connection
+ * binds database.
+ */
+function pg_list_dbs($db)
+{
+ assert(is_resource($db));
+ $query = '
+SELECT
+ d.datname as "Name",
+ u.usename as "Owner",
+ pg_encoding_to_char(d.encoding) as "Encoding"
+FROM
+ pg_database d LEFT JOIN pg_user u ON d.datdba = u.usesysid
+ORDER BY 1;
+';
+ return pg_query($db, $query);
+}
+
+
+/*
+ * mysql_list_tables()
+ */
+function pg_list_tables($db)
+{
+ assert(is_resource($db));
+ $query = "
+SELECT
+ c.relname as \"Name\",
+ CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' END as \"Type\",
+ u.usename as \"Owner\"
+FROM
+ pg_class c LEFT JOIN pg_user u ON c.relowner = u.usesysid
+WHERE
+ c.relkind IN ('r','v','S','')
+ AND c.relname !~ '^pg_'
+ORDER BY 1;
+";
+ return pg_query($db, $query);
+}
+
+/*
+ * mysql_list_fields()
+ *
+ * See also pg_meta_data(). It returns field defintion as array.
+ */
+function pg_list_fields($db, $table)
+{
+ assert(is_resource($db));
+ $query = "
+SELECT
+ a.attname,
+ format_type(a.atttypid, a.atttypmod),
+ a.attnotnull,
+ a.atthasdef,
+ a.attnum
+FROM
+ pg_class c,
+ pg_attribute a
+WHERE
+ c.relname = '".$table."'
+ AND a.attnum > 0 AND a.attrelid = c.oid
+ORDER BY a.attnum;
+";
+ return pg_query($db, $query);
+}
+
+?>
diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c
new file mode 100644
index 0000000..d01dda6
--- /dev/null
+++ b/ext/pgsql/pgsql.c
@@ -0,0 +1,6484 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Zeev Suraski <zeev@zend.com> |
+ | Jouni Ahto <jouni.ahto@exdec.fi> |
+ | Yasuo Ohgaki <yohgaki@php.net> |
+ | Youichi Iwakiri <yiwakiri@st.rim.or.jp> (pg_copy_*) |
+ | Chris Kings-Lynne <chriskl@php.net> (v3 protocol) |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include <stdlib.h>
+
+#define PHP_PGSQL_PRIVATE 1
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define SMART_STR_PREALLOC 512
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/php_standard.h"
+#include "ext/standard/php_smart_str.h"
+#include "ext/ereg/php_regex.h"
+
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include "php_pgsql.h"
+#include "php_globals.h"
+#include "zend_exceptions.h"
+
+#if HAVE_PGSQL
+
+#ifndef InvalidOid
+#define InvalidOid ((Oid) 0)
+#endif
+
+#define PGSQL_ASSOC 1<<0
+#define PGSQL_NUM 1<<1
+#define PGSQL_BOTH (PGSQL_ASSOC|PGSQL_NUM)
+
+#define PGSQL_STATUS_LONG 1
+#define PGSQL_STATUS_STRING 2
+
+#define PGSQL_MAX_LENGTH_OF_LONG 30
+#define PGSQL_MAX_LENGTH_OF_DOUBLE 60
+
+#define PGSQL_RETURN_OID(oid) do { \
+ if (oid > LONG_MAX) { \
+ smart_str s = {0}; \
+ smart_str_append_unsigned(&s, oid); \
+ smart_str_0(&s); \
+ RETURN_STRINGL(s.c, s.len, 0); \
+ } \
+ RETURN_LONG((long)oid); \
+} while(0)
+
+
+#if HAVE_PQSETNONBLOCKING
+#define PQ_SETNONBLOCKING(pg_link, flag) PQsetnonblocking(pg_link, flag)
+#else
+#define PQ_SETNONBLOCKING(pg_link, flag) 0
+#endif
+
+#define CHECK_DEFAULT_LINK(x) if ((x) == -1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "No PostgreSQL link opened yet"); }
+
+#ifndef HAVE_PQFREEMEM
+#define PQfreemem free
+#endif
+
+ZEND_DECLARE_MODULE_GLOBALS(pgsql)
+static PHP_GINIT_FUNCTION(pgsql);
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connect, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection_string)
+ ZEND_ARG_INFO(0, connect_type)
+ ZEND_ARG_INFO(0, host)
+ ZEND_ARG_INFO(0, port)
+ ZEND_ARG_INFO(0, options)
+ ZEND_ARG_INFO(0, tty)
+ ZEND_ARG_INFO(0, database)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_pconnect, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection_string)
+ ZEND_ARG_INFO(0, host)
+ ZEND_ARG_INFO(0, port)
+ ZEND_ARG_INFO(0, options)
+ ZEND_ARG_INFO(0, tty)
+ ZEND_ARG_INFO(0, database)
+ZEND_END_ARG_INFO()
+
+#if HAVE_PQPARAMETERSTATUS
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_parameter_status, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, param_name)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_close, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_dbname, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_error, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_options, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_port, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_tty, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_host, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_version, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_ping, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO()
+
+#if HAVE_PQEXECPARAMS
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_query_params, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, query)
+ ZEND_ARG_INFO(0, params)
+ZEND_END_ARG_INFO()
+#endif
+
+#if HAVE_PQPREPARE
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_prepare, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, stmtname)
+ ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO()
+#endif
+
+#if HAVE_PQEXECPREPARED
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_execute, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, stmtname)
+ ZEND_ARG_INFO(0, params)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_rows, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_num_fields, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ZEND_END_ARG_INFO()
+
+#if HAVE_PQCMDTUPLES
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_affected_rows, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_notice, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+#ifdef HAVE_PQFTABLE
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_table, 0, 0, 2)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, field_number)
+ ZEND_ARG_INFO(0, oid_only)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_name, 0, 0, 2)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, field_number)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_size, 0, 0, 2)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, field_number)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type, 0, 0, 2)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, field_number)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_type_oid, 0, 0, 2)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, field_number)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_num, 0, 0, 2)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, field_name)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_result, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, row_number)
+ ZEND_ARG_INFO(0, field_name)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_row, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, row)
+ ZEND_ARG_INFO(0, result_type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_assoc, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, row)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_array, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, row)
+ ZEND_ARG_INFO(0, result_type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_object, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, row)
+ ZEND_ARG_INFO(0, class_name)
+ ZEND_ARG_INFO(0, l)
+ ZEND_ARG_INFO(0, ctor_params)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all_columns, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, column_number)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_seek, 0, 0, 2)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, offset)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_prtlen, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, row)
+ ZEND_ARG_INFO(0, field_name_or_number)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_field_is_null, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, row)
+ ZEND_ARG_INFO(0, field_name_or_number)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_free_result, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_oid, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_trace, 0, 0, 1)
+ ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_untrace, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_create, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, large_object_id)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_unlink, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, large_object_oid)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_open, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, large_object_oid)
+ ZEND_ARG_INFO(0, mode)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_close, 0, 0, 1)
+ ZEND_ARG_INFO(0, large_object)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read, 0, 0, 1)
+ ZEND_ARG_INFO(0, large_object)
+ ZEND_ARG_INFO(0, len)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_write, 0, 0, 2)
+ ZEND_ARG_INFO(0, large_object)
+ ZEND_ARG_INFO(0, buf)
+ ZEND_ARG_INFO(0, len)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_read_all, 0, 0, 1)
+ ZEND_ARG_INFO(0, large_object)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_import, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, filename)
+ ZEND_ARG_INFO(0, large_object_oid)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_export, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, objoid)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_seek, 0, 0, 2)
+ ZEND_ARG_INFO(0, large_object)
+ ZEND_ARG_INFO(0, offset)
+ ZEND_ARG_INFO(0, whence)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_lo_tell, 0, 0, 1)
+ ZEND_ARG_INFO(0, large_object)
+ZEND_END_ARG_INFO()
+
+#if HAVE_PQSETERRORVERBOSITY
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_error_verbosity, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, verbosity)
+ZEND_END_ARG_INFO()
+#endif
+
+#if HAVE_PQCLIENTENCODING
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_set_client_encoding, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, encoding)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_client_encoding, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_end_copy, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_put_line, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_to, 0, 0, 2)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, table_name)
+ ZEND_ARG_INFO(0, delimiter)
+ ZEND_ARG_INFO(0, null_as)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_copy_from, 0, 0, 3)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, table_name)
+ ZEND_ARG_INFO(0, rows)
+ ZEND_ARG_INFO(0, delimiter)
+ ZEND_ARG_INFO(0, null_as)
+ZEND_END_ARG_INFO()
+
+#if HAVE_PQESCAPE
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_string, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_bytea, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_unescape_bytea, 0, 0, 1)
+ ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO()
+#endif
+
+#if HAVE_PQESCAPE
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_literal, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_escape_identifier, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ZEND_END_ARG_INFO()
+
+#if HAVE_PQRESULTERRORFIELD
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_error_field, 0, 0, 2)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, fieldcode)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_status, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+#if HAVE_PGTRANSACTIONSTATUS
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_transaction_status, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_reset, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_cancel_query, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_connection_busy, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query, 0, 0, 2)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO()
+
+#if HAVE_PQSENDQUERYPARAMS
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_query_params, 0, 0, 3)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, query)
+ ZEND_ARG_INFO(0, params)
+ZEND_END_ARG_INFO()
+#endif
+
+#if HAVE_PQSENDPREPARE
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_prepare, 0, 0, 3)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, stmtname)
+ ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO()
+#endif
+
+#if HAVE_PQSENDQUERYPREPARED
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_send_execute, 0, 0, 3)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, stmtname)
+ ZEND_ARG_INFO(0, params)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_result, 0, 0, 1)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_result_status, 0, 0, 1)
+ ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, result_type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_notify, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, e)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_get_pid, 0, 0, 0)
+ ZEND_ARG_INFO(0, connection)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_meta_data, 0, 0, 2)
+ ZEND_ARG_INFO(0, db)
+ ZEND_ARG_INFO(0, table)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_convert, 0, 0, 3)
+ ZEND_ARG_INFO(0, db)
+ ZEND_ARG_INFO(0, table)
+ ZEND_ARG_INFO(0, values)
+ ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_insert, 0, 0, 3)
+ ZEND_ARG_INFO(0, db)
+ ZEND_ARG_INFO(0, table)
+ ZEND_ARG_INFO(0, values)
+ ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_update, 0, 0, 4)
+ ZEND_ARG_INFO(0, db)
+ ZEND_ARG_INFO(0, table)
+ ZEND_ARG_INFO(0, fields)
+ ZEND_ARG_INFO(0, ids)
+ ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_delete, 0, 0, 3)
+ ZEND_ARG_INFO(0, db)
+ ZEND_ARG_INFO(0, table)
+ ZEND_ARG_INFO(0, ids)
+ ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_select, 0, 0, 3)
+ ZEND_ARG_INFO(0, db)
+ ZEND_ARG_INFO(0, table)
+ ZEND_ARG_INFO(0, ids)
+ ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ pgsql_functions[]
+ */
+const zend_function_entry pgsql_functions[] = {
+ /* connection functions */
+ PHP_FE(pg_connect, arginfo_pg_connect)
+ PHP_FE(pg_pconnect, arginfo_pg_pconnect)
+ PHP_FE(pg_close, arginfo_pg_close)
+ PHP_FE(pg_connection_status, arginfo_pg_connection_status)
+ PHP_FE(pg_connection_busy, arginfo_pg_connection_busy)
+ PHP_FE(pg_connection_reset, arginfo_pg_connection_reset)
+ PHP_FE(pg_host, arginfo_pg_host)
+ PHP_FE(pg_dbname, arginfo_pg_dbname)
+ PHP_FE(pg_port, arginfo_pg_port)
+ PHP_FE(pg_tty, arginfo_pg_tty)
+ PHP_FE(pg_options, arginfo_pg_options)
+ PHP_FE(pg_version, arginfo_pg_version)
+ PHP_FE(pg_ping, arginfo_pg_ping)
+#if HAVE_PQPARAMETERSTATUS
+ PHP_FE(pg_parameter_status, arginfo_pg_parameter_status)
+#endif
+#if HAVE_PGTRANSACTIONSTATUS
+ PHP_FE(pg_transaction_status, arginfo_pg_transaction_status)
+#endif
+ /* query functions */
+ PHP_FE(pg_query, arginfo_pg_query)
+#if HAVE_PQEXECPARAMS
+ PHP_FE(pg_query_params, arginfo_pg_query_params)
+#endif
+#if HAVE_PQPREPARE
+ PHP_FE(pg_prepare, arginfo_pg_prepare)
+#endif
+#if HAVE_PQEXECPREPARED
+ PHP_FE(pg_execute, arginfo_pg_execute)
+#endif
+ PHP_FE(pg_send_query, arginfo_pg_send_query)
+#if HAVE_PQSENDQUERYPARAMS
+ PHP_FE(pg_send_query_params, arginfo_pg_send_query_params)
+#endif
+#if HAVE_PQSENDPREPARE
+ PHP_FE(pg_send_prepare, arginfo_pg_send_prepare)
+#endif
+#if HAVE_PQSENDQUERYPREPARED
+ PHP_FE(pg_send_execute, arginfo_pg_send_execute)
+#endif
+ PHP_FE(pg_cancel_query, arginfo_pg_cancel_query)
+ /* result functions */
+ PHP_FE(pg_fetch_result, arginfo_pg_fetch_result)
+ PHP_FE(pg_fetch_row, arginfo_pg_fetch_row)
+ PHP_FE(pg_fetch_assoc, arginfo_pg_fetch_assoc)
+ PHP_FE(pg_fetch_array, arginfo_pg_fetch_array)
+ PHP_FE(pg_fetch_object, arginfo_pg_fetch_object)
+ PHP_FE(pg_fetch_all, arginfo_pg_fetch_all)
+ PHP_FE(pg_fetch_all_columns, arginfo_pg_fetch_all_columns)
+#if HAVE_PQCMDTUPLES
+ PHP_FE(pg_affected_rows,arginfo_pg_affected_rows)
+#endif
+ PHP_FE(pg_get_result, arginfo_pg_get_result)
+ PHP_FE(pg_result_seek, arginfo_pg_result_seek)
+ PHP_FE(pg_result_status,arginfo_pg_result_status)
+ PHP_FE(pg_free_result, arginfo_pg_free_result)
+ PHP_FE(pg_last_oid, arginfo_pg_last_oid)
+ PHP_FE(pg_num_rows, arginfo_pg_num_rows)
+ PHP_FE(pg_num_fields, arginfo_pg_num_fields)
+ PHP_FE(pg_field_name, arginfo_pg_field_name)
+ PHP_FE(pg_field_num, arginfo_pg_field_num)
+ PHP_FE(pg_field_size, arginfo_pg_field_size)
+ PHP_FE(pg_field_type, arginfo_pg_field_type)
+ PHP_FE(pg_field_type_oid, arginfo_pg_field_type_oid)
+ PHP_FE(pg_field_prtlen, arginfo_pg_field_prtlen)
+ PHP_FE(pg_field_is_null,arginfo_pg_field_is_null)
+#ifdef HAVE_PQFTABLE
+ PHP_FE(pg_field_table, arginfo_pg_field_table)
+#endif
+ /* async message function */
+ PHP_FE(pg_get_notify, arginfo_pg_get_notify)
+ PHP_FE(pg_get_pid, arginfo_pg_get_pid)
+ /* error message functions */
+ PHP_FE(pg_result_error, arginfo_pg_result_error)
+#if HAVE_PQRESULTERRORFIELD
+ PHP_FE(pg_result_error_field, arginfo_pg_result_error_field)
+#endif
+ PHP_FE(pg_last_error, arginfo_pg_last_error)
+ PHP_FE(pg_last_notice, arginfo_pg_last_notice)
+ /* copy functions */
+ PHP_FE(pg_put_line, arginfo_pg_put_line)
+ PHP_FE(pg_end_copy, arginfo_pg_end_copy)
+ PHP_FE(pg_copy_to, arginfo_pg_copy_to)
+ PHP_FE(pg_copy_from, arginfo_pg_copy_from)
+ /* debug functions */
+ PHP_FE(pg_trace, arginfo_pg_trace)
+ PHP_FE(pg_untrace, arginfo_pg_untrace)
+ /* large object functions */
+ PHP_FE(pg_lo_create, arginfo_pg_lo_create)
+ PHP_FE(pg_lo_unlink, arginfo_pg_lo_unlink)
+ PHP_FE(pg_lo_open, arginfo_pg_lo_open)
+ PHP_FE(pg_lo_close, arginfo_pg_lo_close)
+ PHP_FE(pg_lo_read, arginfo_pg_lo_read)
+ PHP_FE(pg_lo_write, arginfo_pg_lo_write)
+ PHP_FE(pg_lo_read_all, arginfo_pg_lo_read_all)
+ PHP_FE(pg_lo_import, arginfo_pg_lo_import)
+ PHP_FE(pg_lo_export, arginfo_pg_lo_export)
+ PHP_FE(pg_lo_seek, arginfo_pg_lo_seek)
+ PHP_FE(pg_lo_tell, arginfo_pg_lo_tell)
+ /* utility functions */
+#if HAVE_PQESCAPE
+ PHP_FE(pg_escape_string, arginfo_pg_escape_string)
+ PHP_FE(pg_escape_bytea, arginfo_pg_escape_bytea)
+ PHP_FE(pg_unescape_bytea, arginfo_pg_unescape_bytea)
+ PHP_FE(pg_escape_literal, arginfo_pg_escape_literal)
+ PHP_FE(pg_escape_identifier, arginfo_pg_escape_identifier)
+#endif
+#if HAVE_PQSETERRORVERBOSITY
+ PHP_FE(pg_set_error_verbosity, arginfo_pg_set_error_verbosity)
+#endif
+#if HAVE_PQCLIENTENCODING
+ PHP_FE(pg_client_encoding, arginfo_pg_client_encoding)
+ PHP_FE(pg_set_client_encoding, arginfo_pg_set_client_encoding)
+#endif
+ /* misc function */
+ PHP_FE(pg_meta_data, arginfo_pg_meta_data)
+ PHP_FE(pg_convert, arginfo_pg_convert)
+ PHP_FE(pg_insert, arginfo_pg_insert)
+ PHP_FE(pg_update, arginfo_pg_update)
+ PHP_FE(pg_delete, arginfo_pg_delete)
+ PHP_FE(pg_select, arginfo_pg_select)
+ /* aliases for downwards compatibility */
+ PHP_FALIAS(pg_exec, pg_query, arginfo_pg_query)
+ PHP_FALIAS(pg_getlastoid, pg_last_oid, arginfo_pg_last_oid)
+#if HAVE_PQCMDTUPLES
+ PHP_FALIAS(pg_cmdtuples, pg_affected_rows, arginfo_pg_affected_rows)
+#endif
+ PHP_FALIAS(pg_errormessage, pg_last_error, arginfo_pg_last_error)
+ PHP_FALIAS(pg_numrows, pg_num_rows, arginfo_pg_num_rows)
+ PHP_FALIAS(pg_numfields, pg_num_fields, arginfo_pg_num_fields)
+ PHP_FALIAS(pg_fieldname, pg_field_name, arginfo_pg_field_name)
+ PHP_FALIAS(pg_fieldsize, pg_field_size, arginfo_pg_field_size)
+ PHP_FALIAS(pg_fieldtype, pg_field_type, arginfo_pg_field_type)
+ PHP_FALIAS(pg_fieldnum, pg_field_num, arginfo_pg_field_num)
+ PHP_FALIAS(pg_fieldprtlen, pg_field_prtlen, arginfo_pg_field_prtlen)
+ PHP_FALIAS(pg_fieldisnull, pg_field_is_null, arginfo_pg_field_is_null)
+ PHP_FALIAS(pg_freeresult, pg_free_result, arginfo_pg_free_result)
+ PHP_FALIAS(pg_result, pg_fetch_result, arginfo_pg_get_result)
+ PHP_FALIAS(pg_loreadall, pg_lo_read_all, arginfo_pg_lo_read_all)
+ PHP_FALIAS(pg_locreate, pg_lo_create, arginfo_pg_lo_create)
+ PHP_FALIAS(pg_lounlink, pg_lo_unlink, arginfo_pg_lo_unlink)
+ PHP_FALIAS(pg_loopen, pg_lo_open, arginfo_pg_lo_open)
+ PHP_FALIAS(pg_loclose, pg_lo_close, arginfo_pg_lo_close)
+ PHP_FALIAS(pg_loread, pg_lo_read, arginfo_pg_lo_read)
+ PHP_FALIAS(pg_lowrite, pg_lo_write, arginfo_pg_lo_write)
+ PHP_FALIAS(pg_loimport, pg_lo_import, arginfo_pg_lo_import)
+ PHP_FALIAS(pg_loexport, pg_lo_export, arginfo_pg_lo_export)
+#if HAVE_PQCLIENTENCODING
+ PHP_FALIAS(pg_clientencoding, pg_client_encoding, arginfo_pg_client_encoding)
+ PHP_FALIAS(pg_setclientencoding, pg_set_client_encoding, arginfo_pg_set_client_encoding)
+#endif
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ pgsql_module_entry
+ */
+zend_module_entry pgsql_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "pgsql",
+ pgsql_functions,
+ PHP_MINIT(pgsql),
+ PHP_MSHUTDOWN(pgsql),
+ PHP_RINIT(pgsql),
+ PHP_RSHUTDOWN(pgsql),
+ PHP_MINFO(pgsql),
+ NO_VERSION_YET,
+ PHP_MODULE_GLOBALS(pgsql),
+ PHP_GINIT(pgsql),
+ NULL,
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
+};
+/* }}} */
+
+#ifdef COMPILE_DL_PGSQL
+ZEND_GET_MODULE(pgsql)
+#endif
+
+static int le_link, le_plink, le_result, le_lofp, le_string;
+
+/* {{{ _php_pgsql_trim_message */
+static char * _php_pgsql_trim_message(const char *message, int *len)
+{
+ register int i = strlen(message)-1;
+
+ 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;
+ if (len) {
+ *len = i;
+ }
+ return estrndup(message, i);
+}
+/* }}} */
+
+/* {{{ _php_pgsql_trim_result */
+static inline char * _php_pgsql_trim_result(PGconn * pgsql, char **buf)
+{
+ return *buf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL);
+}
+/* }}} */
+
+#define PQErrorMessageTrim(pgsql, buf) _php_pgsql_trim_result(pgsql, buf)
+
+#define PHP_PQ_ERROR(text, pgsql) { \
+ char *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql), NULL); \
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, text, msgbuf); \
+ efree(msgbuf); \
+} \
+
+/* {{{ php_pgsql_set_default_link
+ */
+static void php_pgsql_set_default_link(int id TSRMLS_DC)
+{
+ zend_list_addref(id);
+
+ if (PGG(default_link) != -1) {
+ zend_list_delete(PGG(default_link));
+ }
+
+ PGG(default_link) = id;
+}
+/* }}} */
+
+/* {{{ _close_pgsql_link
+ */
+static void _close_pgsql_link(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ PGconn *link = (PGconn *)rsrc->ptr;
+ PGresult *res;
+
+ while ((res = PQgetResult(link))) {
+ PQclear(res);
+ }
+ PQfinish(link);
+ PGG(num_links)--;
+}
+/* }}} */
+
+/* {{{ _close_pgsql_plink
+ */
+static void _close_pgsql_plink(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ PGconn *link = (PGconn *)rsrc->ptr;
+ PGresult *res;
+
+ while ((res = PQgetResult(link))) {
+ PQclear(res);
+ }
+ PQfinish(link);
+ PGG(num_persistent)--;
+ PGG(num_links)--;
+}
+/* }}} */
+
+/* {{{ _php_pgsql_notice_handler
+ */
+static void _php_pgsql_notice_handler(void *resource_id, const char *message)
+{
+ php_pgsql_notice *notice;
+
+ TSRMLS_FETCH();
+ if (! PGG(ignore_notices)) {
+ notice = (php_pgsql_notice *)emalloc(sizeof(php_pgsql_notice));
+ notice->message = _php_pgsql_trim_message(message, (int *)&notice->len);
+ if (PGG(log_notices)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s", notice->message);
+ }
+ zend_hash_index_update(&PGG(notices), (ulong)resource_id, (void **)&notice, sizeof(php_pgsql_notice *), NULL);
+ }
+}
+/* }}} */
+
+#define PHP_PGSQL_NOTICE_PTR_DTOR (void (*)(void *))_php_pgsql_notice_ptr_dtor
+
+/* {{{ _php_pgsql_notice_dtor
+ */
+static void _php_pgsql_notice_ptr_dtor(void **ptr)
+{
+ php_pgsql_notice *notice = (php_pgsql_notice *)*ptr;
+ if (notice) {
+ efree(notice->message);
+ efree(notice);
+ notice = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ _rollback_transactions
+ */
+static int _rollback_transactions(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ PGconn *link;
+ PGresult *res;
+ int orig;
+
+ if (Z_TYPE_P(rsrc) != le_plink)
+ return 0;
+
+ link = (PGconn *) rsrc->ptr;
+
+ if (PQ_SETNONBLOCKING(link, 0)) {
+ php_error_docref("ref.pgsql" TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
+ return -1;
+ }
+
+ while ((res = PQgetResult(link))) {
+ PQclear(res);
+ }
+#if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
+ if ((PQprotocolVersion(link) >= 3 && PQtransactionStatus(link) != PQTRANS_IDLE) || PQprotocolVersion(link) < 3)
+#endif
+ {
+ orig = PGG(ignore_notices);
+ PGG(ignore_notices) = 1;
+#if HAVE_PGTRANSACTIONSTATUS && HAVE_PQPROTOCOLVERSION
+ res = PQexec(link,"ROLLBACK;");
+#else
+ res = PQexec(link,"BEGIN;");
+ PQclear(res);
+ res = PQexec(link,"ROLLBACK;");
+#endif
+ PQclear(res);
+ PGG(ignore_notices) = orig;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/* {{{ _free_ptr
+ */
+static void _free_ptr(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ pgLofp *lofp = (pgLofp *)rsrc->ptr;
+ efree(lofp);
+}
+/* }}} */
+
+/* {{{ _free_result
+ */
+static void _free_result(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ pgsql_result_handle *pg_result = (pgsql_result_handle *)rsrc->ptr;
+
+ PQclear(pg_result->result);
+ efree(pg_result);
+}
+/* }}} */
+
+/* {{{ PHP_INI
+ */
+PHP_INI_BEGIN()
+STD_PHP_INI_BOOLEAN( "pgsql.allow_persistent", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_persistent, zend_pgsql_globals, pgsql_globals)
+STD_PHP_INI_ENTRY_EX("pgsql.max_persistent", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_persistent, zend_pgsql_globals, pgsql_globals, display_link_numbers)
+STD_PHP_INI_ENTRY_EX("pgsql.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_pgsql_globals, pgsql_globals, display_link_numbers)
+STD_PHP_INI_BOOLEAN( "pgsql.auto_reset_persistent", "0", PHP_INI_SYSTEM, OnUpdateBool, auto_reset_persistent, zend_pgsql_globals, pgsql_globals)
+STD_PHP_INI_BOOLEAN( "pgsql.ignore_notice", "0", PHP_INI_ALL, OnUpdateBool, ignore_notices, zend_pgsql_globals, pgsql_globals)
+STD_PHP_INI_BOOLEAN( "pgsql.log_notice", "0", PHP_INI_ALL, OnUpdateBool, log_notices, zend_pgsql_globals, pgsql_globals)
+PHP_INI_END()
+/* }}} */
+
+/* {{{ PHP_GINIT_FUNCTION
+ */
+static PHP_GINIT_FUNCTION(pgsql)
+{
+ memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
+ /* Initilize notice message hash at MINIT only */
+ zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, PHP_PGSQL_NOTICE_PTR_DTOR, 1, 0);
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(pgsql)
+{
+ REGISTER_INI_ENTRIES();
+
+ le_link = zend_register_list_destructors_ex(_close_pgsql_link, NULL, "pgsql link", module_number);
+ le_plink = zend_register_list_destructors_ex(NULL, _close_pgsql_plink, "pgsql link persistent", module_number);
+ le_result = zend_register_list_destructors_ex(_free_result, NULL, "pgsql result", module_number);
+ le_lofp = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql large object", module_number);
+ le_string = zend_register_list_destructors_ex(_free_ptr, NULL, "pgsql string", module_number);
+#if HAVE_PG_CONFIG_H
+ /* PG_VERSION - libpq version */
+ REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION", PG_VERSION, CONST_CS | CONST_PERSISTENT);
+ REGISTER_STRING_CONSTANT("PGSQL_LIBPQ_VERSION_STR", PG_VERSION_STR, CONST_CS | CONST_PERSISTENT);
+#endif
+ /* For connection option */
+ REGISTER_LONG_CONSTANT("PGSQL_CONNECT_FORCE_NEW", PGSQL_CONNECT_FORCE_NEW, CONST_CS | CONST_PERSISTENT);
+ /* For pg_fetch_array() */
+ REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
+ /* For pg_connection_status() */
+ REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | CONST_PERSISTENT);
+#if HAVE_PGTRANSACTIONSTATUS
+ /* For pg_transaction_status() */
+ REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_IDLE", PQTRANS_IDLE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_ACTIVE", PQTRANS_ACTIVE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INTRANS", PQTRANS_INTRANS, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_INERROR", PQTRANS_INERROR, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_TRANSACTION_UNKNOWN", PQTRANS_UNKNOWN, CONST_CS | CONST_PERSISTENT);
+#endif
+#if HAVE_PQSETERRORVERBOSITY
+ /* For pg_set_error_verbosity() */
+ REGISTER_LONG_CONSTANT("PGSQL_ERRORS_TERSE", PQERRORS_TERSE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_ERRORS_DEFAULT", PQERRORS_DEFAULT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_ERRORS_VERBOSE", PQERRORS_VERBOSE, CONST_CS | CONST_PERSISTENT);
+#endif
+ /* For lo_seek() */
+ REGISTER_LONG_CONSTANT("PGSQL_SEEK_SET", SEEK_SET, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_SEEK_CUR", SEEK_CUR, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_SEEK_END", SEEK_END, CONST_CS | CONST_PERSISTENT);
+ /* For pg_result_status() return value type */
+ REGISTER_LONG_CONSTANT("PGSQL_STATUS_LONG", PGSQL_STATUS_LONG, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_STATUS_STRING", PGSQL_STATUS_STRING, CONST_CS | CONST_PERSISTENT);
+ /* For pg_result_status() return value */
+ REGISTER_LONG_CONSTANT("PGSQL_EMPTY_QUERY", PGRES_EMPTY_QUERY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_COMMAND_OK", PGRES_COMMAND_OK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_TUPLES_OK", PGRES_TUPLES_OK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_COPY_OUT", PGRES_COPY_OUT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_COPY_IN", PGRES_COPY_IN, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_BAD_RESPONSE", PGRES_BAD_RESPONSE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_NONFATAL_ERROR", PGRES_NONFATAL_ERROR, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_FATAL_ERROR", PGRES_FATAL_ERROR, CONST_CS | CONST_PERSISTENT);
+#if HAVE_PQRESULTERRORFIELD
+ /* For pg_result_error_field() field codes */
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_SEVERITY", PG_DIAG_SEVERITY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_SQLSTATE", PG_DIAG_SQLSTATE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_PRIMARY", PG_DIAG_MESSAGE_PRIMARY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_DETAIL", PG_DIAG_MESSAGE_DETAIL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_MESSAGE_HINT", PG_DIAG_MESSAGE_HINT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_STATEMENT_POSITION", PG_DIAG_STATEMENT_POSITION, CONST_CS | CONST_PERSISTENT);
+#ifdef PG_DIAG_INTERNAL_POSITION
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_POSITION", PG_DIAG_INTERNAL_POSITION, CONST_CS | CONST_PERSISTENT);
+#endif
+#ifdef PG_DIAG_INTERNAL_QUERY
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_INTERNAL_QUERY", PG_DIAG_INTERNAL_QUERY, CONST_CS | CONST_PERSISTENT);
+#endif
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_CONTEXT", PG_DIAG_CONTEXT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FILE", PG_DIAG_SOURCE_FILE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_LINE", PG_DIAG_SOURCE_LINE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DIAG_SOURCE_FUNCTION", PG_DIAG_SOURCE_FUNCTION, CONST_CS | CONST_PERSISTENT);
+#endif
+ /* pg_convert options */
+ REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_DEFAULT", PGSQL_CONV_IGNORE_DEFAULT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_CONV_FORCE_NULL", PGSQL_CONV_FORCE_NULL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_CONV_IGNORE_NOT_NULL", PGSQL_CONV_IGNORE_NOT_NULL, CONST_CS | CONST_PERSISTENT);
+ /* pg_insert/update/delete/select options */
+ REGISTER_LONG_CONSTANT("PGSQL_DML_NO_CONV", PGSQL_DML_NO_CONV, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DML_EXEC", PGSQL_DML_EXEC, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DML_ASYNC", PGSQL_DML_ASYNC, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_DML_STRING", PGSQL_DML_STRING, CONST_CS | CONST_PERSISTENT);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(pgsql)
+{
+ UNREGISTER_INI_ENTRIES();
+ zend_hash_destroy(&PGG(notices));
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RINIT_FUNCTION
+ */
+PHP_RINIT_FUNCTION(pgsql)
+{
+ PGG(default_link)=-1;
+ PGG(num_links) = PGG(num_persistent);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+PHP_RSHUTDOWN_FUNCTION(pgsql)
+{
+ /* clean up notice messages */
+ zend_hash_clean(&PGG(notices));
+ /* clean up persistent connection */
+ zend_hash_apply(&EG(persistent_list), (apply_func_t) _rollback_transactions TSRMLS_CC);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(pgsql)
+{
+ char buf[256];
+
+ php_info_print_table_start();
+ php_info_print_table_header(2, "PostgreSQL Support", "enabled");
+#if HAVE_PG_CONFIG_H
+ php_info_print_table_row(2, "PostgreSQL(libpq) Version", PG_VERSION);
+ php_info_print_table_row(2, "PostgreSQL(libpq) ", PG_VERSION_STR);
+#ifdef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
+ php_info_print_table_row(2, "Multibyte character support", "enabled");
+#else
+ php_info_print_table_row(2, "Multibyte character support", "disabled");
+#endif
+#ifdef USE_SSL
+ php_info_print_table_row(2, "SSL support", "enabled");
+#else
+ php_info_print_table_row(2, "SSL support", "disabled");
+#endif
+#endif /* HAVE_PG_CONFIG_H */
+ snprintf(buf, sizeof(buf), "%ld", PGG(num_persistent));
+ php_info_print_table_row(2, "Active Persistent Links", buf);
+ snprintf(buf, sizeof(buf), "%ld", PGG(num_links));
+ php_info_print_table_row(2, "Active Links", buf);
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+
+
+/* {{{ php_pgsql_do_connect
+ */
+static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
+{
+ char *host=NULL,*port=NULL,*options=NULL,*tty=NULL,*dbname=NULL,*connstring=NULL;
+ PGconn *pgsql;
+ smart_str str = {0};
+ zval **args[5];
+ int i, connect_type = 0;
+ PGresult *pg_result;
+
+ if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 5
+ || zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ smart_str_appends(&str, "pgsql");
+
+ for (i = 0; i < ZEND_NUM_ARGS(); i++) {
+ /* make sure that the PGSQL_CONNECT_FORCE_NEW bit is not part of the hash so that subsequent connections
+ * can re-use this connection. Bug #39979
+ */
+ if (i == 1 && ZEND_NUM_ARGS() == 2 && Z_TYPE_PP(args[i]) == IS_LONG) {
+ if (Z_LVAL_PP(args[1]) == PGSQL_CONNECT_FORCE_NEW) {
+ continue;
+ } else if (Z_LVAL_PP(args[1]) & PGSQL_CONNECT_FORCE_NEW) {
+ smart_str_append_long(&str, Z_LVAL_PP(args[1]) ^ PGSQL_CONNECT_FORCE_NEW);
+ }
+ }
+ convert_to_string_ex(args[i]);
+ smart_str_appendc(&str, '_');
+ smart_str_appendl(&str, Z_STRVAL_PP(args[i]), Z_STRLEN_PP(args[i]));
+ }
+
+ smart_str_0(&str);
+
+ if (ZEND_NUM_ARGS() == 1) { /* new style, using connection string */
+ connstring = Z_STRVAL_PP(args[0]);
+ } else if (ZEND_NUM_ARGS() == 2 ) { /* Safe to add conntype_option, since 2 args was illegal */
+ connstring = Z_STRVAL_PP(args[0]);
+ convert_to_long_ex(args[1]);
+ connect_type = Z_LVAL_PP(args[1]);
+ } else {
+ host = Z_STRVAL_PP(args[0]);
+ port = Z_STRVAL_PP(args[1]);
+ dbname = Z_STRVAL_PP(args[ZEND_NUM_ARGS()-1]);
+
+ switch (ZEND_NUM_ARGS()) {
+ case 5:
+ tty = Z_STRVAL_PP(args[3]);
+ /* fall through */
+ case 4:
+ options = Z_STRVAL_PP(args[2]);
+ break;
+ }
+ }
+
+ if (persistent && PGG(allow_persistent)) {
+ zend_rsrc_list_entry *le;
+
+ /* try to find if we already have this link in our persistent list */
+ if (zend_hash_find(&EG(persistent_list), str.c, str.len+1, (void **) &le)==FAILURE) { /* we don't */
+ zend_rsrc_list_entry new_le;
+
+ if (PGG(max_links)!=-1 && PGG(num_links)>=PGG(max_links)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Cannot create new link. Too many open links (%ld)", PGG(num_links));
+ goto err;
+ }
+ if (PGG(max_persistent)!=-1 && PGG(num_persistent)>=PGG(max_persistent)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Cannot create new link. Too many open persistent links (%ld)", PGG(num_persistent));
+ goto err;
+ }
+
+ /* create the link */
+ if (connstring) {
+ pgsql=PQconnectdb(connstring);
+ } else {
+ pgsql=PQsetdb(host,port,options,tty,dbname);
+ }
+ if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
+ PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql)
+ if (pgsql) {
+ PQfinish(pgsql);
+ }
+ goto err;
+ }
+
+ /* hash it up */
+ Z_TYPE(new_le) = le_plink;
+ new_le.ptr = pgsql;
+ if (zend_hash_update(&EG(persistent_list), str.c, str.len+1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL)==FAILURE) {
+ goto err;
+ }
+ PGG(num_links)++;
+ PGG(num_persistent)++;
+ } else { /* we do */
+ if (Z_TYPE_P(le) != le_plink) {
+ RETURN_FALSE;
+ }
+ /* ensure that the link did not die */
+ if (PGG(auto_reset_persistent) & 1) {
+ /* need to send & get something from backend to
+ make sure we catch CONNECTION_BAD everytime */
+ PGresult *pg_result;
+ pg_result = PQexec(le->ptr, "select 1");
+ PQclear(pg_result);
+ }
+ if (PQstatus(le->ptr)==CONNECTION_BAD) { /* the link died */
+ if (le->ptr == NULL) {
+ if (connstring) {
+ le->ptr=PQconnectdb(connstring);
+ } else {
+ le->ptr=PQsetdb(host,port,options,tty,dbname);
+ }
+ }
+ else {
+ PQreset(le->ptr);
+ }
+ if (le->ptr==NULL || PQstatus(le->ptr)==CONNECTION_BAD) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"PostgreSQL link lost, unable to reconnect");
+ zend_hash_del(&EG(persistent_list),str.c,str.len+1);
+ goto err;
+ }
+ }
+ pgsql = (PGconn *) le->ptr;
+#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
+ if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 7.2) {
+#else
+ if (atof(PG_VERSION) >= 7.2) {
+#endif
+ pg_result = PQexec(pgsql, "RESET ALL;");
+ PQclear(pg_result);
+ }
+ }
+ ZEND_REGISTER_RESOURCE(return_value, pgsql, le_plink);
+ } else { /* Non persistent connection */
+ zend_rsrc_list_entry *index_ptr,new_index_ptr;
+
+ /* first we check the hash for the hashed_details key. if it exists,
+ * it should point us to the right offset where the actual pgsql link sits.
+ * if it doesn't, open a new pgsql link, add it to the resource list,
+ * and add a pointer to it with hashed_details as the key.
+ */
+ if (!(connect_type & PGSQL_CONNECT_FORCE_NEW)
+ && zend_hash_find(&EG(regular_list),str.c,str.len+1,(void **) &index_ptr)==SUCCESS) {
+ int type;
+ ulong link;
+ void *ptr;
+
+ if (Z_TYPE_P(index_ptr) != le_index_ptr) {
+ RETURN_FALSE;
+ }
+ link = (ulong) index_ptr->ptr;
+ ptr = zend_list_find(link,&type); /* check if the link is still there */
+ if (ptr && (type==le_link || type==le_plink)) {
+ Z_LVAL_P(return_value) = link;
+ zend_list_addref(link);
+ php_pgsql_set_default_link(link TSRMLS_CC);
+ Z_TYPE_P(return_value) = IS_RESOURCE;
+ goto cleanup;
+ } else {
+ zend_hash_del(&EG(regular_list),str.c,str.len+1);
+ }
+ }
+ if (PGG(max_links)!=-1 && PGG(num_links)>=PGG(max_links)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create new link. Too many open links (%ld)", PGG(num_links));
+ goto err;
+ }
+ if (connstring) {
+ pgsql = PQconnectdb(connstring);
+ } else {
+ pgsql = PQsetdb(host,port,options,tty,dbname);
+ }
+ if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
+ PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
+ if (pgsql) {
+ PQfinish(pgsql);
+ }
+ goto err;
+ }
+
+ /* add it to the list */
+ ZEND_REGISTER_RESOURCE(return_value, pgsql, le_link);
+
+ /* add it to the hash */
+ new_index_ptr.ptr = (void *) Z_LVAL_P(return_value);
+ Z_TYPE(new_index_ptr) = le_index_ptr;
+ if (zend_hash_update(&EG(regular_list),str.c,str.len+1,(void *) &new_index_ptr, sizeof(zend_rsrc_list_entry), NULL)==FAILURE) {
+ goto err;
+ }
+ PGG(num_links)++;
+ }
+ /* set notice processer */
+ if (! PGG(ignore_notices) && Z_TYPE_P(return_value) == IS_RESOURCE) {
+ PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, (void*)Z_RESVAL_P(return_value));
+ }
+ php_pgsql_set_default_link(Z_LVAL_P(return_value) TSRMLS_CC);
+
+cleanup:
+ smart_str_free(&str);
+ return;
+
+err:
+ smart_str_free(&str);
+ RETURN_FALSE;
+}
+/* }}} */
+
+#if 0
+/* {{{ php_pgsql_get_default_link
+ */
+static int php_pgsql_get_default_link(INTERNAL_FUNCTION_PARAMETERS)
+{
+ if (PGG(default_link)==-1) { /* no link opened yet, implicitly open one */
+ ht = 0;
+ php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,0);
+ }
+ return PGG(default_link);
+}
+/* }}} */
+#endif
+
+/* {{{ proto resource pg_connect(string connection_string[, int connect_type] | [string host, string port [, string options [, string tty,]]] string database)
+ Open a PostgreSQL connection */
+PHP_FUNCTION(pg_connect)
+{
+ php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,0);
+}
+/* }}} */
+
+/* {{{ proto resource pg_pconnect(string connection_string | [string host, string port [, string options [, string tty,]]] string database)
+ Open a persistent PostgreSQL connection */
+PHP_FUNCTION(pg_pconnect)
+{
+ php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,1);
+}
+/* }}} */
+
+/* {{{ proto bool pg_close([resource connection])
+ Close a PostgreSQL connection */
+PHP_FUNCTION(pg_close)
+{
+ zval *pgsql_link = NULL;
+ int id = -1, argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "|r", &pgsql_link) == FAILURE) {
+ return;
+ }
+
+ if (argc == 0) {
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (id==-1) { /* explicit resource number */
+ zend_list_delete(Z_RESVAL_P(pgsql_link));
+ }
+
+ if (id!=-1
+ || (pgsql_link && Z_RESVAL_P(pgsql_link)==PGG(default_link))) {
+ zend_list_delete(PGG(default_link));
+ PGG(default_link) = -1;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+
+#define PHP_PG_DBNAME 1
+#define PHP_PG_ERROR_MESSAGE 2
+#define PHP_PG_OPTIONS 3
+#define PHP_PG_PORT 4
+#define PHP_PG_TTY 5
+#define PHP_PG_HOST 6
+#define PHP_PG_VERSION 7
+
+/* {{{ php_pgsql_get_link_info
+ */
+static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
+{
+ zval *pgsql_link = NULL;
+ int id = -1, argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+ char *msgbuf;
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "|r", &pgsql_link) == FAILURE) {
+ return;
+ }
+
+ if (argc == 0) {
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ switch(entry_type) {
+ case PHP_PG_DBNAME:
+ Z_STRVAL_P(return_value) = PQdb(pgsql);
+ break;
+ case PHP_PG_ERROR_MESSAGE:
+ RETURN_STRING(PQErrorMessageTrim(pgsql, &msgbuf), 0);
+ return;
+ case PHP_PG_OPTIONS:
+ Z_STRVAL_P(return_value) = PQoptions(pgsql);
+ break;
+ case PHP_PG_PORT:
+ Z_STRVAL_P(return_value) = PQport(pgsql);
+ break;
+ case PHP_PG_TTY:
+ Z_STRVAL_P(return_value) = PQtty(pgsql);
+ break;
+ case PHP_PG_HOST:
+ Z_STRVAL_P(return_value) = PQhost(pgsql);
+ break;
+ case PHP_PG_VERSION:
+ array_init(return_value);
+ add_assoc_string(return_value, "client", PG_VERSION, 1);
+#if HAVE_PQPROTOCOLVERSION
+ add_assoc_long(return_value, "protocol", PQprotocolVersion(pgsql));
+#if HAVE_PQPARAMETERSTATUS
+ if (PQprotocolVersion(pgsql) >= 3) {
+ add_assoc_string(return_value, "server", (char*)PQparameterStatus(pgsql, "server_version"), 1);
+ }
+#endif
+#endif
+ return;
+ default:
+ RETURN_FALSE;
+ }
+ if (Z_STRVAL_P(return_value)) {
+ Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
+ Z_STRVAL_P(return_value) = (char *) estrdup(Z_STRVAL_P(return_value));
+ } else {
+ Z_STRLEN_P(return_value) = 0;
+ Z_STRVAL_P(return_value) = (char *) estrdup("");
+ }
+ Z_TYPE_P(return_value) = IS_STRING;
+}
+/* }}} */
+
+/* {{{ proto string pg_dbname([resource connection])
+ Get the database name */
+PHP_FUNCTION(pg_dbname)
+{
+ php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_DBNAME);
+}
+/* }}} */
+
+/* {{{ proto string pg_last_error([resource connection])
+ Get the error message string */
+PHP_FUNCTION(pg_last_error)
+{
+ php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_ERROR_MESSAGE);
+}
+/* }}} */
+
+/* {{{ proto string pg_options([resource connection])
+ Get the options associated with the connection */
+PHP_FUNCTION(pg_options)
+{
+ php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_OPTIONS);
+}
+/* }}} */
+
+/* {{{ proto int pg_port([resource connection])
+ Return the port number associated with the connection */
+PHP_FUNCTION(pg_port)
+{
+ php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_PORT);
+}
+/* }}} */
+
+/* {{{ proto string pg_tty([resource connection])
+ Return the tty name associated with the connection */
+PHP_FUNCTION(pg_tty)
+{
+ php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_TTY);
+}
+/* }}} */
+
+/* {{{ proto string pg_host([resource connection])
+ Returns the host name associated with the connection */
+PHP_FUNCTION(pg_host)
+{
+ php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_HOST);
+}
+/* }}} */
+
+/* {{{ proto array pg_version([resource connection])
+ Returns an array with client, protocol and server version (when available) */
+PHP_FUNCTION(pg_version)
+{
+ php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_VERSION);
+}
+/* }}} */
+
+#if HAVE_PQPARAMETERSTATUS
+/* {{{ proto string|false pg_parameter_status([resource connection,] string param_name)
+ Returns the value of a server parameter */
+PHP_FUNCTION(pg_parameter_status)
+{
+ zval *pgsql_link;
+ int id;
+ PGconn *pgsql;
+ char *param;
+ int len;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &param, &len) == SUCCESS) {
+ id = -1;
+ } else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &param, &len) == SUCCESS) {
+ pgsql_link = NULL;
+ id = PGG(default_link);
+ } else {
+ RETURN_FALSE;
+ }
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ param = (char*)PQparameterStatus(pgsql, param);
+ if (param) {
+ RETURN_STRING(param, 1);
+ } else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+#endif
+
+/* {{{ proto bool pg_ping([resource connection])
+ Ping database. If connection is bad, try to reconnect. */
+PHP_FUNCTION(pg_ping)
+{
+ zval *pgsql_link;
+ int id;
+ PGconn *pgsql;
+ PGresult *res;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_link) == SUCCESS) {
+ id = -1;
+ } else {
+ pgsql_link = NULL;
+ id = PGG(default_link);
+ }
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ /* ping connection */
+ res = PQexec(pgsql, "SELECT 1;");
+ PQclear(res);
+
+ /* check status. */
+ if (PQstatus(pgsql) == CONNECTION_OK)
+ RETURN_TRUE;
+
+ /* reset connection if it's broken */
+ PQreset(pgsql);
+ if (PQstatus(pgsql) == CONNECTION_OK) {
+ RETURN_TRUE;
+ }
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto resource pg_query([resource connection,] string query)
+ Execute a query */
+PHP_FUNCTION(pg_query)
+{
+ zval *pgsql_link = NULL;
+ char *query;
+ int id = -1, query_len, argc = ZEND_NUM_ARGS();
+ int leftover = 0;
+ PGconn *pgsql;
+ PGresult *pgsql_result;
+ ExecStatusType status;
+ pgsql_result_handle *pg_result;
+
+ if (argc == 1) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query, &query_len) == FAILURE) {
+ return;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ } else {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &query, &query_len) == FAILURE) {
+ return;
+ }
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE,"Cannot set connection to blocking mode");
+ RETURN_FALSE;
+ }
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ PQclear(pgsql_result);
+ leftover = 1;
+ }
+ if (leftover) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
+ }
+ pgsql_result = PQexec(pgsql, query);
+ if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
+ PQclear(pgsql_result);
+ PQreset(pgsql);
+ pgsql_result = PQexec(pgsql, query);
+ }
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(pgsql);
+ }
+
+ switch (status) {
+ case PGRES_EMPTY_QUERY:
+ case PGRES_BAD_RESPONSE:
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ PHP_PQ_ERROR("Query failed: %s", pgsql);
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ break;
+ case PGRES_COMMAND_OK: /* successful command that did not return rows */
+ default:
+ if (pgsql_result) {
+ pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
+ pg_result->conn = pgsql;
+ pg_result->result = pgsql_result;
+ pg_result->row = 0;
+ ZEND_REGISTER_RESOURCE(return_value, pg_result, le_result);
+ } else {
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ }
+ break;
+ }
+}
+/* }}} */
+
+#if HAVE_PQEXECPARAMS || HAVE_PQEXECPREPARED || HAVE_PQSENDQUERYPARAMS || HAVE_PQSENDQUERYPREPARED
+/* {{{ _php_pgsql_free_params */
+static void _php_pgsql_free_params(char **params, int num_params)
+{
+ if (num_params > 0) {
+ int i;
+ for (i = 0; i < num_params; i++) {
+ if (params[i]) {
+ efree(params[i]);
+ }
+ }
+ efree(params);
+ }
+}
+/* }}} */
+#endif
+
+#if HAVE_PQEXECPARAMS
+/* {{{ proto resource pg_query_params([resource connection,] string query, array params)
+ Execute a query */
+PHP_FUNCTION(pg_query_params)
+{
+ zval *pgsql_link = NULL;
+ zval *pv_param_arr, **tmp;
+ char *query;
+ int query_len, id = -1, argc = ZEND_NUM_ARGS();
+ int leftover = 0;
+ int num_params = 0;
+ char **params = NULL;
+ PGconn *pgsql;
+ PGresult *pgsql_result;
+ ExecStatusType status;
+ pgsql_result_handle *pg_result;
+
+ if (argc == 2) {
+ if (zend_parse_parameters(argc TSRMLS_CC, "sa", &query, &query_len, &pv_param_arr) == FAILURE) {
+ return;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ } else {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rsa", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
+ return;
+ }
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE,"Cannot set connection to blocking mode");
+ RETURN_FALSE;
+ }
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ PQclear(pgsql_result);
+ leftover = 1;
+ }
+ if (leftover) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
+ }
+
+ zend_hash_internal_pointer_reset(Z_ARRVAL_P(pv_param_arr));
+ num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
+ if (num_params > 0) {
+ int i = 0;
+ params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
+
+ for(i = 0; i < num_params; i++) {
+ if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter");
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+
+ if (Z_TYPE_PP(tmp) == IS_NULL) {
+ params[i] = NULL;
+ } else {
+ zval tmp_val = **tmp;
+ zval_copy_ctor(&tmp_val);
+ convert_to_string(&tmp_val);
+ if (Z_TYPE(tmp_val) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error converting parameter");
+ zval_dtor(&tmp_val);
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+ params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
+ zval_dtor(&tmp_val);
+ }
+
+ zend_hash_move_forward(Z_ARRVAL_P(pv_param_arr));
+ }
+ }
+
+ pgsql_result = PQexecParams(pgsql, query, num_params,
+ NULL, (const char * const *)params, NULL, NULL, 0);
+ if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
+ PQclear(pgsql_result);
+ PQreset(pgsql);
+ pgsql_result = PQexecParams(pgsql, query, num_params,
+ NULL, (const char * const *)params, NULL, NULL, 0);
+ }
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(pgsql);
+ }
+
+ _php_pgsql_free_params(params, num_params);
+
+ switch (status) {
+ case PGRES_EMPTY_QUERY:
+ case PGRES_BAD_RESPONSE:
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ PHP_PQ_ERROR("Query failed: %s", pgsql);
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ break;
+ case PGRES_COMMAND_OK: /* successful command that did not return rows */
+ default:
+ if (pgsql_result) {
+ pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
+ pg_result->conn = pgsql;
+ pg_result->result = pgsql_result;
+ pg_result->row = 0;
+ ZEND_REGISTER_RESOURCE(return_value, pg_result, le_result);
+ } else {
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ }
+ break;
+ }
+}
+/* }}} */
+#endif
+
+#if HAVE_PQPREPARE
+/* {{{ proto resource pg_prepare([resource connection,] string stmtname, string query)
+ Prepare a query for future execution */
+PHP_FUNCTION(pg_prepare)
+{
+ zval *pgsql_link = NULL;
+ char *query, *stmtname;
+ int query_len, stmtname_len, id = -1, argc = ZEND_NUM_ARGS();
+ int leftover = 0;
+ PGconn *pgsql;
+ PGresult *pgsql_result;
+ ExecStatusType status;
+ pgsql_result_handle *pg_result;
+
+ if (argc == 2) {
+ if (zend_parse_parameters(argc TSRMLS_CC, "ss", &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
+ return;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ } else {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
+ return;
+ }
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE,"Cannot set connection to blocking mode");
+ RETURN_FALSE;
+ }
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ PQclear(pgsql_result);
+ leftover = 1;
+ }
+ if (leftover) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
+ }
+ pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
+ if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
+ PQclear(pgsql_result);
+ PQreset(pgsql);
+ pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
+ }
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(pgsql);
+ }
+
+ switch (status) {
+ case PGRES_EMPTY_QUERY:
+ case PGRES_BAD_RESPONSE:
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ PHP_PQ_ERROR("Query failed: %s", pgsql);
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ break;
+ case PGRES_COMMAND_OK: /* successful command that did not return rows */
+ default:
+ if (pgsql_result) {
+ pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
+ pg_result->conn = pgsql;
+ pg_result->result = pgsql_result;
+ pg_result->row = 0;
+ ZEND_REGISTER_RESOURCE(return_value, pg_result, le_result);
+ } else {
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ }
+ break;
+ }
+}
+/* }}} */
+#endif
+
+#if HAVE_PQEXECPREPARED
+/* {{{ proto resource pg_execute([resource connection,] string stmtname, array params)
+ Execute a prepared query */
+PHP_FUNCTION(pg_execute)
+{
+ zval *pgsql_link = NULL;
+ zval *pv_param_arr, **tmp;
+ char *stmtname;
+ int stmtname_len, id = -1, argc = ZEND_NUM_ARGS();
+ int leftover = 0;
+ int num_params = 0;
+ char **params = NULL;
+ PGconn *pgsql;
+ PGresult *pgsql_result;
+ ExecStatusType status;
+ pgsql_result_handle *pg_result;
+
+ if (argc == 2) {
+ if (zend_parse_parameters(argc TSRMLS_CC, "sa/", &stmtname, &stmtname_len, &pv_param_arr)==FAILURE) {
+ return;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ } else {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rsa/", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
+ return;
+ }
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE,"Cannot set connection to blocking mode");
+ RETURN_FALSE;
+ }
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ PQclear(pgsql_result);
+ leftover = 1;
+ }
+ if (leftover) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
+ }
+
+ zend_hash_internal_pointer_reset(Z_ARRVAL_P(pv_param_arr));
+ num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
+ if (num_params > 0) {
+ int i = 0;
+ params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
+
+ for(i = 0; i < num_params; i++) {
+ if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter");
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+
+ if (Z_TYPE_PP(tmp) == IS_NULL) {
+ params[i] = NULL;
+ } else {
+ zval tmp_val = **tmp;
+ zval_copy_ctor(&tmp_val);
+ convert_to_string(&tmp_val);
+ if (Z_TYPE(tmp_val) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error converting parameter");
+ zval_dtor(&tmp_val);
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+ params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
+ zval_dtor(&tmp_val);
+ }
+
+ zend_hash_move_forward(Z_ARRVAL_P(pv_param_arr));
+ }
+ }
+
+ pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
+ (const char * const *)params, NULL, NULL, 0);
+ if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
+ PQclear(pgsql_result);
+ PQreset(pgsql);
+ pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
+ (const char * const *)params, NULL, NULL, 0);
+ }
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(pgsql);
+ }
+
+ _php_pgsql_free_params(params, num_params);
+
+ switch (status) {
+ case PGRES_EMPTY_QUERY:
+ case PGRES_BAD_RESPONSE:
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ PHP_PQ_ERROR("Query failed: %s", pgsql);
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ break;
+ case PGRES_COMMAND_OK: /* successful command that did not return rows */
+ default:
+ if (pgsql_result) {
+ pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
+ pg_result->conn = pgsql;
+ pg_result->result = pgsql_result;
+ pg_result->row = 0;
+ ZEND_REGISTER_RESOURCE(return_value, pg_result, le_result);
+ } else {
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ }
+ break;
+ }
+}
+/* }}} */
+#endif
+
+#define PHP_PG_NUM_ROWS 1
+#define PHP_PG_NUM_FIELDS 2
+#define PHP_PG_CMD_TUPLES 3
+
+/* {{{ php_pgsql_get_result_info
+ */
+static void php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
+{
+ zval *result;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+
+ switch (entry_type) {
+ case PHP_PG_NUM_ROWS:
+ Z_LVAL_P(return_value) = PQntuples(pgsql_result);
+ break;
+ case PHP_PG_NUM_FIELDS:
+ Z_LVAL_P(return_value) = PQnfields(pgsql_result);
+ break;
+ case PHP_PG_CMD_TUPLES:
+#if HAVE_PQCMDTUPLES
+ Z_LVAL_P(return_value) = atoi(PQcmdTuples(pgsql_result));
+#else
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not supported under this build");
+ Z_LVAL_P(return_value) = 0;
+#endif
+ break;
+ default:
+ RETURN_FALSE;
+ }
+ Z_TYPE_P(return_value) = IS_LONG;
+}
+/* }}} */
+
+/* {{{ proto int pg_num_rows(resource result)
+ Return the number of rows in the result */
+PHP_FUNCTION(pg_num_rows)
+{
+ php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_ROWS);
+}
+/* }}} */
+
+/* {{{ proto int pg_num_fields(resource result)
+ Return the number of fields in the result */
+PHP_FUNCTION(pg_num_fields)
+{
+ php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_FIELDS);
+}
+/* }}} */
+
+#if HAVE_PQCMDTUPLES
+/* {{{ proto int pg_affected_rows(resource result)
+ Returns the number of affected tuples */
+PHP_FUNCTION(pg_affected_rows)
+{
+ php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_CMD_TUPLES);
+}
+/* }}} */
+#endif
+
+/* {{{ proto string pg_last_notice(resource connection)
+ Returns the last notice set by the backend */
+PHP_FUNCTION(pg_last_notice)
+{
+ zval *pgsql_link;
+ PGconn *pg_link;
+ int id = -1;
+ php_pgsql_notice **notice;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_link) == FAILURE) {
+ return;
+ }
+ /* Just to check if user passed valid resoruce */
+ ZEND_FETCH_RESOURCE2(pg_link, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (zend_hash_index_find(&PGG(notices), Z_RESVAL_P(pgsql_link), (void **)&notice) == FAILURE) {
+ RETURN_FALSE;
+ }
+ RETURN_STRINGL((*notice)->message, (*notice)->len, 1);
+}
+/* }}} */
+
+/* {{{ get_field_name
+ */
+static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list TSRMLS_DC)
+{
+ PGresult *result;
+ smart_str str = {0};
+ zend_rsrc_list_entry *field_type;
+ char *ret=NULL;
+
+ /* try to lookup the type in the resource list */
+ smart_str_appends(&str, "pgsql_oid_");
+ smart_str_append_unsigned(&str, oid);
+ smart_str_0(&str);
+
+ if (zend_hash_find(list,str.c,str.len+1,(void **) &field_type)==SUCCESS) {
+ ret = estrdup((char *)field_type->ptr);
+ } else { /* hash all oid's */
+ int i,num_rows;
+ int oid_offset,name_offset;
+ char *tmp_oid, *end_ptr, *tmp_name;
+ zend_rsrc_list_entry new_oid_entry;
+
+ if ((result = PQexec(pgsql,"select oid,typname from pg_type")) == NULL || PQresultStatus(result) != PGRES_TUPLES_OK) {
+ if (result) {
+ PQclear(result);
+ }
+ smart_str_free(&str);
+ return STR_EMPTY_ALLOC();
+ }
+ num_rows = PQntuples(result);
+ oid_offset = PQfnumber(result,"oid");
+ name_offset = PQfnumber(result,"typname");
+
+ for (i=0; i<num_rows; i++) {
+ if ((tmp_oid = PQgetvalue(result,i,oid_offset))==NULL) {
+ continue;
+ }
+
+ str.len = 0;
+ smart_str_appends(&str, "pgsql_oid_");
+ smart_str_appends(&str, tmp_oid);
+ smart_str_0(&str);
+
+ if ((tmp_name = PQgetvalue(result,i,name_offset))==NULL) {
+ continue;
+ }
+ Z_TYPE(new_oid_entry) = le_string;
+ new_oid_entry.ptr = estrdup(tmp_name);
+ zend_hash_update(list,str.c,str.len+1,(void *) &new_oid_entry, sizeof(zend_rsrc_list_entry), NULL);
+ if (!ret && strtoul(tmp_oid, &end_ptr, 10)==oid) {
+ ret = estrdup(tmp_name);
+ }
+ }
+ PQclear(result);
+ }
+
+ smart_str_free(&str);
+ return ret;
+}
+/* }}} */
+
+#ifdef HAVE_PQFTABLE
+/* {{{ proto mixed pg_field_table(resource result, int field_number[, bool oid_only])
+ Returns the name of the table field belongs to, or table's oid if oid_only is true */
+PHP_FUNCTION(pg_field_table)
+{
+ zval *result;
+ pgsql_result_handle *pg_result;
+ long fnum = -1;
+ zend_bool return_oid = 0;
+ Oid oid;
+ smart_str hash_key = {0};
+ char *table_name;
+ zend_rsrc_list_entry *field_table;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|b", &result, &fnum, &return_oid) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ if (fnum < 0 || fnum >= PQnfields(pg_result->result)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad field offset specified");
+ RETURN_FALSE;
+ }
+
+ oid = PQftable(pg_result->result, fnum);
+
+ if (InvalidOid == oid) {
+ RETURN_FALSE;
+ }
+
+
+ if (return_oid) {
+#if UINT_MAX > LONG_MAX /* Oid is unsigned int, we don't need this code, where LONG is wider */
+ if (oid > LONG_MAX) {
+ smart_str oidstr = {0};
+ smart_str_append_unsigned(&oidstr, oid);
+ smart_str_0(&oidstr);
+ RETURN_STRINGL(oidstr.c, oidstr.len, 0);
+ } else
+#endif
+ RETURN_LONG((long)oid);
+ }
+
+ /* try to lookup the table name in the resource list */
+ smart_str_appends(&hash_key, "pgsql_table_oid_");
+ smart_str_append_unsigned(&hash_key, oid);
+ smart_str_0(&hash_key);
+
+ if (zend_hash_find(&EG(regular_list), hash_key.c, hash_key.len+1, (void **) &field_table) == SUCCESS) {
+ smart_str_free(&hash_key);
+ RETURN_STRING((char *)field_table->ptr, 1);
+ } else { /* Not found, lookup by querying PostgreSQL system tables */
+ PGresult *tmp_res;
+ smart_str querystr = {0};
+ zend_rsrc_list_entry new_field_table;
+
+ smart_str_appends(&querystr, "select relname from pg_class where oid=");
+ smart_str_append_unsigned(&querystr, oid);
+ smart_str_0(&querystr);
+
+
+ if ((tmp_res = PQexec(pg_result->conn, querystr.c)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {
+ if (tmp_res) {
+ PQclear(tmp_res);
+ }
+ smart_str_free(&querystr);
+ smart_str_free(&hash_key);
+ RETURN_FALSE;
+ }
+
+ smart_str_free(&querystr);
+
+ if ((table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {
+ PQclear(tmp_res);
+ smart_str_free(&hash_key);
+ RETURN_FALSE;
+ }
+
+ Z_TYPE(new_field_table) = le_string;
+ new_field_table.ptr = estrdup(table_name);
+ zend_hash_update(&EG(regular_list), hash_key.c, hash_key.len+1, (void *) &new_field_table, sizeof(zend_rsrc_list_entry), NULL);
+
+ smart_str_free(&hash_key);
+ PQclear(tmp_res);
+ RETURN_STRING(table_name, 1);
+ }
+
+}
+/* }}} */
+#endif
+
+#define PHP_PG_FIELD_NAME 1
+#define PHP_PG_FIELD_SIZE 2
+#define PHP_PG_FIELD_TYPE 3
+#define PHP_PG_FIELD_TYPE_OID 4
+
+/* {{{ php_pgsql_get_field_info
+ */
+static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
+{
+ zval *result;
+ long field;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+ Oid oid;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &result, &field) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+
+ if (field < 0 || field >= PQnfields(pgsql_result)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad field offset specified");
+ RETURN_FALSE;
+ }
+
+ switch (entry_type) {
+ case PHP_PG_FIELD_NAME:
+ Z_STRVAL_P(return_value) = PQfname(pgsql_result, field);
+ Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
+ Z_STRVAL_P(return_value) = estrndup(Z_STRVAL_P(return_value),Z_STRLEN_P(return_value));
+ Z_TYPE_P(return_value) = IS_STRING;
+ break;
+ case PHP_PG_FIELD_SIZE:
+ Z_LVAL_P(return_value) = PQfsize(pgsql_result, field);
+ Z_TYPE_P(return_value) = IS_LONG;
+ break;
+ case PHP_PG_FIELD_TYPE:
+ Z_STRVAL_P(return_value) = get_field_name(pg_result->conn, PQftype(pgsql_result, field), &EG(regular_list) TSRMLS_CC);
+ Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
+ Z_TYPE_P(return_value) = IS_STRING;
+ break;
+ case PHP_PG_FIELD_TYPE_OID:
+
+ oid = PQftype(pgsql_result, field);
+#if UINT_MAX > LONG_MAX
+ if (oid > LONG_MAX) {
+ smart_str s = {0};
+ smart_str_append_unsigned(&s, oid);
+ smart_str_0(&s);
+ Z_STRVAL_P(return_value) = s.c;
+ Z_STRLEN_P(return_value) = s.len;
+ Z_TYPE_P(return_value) = IS_STRING;
+ } else
+#endif
+ {
+ Z_LVAL_P(return_value) = (long)oid;
+ Z_TYPE_P(return_value) = IS_LONG;
+ }
+ break;
+ default:
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto string pg_field_name(resource result, int field_number)
+ Returns the name of the field */
+PHP_FUNCTION(pg_field_name)
+{
+ php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_NAME);
+}
+/* }}} */
+
+/* {{{ proto int pg_field_size(resource result, int field_number)
+ Returns the internal size of the field */
+PHP_FUNCTION(pg_field_size)
+{
+ php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_SIZE);
+}
+/* }}} */
+
+/* {{{ proto string pg_field_type(resource result, int field_number)
+ Returns the type name for the given field */
+PHP_FUNCTION(pg_field_type)
+{
+ php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE);
+}
+/* }}} */
+
+
+/* {{{ proto string pg_field_type_oid(resource result, int field_number)
+ Returns the type oid for the given field */
+PHP_FUNCTION(pg_field_type_oid)
+{
+ php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE_OID);
+}
+/* }}} */
+
+/* {{{ proto int pg_field_num(resource result, string field_name)
+ Returns the field number of the named field */
+PHP_FUNCTION(pg_field_num)
+{
+ zval *result;
+ char *field;
+ int field_len;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &result, &field, &field_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+
+ Z_LVAL_P(return_value) = PQfnumber(pgsql_result, field);
+ Z_TYPE_P(return_value) = IS_LONG;
+}
+/* }}} */
+
+/* {{{ proto mixed pg_fetch_result(resource result, [int row_number,] mixed field_name)
+ Returns values from a result identifier */
+PHP_FUNCTION(pg_fetch_result)
+{
+ zval *result, **field=NULL;
+ long row;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+ int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
+
+ if (argc == 2) {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rZ", &result, &field) == FAILURE) {
+ return;
+ }
+ } else {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rlZ", &result, &row, &field) == FAILURE) {
+ return;
+ }
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+ if (argc == 2) {
+ if (pg_result->row < 0) {
+ pg_result->row = 0;
+ }
+ pgsql_row = pg_result->row;
+ if (pgsql_row >= PQntuples(pgsql_result)) {
+ RETURN_FALSE;
+ }
+ } else {
+ pgsql_row = row;
+ if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to jump to row %ld on PostgreSQL result index %ld",
+ row, Z_LVAL_P(result));
+ RETURN_FALSE;
+ }
+ }
+ switch(Z_TYPE_PP(field)) {
+ case IS_STRING:
+ field_offset = PQfnumber(pgsql_result, Z_STRVAL_PP(field));
+ break;
+ default:
+ convert_to_long_ex(field);
+ field_offset = Z_LVAL_PP(field);
+ break;
+ }
+ if (field_offset<0 || field_offset>=PQnfields(pgsql_result)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad column offset specified");
+ RETURN_FALSE;
+ }
+
+ if (PQgetisnull(pgsql_result, pgsql_row, field_offset)) {
+ Z_TYPE_P(return_value) = IS_NULL;
+ } else {
+ char *value = PQgetvalue(pgsql_result, pgsql_row, field_offset);
+ int value_len = PQgetlength(pgsql_result, pgsql_row, field_offset);
+ ZVAL_STRINGL(return_value, value, value_len, 1);
+ }
+}
+/* }}} */
+
+/* {{{ void php_pgsql_fetch_hash */
+static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, long result_type, int into_object)
+{
+ zval *result, *zrow = NULL;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+ int i, num_fields, pgsql_row, use_row;
+ long row = -1;
+ char *field_name;
+ zval *ctor_params = NULL;
+ zend_class_entry *ce = NULL;
+
+ if (into_object) {
+ char *class_name = NULL;
+ int class_name_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z!sz", &result, &zrow, &class_name, &class_name_len, &ctor_params) == FAILURE) {
+ return;
+ }
+ if (!class_name) {
+ ce = zend_standard_class_def;
+ } else {
+ ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
+ }
+ if (!ce) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find class '%s'", class_name);
+ return;
+ }
+ result_type = PGSQL_ASSOC;
+ } else {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z!l", &result, &zrow, &result_type) == FAILURE) {
+ return;
+ }
+ }
+ if (zrow == NULL) {
+ row = -1;
+ } else {
+ convert_to_long(zrow);
+ row = Z_LVAL_P(zrow);
+ if (row < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The row parameter must be greater or equal to zero");
+ RETURN_FALSE;
+ }
+ }
+ use_row = ZEND_NUM_ARGS() > 1 && row != -1;
+
+ if (!(result_type & PGSQL_BOTH)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid result type");
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+
+ if (use_row) {
+ pgsql_row = row;
+ pg_result->row = pgsql_row;
+ if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to jump to row %ld on PostgreSQL result index %ld",
+ row, Z_LVAL_P(result));
+ RETURN_FALSE;
+ }
+ } else {
+ /* If 2nd param is NULL, use internal row counter to access next row */
+ pgsql_row = pg_result->row;
+ if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
+ RETURN_FALSE;
+ }
+ pg_result->row++;
+ }
+
+ array_init(return_value);
+ for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) {
+ if (PQgetisnull(pgsql_result, pgsql_row, i)) {
+ if (result_type & PGSQL_NUM) {
+ add_index_null(return_value, i);
+ }
+ if (result_type & PGSQL_ASSOC) {
+ field_name = PQfname(pgsql_result, i);
+ add_assoc_null(return_value, field_name);
+ }
+ } else {
+ char *element = PQgetvalue(pgsql_result, pgsql_row, i);
+ if (element) {
+ char *data;
+ int data_len;
+ int should_copy=0;
+ const uint element_len = strlen(element);
+
+ data = safe_estrndup(element, element_len);
+ data_len = element_len;
+
+ if (result_type & PGSQL_NUM) {
+ add_index_stringl(return_value, i, data, data_len, should_copy);
+ should_copy=1;
+ }
+
+ if (result_type & PGSQL_ASSOC) {
+ field_name = PQfname(pgsql_result, i);
+ add_assoc_stringl(return_value, field_name, data, data_len, should_copy);
+ }
+ }
+ }
+ }
+
+ if (into_object) {
+ zval dataset = *return_value;
+ zend_fcall_info fci;
+ zend_fcall_info_cache fcc;
+ zval *retval_ptr;
+
+ object_and_properties_init(return_value, ce, NULL);
+ zend_merge_properties(return_value, Z_ARRVAL(dataset), 1 TSRMLS_CC);
+
+ if (ce->constructor) {
+ fci.size = sizeof(fci);
+ fci.function_table = &ce->function_table;
+ fci.function_name = NULL;
+ fci.symbol_table = NULL;
+ fci.object_ptr = return_value;
+ fci.retval_ptr_ptr = &retval_ptr;
+ if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
+ if (Z_TYPE_P(ctor_params) == IS_ARRAY) {
+ HashTable *ht = Z_ARRVAL_P(ctor_params);
+ Bucket *p;
+
+ fci.param_count = 0;
+ fci.params = safe_emalloc(sizeof(zval*), ht->nNumOfElements, 0);
+ p = ht->pListHead;
+ while (p != NULL) {
+ fci.params[fci.param_count++] = (zval**)p->pData;
+ p = p->pListNext;
+ }
+ } else {
+ /* Two problems why we throw exceptions here: PHP is typeless
+ * and hence passing one argument that's not an array could be
+ * by mistake and the other way round is possible, too. The
+ * single value is an array. Also we'd have to make that one
+ * argument passed by reference.
+ */
+ zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Parameter ctor_params must be an array", 0 TSRMLS_CC);
+ return;
+ }
+ } else {
+ fci.param_count = 0;
+ fci.params = NULL;
+ }
+ fci.no_separation = 1;
+
+ fcc.initialized = 1;
+ fcc.function_handler = ce->constructor;
+ fcc.calling_scope = EG(scope);
+ fcc.called_scope = Z_OBJCE_P(return_value);
+ fcc.object_ptr = return_value;
+
+ if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) {
+ zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Could not execute %s::%s()", ce->name, ce->constructor->common.function_name);
+ } else {
+ if (retval_ptr) {
+ zval_ptr_dtor(&retval_ptr);
+ }
+ }
+ if (fci.params) {
+ efree(fci.params);
+ }
+ } else if (ctor_params) {
+ zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Class %s does not have a constructor hence you cannot use ctor_params", ce->name);
+ }
+ }
+}
+/* }}} */
+
+/* {{{ proto array pg_fetch_row(resource result [, int row [, int result_type]])
+ Get a row as an enumerated array */
+PHP_FUNCTION(pg_fetch_row)
+{
+ php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0);
+}
+/* }}} */
+
+/* {{{ proto array pg_fetch_assoc(resource result [, int row])
+ Fetch a row as an assoc array */
+PHP_FUNCTION(pg_fetch_assoc)
+{
+ /* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when
+ there is 3rd parameter */
+ if (ZEND_NUM_ARGS() > 2)
+ WRONG_PARAM_COUNT;
+ php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0);
+}
+/* }}} */
+
+/* {{{ proto array pg_fetch_array(resource result [, int row [, int result_type]])
+ Fetch a row as an array */
+PHP_FUNCTION(pg_fetch_array)
+{
+ php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0);
+}
+/* }}} */
+
+/* {{{ proto object pg_fetch_object(resource result [, int row [, string class_name [, NULL|array ctor_params]]])
+ Fetch a row as an object */
+PHP_FUNCTION(pg_fetch_object)
+{
+ /* pg_fetch_object() allowed result_type used to be. 3rd parameter
+ must be allowed for compatibility */
+ php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1);
+}
+/* }}} */
+
+/* {{{ proto array pg_fetch_all(resource result)
+ Fetch all rows into array */
+PHP_FUNCTION(pg_fetch_all)
+{
+ zval *result;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+ array_init(return_value);
+ if (php_pgsql_result2array(pgsql_result, return_value TSRMLS_CC) == FAILURE) {
+ zval_dtor(return_value);
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto array pg_fetch_all_columns(resource result [, int column_number])
+ Fetch all rows into array */
+PHP_FUNCTION(pg_fetch_all_columns)
+{
+ zval *result;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+ unsigned long colno=0;
+ int pg_numrows, pg_row;
+ size_t num_fields;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &result, &colno) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+
+ num_fields = PQnfields(pgsql_result);
+ if (colno >= num_fields || colno < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid column number '%ld'", colno);
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+
+ if ((pg_numrows = PQntuples(pgsql_result)) <= 0) {
+ return;
+ }
+
+ for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
+ if (PQgetisnull(pgsql_result, pg_row, colno)) {
+ add_next_index_null(return_value);
+ } else {
+ add_next_index_string(return_value, PQgetvalue(pgsql_result, pg_row, colno), 1);
+ }
+ }
+}
+/* }}} */
+
+/* {{{ proto bool pg_result_seek(resource result, int offset)
+ Set internal row offset */
+PHP_FUNCTION(pg_result_seek)
+{
+ zval *result;
+ long row;
+ pgsql_result_handle *pg_result;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &result, &row) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ if (row < 0 || row >= PQntuples(pg_result->result)) {
+ RETURN_FALSE;
+ }
+
+ /* seek to offset */
+ pg_result->row = row;
+ RETURN_TRUE;
+}
+/* }}} */
+
+
+#define PHP_PG_DATA_LENGTH 1
+#define PHP_PG_DATA_ISNULL 2
+
+/* {{{ php_pgsql_data_info
+ */
+static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
+{
+ zval *result, **field;
+ long row;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+ int field_offset, pgsql_row, argc = ZEND_NUM_ARGS();
+
+ if (argc == 2) {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rZ", &result, &field) == FAILURE) {
+ return;
+ }
+ } else {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rlZ", &result, &row, &field) == FAILURE) {
+ return;
+ }
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+ if (argc == 2) {
+ if (pg_result->row < 0) {
+ pg_result->row = 0;
+ }
+ pgsql_row = pg_result->row;
+ if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
+ RETURN_FALSE;
+ }
+ } else {
+ pgsql_row = row;
+ if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to jump to row %ld on PostgreSQL result index %ld",
+ row, Z_LVAL_P(result));
+ RETURN_FALSE;
+ }
+ }
+
+ switch(Z_TYPE_PP(field)) {
+ case IS_STRING:
+ convert_to_string_ex(field);
+ field_offset = PQfnumber(pgsql_result, Z_STRVAL_PP(field));
+ break;
+ default:
+ convert_to_long_ex(field);
+ field_offset = Z_LVAL_PP(field);
+ break;
+ }
+ if (field_offset < 0 || field_offset >= PQnfields(pgsql_result)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad column offset specified");
+ RETURN_FALSE;
+ }
+
+ switch (entry_type) {
+ case PHP_PG_DATA_LENGTH:
+ Z_LVAL_P(return_value) = PQgetlength(pgsql_result, pgsql_row, field_offset);
+ break;
+ case PHP_PG_DATA_ISNULL:
+ Z_LVAL_P(return_value) = PQgetisnull(pgsql_result, pgsql_row, field_offset);
+ break;
+ }
+ Z_TYPE_P(return_value) = IS_LONG;
+}
+/* }}} */
+
+/* {{{ proto int pg_field_prtlen(resource result, [int row,] mixed field_name_or_number)
+ Returns the printed length */
+PHP_FUNCTION(pg_field_prtlen)
+{
+ php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_LENGTH);
+}
+/* }}} */
+
+/* {{{ proto int pg_field_is_null(resource result, [int row,] mixed field_name_or_number)
+ Test if a field is NULL */
+PHP_FUNCTION(pg_field_is_null)
+{
+ php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_ISNULL);
+}
+/* }}} */
+
+/* {{{ proto bool pg_free_result(resource result)
+ Free result memory */
+PHP_FUNCTION(pg_free_result)
+{
+ zval *result;
+ pgsql_result_handle *pg_result;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+ if (Z_LVAL_P(result) == 0) {
+ RETURN_FALSE;
+ }
+ zend_list_delete(Z_RESVAL_P(result));
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string pg_last_oid(resource result)
+ Returns the last object identifier */
+PHP_FUNCTION(pg_last_oid)
+{
+ zval *result;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+#ifdef HAVE_PQOIDVALUE
+ Oid oid;
+#endif
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+ pgsql_result = pg_result->result;
+#ifdef HAVE_PQOIDVALUE
+ oid = PQoidValue(pgsql_result);
+ if (oid == InvalidOid) {
+ RETURN_FALSE;
+ }
+ PGSQL_RETURN_OID(oid);
+#else
+ Z_STRVAL_P(return_value) = (char *) PQoidStatus(pgsql_result);
+ if (Z_STRVAL_P(return_value)) {
+ RETURN_STRING(Z_STRVAL_P(return_value), 1);
+ }
+ RETURN_STRING("", 1);
+#endif
+}
+/* }}} */
+
+/* {{{ proto bool pg_trace(string filename [, string mode [, resource connection]])
+ Enable tracing a PostgreSQL connection */
+PHP_FUNCTION(pg_trace)
+{
+ char *z_filename, *mode = "w";
+ int z_filename_len, mode_len;
+ zval *pgsql_link = NULL;
+ int id = -1, argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+ FILE *fp = NULL;
+ php_stream *stream;
+ id = PGG(default_link);
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "s|sr", &z_filename, &z_filename_len, &mode, &mode_len, &pgsql_link) == FAILURE) {
+ return;
+ }
+
+ if (argc < 3) {
+ CHECK_DEFAULT_LINK(id);
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ stream = php_stream_open_wrapper(z_filename, mode, REPORT_ERRORS, NULL);
+
+ if (!stream) {
+ RETURN_FALSE;
+ }
+
+ if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS)) {
+ php_stream_close(stream);
+ RETURN_FALSE;
+ }
+ php_stream_auto_cleanup(stream);
+ PQtrace(pgsql, fp);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool pg_untrace([resource connection])
+ Disable tracing of a PostgreSQL connection */
+PHP_FUNCTION(pg_untrace)
+{
+ zval *pgsql_link = NULL;
+ int id = -1, argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "|r", &pgsql_link) == FAILURE) {
+ return;
+ }
+
+ if (argc == 0) {
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+ PQuntrace(pgsql);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto mixed pg_lo_create([resource connection],[mixed large_object_oid])
+ Create a large object */
+PHP_FUNCTION(pg_lo_create)
+{
+ zval *pgsql_link = NULL, *oid = NULL;
+ PGconn *pgsql;
+ Oid pgsql_oid, wanted_oid = InvalidOid;
+ int id = -1, argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "|zz", &pgsql_link, &oid) == FAILURE) {
+ return;
+ }
+
+ if ((argc == 1) && (Z_TYPE_P(pgsql_link) != IS_RESOURCE)) {
+ oid = pgsql_link;
+ pgsql_link = NULL;
+ }
+
+ if (pgsql_link == NULL) {
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ if (id == -1) {
+ RETURN_FALSE;
+ }
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (oid) {
+#ifndef HAVE_PG_LO_CREATE
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "OID value passing not supported");
+#else
+ switch (Z_TYPE_P(oid)) {
+ case IS_STRING:
+ {
+ char *end_ptr;
+ wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
+ if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "invalid OID value passed");
+ RETURN_FALSE;
+ }
+ }
+ break;
+ case IS_LONG:
+ if (Z_LVAL_P(oid) < (long)InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "invalid OID value passed");
+ RETURN_FALSE;
+ }
+ wanted_oid = (Oid)Z_LVAL_P(oid);
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "invalid OID value passed");
+ RETURN_FALSE;
+ }
+ if ((pgsql_oid = lo_create(pgsql, wanted_oid)) == InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create PostgreSQL large object");
+ RETURN_FALSE;
+ }
+
+ PGSQL_RETURN_OID(pgsql_oid);
+#endif
+ }
+
+ if ((pgsql_oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create PostgreSQL large object");
+ RETURN_FALSE;
+ }
+
+ PGSQL_RETURN_OID(pgsql_oid);
+}
+/* }}} */
+
+/* {{{ proto bool pg_lo_unlink([resource connection,] string large_object_oid)
+ Delete a large object */
+PHP_FUNCTION(pg_lo_unlink)
+{
+ zval *pgsql_link = NULL;
+ long oid_long;
+ char *oid_string, *end_ptr;
+ int oid_strlen;
+ PGconn *pgsql;
+ Oid oid;
+ int id = -1;
+ int argc = ZEND_NUM_ARGS();
+
+ /* accept string type since Oid type is unsigned int */
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "rs", &pgsql_link, &oid_string, &oid_strlen) == SUCCESS) {
+ oid = (Oid)strtoul(oid_string, &end_ptr, 10);
+ if ((oid_string+oid_strlen) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong OID value passed");
+ RETURN_FALSE;
+ }
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "rl", &pgsql_link, &oid_long) == SUCCESS) {
+ if (oid_long <= InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid OID specified");
+ RETURN_FALSE;
+ }
+ oid = (Oid)oid_long;
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "s", &oid_string, &oid_strlen) == SUCCESS) {
+ oid = (Oid)strtoul(oid_string, &end_ptr, 10);
+ if ((oid_string+oid_strlen) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong OID value passed");
+ RETURN_FALSE;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "l", &oid_long) == SUCCESS) {
+ if (oid_long <= InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid OID is specified");
+ RETURN_FALSE;
+ }
+ oid = (Oid)oid_long;
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+ else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Requires 1 or 2 arguments");
+ RETURN_FALSE;
+ }
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (lo_unlink(pgsql, oid) == -1) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to delete PostgreSQL large object %u", oid);
+ RETURN_FALSE;
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto resource pg_lo_open([resource connection,] int large_object_oid, string mode)
+ Open a large object and return fd */
+PHP_FUNCTION(pg_lo_open)
+{
+ zval *pgsql_link = NULL;
+ long oid_long;
+ char *oid_string, *end_ptr, *mode_string;
+ int oid_strlen, mode_strlen;
+ PGconn *pgsql;
+ Oid oid;
+ int id = -1, pgsql_mode=0, pgsql_lofd;
+ int create=0;
+ pgLofp *pgsql_lofp;
+ int argc = ZEND_NUM_ARGS();
+
+ /* accept string type since Oid is unsigned int */
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "rss", &pgsql_link, &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
+ oid = (Oid)strtoul(oid_string, &end_ptr, 10);
+ if ((oid_string+oid_strlen) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong OID value passed");
+ RETURN_FALSE;
+ }
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "rls", &pgsql_link, &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
+ if (oid_long <= InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid OID specified");
+ RETURN_FALSE;
+ }
+ oid = (Oid)oid_long;
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "ss", &oid_string, &oid_strlen, &mode_string, &mode_strlen) == SUCCESS) {
+ oid = (Oid)strtoul(oid_string, &end_ptr, 10);
+ if ((oid_string+oid_strlen) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong OID value passed");
+ RETURN_FALSE;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "ls", &oid_long, &mode_string, &mode_strlen) == SUCCESS) {
+ if (oid_long <= InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid OID specified");
+ RETURN_FALSE;
+ }
+ oid = (Oid)oid_long;
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+ else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Requires 1 or 2 arguments");
+ RETURN_FALSE;
+ }
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ /* r/w/+ is little bit more PHP-like than INV_READ/INV_WRITE and a lot of
+ faster to type. Unfortunately, doesn't behave the same way as fopen()...
+ (Jouni)
+ */
+
+ if (strchr(mode_string, 'r') == mode_string) {
+ pgsql_mode |= INV_READ;
+ if (strchr(mode_string, '+') == mode_string+1) {
+ pgsql_mode |= INV_WRITE;
+ }
+ }
+ if (strchr(mode_string, 'w') == mode_string) {
+ pgsql_mode |= INV_WRITE;
+ create = 1;
+ if (strchr(mode_string, '+') == mode_string+1) {
+ pgsql_mode |= INV_READ;
+ }
+ }
+
+ pgsql_lofp = (pgLofp *) emalloc(sizeof(pgLofp));
+
+ if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
+ if (create) {
+ if ((oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == 0) {
+ efree(pgsql_lofp);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create PostgreSQL large object");
+ RETURN_FALSE;
+ } else {
+ if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
+ if (lo_unlink(pgsql, oid) == -1) {
+ efree(pgsql_lofp);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Something is really messed up! Your database is badly corrupted in a way NOT related to PHP");
+ RETURN_FALSE;
+ }
+ efree(pgsql_lofp);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open PostgreSQL large object");
+ RETURN_FALSE;
+ } else {
+ pgsql_lofp->conn = pgsql;
+ pgsql_lofp->lofd = pgsql_lofd;
+ Z_LVAL_P(return_value) = zend_list_insert(pgsql_lofp, le_lofp TSRMLS_CC);
+ Z_TYPE_P(return_value) = IS_LONG;
+ }
+ }
+ } else {
+ efree(pgsql_lofp);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open PostgreSQL large object");
+ RETURN_FALSE;
+ }
+ } else {
+ pgsql_lofp->conn = pgsql;
+ pgsql_lofp->lofd = pgsql_lofd;
+ ZEND_REGISTER_RESOURCE(return_value, pgsql_lofp, le_lofp);
+ }
+}
+/* }}} */
+
+/* {{{ proto bool pg_lo_close(resource large_object)
+ Close a large object */
+PHP_FUNCTION(pg_lo_close)
+{
+ zval *pgsql_lofp;
+ pgLofp *pgsql;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_lofp) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pgsql, pgLofp *, &pgsql_lofp, -1, "PostgreSQL large object", le_lofp);
+
+ if (lo_close((PGconn *)pgsql->conn, pgsql->lofd) < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to close PostgreSQL large object descriptor %d", pgsql->lofd);
+ RETVAL_FALSE;
+ } else {
+ RETVAL_TRUE;
+ }
+
+ zend_list_delete(Z_RESVAL_P(pgsql_lofp));
+ return;
+}
+/* }}} */
+
+#define PGSQL_LO_READ_BUF_SIZE 8192
+
+/* {{{ proto string pg_lo_read(resource large_object [, int len])
+ Read a large object */
+PHP_FUNCTION(pg_lo_read)
+{
+ zval *pgsql_id;
+ long len;
+ int buf_len = PGSQL_LO_READ_BUF_SIZE, nbytes, argc = ZEND_NUM_ARGS();
+ char *buf;
+ pgLofp *pgsql;
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "r|l", &pgsql_id, &len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pgsql, pgLofp *, &pgsql_id, -1, "PostgreSQL large object", le_lofp);
+
+ if (argc > 1) {
+ buf_len = len;
+ }
+
+ buf = (char *) safe_emalloc(sizeof(char), (buf_len+1), 0);
+ if ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, buf, buf_len))<0) {
+ efree(buf);
+ RETURN_FALSE;
+ }
+
+ buf[nbytes] = '\0';
+ RETURN_STRINGL(buf, nbytes, 0);
+}
+/* }}} */
+
+/* {{{ proto int pg_lo_write(resource large_object, string buf [, int len])
+ Write a large object */
+PHP_FUNCTION(pg_lo_write)
+{
+ zval *pgsql_id;
+ char *str;
+ long z_len;
+ int str_len, nbytes;
+ int len;
+ pgLofp *pgsql;
+ int argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "rs|l", &pgsql_id, &str, &str_len, &z_len) == FAILURE) {
+ return;
+ }
+
+ if (argc > 2) {
+ if (z_len > str_len) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot write more than buffer size %d. Tried to write %ld", str_len, z_len);
+ RETURN_FALSE;
+ }
+ if (z_len < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Buffer size must be larger than 0, but %ld was specified", z_len);
+ RETURN_FALSE;
+ }
+ len = z_len;
+ }
+ else {
+ len = str_len;
+ }
+
+ ZEND_FETCH_RESOURCE(pgsql, pgLofp *, &pgsql_id, -1, "PostgreSQL large object", le_lofp);
+
+ if ((nbytes = lo_write((PGconn *)pgsql->conn, pgsql->lofd, str, len)) == -1) {
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(nbytes);
+}
+/* }}} */
+
+/* {{{ proto int pg_lo_read_all(resource large_object)
+ Read a large object and send straight to browser */
+PHP_FUNCTION(pg_lo_read_all)
+{
+ zval *pgsql_id;
+ int tbytes;
+ volatile int nbytes;
+ char buf[PGSQL_LO_READ_BUF_SIZE];
+ pgLofp *pgsql;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_id) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pgsql, pgLofp *, &pgsql_id, -1, "PostgreSQL large object", le_lofp);
+
+ tbytes = 0;
+ while ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, buf, PGSQL_LO_READ_BUF_SIZE))>0) {
+ PHPWRITE(buf, nbytes);
+ tbytes += nbytes;
+ }
+ RETURN_LONG(tbytes);
+}
+/* }}} */
+
+/* {{{ proto int pg_lo_import([resource connection, ] string filename [, mixed oid])
+ Import large object direct from filesystem */
+PHP_FUNCTION(pg_lo_import)
+{
+ zval *pgsql_link = NULL, *oid = NULL;
+ char *file_in;
+ int id = -1, name_len;
+ int argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+ Oid returned_oid;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "rp|z", &pgsql_link, &file_in, &name_len, &oid) == SUCCESS) {
+ ;
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "p|z", &file_in, &name_len, &oid) == SUCCESS) {
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+ /* old calling convention, deprecated since PHP 4.2 */
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "pr", &file_in, &name_len, &pgsql_link ) == SUCCESS) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Old API is used");
+ }
+ else {
+ WRONG_PARAM_COUNT;
+ }
+
+ if (php_check_open_basedir(file_in TSRMLS_CC)) {
+ RETURN_FALSE;
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (oid) {
+#ifndef HAVE_PG_LO_IMPORT_WITH_OID
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "OID value passing not supported");
+#else
+ Oid wanted_oid;
+ switch (Z_TYPE_P(oid)) {
+ case IS_STRING:
+ {
+ char *end_ptr;
+ wanted_oid = (Oid)strtoul(Z_STRVAL_P(oid), &end_ptr, 10);
+ if ((Z_STRVAL_P(oid)+Z_STRLEN_P(oid)) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "invalid OID value passed");
+ RETURN_FALSE;
+ }
+ }
+ break;
+ case IS_LONG:
+ if (Z_LVAL_P(oid) < (long)InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "invalid OID value passed");
+ RETURN_FALSE;
+ }
+ wanted_oid = (Oid)Z_LVAL_P(oid);
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "invalid OID value passed");
+ RETURN_FALSE;
+ }
+
+ returned_oid = lo_import_with_oid(pgsql, file_in, wanted_oid);
+
+ if (returned_oid == InvalidOid) {
+ RETURN_FALSE;
+ }
+
+ PGSQL_RETURN_OID(returned_oid);
+#endif
+ }
+
+ returned_oid = lo_import(pgsql, file_in);
+
+ if (returned_oid == InvalidOid) {
+ RETURN_FALSE;
+ }
+ PGSQL_RETURN_OID(returned_oid);
+}
+/* }}} */
+
+/* {{{ proto bool pg_lo_export([resource connection, ] int objoid, string filename)
+ Export large object direct to filesystem */
+PHP_FUNCTION(pg_lo_export)
+{
+ zval *pgsql_link = NULL;
+ char *file_out, *oid_string, *end_ptr;
+ int oid_strlen;
+ int id = -1, name_len;
+ long oid_long;
+ Oid oid;
+ PGconn *pgsql;
+ int argc = ZEND_NUM_ARGS();
+
+ /* allow string to handle large OID value correctly */
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "rlp", &pgsql_link, &oid_long, &file_out, &name_len) == SUCCESS) {
+ if (oid_long <= InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid OID specified");
+ RETURN_FALSE;
+ }
+ oid = (Oid)oid_long;
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "rss", &pgsql_link, &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
+ oid = (Oid)strtoul(oid_string, &end_ptr, 10);
+ if ((oid_string+oid_strlen) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong OID value passed");
+ RETURN_FALSE;
+ }
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "lp", &oid_long, &file_out, &name_len) == SUCCESS) {
+ if (oid_long <= InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid OID specified");
+ RETURN_FALSE;
+ }
+ oid = (Oid)oid_long;
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "sp", &oid_string, &oid_strlen, &file_out, &name_len) == SUCCESS) {
+ oid = (Oid)strtoul(oid_string, &end_ptr, 10);
+ if ((oid_string+oid_strlen) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong OID value passed");
+ RETURN_FALSE;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "spr", &oid_string, &oid_strlen, &file_out, &name_len, &pgsql_link) == SUCCESS) {
+ oid = (Oid)strtoul(oid_string, &end_ptr, 10);
+ if ((oid_string+oid_strlen) != end_ptr) {
+ /* wrong integer format */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong OID value passed");
+ RETURN_FALSE;
+ }
+ }
+ else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc TSRMLS_CC,
+ "lpr", &oid_long, &file_out, &name_len, &pgsql_link) == SUCCESS) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Old API is used");
+ if (oid_long <= InvalidOid) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid OID specified");
+ RETURN_FALSE;
+ }
+ oid = (Oid)oid_long;
+ }
+ else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Requires 2 or 3 arguments");
+ RETURN_FALSE;
+ }
+
+ if (php_check_open_basedir(file_out TSRMLS_CC)) {
+ RETURN_FALSE;
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (lo_export(pgsql, oid, file_out)) {
+ RETURN_TRUE;
+ }
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool pg_lo_seek(resource large_object, int offset [, int whence])
+ Seeks position of large object */
+PHP_FUNCTION(pg_lo_seek)
+{
+ zval *pgsql_id = NULL;
+ long offset = 0, whence = SEEK_CUR;
+ pgLofp *pgsql;
+ int argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "rl|l", &pgsql_id, &offset, &whence) == FAILURE) {
+ return;
+ }
+ if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid whence parameter");
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pgsql, pgLofp *, &pgsql_id, -1, "PostgreSQL large object", le_lofp);
+
+ if (lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, offset, whence) > -1) {
+ RETURN_TRUE;
+ } else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto int pg_lo_tell(resource large_object)
+ Returns current position of large object */
+PHP_FUNCTION(pg_lo_tell)
+{
+ zval *pgsql_id = NULL;
+ int offset = 0;
+ pgLofp *pgsql;
+ int argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "r", &pgsql_id) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(pgsql, pgLofp *, &pgsql_id, -1, "PostgreSQL large object", le_lofp);
+
+ offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
+ RETURN_LONG(offset);
+}
+/* }}} */
+
+#if HAVE_PQSETERRORVERBOSITY
+/* {{{ proto int pg_set_error_verbosity([resource connection,] int verbosity)
+ Set error verbosity */
+PHP_FUNCTION(pg_set_error_verbosity)
+{
+ zval *pgsql_link = NULL;
+ long verbosity;
+ int id = -1, argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+
+ if (argc == 1) {
+ if (zend_parse_parameters(argc TSRMLS_CC, "l", &verbosity) == FAILURE) {
+ return;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ } else {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rl", &pgsql_link, &verbosity) == FAILURE) {
+ return;
+ }
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (verbosity & (PQERRORS_TERSE|PQERRORS_DEFAULT|PQERRORS_VERBOSE)) {
+ Z_LVAL_P(return_value) = PQsetErrorVerbosity(pgsql, verbosity);
+ Z_TYPE_P(return_value) = IS_LONG;
+ } else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+#endif
+
+#ifdef HAVE_PQCLIENTENCODING
+/* {{{ proto int pg_set_client_encoding([resource connection,] string encoding)
+ Set client encoding */
+PHP_FUNCTION(pg_set_client_encoding)
+{
+ char *encoding;
+ int encoding_len;
+ zval *pgsql_link = NULL;
+ int id = -1, argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+
+ if (argc == 1) {
+ if (zend_parse_parameters(argc TSRMLS_CC, "s", &encoding, &encoding_len) == FAILURE) {
+ return;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ } else {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rs", &pgsql_link, &encoding, &encoding_len) == FAILURE) {
+ return;
+ }
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ Z_LVAL_P(return_value) = PQsetClientEncoding(pgsql, encoding);
+ Z_TYPE_P(return_value) = IS_LONG;
+}
+/* }}} */
+
+/* {{{ proto string pg_client_encoding([resource connection])
+ Get the current client encoding */
+PHP_FUNCTION(pg_client_encoding)
+{
+ zval *pgsql_link = NULL;
+ int id = -1, argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "|r", &pgsql_link) == FAILURE) {
+ return;
+ }
+
+ if (argc == 0) {
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ /* Just do the same as found in PostgreSQL sources... */
+
+#ifndef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
+#define pg_encoding_to_char(x) "SQL_ASCII"
+#endif
+
+ Z_STRVAL_P(return_value) = (char *) pg_encoding_to_char(PQclientEncoding(pgsql));
+ Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value));
+ Z_STRVAL_P(return_value) = (char *) estrdup(Z_STRVAL_P(return_value));
+ Z_TYPE_P(return_value) = IS_STRING;
+}
+/* }}} */
+#endif
+
+#if !HAVE_PQGETCOPYDATA
+#define COPYBUFSIZ 8192
+#endif
+
+/* {{{ proto bool pg_end_copy([resource connection])
+ Sync with backend. Completes the Copy command */
+PHP_FUNCTION(pg_end_copy)
+{
+ zval *pgsql_link = NULL;
+ int id = -1, argc = ZEND_NUM_ARGS();
+ PGconn *pgsql;
+ int result = 0;
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "|r", &pgsql_link) == FAILURE) {
+ return;
+ }
+
+ if (argc == 0) {
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ result = PQendcopy(pgsql);
+
+ if (result!=0) {
+ PHP_PQ_ERROR("Query failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto bool pg_put_line([resource connection,] string query)
+ Send null-terminated string to backend server*/
+PHP_FUNCTION(pg_put_line)
+{
+ char *query;
+ zval *pgsql_link = NULL;
+ int query_len, id = -1;
+ PGconn *pgsql;
+ int result = 0, argc = ZEND_NUM_ARGS();
+
+ if (argc == 1) {
+ if (zend_parse_parameters(argc TSRMLS_CC, "s", &query, &query_len) == FAILURE) {
+ return;
+ }
+ id = PGG(default_link);
+ CHECK_DEFAULT_LINK(id);
+ } else {
+ if (zend_parse_parameters(argc TSRMLS_CC, "rs", &pgsql_link, &query, &query_len) == FAILURE) {
+ return;
+ }
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ result = PQputline(pgsql, query);
+ if (result==EOF) {
+ PHP_PQ_ERROR("Query failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto array pg_copy_to(resource connection, string table_name [, string delimiter [, string null_as]])
+ Copy table to array */
+PHP_FUNCTION(pg_copy_to)
+{
+ zval *pgsql_link;
+ char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
+ int table_name_len, pg_delim_len, pg_null_as_len, free_pg_null = 0;
+ char *query;
+ int id = -1;
+ PGconn *pgsql;
+ PGresult *pgsql_result;
+ ExecStatusType status;
+ int copydone = 0;
+#if !HAVE_PQGETCOPYDATA
+ char copybuf[COPYBUFSIZ];
+#endif
+ char *csv = (char *)NULL;
+ int ret;
+ int argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "rs|ss",
+ &pgsql_link, &table_name, &table_name_len,
+ &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
+ return;
+ }
+ if (!pg_delim) {
+ pg_delim = "\t";
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (!pg_null_as) {
+ pg_null_as = safe_estrdup("\\\\N");
+ free_pg_null = 1;
+ }
+
+ spprintf(&query, 0, "COPY %s TO STDOUT DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
+
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ PQclear(pgsql_result);
+ }
+ pgsql_result = PQexec(pgsql, query);
+ if (free_pg_null) {
+ efree(pg_null_as);
+ }
+ efree(query);
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(pgsql);
+ }
+
+ switch (status) {
+ case PGRES_COPY_OUT:
+ if (pgsql_result) {
+ PQclear(pgsql_result);
+ array_init(return_value);
+#if HAVE_PQGETCOPYDATA
+ while (!copydone)
+ {
+ ret = PQgetCopyData(pgsql, &csv, 0);
+ switch (ret) {
+ case -1:
+ copydone = 1;
+ break;
+ case 0:
+ case -2:
+ PHP_PQ_ERROR("getline failed: %s", pgsql);
+ RETURN_FALSE;
+ break;
+ default:
+ add_next_index_string(return_value, csv, 1);
+ PQfreemem(csv);
+ break;
+ }
+ }
+#else
+ while (!copydone)
+ {
+ if ((ret = PQgetline(pgsql, copybuf, COPYBUFSIZ))) {
+ PHP_PQ_ERROR("getline failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+
+ if (copybuf[0] == '\\' &&
+ copybuf[1] == '.' &&
+ copybuf[2] == '\0')
+ {
+ copydone = 1;
+ }
+ else
+ {
+ if (csv == (char *)NULL) {
+ csv = estrdup(copybuf);
+ } else {
+ csv = (char *)erealloc(csv, strlen(csv) + sizeof(char)*(COPYBUFSIZ+1));
+ strcat(csv, copybuf);
+ }
+
+ switch (ret)
+ {
+ case EOF:
+ copydone = 1;
+ case 0:
+ add_next_index_string(return_value, csv, 1);
+ efree(csv);
+ csv = (char *)NULL;
+ break;
+ case 1:
+ break;
+ }
+ }
+ }
+ if (PQendcopy(pgsql)) {
+ PHP_PQ_ERROR("endcopy failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+#endif
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ PQclear(pgsql_result);
+ }
+ } else {
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ }
+ break;
+ default:
+ PQclear(pgsql_result);
+ PHP_PQ_ERROR("Copy command failed: %s", pgsql);
+ RETURN_FALSE;
+ break;
+ }
+}
+/* }}} */
+
+/* {{{ proto bool pg_copy_from(resource connection, string table_name , array rows [, string delimiter [, string null_as]])
+ Copy table from array */
+PHP_FUNCTION(pg_copy_from)
+{
+ zval *pgsql_link = NULL, *pg_rows;
+ zval **tmp;
+ char *table_name, *pg_delim = NULL, *pg_null_as = NULL;
+ int table_name_len, pg_delim_len, pg_null_as_len;
+ int pg_null_as_free = 0;
+ char *query;
+ HashPosition pos;
+ int id = -1;
+ PGconn *pgsql;
+ PGresult *pgsql_result;
+ ExecStatusType status;
+ int argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "rsa|ss",
+ &pgsql_link, &table_name, &table_name_len, &pg_rows,
+ &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len) == FAILURE) {
+ return;
+ }
+ if (!pg_delim) {
+ pg_delim = "\t";
+ }
+ if (!pg_null_as) {
+ pg_null_as = safe_estrdup("\\\\N");
+ pg_null_as_free = 1;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, *pg_delim, pg_null_as);
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ PQclear(pgsql_result);
+ }
+ pgsql_result = PQexec(pgsql, query);
+
+ if (pg_null_as_free) {
+ efree(pg_null_as);
+ }
+ efree(query);
+
+ if (pgsql_result) {
+ status = PQresultStatus(pgsql_result);
+ } else {
+ status = (ExecStatusType) PQstatus(pgsql);
+ }
+
+ switch (status) {
+ case PGRES_COPY_IN:
+ if (pgsql_result) {
+ int command_failed = 0;
+ PQclear(pgsql_result);
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos);
+#if HAVE_PQPUTCOPYDATA
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
+ convert_to_string_ex(tmp);
+ query = (char *)emalloc(Z_STRLEN_PP(tmp) + 2);
+ strlcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp) + 2);
+ if(Z_STRLEN_PP(tmp) > 0 && *(query + Z_STRLEN_PP(tmp) - 1) != '\n') {
+ strlcat(query, "\n", Z_STRLEN_PP(tmp) + 2);
+ }
+ if (PQputCopyData(pgsql, query, strlen(query)) != 1) {
+ efree(query);
+ PHP_PQ_ERROR("copy failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+ efree(query);
+ zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
+ }
+ if (PQputCopyEnd(pgsql, NULL) != 1) {
+ PHP_PQ_ERROR("putcopyend failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+#else
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
+ convert_to_string_ex(tmp);
+ query = (char *)emalloc(Z_STRLEN_PP(tmp) + 2);
+ strlcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp) + 2);
+ if(Z_STRLEN_PP(tmp) > 0 && *(query + Z_STRLEN_PP(tmp) - 1) != '\n') {
+ strlcat(query, "\n", Z_STRLEN_PP(tmp) + 2);
+ }
+ if (PQputline(pgsql, query)==EOF) {
+ efree(query);
+ PHP_PQ_ERROR("copy failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+ efree(query);
+ zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
+ }
+ if (PQputline(pgsql, "\\.\n") == EOF) {
+ PHP_PQ_ERROR("putline failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+ if (PQendcopy(pgsql)) {
+ PHP_PQ_ERROR("endcopy failed: %s", pgsql);
+ RETURN_FALSE;
+ }
+#endif
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
+ PHP_PQ_ERROR("Copy command failed: %s", pgsql);
+ command_failed = 1;
+ }
+ PQclear(pgsql_result);
+ }
+ if (command_failed) {
+ RETURN_FALSE;
+ }
+ } else {
+ PQclear(pgsql_result);
+ RETURN_FALSE;
+ }
+ RETURN_TRUE;
+ break;
+ default:
+ PQclear(pgsql_result);
+ PHP_PQ_ERROR("Copy command failed: %s", pgsql);
+ RETURN_FALSE;
+ break;
+ }
+}
+/* }}} */
+
+#ifdef HAVE_PQESCAPE
+/* {{{ proto string pg_escape_string([resource connection,] string data)
+ Escape string for text/char type */
+PHP_FUNCTION(pg_escape_string)
+{
+ char *from = NULL, *to = NULL;
+ zval *pgsql_link;
+#ifdef HAVE_PQESCAPE_CONN
+ PGconn *pgsql;
+#endif
+ int to_len;
+ int from_len;
+ int id = -1;
+
+ switch (ZEND_NUM_ARGS()) {
+ case 1:
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &from, &from_len) == FAILURE) {
+ return;
+ }
+ pgsql_link = NULL;
+ id = PGG(default_link);
+ break;
+
+ default:
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &from, &from_len) == FAILURE) {
+ return;
+ }
+ break;
+ }
+
+ to = (char *) safe_emalloc(from_len, 2, 1);
+
+#ifdef HAVE_PQESCAPE_CONN
+ if (pgsql_link != NULL || id != -1) {
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+ to_len = (int) PQescapeStringConn(pgsql, to, from, (size_t)from_len, NULL);
+ } else
+#endif
+ to_len = (int) PQescapeString(to, from, (size_t)from_len);
+
+ RETURN_STRINGL(to, to_len, 0);
+}
+/* }}} */
+
+/* {{{ proto string pg_escape_bytea([resource connection,] string data)
+ Escape binary for bytea type */
+PHP_FUNCTION(pg_escape_bytea)
+{
+ char *from = NULL, *to = NULL;
+ size_t to_len;
+ int from_len, id = -1;
+#ifdef HAVE_PQESCAPE_BYTEA_CONN
+ PGconn *pgsql;
+#endif
+ zval *pgsql_link;
+
+ switch (ZEND_NUM_ARGS()) {
+ case 1:
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &from, &from_len) == FAILURE) {
+ return;
+ }
+ pgsql_link = NULL;
+ id = PGG(default_link);
+ break;
+
+ default:
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &from, &from_len) == FAILURE) {
+ return;
+ }
+ break;
+ }
+
+#ifdef HAVE_PQESCAPE_BYTEA_CONN
+ if (pgsql_link != NULL || id != -1) {
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+ to = (char *)PQescapeByteaConn(pgsql, from, (size_t)from_len, &to_len);
+ } else
+#endif
+ to = (char *)PQescapeBytea((unsigned char*)from, from_len, &to_len);
+
+ RETVAL_STRINGL(to, to_len-1, 1); /* to_len includes addtional '\0' */
+ PQfreemem(to);
+}
+/* }}} */
+
+#if !HAVE_PQUNESCAPEBYTEA
+/* PQunescapeBytea() from PostgreSQL 7.3 to provide bytea unescape feature to 7.2 users.
+ Renamed to php_pgsql_unescape_bytea() */
+/*
+ * PQunescapeBytea - converts the null terminated string representation
+ * of a bytea, strtext, into binary, filling a buffer. It returns a
+ * pointer to the buffer which is NULL on error, and the size of the
+ * buffer in retbuflen. The pointer may subsequently be used as an
+ * argument to the function free(3). It is the reverse of PQescapeBytea.
+ *
+ * The following transformations are reversed:
+ * '\0' == ASCII 0 == \000
+ * '\'' == ASCII 39 == \'
+ * '\\' == ASCII 92 == \\
+ *
+ * States:
+ * 0 normal 0->1->2->3->4
+ * 1 \ 1->5
+ * 2 \0 1->6
+ * 3 \00
+ * 4 \000
+ * 5 \'
+ * 6 \\
+ */
+static unsigned char * php_pgsql_unescape_bytea(unsigned char *strtext, size_t *retbuflen)
+{
+ size_t buflen;
+ unsigned char *buffer,
+ *sp,
+ *bp;
+ unsigned int state = 0;
+
+ if (strtext == NULL)
+ return NULL;
+ buflen = strlen(strtext); /* will shrink, also we discover if
+ * strtext */
+ buffer = (unsigned char *) emalloc(buflen); /* isn't NULL terminated */
+ for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
+ {
+ switch (state)
+ {
+ case 0:
+ if (*sp == '\\')
+ state = 1;
+ *bp = *sp;
+ break;
+ case 1:
+ if (*sp == '\'') /* state=5 */
+ { /* replace \' with 39 */
+ bp--;
+ *bp = '\'';
+ buflen--;
+ state = 0;
+ }
+ else if (*sp == '\\') /* state=6 */
+ { /* replace \\ with 92 */
+ bp--;
+ *bp = '\\';
+ buflen--;
+ state = 0;
+ }
+ else
+ {
+ if (isdigit(*sp))
+ state = 2;
+ else
+ state = 0;
+ *bp = *sp;
+ }
+ break;
+ case 2:
+ if (isdigit(*sp))
+ state = 3;
+ else
+ state = 0;
+ *bp = *sp;
+ break;
+ case 3:
+ if (isdigit(*sp)) /* state=4 */
+ {
+ unsigned char *start, *end, buf[4]; /* 000 + '\0' */
+
+ bp -= 3;
+ memcpy(buf, sp-2, 3);
+ buf[3] = '\0';
+ start = buf;
+ *bp = (unsigned char)strtoul(start, (char **)&end, 8);
+ buflen -= 3;
+ state = 0;
+ }
+ else
+ {
+ *bp = *sp;
+ state = 0;
+ }
+ break;
+ }
+ }
+ buffer = erealloc(buffer, buflen+1);
+ buffer[buflen] = '\0';
+
+ *retbuflen = buflen;
+ return buffer;
+}
+#endif
+
+/* {{{ proto string pg_unescape_bytea(string data)
+ Unescape binary for bytea type */
+PHP_FUNCTION(pg_unescape_bytea)
+{
+ char *from = NULL, *to = NULL, *tmp = NULL;
+ size_t to_len;
+ int from_len;
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &from, &from_len) == FAILURE) {
+ return;
+ }
+
+#if HAVE_PQUNESCAPEBYTEA
+ tmp = (char *)PQunescapeBytea((unsigned char*)from, &to_len);
+ to = estrndup(tmp, to_len);
+ PQfreemem(tmp);
+#else
+ to = (char *)php_pgsql_unescape_bytea((unsigned char*)from, &to_len);
+#endif
+ if (!to) {
+ RETURN_FALSE;
+ }
+ RETVAL_STRINGL(to, to_len, 0);
+}
+/* }}} */
+#endif
+
+#ifdef HAVE_PQESCAPE
+#if !HAVE_PQESCAPELITERAL
+/* emulate libpq's PQescapeInternal() 9.0 or later */
+static char* php_pgsql_PQescapeInternal(PGconn *conn, const char *str, size_t len, int escape_literal) {
+ char *result, *rp;
+ const char *s;
+ size_t tmp_len;
+ int input_len = len;
+ char quote_char = escape_literal ? '\'' : '"';
+
+ if (!conn) {
+ return NULL;
+ }
+
+ /*
+ * NOTE: multibyte strings that could cointain slashes should be considered.
+ * (e.g. SJIS, BIG5) However, it cannot be done without valid PGconn and mbstring.
+ * Therefore, this function does not support such encodings currently.
+ * FIXME: add encoding check and skip multibyte char bytes if there is vaild PGconn.
+ */
+
+ /* allocate enough memory */
+ rp = result = (char *)emalloc(len*2 + 5); /* leading " E" needs extra 2 bytes + quote_chars on both end for 2 bytes + NULL */
+
+ if (escape_literal) {
+ /* check backslashes */
+ tmp_len = strspn(str, "\\");
+ if (tmp_len != len) {
+ /* add " E" for escaping slashes */
+ *rp++ = ' ';
+ *rp++ = 'E';
+ }
+ }
+ /* open quote */
+ *rp++ = quote_char;
+ for (s = str; s - str < input_len; ++s) {
+ if (*s == quote_char || (escape_literal && *s == '\\')) {
+ *rp++ = *s;
+ *rp++ = *s;
+ } else {
+ *rp++ = *s;
+ }
+ }
+ *rp++ = quote_char;
+ *rp = '\0';
+
+ return result;
+}
+#endif
+
+static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_literal) {
+ char *from = NULL, *to = NULL, *tmp = NULL;
+ zval *pgsql_link = NULL;
+ PGconn *pgsql;
+ int to_len;
+ int from_len;
+ int id = -1;
+
+ switch (ZEND_NUM_ARGS()) {
+ case 1:
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &from, &from_len) == FAILURE) {
+ return;
+ }
+ pgsql_link = NULL;
+ id = PGG(default_link);
+ break;
+
+ default:
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &pgsql_link, &from, &from_len) == FAILURE) {
+ return;
+ }
+ break;
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+ if (pgsql == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Cannot get default pgsql link");
+ RETURN_FALSE;
+ }
+#ifdef HAVE_PQESCAPELITERAL
+ if (escape_literal) {
+ tmp = PQescapeLiteral(pgsql, from, (size_t)from_len);
+ } else {
+ tmp = PQescapeIdentifier(pgsql, from, (size_t)from_len);
+ }
+ if (!tmp) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Failed to escape");
+ RETURN_FALSE;
+ }
+ to = estrdup(tmp);
+ PQfreemem(tmp);
+#else
+ to = php_pgsql_PQescapeInternal(pgsql, from, (size_t)from_len, escape_literal);
+ if (!to) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Failed to escape");
+ RETURN_FALSE;
+ }
+#endif
+
+ RETURN_STRING(to, 0);
+}
+
+/* {{{ proto string pg_escape_literal([resource connection,] string data)
+ Escape parameter as string literal (i.e. parameter) */
+PHP_FUNCTION(pg_escape_literal)
+{
+ php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+/* }}} */
+
+/* {{{ proto string pg_escape_identifier([resource connection,] string data)
+ Escape identifier (i.e. table name, field name) */
+PHP_FUNCTION(pg_escape_identifier)
+{
+ php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+/* }}} */
+#endif
+
+
+/* {{{ proto string pg_result_error(resource result)
+ Get error message associated with result */
+PHP_FUNCTION(pg_result_error)
+{
+ zval *result;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+ char *err = NULL;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
+ &result) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+ if (!pgsql_result) {
+ RETURN_FALSE;
+ }
+ err = (char *)PQresultErrorMessage(pgsql_result);
+ RETURN_STRING(err,1);
+}
+/* }}} */
+
+#if HAVE_PQRESULTERRORFIELD
+/* {{{ proto string pg_result_error_field(resource result, int fieldcode)
+ Get error message field associated with result */
+PHP_FUNCTION(pg_result_error_field)
+{
+ zval *result;
+ long fieldcode;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+ char *field = NULL;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "rl",
+ &result, &fieldcode) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+ if (!pgsql_result) {
+ RETURN_FALSE;
+ }
+ if (fieldcode & (PG_DIAG_SEVERITY|PG_DIAG_SQLSTATE|PG_DIAG_MESSAGE_PRIMARY|PG_DIAG_MESSAGE_DETAIL
+ |PG_DIAG_MESSAGE_HINT|PG_DIAG_STATEMENT_POSITION
+#if PG_DIAG_INTERNAL_POSITION
+ |PG_DIAG_INTERNAL_POSITION
+#endif
+#if PG_DIAG_INTERNAL_QUERY
+ |PG_DIAG_INTERNAL_QUERY
+#endif
+ |PG_DIAG_CONTEXT|PG_DIAG_SOURCE_FILE|PG_DIAG_SOURCE_LINE
+ |PG_DIAG_SOURCE_FUNCTION)) {
+ field = (char *)PQresultErrorField(pgsql_result, fieldcode);
+ if (field == NULL) {
+ RETURN_NULL();
+ } else {
+ RETURN_STRING(field, 1);
+ }
+ } else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+#endif
+
+/* {{{ proto int pg_connection_status(resource connection)
+ Get connection status */
+PHP_FUNCTION(pg_connection_status)
+{
+ zval *pgsql_link = NULL;
+ int id = -1;
+ PGconn *pgsql;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
+ &pgsql_link) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ RETURN_LONG(PQstatus(pgsql));
+}
+
+/* }}} */
+
+#if HAVE_PGTRANSACTIONSTATUS
+/* {{{ proto int pg_transaction_status(resource connection)
+ Get transaction status */
+PHP_FUNCTION(pg_transaction_status)
+{
+ zval *pgsql_link = NULL;
+ int id = -1;
+ PGconn *pgsql;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
+ &pgsql_link) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ RETURN_LONG(PQtransactionStatus(pgsql));
+}
+#endif
+
+/* }}} */
+
+/* {{{ proto bool pg_connection_reset(resource connection)
+ Reset connection (reconnect) */
+PHP_FUNCTION(pg_connection_reset)
+{
+ zval *pgsql_link;
+ int id = -1;
+ PGconn *pgsql;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
+ &pgsql_link) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ PQreset(pgsql);
+ if (PQstatus(pgsql) == CONNECTION_BAD) {
+ RETURN_FALSE;
+ }
+ RETURN_TRUE;
+}
+
+/* }}} */
+
+#define PHP_PG_ASYNC_IS_BUSY 1
+#define PHP_PG_ASYNC_REQUEST_CANCEL 2
+
+/* {{{ php_pgsql_flush_query
+ */
+static int php_pgsql_flush_query(PGconn *pgsql TSRMLS_DC)
+{
+ PGresult *res;
+ int leftover = 0;
+
+ if (PQ_SETNONBLOCKING(pgsql, 1)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE,"Cannot set connection to nonblocking mode");
+ return -1;
+ }
+ while ((res = PQgetResult(pgsql))) {
+ PQclear(res);
+ leftover++;
+ }
+ PQ_SETNONBLOCKING(pgsql, 0);
+ return leftover;
+}
+/* }}} */
+
+/* {{{ php_pgsql_do_async
+ */
+static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
+{
+ zval *pgsql_link;
+ int id = -1;
+ PGconn *pgsql;
+ PGresult *pgsql_result;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
+ &pgsql_link) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 1)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
+ RETURN_FALSE;
+ }
+ switch(entry_type) {
+ case PHP_PG_ASYNC_IS_BUSY:
+ PQconsumeInput(pgsql);
+ Z_LVAL_P(return_value) = PQisBusy(pgsql);
+ Z_TYPE_P(return_value) = IS_LONG;
+ break;
+ case PHP_PG_ASYNC_REQUEST_CANCEL:
+ Z_LVAL_P(return_value) = PQrequestCancel(pgsql);
+ Z_TYPE_P(return_value) = IS_LONG;
+ while ((pgsql_result = PQgetResult(pgsql))) {
+ PQclear(pgsql_result);
+ }
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "PostgreSQL module error, please report this error");
+ break;
+ }
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
+ }
+ convert_to_boolean_ex(&return_value);
+}
+/* }}} */
+
+/* {{{ proto bool pg_cancel_query(resource connection)
+ Cancel request */
+PHP_FUNCTION(pg_cancel_query)
+{
+ php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_REQUEST_CANCEL);
+}
+/* }}} */
+
+/* {{{ proto bool pg_connection_busy(resource connection)
+ Get connection is busy or not */
+PHP_FUNCTION(pg_connection_busy)
+{
+ php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_IS_BUSY);
+}
+/* }}} */
+
+/* {{{ proto bool pg_send_query(resource connection, string query)
+ Send asynchronous query */
+PHP_FUNCTION(pg_send_query)
+{
+ zval *pgsql_link;
+ char *query;
+ int len;
+ int id = -1;
+ PGconn *pgsql;
+ PGresult *res;
+ int leftover = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",
+ &pgsql_link, &query, &len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 1)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
+ RETURN_FALSE;
+ }
+ while ((res = PQgetResult(pgsql))) {
+ PQclear(res);
+ leftover = 1;
+ }
+ if (leftover) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE");
+ }
+ if (!PQsendQuery(pgsql, query)) {
+ if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
+ PQreset(pgsql);
+ }
+ if (!PQsendQuery(pgsql, query)) {
+ RETURN_FALSE;
+ }
+ }
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+#if HAVE_PQSENDQUERYPARAMS
+/* {{{ proto bool pg_send_query_params(resource connection, string query, array params)
+ Send asynchronous parameterized query */
+PHP_FUNCTION(pg_send_query_params)
+{
+ zval *pgsql_link, *pv_param_arr, **tmp;
+ int num_params = 0;
+ char **params = NULL;
+ char *query;
+ int query_len, id = -1;
+ PGconn *pgsql;
+ PGresult *res;
+ int leftover = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa/", &pgsql_link, &query, &query_len, &pv_param_arr) == FAILURE) {
+ return;
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 1)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
+ RETURN_FALSE;
+ }
+ while ((res = PQgetResult(pgsql))) {
+ PQclear(res);
+ leftover = 1;
+ }
+ if (leftover) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE");
+ }
+
+ zend_hash_internal_pointer_reset(Z_ARRVAL_P(pv_param_arr));
+ num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
+ if (num_params > 0) {
+ int i = 0;
+ params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
+
+ for(i = 0; i < num_params; i++) {
+ if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter");
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+
+ if (Z_TYPE_PP(tmp) == IS_NULL) {
+ params[i] = NULL;
+ } else {
+ zval tmp_val = **tmp;
+ zval_copy_ctor(&tmp_val);
+ convert_to_string(&tmp_val);
+ if (Z_TYPE(tmp_val) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error converting parameter");
+ zval_dtor(&tmp_val);
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+ params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
+ zval_dtor(&tmp_val);
+ }
+
+ zend_hash_move_forward(Z_ARRVAL_P(pv_param_arr));
+ }
+ }
+
+ if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
+ if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
+ PQreset(pgsql);
+ }
+ if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+ }
+ _php_pgsql_free_params(params, num_params);
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+#endif
+
+#if HAVE_PQSENDPREPARE
+/* {{{ proto bool pg_send_prepare(resource connection, string stmtname, string query)
+ Asynchronously prepare a query for future execution */
+PHP_FUNCTION(pg_send_prepare)
+{
+ zval *pgsql_link;
+ char *query, *stmtname;
+ int stmtname_len, query_len, id = -1;
+ PGconn *pgsql;
+ PGresult *res;
+ int leftover = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &pgsql_link, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
+ return;
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 1)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
+ RETURN_FALSE;
+ }
+ while ((res = PQgetResult(pgsql))) {
+ PQclear(res);
+ leftover = 1;
+ }
+ if (leftover) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE");
+ }
+ if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
+ if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
+ PQreset(pgsql);
+ }
+ if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
+ RETURN_FALSE;
+ }
+ }
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+#endif
+
+#if HAVE_PQSENDQUERYPREPARED
+/* {{{ proto bool pg_send_execute(resource connection, string stmtname, array params)
+ Executes prevriously prepared stmtname asynchronously */
+PHP_FUNCTION(pg_send_execute)
+{
+ zval *pgsql_link;
+ zval *pv_param_arr, **tmp;
+ int num_params = 0;
+ char **params = NULL;
+ char *stmtname;
+ int stmtname_len, id = -1;
+ PGconn *pgsql;
+ PGresult *res;
+ int leftover = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &pgsql_link, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
+ return;
+ }
+
+ if (pgsql_link == NULL && id == -1) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (PQ_SETNONBLOCKING(pgsql, 1)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
+ RETURN_FALSE;
+ }
+ while ((res = PQgetResult(pgsql))) {
+ PQclear(res);
+ leftover = 1;
+ }
+ if (leftover) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "There are results on this connection. Call pg_get_result() until it returns FALSE");
+ }
+
+ zend_hash_internal_pointer_reset(Z_ARRVAL_P(pv_param_arr));
+ num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
+ if (num_params > 0) {
+ int i = 0;
+ params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
+
+ for(i = 0; i < num_params; i++) {
+ if (zend_hash_get_current_data(Z_ARRVAL_P(pv_param_arr), (void **) &tmp) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error getting parameter");
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+
+ if (Z_TYPE_PP(tmp) == IS_NULL) {
+ params[i] = NULL;
+ } else {
+ zval tmp_val = **tmp;
+ zval_copy_ctor(&tmp_val);
+ convert_to_string(&tmp_val);
+ if (Z_TYPE(tmp_val) != IS_STRING) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error converting parameter");
+ zval_dtor(&tmp_val);
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+ params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
+ zval_dtor(&tmp_val);
+ }
+
+ zend_hash_move_forward(Z_ARRVAL_P(pv_param_arr));
+ }
+ }
+
+ if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
+ if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
+ PQreset(pgsql);
+ }
+ if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
+ _php_pgsql_free_params(params, num_params);
+ RETURN_FALSE;
+ }
+ }
+ _php_pgsql_free_params(params, num_params);
+ if (PQ_SETNONBLOCKING(pgsql, 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+#endif
+
+/* {{{ proto resource pg_get_result(resource connection)
+ Get asynchronous query result */
+PHP_FUNCTION(pg_get_result)
+{
+ zval *pgsql_link;
+ int id = -1;
+ PGconn *pgsql;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r", &pgsql_link) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ pgsql_result = PQgetResult(pgsql);
+ if (!pgsql_result) {
+ /* no result */
+ RETURN_FALSE;
+ }
+ pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
+ pg_result->conn = pgsql;
+ pg_result->result = pgsql_result;
+ pg_result->row = 0;
+ ZEND_REGISTER_RESOURCE(return_value, pg_result, le_result);
+}
+/* }}} */
+
+/* {{{ proto mixed pg_result_status(resource result[, long result_type])
+ Get status of query result */
+PHP_FUNCTION(pg_result_status)
+{
+ zval *result;
+ long result_type = PGSQL_STATUS_LONG;
+ ExecStatusType status;
+ PGresult *pgsql_result;
+ pgsql_result_handle *pg_result;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r|l",
+ &result, &result_type) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE(pg_result, pgsql_result_handle *, &result, -1, "PostgreSQL result", le_result);
+
+ pgsql_result = pg_result->result;
+ if (result_type == PGSQL_STATUS_LONG) {
+ status = PQresultStatus(pgsql_result);
+ RETURN_LONG((int)status);
+ }
+ else if (result_type == PGSQL_STATUS_STRING) {
+ RETURN_STRING(PQcmdStatus(pgsql_result), 1);
+ }
+ else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Optional 2nd parameter should be PGSQL_STATUS_LONG or PGSQL_STATUS_STRING");
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+
+/* {{{ proto array pg_get_notify([resource connection[, result_type]])
+ Get asynchronous notification */
+PHP_FUNCTION(pg_get_notify)
+{
+ zval *pgsql_link;
+ int id = -1;
+ long result_type = PGSQL_ASSOC;
+ PGconn *pgsql;
+ PGnotify *pgsql_notify;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r|l",
+ &pgsql_link, &result_type) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (!(result_type & PGSQL_BOTH)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid result type");
+ RETURN_FALSE;
+ }
+
+ PQconsumeInput(pgsql);
+ pgsql_notify = PQnotifies(pgsql);
+ if (!pgsql_notify) {
+ /* no notify message */
+ RETURN_FALSE;
+ }
+ array_init(return_value);
+ if (result_type & PGSQL_NUM) {
+ add_index_string(return_value, 0, pgsql_notify->relname, 1);
+ add_index_long(return_value, 1, pgsql_notify->be_pid);
+#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
+ if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
+#else
+ if (atof(PG_VERSION) >= 9.0) {
+#endif
+#if HAVE_PQPARAMETERSTATUS
+ add_index_string(return_value, 2, pgsql_notify->extra, 1);
+#endif
+ }
+ }
+ if (result_type & PGSQL_ASSOC) {
+ add_assoc_string(return_value, "message", pgsql_notify->relname, 1);
+ add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
+#if HAVE_PQPROTOCOLVERSION && HAVE_PQPARAMETERSTATUS
+ if (PQprotocolVersion(pgsql) >= 3 && atof(PQparameterStatus(pgsql, "server_version")) >= 9.0) {
+#else
+ if (atof(PG_VERSION) >= 9.0) {
+#endif
+#if HAVE_PQPARAMETERSTATUS
+ add_assoc_string(return_value, "payload", pgsql_notify->extra, 1);
+#endif
+ }
+ }
+ PQfreemem(pgsql_notify);
+}
+/* }}} */
+
+/* {{{ proto int pg_get_pid([resource connection)
+ Get backend(server) pid */
+PHP_FUNCTION(pg_get_pid)
+{
+ zval *pgsql_link;
+ int id = -1;
+ PGconn *pgsql;
+
+ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "r",
+ &pgsql_link) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ RETURN_LONG(PQbackendPID(pgsql));
+}
+/* }}} */
+
+/* {{{ php_pgsql_meta_data
+ * TODO: Add meta_data cache for better performance
+ */
+PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta TSRMLS_DC)
+{
+ PGresult *pg_result;
+ char *src, *tmp_name, *tmp_name2 = NULL;
+ smart_str querystr = {0};
+ int new_len;
+ int i, num_rows;
+ zval *elem;
+
+ if (!*table_name) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The table name must be specified");
+ return FAILURE;
+ }
+
+ src = estrdup(table_name);
+ tmp_name = php_strtok_r(src, ".", &tmp_name2);
+
+ if (!tmp_name2 || !*tmp_name2) {
+ /* Default schema */
+ tmp_name2 = tmp_name;
+ tmp_name = "public";
+ }
+
+ smart_str_appends(&querystr,
+ "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims "
+ "FROM pg_class as c, pg_attribute a, pg_type t, pg_namespace n "
+ "WHERE a.attnum > 0 AND a.attrelid = c.oid AND c.relname = '");
+ tmp_name2 = php_addslashes(tmp_name2, strlen(tmp_name2), &new_len, 0 TSRMLS_CC);
+ smart_str_appendl(&querystr, tmp_name2, new_len);
+
+ smart_str_appends(&querystr, "' AND c.relnamespace = n.oid AND n.nspname = '");
+ tmp_name = php_addslashes(tmp_name, strlen(tmp_name), &new_len, 0 TSRMLS_CC);
+ smart_str_appendl(&querystr, tmp_name, new_len);
+
+ smart_str_appends(&querystr, "' AND a.atttypid = t.oid ORDER BY a.attnum;");
+ smart_str_0(&querystr);
+
+ efree(tmp_name2);
+ efree(tmp_name);
+ efree(src);
+
+ pg_result = PQexec(pg_link, querystr.c);
+ if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Table '%s' doesn't exists", table_name);
+ smart_str_free(&querystr);
+ PQclear(pg_result);
+ return FAILURE;
+ }
+ smart_str_free(&querystr);
+
+ for (i = 0; i < num_rows; i++) {
+ char *name;
+ MAKE_STD_ZVAL(elem);
+ array_init(elem);
+ add_assoc_long(elem, "num", atoi(PQgetvalue(pg_result,i,1)));
+ add_assoc_string(elem, "type", PQgetvalue(pg_result,i,2), 1);
+ add_assoc_long(elem, "len", atoi(PQgetvalue(pg_result,i,3)));
+ if (!strcmp(PQgetvalue(pg_result,i,4), "t")) {
+ add_assoc_bool(elem, "not null", 1);
+ }
+ else {
+ add_assoc_bool(elem, "not null", 0);
+ }
+ if (!strcmp(PQgetvalue(pg_result,i,5), "t")) {
+ add_assoc_bool(elem, "has default", 1);
+ }
+ else {
+ add_assoc_bool(elem, "has default", 0);
+ }
+ add_assoc_long(elem, "array dims", atoi(PQgetvalue(pg_result,i,6)));
+ name = PQgetvalue(pg_result,i,0);
+ add_assoc_zval(meta, name, elem);
+ }
+ PQclear(pg_result);
+
+ return SUCCESS;
+}
+
+/* }}} */
+
+
+/* {{{ proto array pg_meta_data(resource db, string table)
+ Get meta_data */
+PHP_FUNCTION(pg_meta_data)
+{
+ zval *pgsql_link;
+ char *table_name;
+ uint table_name_len;
+ PGconn *pgsql;
+ int id = -1;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",
+ &pgsql_link, &table_name, &table_name_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE2(pgsql, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ array_init(return_value);
+ if (php_pgsql_meta_data(pgsql, table_name, return_value TSRMLS_CC) == FAILURE) {
+ zval_dtor(return_value); /* destroy array */
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ php_pgsql_get_data_type
+ */
+static php_pgsql_data_type php_pgsql_get_data_type(const char *type_name, size_t len)
+{
+ /* This is stupid way to do. I'll fix it when I decied how to support
+ user defined types. (Yasuo) */
+
+ /* boolean */
+ if (!strcmp(type_name, "bool")|| !strcmp(type_name, "boolean"))
+ return PG_BOOL;
+ /* object id */
+ if (!strcmp(type_name, "oid"))
+ return PG_OID;
+ /* integer */
+ if (!strcmp(type_name, "int2") || !strcmp(type_name, "smallint"))
+ return PG_INT2;
+ if (!strcmp(type_name, "int4") || !strcmp(type_name, "integer"))
+ return PG_INT4;
+ if (!strcmp(type_name, "int8") || !strcmp(type_name, "bigint"))
+ return PG_INT8;
+ /* real and other */
+ if (!strcmp(type_name, "float4") || !strcmp(type_name, "real"))
+ return PG_FLOAT4;
+ if (!strcmp(type_name, "float8") || !strcmp(type_name, "double precision"))
+ return PG_FLOAT8;
+ if (!strcmp(type_name, "numeric"))
+ return PG_NUMERIC;
+ if (!strcmp(type_name, "money"))
+ return PG_MONEY;
+ /* character */
+ if (!strcmp(type_name, "text"))
+ return PG_TEXT;
+ if (!strcmp(type_name, "bpchar") || !strcmp(type_name, "character"))
+ return PG_CHAR;
+ if (!strcmp(type_name, "varchar") || !strcmp(type_name, "character varying"))
+ return PG_VARCHAR;
+ /* time and interval */
+ if (!strcmp(type_name, "abstime"))
+ return PG_UNIX_TIME;
+ if (!strcmp(type_name, "reltime"))
+ return PG_UNIX_TIME_INTERVAL;
+ if (!strcmp(type_name, "tinterval"))
+ return PG_UNIX_TIME_INTERVAL;
+ if (!strcmp(type_name, "date"))
+ return PG_DATE;
+ if (!strcmp(type_name, "time"))
+ return PG_TIME;
+ if (!strcmp(type_name, "time with time zone") || !strcmp(type_name, "timetz"))
+ return PG_TIME_WITH_TIMEZONE;
+ if (!strcmp(type_name, "timestamp without time zone") || !strcmp(type_name, "timestamp"))
+ return PG_TIMESTAMP;
+ if (!strcmp(type_name, "timestamp with time zone") || !strcmp(type_name, "timestamptz"))
+ return PG_TIMESTAMP_WITH_TIMEZONE;
+ if (!strcmp(type_name, "interval"))
+ return PG_INTERVAL;
+ /* binary */
+ if (!strcmp(type_name, "bytea"))
+ return PG_BYTEA;
+ /* network */
+ if (!strcmp(type_name, "cidr"))
+ return PG_CIDR;
+ if (!strcmp(type_name, "inet"))
+ return PG_INET;
+ if (!strcmp(type_name, "macaddr"))
+ return PG_MACADDR;
+ /* bit */
+ if (!strcmp(type_name, "bit"))
+ return PG_BIT;
+ if (!strcmp(type_name, "bit varying"))
+ return PG_VARBIT;
+ /* geometric */
+ if (!strcmp(type_name, "line"))
+ return PG_LINE;
+ if (!strcmp(type_name, "lseg"))
+ return PG_LSEG;
+ if (!strcmp(type_name, "box"))
+ return PG_BOX;
+ if (!strcmp(type_name, "path"))
+ return PG_PATH;
+ if (!strcmp(type_name, "point"))
+ return PG_POINT;
+ if (!strcmp(type_name, "polygon"))
+ return PG_POLYGON;
+ if (!strcmp(type_name, "circle"))
+ return PG_CIRCLE;
+
+ return PG_UNKNOWN;
+}
+/* }}} */
+
+/* {{{ php_pgsql_convert_match
+ * test field value with regular expression specified.
+ */
+static int php_pgsql_convert_match(const char *str, const char *regex , int icase TSRMLS_DC)
+{
+ regex_t re;
+ regmatch_t *subs;
+ int regopt = REG_EXTENDED;
+ int regerr, ret = SUCCESS;
+
+ if (icase) {
+ regopt |= REG_ICASE;
+ }
+
+ regerr = regcomp(&re, regex, regopt);
+ if (regerr) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot compile regex");
+ regfree(&re);
+ return FAILURE;
+ }
+ subs = (regmatch_t *)ecalloc(sizeof(regmatch_t), re.re_nsub+1);
+
+ regerr = regexec(&re, str, re.re_nsub+1, subs, 0);
+ if (regerr == REG_NOMATCH) {
+#ifdef PHP_DEBUG
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "'%s' does not match with '%s'", str, regex);
+#endif
+ ret = FAILURE;
+ }
+ else if (regerr) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot exec regex");
+ ret = FAILURE;
+ }
+ regfree(&re);
+ efree(subs);
+ return ret;
+}
+
+/* }}} */
+
+/* {{{ php_pgsql_add_quote
+ * add quotes around string.
+ */
+static int php_pgsql_add_quotes(zval *src, zend_bool should_free TSRMLS_DC)
+{
+ smart_str str = {0};
+
+ assert(Z_TYPE_P(src) == IS_STRING);
+ assert(should_free == 1 || should_free == 0);
+
+ smart_str_appendc(&str, '\'');
+ smart_str_appendl(&str, Z_STRVAL_P(src), Z_STRLEN_P(src));
+ smart_str_appendc(&str, '\'');
+ smart_str_0(&str);
+
+ if (should_free) {
+ efree(Z_STRVAL_P(src));
+ }
+ Z_STRVAL_P(src) = str.c;
+ Z_STRLEN_P(src) = str.len;
+
+ return SUCCESS;
+}
+/* }}} */
+
+#define PGSQL_CONV_CHECK_IGNORE() \
+ if (!err && Z_TYPE_P(new_val) == IS_STRING && !strcmp(Z_STRVAL_P(new_val), "NULL")) { \
+ /* if new_value is string "NULL" and field has default value, remove element to use default value */ \
+ if (!(opt & PGSQL_CONV_IGNORE_DEFAULT) && Z_BVAL_PP(has_default)) { \
+ zval_dtor(new_val); \
+ FREE_ZVAL(new_val); \
+ skip_field = 1; \
+ } \
+ /* raise error if it's not null and cannot be ignored */ \
+ else if (!(opt & PGSQL_CONV_IGNORE_NOT_NULL) && Z_BVAL_PP(not_null)) { \
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected NULL for 'NOT NULL' field '%s'", field ); \
+ err = 1; \
+ } \
+ }
+
+/* {{{ php_pgsql_convert
+ * check and convert array values (fieldname=>vlaue pair) for sql
+ */
+PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, const zval *values, zval *result, ulong opt TSRMLS_DC)
+{
+ HashPosition pos;
+ char *field = NULL;
+ uint field_len = -1;
+ ulong num_idx = -1;
+ zval *meta, **def, **type, **not_null, **has_default, **val, *new_val;
+ int new_len, key_type, err = 0, skip_field;
+
+ assert(pg_link != NULL);
+ assert(Z_TYPE_P(values) == IS_ARRAY);
+ assert(Z_TYPE_P(result) == IS_ARRAY);
+ assert(!(opt & ~PGSQL_CONV_OPTS));
+
+ if (!table_name) {
+ return FAILURE;
+ }
+ MAKE_STD_ZVAL(meta);
+ array_init(meta);
+ if (php_pgsql_meta_data(pg_link, table_name, meta TSRMLS_CC) == FAILURE) {
+ zval_dtor(meta);
+ FREE_ZVAL(meta);
+ return FAILURE;
+ }
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos);
+ zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&val, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos)) {
+ skip_field = 0;
+ new_val = NULL;
+
+ if ((key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &field, &field_len, &num_idx, 0, &pos)) == HASH_KEY_NON_EXISTANT) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get array key type");
+ err = 1;
+ }
+ if (!err && key_type == HASH_KEY_IS_LONG) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Accepts only string key for values");
+ err = 1;
+ }
+ if (!err && key_type == HASH_KEY_NON_EXISTANT) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Accepts only string key for values");
+ err = 1;
+ }
+ if (!err && zend_hash_find(Z_ARRVAL_P(meta), field, field_len, (void **)&def) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Invalid field name (%s) in values", field);
+ err = 1;
+ }
+ if (!err && zend_hash_find(Z_ARRVAL_PP(def), "type", sizeof("type"), (void **)&type) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'type'");
+ err = 1;
+ }
+ if (!err && zend_hash_find(Z_ARRVAL_PP(def), "not null", sizeof("not null"), (void **)&not_null) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'not null'");
+ err = 1;
+ }
+ if (!err && zend_hash_find(Z_ARRVAL_PP(def), "has default", sizeof("has default"), (void **)&has_default) == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'has default'");
+ err = 1;
+ }
+ if (!err && (Z_TYPE_PP(val) == IS_ARRAY ||
+ Z_TYPE_PP(val) == IS_OBJECT ||
+ Z_TYPE_PP(val) == IS_CONSTANT_ARRAY)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects scaler values as field values");
+ err = 1;
+ }
+ if (err) {
+ break; /* break out for() */
+ }
+ ALLOC_INIT_ZVAL(new_val);
+ switch(php_pgsql_get_data_type(Z_STRVAL_PP(type), Z_STRLEN_PP(type)))
+ {
+ case PG_BOOL:
+ switch (Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ if (!strcmp(Z_STRVAL_PP(val), "t") || !strcmp(Z_STRVAL_PP(val), "T") ||
+ !strcmp(Z_STRVAL_PP(val), "y") || !strcmp(Z_STRVAL_PP(val), "Y") ||
+ !strcmp(Z_STRVAL_PP(val), "true") || !strcmp(Z_STRVAL_PP(val), "True") ||
+ !strcmp(Z_STRVAL_PP(val), "yes") || !strcmp(Z_STRVAL_PP(val), "Yes") ||
+ !strcmp(Z_STRVAL_PP(val), "1")) {
+ ZVAL_STRING(new_val, "'t'", 1);
+ }
+ else if (!strcmp(Z_STRVAL_PP(val), "f") || !strcmp(Z_STRVAL_PP(val), "F") ||
+ !strcmp(Z_STRVAL_PP(val), "n") || !strcmp(Z_STRVAL_PP(val), "N") ||
+ !strcmp(Z_STRVAL_PP(val), "false") || !strcmp(Z_STRVAL_PP(val), "False") ||
+ !strcmp(Z_STRVAL_PP(val), "no") || !strcmp(Z_STRVAL_PP(val), "No") ||
+ !strcmp(Z_STRVAL_PP(val), "0")) {
+ ZVAL_STRING(new_val, "'f'", 1);
+ }
+ else {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected invalid value (%s) for PostgreSQL %s field (%s)", Z_STRVAL_PP(val), Z_STRVAL_PP(type), field);
+ err = 1;
+ }
+ }
+ break;
+
+ case IS_LONG:
+ case IS_BOOL:
+ if (Z_LVAL_PP(val)) {
+ ZVAL_STRING(new_val, "'t'", 1);
+ }
+ else {
+ ZVAL_STRING(new_val, "'f'", 1);
+ }
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects string, null, long or boolelan value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_OID:
+ case PG_INT2:
+ case PG_INT4:
+ case PG_INT8:
+ switch (Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ /* FIXME: better regex must be used */
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^([+-]{0,1}[0-9]+)$", 0 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ }
+ else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ }
+ }
+ break;
+
+ case IS_DOUBLE:
+ ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
+ convert_to_long_ex(&new_val);
+ break;
+
+ case IS_LONG:
+ ZVAL_LONG(new_val, Z_LVAL_PP(val));
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for pgsql '%s' (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_NUMERIC:
+ case PG_MONEY:
+ case PG_FLOAT4:
+ case PG_FLOAT8:
+ switch (Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ /* FIXME: better regex must be used */
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^([+-]{0,1}[0-9]+)|([+-]{0,1}[0-9]*[\\.][0-9]+)|([+-]{0,1}[0-9]+[\\.][0-9]*)$", 0 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ }
+ else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ }
+ }
+ break;
+
+ case IS_LONG:
+ ZVAL_LONG(new_val, Z_LVAL_PP(val));
+ break;
+
+ case IS_DOUBLE:
+ ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_TEXT:
+ case PG_CHAR:
+ case PG_VARCHAR:
+ switch (Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ if (opt & PGSQL_CONV_FORCE_NULL) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ } else {
+ ZVAL_STRING(new_val, "''", 1);
+ }
+ }
+ else {
+ Z_TYPE_P(new_val) = IS_STRING;
+#if HAVE_PQESCAPE
+ {
+ char *tmp;
+ tmp = (char *)safe_emalloc(Z_STRLEN_PP(val), 2, 1);
+ Z_STRLEN_P(new_val) = (int)PQescapeString(tmp, Z_STRVAL_PP(val), Z_STRLEN_PP(val));
+ Z_STRVAL_P(new_val) = tmp;
+ }
+#else
+ Z_STRVAL_P(new_val) = php_addslashes(Z_STRVAL_PP(val), Z_STRLEN_PP(val), &Z_STRLEN_P(new_val), 0 TSRMLS_CC);
+#endif
+ php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
+ }
+ break;
+
+ case IS_LONG:
+ ZVAL_LONG(new_val, Z_LVAL_PP(val));
+ convert_to_string_ex(&new_val);
+ break;
+
+ case IS_DOUBLE:
+ ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
+ convert_to_string_ex(&new_val);
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_UNIX_TIME:
+ case PG_UNIX_TIME_INTERVAL:
+ /* these are the actallay a integer */
+ switch (Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ /* FIXME: Better regex must be used */
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^[0-9]+$", 0 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ }
+ else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ convert_to_long_ex(&new_val);
+ }
+ }
+ break;
+
+ case IS_DOUBLE:
+ ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
+ convert_to_long_ex(&new_val);
+ break;
+
+ case IS_LONG:
+ ZVAL_LONG(new_val, Z_LVAL_PP(val));
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for '%s' (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_CIDR:
+ case PG_INET:
+ switch (Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ /* FIXME: Better regex must be used */
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/[0-9]{1,2}){0,1}$", 0 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ }
+ else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
+ }
+ }
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for '%s' (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_TIME_WITH_TIMEZONE:
+ case PG_TIMESTAMP:
+ case PG_TIMESTAMP_WITH_TIMEZONE:
+ switch(Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1);
+ } else if (!strcasecmp(Z_STRVAL_PP(val), "now()")) {
+ ZVAL_STRINGL(new_val, "NOW()", sizeof("NOW()")-1, 1);
+ } else {
+ /* FIXME: better regex must be used */
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})([ \\t]+(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}(\\.[0-9]+){0,1}([ \\t]*([+-][0-9]{1,4}(:[0-9]{1,2}){0,1}|[-a-zA-Z_/+]{1,50})){0,1})){0,1}$", 1 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ } else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
+ }
+ }
+ break;
+
+ case IS_NULL:
+ ZVAL_STRINGL(new_val, "NULL", sizeof("NULL")-1, 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_DATE:
+ switch(Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ /* FIXME: better regex must be used */
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$", 1 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ }
+ else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
+ }
+ }
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_TIME:
+ switch(Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ /* FIXME: better regex must be used */
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1})){0,1}$", 1 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ }
+ else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
+ }
+ }
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ case PG_INTERVAL:
+ switch(Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+
+ /* From the Postgres docs:
+
+ interval values can be written with the following syntax:
+ [@] quantity unit [quantity unit...] [direction]
+
+ Where: quantity is a number (possibly signed); unit is second, minute, hour,
+ day, week, month, year, decade, century, millennium, or abbreviations or
+ plurals of these units [note not *all* abbreviations] ; direction can be
+ ago or empty. The at sign (@) is optional noise.
+
+ ...
+
+ Quantities of days, hours, minutes, and seconds can be specified without explicit
+ unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10
+ sec'.
+ */
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val),
+ "^(@?[ \\t]+)?("
+
+ /* Textual time units and their abbreviations: */
+ "(([-+]?[ \\t]+)?"
+ "[0-9]+(\\.[0-9]*)?[ \\t]*"
+ "(millenniums|millennia|millennium|mil|mils|"
+ "centuries|century|cent|c|"
+ "decades|decade|dec|decs|"
+ "years|year|y|"
+ "months|month|mon|"
+ "weeks|week|w|"
+ "days|day|d|"
+ "hours|hour|hr|hrs|h|"
+ "minutes|minute|mins|min|m|"
+ "seconds|second|secs|sec|s))+|"
+
+ /* Textual time units plus (dd)* hh[:mm[:ss]] */
+ "((([-+]?[ \\t]+)?"
+ "[0-9]+(\\.[0-9]*)?[ \\t]*"
+ "(millenniums|millennia|millennium|mil|mils|"
+ "centuries|century|cent|c|"
+ "decades|decade|dec|decs|"
+ "years|year|y|"
+ "months|month|mon|"
+ "weeks|week|w|"
+ "days|day|d))+"
+ "([-+]?[ \\t]+"
+ "([0-9]+[ \\t]+)+" /* dd */
+ "(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */
+ ")?))"
+ "([ \\t]+ago)?$",
+ 1 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ }
+ else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
+ }
+ }
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+#ifdef HAVE_PQESCAPE
+ case PG_BYTEA:
+ switch (Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ unsigned char *tmp;
+ size_t to_len;
+#ifdef HAVE_PQESCAPE_BYTEA_CONN
+ tmp = PQescapeByteaConn(pg_link, Z_STRVAL_PP(val), Z_STRLEN_PP(val), &to_len);
+#else
+ tmp = PQescapeBytea(Z_STRVAL_PP(val), Z_STRLEN_PP(val), &to_len);
+#endif
+ Z_TYPE_P(new_val) = IS_STRING;
+ Z_STRLEN_P(new_val) = to_len-1; /* PQescapeBytea's to_len includes additional '\0' */
+ Z_STRVAL_P(new_val) = emalloc(to_len);
+ memcpy(Z_STRVAL_P(new_val), tmp, to_len);
+ PQfreemem(tmp);
+ php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
+
+ }
+ break;
+
+ case IS_LONG:
+ ZVAL_LONG(new_val, Z_LVAL_PP(val));
+ convert_to_string_ex(&new_val);
+ break;
+
+ case IS_DOUBLE:
+ ZVAL_DOUBLE(new_val, Z_DVAL_PP(val));
+ convert_to_string_ex(&new_val);
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+#endif
+ case PG_MACADDR:
+ switch(Z_TYPE_PP(val)) {
+ case IS_STRING:
+ if (Z_STRLEN_PP(val) == 0) {
+ ZVAL_STRING(new_val, "NULL", 1);
+ }
+ else {
+ if (php_pgsql_convert_match(Z_STRVAL_PP(val), "^([0-9a-f]{2,2}:){5,5}[0-9a-f]{2,2}$", 1 TSRMLS_CC) == FAILURE) {
+ err = 1;
+ }
+ else {
+ ZVAL_STRING(new_val, Z_STRVAL_PP(val), 1);
+ php_pgsql_add_quotes(new_val, 1 TSRMLS_CC);
+ }
+ }
+ break;
+
+ case IS_NULL:
+ ZVAL_STRING(new_val, "NULL", 1);
+ break;
+
+ default:
+ err = 1;
+ }
+ PGSQL_CONV_CHECK_IGNORE();
+ if (err) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects NULL or string for PostgreSQL %s field (%s)", Z_STRVAL_PP(type), field);
+ }
+ break;
+
+ /* bit */
+ case PG_BIT:
+ case PG_VARBIT:
+ /* geometric */
+ case PG_LINE:
+ case PG_LSEG:
+ case PG_POINT:
+ case PG_BOX:
+ case PG_PATH:
+ case PG_POLYGON:
+ case PG_CIRCLE:
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "PostgreSQL '%s' type (%s) is not supported", Z_STRVAL_PP(type), field);
+ err = 1;
+ break;
+
+ case PG_UNKNOWN:
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unknown or system data type '%s' for '%s'", Z_STRVAL_PP(type), field);
+ err = 1;
+ break;
+ } /* switch */
+
+ if (err) {
+ zval_dtor(new_val);
+ FREE_ZVAL(new_val);
+ break; /* break out for() */
+ }
+ if (!skip_field) {
+ /* If field is NULL and HAS DEFAULT, should be skipped */
+ field = php_addslashes(field, strlen(field), &new_len, 0 TSRMLS_CC);
+ add_assoc_zval(result, field, new_val);
+ efree(field);
+ }
+ } /* for */
+ zval_dtor(meta);
+ FREE_ZVAL(meta);
+
+ if (err) {
+ /* shouldn't destroy & free zval here */
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+
+/* {{{ proto array pg_convert(resource db, string table, array values[, int options])
+ Check and convert values for PostgreSQL SQL statement */
+PHP_FUNCTION(pg_convert)
+{
+ zval *pgsql_link, *values;
+ char *table_name;
+ int table_name_len;
+ ulong option = 0;
+ PGconn *pg_link;
+ int id = -1;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "rsa|l", &pgsql_link, &table_name, &table_name_len, &values, &option) == FAILURE) {
+ return;
+ }
+ if (option & ~PGSQL_CONV_OPTS) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified");
+ RETURN_FALSE;
+ }
+ if (!table_name_len) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Table name is invalid");
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pg_link, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (php_pgsql_flush_query(pg_link TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected unhandled result(s) in connection");
+ }
+ array_init(return_value);
+ if (php_pgsql_convert(pg_link, table_name, values, return_value, option TSRMLS_CC) == FAILURE) {
+ zval_dtor(return_value);
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+static int do_exec(smart_str *querystr, int expect, PGconn *pg_link, ulong opt TSRMLS_DC)
+{
+ if (opt & PGSQL_DML_ASYNC) {
+ if (PQsendQuery(pg_link, querystr->c)) {
+ return 0;
+ }
+ }
+ else {
+ PGresult *pg_result;
+
+ pg_result = PQexec(pg_link, querystr->c);
+ if (PQresultStatus(pg_result) == expect) {
+ PQclear(pg_result);
+ return 0;
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", PQresultErrorMessage(pg_result));
+ PQclear(pg_result);
+ }
+ }
+
+ return -1;
+}
+
+/* {{{ php_pgsql_insert
+ */
+PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var_array, ulong opt, char **sql TSRMLS_DC)
+{
+ zval **val, *converted = NULL;
+ char buf[256];
+ char *fld;
+ smart_str querystr = {0};
+ int key_type, ret = FAILURE;
+ uint fld_len;
+ ulong num_idx;
+ HashPosition pos;
+
+ assert(pg_link != NULL);
+ assert(table != NULL);
+ assert(Z_TYPE_P(var_array) == IS_ARRAY);
+
+ if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) {
+ smart_str_appends(&querystr, "INSERT INTO ");
+ smart_str_appends(&querystr, table);
+ smart_str_appends(&querystr, " DEFAULT VALUES");
+
+ goto no_values;
+ }
+
+ /* convert input array if needed */
+ if (!(opt & PGSQL_DML_NO_CONV)) {
+ MAKE_STD_ZVAL(converted);
+ array_init(converted);
+ if (php_pgsql_convert(pg_link, table, var_array, converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) {
+ goto cleanup;
+ }
+ var_array = converted;
+ }
+
+ smart_str_appends(&querystr, "INSERT INTO ");
+ smart_str_appends(&querystr, table);
+ smart_str_appends(&querystr, " (");
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(var_array), &pos);
+ while ((key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(var_array), &fld,
+ &fld_len, &num_idx, 0, &pos)) != HASH_KEY_NON_EXISTANT) {
+ if (key_type == HASH_KEY_IS_LONG) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects associative array for values to be inserted");
+ goto cleanup;
+ }
+ smart_str_appendl(&querystr, fld, fld_len - 1);
+ smart_str_appendc(&querystr, ',');
+ zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos);
+ }
+ querystr.len--;
+ smart_str_appends(&querystr, ") VALUES (");
+
+ /* make values string */
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(var_array), &pos);
+ zend_hash_get_current_data_ex(Z_ARRVAL_P(var_array), (void **)&val, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos)) {
+
+ /* we can avoid the key_type check here, because we tested it in the other loop */
+ switch(Z_TYPE_PP(val)) {
+ case IS_STRING:
+ smart_str_appendl(&querystr, Z_STRVAL_PP(val), Z_STRLEN_PP(val));
+ break;
+ case IS_LONG:
+ smart_str_append_long(&querystr, Z_LVAL_PP(val));
+ break;
+ case IS_DOUBLE:
+ smart_str_appendl(&querystr, buf, snprintf(buf, sizeof(buf), "%F", Z_DVAL_PP(val)));
+ break;
+ default:
+ /* should not happen */
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Report this error to php-dev@lists.php.net, type = %d", Z_TYPE_PP(val));
+ goto cleanup;
+ break;
+ }
+ smart_str_appendc(&querystr, ',');
+ }
+ /* Remove the trailing "," */
+ querystr.len--;
+ smart_str_appends(&querystr, ");");
+
+no_values:
+
+ smart_str_0(&querystr);
+
+ if ((opt & (PGSQL_DML_EXEC|PGSQL_DML_ASYNC)) &&
+ do_exec(&querystr, PGRES_COMMAND_OK, pg_link, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == 0) {
+ ret = SUCCESS;
+ }
+ else if (opt & PGSQL_DML_STRING) {
+ ret = SUCCESS;
+ }
+
+cleanup:
+ if (!(opt & PGSQL_DML_NO_CONV) && converted) {
+ zval_dtor(converted);
+ FREE_ZVAL(converted);
+ }
+ if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
+ *sql = querystr.c;
+ }
+ else {
+ smart_str_free(&querystr);
+ }
+ return ret;
+}
+/* }}} */
+
+/* {{{ proto mixed pg_insert(resource db, string table, array values[, int options])
+ Insert values (filed=>value) to table */
+PHP_FUNCTION(pg_insert)
+{
+ zval *pgsql_link, *values;
+ char *table, *sql = NULL;
+ int table_len;
+ ulong option = PGSQL_DML_EXEC;
+ PGconn *pg_link;
+ int id = -1, argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "rsa|l",
+ &pgsql_link, &table, &table_len, &values, &option) == FAILURE) {
+ return;
+ }
+ if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified");
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pg_link, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (php_pgsql_flush_query(pg_link TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected unhandled result(s) in connection");
+ }
+ if (php_pgsql_insert(pg_link, table, values, option, &sql TSRMLS_CC) == FAILURE) {
+ RETURN_FALSE;
+ }
+ if (option & PGSQL_DML_STRING) {
+ RETURN_STRING(sql, 0);
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+static inline int build_assignment_string(smart_str *querystr, HashTable *ht, int where_cond, const char *pad, int pad_len TSRMLS_DC)
+{
+ HashPosition pos;
+ uint fld_len;
+ int key_type;
+ ulong num_idx;
+ char *fld;
+ char buf[256];
+ zval **val;
+
+ for (zend_hash_internal_pointer_reset_ex(ht, &pos);
+ zend_hash_get_current_data_ex(ht, (void **)&val, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(ht, &pos)) {
+ key_type = zend_hash_get_current_key_ex(ht, &fld, &fld_len, &num_idx, 0, &pos);
+ if (key_type == HASH_KEY_IS_LONG) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects associative array for values to be inserted");
+ return -1;
+ }
+ smart_str_appendl(querystr, fld, fld_len - 1);
+ if (where_cond && Z_TYPE_PP(val) == IS_STRING && !strcmp(Z_STRVAL_PP(val), "NULL")) {
+ smart_str_appends(querystr, " IS ");
+ } else {
+ smart_str_appendc(querystr, '=');
+ }
+
+ switch(Z_TYPE_PP(val)) {
+ case IS_STRING:
+ smart_str_appendl(querystr, Z_STRVAL_PP(val), Z_STRLEN_PP(val));
+ break;
+ case IS_LONG:
+ smart_str_append_long(querystr, Z_LVAL_PP(val));
+ break;
+ case IS_DOUBLE:
+ smart_str_appendl(querystr, buf, MIN(snprintf(buf, sizeof(buf), "%F", Z_DVAL_PP(val)), sizeof(buf)-1));
+ break;
+ default:
+ /* should not happen */
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects scaler values other than NULL. Need to convert?");
+ return -1;
+ }
+ smart_str_appendl(querystr, pad, pad_len);
+ }
+ querystr->len -= pad_len;
+
+ return 0;
+}
+
+/* {{{ php_pgsql_update
+ */
+PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *var_array, zval *ids_array, ulong opt, char **sql TSRMLS_DC)
+{
+ zval *var_converted = NULL, *ids_converted = NULL;
+ smart_str querystr = {0};
+ int ret = FAILURE;
+
+ assert(pg_link != NULL);
+ assert(table != NULL);
+ assert(Z_TYPE_P(var_array) == IS_ARRAY);
+ assert(Z_TYPE_P(ids_array) == IS_ARRAY);
+ assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING)));
+
+ if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0
+ || zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
+ return FAILURE;
+ }
+
+ if (!(opt & PGSQL_DML_NO_CONV)) {
+ MAKE_STD_ZVAL(var_converted);
+ array_init(var_converted);
+ if (php_pgsql_convert(pg_link, table, var_array, var_converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) {
+ goto cleanup;
+ }
+ var_array = var_converted;
+ MAKE_STD_ZVAL(ids_converted);
+ array_init(ids_converted);
+ if (php_pgsql_convert(pg_link, table, ids_array, ids_converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) {
+ goto cleanup;
+ }
+ ids_array = ids_converted;
+ }
+
+ smart_str_appends(&querystr, "UPDATE ");
+ smart_str_appends(&querystr, table);
+ smart_str_appends(&querystr, " SET ");
+
+ if (build_assignment_string(&querystr, Z_ARRVAL_P(var_array), 0, ",", 1 TSRMLS_CC))
+ goto cleanup;
+
+ smart_str_appends(&querystr, " WHERE ");
+
+ if (build_assignment_string(&querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1 TSRMLS_CC))
+ goto cleanup;
+
+ smart_str_appendc(&querystr, ';');
+ smart_str_0(&querystr);
+
+ if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt TSRMLS_CC) == 0) {
+ ret = SUCCESS;
+ } else if (opt & PGSQL_DML_STRING) {
+ ret = SUCCESS;
+ }
+
+cleanup:
+ if (var_converted) {
+ zval_dtor(var_converted);
+ FREE_ZVAL(var_converted);
+ }
+ if (ids_converted) {
+ zval_dtor(ids_converted);
+ FREE_ZVAL(ids_converted);
+ }
+ if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
+ *sql = querystr.c;
+ }
+ else {
+ smart_str_free(&querystr);
+ }
+ return ret;
+}
+/* }}} */
+
+/* {{{ proto mixed pg_update(resource db, string table, array fields, array ids[, int options])
+ Update table using values (field=>value) and ids (id=>value) */
+PHP_FUNCTION(pg_update)
+{
+ zval *pgsql_link, *values, *ids;
+ char *table, *sql = NULL;
+ int table_len;
+ ulong option = PGSQL_DML_EXEC;
+ PGconn *pg_link;
+ int id = -1, argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "rsaa|l",
+ &pgsql_link, &table, &table_len, &values, &ids, &option) == FAILURE) {
+ return;
+ }
+ if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified");
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pg_link, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (php_pgsql_flush_query(pg_link TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected unhandled result(s) in connection");
+ }
+ if (php_pgsql_update(pg_link, table, values, ids, option, &sql TSRMLS_CC) == FAILURE) {
+ RETURN_FALSE;
+ }
+ if (option & PGSQL_DML_STRING) {
+ RETURN_STRING(sql, 0);
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ php_pgsql_delete
+ */
+PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids_array, ulong opt, char **sql TSRMLS_DC)
+{
+ zval *ids_converted = NULL;
+ smart_str querystr = {0};
+ int ret = FAILURE;
+
+ assert(pg_link != NULL);
+ assert(table != NULL);
+ assert(Z_TYPE_P(ids_array) == IS_ARRAY);
+ assert(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING)));
+
+ if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
+ return FAILURE;
+ }
+
+ if (!(opt & PGSQL_DML_NO_CONV)) {
+ MAKE_STD_ZVAL(ids_converted);
+ array_init(ids_converted);
+ if (php_pgsql_convert(pg_link, table, ids_array, ids_converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) {
+ goto cleanup;
+ }
+ ids_array = ids_converted;
+ }
+
+ smart_str_appends(&querystr, "DELETE FROM ");
+ smart_str_appends(&querystr, table);
+ smart_str_appends(&querystr, " WHERE ");
+
+ if (build_assignment_string(&querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1 TSRMLS_CC))
+ goto cleanup;
+
+ smart_str_appendc(&querystr, ';');
+ smart_str_0(&querystr);
+
+ if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt TSRMLS_CC) == 0) {
+ ret = SUCCESS;
+ } else if (opt & PGSQL_DML_STRING) {
+ ret = SUCCESS;
+ }
+
+cleanup:
+ if (!(opt & PGSQL_DML_NO_CONV)) {
+ zval_dtor(ids_converted);
+ FREE_ZVAL(ids_converted);
+ }
+ if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
+ *sql = querystr.c;
+ }
+ else {
+ smart_str_free(&querystr);
+ }
+ return ret;
+}
+/* }}} */
+
+/* {{{ proto mixed pg_delete(resource db, string table, array ids[, int options])
+ Delete records has ids (id=>value) */
+PHP_FUNCTION(pg_delete)
+{
+ zval *pgsql_link, *ids;
+ char *table, *sql = NULL;
+ int table_len;
+ ulong option = PGSQL_DML_EXEC;
+ PGconn *pg_link;
+ int id = -1, argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "rsa|l",
+ &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) {
+ return;
+ }
+ if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified");
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pg_link, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (php_pgsql_flush_query(pg_link TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected unhandled result(s) in connection");
+ }
+ if (php_pgsql_delete(pg_link, table, ids, option, &sql TSRMLS_CC) == FAILURE) {
+ RETURN_FALSE;
+ }
+ if (option & PGSQL_DML_STRING) {
+ RETURN_STRING(sql, 0);
+ }
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ php_pgsql_result2array
+ */
+PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array TSRMLS_DC)
+{
+ zval *row;
+ char *field_name;
+ size_t num_fields;
+ int pg_numrows, pg_row;
+ uint i;
+ assert(Z_TYPE_P(ret_array) == IS_ARRAY);
+
+ if ((pg_numrows = PQntuples(pg_result)) <= 0) {
+ return FAILURE;
+ }
+ for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
+ MAKE_STD_ZVAL(row);
+ array_init(row);
+ add_index_zval(ret_array, pg_row, row);
+ for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
+ if (PQgetisnull(pg_result, pg_row, i)) {
+ field_name = PQfname(pg_result, i);
+ add_assoc_null(row, field_name);
+ } else {
+ char *element = PQgetvalue(pg_result, pg_row, i);
+ if (element) {
+ char *data;
+ size_t data_len;
+ const size_t element_len = strlen(element);
+
+ data = safe_estrndup(element, element_len);
+ data_len = element_len;
+
+ field_name = PQfname(pg_result, i);
+ add_assoc_stringl(row, field_name, data, data_len, 0);
+ }
+ }
+ }
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ php_pgsql_select
+ */
+PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, ulong opt, char **sql TSRMLS_DC)
+{
+ zval *ids_converted = NULL;
+ smart_str querystr = {0};
+ int ret = FAILURE;
+ PGresult *pg_result;
+
+ assert(pg_link != NULL);
+ assert(table != NULL);
+ assert(Z_TYPE_P(ids_array) == IS_ARRAY);
+ assert(Z_TYPE_P(ret_array) == IS_ARRAY);
+ assert(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING)));
+
+ if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
+ return FAILURE;
+ }
+
+ if (!(opt & PGSQL_DML_NO_CONV)) {
+ MAKE_STD_ZVAL(ids_converted);
+ array_init(ids_converted);
+ if (php_pgsql_convert(pg_link, table, ids_array, ids_converted, (opt & PGSQL_CONV_OPTS) TSRMLS_CC) == FAILURE) {
+ goto cleanup;
+ }
+ ids_array = ids_converted;
+ }
+
+ smart_str_appends(&querystr, "SELECT * FROM ");
+ smart_str_appends(&querystr, table);
+ smart_str_appends(&querystr, " WHERE ");
+
+ if (build_assignment_string(&querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1 TSRMLS_CC))
+ goto cleanup;
+
+ smart_str_appendc(&querystr, ';');
+ smart_str_0(&querystr);
+
+ pg_result = PQexec(pg_link, querystr.c);
+ if (PQresultStatus(pg_result) == PGRES_TUPLES_OK) {
+ ret = php_pgsql_result2array(pg_result, ret_array TSRMLS_CC);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Failed to execute '%s'", querystr.c);
+ }
+ PQclear(pg_result);
+
+cleanup:
+ if (!(opt & PGSQL_DML_NO_CONV)) {
+ zval_dtor(ids_converted);
+ FREE_ZVAL(ids_converted);
+ }
+ if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
+ *sql = querystr.c;
+ }
+ else {
+ smart_str_free(&querystr);
+ }
+ return ret;
+}
+/* }}} */
+
+/* {{{ proto mixed pg_select(resource db, string table, array ids[, int options])
+ Select records that has ids (id=>value) */
+PHP_FUNCTION(pg_select)
+{
+ zval *pgsql_link, *ids;
+ char *table, *sql = NULL;
+ int table_len;
+ ulong option = PGSQL_DML_EXEC;
+ PGconn *pg_link;
+ int id = -1, argc = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "rsa|l",
+ &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) {
+ return;
+ }
+ if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid option is specified");
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE2(pg_link, PGconn *, &pgsql_link, id, "PostgreSQL link", le_link, le_plink);
+
+ if (php_pgsql_flush_query(pg_link TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected unhandled result(s) in connection");
+ }
+ array_init(return_value);
+ if (php_pgsql_select(pg_link, table, ids, return_value, option, &sql TSRMLS_CC) == FAILURE) {
+ zval_dtor(return_value);
+ RETURN_FALSE;
+ }
+ if (option & PGSQL_DML_STRING) {
+ zval_dtor(return_value);
+ RETURN_STRING(sql, 0);
+ }
+ return;
+}
+/* }}} */
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/ext/pgsql/pgsql.dsp b/ext/pgsql/pgsql.dsp
new file mode 100644
index 0000000..098469f
--- /dev/null
+++ b/ext/pgsql/pgsql.dsp
@@ -0,0 +1,167 @@
+# Microsoft Developer Studio Project File - Name="pgsql" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=pgsql - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "pgsql.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "pgsql.mak" CFG="pgsql - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "pgsql - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "pgsql - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "pgsql - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "pgsql - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "pgsql - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PGSQL_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\php_build\include\pgsql" /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "PGSQL_EXPORTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ZEND_WIN32" /D "PHP_WIN32" /D "COMPILE_DL_PGSQL" /D HAVE_PGSQL=1 /D "HAVE_PQCMDTUPLES" /D "HAVE_PQCLIENTENCODING" /D "HAVE_PQESCAPE" /D "HAVE_PG_CONFIG_H" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib libpqdll.lib php5ts.lib /nologo /dll /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../Release/pgsql.dll" /libpath:"..\..\..\PostgreSQL\lib" /libpath:"..\..\Release_TS" /libpath:"..\..\Release_TS_Inline"
+
+!ELSEIF "$(CFG)" == "pgsql - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PGSQL_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\php_build\include\pgsql" /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /D "_DEBUG" /D ZEND_DEBUG=1 /D "PGSQL_EXPORTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ZEND_WIN32" /D "PHP_WIN32" /D "COMPILE_DL_PGSQL" /D HAVE_PGSQL=1 /D "HAVE_PQCMDTUPLES" /D "HAVE_PQCLIENTENCODING" /D "HAVE_PQESCAPE" /D "HAVE_PG_CONFIG_H" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../Debug/pgsql.dll" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "pgsql - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\Zend" /I "..\..\..\PostgreSQL\include" /I "..\..\..\bindlib_w32" /D "NDEBUG" /D "ZTS" /D "PGSQL_EXPORTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ZEND_WIN32" /D "PHP_WIN32" /D "COMPILE_DL_PGSQL" /D HAVE_PGSQL=1 /D ZTS=1 /FD /c
+# SUBTRACT BASE CPP /YX
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\php_build\include\pgsql" /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /D "NDEBUG" /D "ZTS" /D ZEND_DEBUG=0 /D "PGSQL_EXPORTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ZEND_WIN32" /D "PHP_WIN32" /D "COMPILE_DL_PGSQL" /D HAVE_PGSQL=1 /D "HAVE_PQCMDTUPLES" /D "HAVE_PQCLIENTENCODING" /D "HAVE_PQESCAPE" /D "HAVE_PG_CONFIG_H" /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib libpqdll.lib php5ts.lib /nologo /dll /machine:I386 /libpath:"..\..\..\PostgreSQL\lib" /libpath:"..\..\Release_TS"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib libpq.lib php5ts.lib /nologo /dll /machine:I386 /out:"../../Release_TS/php_pgsql.dll" /libpath:"..\..\..\php_build\postgresql\src\interfaces\libpq\Release" /libpath:"..\..\Release_TS" /libpath:"..\..\Release_TS_Inline"
+
+!ELSEIF "$(CFG)" == "pgsql - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "PGSQL_EXPORTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ZEND_WIN32" /D "PHP_WIN32" /D "COMPILE_DL_PGSQL" /D HAVE_PGSQL=1 /D ZTS=1 /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\php_build\include\pgsql" /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /D "_DEBUG" /D "ZTS" /D ZEND_DEBUG=1 /D "PGSQL_EXPORTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ZEND_WIN32" /D "PHP_WIN32" /D "COMPILE_DL_PGSQL" /D HAVE_PGSQL=1 /D "HAVE_PQCMDTUPLES" /D "HAVE_PQCLIENTENCODING" /D "HAVE_PQESCAPE" /D "HAVE_PG_CONFIG_H" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libpq.lib /nologo /dll /debug /machine:I386 /out:"../../Debug_TS/pgsql.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "pgsql - Win32 Release"
+# Name "pgsql - Win32 Debug"
+# Name "pgsql - Win32 Release_TS"
+# Name "pgsql - Win32 Debug_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\pgsql.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\php_pgsql.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/ext/pgsql/pgsql.mak b/ext/pgsql/pgsql.mak
new file mode 100644
index 0000000..908056a
--- /dev/null
+++ b/ext/pgsql/pgsql.mak
@@ -0,0 +1,170 @@
+# Temporarily here -- later may go into some batch file
+# which will set this as an environment variable
+PROJECT_ROOT = ..\..
+
+# Module details
+MODULE_NAME = phppgsql
+MODULE_DESC = "PHP 4.3 - PostgreSQL Extension"
+VMAJ = 3
+VMIN = 0
+VREV = 0
+
+#include the common settings
+include $(PROJECT_ROOT)/netware/common.mif
+
+# Build type defaults to 'release'
+ifndef BUILD
+BUILD = release
+endif
+
+# Extensions of all input and output files
+.SUFFIXES:
+.SUFFIXES: .nlm .lib .obj .cpp .c .msg .mlc .mdb .xdc .d
+
+# Source files
+C_SRC = pgsql.c \
+ start.c
+
+CPP_SRC_NODIR = $(notdir $(CPP_SRC))
+C_SRC_NODIR = $(notdir $(C_SRC))
+SRC_DIR = $(dir $(CPP_SRC) $(C_SRC))
+
+# Library files
+LIBRARY =
+
+# Destination directories and files
+OBJ_DIR = $(BUILD)
+FINAL_DIR = $(BUILD)
+OBJECTS = $(addprefix $(OBJ_DIR)/,$(CPP_SRC_NODIR:.c=.obj) $(C_SRC_NODIR:.c=.obj))
+DEPDS = $(addprefix $(OBJ_DIR)/,$(CPP_SRC_NODIR:.c=.d) $(C_SRC_NODIR:.c=.d))
+
+# Binary file
+ifndef BINARY
+ BINARY=$(FINAL_DIR)\$(MODULE_NAME).nlm
+endif
+
+# Compile flags
+C_FLAGS += -c -maxerrors 25 -msgstyle gcc -wchar_t on -bool on -processor Pentium
+C_FLAGS += -nostdinc -nosyspath
+C_FLAGS += -relax_pointers # To remove type-casting errors
+C_FLAGS += -DNETWARE -DZTS -DNEW_LIBC -DUSE_OLD_FUNCTIONS
+C_FLAGS += -D__BIT_TYPES_DEFINED__ -DCOMPILE_DL_PGSQL=1
+C_FLAGS += -I. -I- -I$(PROJECT_ROOT) -I$(PROJECT_ROOT)/main
+C_FLAGS += -I$(PROJECT_ROOT)/ext/standard -I$(PROJECT_ROOT)/netware
+C_FLAGS += -I$(PROJECT_ROOT)/zend -I$(PROJECT_ROOT)/tsrm
+C_FLAGS += -I$(SDK_DIR)/include -I$(MWCIncludes)
+C_FLAGS += -I$(WINSOCK_DIR)/include/nlm -I$(WINSOCK_DIR)/include
+
+
+# Extra stuff based on debug / release builds
+ifeq '$(BUILD)' 'debug'
+ SYM_FILE = $(FINAL_DIR)\$(MODULE_NAME).sym
+ C_FLAGS += -inline smart -sym on -sym codeview4 -opt off -opt intrinsics -DDEBUGGING -DDKFBPON
+ C_FLAGS += -exc cw -DZEND_DEBUG=1
+ LD_FLAGS += -sym on -sym codeview4 -osym $(SYM_FILE)
+ export MWLibraryFiles=$(SDK_DIR)/imports/libcpre.o;mwcrtld.lib
+else
+ C_FLAGS += -opt speed -inline on -inline smart -inline auto -sym off -opt intrinsics
+ C_FLAGS += -opt level=4 -DZEND_DEBUG=0
+ LD_FLAGS += -sym off
+ export MWLibraryFiles=$(SDK_DIR)/imports/libcpre.o;mwcrtl.lib
+endif
+
+
+# Dependencies
+MODULE = LibC \
+ phplib \
+ libpq
+IMPORT = @$(SDK_DIR)/imports/libc.imp \
+ @$(SDK_DIR)/imports/ws2nlm.imp \
+ @$(MPK_DIR)/import/mpkOrg.imp \
+ @$(PROJECT_ROOT)/netware/phplib.imp \
+ @$(PROJECT_ROOT)/netware/libpq.imp
+EXPORT = ($(MODULE_NAME)) get_module
+API = OutputToScreen
+
+# Virtual paths
+vpath %.cpp .
+vpath %.c . ..\..\netware
+vpath %.obj $(OBJ_DIR)
+
+
+all: prebuild project
+
+.PHONY: all
+
+prebuild:
+ @if not exist $(OBJ_DIR) md $(OBJ_DIR)
+
+project: $(BINARY)
+ @echo Build complete.
+
+$(OBJ_DIR)/%.d: %.cpp
+ @echo Building Dependencies for $(<F)
+ @$(CC) -M $< $(C_FLAGS) -o $@
+
+$(OBJ_DIR)/%.d: %.c
+ @echo Building Dependencies for $(<F)
+ @$(CC) -M $< $(C_FLAGS) -o $@
+
+$(OBJ_DIR)/%.obj: %.cpp
+ @echo Compiling $?...
+ @$(CC) $< $(C_FLAGS) -o $@
+
+$(OBJ_DIR)/%.obj: %.c
+ @echo Compiling $?...
+ @$(CC) $< $(C_FLAGS) -o $@
+
+
+$(BINARY): $(OBJECTS)
+ @echo Import $(IMPORT) > $(basename $@).def
+ifdef API
+ @echo Import $(API) >> $(basename $@).def
+endif
+ @echo Module $(MODULE) >> $(basename $@).def
+ifdef EXPORT
+ @echo Export $(EXPORT) >> $(basename $@).def
+endif
+ @echo AutoUnload >> $(basename $@).def
+ifeq '$(BUILD)' 'debug'
+ @echo Debug >> $(basename $@).def
+endif
+ @echo Flag_On 0x00000008 >> $(basename $@).def
+ @echo Start _LibCPrelude >> $(basename $@).def
+ @echo Exit _LibCPostlude >> $(basename $@).def
+
+ $(MPKTOOL) $(XDCFLAGS) $(basename $@).xdc
+ @echo xdcdata $(basename $@).xdc >> $(basename $@).def
+
+ @echo Linking $@...
+ @echo $(LD_FLAGS) -commandfile $(basename $@).def > $(basename $@).link
+
+ @echo $(LIBRARY) $(OBJECTS) >> $(basename $@).link
+
+ @$(LINK) @$(basename $@).link
+
+
+.PHONY: clean
+clean: cleanobj cleanbin
+
+.PHONY: cleand
+cleand:
+ @echo Deleting all dependency files...
+ -@del "$(OBJ_DIR)\*.d"
+
+.PHONY: cleanobj
+cleanobj:
+ @echo Deleting all object files...
+ -@del "$(OBJ_DIR)\*.obj"
+
+.PHONY: cleanbin
+cleanbin:
+ @echo Deleting binary files...
+ -@del "$(FINAL_DIR)\$(MODULE_NAME).nlm"
+ @echo Deleting MAP, DEF files, etc....
+ -@del "$(FINAL_DIR)\$(MODULE_NAME).map"
+ -@del "$(FINAL_DIR)\$(MODULE_NAME).def"
+ -@del "$(FINAL_DIR)\$(MODULE_NAME).link"
+ifeq '$(BUILD)' 'debug'
+ -@del $(FINAL_DIR)\$(MODULE_NAME).sym
+endif
diff --git a/ext/pgsql/php_pgsql.h b/ext/pgsql/php_pgsql.h
new file mode 100644
index 0000000..63f50f0
--- /dev/null
+++ b/ext/pgsql/php_pgsql.h
@@ -0,0 +1,309 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Zeev Suraski <zeev@zend.com> |
+ | Jouni Ahto <jouni.ahto@exdec.fi> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifndef PHP_PGSQL_H
+#define PHP_PGSQL_H
+
+#if HAVE_PGSQL
+
+extern zend_module_entry pgsql_module_entry;
+#define pgsql_module_ptr &pgsql_module_entry
+
+#ifdef PHP_PGSQL_PRIVATE
+#undef SOCKET_SIZE_TYPE
+#include <libpq-fe.h>
+
+#ifdef PHP_WIN32
+#define INV_WRITE 0x00020000
+#define INV_READ 0x00040000
+#undef PHP_PGSQL_API
+#ifdef PGSQL_EXPORTS
+#define PHP_PGSQL_API __declspec(dllexport)
+#else
+#define PHP_PGSQL_API __declspec(dllimport)
+#endif
+#else
+#include <libpq/libpq-fs.h>
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define PHP_PGSQL_API __attribute__ ((visibility("default")))
+# else
+# define PHP_PGSQL_API
+# endif
+#endif
+
+#ifdef HAVE_PG_CONFIG_H
+#include <pg_config.h>
+#endif
+
+#ifdef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
+const char * pg_encoding_to_char(int encoding);
+#endif
+
+PHP_MINIT_FUNCTION(pgsql);
+PHP_MSHUTDOWN_FUNCTION(pgsql);
+PHP_RINIT_FUNCTION(pgsql);
+PHP_RSHUTDOWN_FUNCTION(pgsql);
+PHP_MINFO_FUNCTION(pgsql);
+/* connection functions */
+PHP_FUNCTION(pg_connect);
+PHP_FUNCTION(pg_pconnect);
+PHP_FUNCTION(pg_close);
+PHP_FUNCTION(pg_connection_reset);
+PHP_FUNCTION(pg_connection_status);
+PHP_FUNCTION(pg_connection_busy);
+PHP_FUNCTION(pg_host);
+PHP_FUNCTION(pg_dbname);
+PHP_FUNCTION(pg_port);
+PHP_FUNCTION(pg_tty);
+PHP_FUNCTION(pg_options);
+PHP_FUNCTION(pg_version);
+PHP_FUNCTION(pg_ping);
+#if HAVE_PQPARAMETERSTATUS
+PHP_FUNCTION(pg_parameter_status);
+#endif
+#if HAVE_PGTRANSACTIONSTATUS
+PHP_FUNCTION(pg_transaction_status);
+#endif
+/* query functions */
+PHP_FUNCTION(pg_query);
+#if HAVE_PQEXECPARAMS
+PHP_FUNCTION(pg_query_params);
+#endif
+#if HAVE_PQPREPARE
+PHP_FUNCTION(pg_prepare);
+#endif
+#if HAVE_PQEXECPREPARED
+PHP_FUNCTION(pg_execute);
+#endif
+PHP_FUNCTION(pg_send_query);
+#if HAVE_PQSENDQUERYPARAMS
+PHP_FUNCTION(pg_send_query_params);
+#endif
+#if HAVE_PQSENDPREPARE
+PHP_FUNCTION(pg_send_prepare);
+#endif
+#if HAVE_PQSENDQUERYPREPARED
+PHP_FUNCTION(pg_send_execute);
+#endif
+PHP_FUNCTION(pg_cancel_query);
+/* result functions */
+PHP_FUNCTION(pg_fetch_assoc);
+PHP_FUNCTION(pg_fetch_array);
+PHP_FUNCTION(pg_fetch_object);
+PHP_FUNCTION(pg_fetch_result);
+PHP_FUNCTION(pg_fetch_row);
+PHP_FUNCTION(pg_fetch_all);
+PHP_FUNCTION(pg_fetch_all_columns);
+#if HAVE_PQCMDTUPLES
+PHP_FUNCTION(pg_affected_rows);
+#endif
+PHP_FUNCTION(pg_get_result);
+PHP_FUNCTION(pg_result_seek);
+PHP_FUNCTION(pg_result_status);
+PHP_FUNCTION(pg_free_result);
+PHP_FUNCTION(pg_last_oid);
+PHP_FUNCTION(pg_num_rows);
+PHP_FUNCTION(pg_num_fields);
+PHP_FUNCTION(pg_field_name);
+PHP_FUNCTION(pg_field_num);
+PHP_FUNCTION(pg_field_size);
+PHP_FUNCTION(pg_field_type);
+PHP_FUNCTION(pg_field_type_oid);
+PHP_FUNCTION(pg_field_prtlen);
+PHP_FUNCTION(pg_field_is_null);
+PHP_FUNCTION(pg_field_table);
+/* async message functions */
+PHP_FUNCTION(pg_get_notify);
+PHP_FUNCTION(pg_get_pid);
+/* error message functions */
+PHP_FUNCTION(pg_result_error);
+#if HAVE_PQRESULTERRORFIELD
+PHP_FUNCTION(pg_result_error_field);
+#endif
+PHP_FUNCTION(pg_last_error);
+PHP_FUNCTION(pg_last_notice);
+/* copy functions */
+PHP_FUNCTION(pg_put_line);
+PHP_FUNCTION(pg_end_copy);
+PHP_FUNCTION(pg_copy_to);
+PHP_FUNCTION(pg_copy_from);
+/* large object functions */
+PHP_FUNCTION(pg_lo_create);
+PHP_FUNCTION(pg_lo_unlink);
+PHP_FUNCTION(pg_lo_open);
+PHP_FUNCTION(pg_lo_close);
+PHP_FUNCTION(pg_lo_read);
+PHP_FUNCTION(pg_lo_write);
+PHP_FUNCTION(pg_lo_read_all);
+PHP_FUNCTION(pg_lo_import);
+PHP_FUNCTION(pg_lo_export);
+PHP_FUNCTION(pg_lo_seek);
+PHP_FUNCTION(pg_lo_tell);
+
+/* debugging functions */
+PHP_FUNCTION(pg_trace);
+PHP_FUNCTION(pg_untrace);
+
+/* utility functions */
+PHP_FUNCTION(pg_client_encoding);
+PHP_FUNCTION(pg_set_client_encoding);
+#if HAVE_PQSETERRORVERBOSITY
+PHP_FUNCTION(pg_set_error_verbosity);
+#endif
+#if HAVE_PQESCAPE
+PHP_FUNCTION(pg_escape_string);
+PHP_FUNCTION(pg_escape_bytea);
+PHP_FUNCTION(pg_unescape_bytea);
+PHP_FUNCTION(pg_escape_literal);
+PHP_FUNCTION(pg_escape_identifier);
+#endif
+
+/* misc functions */
+PHP_FUNCTION(pg_meta_data);
+PHP_FUNCTION(pg_convert);
+PHP_FUNCTION(pg_insert);
+PHP_FUNCTION(pg_update);
+PHP_FUNCTION(pg_delete);
+PHP_FUNCTION(pg_select);
+
+/* connection options - ToDo: Add async connection option */
+#define PGSQL_CONNECT_FORCE_NEW (1<<1)
+/* php_pgsql_convert options */
+#define PGSQL_CONV_IGNORE_DEFAULT (1<<1) /* Do not use DEAFULT value by removing field from returned array */
+#define PGSQL_CONV_FORCE_NULL (1<<2) /* Convert to NULL if string is null string */
+#define PGSQL_CONV_IGNORE_NOT_NULL (1<<3) /* Ignore NOT NULL constraints */
+#define PGSQL_CONV_OPTS (PGSQL_CONV_IGNORE_DEFAULT|PGSQL_CONV_FORCE_NULL|PGSQL_CONV_IGNORE_NOT_NULL)
+/* php_pgsql_insert/update/select/delete options */
+#define PGSQL_DML_NO_CONV (1<<8) /* Do not call php_pgsql_convert() */
+#define PGSQL_DML_EXEC (1<<9) /* Execute query */
+#define PGSQL_DML_ASYNC (1<<10) /* Do async query */
+#define PGSQL_DML_STRING (1<<11) /* Return query string */
+
+/* exported functions */
+PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta TSRMLS_DC);
+PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, const zval *values, zval *result, ulong opt TSRMLS_DC);
+PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *values, ulong opt, char **sql TSRMLS_DC);
+PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *values, zval *ids, ulong opt , char **sql TSRMLS_DC);
+PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids, ulong opt, char **sql TSRMLS_DC);
+PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids, zval *ret_array, ulong opt, char **sql TSRMLS_DC);
+PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array TSRMLS_DC);
+
+/* internal functions */
+static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent);
+static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type);
+static void php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type);
+static char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list TSRMLS_DC);
+static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type);
+static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type);
+static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS,int entry_type);
+
+typedef enum _php_pgsql_data_type {
+ /* boolean */
+ PG_BOOL,
+ /* number */
+ PG_OID,
+ PG_INT2,
+ PG_INT4,
+ PG_INT8,
+ PG_FLOAT4,
+ PG_FLOAT8,
+ PG_NUMERIC,
+ PG_MONEY,
+ /* character */
+ PG_TEXT,
+ PG_CHAR,
+ PG_VARCHAR,
+ /* time and interval */
+ PG_UNIX_TIME,
+ PG_UNIX_TIME_INTERVAL,
+ PG_DATE,
+ PG_TIME,
+ PG_TIME_WITH_TIMEZONE,
+ PG_TIMESTAMP,
+ PG_TIMESTAMP_WITH_TIMEZONE,
+ PG_INTERVAL,
+ /* binary */
+ PG_BYTEA,
+ /* network */
+ PG_CIDR,
+ PG_INET,
+ PG_MACADDR,
+ /* bit */
+ PG_BIT,
+ PG_VARBIT,
+ /* geometoric */
+ PG_LINE,
+ PG_LSEG,
+ PG_POINT,
+ PG_BOX,
+ PG_PATH,
+ PG_POLYGON,
+ PG_CIRCLE,
+ /* unkown and system */
+ PG_UNKNOWN
+} php_pgsql_data_type;
+
+typedef struct pgLofp {
+ PGconn *conn;
+ int lofd;
+} pgLofp;
+
+typedef struct _php_pgsql_result_handle {
+ PGconn *conn;
+ PGresult *result;
+ int row;
+} pgsql_result_handle;
+
+typedef struct _php_pgsql_notice {
+ char *message;
+ size_t len;
+} php_pgsql_notice;
+
+ZEND_BEGIN_MODULE_GLOBALS(pgsql)
+ long default_link; /* default link when connection is omitted */
+ long num_links,num_persistent;
+ long max_links,max_persistent;
+ long allow_persistent;
+ long auto_reset_persistent;
+ int le_lofp,le_string;
+ int ignore_notices,log_notices;
+ HashTable notices; /* notice message for each connection */
+ZEND_END_MODULE_GLOBALS(pgsql)
+
+ZEND_EXTERN_MODULE_GLOBALS(pgsql)
+
+#ifdef ZTS
+# define PGG(v) TSRMG(pgsql_globals_id, zend_pgsql_globals *, v)
+#else
+# define PGG(v) (pgsql_globals.v)
+#endif
+
+#endif
+
+#else
+
+#define pgsql_module_ptr NULL
+
+#endif
+
+#define phpext_pgsql_ptr pgsql_module_ptr
+
+#endif /* PHP_PGSQL_H */
diff --git a/ext/pgsql/tests/01createdb.phpt b/ext/pgsql/tests/01createdb.phpt
new file mode 100644
index 0000000..1584939
--- /dev/null
+++ b/ext/pgsql/tests/01createdb.phpt
@@ -0,0 +1,28 @@
+--TEST--
+PostgreSQL create db
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+// create test table
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+if (!@pg_num_rows(@pg_query($db, "SELECT * FROM ".$table_name)))
+{
+ @pg_query($db,$table_def); // Create table here
+ for ($i=0; $i < $num_test_record; $i++) {
+ pg_query($db,"INSERT INTO ".$table_name." VALUES ($i, 'ABC');");
+ }
+}
+else {
+ echo pg_last_error()."\n";
+}
+
+pg_close($db);
+
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/02connection.phpt b/ext/pgsql/tests/02connection.phpt
new file mode 100644
index 0000000..234427b
--- /dev/null
+++ b/ext/pgsql/tests/02connection.phpt
@@ -0,0 +1,59 @@
+--TEST--
+PostgreSQL connection
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+// connection function tests
+
+include('config.inc');
+
+$db = pg_pconnect($conn_str);
+var_dump($db);
+
+if (pg_connection_status($db) != PGSQL_CONNECTION_OK)
+{
+ echo "pg_connection_status() error\n";
+}
+if (!pg_connection_reset($db))
+{
+ echo "pg_connection_reset() error\n";
+}
+if (pg_connection_busy($db))
+{
+ echo "pg_connection_busy() error\n";
+}
+if (function_exists('pg_transaction_status')) {
+ if (pg_transaction_status($db) != PGSQL_TRANSACTION_IDLE)
+ {
+ echo "pg_transaction_status() error\n";
+ }
+}
+if (false === pg_host($db))
+{
+ echo "pg_host() error\n";
+}
+if (!pg_dbname($db))
+{
+ echo "pg_dbname() error\n";
+}
+if (!pg_port($db))
+{
+ echo "pg_port() error\n";
+}
+if (pg_tty($db))
+{
+ echo "pg_tty() error\n";
+}
+if (pg_options($db))
+{
+ echo "pg_options() error\n";
+}
+
+pg_close($db);
+
+?>
+===DONE===
+--EXPECTF--
+resource(%d) of type (pgsql link%s)
+===DONE===
diff --git a/ext/pgsql/tests/03sync_query.phpt b/ext/pgsql/tests/03sync_query.phpt
new file mode 100644
index 0000000..afb6bb4
--- /dev/null
+++ b/ext/pgsql/tests/03sync_query.phpt
@@ -0,0 +1,73 @@
+--TEST--
+PostgreSQL sync query
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+$result = pg_query($db, "SELECT * FROM ".$table_name.";");
+if (!($rows = pg_num_rows($result)))
+{
+ echo "pg_num_row() error\n";
+}
+for ($i=0; $i < $rows; $i++)
+{
+ pg_fetch_array($result, $i, PGSQL_NUM);
+}
+for ($i=0; $i < $rows; $i++)
+{
+ pg_fetch_object($result);
+}
+for ($i=0; $i < $rows; $i++)
+{
+ pg_fetch_row($result, $i);
+}
+for ($i=0; $i < $rows; $i++)
+{
+ pg_fetch_result($result, $i, 0);
+}
+
+pg_result_error($result);
+if (function_exists('pg_result_error_field')) {
+ pg_result_error_field($result, PGSQL_DIAG_SEVERITY);
+ pg_result_error_field($result, PGSQL_DIAG_SQLSTATE);
+ pg_result_error_field($result, PGSQL_DIAG_MESSAGE_PRIMARY);
+ pg_result_error_field($result, PGSQL_DIAG_MESSAGE_DETAIL);
+ pg_result_error_field($result, PGSQL_DIAG_MESSAGE_HINT);
+ pg_result_error_field($result, PGSQL_DIAG_STATEMENT_POSITION);
+ if (defined('PGSQL_DIAG_INTERNAL_POSITION'))
+ {
+ pg_result_error_field($result, PGSQL_DIAG_INTERNAL_POSITION);
+ }
+ if (defined('PGSQL_DIAG_INTERNAL_QUERY'))
+ {
+ pg_result_error_field($result, PGSQL_DIAG_INTERNAL_QUERY);
+ }
+ pg_result_error_field($result, PGSQL_DIAG_CONTEXT);
+ pg_result_error_field($result, PGSQL_DIAG_SOURCE_FILE);
+ pg_result_error_field($result, PGSQL_DIAG_SOURCE_LINE);
+ pg_result_error_field($result, PGSQL_DIAG_SOURCE_FUNCTION);
+}
+pg_num_rows(pg_query($db, "SELECT * FROM ".$table_name.";"));
+pg_num_fields(pg_query($db, "SELECT * FROM ".$table_name.";"));
+pg_field_name($result, 0);
+pg_field_num($result, $field_name);
+pg_field_size($result, 0);
+pg_field_type($result, 0);
+pg_field_prtlen($result, 0);
+pg_field_is_null($result, 0);
+
+$result = pg_query($db, "INSERT INTO ".$table_name." VALUES (9999, 'ABC');");
+pg_last_oid($result);
+
+pg_free_result($result);
+pg_close($db);
+
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/04async_query.phpt b/ext/pgsql/tests/04async_query.phpt
new file mode 100644
index 0000000..7711240
--- /dev/null
+++ b/ext/pgsql/tests/04async_query.phpt
@@ -0,0 +1,65 @@
+--TEST--
+PostgreSQL async query
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+if (!pg_send_query($db, "SELECT * FROM ".$table_name.";")) {
+ echo "pg_send_query() error\n";
+}
+while(pg_connection_busy($db)); // busy wait: intended
+if (pg_connection_status($db) === PGSQL_CONNECTION_BAD) {
+ echo "pg_connection_status() error\n";
+}
+if (!($result = pg_get_result($db)))
+{
+ echo "pg_get_result() error\n";
+}
+
+if (!($rows = pg_num_rows($result))) {
+ echo "pg_num_rows() error\n";
+}
+for ($i=0; $i < $rows; $i++)
+{
+ pg_fetch_array($result, $i, PGSQL_NUM);
+}
+for ($i=0; $i < $rows; $i++)
+{
+ pg_fetch_object($result);
+}
+for ($i=0; $i < $rows; $i++)
+{
+ pg_fetch_row($result, $i);
+}
+for ($i=0; $i < $rows; $i++)
+{
+ pg_fetch_result($result, $i, 0);
+}
+
+pg_num_rows(pg_query($db, "SELECT * FROM ".$table_name.";"));
+pg_num_fields(pg_query($db, "SELECT * FROM ".$table_name.";"));
+pg_field_name($result, 0);
+pg_field_num($result, $field_name);
+pg_field_size($result, 0);
+pg_field_type($result, 0);
+pg_field_prtlen($result, 0);
+pg_field_is_null($result, 0);
+
+if (!pg_send_query($db, "INSERT INTO ".$table_name." VALUES (8888, 'GGG');"))
+{
+ echo "pg_send_query() error\n";
+}
+
+pg_last_oid($result);
+pg_free_result($result);
+
+
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/05large_object.phpt b/ext/pgsql/tests/05large_object.phpt
new file mode 100644
index 0000000..a6f3019
--- /dev/null
+++ b/ext/pgsql/tests/05large_object.phpt
@@ -0,0 +1,82 @@
+--TEST--
+PostgreSQL large object
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+echo "create/write/close LO\n";
+pg_exec ($db, "begin");
+$oid = pg_lo_create ($db);
+if (!$oid) echo ("pg_lo_create() error\n");
+$handle = pg_lo_open ($db, $oid, "w");
+if (!$handle) echo ("pg_lo_open() error\n");
+pg_lo_write ($handle, "large object data\n");
+pg_lo_close ($handle);
+pg_exec ($db, "commit");
+
+echo "open/read/tell/seek/close LO\n";
+pg_exec ($db, "begin");
+$handle = pg_lo_open ($db, $oid, "w");
+pg_lo_read($handle, 100);
+pg_lo_tell($handle);
+pg_lo_seek($handle, 2);
+pg_lo_close($handle);
+pg_exec ($db, "commit");
+
+echo "open/read_all/close LO\n";
+pg_exec ($db, "begin");
+$handle = pg_lo_open ($db, $oid, "w");
+pg_lo_read_all($handle);
+if (pg_last_error()) echo "pg_lo_read_all() error\n".pg_last_error();
+pg_lo_close($handle);
+pg_exec ($db, "commit");
+
+echo "unlink LO\n";
+pg_exec ($db, "begin");
+pg_lo_unlink($db, $oid) or print("pg_lo_unlink() error 1\n");
+pg_exec ($db, "commit");
+
+// more pg_lo_unlink() tests
+echo "Test without connection\n";
+pg_exec ($db, "begin");
+$oid = pg_lo_create ($db) or print("pg_lo_create() error\n");
+pg_lo_unlink($oid) or print("pg_lo_unlink() error 2\n");
+pg_exec ($db, "commit");
+
+echo "Test with string oid value\n";
+pg_exec ($db, "begin");
+$oid = pg_lo_create ($db) or print("pg_lo_create() error\n");
+pg_lo_unlink($db, (string)$oid) or print("pg_lo_unlink() error 3\n");
+pg_exec ($db, "commit");
+
+echo "import/export LO\n";
+$path = dirname(__FILE__) . '/';
+pg_query($db, 'begin');
+$oid = pg_lo_import($db, $path . 'php.gif');
+pg_query($db, 'commit');
+pg_query($db, 'begin');
+@unlink($path . 'php.gif.exported');
+pg_lo_export($oid, $path . 'php.gif.exported', $db);
+if (!file_exists($path . 'php.gif.exported')) {
+ echo "Export failed\n";
+}
+@unlink($path . 'php.gif.exported');
+pg_query($db, 'commit');
+
+echo "OK";
+?>
+--EXPECT--
+create/write/close LO
+open/read/tell/seek/close LO
+open/read_all/close LO
+large object data
+unlink LO
+Test without connection
+Test with string oid value
+import/export LO
+OK
diff --git a/ext/pgsql/tests/06copy.phpt b/ext/pgsql/tests/06copy.phpt
new file mode 100644
index 0000000..bed783a
--- /dev/null
+++ b/ext/pgsql/tests/06copy.phpt
@@ -0,0 +1,22 @@
+--TEST--
+PostgreSQL copy functions
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+$rows = pg_copy_to($db, $table_name);
+
+pg_query($db, "DELETE FROM $table_name");
+
+pg_copy_from($db, $table_name, $rows);
+
+echo "OK";
+
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/07optional.phpt b/ext/pgsql/tests/07optional.phpt
new file mode 100644
index 0000000..9fa6f16
--- /dev/null
+++ b/ext/pgsql/tests/07optional.phpt
@@ -0,0 +1,24 @@
+--TEST--
+PostgreSQL optional functions
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+// optional functions
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+$enc = pg_client_encoding($db);
+
+pg_set_client_encoding($db, $enc);
+
+if (function_exists('pg_set_error_verbosity')) {
+ pg_set_error_verbosity(PGSQL_ERRORS_TERSE);
+ pg_set_error_verbosity(PGSQL_ERRORS_DEFAULT);
+ pg_set_error_verbosity(PGSQL_ERRORS_VERBOSE);
+}
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/08escape.phpt b/ext/pgsql/tests/08escape.phpt
new file mode 100644
index 0000000..90b4ed8
--- /dev/null
+++ b/ext/pgsql/tests/08escape.phpt
@@ -0,0 +1,95 @@
+--TEST--
+PostgreSQL escape functions
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include 'config.inc';
+define('FILE_NAME', dirname(__FILE__) . '/php.gif');
+
+// pg_escape_string() test
+$before = "ABC\\ABC\'";
+$expect = "ABC\\\\ABC\\'";
+$expect2 = "ABC\\\\ABC\\\\''"; //the way escape string differs from PostgreSQL 9.0
+$after = pg_escape_string($before);
+if ($expect === $after || $expect2 === $after) {
+ echo "pg_escape_string() is Ok\n";
+}
+else {
+ echo "pg_escape_string() is NOT Ok\n";
+ var_dump($before);
+ var_dump($after);
+ var_dump($expect);
+}
+
+// pg_escape_bytea() test
+$before = "ABC\\ABC";
+$expect = "ABC\\\\\\\\ABC";
+$after = pg_escape_bytea($before);
+if ($expect === $after) {
+ echo "pg_escape_bytea() is Ok\n";
+}
+else {
+ echo "pg_escape_byte() is NOT Ok\n";
+ var_dump($before);
+ var_dump($after);
+ var_dump($expect);
+}
+
+// Test using database
+$data = file_get_contents(FILE_NAME);
+$db = pg_connect($conn_str);
+
+// Insert binary to DB
+$escaped_data = pg_escape_bytea($data);
+pg_query("DELETE FROM ".$table_name." WHERE num = -9999;");
+$sql = "INSERT INTO ".$table_name." (num, bin) VALUES (-9999, CAST ('".$escaped_data."' AS BYTEA));";
+pg_query($db, $sql);
+
+// Retrieve binary from DB
+$sql = "SELECT bin::bytea FROM ".$table_name." WHERE num = -9999";
+$result = pg_query($db, $sql);
+$row = pg_fetch_array($result, 0, PGSQL_ASSOC);
+
+if ($data === pg_unescape_bytea($row['bin'])) {
+ echo "pg_escape_bytea() actually works with database\n";
+}
+else {
+ echo "pg_escape_bytea() is broken\n";
+}
+
+// pg_escape_literal/pg_escape_identifier
+$before = "ABC\\ABC\'";
+$expect = " E'ABC\\\\ABC\\\\'''";
+$after = pg_escape_literal($before);
+if ($expect === $after) {
+ echo "pg_escape_literal() is Ok\n";
+}
+else {
+ echo "pg_escape_literal() is NOT Ok\n";
+ var_dump($before);
+ var_dump($after);
+ var_dump($expect);
+}
+
+$before = "ABC\\ABC\'";
+$expect = "\"ABC\ABC\'\"";
+$after = pg_escape_identifier($before);
+if ($expect === $after) {
+ echo "pg_escape_identifier() is Ok\n";
+}
+else {
+ echo "pg_escape_identifier() is NOT Ok\n";
+ var_dump($before);
+ var_dump($after);
+ var_dump($expect);
+}
+
+?>
+--EXPECT--
+pg_escape_string() is Ok
+pg_escape_bytea() is Ok
+pg_escape_bytea() actually works with database
+pg_escape_literal() is Ok
+pg_escape_identifier() is Ok \ No newline at end of file
diff --git a/ext/pgsql/tests/09notice.phpt b/ext/pgsql/tests/09notice.phpt
new file mode 100644
index 0000000..3167069
--- /dev/null
+++ b/ext/pgsql/tests/09notice.phpt
@@ -0,0 +1,38 @@
+--TEST--
+PostgreSQL notice function
+--SKIPIF--
+<?php
+
+include("skipif.inc");
+
+_skip_lc_messages();
+
+?>
+--INI--
+pgsql.log_notice=1
+pgsql.ignore_notices=0
+--FILE--
+<?php
+include 'config.inc';
+include 'lcmess.inc';
+
+$db = pg_connect($conn_str);
+
+_set_lc_messages();
+
+pg_query($db, "BEGIN;");
+pg_query($db, "BEGIN;");
+
+$msg = pg_last_notice($db);
+if ($msg === FALSE) {
+ echo "Cannot find notice message in hash\n";
+ var_dump($msg);
+}
+echo $msg."\n";
+echo "pg_last_notice() is Ok\n";
+
+?>
+--EXPECTF--
+Notice: pg_query(): %s already a transaction in progress in %s on line %d
+%s already a transaction in progress
+pg_last_notice() is Ok
diff --git a/ext/pgsql/tests/10pg_convert.phpt b/ext/pgsql/tests/10pg_convert.phpt
new file mode 100644
index 0000000..73bf2b6
--- /dev/null
+++ b/ext/pgsql/tests/10pg_convert.phpt
@@ -0,0 +1,29 @@
+--TEST--
+PostgreSQL pg_convert()
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '>=');
+?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+
+$fields = array('num'=>'1234', 'str'=>'AAA', 'bin'=>'BBB');
+$converted = pg_convert($db, $table_name, $fields);
+
+var_dump($converted);
+?>
+--EXPECT--
+array(3) {
+ ["num"]=>
+ string(4) "1234"
+ ["str"]=>
+ string(5) "'AAA'"
+ ["bin"]=>
+ string(5) "'BBB'"
+}
diff --git a/ext/pgsql/tests/10pg_convert_85.phpt b/ext/pgsql/tests/10pg_convert_85.phpt
new file mode 100644
index 0000000..4f1c92b
--- /dev/null
+++ b/ext/pgsql/tests/10pg_convert_85.phpt
@@ -0,0 +1,29 @@
+--TEST--
+PostgreSQL pg_convert() (8.5+)
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '<');
+?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+
+$fields = array('num'=>'1234', 'str'=>'AAA', 'bin'=>'BBB');
+$converted = pg_convert($db, $table_name, $fields);
+
+var_dump($converted);
+?>
+--EXPECT--
+array(3) {
+ ["num"]=>
+ string(4) "1234"
+ ["str"]=>
+ string(5) "'AAA'"
+ ["bin"]=>
+ string(11) "'\\x424242'"
+}
diff --git a/ext/pgsql/tests/11pg_meta_data.phpt b/ext/pgsql/tests/11pg_meta_data.phpt
new file mode 100644
index 0000000..a7f8ed4
--- /dev/null
+++ b/ext/pgsql/tests/11pg_meta_data.phpt
@@ -0,0 +1,64 @@
+--TEST--
+PostgreSQL pg_metadata()
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+
+$meta = pg_meta_data($db, $table_name);
+
+var_dump($meta);
+?>
+--EXPECT--
+array(3) {
+ ["num"]=>
+ array(6) {
+ ["num"]=>
+ int(1)
+ ["type"]=>
+ string(4) "int4"
+ ["len"]=>
+ int(4)
+ ["not null"]=>
+ bool(false)
+ ["has default"]=>
+ bool(false)
+ ["array dims"]=>
+ int(0)
+ }
+ ["str"]=>
+ array(6) {
+ ["num"]=>
+ int(2)
+ ["type"]=>
+ string(4) "text"
+ ["len"]=>
+ int(-1)
+ ["not null"]=>
+ bool(false)
+ ["has default"]=>
+ bool(false)
+ ["array dims"]=>
+ int(0)
+ }
+ ["bin"]=>
+ array(6) {
+ ["num"]=>
+ int(3)
+ ["type"]=>
+ string(5) "bytea"
+ ["len"]=>
+ int(-1)
+ ["not null"]=>
+ bool(false)
+ ["has default"]=>
+ bool(false)
+ ["array dims"]=>
+ int(0)
+ }
+}
diff --git a/ext/pgsql/tests/12pg_insert.phpt b/ext/pgsql/tests/12pg_insert.phpt
new file mode 100644
index 0000000..f5cd868
--- /dev/null
+++ b/ext/pgsql/tests/12pg_insert.phpt
@@ -0,0 +1,24 @@
+--TEST--
+PostgreSQL pg_insert()
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '>=');
+?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+$fields = array('num'=>'1234', 'str'=>'AAA', 'bin'=>'BBB');
+
+pg_insert($db, $table_name, $fields) or print "Error in test 1\n";
+echo pg_insert($db, $table_name, $fields, PGSQL_DML_STRING)."\n";
+
+echo "Ok\n";
+?>
+--EXPECT--
+INSERT INTO php_pgsql_test (num,str,bin) VALUES (1234,'AAA','BBB');
+Ok
diff --git a/ext/pgsql/tests/12pg_insert_85.phpt b/ext/pgsql/tests/12pg_insert_85.phpt
new file mode 100644
index 0000000..a85dea0
--- /dev/null
+++ b/ext/pgsql/tests/12pg_insert_85.phpt
@@ -0,0 +1,24 @@
+--TEST--
+PostgreSQL pg_insert() (8.5+)
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '<');
+?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+$fields = array('num'=>'1234', 'str'=>'AAA', 'bin'=>'BBB');
+
+pg_insert($db, $table_name, $fields) or print "Error in test 1\n";
+echo pg_insert($db, $table_name, $fields, PGSQL_DML_STRING)."\n";
+
+echo "Ok\n";
+?>
+--EXPECT--
+INSERT INTO php_pgsql_test (num,str,bin) VALUES (1234,'AAA','\\x424242');
+Ok
diff --git a/ext/pgsql/tests/13pg_select.phpt b/ext/pgsql/tests/13pg_select.phpt
new file mode 100644
index 0000000..f1504a8
--- /dev/null
+++ b/ext/pgsql/tests/13pg_select.phpt
@@ -0,0 +1,37 @@
+--TEST--
+PostgreSQL pg_select()
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '>=');
+?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+$fields = array('num'=>'1234', 'str'=>'ABC', 'bin'=>'XYZ');
+$ids = array('num'=>'1234');
+
+$res = pg_select($db, $table_name, $ids) or print "Error\n";
+var_dump($res);
+echo pg_select($db, $table_name, $ids, PGSQL_DML_STRING)."\n";
+echo "Ok\n";
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ array(3) {
+ ["num"]=>
+ string(4) "1234"
+ ["str"]=>
+ string(3) "AAA"
+ ["bin"]=>
+ string(3) "BBB"
+ }
+}
+SELECT * FROM php_pgsql_test WHERE num=1234;
+Ok
diff --git a/ext/pgsql/tests/13pg_select_85.phpt b/ext/pgsql/tests/13pg_select_85.phpt
new file mode 100644
index 0000000..e6d86bd
--- /dev/null
+++ b/ext/pgsql/tests/13pg_select_85.phpt
@@ -0,0 +1,37 @@
+--TEST--
+PostgreSQL pg_select() (8.5+)
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '<');
+?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+$fields = array('num'=>'1234', 'str'=>'ABC', 'bin'=>'XYZ');
+$ids = array('num'=>'1234');
+
+$res = pg_select($db, $table_name, $ids) or print "Error\n";
+var_dump($res);
+echo pg_select($db, $table_name, $ids, PGSQL_DML_STRING)."\n";
+echo "Ok\n";
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ array(3) {
+ ["num"]=>
+ string(4) "1234"
+ ["str"]=>
+ string(3) "AAA"
+ ["bin"]=>
+ string(8) "\x424242"
+ }
+}
+SELECT * FROM php_pgsql_test WHERE num=1234;
+Ok
diff --git a/ext/pgsql/tests/14pg_update.phpt b/ext/pgsql/tests/14pg_update.phpt
new file mode 100644
index 0000000..b41dd1a
--- /dev/null
+++ b/ext/pgsql/tests/14pg_update.phpt
@@ -0,0 +1,25 @@
+--TEST--
+PostgreSQL pg_update()
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '>=');
+?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+$fields = array('num'=>'1234', 'str'=>'ABC', 'bin'=>'XYZ');
+$ids = array('num'=>'1234');
+
+pg_update($db, $table_name, $fields, $ids) or print "Error in test 1\n";
+echo pg_update($db, $table_name, $fields, $ids, PGSQL_DML_STRING)."\n";
+
+echo "Ok\n";
+?>
+--EXPECT--
+UPDATE php_pgsql_test SET num=1234,str='ABC',bin='XYZ' WHERE num=1234;
+Ok
diff --git a/ext/pgsql/tests/14pg_update_85.phpt b/ext/pgsql/tests/14pg_update_85.phpt
new file mode 100644
index 0000000..f1c77ea
--- /dev/null
+++ b/ext/pgsql/tests/14pg_update_85.phpt
@@ -0,0 +1,25 @@
+--TEST--
+PostgreSQL pg_update() (8.5+)
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '<');
+?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+$fields = array('num'=>'1234', 'str'=>'ABC', 'bin'=>'XYZ');
+$ids = array('num'=>'1234');
+
+pg_update($db, $table_name, $fields, $ids) or print "Error in test 1\n";
+echo pg_update($db, $table_name, $fields, $ids, PGSQL_DML_STRING)."\n";
+
+echo "Ok\n";
+?>
+--EXPECT--
+UPDATE php_pgsql_test SET num=1234,str='ABC',bin='\\x58595a' WHERE num=1234;
+Ok
diff --git a/ext/pgsql/tests/15pg_delete.phpt b/ext/pgsql/tests/15pg_delete.phpt
new file mode 100644
index 0000000..e35f4ba
--- /dev/null
+++ b/ext/pgsql/tests/15pg_delete.phpt
@@ -0,0 +1,23 @@
+--TEST--
+PostgreSQL pg_delete()
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+
+$fields = array('num'=>'1234', 'str'=>'XXX', 'bin'=>'YYY');
+$ids = array('num'=>'1234');
+if (!pg_delete($db, $table_name, $ids)) {
+ echo "Error\n";
+}
+else {
+ echo "Ok\n";
+}
+?>
+--EXPECT--
+Ok
diff --git a/ext/pgsql/tests/16pg_result_status.phpt b/ext/pgsql/tests/16pg_result_status.phpt
new file mode 100644
index 0000000..268dc9f
--- /dev/null
+++ b/ext/pgsql/tests/16pg_result_status.phpt
@@ -0,0 +1,19 @@
+--TEST--
+PostgreSQL pg_result_status()
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+
+$sql = "SELECT * FROM ".$table_name." WHERE num = -2";
+$result = pg_query($db, "BEGIN;END");
+
+echo pg_result_status($result)."\n";
+echo pg_result_status($result, PGSQL_STATUS_STRING)."\n";
+?>
+--EXPECT--
+1
+COMMIT
diff --git a/ext/pgsql/tests/17result.phpt b/ext/pgsql/tests/17result.phpt
new file mode 100644
index 0000000..c3f9959
--- /dev/null
+++ b/ext/pgsql/tests/17result.phpt
@@ -0,0 +1,67 @@
+--TEST--
+PostgreSQL pg_fetch_*() functions
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+
+$sql = "SELECT * FROM $table_name";
+$result = pg_query($db, $sql) or die('Cannot qeury db');
+$rows = pg_num_rows($result);
+
+var_dump(pg_result_seek($result, 1));
+var_dump(pg_fetch_object($result));
+var_dump(pg_fetch_array($result, 1));
+var_dump(pg_fetch_row($result, 1));
+var_dump(pg_fetch_assoc($result, 1));
+var_dump(pg_result_seek($result, 0));
+
+echo "Ok\n";
+?>
+--EXPECT--
+bool(true)
+object(stdClass)#1 (3) {
+ ["num"]=>
+ string(1) "1"
+ ["str"]=>
+ string(3) "ABC"
+ ["bin"]=>
+ NULL
+}
+array(6) {
+ [0]=>
+ string(1) "1"
+ ["num"]=>
+ string(1) "1"
+ [1]=>
+ string(3) "ABC"
+ ["str"]=>
+ string(3) "ABC"
+ [2]=>
+ NULL
+ ["bin"]=>
+ NULL
+}
+array(3) {
+ [0]=>
+ string(1) "1"
+ [1]=>
+ string(3) "ABC"
+ [2]=>
+ NULL
+}
+array(3) {
+ ["num"]=>
+ string(1) "1"
+ ["str"]=>
+ string(3) "ABC"
+ ["bin"]=>
+ NULL
+}
+bool(true)
+Ok
diff --git a/ext/pgsql/tests/18pg_escape_bytea.phpt b/ext/pgsql/tests/18pg_escape_bytea.phpt
new file mode 100644
index 0000000..43f98c4
--- /dev/null
+++ b/ext/pgsql/tests/18pg_escape_bytea.phpt
@@ -0,0 +1,28 @@
+--TEST--
+PostgreSQL pg_escape_bytea() functions
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+// optional functions
+
+include('config.inc');
+
+$image = file_get_contents(dirname(__FILE__) . '/php.gif');
+$esc_image = pg_escape_bytea($image);
+
+$db = pg_connect($conn_str);
+pg_query($db, 'INSERT INTO '.$table_name.' (num, bin) VALUES (9876, \''.$esc_image.'\');');
+$result = pg_query($db, 'SELECT * FROM '.$table_name.' WHERE num = 9876');
+$rows = pg_fetch_all($result);
+$unesc_image = pg_unescape_bytea($rows[0]['bin']);
+
+if ($unesc_image !== $image) {
+ echo "NG";
+}
+else {
+ echo "OK";
+}
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/19pg_ping.phpt b/ext/pgsql/tests/19pg_ping.phpt
new file mode 100644
index 0000000..9059bde
--- /dev/null
+++ b/ext/pgsql/tests/19pg_ping.phpt
@@ -0,0 +1,15 @@
+--TEST--
+PostgreSQL pg_ping() functions
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+// optional functions
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+var_dump(pg_ping($db));
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/pgsql/tests/20pg_get_pid.phpt b/ext/pgsql/tests/20pg_get_pid.phpt
new file mode 100644
index 0000000..06cdfa8
--- /dev/null
+++ b/ext/pgsql/tests/20pg_get_pid.phpt
@@ -0,0 +1,17 @@
+--TEST--
+PostgreSQL pg_get_pid() functions
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+// optional functions
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+$pid = pg_get_pid($db);
+
+is_integer($pid) ? print 'OK' : print 'NG';
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/21pg_get_notify.phpt b/ext/pgsql/tests/21pg_get_notify.phpt
new file mode 100644
index 0000000..9171fc0
--- /dev/null
+++ b/ext/pgsql/tests/21pg_get_notify.phpt
@@ -0,0 +1,20 @@
+--TEST--
+PostgreSQL pg_get_notify() functions
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+// optional functions
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+pg_query($db, 'LISTEN test_msg');
+pg_query($db, 'NOTIFY test_msg');
+
+$msg = pg_get_notify($db);
+
+isset($msg['message'],$msg['pid']) ? print 'OK' : print 'NG';
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/22pg_fetch_object.phpt b/ext/pgsql/tests/22pg_fetch_object.phpt
new file mode 100644
index 0000000..76a3fbe
--- /dev/null
+++ b/ext/pgsql/tests/22pg_fetch_object.phpt
@@ -0,0 +1,37 @@
+--TEST--
+PostgreSQL pg_fetch_object()
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+include 'config.inc';
+
+class test_class {
+ function __construct($arg1, $arg2) {
+ echo __METHOD__ . "($arg1,$arg2)\n";
+ }
+}
+
+$db = pg_connect($conn_str);
+
+$sql = "SELECT * FROM $table_name WHERE num = 0";
+$result = pg_query($db, $sql) or die('Cannot qeury db');
+$rows = pg_num_rows($result);
+
+var_dump(pg_fetch_object($result, NULL, 'test_class', array(1, 2)));
+
+echo "Ok\n";
+?>
+--EXPECT--
+test_class::__construct(1,2)
+object(test_class)#1 (3) {
+ ["num"]=>
+ string(1) "0"
+ ["str"]=>
+ string(3) "ABC"
+ ["bin"]=>
+ NULL
+}
+Ok
diff --git a/ext/pgsql/tests/23sync_query_params.phpt b/ext/pgsql/tests/23sync_query_params.phpt
new file mode 100644
index 0000000..6959cd7
--- /dev/null
+++ b/ext/pgsql/tests/23sync_query_params.phpt
@@ -0,0 +1,59 @@
+--TEST--
+PostgreSQL sync query params
+--SKIPIF--
+<?php
+include("skipif.inc");
+if (!function_exists('pg_query_params')) die('skip function pg_query_params() does not exist');
+?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+$version = pg_version($db);
+if ($version['protocol'] >= 3) {
+ $result = pg_query_params($db, "SELECT * FROM ".$table_name." WHERE num > \$1;", array(100));
+ if (!($rows = pg_num_rows($result)))
+ {
+ echo "pg_num_row() error\n";
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_array($result, $i, PGSQL_NUM);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_object($result);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_row($result, $i);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_result($result, $i, 0);
+ }
+
+ pg_result_error($result);
+ pg_num_rows(pg_query_params($db, "SELECT * FROM ".$table_name." WHERE num > \$1;", array(100)));
+ pg_num_fields(pg_query_params($db, "SELECT * FROM ".$table_name." WHERE num > \$1;", array(100)));
+ pg_field_name($result, 0);
+ pg_field_num($result, $field_name);
+ pg_field_size($result, 0);
+ pg_field_type($result, 0);
+ pg_field_prtlen($result, 0);
+ pg_field_is_null($result, 0);
+
+ $result = pg_query_params($db, "INSERT INTO ".$table_name." VALUES (\$1, \$2);", array(9999, "A'BC"));
+ pg_last_oid($result);
+
+ pg_free_result($result);
+}
+pg_close($db);
+
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/24sync_query_prepared.phpt b/ext/pgsql/tests/24sync_query_prepared.phpt
new file mode 100644
index 0000000..ea8f146
--- /dev/null
+++ b/ext/pgsql/tests/24sync_query_prepared.phpt
@@ -0,0 +1,65 @@
+--TEST--
+PostgreSQL sync prepared queries
+--SKIPIF--
+<?php
+include("skipif.inc");
+if (!function_exists('pg_prepare')) die('skip function pg_prepare() does not exist');
+?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+$version = pg_version($db);
+if ($version['protocol'] >= 3) {
+ $result = pg_prepare($db, "php_test", "SELECT * FROM ".$table_name." WHERE num > \$1;");
+ pg_result_error($result);
+ pg_free_result($result);
+ $result = pg_execute($db, "php_test", array(100));
+ if (!($rows = pg_num_rows($result)))
+ {
+ echo "pg_num_row() error\n";
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_array($result, $i, PGSQL_NUM);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_object($result);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_row($result, $i);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_result($result, $i, 0);
+ }
+
+ pg_result_error($result);
+ pg_num_rows(pg_execute($db, "php_test", array(100)));
+ pg_num_fields(pg_execute($db, "php_test", array(100)));
+ pg_field_name($result, 0);
+ pg_field_num($result, $field_name);
+ pg_field_size($result, 0);
+ pg_field_type($result, 0);
+ pg_field_prtlen($result, 0);
+ pg_field_is_null($result, 0);
+
+ $result = pg_prepare($db, "php_test2", "INSERT INTO ".$table_name." VALUES (\$1, \$2);");
+ pg_result_error($result);
+ pg_free_result($result);
+ $result = pg_execute($db, "php_test2", array(9999, "A'BC"));
+ pg_last_oid($result);
+
+ pg_free_result($result);
+}
+pg_close($db);
+
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/25async_query_params.phpt b/ext/pgsql/tests/25async_query_params.phpt
new file mode 100644
index 0000000..6e7dafe
--- /dev/null
+++ b/ext/pgsql/tests/25async_query_params.phpt
@@ -0,0 +1,70 @@
+--TEST--
+PostgreSQL async query params
+--SKIPIF--
+<?php
+include("skipif.inc");
+if (!function_exists('pg_send_query_params')) die('skip function pg_send_query_params() does not exist');
+?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+$version = pg_version($db);
+if ($version['protocol'] >= 3) {
+ if (!pg_send_query_params($db, "SELECT * FROM ".$table_name." WHERE num > \$1;", array(100))) {
+ echo "pg_send_query_params() error\n";
+ }
+ while(pg_connection_busy($db)); // busy wait: intended
+ if (pg_connection_status($db) === PGSQL_CONNECTION_BAD) {
+ echo "pg_connection_status() error\n";
+ }
+ if (!($result = pg_get_result($db)))
+ {
+ echo "pg_get_result() error\n";
+ }
+ if (!($rows = pg_num_rows($result))) {
+ echo "pg_num_rows() error\n";
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_array($result, $i, PGSQL_NUM);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_object($result);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_row($result, $i);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_result($result, $i, 0);
+ }
+
+ pg_num_rows(pg_query_params($db, "SELECT * FROM ".$table_name." WHERE num > \$1;", array(100)));
+ pg_num_fields(pg_query_params($db, "SELECT * FROM ".$table_name." WHERE num > \$1;", array(100)));
+ pg_field_name($result, 0);
+ pg_field_num($result, $field_name);
+ pg_field_size($result, 0);
+ pg_field_type($result, 0);
+ pg_field_prtlen($result, 0);
+ pg_field_is_null($result, 0);
+
+ if (!pg_send_query_params($db, "INSERT INTO ".$table_name." VALUES (\$1, \$2);", array(9999, "A'BC")))
+ {
+ echo "pg_send_query_params() error\n";
+ }
+
+ pg_last_oid($result);
+ pg_free_result($result);
+}
+pg_close($db);
+
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/26async_query_prepared.phpt b/ext/pgsql/tests/26async_query_prepared.phpt
new file mode 100644
index 0000000..bda363b
--- /dev/null
+++ b/ext/pgsql/tests/26async_query_prepared.phpt
@@ -0,0 +1,106 @@
+--TEST--
+PostgreSQL async prepared queries
+--SKIPIF--
+<?php
+include("skipif.inc");
+if (!function_exists('pg_send_prepare')) die('skip function pg_send_prepare() does not exist');
+?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+$version = pg_version($db);
+if ($version['protocol'] >= 3) {
+ if (!pg_send_prepare($db, 'php_test', "SELECT * FROM ".$table_name." WHERE num > \$1;")) {
+ echo "pg_send_prepare() error\n";
+ }
+ while(pg_connection_busy($db)); // busy wait: intended
+ if (pg_connection_status($db) === PGSQL_CONNECTION_BAD) {
+ echo "pg_connection_status() error\n";
+ }
+ if (!($result = pg_get_result($db)))
+ {
+ echo "pg_get_result() error\n";
+ }
+ pg_free_result($result);
+
+ if (!pg_send_execute($db, 'php_test', array(100))) {
+ echo "pg_send_execute() error\n";
+ }
+ while(pg_connection_busy($db)); // busy wait: intended
+ if (pg_connection_status($db) === PGSQL_CONNECTION_BAD) {
+ echo "pg_connection_status() error\n";
+ }
+ if (!($result = pg_get_result($db)))
+ {
+ echo "pg_get_result() error\n";
+ }
+
+ if (!($rows = pg_num_rows($result))) {
+ echo "pg_num_rows() error\n";
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_array($result, $i, PGSQL_NUM);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_object($result);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_row($result, $i);
+ }
+ for ($i=0; $i < $rows; $i++)
+ {
+ pg_fetch_result($result, $i, 0);
+ }
+
+ pg_num_rows(pg_query_params($db, "SELECT * FROM ".$table_name." WHERE num > \$1;", array(100)));
+ pg_num_fields(pg_query_params($db, "SELECT * FROM ".$table_name." WHERE num > \$1;", array(100)));
+ pg_field_name($result, 0);
+ pg_field_num($result, $field_name);
+ pg_field_size($result, 0);
+ pg_field_type($result, 0);
+ pg_field_prtlen($result, 0);
+ pg_field_is_null($result, 0);
+
+ if (!pg_send_prepare($db, "php_test2", "INSERT INTO ".$table_name." VALUES (\$1, \$2);"))
+ {
+ echo "pg_send_prepare() error\n";
+ }
+ while(pg_connection_busy($db)); // busy wait: intended
+ if (pg_connection_status($db) === PGSQL_CONNECTION_BAD) {
+ echo "pg_connection_status() error\n";
+ }
+ if (!($result = pg_get_result($db)))
+ {
+ echo "pg_get_result() error\n";
+ }
+ pg_free_result($result);
+
+ if (!pg_send_execute($db, "php_test2", array(9999, "A'BC")))
+ {
+ echo "pg_send_execute() error\n";
+ }
+ while(pg_connection_busy($db)); // busy wait: intended
+ if (pg_connection_status($db) === PGSQL_CONNECTION_BAD) {
+ echo "pg_connection_status() error\n";
+ }
+ if (!($result = pg_get_result($db)))
+ {
+ echo "pg_get_result() error\n";
+ }
+
+ pg_last_oid($result);
+ pg_free_result($result);
+}
+pg_close($db);
+
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/27large_object_oid.phpt b/ext/pgsql/tests/27large_object_oid.phpt
new file mode 100644
index 0000000..0a469bc
--- /dev/null
+++ b/ext/pgsql/tests/27large_object_oid.phpt
@@ -0,0 +1,47 @@
+--TEST--
+PostgreSQL create large object with given oid
+--SKIPIF--
+<?php
+include("skipif.inc");
+$v = pg_version($conn);
+if (version_compare("8.3", $v["client"]) > 0) die("skip - requires pg client >= 8.3\n");
+if (version_compare("8.3", $v["server"]) > 0) die("skip - requires pg server >= 8.3\n");
+?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+echo "create LO from int\n";
+pg_exec ($db, "begin");
+$oid = pg_lo_create ($db, 21000);
+if (!$oid) echo ("pg_lo_create() error\n");
+if ($oid != 21000) echo ("pg_lo_create() wrong id\n");
+pg_lo_unlink ($db, $oid);
+pg_exec ($db, "commit");
+
+echo "create LO from string\n";
+pg_exec ($db, "begin");
+$oid = pg_lo_create ($db, "21001");
+if (!$oid) echo ("pg_lo_create() error\n");
+if ($oid != 21001) echo ("pg_lo_create() wrong id\n");
+pg_lo_unlink ($db, $oid);
+pg_exec ($db, "commit");
+
+echo "create LO using default connection\n";
+pg_exec ("begin");
+$oid = pg_lo_create (21002);
+if (!$oid) echo ("pg_lo_create() error\n");
+if ($oid != 21002) echo ("pg_lo_create() wrong id\n");
+pg_lo_unlink ($oid);
+pg_exec ("commit");
+
+echo "OK";
+?>
+--EXPECT--
+create LO from int
+create LO from string
+create LO using default connection
+OK
diff --git a/ext/pgsql/tests/28large_object_import_oid.phpt b/ext/pgsql/tests/28large_object_import_oid.phpt
new file mode 100644
index 0000000..323d179
--- /dev/null
+++ b/ext/pgsql/tests/28large_object_import_oid.phpt
@@ -0,0 +1,48 @@
+--TEST--
+PostgreSQL import large object with given oid
+--SKIPIF--
+<?php
+include("skipif.inc");
+$v = pg_version($conn);
+if (version_compare("8.4devel", $v["client"]) > 0) die("skip - requires pg client >= 8.4\n");
+if (version_compare("8.4devel", $v["server"]) > 0) die("skip - requires pg server >= 8.4\n");
+?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+
+echo "import LO from int\n";
+pg_exec($db, 'begin');
+$oid = pg_lo_import($db, __FILE__, 21003);
+if (!$oid) echo ("pg_lo_import() error\n");
+if ($oid != 21003) echo ("pg_lo_import() wrong id\n");
+pg_lo_unlink ($db, $oid);
+pg_exec($db, 'commit');
+
+echo "import LO from string\n";
+pg_exec($db, 'begin');
+$oid = pg_lo_import($db, __FILE__, "21004");
+if (!$oid) echo ("pg_lo_import() error\n");
+if ($oid != 21004) echo ("pg_lo_import() wrong id\n");
+pg_lo_unlink ($db, $oid);
+pg_exec($db, 'commit');
+
+echo "import LO using default connection\n";
+pg_exec('begin');
+$oid = pg_lo_import($db, __FILE__, 21005);
+if (!$oid) echo ("pg_lo_import() error\n");
+if ($oid != 21005) echo ("pg_lo_import() wrong id\n");
+pg_lo_unlink ($oid);
+pg_exec('commit');
+
+
+echo "OK";
+?>
+--EXPECT--
+import LO from int
+import LO from string
+import LO using default connection
+OK
diff --git a/ext/pgsql/tests/80_bug14383.phpt b/ext/pgsql/tests/80_bug14383.phpt
new file mode 100644
index 0000000..a736f34
--- /dev/null
+++ b/ext/pgsql/tests/80_bug14383.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Bug #14383 (using postgres with DBA causes DBA not to be able to find any keys)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__).'/../../dba/tests/skipif.inc');
+require_once('skipif.inc');
+?>
+--FILE--
+<?php
+require_once('config.inc');
+
+$dbh = @pg_connect($conn_str);
+if (!$dbh) {
+ die ("Could not connect to the server");
+}
+pg_close($dbh);
+
+require_once(dirname(__FILE__).'/../../dba/tests/test.inc');
+require_once(dirname(__FILE__).'/../../dba/tests/dba_handler.inc');
+
+?>
+--EXPECTF--
+database handler: %s
+3NYNYY
+Content String 2
+Content 2 replaced
+Read during write:%sallowed
+Content 2 replaced 2nd time
+The 6th value
+array(3) {
+ ["key number 6"]=>
+ string(13) "The 6th value"
+ ["key2"]=>
+ string(27) "Content 2 replaced 2nd time"
+ ["key5"]=>
+ string(23) "The last content string"
+}
+--NO-LOCK--
+3NYNYY
+Content String 2
+Content 2 replaced
+Read during write: not allowed
+Content 2 replaced 2nd time
+The 6th value
+array(3) {
+ ["key number 6"]=>
+ string(13) "The 6th value"
+ ["key2"]=>
+ string(27) "Content 2 replaced 2nd time"
+ ["key5"]=>
+ string(23) "The last content string"
+}
diff --git a/ext/pgsql/tests/80_bug24499.phpt b/ext/pgsql/tests/80_bug24499.phpt
new file mode 100644
index 0000000..32e789d
--- /dev/null
+++ b/ext/pgsql/tests/80_bug24499.phpt
@@ -0,0 +1,66 @@
+--TEST--
+Bug #24499 (Notice: Undefined property: stdClass::)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+?>
+--FILE--
+<?php
+
+require_once('config.inc');
+
+$dbh = @pg_connect($conn_str);
+if (!$dbh) {
+ die ("Could not connect to the server");
+}
+
+@pg_query("DROP SEQUENCE id_id_seq");
+@pg_query("DROP TABLE id");
+pg_query("CREATE TABLE id (id SERIAL, t INT)");
+
+for ($i=0; $i<4; $i++) {
+ pg_query("INSERT INTO id (t) VALUES ($i)");
+}
+
+class Id
+{
+ public $id;
+
+ public function getId()
+ {
+ global $dbh;
+
+ $q = pg_query($dbh, "SELECT id FROM id");
+ print_r(pg_fetch_array($q));
+ print_r(pg_fetch_array($q));
+ $id = pg_fetch_object($q);
+ var_dump($id);
+ return $id->id;
+ }
+}
+
+$id = new Id();
+var_dump($id->getId());
+
+pg_close($dbh);
+
+echo "Done\n";
+
+?>
+--EXPECTF--
+Array
+(
+ [0] => 1
+ [id] => 1
+)
+Array
+(
+ [0] => 2
+ [id] => 2
+)
+object(stdClass)#%d (1) {
+ ["id"]=>
+ string(1) "3"
+}
+string(1) "3"
+Done
diff --git a/ext/pgsql/tests/80_bug27597.phpt b/ext/pgsql/tests/80_bug27597.phpt
new file mode 100644
index 0000000..7d5b5db
--- /dev/null
+++ b/ext/pgsql/tests/80_bug27597.phpt
@@ -0,0 +1,60 @@
+--TEST--
+Bug #27597 (pg_fetch_array not returning false)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+?>
+--FILE--
+<?php
+
+require_once(dirname(__FILE__) . '/config.inc');
+
+$dbh = @pg_connect($conn_str);
+if (!$dbh) {
+ die ("Could not connect to the server");
+}
+
+@pg_query("DROP TABLE id");
+pg_query("CREATE TABLE id (id INT)");
+
+for ($i=0; $i<4; $i++) {
+ pg_query("INSERT INTO id (id) VALUES ($i)");
+}
+
+function xi_fetch_array($res, $type = PGSQL_ASSOC) {
+ $a = pg_fetch_array($res, NULL, $type) ;
+ return $a ;
+}
+
+$res = pg_query("SELECT * FROM id");
+$i = 0; // endless-loop protection
+while($row = xi_fetch_array($res)) {
+ print_r($row);
+ if ($i++ > 4) {
+ echo "ENDLESS-LOOP";
+ exit(1);
+ }
+}
+
+pg_close($dbh);
+
+?>
+===DONE===
+--EXPECTF--
+Array
+(
+ [id] => 0
+)
+Array
+(
+ [id] => 1
+)
+Array
+(
+ [id] => 2
+)
+Array
+(
+ [id] => 3
+)
+===DONE===
diff --git a/ext/pgsql/tests/80_bug32223.phpt b/ext/pgsql/tests/80_bug32223.phpt
new file mode 100644
index 0000000..573742c
--- /dev/null
+++ b/ext/pgsql/tests/80_bug32223.phpt
@@ -0,0 +1,58 @@
+--TEST--
+Bug #32223 (weird behaviour of pg_last_notice)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+
+_skip_lc_messages();
+
+@pg_query($conn, "CREATE LANGUAGE 'plpgsql' HANDLER plpgsql_call_handler LANCOMPILER 'PL/pgSQL'");
+$res = @pg_query($conn, "CREATE OR REPLACE FUNCTION test_notice() RETURNS boolean AS '
+begin
+ RAISE NOTICE ''11111'';
+ return ''f'';
+end;
+' LANGUAGE plpgsql;");
+if (!$res) die('skip PLPGSQL not available');
+?>
+--FILE--
+<?php
+
+require_once('config.inc');
+require_once('lcmess.inc');
+
+$dbh = @pg_connect($conn_str);
+if (!$dbh) {
+ die ("Could not connect to the server");
+}
+
+_set_lc_messages();
+
+$res = pg_query($dbh, "CREATE OR REPLACE FUNCTION test_notice() RETURNS boolean AS '
+begin
+ RAISE NOTICE ''11111'';
+ return ''f'';
+end;
+' LANGUAGE plpgsql;");
+
+
+$res = pg_query($dbh, 'SELECT test_notice()');
+$row = pg_fetch_row($res, 0);
+var_dump($row);
+pg_free_result($res);
+if ($row[0] == 'f')
+{
+ var_dump(pg_last_notice($dbh));
+}
+
+pg_close($dbh);
+
+?>
+===DONE===
+--EXPECTF--
+array(1) {
+ [0]=>
+ string(1) "f"
+}
+string(14) "NOTICE: 11111"
+===DONE===
diff --git a/ext/pgsql/tests/80_bug32223b.phpt b/ext/pgsql/tests/80_bug32223b.phpt
new file mode 100644
index 0000000..aada3f0
--- /dev/null
+++ b/ext/pgsql/tests/80_bug32223b.phpt
@@ -0,0 +1,60 @@
+--TEST--
+Bug #32223 (weird behaviour of pg_last_notice using define)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+
+_skip_lc_messages();
+
+@pg_query($conn, "CREATE LANGUAGE 'plpgsql' HANDLER plpgsql_call_handler LANCOMPILER 'PL/pgSQL'");
+$res = @pg_query($conn, "CREATE OR REPLACE FUNCTION test_notice() RETURNS boolean AS '
+begin
+ RAISE NOTICE ''11111'';
+ return ''f'';
+end;
+' LANGUAGE plpgsql;");
+if (!$res) die('skip PLPGSQL not available');
+?>
+--FILE--
+<?php
+
+require_once('config.inc');
+require_once('lcmess.inc');
+
+define('dbh', pg_connect($conn_str));
+if (!dbh) {
+ die ("Could not connect to the server");
+}
+
+_set_lc_messages();
+
+$res = pg_query(dbh, "CREATE OR REPLACE FUNCTION test_notice() RETURNS boolean AS '
+begin
+ RAISE NOTICE ''11111'';
+ return ''f'';
+end;
+' LANGUAGE plpgsql;");
+
+function tester() {
+ $res = pg_query(dbh, 'SELECT test_notice()');
+ $row = pg_fetch_row($res, 0);
+ var_dump($row);
+ pg_free_result($res);
+ if ($row[0] == 'f')
+ {
+ var_dump(pg_last_notice(dbh));
+ }
+}
+tester();
+
+pg_close(dbh);
+
+?>
+===DONE===
+--EXPECTF--
+array(1) {
+ [0]=>
+ string(1) "f"
+}
+string(14) "NOTICE: 11111"
+===DONE===
diff --git a/ext/pgsql/tests/80_bug36625.phpt b/ext/pgsql/tests/80_bug36625.phpt
new file mode 100644
index 0000000..a95cea7
--- /dev/null
+++ b/ext/pgsql/tests/80_bug36625.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Bug #36625 (pg_trace() does not work)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+?>
+--FILE--
+<?php
+
+require_once('config.inc');
+
+$dbh = @pg_connect($conn_str);
+if (!$dbh) {
+ die ('Could not connect to the server');
+}
+
+$tracefile = dirname(__FILE__) . '/trace.tmp';
+
+@unlink($tracefile);
+var_dump(file_exists($tracefile));
+
+pg_trace($tracefile, 'w', $dbh);
+$res = pg_query($dbh, 'select 1');
+var_dump($res);
+pg_close($dbh);
+
+$found = 0;
+function search_trace_file($line)
+{
+ if (strpos($line, '"select 1"') !== false || strpos($line, "'select 1'") !== false) {
+ $GLOBALS['found']++;
+ }
+}
+
+$trace = file($tracefile);
+array_walk($trace, 'search_trace_file');
+var_dump($found > 0);
+var_dump(file_exists($tracefile));
+
+?>
+===DONE===
+--CLEAN--
+<?php unlink($tracefile); ?>
+--EXPECTF--
+bool(false)
+resource(%d) of type (pgsql result)
+bool(true)
+bool(true)
+===DONE===
diff --git a/ext/pgsql/tests/80_bug39971.phpt b/ext/pgsql/tests/80_bug39971.phpt
new file mode 100644
index 0000000..45d2631
--- /dev/null
+++ b/ext/pgsql/tests/80_bug39971.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug #39971 (pg_insert/pg_update do not allow now() to be used for timestamp fields)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+?>
+--FILE--
+<?php
+
+require_once('config.inc');
+
+$dbh = @pg_connect($conn_str);
+if (!$dbh) {
+ die ("Could not connect to the server");
+}
+
+pg_query("CREATE TABLE php_test (id SERIAL, tm timestamp NOT NULL)");
+
+$values = array('tm' => 'now()');
+pg_insert($dbh, 'php_test', $values);
+
+$ids = array('id' => 1);
+pg_update($dbh, 'php_test', $values, $ids);
+
+pg_query($dbh, "DROP TABLE php_test");
+pg_close($dbh);
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/ext/pgsql/tests/80_bug42783.phpt b/ext/pgsql/tests/80_bug42783.phpt
new file mode 100644
index 0000000..575e527
--- /dev/null
+++ b/ext/pgsql/tests/80_bug42783.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Bug #42783 (pg_insert() does not support an empty value array)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+?>
+--FILE--
+<?php
+
+require_once('config.inc');
+
+$dbh = @pg_connect($conn_str);
+if (!$dbh) {
+ die ("Could not connect to the server");
+}
+
+pg_query("CREATE TABLE php_test (id SERIAL PRIMARY KEY, time TIMESTAMP NOT NULL DEFAULT now())");
+
+pg_insert($dbh, 'php_test', array());
+
+var_dump(pg_fetch_assoc(pg_query("SELECT * FROM php_test")));
+
+pg_query($dbh, "DROP TABLE php_test");
+pg_close($dbh);
+?>
+===DONE===
+--EXPECTF--
+array(2) {
+ ["id"]=>
+ string(%d) "%d"
+ ["time"]=>
+ string(%d) "%s"
+}
+===DONE===
diff --git a/ext/pgsql/tests/98old_api.phpt b/ext/pgsql/tests/98old_api.phpt
new file mode 100644
index 0000000..b391097
--- /dev/null
+++ b/ext/pgsql/tests/98old_api.phpt
@@ -0,0 +1,33 @@
+--TEST--
+PostgreSQL old api
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+$result = pg_exec("SELECT * FROM ".$table_name);
+pg_numrows($result);
+pg_numfields($result);
+pg_fieldname($result, 0);
+pg_fieldsize($result, 0);
+pg_fieldtype($result, 0);
+pg_fieldprtlen($result, 0);
+pg_fieldisnull($result, 0);
+
+pg_result($result,0,0);
+$result = pg_exec("INSERT INTO ".$table_name." VALUES (7777, 'KKK')");
+$oid = pg_getlastoid($result);
+pg_freeresult($result);
+pg_errormessage();
+$result = pg_exec("UPDATE ".$table_name." SET str = 'QQQ' WHERE str like 'RGD';");
+pg_cmdtuples($result);
+
+
+
+echo "OK";
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/9999dropdb.phpt b/ext/pgsql/tests/9999dropdb.phpt
new file mode 100644
index 0000000..c60eeda
--- /dev/null
+++ b/ext/pgsql/tests/9999dropdb.phpt
@@ -0,0 +1,18 @@
+--TEST--
+PostgreSQL drop db
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+// drop test table
+
+include('config.inc');
+
+$db = pg_connect($conn_str);
+pg_query($db, "DROP TABLE ".$table_name);
+
+echo "OK";
+
+?>
+--EXPECT--
+OK
diff --git a/ext/pgsql/tests/README b/ext/pgsql/tests/README
new file mode 100644
index 0000000..1a29a34
--- /dev/null
+++ b/ext/pgsql/tests/README
@@ -0,0 +1,16 @@
+Test scripts assumes:
+ - PostgreSQL server is installed locally
+ - there is a PostgreSQL account for the users running the test scripts
+ - there is database named "test"
+
+For instance, if your login name is 'testuser', you should have PostgreSQL
+user account named 'testuser' and grant that user access to the database
+'test'.
+
+If you have account and database, type "createdb test" from command prompt
+to create the database to execute the test scripts. By executing the above
+command as the same user running the tests you ensure that the user is
+granted access to the database.
+
+If you find problems in PostgreSQL module, please mail to
+intenals@lists.php.net, helly@php.net or yohgaki@php.net.
diff --git a/ext/pgsql/tests/bug37100.phpt b/ext/pgsql/tests/bug37100.phpt
new file mode 100644
index 0000000..fa6b9ba
--- /dev/null
+++ b/ext/pgsql/tests/bug37100.phpt
@@ -0,0 +1,46 @@
+--TEST--
+Bug #37100 (data is returned truncated with BINARY CURSOR)
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '>=');
+?>
+--FILE--
+<?php
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+
+@pg_query('DROP TABLE test_bug');
+
+pg_query('CREATE TABLE test_bug (binfield byteA) ;');
+pg_query("INSERT INTO test_bug VALUES (decode('0103AA000812','hex'))");
+
+
+$data = pg_query("SELECT binfield FROM test_bug");
+$res = pg_fetch_result($data,0);
+var_dump($res);
+var_dump(bin2hex(pg_unescape_bytea($res)));
+
+$sql = "BEGIN; DECLARE mycursor BINARY CURSOR FOR SELECT binfield FROM test_bug; FETCH ALL IN mycursor;";
+
+$data = pg_query($sql);
+$res = pg_fetch_result($data,0);
+
+var_dump(strlen($res));
+var_dump(bin2hex($res));
+
+pg_close($db);
+
+$db = pg_connect($conn_str);
+pg_query('DROP TABLE test_bug');
+pg_close($db);
+
+
+?>
+--EXPECT--
+string(24) "\001\003\252\000\010\022"
+string(12) "0103aa000812"
+int(6)
+string(12) "0103aa000812"
diff --git a/ext/pgsql/tests/bug37100_85.phpt b/ext/pgsql/tests/bug37100_85.phpt
new file mode 100644
index 0000000..aa24776
--- /dev/null
+++ b/ext/pgsql/tests/bug37100_85.phpt
@@ -0,0 +1,46 @@
+--TEST--
+Bug #37100 (data is returned truncated with BINARY CURSOR) (8.5+)
+--SKIPIF--
+<?php
+include("skipif.inc");
+skip_server_version('8.5dev', '<');
+?>
+--FILE--
+<?php
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+
+@pg_query('DROP TABLE test_bug');
+
+pg_query('CREATE TABLE test_bug (binfield byteA) ;');
+pg_query("INSERT INTO test_bug VALUES (decode('0103AA000812','hex'))");
+
+
+$data = pg_query("SELECT binfield FROM test_bug");
+$res = pg_fetch_result($data,0);
+var_dump($res);
+var_dump(bin2hex(pg_unescape_bytea($res)));
+
+$sql = "BEGIN; DECLARE mycursor BINARY CURSOR FOR SELECT binfield FROM test_bug; FETCH ALL IN mycursor;";
+
+$data = pg_query($sql);
+$res = pg_fetch_result($data,0);
+
+var_dump(strlen($res));
+var_dump(bin2hex($res));
+
+pg_close($db);
+
+$db = pg_connect($conn_str);
+pg_query('DROP TABLE test_bug');
+pg_close($db);
+
+
+?>
+--EXPECT--
+string(14) "\x0103aa000812"
+string(12) "0103aa000812"
+int(6)
+string(12) "0103aa000812"
diff --git a/ext/pgsql/tests/bug47199.phpt b/ext/pgsql/tests/bug47199.phpt
new file mode 100644
index 0000000..5bfac0b
--- /dev/null
+++ b/ext/pgsql/tests/bug47199.phpt
@@ -0,0 +1,67 @@
+--TEST--
+Bug #47199 (pg_delete fails on NULL)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+?>
+--FILE--
+<?php
+
+require_once('config.inc');
+
+$dbh = pg_connect($conn_str);
+$tbl_name = 'test_47199';
+@pg_query("DROP TABLE $tbl_name");
+pg_query("CREATE TABLE $tbl_name (null_field INT, not_null_field INT NOT NULL)");
+
+pg_insert($dbh, $tbl_name, array('null_field' => null, 'not_null_field' => 1));
+pg_insert($dbh, $tbl_name, array('null_field' => null, 'not_null_field' => 2));
+
+var_dump(pg_fetch_all(pg_query('SELECT * FROM '. $tbl_name)));
+
+$query = pg_delete($dbh, $tbl_name, array('null_field' => NULL,'not_null_field' => 2), PGSQL_DML_STRING|PGSQL_DML_EXEC);
+
+echo $query, "\n";
+
+$query = pg_update($dbh, $tbl_name, array('null_field' => NULL, 'not_null_field' => 0), array('not_null_field' => 1, 'null_field' => ''), PGSQL_DML_STRING|PGSQL_DML_EXEC);
+
+echo $query, "\n";
+
+var_dump(pg_fetch_all(pg_query('SELECT * FROM '. $tbl_name)));
+
+@pg_query("DROP TABLE $tbl_name");
+pg_close($dbh);
+
+echo PHP_EOL."Done".PHP_EOL;
+
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ array(2) {
+ ["null_field"]=>
+ NULL
+ ["not_null_field"]=>
+ string(1) "1"
+ }
+ [1]=>
+ array(2) {
+ ["null_field"]=>
+ NULL
+ ["not_null_field"]=>
+ string(1) "2"
+ }
+}
+DELETE FROM test_47199 WHERE null_field IS NULL AND not_null_field=2;
+UPDATE test_47199 SET null_field=NULL,not_null_field=0 WHERE not_null_field=1 AND null_field IS NULL;
+array(1) {
+ [0]=>
+ array(2) {
+ ["null_field"]=>
+ NULL
+ ["not_null_field"]=>
+ string(1) "0"
+ }
+}
+
+Done \ No newline at end of file
diff --git a/ext/pgsql/tests/bug60244.phpt b/ext/pgsql/tests/bug60244.phpt
new file mode 100644
index 0000000..94568b6
--- /dev/null
+++ b/ext/pgsql/tests/bug60244.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Bug #60244 (pg_fetch_* functions do not validate that row param is >0)
+--SKIPIF--
+<?php
+include("skipif.inc");
+?>
+--FILE--
+<?php
+
+include 'config.inc';
+
+$db = pg_connect($conn_str);
+$result = pg_query("select 'a' union select 'b'");
+
+var_dump(pg_fetch_array($result, -1));
+var_dump(pg_fetch_assoc($result, -1));
+var_dump(pg_fetch_object($result, -1));
+var_dump(pg_fetch_row($result, -1));
+
+var_dump(pg_fetch_array($result, 0));
+var_dump(pg_fetch_assoc($result, 0));
+var_dump(pg_fetch_object($result, 0));
+var_dump(pg_fetch_row($result, 0));
+
+pg_close($db);
+
+?>
+--EXPECTF--
+Warning: pg_fetch_array(): The row parameter must be greater or equal to zero in %sbug60244.php on line %d
+bool(false)
+
+Warning: pg_fetch_assoc(): The row parameter must be greater or equal to zero in %sbug60244.php on line %d
+bool(false)
+
+Warning: pg_fetch_object(): The row parameter must be greater or equal to zero in %sbug60244.php on line %d
+bool(false)
+
+Warning: pg_fetch_row(): The row parameter must be greater or equal to zero in %sbug60244.php on line %d
+bool(false)
+array(2) {
+ [0]=>
+ string(1) "a"
+ ["?column?"]=>
+ string(1) "a"
+}
+array(1) {
+ ["?column?"]=>
+ string(1) "a"
+}
+object(stdClass)#1 (1) {
+ ["?column?"]=>
+ string(1) "a"
+}
+array(1) {
+ [0]=>
+ string(1) "a"
+}
diff --git a/ext/pgsql/tests/config.inc b/ext/pgsql/tests/config.inc
new file mode 100644
index 0000000..2b5f05a
--- /dev/null
+++ b/ext/pgsql/tests/config.inc
@@ -0,0 +1,12 @@
+<?php
+// These vars are used to connect db and create test table.
+// values can be set to meet your environment
+
+$conn_str = "host=localhost dbname=test"; // connection string
+$table_name = "php_pgsql_test"; // test table that should be exist
+$num_test_record = 1000; // Number of records to create
+
+$table_def = "CREATE TABLE php_pgsql_test (num int, str text, bin bytea);"; // Test table
+$field_name = "num"; // For pg_field_num()
+
+?> \ No newline at end of file
diff --git a/ext/pgsql/tests/lcmess.inc b/ext/pgsql/tests/lcmess.inc
new file mode 100644
index 0000000..6e0ac25
--- /dev/null
+++ b/ext/pgsql/tests/lcmess.inc
@@ -0,0 +1,21 @@
+<?php
+
+function _skip_lc_messages($lc_messages = 'C')
+{
+ if (!_set_lc_messages($lc_messages)) {
+ die("skip Cannot set LC_MESSAGES to '{$lc_messages}'\n");
+ }
+}
+
+function _set_lc_messages($lc_messages = 'C')
+{
+ if (pg_result(pg_query("SHOW LC_MESSAGES"), 0, 0) != $lc_messages) {
+ if (!@pg_exec("SET LC_MESSAGES='{$lc_messages}'")) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+?>
diff --git a/ext/pgsql/tests/pg_delete_001.phpt b/ext/pgsql/tests/pg_delete_001.phpt
new file mode 100644
index 0000000..abb65be
--- /dev/null
+++ b/ext/pgsql/tests/pg_delete_001.phpt
@@ -0,0 +1,86 @@
+--TEST--
+PostgreSQL pg_delete() - basic test using schema
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$conn = pg_connect($conn_str);
+
+pg_query('CREATE SCHEMA phptests');
+
+pg_query('CREATE TABLE foo (id INT, id2 INT)');
+pg_query('CREATE TABLE phptests.foo (id INT, id2 INT)');
+
+pg_insert($conn, 'foo', array('id' => 1, 'id2' => 1));
+pg_insert($conn, 'foo', array('id' => 1, 'id2' => 2));
+pg_insert($conn, 'foo', array('id' => 1, 'id2' => 2));
+pg_insert($conn, 'foo', array('id' => 3, 'id2' => 3));
+
+pg_insert($conn, 'phptests.foo', array('id' => 1, 'id2' => 1));
+pg_insert($conn, 'phptests.foo', array('id' => 1, 'id2' => 2));
+pg_insert($conn, 'phptests.foo', array('id' => 2, 'id2' => 3));
+pg_insert($conn, 'phptests.foo', array('id' => 2, 'id2' => 3));
+
+pg_delete($conn, 'foo', array('id' => 1, 'id2' => 0));
+pg_delete($conn, 'foo', array('id' => 1, 'id2' => 2));
+var_dump(pg_delete($conn, 'foo', array('id' => 1, 'id2' => 2), PGSQL_DML_STRING));
+
+pg_delete($conn, 'phptests.foo', array('id' => 2, 'id2' => 1));
+pg_delete($conn, 'phptests.foo', array('id' => 2, 'id2' => 3));
+var_dump(pg_delete($conn, 'phptests.foo', array('id' => 2, 'id2' => 3), PGSQL_DML_STRING));
+
+var_dump(pg_fetch_all(pg_query('SELECT * FROM foo')));
+var_dump(pg_fetch_all(pg_query('SELECT * FROM phptests.foo')));
+
+/* Inexistent */
+pg_delete($conn, 'bar', array('id' => 1, 'id2' => 2));
+var_dump(pg_delete($conn, 'bar', array('id' => 1, 'id2' => 2), PGSQL_DML_STRING));
+
+pg_query('DROP TABLE foo');
+pg_query('DROP TABLE phptests.foo');
+pg_query('DROP SCHEMA phptests');
+
+?>
+--EXPECTF--
+string(37) "DELETE FROM foo WHERE id=1 AND id2=2;"
+string(46) "DELETE FROM phptests.foo WHERE id=2 AND id2=3;"
+array(2) {
+ [0]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["id2"]=>
+ string(1) "1"
+ }
+ [1]=>
+ array(2) {
+ ["id"]=>
+ string(1) "3"
+ ["id2"]=>
+ string(1) "3"
+ }
+}
+array(2) {
+ [0]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["id2"]=>
+ string(1) "1"
+ }
+ [1]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["id2"]=>
+ string(1) "2"
+ }
+}
+
+Warning: pg_delete(): Table 'bar' doesn't exists in %s on line %d
+
+Warning: pg_delete(): Table 'bar' doesn't exists in %s on line %d
+bool(false)
diff --git a/ext/pgsql/tests/pg_insert_001.phpt b/ext/pgsql/tests/pg_insert_001.phpt
new file mode 100644
index 0000000..7d27219
--- /dev/null
+++ b/ext/pgsql/tests/pg_insert_001.phpt
@@ -0,0 +1,40 @@
+--TEST--
+PostgreSQL pg_select() - basic test using schema
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$conn = pg_connect($conn_str);
+
+pg_query('CREATE SCHEMA phptests');
+pg_query('CREATE TABLE phptests.foo (id INT, id2 INT)');
+
+
+pg_insert($conn, 'foo', array('id' => 1, 'id2' => 1));
+
+pg_insert($conn, 'phptests.foo', array('id' => 1, 'id2' => 2));
+
+var_dump(pg_insert($conn, 'phptests.foo', array('id' => 1, 'id2' => 2), PGSQL_DML_STRING));
+
+var_dump(pg_select($conn, 'phptests.foo', array('id' => 1)));
+
+pg_query('DROP TABLE phptests.foo');
+pg_query('DROP SCHEMA phptests');
+
+?>
+--EXPECTF--
+
+Warning: pg_insert(): Table 'foo' doesn't exists in %s on line %d
+string(47) "INSERT INTO phptests.foo (id,id2) VALUES (1,2);"
+array(1) {
+ [0]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["id2"]=>
+ string(1) "2"
+ }
+}
diff --git a/ext/pgsql/tests/pg_meta_data_001.phpt b/ext/pgsql/tests/pg_meta_data_001.phpt
new file mode 100644
index 0000000..2841de8
--- /dev/null
+++ b/ext/pgsql/tests/pg_meta_data_001.phpt
@@ -0,0 +1,92 @@
+--TEST--
+PostgreSQL pg_meta_data() - basic test using schema
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$conn = pg_connect($conn_str);
+
+pg_query('CREATE SCHEMA phptests');
+
+pg_query('CREATE TABLE phptests.foo (id INT, id2 INT)');
+
+pg_query('CREATE TABLE foo (id INT, id3 INT)');
+
+
+var_dump(pg_meta_data($conn, 'foo'));
+var_dump(pg_meta_data($conn, 'phptests.foo'));
+
+
+pg_query('DROP TABLE foo');
+pg_query('DROP TABLE phptests.foo');
+pg_query('DROP SCHEMA phptests');
+
+?>
+--EXPECT--
+array(2) {
+ ["id"]=>
+ array(6) {
+ ["num"]=>
+ int(1)
+ ["type"]=>
+ string(4) "int4"
+ ["len"]=>
+ int(4)
+ ["not null"]=>
+ bool(false)
+ ["has default"]=>
+ bool(false)
+ ["array dims"]=>
+ int(0)
+ }
+ ["id3"]=>
+ array(6) {
+ ["num"]=>
+ int(2)
+ ["type"]=>
+ string(4) "int4"
+ ["len"]=>
+ int(4)
+ ["not null"]=>
+ bool(false)
+ ["has default"]=>
+ bool(false)
+ ["array dims"]=>
+ int(0)
+ }
+}
+array(2) {
+ ["id"]=>
+ array(6) {
+ ["num"]=>
+ int(1)
+ ["type"]=>
+ string(4) "int4"
+ ["len"]=>
+ int(4)
+ ["not null"]=>
+ bool(false)
+ ["has default"]=>
+ bool(false)
+ ["array dims"]=>
+ int(0)
+ }
+ ["id2"]=>
+ array(6) {
+ ["num"]=>
+ int(2)
+ ["type"]=>
+ string(4) "int4"
+ ["len"]=>
+ int(4)
+ ["not null"]=>
+ bool(false)
+ ["has default"]=>
+ bool(false)
+ ["array dims"]=>
+ int(0)
+ }
+}
diff --git a/ext/pgsql/tests/pg_select_001.phpt b/ext/pgsql/tests/pg_select_001.phpt
new file mode 100644
index 0000000..9bcf130
--- /dev/null
+++ b/ext/pgsql/tests/pg_select_001.phpt
@@ -0,0 +1,63 @@
+--TEST--
+PostgreSQL pg_select() - basic test using schema
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$conn = pg_connect($conn_str);
+
+pg_query('CREATE SCHEMA phptests');
+
+pg_query('CREATE TABLE phptests.foo (id INT, id2 INT)');
+pg_query('INSERT INTO phptests.foo VALUES (1,2)');
+pg_query('INSERT INTO phptests.foo VALUES (2,3)');
+
+pg_query('CREATE TABLE phptests.bar (id4 INT, id3 INT)');
+pg_query('INSERT INTO phptests.bar VALUES (4,5)');
+pg_query('INSERT INTO phptests.bar VALUES (6,7)');
+
+/* Inexistent table */
+var_dump(pg_select($conn, 'foo', array('id' => 1)));
+
+/* Existent column */
+var_dump(pg_select($conn, 'phptests.foo', array('id' => 1)));
+
+/* Testing with inexistent column */
+var_dump(pg_select($conn, 'phptests.bar', array('id' => 1)));
+
+/* Existent column */
+var_dump(pg_select($conn, 'phptests.bar', array('id4' => 4)));
+
+
+pg_query('DROP TABLE phptests.foo');
+pg_query('DROP TABLE phptests.bar');
+pg_query('DROP SCHEMA phptests');
+
+?>
+--EXPECTF--
+Warning: pg_select(): Table 'foo' doesn't exists in %s on line %d
+bool(false)
+array(1) {
+ [0]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["id2"]=>
+ string(1) "2"
+ }
+}
+
+Notice: pg_select(): Invalid field name (id) in values in %s on line %d
+bool(false)
+array(1) {
+ [0]=>
+ array(2) {
+ ["id4"]=>
+ string(1) "4"
+ ["id3"]=>
+ string(1) "5"
+ }
+}
diff --git a/ext/pgsql/tests/pg_update_001.phpt b/ext/pgsql/tests/pg_update_001.phpt
new file mode 100644
index 0000000..95fa692
--- /dev/null
+++ b/ext/pgsql/tests/pg_update_001.phpt
@@ -0,0 +1,51 @@
+--TEST--
+PostgreSQL pg_update() - basic test using schema
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+
+include('config.inc');
+
+$conn = pg_connect($conn_str);
+
+pg_query('CREATE SCHEMA phptests');
+
+pg_query('CREATE TABLE foo (id INT, id2 INT)');
+pg_query('CREATE TABLE phptests.foo (id INT, id2 INT)');
+
+
+pg_insert($conn, 'foo', array('id' => 1, 'id2' => 1));
+pg_insert($conn, 'phptests.foo', array('id' => 1, 'id2' => 2));
+
+pg_update($conn, 'foo', array('id' => 10), array('id' => 1));
+var_dump(pg_update($conn, 'foo', array('id' => 10), array('id' => 1), PGSQL_DML_STRING));
+
+pg_update($conn, 'phptests.foo', array('id' => 100), array('id2' => 2));
+var_dump(pg_update($conn, 'phptests.foo', array('id' => 100), array('id2' => 2), PGSQL_DML_STRING));
+
+$rs = pg_query('SELECT * FROM foo UNION SELECT * FROM phptests.foo');
+while ($row = pg_fetch_assoc($rs)) {
+ var_dump($row);
+}
+
+pg_query('DROP TABLE foo');
+pg_query('DROP TABLE phptests.foo');
+pg_query('DROP SCHEMA phptests');
+
+?>
+--EXPECT--
+string(32) "UPDATE foo SET id=10 WHERE id=1;"
+string(43) "UPDATE phptests.foo SET id=100 WHERE id2=2;"
+array(2) {
+ ["id"]=>
+ string(2) "10"
+ ["id2"]=>
+ string(1) "1"
+}
+array(2) {
+ ["id"]=>
+ string(3) "100"
+ ["id2"]=>
+ string(1) "2"
+}
diff --git a/ext/pgsql/tests/php.gif b/ext/pgsql/tests/php.gif
new file mode 100644
index 0000000..7beda43
--- /dev/null
+++ b/ext/pgsql/tests/php.gif
Binary files differ
diff --git a/ext/pgsql/tests/skipif.inc b/ext/pgsql/tests/skipif.inc
new file mode 100644
index 0000000..7c5153e
--- /dev/null
+++ b/ext/pgsql/tests/skipif.inc
@@ -0,0 +1,32 @@
+<?php
+// This script prints "skip" unless:
+// * the pgsql extension is built-in or loadable, AND
+// * there is a database called "test" accessible
+// with no username/password, AND
+// * we have create/drop privileges on the entire "test"
+// database
+
+include("config.inc");
+include("lcmess.inc");
+
+if (!extension_loaded("pgsql")) {
+ die("skip\n");
+}
+$conn = @pg_connect($conn_str);
+if (!is_resource($conn)) {
+ die("skip could not connect\n");
+}
+
+function skip_server_version($version, $op = '<') { _skip_version('server', $version, $op); }
+function skip_client_version($version, $op = '<') { _skip_version('client', $version, $op); }
+
+
+function _skip_version($type, $version, $op)
+{
+ $pg = pg_parameter_status($type.'_version');
+ if (version_compare($pg, $version, $op)) {
+ die("skip {$type} version {$pg} is {$op} {$version}\n");
+ }
+}
+
+?>