diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-03-14 05:42:27 +0000 |
---|---|---|
committer | <> | 2013-04-03 16:25:08 +0000 |
commit | c4dd7a1a684490673e25aaf4fabec5df138854c4 (patch) | |
tree | 4d57c44caae4480efff02b90b9be86f44bf25409 /ext/pgsql | |
download | php2-master.tar.gz |
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/pgsql')
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 *)¬ice->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 **)¬ice, 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, ¶m, &len) == SUCCESS) { + id = -1; + } else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", ¶m, &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 **)¬ice) == 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 **)¬_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 Binary files differnew file mode 100644 index 0000000..7beda43 --- /dev/null +++ b/ext/pgsql/tests/php.gif 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"); + } +} + +?> |