diff options
Diffstat (limited to 'ext/pdo_oci')
32 files changed, 3573 insertions, 0 deletions
diff --git a/ext/pdo_oci/CREDITS b/ext/pdo_oci/CREDITS new file mode 100755 index 0000000..63e863a --- /dev/null +++ b/ext/pdo_oci/CREDITS @@ -0,0 +1,2 @@ +Oracle (OCI) driver for PDO +Wez Furlong diff --git a/ext/pdo_oci/EXPERIMENTAL b/ext/pdo_oci/EXPERIMENTAL new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ext/pdo_oci/EXPERIMENTAL diff --git a/ext/pdo_oci/config.m4 b/ext/pdo_oci/config.m4 new file mode 100755 index 0000000..309bde8 --- /dev/null +++ b/ext/pdo_oci/config.m4 @@ -0,0 +1,241 @@ +dnl $Id$ +dnl config.m4 for extension pdo_oci +dnl vim:et:sw=2:ts=2: + +SUPPORTED_LIB_VERS="9.0 10.1 11.1" # This caters for all Oracle 9.x, 10.x and 11.1 installs +AC_DEFUN([AC_PDO_OCI_VERSION],[ + AC_MSG_CHECKING([Oracle version]) + for OCI_VER in $SUPPORTED_LIB_VERS; do + if test -f $PDO_OCI_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME.$OCI_VER; then + PDO_OCI_VERSION="$OCI_VER" + fi + done + if test -z "$PDO_OCI_VERSION"; then + AC_MSG_ERROR([Oracle required OCI8 libraries not found under $PDO_OCI_DIR]) + fi + AC_MSG_RESULT($PDO_OCI_VERSION) +]) + +AC_DEFUN([AC_PDO_OCI_CHECK_LIB_DIR],[ + AC_CHECK_SIZEOF(long int, 4) + AC_MSG_CHECKING([if we're on a 64-bit platform]) + if test "$ac_cv_sizeof_long_int" = "4" ; then + AC_MSG_RESULT([no]) + TMP_PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib32" + else + AC_MSG_RESULT([yes]) + TMP_PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib" + fi + + AC_MSG_CHECKING([OCI8 libraries dir]) + if test -d "$PDO_OCI_DIR/lib" && test ! -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib" + elif test ! -d "$PDO_OCI_DIR/lib" && test -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib32" + elif test -d "$PDO_OCI_DIR/lib" && test -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR=$TMP_PDO_OCI_LIB_DIR + else + AC_MSG_ERROR([Oracle required OCI8 libraries not found]) + fi + AC_MSG_RESULT($PDO_OCI_LIB_DIR) +]) + +PHP_ARG_WITH(pdo-oci, Oracle OCI support for PDO, +[ --with-pdo-oci[=DIR] PDO: Oracle OCI support. DIR defaults to \$ORACLE_HOME. + Use --with-pdo-oci=instantclient,prefix,version + for an Oracle Instant Client SDK. + For example on Linux with 11.2 RPMs use: + --with-pdo-oci=instantclient,/usr,11.2 + With 10.2 RPMs use: + --with-pdo-oci=instantclient,/usr,10.2.0.4]) + +if test "$PHP_PDO_OCI" != "no"; then + + if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then + AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.]) + fi + + AC_MSG_CHECKING([Oracle Install-Dir]) + if test "$PHP_PDO_OCI" = "yes" || test -z "$PHP_PDO_OCI"; then + PDO_OCI_DIR=$ORACLE_HOME + else + PDO_OCI_DIR=$PHP_PDO_OCI + fi + AC_MSG_RESULT($PHP_PDO_OCI) + + AC_MSG_CHECKING([if that is sane]) + if test -z "$PDO_OCI_DIR"; then + AC_MSG_ERROR([ +You need to tell me where to find your Oracle Instant Client SDK, or set ORACLE_HOME. +]) + else + AC_MSG_RESULT([yes]) + fi + + if test "instantclient" = "`echo $PDO_OCI_DIR | cut -d, -f1`" ; then + AC_CHECK_SIZEOF(long int, 4) + if test "$ac_cv_sizeof_long_int" = "4" ; then + PDO_OCI_CLIENT_DIR="client" + else + PDO_OCI_CLIENT_DIR="client64" + fi + PDO_OCI_IC_PREFIX="`echo $PDO_OCI_DIR | cut -d, -f2`" + PDO_OCI_IC_VERS="`echo $PDO_OCI_DIR | cut -d, -f3`" + if test -n "$PDO_OCI_IC_VERS"; then + PDO_OCI_IC_MAJ_VER="`echo $PDO_OCI_IC_VERS | cut -d. -f1`" + if test "$PDO_OCI_IC_MAJ_VER" -ge 11; then + # From 11.1.0.7 the RPM path only has an X.Y component + PDO_OCI_IC_VERS="`echo $PDO_OCI_IC_VERS | cut -d. -f1-2`" + fi + fi + AC_MSG_CHECKING([for oci.h]) + if test -f $PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/oci.h ; then + PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR) + AC_MSG_RESULT($PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR) + elif test -f $PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/include/oci.h ; then + PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/include) + AC_MSG_RESULT($PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/include) + elif test -f $PDO_OCI_IC_PREFIX/sdk/include/oci.h ; then + PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/sdk/include) + AC_MSG_RESULT($PDO_OCI_IC_PREFIX/sdk/include) + elif test -f $PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/include/oci.h ; then + PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/include) + AC_MSG_RESULT($PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/include) + else + AC_MSG_ERROR([I'm too dumb to figure out where the include dir is in your Instant Client install]) + fi + if test -f "$PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME" ; then + PDO_OCI_LIB_DIR="$PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/lib" + elif test -f "$PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME" ; then + PDO_OCI_LIB_DIR="$PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/lib" + elif test -f "$PDO_OCI_IC_PREFIX/libclntsh.$SHLIB_SUFFIX_NAME" ; then + PDO_OCI_LIB_DIR="$PDO_OCI_IC_PREFIX" + else + AC_MSG_ERROR([I'm too dumb to figure out where the libraries are in your Instant Client install]) + fi + PDO_OCI_VERSION="`echo $PDO_OCI_IC_VERS | cut -d. -f1-2`" + else + AC_PDO_OCI_CHECK_LIB_DIR($PDO_OCI_DIR) + + if test -d "$PDO_OCI_DIR/rdbms/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/public" + fi + if test -d "$PDO_OCI_DIR/rdbms/demo"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/demo) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/demo" + fi + if test -d "$PDO_OCI_DIR/network/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/network/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/network/public" + fi + if test -d "$PDO_OCI_DIR/plsql/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/plsql/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/plsql/public" + fi + if test -d "$PDO_OCI_DIR/include"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/include) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/include" + fi + + if test -f "$PDO_OCI_LIB_DIR/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $PDO_OCI_LIB_DIR/sysliblist`, PDO_OCI_SYSLIB) + elif test -f "$PDO_OCI_DIR/rdbms/lib/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $PDO_OCI_DIR/rdbms/lib/sysliblist`, PDO_OCI_SYSLIB) + fi + AC_PDO_OCI_VERSION($PDO_OCI_DIR) + fi + + case $PDO_OCI_VERSION in + 9.0|10.1|10.2|11.1|11.2) + PHP_ADD_LIBRARY(clntsh, 1, PDO_OCI_SHARED_LIBADD) + ;; + + *) + AC_MSG_ERROR(Unsupported Oracle version $PDO_OCI_VERSION) + ;; + esac + + PHP_ADD_LIBPATH($PDO_OCI_LIB_DIR, PDO_OCI_SHARED_LIBADD) + + PHP_CHECK_LIBRARY(clntsh, OCIEnvCreate, + [ + AC_DEFINE(HAVE_OCIENVCREATE,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD + ]) + + PHP_CHECK_LIBRARY(clntsh, OCIEnvNlsCreate, + [ + AC_DEFINE(HAVE_OCIENVNLSCREATE,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD + ]) + + dnl + dnl Check if we need to add -locijdbc8 + dnl + PHP_CHECK_LIBRARY(clntsh, OCILobIsTemporary, + [ + AC_DEFINE(HAVE_OCILOBISTEMPORARY,1,[ ]) + ], [ + PHP_CHECK_LIBRARY(ocijdbc8, OCILobIsTemporary, + [ + PHP_ADD_LIBRARY(ocijdbc8, 1, PDO_OCI_SHARED_LIBADD) + AC_DEFINE(HAVE_OCILOBISTEMPORARY,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD + ]) + ], [ + -L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD + ]) + + dnl + dnl Check if we have collections + dnl + PHP_CHECK_LIBRARY(clntsh, OCICollAssign, + [ + AC_DEFINE(HAVE_OCICOLLASSIGN,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD + ]) + + dnl Scrollable cursors? + PHP_CHECK_LIBRARY(clntsh, OCIStmtFetch2, + [ + AC_DEFINE(HAVE_OCISTMTFETCH2,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $PDO_OCI_SHARED_LIBADD + ]) + + ifdef([PHP_CHECK_PDO_INCLUDES], + [ + PHP_CHECK_PDO_INCLUDES + ],[ + AC_MSG_CHECKING([for PDO includes]) + if test -f $abs_srcdir/include/php/ext/pdo/php_pdo_driver.h; then + pdo_cv_inc_path=$abs_srcdir/ext + elif test -f $abs_srcdir/ext/pdo/php_pdo_driver.h; then + pdo_cv_inc_path=$abs_srcdir/ext + elif test -f $prefix/include/php/ext/pdo/php_pdo_driver.h; then + pdo_cv_inc_path=$prefix/include/php/ext + else + AC_MSG_ERROR([Cannot find php_pdo_driver.h.]) + fi + AC_MSG_RESULT($pdo_cv_inc_path) + ]) + + PHP_NEW_EXTENSION(pdo_oci, pdo_oci.c oci_driver.c oci_statement.c, $ext_shared,,-I$pdo_cv_inc_path) + + PHP_SUBST_OLD(PDO_OCI_SHARED_LIBADD) + PHP_SUBST_OLD(PDO_OCI_DIR) + PHP_SUBST_OLD(PDO_OCI_VERSION) + + ifdef([PHP_ADD_EXTENSION_DEP], + [ + PHP_ADD_EXTENSION_DEP(pdo_oci, pdo) + ]) + + AC_DEFINE_UNQUOTED(PHP_PDO_OCI_CLIENT_VERSION, "$PDO_OCI_VERSION", [ ]) +fi + diff --git a/ext/pdo_oci/config.w32 b/ext/pdo_oci/config.w32 new file mode 100755 index 0000000..0edf885 --- /dev/null +++ b/ext/pdo_oci/config.w32 @@ -0,0 +1,46 @@ +// $Id$ +// vim:ft=javascript + +ARG_WITH("pdo-oci", "Oracle OCI support for PDO", "no"); + +if (PHP_PDO_OCI != "no") { + + pdo_oci_dirs = new Array( + PHP_PDO_OCI, + PHP_PDO_OCI + "\\oci", + PHP_PHP_BUILD + "\\instantclient10_*\\sdk", + PHP_PHP_BUILD + "\\oci92" + ); + + pdo_oci_lib_paths = ""; + pdo_oci_inc_paths = ""; + + // find the oracle install + for (i = 0; i < pdo_oci_dirs.length; i++) { + pdo_oci_lib_paths += pdo_oci_dirs[i] + "\\lib;"; + pdo_oci_lib_paths += pdo_oci_dirs[i] + "\\lib\\msvc;"; + pdo_oci_inc_paths += pdo_oci_dirs[i] + "\\include;"; + } + + pdo_oci_inc_paths += PHP_PHP_BUILD + "\\include\\instantclient;" + pdo_oci_lib_paths += PHP_PHP_BUILD + "\\lib\\instantclient;"; + + pdo_oci_header = CHECK_HEADER_ADD_INCLUDE("oci.h", "CFLAGS_PDO_OCI", pdo_oci_inc_paths, null, null, true); + + if (pdo_oci_header && CHECK_LIB("oci.lib", "pdo_oci", pdo_oci_lib_paths)) { + + pdo_oci_inc_dir = FSO.GetParentFolderName(pdo_oci_header); + + EXTENSION('pdo_oci', 'pdo_oci.c oci_driver.c oci_statement.c'); + + /* probe for some functions not present in older versions */ + pdo_oci_inc_dir = FSO.GetFolder(pdo_oci_header); + CHECK_FUNC_IN_HEADER('oci.h', 'OCIEnvCreate', pdo_oci_inc_dir, 'CFLAGS_PDO_OCI'); + CHECK_FUNC_IN_HEADER('ociap.h', 'OCIStmtFetch2', pdo_oci_inc_dir, 'CFLAGS_PDO_OCI'); + CHECK_FUNC_IN_HEADER('ociap.h', 'OCIEnvNlsCreate', pdo_oci_inc_dir, 'CFLAGS_PDO_OCI'); + + } else { + WARNING("pdo-oci not enabled; libraries and headers not found"); + } + ADD_EXTENSION_DEP('pdo_oci', 'pdo'); +} diff --git a/ext/pdo_oci/oci_driver.c b/ext/pdo_oci/oci_driver.c new file mode 100644 index 0000000..5497beb --- /dev/null +++ b/ext/pdo_oci/oci_driver.c @@ -0,0 +1,740 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong <wez@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "php_pdo_oci.h" +#include "php_pdo_oci_int.h" +#include "Zend/zend_exceptions.h" + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch); + +static int pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + pdo_oci_error_info *einfo; + + einfo = &H->einfo; + + if (stmt) { + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + + if (S->einfo.errmsg) { + einfo = &S->einfo; + } + } + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg, 1); + } + + return 1; +} +/* }}} */ + +ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line TSRMLS_DC) /* {{{ */ +{ + text errbuf[1024] = "<<Unknown>>"; + char tmp_buf[2048]; + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + pdo_oci_error_info *einfo; + pdo_oci_stmt *S = NULL; + pdo_error_type *pdo_err = &dbh->error_code; + + if (stmt) { + S = (pdo_oci_stmt*)stmt->driver_data; + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } + else { + einfo = &H->einfo; + } + + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + + einfo->errmsg = NULL; + einfo->errcode = 0; + einfo->file = file; + einfo->line = line; + + if (isinit) { /* Initialization error */ + strcpy(*pdo_err, "HY000"); + slprintf(tmp_buf, sizeof(tmp_buf), "%s (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + } + else { + switch (status) { + case OCI_SUCCESS: + strcpy(*pdo_err, "00000"); + break; + case OCI_ERROR: + OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_SUCCESS_WITH_INFO: + OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NEED_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NEED_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_NO_DATA: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_NO_DATA (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_INVALID_HANDLE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_STILL_EXECUTING: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + case OCI_CONTINUE: + slprintf(tmp_buf, sizeof(tmp_buf), "%s: OCI_CONTINUE (%s:%d)", what, file, line); + einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent); + break; + } + + if (einfo->errcode) { + switch (einfo->errcode) { + case 1013: /* user requested cancel of current operation */ + zend_bailout(); + break; + +#if 0 + case 955: /* ORA-00955: name is already used by an existing object */ + *pdo_err = PDO_ERR_ALREADY_EXISTS; + break; +#endif + + case 12154: /* ORA-12154: TNS:could not resolve service name */ + strcpy(*pdo_err, "42S02"); + break; + + case 22: /* ORA-00022: invalid session id */ + case 378: + case 602: + case 603: + case 604: + case 609: + case 1012: /* ORA-01012: */ + case 1033: + case 1041: + case 1043: + case 1089: + case 1090: + case 1092: + case 3113: /* ORA-03133: end of file on communication channel */ + case 3114: + case 3122: + case 3135: + case 12153: + case 27146: + case 28511: + /* consider the connection closed */ + dbh->is_closed = 1; + H->attached = 0; + strcpy(*pdo_err, "01002"); /* FIXME */ + break; + + default: + strcpy(*pdo_err, "HY000"); + } + } + + if (stmt) { + /* always propogate the error code back up to the dbh, + * so that we can catch the error information when execute + * is called via query. See Bug #33707 */ + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + } + H->einfo = *einfo; + H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL; + strcpy(dbh->error_code, stmt->error_code); + } + } + + /* little mini hack so that we can use this code from the dbh ctor */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode TSRMLS_CC, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); + } + + return einfo->errcode; +} +/* }}} */ + +static int oci_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + + if (H->svc) { + /* rollback any outstanding work */ + OCITransRollback(H->svc, H->err, 0); + } + + if (H->session) { + OCIHandleFree(H->session, OCI_HTYPE_SESSION); + H->session = NULL; + } + + if (H->svc) { + OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX); + H->svc = NULL; + } + + if (H->server && H->attached) { + H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCIServerDetach"); + } + H->attached = 0; + } + + if (H->server) { + OCIHandleFree(H->server, OCI_HTYPE_SERVER); + H->server = NULL; + } + + if (H->err) { + OCIHandleFree(H->err, OCI_HTYPE_ERROR); + H->err = NULL; + } + + if (H->charset && H->env) { + OCIHandleFree(H->env, OCI_HTYPE_ENV); + H->env = NULL; + } + + if (H->einfo.errmsg) { + pefree(H->einfo.errmsg, dbh->is_persistent); + H->einfo.errmsg = NULL; + } + + pefree(H, dbh->is_persistent); + + return 0; +} +/* }}} */ + +static int oci_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); + ub4 prefetch; + char *nsql = NULL; + int nsql_len = 0; + int ret; + +#if HAVE_OCISTMTFETCH2 + S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, + PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL ? + OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT; +#else + S->exec_type = OCI_DEFAULT; +#endif + + S->H = H; + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + sql_len = nsql_len; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + efree(S); + return 0; + } + + /* create an OCI statement handle */ + OCIHandleAlloc(H->env, (dvoid*)&S->stmt, OCI_HTYPE_STMT, 0, NULL); + + /* and our own private error handle */ + OCIHandleAlloc(H->env, (dvoid*)&S->err, OCI_HTYPE_ERROR, 0, NULL); + + if (sql_len) { + H->last_err = OCIStmtPrepare(S->stmt, H->err, (text*)sql, sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); + if (nsql) { + efree(nsql); + nsql = NULL; + } + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + efree(S); + return 0; + } + + } + + prefetch = pdo_oci_sanitize_prefetch(pdo_attr_lval(driver_options, PDO_ATTR_PREFETCH, PDO_OCI_PREFETCH_DEFAULT TSRMLS_CC)); + if (prefetch) { + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, + OCI_ATTR_PREFETCH_ROWS, H->err); + if (!H->last_err) { + prefetch *= PDO_OCI_PREFETCH_ROWSIZE; + H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, + OCI_ATTR_PREFETCH_MEMORY, H->err); + } + } + + stmt->driver_data = S; + stmt->methods = &oci_stmt_methods; + if (nsql) { + efree(nsql); + nsql = NULL; + } + + return 1; +} +/* }}} */ + +static long oci_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + OCIStmt *stmt; + ub2 stmt_type; + ub4 rowcount; + int ret = -1; + + OCIHandleAlloc(H->env, (dvoid*)&stmt, OCI_HTYPE_STMT, 0, NULL); + + H->last_err = OCIStmtPrepare(stmt, H->err, (text*)sql, sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(stmt, OCI_HTYPE_STMT); + return -1; + } + + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); + + if (stmt_type == OCI_STMT_SELECT) { + /* invalid usage; cancel it */ + OCIHandleFree(stmt, OCI_HTYPE_STMT); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "issuing a SELECT query here is invalid"); + return -1; + } + + /* now we are good to go */ + H->last_err = OCIStmtExecute(H->svc, stmt, H->err, 1, 0, NULL, NULL, + (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); + + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtExecute"); + } else { + /* return the number of affected rows */ + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); + ret = rowcount; + } + + OCIHandleFree(stmt, OCI_HTYPE_STMT); + + return ret; +} +/* }}} */ + +static int oci_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC) /* {{{ */ +{ + int qcount = 0; + char const *cu, *l, *r; + char *c; + + if (!unquotedlen) { + *quotedlen = 2; + *quoted = emalloc(*quotedlen+1); + strcpy(*quoted, "''"); + return 1; + } + + /* count single quotes */ + for (cu = unquoted; (cu = strchr(cu,'\'')); qcount++, cu++) + ; /* empty loop */ + + *quotedlen = unquotedlen + qcount + 2; + *quoted = c = emalloc(*quotedlen+1); + *c++ = '\''; + + /* foreach (chunk that ends in a quote) */ + for (l = unquoted; (r = strchr(l,'\'')); l = r+1) { + strncpy(c, l, r-l+1); + c += (r-l+1); + *c++ = '\''; /* add second quote */ + } + + /* Copy remainder and add enclosing quote */ + strncpy(c, l, *quotedlen-(c-*quoted)-1); + (*quoted)[*quotedlen-1] = '\''; + (*quoted)[*quotedlen] = '\0'; + + return 1; +} +/* }}} */ + +static int oci_handle_begin(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ +{ + /* with Oracle, there is nothing special to be done */ + return 1; +} +/* }}} */ + +static int oci_handle_commit(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return 0; + } + return 1; +} +/* }}} */ + +static int oci_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + + H->last_err = OCITransRollback(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransRollback"); + return 0; + } + return 1; +} +/* }}} */ + +static int oci_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + + if (attr == PDO_ATTR_AUTOCOMMIT) { + if (dbh->in_txn) { + /* Assume they want to commit whatever is outstanding */ + H->last_err = OCITransCommit(H->svc, H->err, 0); + + if (H->last_err) { + H->last_err = oci_drv_error("OCITransCommit"); + return 0; + } + dbh->in_txn = 0; + } + + convert_to_long(val); + + dbh->auto_commit = Z_LVAL_P(val); + return 1; + } else { + return 0; + } + +} +/* }}} */ + +static int oci_handle_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_SERVER_INFO: + { + text infostr[512]; + char verstr[15]; + ub4 vernum; + + if (OCIServerRelease(H->svc, H->err, infostr, (ub4)sizeof(infostr), (ub1)OCI_HTYPE_SVCCTX, &vernum)) + { + ZVAL_STRING(return_value, "<<Unknown>>", 1); + } else { + if (attr == PDO_ATTR_SERVER_INFO) { + ZVAL_STRING(return_value, (char *)infostr, 1); + } else { + slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", + (int)((vernum>>24) & 0xFF), /* version number */ + (int)((vernum>>20) & 0x0F), /* release number*/ + (int)((vernum>>12) & 0xFF), /* update number */ + (int)((vernum>>8) & 0x0F), /* port release number */ + (int)((vernum>>0) & 0xFF)); /* port update number */ + + ZVAL_STRING(return_value, verstr, 1); + } + } + return TRUE; + } + + case PDO_ATTR_CLIENT_VERSION: + { +#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2) + /* Run time client version */ + sword major, minor, update, patch, port_update; + char verstr[15]; + + OCIClientVersion(&major, &minor, &update, &patch, &port_update); + slprintf(verstr, sizeof(verstr), "%d.%d.%d.%d.%d", major, minor, update, patch, port_update); + ZVAL_STRING(return_value, verstr, 1); +#elif defined(PHP_PDO_OCI_CLIENT_VERSION) + /* Compile time client version */ + ZVAL_STRING(return_value, PHP_PDO_OCI_CLIENT_VERSION, 1); +#else + return FALSE; + +#endif /* Check for OCIClientVersion() support */ + + return TRUE; + } + + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(return_value, dbh->auto_commit); + return TRUE; + + default: + return FALSE; + + } + return FALSE; + +} +/* }}} */ + +static int pdo_oci_check_liveness(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + sb4 error_code = 0; + char version[256]; + + /* TODO move attached check to PDO level */ + if (H->attached == 0) { + return FAILURE; + } + /* TODO add persistent_timeout check at PDO level */ + + + /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) + * such as from Pre-10.1 servers, the error is still from the server and we would have + * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for + * Pre-10.2 clients + */ +#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ + H->last_err = OCIPing (H->svc, H->err, OCI_DEFAULT); +#else + /* use good old OCIServerVersion() */ + H->last_err = OCIServerVersion (H->svc, H->err, (text *)version, sizeof(version), OCI_HTYPE_SVCCTX); +#endif + if (H->last_err == OCI_SUCCESS) { + return SUCCESS; + } + + OCIErrorGet (H->err, (ub4)1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR); + + if (error_code == 1010) { + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +static struct pdo_dbh_methods oci_methods = { + oci_handle_closer, + oci_handle_preparer, + oci_handle_doer, + oci_handle_quoter, + oci_handle_begin, + oci_handle_commit, + oci_handle_rollback, + oci_handle_set_attribute, + NULL, + pdo_oci_fetch_error_func, + oci_handle_get_attribute, + pdo_oci_check_liveness, /* check_liveness */ + NULL /* get_driver_methods */ +}; + +static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */ +{ + pdo_oci_db_handle *H; + int i, ret = 0; + struct pdo_data_src_parser vars[] = { + { "charset", NULL, 0 }, + { "dbname", "", 0 } + }; + + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 2); + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + dbh->driver_data = H; + + /* allocate an environment */ +#if HAVE_OCIENVNLSCREATE + if (vars[0].optval) { + H->charset = OCINlsCharSetNameToId(pdo_oci_Env, (const oratext *)vars[0].optval); + if (!H->charset) { + oci_init_error("OCINlsCharSetNameToId: unknown character set name"); + goto cleanup; + } else { + if (OCIEnvNlsCreate(&H->env, PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) != OCI_SUCCESS) { + oci_init_error("OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle libraries and NLS data"); + goto cleanup; + } + } + } +#endif + if (H->env == NULL) { + /* use the global environment */ + H->env = pdo_oci_Env; + } + + /* something to hold errors */ + OCIHandleAlloc(H->env, (dvoid **)&H->err, OCI_HTYPE_ERROR, 0, NULL); + + /* handle for the server */ + OCIHandleAlloc(H->env, (dvoid **)&H->server, OCI_HTYPE_SERVER, 0, NULL); + + H->last_err = OCIServerAttach(H->server, H->err, (text*)vars[1].optval, + strlen(vars[1].optval), OCI_DEFAULT); + + if (H->last_err) { + oci_drv_error("pdo_oci_handle_factory"); + goto cleanup; + } + + H->attached = 1; + + /* create a service context */ + H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->svc, OCI_HTYPE_SVCCTX, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); + goto cleanup; + } + + H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->session, OCI_HTYPE_SESSION, 0, NULL); + if (H->last_err) { + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); + goto cleanup; + } + + /* set server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); + goto cleanup; + } + + /* username */ + if (dbh->username) { + H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION, + dbh->username, strlen(dbh->username), + OCI_ATTR_USERNAME, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); + goto cleanup; + } + } + + /* password */ + if (dbh->password) { + H->last_err = OCIAttrSet(H->session, OCI_HTYPE_SESSION, + dbh->password, strlen(dbh->password), + OCI_ATTR_PASSWORD, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); + goto cleanup; + } + } + + /* Now fire up the session */ + H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); + if (H->last_err) { + oci_drv_error("OCISessionBegin"); + goto cleanup; + } + + /* set the server handle into service handle */ + H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); + if (H->last_err) { + oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION"); + goto cleanup; + } + + dbh->methods = &oci_methods; + dbh->alloc_own_columns = 1; + dbh->native_case = PDO_CASE_UPPER; + + ret = 1; + +cleanup: + for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) { + if (vars[i].freeme) { + efree(vars[i].optval); + } + } + + if (!ret) { + oci_handle_closer(dbh TSRMLS_CC); + } + + return ret; +} +/* }}} */ + +pdo_driver_t pdo_oci_driver = { + PDO_DRIVER_HEADER(oci), + pdo_oci_handle_factory +}; + +static inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */ +{ + if (prefetch < 0) { + prefetch = 0; + } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) { + prefetch = PDO_OCI_PREFETCH_DEFAULT; + } + return ((ub4)prefetch); +} +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/pdo_oci/oci_statement.c b/ext/pdo_oci/oci_statement.c new file mode 100644 index 0000000..dcb9557 --- /dev/null +++ b/ext/pdo_oci/oci_statement.c @@ -0,0 +1,767 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong <wez@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "php_pdo_oci.h" +#include "php_pdo_oci_int.h" +#include "Zend/zend_extensions.h" + +#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ + +#define STMT_CALL(name, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__ TSRMLS_CC); \ + if (S->last_err) { \ + return 0; \ + } \ + } while(0) + +#define STMT_CALL_MSG(name, msg, params) \ + do { \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__ TSRMLS_CC); \ + if (S->last_err) { \ + return 0; \ + } \ + } while(0) + +static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob TSRMLS_DC); + +static int oci_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + HashTable *BC = stmt->bound_columns; + HashTable *BP = stmt->bound_params; + + int i; + + if (S->stmt) { + /* cancel server side resources for the statement if we didn't + * fetch it all */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + + /* free the handle */ + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + S->stmt = NULL; + } + if (S->err) { + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + S->err = NULL; + } + + /* need to ensure these go away now */ + if (BC) { + zend_hash_destroy(BC); + FREE_HASHTABLE(stmt->bound_columns); + stmt->bound_columns = NULL; + } + + if (BP) { + zend_hash_destroy(BP); + FREE_HASHTABLE(stmt->bound_params); + stmt->bound_params = NULL; + } + + if (S->einfo.errmsg) { + pefree(S->einfo.errmsg, stmt->dbh->is_persistent); + S->einfo.errmsg = NULL; + } + + if (S->cols) { + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + S->cols = NULL; + } + efree(S); + + stmt->driver_data = NULL; + + return 1; +} /* }}} */ + +static int oci_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + ub4 rowcount; + b4 mode; + + if (!S->stmt_type) { + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_STMT_TYPE", + (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); + } + + if (stmt->executed) { + /* ensure that we cancel the cursor from a previous fetch */ + OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + } + +#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ + if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { + mode = OCI_STMT_SCROLLABLE_READONLY; + } else +#endif + if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { + mode = OCI_COMMIT_ON_SUCCESS; + } else { + mode = OCI_DEFAULT; + } + + STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err, + (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, + mode)); + + if (!stmt->executed) { + ub4 colcount; + /* do first-time-only definition of bind/mapping stuff */ + + /* how many columns do we have ? */ + STMT_CALL_MSG(OCIAttrGet, "ATTR_PARAM_COUNT", + (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); + + stmt->column_count = (int)colcount; + + if (S->cols) { + int i; + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + switch (S->cols[i].dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + /* do nothing */ + break; + default: + efree(S->cols[i].data); + } + } + } + efree(S->cols); + } + + S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); + } + + STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", + (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); + stmt->row_count = (long)rowcount; + + return 1; +} /* }}} */ + +static sb4 oci_bind_input_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data; + TSRMLS_FETCH(); + + if (!param || !param->parameter) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "param is NULL in oci_bind_input_cb; this should not happen"); + return OCI_ERROR; + } + + *indpp = &P->indicator; + + if (P->thing) { + *bufpp = P->thing; + *alenp = sizeof(void*); + } else if (ZVAL_IS_NULL(param->parameter)) { + /* insert a NULL value into the column */ + P->indicator = -1; /* NULL */ + *bufpp = 0; + *alenp = -1; + } else if (!P->thing) { + /* regular string bind */ + convert_to_string(param->parameter); + *bufpp = Z_STRVAL_P(param->parameter); + *alenp = Z_STRLEN_P(param->parameter); + } + + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; +} /* }}} */ + +static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) /* {{{ */ +{ + struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx; + pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data; + TSRMLS_FETCH(); + + if (!param || !param->parameter) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "param is NULL in oci_bind_output_cb; this should not happen"); + return OCI_ERROR; + } + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + P->actual_len = sizeof(OCILobLocator*); + *bufpp = P->thing; + *alenpp = &P->actual_len; + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + return OCI_CONTINUE; + } + + if (Z_TYPE_P(param->parameter) == IS_OBJECT || Z_TYPE_P(param->parameter) == IS_RESOURCE) { + return OCI_CONTINUE; + } + + convert_to_string(param->parameter); + zval_dtor(param->parameter); + + Z_STRLEN_P(param->parameter) = param->max_value_len; + Z_STRVAL_P(param->parameter) = ecalloc(1, Z_STRLEN_P(param->parameter)+1); + P->used_for_output = 1; + + P->actual_len = Z_STRLEN_P(param->parameter); + *alenpp = &P->actual_len; + *bufpp = Z_STRVAL_P(param->parameter); + *piecep = OCI_ONE_PIECE; + *rcodepp = &P->retcode; + *indpp = &P->indicator; + + return OCI_CONTINUE; +} /* }}} */ + +static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type TSRMLS_DC) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + + /* we're only interested in parameters for prepared SQL right now */ + if (param->is_param) { + pdo_oci_bound_param *P; + sb4 value_sz = -1; + + P = (pdo_oci_bound_param*)param->driver_data; + + switch (event_type) { + case PDO_PARAM_EVT_FREE: + P = param->driver_data; + if (P) { + efree(P); + } + break; + + case PDO_PARAM_EVT_ALLOC: + P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param)); + param->driver_data = P; + + /* figure out what we're doing */ + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_LOB: + /* P->thing is now an OCILobLocator * */ + P->oci_type = SQLT_BLOB; + value_sz = sizeof(OCILobLocator*); + break; + + case PDO_PARAM_STR: + default: + P->oci_type = SQLT_CHR; + value_sz = param->max_value_len; + if (param->max_value_len == 0) { + value_sz = 1332; /* maximum size before value is interpreted as a LONG value */ + } + + } + + if (param->name) { + STMT_CALL(OCIBindByName, (S->stmt, + &P->bind, S->err, (text*)param->name, + param->namelen, 0, value_sz, P->oci_type, + &P->indicator, 0, &P->retcode, 0, 0, + OCI_DATA_AT_EXEC)); + } else { + STMT_CALL(OCIBindByPos, (S->stmt, + &P->bind, S->err, param->paramno+1, + 0, value_sz, P->oci_type, + &P->indicator, 0, &P->retcode, 0, 0, + OCI_DATA_AT_EXEC)); + } + + STMT_CALL(OCIBindDynamic, (P->bind, + S->err, + param, oci_bind_input_cb, + param, oci_bind_output_cb)); + + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + P->indicator = 0; + P->used_for_output = 0; + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + ub4 empty = 0; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); + STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); + S->have_blobs = 1; + } + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + /* fixup stuff set in motion in oci_bind_output_cb */ + if (P->used_for_output) { + if (P->indicator == -1) { + /* set up a NULL value */ + if (Z_TYPE_P(param->parameter) == IS_STRING +#if ZEND_EXTENSION_API_NO < 220040718 + && Z_STRVAL_P(param->parameter) != empty_string +#endif + ) { + /* OCI likes to stick non-terminated strings in things */ + *Z_STRVAL_P(param->parameter) = '\0'; + } + zval_dtor(param->parameter); + ZVAL_NULL(param->parameter); + } else if (Z_TYPE_P(param->parameter) == IS_STRING +#if ZEND_EXTENSION_API_NO < 220040718 + && Z_STRVAL_P(param->parameter) != empty_string +#endif + ) { + Z_STRLEN_P(param->parameter) = P->actual_len; + Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), P->actual_len+1); + Z_STRVAL_P(param->parameter)[P->actual_len] = '\0'; + } + } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { + php_stream *stm; + + if (Z_TYPE_P(param->parameter) == IS_NULL) { + /* if the param is NULL, then we assume that they + * wanted to bind a lob locator into it from the query + * */ + + stm = oci_create_lob_stream(stmt, (OCILobLocator*)P->thing TSRMLS_CC); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE); + php_stream_to_zval(stm, param->parameter); + P->thing = NULL; + } + } else { + /* we're a LOB being used for insert; transfer the data now */ + size_t n; + ub4 amt, offset = 1; + char *consume; + + php_stream_from_zval_no_verify(stm, ¶m->parameter); + if (stm) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE); + do { + char buf[8192]; + n = php_stream_read(stm, buf, sizeof(buf)); + if ((int)n <= 0) { + break; + } + consume = buf; + do { + amt = n; + OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing, + &amt, offset, consume, n, + OCI_ONE_PIECE, + NULL, NULL, 0, SQLCS_IMPLICIT); + offset += amt; + n -= amt; + consume += amt; + } while (n); + } while (1); + OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing); + OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0); + } else if (Z_TYPE_P(param->parameter) == IS_STRING) { + /* stick the string into the LOB */ + consume = Z_STRVAL_P(param->parameter); + n = Z_STRLEN_P(param->parameter); + if (n) { + OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE); + while (n) { + amt = n; + OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing, + &amt, offset, consume, n, + OCI_ONE_PIECE, + NULL, NULL, 0, SQLCS_IMPLICIT); + consume += amt; + n -= amt; + } + OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing); + } + } + OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); + P->thing = NULL; + } + } + + return 1; + } + } + + return 1; +} /* }}} */ + +static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, long offset TSRMLS_DC) /* {{{ */ +{ +#if HAVE_OCISTMTFETCH2 + ub4 ociori; +#endif + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + +#if HAVE_OCISTMTFETCH2 + switch (ori) { + case PDO_FETCH_ORI_NEXT: ociori = OCI_FETCH_NEXT; break; + case PDO_FETCH_ORI_PRIOR: ociori = OCI_FETCH_PRIOR; break; + case PDO_FETCH_ORI_FIRST: ociori = OCI_FETCH_FIRST; break; + case PDO_FETCH_ORI_LAST: ociori = OCI_FETCH_LAST; break; + case PDO_FETCH_ORI_ABS: ociori = OCI_FETCH_ABSOLUTE; break; + case PDO_FETCH_ORI_REL: ociori = OCI_FETCH_RELATIVE; break; + } + S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, offset, OCI_DEFAULT); +#else + S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); +#endif + + if (S->last_err == OCI_NO_DATA) { + /* no (more) data */ + return 0; + } + + if (S->last_err == OCI_NEED_DATA) { + oci_stmt_error("OCI_NEED_DATA"); + return 0; + } + + if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { + return 1; + } + + oci_stmt_error("OCIStmtFetch"); + + return 0; +} /* }}} */ + +static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, + ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) +{ + pdo_oci_column *col = (pdo_oci_column*)octxp; + TSRMLS_FETCH(); + + switch (col->dtype) { + case SQLT_BLOB: + case SQLT_CLOB: + *piecep = OCI_ONE_PIECE; + *bufpp = col->data; + *alenpp = &col->datalen; + *indpp = (dvoid *)&col->indicator; + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "unhandled datatype in oci_define_callback; this should not happen"); + return OCI_ERROR; + } + + return OCI_CONTINUE; +} + +static int oci_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + OCIParam *param = NULL; + text *colname; + ub2 dtype, data_size, scale, precis; + ub4 namelen; + struct pdo_column_data *col = &stmt->columns[colno]; + zend_bool dyn = FALSE; + + /* describe the column */ + STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1)); + + /* what type ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", + (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); + + /* how big ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE", + (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); + + /* scale ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", + (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); + + /* precision ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", + (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); + + /* name ? */ + STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", + (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); + + col->precision = scale; + col->maxlen = data_size; + col->namelen = namelen; + col->name = estrndup((char *)colname, namelen); + + S->cols[colno].dtype = dtype; + + /* how much room do we need to store the field */ + switch (dtype) { + case SQLT_LBI: + case SQLT_LNG: + if (dtype == SQLT_LBI) { + dtype = SQLT_BIN; + } else { + dtype = SQLT_CHR; + } + S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + col->param_type = PDO_PARAM_STR; + break; + + case SQLT_BLOB: + case SQLT_CLOB: + col->param_type = PDO_PARAM_LOB; + STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); + S->cols[colno].datalen = sizeof(OCILobLocator*); + dyn = TRUE; + break; + + case SQLT_BIN: + default: + if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD +#ifdef SQLT_TIMESTAMP + || dtype == SQLT_TIMESTAMP +#endif +#ifdef SQLT_TIMESTAMP_TZ + || dtype == SQLT_TIMESTAMP_TZ +#endif + ) { + /* should be big enough for most date formats and numbers */ + S->cols[colno].datalen = 512; +#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) + } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { + S->cols[colno].datalen = 1024; +#endif + } else { + S->cols[colno].datalen = col->maxlen; + } + if (dtype == SQLT_BIN) { + S->cols[colno].datalen *= 3; + } + S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); + dtype = SQLT_CHR; + + /* returning data as a string */ + col->param_type = PDO_PARAM_STR; + } + + STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1, + S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator, + &S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); + + if (dyn) { + STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], + oci_define_callback)); + } + + return 1; +} /* }}} */ + +struct oci_lob_self { + pdo_stmt_t *stmt; + pdo_oci_stmt *S; + OCILobLocator *lob; + ub4 offset; +}; + +static size_t oci_blob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; + ub4 amt; + sword r; + + amt = count; + r = OCILobWrite(self->S->H->svc, self->S->err, self->lob, + &amt, self->offset, (char*)buf, count, + OCI_ONE_PIECE, + NULL, NULL, 0, SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS) { + return (size_t)-1; + } + + self->offset += amt; + return amt; +} + +static size_t oci_blob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; + ub4 amt; + sword r; + + amt = count; + r = OCILobRead(self->S->H->svc, self->S->err, self->lob, + &amt, self->offset, buf, count, + NULL, NULL, 0, SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { + return (size_t)-1; + } + + self->offset += amt; + if (amt < count) { + stream->eof = 1; + } + return amt; +} + +static int oci_blob_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; + pdo_stmt_t *stmt = self->stmt; + + if (close_handle) { + OCILobClose(self->S->H->svc, self->S->err, self->lob); + efree(self); + } + + php_pdo_stmt_delref(stmt TSRMLS_CC); + return 0; +} + +static int oci_blob_flush(php_stream *stream TSRMLS_DC) +{ + struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; + OCILobFlushBuffer(self->S->H->svc, self->S->err, self->lob, 0); + return 0; +} + +static int oci_blob_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) +{ + struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; + + if (offset >= PDO_OCI_LOBMAXSIZE) { + return -1; + } else { + self->offset = offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ + return 0; + } +} + +static php_stream_ops oci_blob_stream_ops = { + oci_blob_write, + oci_blob_read, + oci_blob_close, + oci_blob_flush, + "pdo_oci blob stream", + oci_blob_seek, + NULL, + NULL, + NULL +}; + +static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob TSRMLS_DC) +{ + php_stream *stm; + struct oci_lob_self *self = ecalloc(1, sizeof(*self)); + self->lob = lob; + self->offset = 1; /* 1-based */ + self->stmt = stmt; + self->S = (pdo_oci_stmt*)stmt->driver_data; + + stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); + + if (stm) { + php_pdo_stmt_addref(stmt TSRMLS_CC); + return stm; + } + + efree(self); + return NULL; +} + +static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) /* {{{ */ +{ + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + pdo_oci_column *C = &S->cols[colno]; + + /* check the indicator to ensure that the data is intact */ + if (C->indicator == -1) { + /* A NULL value */ + *ptr = NULL; + *len = 0; + return 1; + } else if (C->indicator == 0) { + /* it was stored perfectly */ + + if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { + if (C->data) { + *ptr = (char*)oci_create_lob_stream(stmt, (OCILobLocator*)C->data TSRMLS_CC); + OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY); + } + *len = 0; + return *ptr ? 1 : 0; + } + + *ptr = C->data; + *len = C->fetched_len; + return 1; + } else { + /* it was truncated */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "column %d data was too large for buffer and was truncated to fit it", colno); + + *ptr = C->data; + *len = C->fetched_len; + return 1; + } +} /* }}} */ + +struct pdo_stmt_methods oci_stmt_methods = { + oci_stmt_dtor, + oci_stmt_execute, + oci_stmt_fetch, + oci_stmt_describe, + oci_stmt_get_col, + oci_stmt_param_hook +}; + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/pdo_oci/package2.xml b/ext/pdo_oci/package2.xml new file mode 100644 index 0000000..1f828d1 --- /dev/null +++ b/ext/pdo_oci/package2.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package packagerversion="1.4.2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 +http://pear.php.net/dtd/tasks-1.0.xsd +http://pear.php.net/dtd/package-2.0 +http://pear.php.net/dtd/package-2.0.xsd"> + <name>PDO_OCI</name> + <channel>pecl.php.net</channel> + <summary>Oracle Call Interface driver for PDO</summary> + <description>This extension provides an Oracle driver for PDO. + </description> + <lead> + <name>Wez Furlong</name> + <user>wez</user> + <email>wez@php.net</email> + <active>yes</active> + </lead> + <date>2006-05-01</date> + <version> + <release>1.0.1</release> + <api>1.0.1</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.php.net/license">PHP</license> + <notes> +- Added support for BINARY_DOUBLE and BINARY_FLOAT to PDO_OCI and OCI8 + (also fixes bug #36764). (Tony) +- Fixed bug #35907 (PDO_OCI uses hardcoded lib path $ORACLE_HOME/lib). (Tony) +- Repackage with package2.xml +- Improved handling of long columns +- Fixed PECL Bug #5722; implemented LOB support. + +You require Oracle OCI 8 or higher client libraries (instantclient is also +supported) installed on the machine where you intend to build and/or use this +package. + +If you are running on windows, you can download the binary from here: +http://pecl4win.php.net/ext.php/php_pdo_oci.dll + </notes> + <contents> + <dir name="/"> + <file name="config.m4" role="src" /> + <file name="config.w32" role="src" /> + <file name="CREDITS" role="doc" /> + <file name="oci_driver.c" role="src" /> + <file name="oci_statement.c" role="src" /> + <file name="pdo_oci.c" role="src" /> + <file name="php_pdo_oci.h" role="src" /> + <file name="php_pdo_oci_int.h" role="src" /> + </dir> <!-- / --> + </contents> + <dependencies> + <required> + <php> + <min>5.0.3</min> + </php> + <pearinstaller> + <min>1.4.0</min> + </pearinstaller> + <package> + <name>pdo</name> + <channel>pecl.php.net</channel> + <min>1.0.3</min> + <providesextension>PDO</providesextension> + </package> + </required> + </dependencies> + <providesextension>PDO_OCI</providesextension> + <extsrcrelease /> +</package> diff --git a/ext/pdo_oci/pdo_oci.c b/ext/pdo_oci/pdo_oci.c new file mode 100644 index 0000000..6957714 --- /dev/null +++ b/ext/pdo_oci/pdo_oci.c @@ -0,0 +1,133 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong <wez@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "pdo/php_pdo_driver.h" +#include "php_pdo_oci.h" +#include "php_pdo_oci_int.h" + +/* {{{ pdo_oci_functions[] */ +const zend_function_entry pdo_oci_functions[] = { + PHP_FE_END +}; +/* }}} */ + +/* {{{ pdo_oci_module_entry */ + +#if ZEND_MODULE_API_NO >= 20050922 +static const zend_module_dep pdo_oci_deps[] = { + ZEND_MOD_REQUIRED("pdo") + ZEND_MOD_END +}; +#endif + +zend_module_entry pdo_oci_module_entry = { +#if ZEND_MODULE_API_NO >= 20050922 + STANDARD_MODULE_HEADER_EX, NULL, + pdo_oci_deps, +#else + STANDARD_MODULE_HEADER, +#endif + "PDO_OCI", + pdo_oci_functions, + PHP_MINIT(pdo_oci), + PHP_MSHUTDOWN(pdo_oci), + NULL, + NULL, + PHP_MINFO(pdo_oci), + "1.0.1", + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_PDO_OCI +ZEND_GET_MODULE(pdo_oci) +#endif + +const ub4 PDO_OCI_INIT_MODE = +#if 0 && defined(OCI_SHARED) + /* shared mode is known to be bad for PHP */ + OCI_SHARED +#else + OCI_DEFAULT +#endif +#ifdef OCI_OBJECT + |OCI_OBJECT +#endif +#ifdef ZTS + |OCI_THREADED +#endif + ; + +/* true global environment */ +OCIEnv *pdo_oci_Env = NULL; + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(pdo_oci) +{ + php_pdo_register_driver(&pdo_oci_driver); + +#if HAVE_OCIENVCREATE + OCIEnvCreate(&pdo_oci_Env, PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL, 0, NULL); +#else + OCIInitialize(PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL); + OCIEnvInit(&pdo_oci_Env, OCI_DEFAULT, 0, NULL); +#endif + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(pdo_oci) +{ + php_pdo_unregister_driver(&pdo_oci_driver); + OCIHandleFree((dvoid*)pdo_oci_Env, OCI_HTYPE_ENV); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(pdo_oci) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "PDO Driver for OCI 8 and later", "enabled"); + php_info_print_table_end(); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/pdo_oci/php_pdo_oci.h b/ext/pdo_oci/php_pdo_oci.h new file mode 100644 index 0000000..d0b1b3d --- /dev/null +++ b/ext/pdo_oci/php_pdo_oci.h @@ -0,0 +1,47 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong <wez@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_PDO_OCI_H +#define PHP_PDO_OCI_H + +extern zend_module_entry pdo_oci_module_entry; +#define phpext_pdo_oci_ptr &pdo_oci_module_entry + +#ifdef ZTS +#include "TSRM.h" +#endif + +PHP_MINIT_FUNCTION(pdo_oci); +PHP_MSHUTDOWN_FUNCTION(pdo_oci); +PHP_RINIT_FUNCTION(pdo_oci); +PHP_RSHUTDOWN_FUNCTION(pdo_oci); +PHP_MINFO_FUNCTION(pdo_oci); + +#endif /* PHP_PDO_OCI_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/pdo_oci/php_pdo_oci_int.h b/ext/pdo_oci/php_pdo_oci_int.h new file mode 100644 index 0000000..af5618b --- /dev/null +++ b/ext/pdo_oci/php_pdo_oci_int.h @@ -0,0 +1,100 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong <wez@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include <oci.h> + +typedef struct { + const char *file; + int line; + sb4 errcode; + char *errmsg; +} pdo_oci_error_info; + +/* stuff we use in an OCI database handle */ +typedef struct { + OCIServer *server; + OCISession *session; + OCIEnv *env; + OCIError *err; + OCISvcCtx *svc; + /* OCI9; 0 == use NLS_LANG */ + ub2 charset; + sword last_err; + + unsigned attached:1; + unsigned _reserved:31; + + pdo_oci_error_info einfo; +} pdo_oci_db_handle; + +typedef struct { + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; + + char *data; + ub4 datalen; + + ub2 dtype; + +} pdo_oci_column; + +typedef struct { + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + ub4 exec_type; + pdo_oci_column *cols; + pdo_oci_error_info einfo; + unsigned int have_blobs:1; +} pdo_oci_stmt; + +typedef struct { + OCIBind *bind; /* allocated by OCI */ + sb2 oci_type; + sb2 indicator; + ub2 retcode; + + ub4 actual_len; + + dvoid *thing; /* for LOBS, REFCURSORS etc. */ + + unsigned used_for_output; +} pdo_oci_bound_param; + +extern const ub4 PDO_OCI_INIT_MODE; +extern pdo_driver_t pdo_oci_driver; +extern OCIEnv *pdo_oci_Env; + +ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line TSRMLS_DC); +#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__ TSRMLS_CC) +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__ TSRMLS_CC) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__ TSRMLS_CC) + +extern struct pdo_stmt_methods oci_stmt_methods; + +/* Default prefetch size in number of rows */ +#define PDO_OCI_PREFETCH_DEFAULT 100 + +/* Arbitrary assumed row length for prefetch memory limit calcuation */ +#define PDO_OCI_PREFETCH_ROWSIZE 1024 diff --git a/ext/pdo_oci/tests/bug41996.phpt b/ext/pdo_oci/tests/bug41996.phpt new file mode 100644 index 0000000..ac8e35f --- /dev/null +++ b/ext/pdo_oci/tests/bug41996.phpt @@ -0,0 +1,20 @@ +--TEST-- +PDO OCI Bug #41996 (Problem accessing Oracle ROWID) +--SKIPIF-- +<?php +/* $Id$ */ +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'; +PDOTest::skip(); +?> +--FILE-- +<?php +require 'ext/pdo/tests/pdo_test.inc'; +$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt'); + +$stmt = $db->prepare('SELECT rowid FROM dual'); +$stmt->execute(); +$row = $stmt->fetch(); +var_dump(strlen($row[0]) > 0); +--EXPECTF-- +bool(true) diff --git a/ext/pdo_oci/tests/bug44301.phpt b/ext/pdo_oci/tests/bug44301.phpt new file mode 100644 index 0000000..c0f7935 --- /dev/null +++ b/ext/pdo_oci/tests/bug44301.phpt @@ -0,0 +1,25 @@ +--TEST-- +PDO OCI Bug #44301 (Segfault when an exception is thrown on persistent connections) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'; +PDOTest::skip(); +?> +--FILE-- +<?php +putenv("PDO_OCI_TEST_ATTR=" . serialize(array(PDO::ATTR_PERSISTENT => true))); +require 'ext/pdo/tests/pdo_test.inc'; +$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt'); +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +try { + $stmt = $db->prepare('SELECT * FROM no_table'); + $stmt->execute(); +} catch (PDOException $e) { + print $e->getMessage(); +} +$db = null; +--EXPECTF-- +SQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view does not exist + (%s/ext/pdo_oci/oci_statement.c:%d) diff --git a/ext/pdo_oci/tests/bug46274.phpt b/ext/pdo_oci/tests/bug46274.phpt new file mode 100644 index 0000000..77f2a01 --- /dev/null +++ b/ext/pdo_oci/tests/bug46274.phpt @@ -0,0 +1,73 @@ +--TEST-- +Bug #46274 (pdo_pgsql - Segfault when using PDO::ATTR_STRINGIFY_FETCHES and blob) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) +die('skip not loaded'); +require dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'; +PDOTest::skip(); +?> +--FILE-- +<?php +require 'ext/pdo/tests/pdo_test.inc'; +$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt'); +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + +try { + $db->exec("DROP TABLE test_one_blob"); +} catch (Exception $e) { +} + +$db->beginTransaction(); + +$db->query('CREATE TABLE test_one_blob (id INT NOT NULL, blob1 BLOB)'); + +$stmt = $db->prepare("INSERT INTO test_one_blob (id, blob1) VALUES (:id, EMPTY_BLOB()) RETURNING blob1 INTO :foo"); + +$data = 'foo'; +$blob = fopen('php://memory', 'a'); +fwrite($blob, $data); +rewind($blob); + +$id = 1; +$stmt->bindparam(':id', $id); +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$data = ''; +$blob = fopen('php://memory', 'a'); +fwrite($blob, $data); +rewind($blob); + +$id = 1; +$stmt->bindparam(':id', $id); +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$res = $db->query("SELECT blob1 from test_one_blob"); +// Resource +var_dump($res->fetch()); + +// Empty string +var_dump($res->fetch()); + +$db->exec("DROP TABLE test_one_blob"); + +?> +--XFAIL-- +Corrupts memory +--EXPECTF-- +array(2) { + ["blob1"]=> + string(3) "foo" + [0]=> + string(3) "foo" +} +array(2) { + ["blob1"]=> + string(0) "" + [0]=> + string(0) "" +} diff --git a/ext/pdo_oci/tests/bug46274_2.phpt b/ext/pdo_oci/tests/bug46274_2.phpt new file mode 100644 index 0000000..9e92254 --- /dev/null +++ b/ext/pdo_oci/tests/bug46274_2.phpt @@ -0,0 +1,79 @@ +--TEST-- +Bug #46274 (pdo_pgsql - Segfault when using PDO::ATTR_STRINGIFY_FETCHES and blob) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) +die('skip not loaded'); +require dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'; +PDOTest::skip(); +?> +--FILE-- +<?php +require 'ext/pdo/tests/pdo_test.inc'; +$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt'); +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + +try { + $db->exec("DROP TABLE test_one_blob"); +} catch (Exception $e) { +} + +$db->beginTransaction(); + +$db->query('CREATE TABLE test_one_blob (id INT NOT NULL, blob1 BLOB)'); + +$stmt = $db->prepare("INSERT INTO test_one_blob (id, blob1) VALUES (:id, EMPTY_BLOB()) RETURNING blob1 INTO :foo"); + +$data = 'foo'; +$blob = fopen('php://memory', 'a'); +fwrite($blob, $data); +rewind($blob); + +$id = 1; +$stmt->bindparam(':id', $id); +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$data = ''; +$blob = fopen('php://memory', 'a'); +fwrite($blob, $data); +rewind($blob); + +$id = 1; +$stmt->bindparam(':id', $id); +$stmt->bindparam(':foo', $blob, PDO::PARAM_LOB); +$stmt->execute(); + +$res = $db->query("SELECT blob1 from test_one_blob"); +// Resource +var_dump($row = $res->fetch()); +var_dump(fread($row[0], 1024)); +fclose($row[0]); + +// Empty string +var_dump($row = $res->fetch()); +var_dump(fread($row[0], 1024)); +fclose($row[0]); + +$db->exec("DROP TABLE test_one_blob"); + +?> +--XFAIL-- +Corrupts memory +--EXPECTF-- +array(2) { + ["blob1"]=> + resource(%d) of type (stream) + [0]=> + resource(%d) of type (stream) +} +string(3) "foo" +array(2) { + ["blob1"]=> + resource(%d) of type (stream) + [0]=> + resource(%d) of type (stream) +} +string(0) "" diff --git a/ext/pdo_oci/tests/bug57702.phpt b/ext/pdo_oci/tests/bug57702.phpt new file mode 100644 index 0000000..9281f6d --- /dev/null +++ b/ext/pdo_oci/tests/bug57702.phpt @@ -0,0 +1,165 @@ +--TEST-- +PDO OCI Bug #57702 (Multi-row BLOB fetches) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require('ext/pdo/tests/pdo_test.inc'); +$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt'); + +// Note the PDO test setup sets PDO::ATTR_STRINGIFY_FETCHES to true +// (and sets PDO::ATTR_CASE to PDO::CASE_LOWER) + +$query = "begin execute immediate 'drop table mytable'; exception when others then if sqlcode <> -942 then raise; end if; end;"; +$stmt = $db->prepare($query); +$stmt->execute(); + +$query = "create table bug57702 (id number, data1 blob, data2 blob)"; +$stmt = $db->prepare($query); +$stmt->execute(); + +function do_insert($db, $id, $data1, $data2) +{ + $db->beginTransaction(); + $stmt = $db->prepare("insert into bug57702 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2"); + $stmt->bindParam(':id', $id); + $stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB); + $stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB); + $blob1 = null; + $blob2 = null; + $stmt->execute(); + + fwrite($blob1, $data1); + fclose($blob1); + fwrite($blob2, $data2); + fclose($blob2); + $db->commit(); +} + +do_insert($db, 1, "row 1 col 1", "row 1 col 2"); +do_insert($db, 2, "row 2 col 1", "row 2 col 2"); + +//////////////////// + +echo "First Query\n"; + +// Fetch it back +$stmt = $db->prepare('select data1, data2 from bug57702 order by id'); +$stmt->execute(); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row['data1']); +var_dump($row['data2']); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row['data1']); +var_dump($row['data2']); + +//////////////////// + +echo "\nSecond Query\n"; + +foreach($db->query("select data1 as d1, data2 as d2 from bug57702 order by id") as $row) { + var_dump($row['d1']); + var_dump($row['d2']); +} + +//////////////////// + +echo "\nThird Query\n"; + +$stmt = $db->prepare('select data1 as d3_1, data2 as d3_2 from bug57702 order by id'); + +$rs = $stmt->execute(); +$stmt->bindColumn('d3_1' , $clob1, PDO::PARAM_LOB); +$stmt->bindColumn('d3_2' , $clob2, PDO::PARAM_LOB); + +while ($stmt->fetch(PDO::FETCH_BOUND)) { + var_dump($clob1); + var_dump($clob2); +} +print "done\n"; + +//////////////////// + +echo "\nFourth Query\n"; + +$a = array(); +$i = 0; +foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + $i++; +} + +for ($i = 0; $i < count($a); $i++) { + var_dump($a[$i][0]); + var_dump($a[$i][1]); +} + +//////////////////// + +echo "\nFifth Query\n"; + +$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + +// Since each column only has one lob descriptor, the last row is +// shown twice because the lob descriptor for each column is reused in +// the stream + +$a = array(); +$i = 0; +foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + $i++; +} + +for ($i = 0; $i < count($a); $i++) { + var_dump(stream_get_contents($a[$i][0])); + var_dump(stream_get_contents($a[$i][1])); +} + +// Cleanup +$query = "drop table bug57702"; +$stmt = $db->prepare($query); +$stmt->execute(); + +print "done\n"; + +?> +--EXPECTF-- +First Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" + +Second Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" + +Third Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" +done + +Fourth Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" + +Fifth Query +string(11) "row 2 col 1" +string(11) "row 2 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" +done diff --git a/ext/pdo_oci/tests/bug_33707.phpt b/ext/pdo_oci/tests/bug_33707.phpt new file mode 100644 index 0000000..1ac9fa4 --- /dev/null +++ b/ext/pdo_oci/tests/bug_33707.phpt @@ -0,0 +1,29 @@ +--TEST-- +PDO OCI Bug #33707 (Errors in select statements not reported) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'; +PDOTest::skip(); +?> +--FILE-- +<?php +require 'ext/pdo/tests/pdo_test.inc'; +$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt'); +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + +$rs = $db->query('select blah from a_table_that_doesnt_exist'); +var_dump($rs); +var_dump($db->errorInfo()); + +--EXPECTF-- +bool(false) +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(942) + [2]=> + string(%d) "OCIStmtExecute: ORA-00942: table or view does not exist + (%s:%d)" +} diff --git a/ext/pdo_oci/tests/common.phpt b/ext/pdo_oci/tests/common.phpt new file mode 100644 index 0000000..5107828 --- /dev/null +++ b/ext/pdo_oci/tests/common.phpt @@ -0,0 +1,26 @@ +--TEST-- +OCI +--SKIPIF-- +<?php # vim:ft=php +if (!extension_loaded('pdo_oci')) print 'skip'; ?> +--REDIRECTTEST-- +# magic auto-configuration + +$config = array( + 'TESTS' => 'ext/pdo/tests' +); + + +if (false !== getenv('PDO_OCI_TEST_DSN')) { + # user set them from their shell + $config['ENV']['PDOTEST_DSN'] = getenv('PDO_OCI_TEST_DSN'); + $config['ENV']['PDOTEST_USER'] = getenv('PDO_OCI_TEST_USER'); + $config['ENV']['PDOTEST_PASS'] = getenv('PDO_OCI_TEST_PASS'); + $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_OCI_TEST_ATTR'); +} else { + $config['ENV']['PDOTEST_DSN'] = 'oci:dbname=localhost/xe;charset=WE8MSWIN1252'; + $config['ENV']['PDOTEST_USER'] = 'SYSTEM'; + $config['ENV']['PDOTEST_PASS'] = 'oracle'; +} + +return $config; diff --git a/ext/pdo_oci/tests/pdo_oci_attr_autocommit_1.phpt b/ext/pdo_oci/tests/pdo_oci_attr_autocommit_1.phpt new file mode 100644 index 0000000..298d59e --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_autocommit_1.phpt @@ -0,0 +1,64 @@ +--TEST-- +PDO_OCI: Attribute: Basic autocommit functionality +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +$dbh = PDOTest::factory(); + +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); +$dbh->exec("drop table pdo_ac_tab"); +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +print "PDO::ATTR_AUTOCOMMIT: Default: "; +var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + +echo "Change setting to false - "; + +$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false); + +print "PDO::ATTR_AUTOCOMMIT: "; +var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + +echo "Change setting back to true - "; + +$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true); + +print "PDO::ATTR_AUTOCOMMIT: "; +var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + +// Use 2nd connection to check that autocommit does commit + +echo "Insert data\n"; +$dbh->exec("create table pdo_ac_tab (col1 varchar2(20))"); +$dbh->exec("insert into pdo_ac_tab (col1) values ('some data')"); + +$dbh2 = PDOTest::factory(); + +echo "Second connection should be able to see committed data\n"; +$s = $dbh2->prepare("select col1 from pdo_ac_tab"); +$s->execute(); +while ($r = $s->fetch()) { + echo "Data is: " . $r[0] . "\n"; +} + +$dbh->exec("drop table pdo_ac_tab"); + +echo "Done\n"; + +?> +--EXPECT-- +PDO::ATTR_AUTOCOMMIT: Default: bool(true) +Change setting to false - PDO::ATTR_AUTOCOMMIT: bool(false) +Change setting back to true - PDO::ATTR_AUTOCOMMIT: bool(true) +Insert data +Second connection should be able to see committed data +Data is: some data +Done
\ No newline at end of file diff --git a/ext/pdo_oci/tests/pdo_oci_attr_autocommit_2.phpt b/ext/pdo_oci/tests/pdo_oci_attr_autocommit_2.phpt new file mode 100644 index 0000000..9addca8 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_autocommit_2.phpt @@ -0,0 +1,127 @@ +--TEST-- +PDO_OCI: Attribute: beginTransaction and native transactions +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); +$dbh = PDOTest::factory(); + +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); +$dbh->exec("drop table pdo_ac_tab"); +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$dbh->exec("create table pdo_ac_tab (col1 varchar2(25))"); + +echo "Test 1 Check beginTransaction insertion\n"; + +$dbh->beginTransaction(); +try { + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 1')"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data 2')"); + $dbh->commit(); +} +catch (PDOException $e) { + echo "Caught unexpected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + $dbh->rollback(); +} + +echo "Test 2 Cause an exception and test beginTransaction rollback\n"; + +$dbh->beginTransaction(); +try { + $dbh->exec("insert into pdo_ac_tab (col1) values ('not committed #1')"); + $dbh->exec("insert into pdo_ac_tab (col1) values ('data that is too long to fit and will barf')"); + $dbh->commit(); +} +catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; + $dbh->rollback(); +} + +echo "Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction\n"; + +$dbh->exec("insert into pdo_ac_tab (col1) values ('data 3')"); +$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true); +print "PDO::ATTR_AUTOCOMMIT: "; +var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); +try { + $dbh->rollback(); +} +catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; +} + +echo "Test 4 Setting ATTR_AUTOCOMMIT to false will commit and end the transaction\n"; + +$dbh->beginTransaction(); +$dbh->exec("insert into pdo_ac_tab (col1) values ('data 4')"); +$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false); +print "PDO::ATTR_AUTOCOMMIT: "; +var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); +try { + $dbh->rollback(); +} +catch (PDOException $e) { + echo "Caught expected exception at line " . __LINE__ . "\n"; + echo $e->getMessage() . "\n"; +} + +echo "Test 5 Handle transactions ourselves\n"; + +print "PDO::ATTR_AUTOCOMMIT: "; +var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + +$dbh->exec("insert into pdo_ac_tab (col1) values ('not committed #2')"); +$dbh->exec("rollback"); +$dbh->exec("insert into pdo_ac_tab (col1) values ('data 5')"); +$dbh->exec("insert into pdo_ac_tab (col1) values ('data 6')"); + +$dbh->exec("commit"); + +// Open new connection to really verify what was inserted + +$dbh2 = PDOTest::factory(); + +echo "Query Results are:\n"; +$s = $dbh2->prepare("select col1 from pdo_ac_tab"); +$s->execute(); +while ($r = $s->fetch()) { + echo $r[0] . "\n"; +} + +echo "Done\n"; + +?> +--EXPECTF-- +Test 1 Check beginTransaction insertion +Test 2 Cause an exception and test beginTransaction rollback +Caught expected exception at line 35 +SQLSTATE[HY000]: General error: 12899 OCIStmtExecute: ORA-12899: %s +%s +Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction +PDO::ATTR_AUTOCOMMIT: bool(true) +Caught expected exception at line %d +There is no active transaction +Test 4 Setting ATTR_AUTOCOMMIT to false will commit and end the transaction +PDO::ATTR_AUTOCOMMIT: bool(false) +Caught expected exception at line %d +There is no active transaction +Test 5 Handle transactions ourselves +PDO::ATTR_AUTOCOMMIT: bool(false) +Query Results are: +data 1 +data 2 +data 3 +data 4 +data 5 +data 6 +Done diff --git a/ext/pdo_oci/tests/pdo_oci_attr_autocommit_3.phpt b/ext/pdo_oci/tests/pdo_oci_attr_autocommit_3.phpt new file mode 100644 index 0000000..81e9b74 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_autocommit_3.phpt @@ -0,0 +1,51 @@ +--TEST-- +PDO_OCI: Atrribute: closing a connection in non-autocommit mode commits data +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +// Check connection can be created with AUTOCOMMIT off +putenv('PDOTEST_ATTR='.serialize(array(PDO::ATTR_AUTOCOMMIT=>false))); +$dbh = PDOTest::factory(); + +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); +$dbh->exec("drop table pdo_ac_tab"); + +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +print "PDO::ATTR_AUTOCOMMIT: "; +var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT)); + +echo "Insert data\n"; + +$dbh->exec("create table pdo_ac_tab (col1 varchar2(20))"); + +$dbh->exec("insert into pdo_ac_tab (col1) values ('some data')"); + +$dbh = null; // close first connection + +echo "Second connection should be able to see committed data\n"; +$dbh2 = PDOTest::factory(); +$s = $dbh2->prepare("select col1 from pdo_ac_tab"); +$s->execute(); +while ($r = $s->fetch()) { + echo "Data is: " . $r[0] . "\n"; +} + +$dbh2->exec("drop table pdo_ac_tab"); + +echo "Done\n"; + +?> +--EXPECTF-- +PDO::ATTR_AUTOCOMMIT: bool(false) +Insert data +Second connection should be able to see committed data +Done diff --git a/ext/pdo_oci/tests/pdo_oci_attr_case.phpt b/ext/pdo_oci/tests/pdo_oci_attr_case.phpt new file mode 100644 index 0000000..4c19d6c --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_case.phpt @@ -0,0 +1,81 @@ +--TEST-- +PDO_OCI: Attribute: Column Case +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +function do_query1($dbh) +{ + var_dump($dbh->getAttribute(PDO::ATTR_CASE)); + $s = $dbh->prepare("select dummy from dual"); + $s->execute(); + while ($r = $s->fetch(PDO::FETCH_ASSOC)) { + var_dump($r); + } +} + +function do_query2($dbh, $mode) +{ + echo "Mode desired is $mode\n"; + $s = $dbh->prepare("select dummy from dual", array(PDO::ATTR_CASE, $mode)); + $s->execute(); + while ($r = $s->fetch(PDO::FETCH_ASSOC)) { + var_dump($r); + } +} + +$dbh = PDOTest::factory(); +$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +echo "Test 1 - Force column names to lower case\n"; +$dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); +do_query1($dbh); + +echo "Test 2 - Leave column names as returned by the database driver\n"; +$dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); +do_query1($dbh); + +echo "Test 3 - Force column names to upper case\n"; +$dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); +do_query1($dbh); + +echo "Test 4 - Setting on statement has no effect. Attempt lower case but get upper\n"; +$dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); // reset +do_query2($dbh, PDO::CASE_LOWER); + +echo "Done\n"; + +?> +--EXPECT-- +Test 1 - Force column names to lower case +int(2) +array(1) { + ["dummy"]=> + string(1) "X" +} +Test 2 - Leave column names as returned by the database driver +int(0) +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Test 3 - Force column names to upper case +int(1) +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Test 4 - Setting on statement has no effect. Attempt lower case but get upper +Mode desired is 2 +array(1) { + ["DUMMY"]=> + string(1) "X" +} +Done diff --git a/ext/pdo_oci/tests/pdo_oci_attr_client.phpt b/ext/pdo_oci/tests/pdo_oci_attr_client.phpt new file mode 100644 index 0000000..372ccec --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_client.phpt @@ -0,0 +1,43 @@ +--TEST-- +PDO_OCI: Attribute: Client version +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +$dbh = PDOTest::factory(); + +echo "ATTR_CLIENT_VERSION: "; +$cv = $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION); +var_dump($cv); + +$s = explode(".", $cv); +if ($s[0] >= 10 && count($s) > 1 && $s[1] >= 2) { + if (count($s) != 5) { + echo "Wrong number of values in array\nVersion was: "; + var_dump($cv); + } else { + echo "Version OK, so far as can be portably checked\n"; + } +} else { + if (count($s) != 2) { + echo "Wrong number of values in array\nVersion was: "; + var_dump($cv); + } else { + echo "Version OK, so far as can be portably checked\n"; + } +} + +echo "Done\n"; + +?> +--EXPECTF-- +ATTR_CLIENT_VERSION: string(%d) "%d.%s" +Version OK, so far as can be portably checked +Done diff --git a/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt b/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt new file mode 100644 index 0000000..7934e79 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt @@ -0,0 +1,21 @@ +--TEST-- +PDO_OCI: Attribute: verify driver name +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'; + +$dbh = PDOTest::factory(); +var_dump($dbh->getAttribute(PDO::ATTR_DRIVER_NAME)); + +echo "Done\n"; +?> +--EXPECT-- +string(3) "oci" +Done diff --git a/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt b/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt new file mode 100644 index 0000000..b9c4612 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt @@ -0,0 +1,61 @@ +--TEST-- +PDO_OCI: Attribute: Oracle Nulls +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +function do_query($dbh) +{ + var_dump($dbh->getAttribute(PDO::ATTR_ORACLE_NULLS)); + $s = $dbh->prepare("select '' as myempty, null as mynull from dual"); + $s->execute(); + while ($r = $s->fetch()) { + var_dump($r[0]); + var_dump($r[1]); + } +} + +$dbh = PDOTest::factory(); + +print "PDO::ATTR_ORACLE_NULLS: Default: "; +do_query($dbh); + +print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_NATURAL: "; +$dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL); // No conversion. + +do_query($dbh); + +print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_EMPTY_STRING: "; +$dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); // Empty string is converted to NULL. + +do_query($dbh); + +print "PDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: "; +$dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING); // NULL is converted to an empty string. + +do_query($dbh); + +echo "Done\n"; + +?> +--EXPECT-- +PDO::ATTR_ORACLE_NULLS: Default: int(0) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_NATURAL: int(0) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_EMPTY_STRING: int(1) +NULL +NULL +PDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: int(2) +string(0) "" +string(0) "" +Done
\ No newline at end of file diff --git a/ext/pdo_oci/tests/pdo_oci_attr_prefetch_1.phpt b/ext/pdo_oci/tests/pdo_oci_attr_prefetch_1.phpt new file mode 100644 index 0000000..839fe83 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_prefetch_1.phpt @@ -0,0 +1,37 @@ +--TEST-- +PDO_OCI: Attribute: Set prefetch on connection +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +$dbh = PDOTest::factory(); + +echo "Test connect: (value is ignored & has no effect)\n"; +putenv('PDOTEST_ATTR='.serialize(array(PDO::ATTR_PREFETCH=>101))); +$dbh = PDOTest::factory(); + +echo "Test set: (value is ignored & has no effect)\n"; +$dbh->setAttribute(PDO::ATTR_PREFETCH, 102); + +// Verify can fetch +$s = $dbh->prepare("select dummy from dual" ); +$s->execute(); +while ($r = $s->fetch()) { + echo $r[0] . "\n"; +} + +echo "Done\n"; + +?> +--EXPECT-- +Test connect: (value is ignored & has no effect) +Test set: (value is ignored & has no effect) +X +Done diff --git a/ext/pdo_oci/tests/pdo_oci_attr_prefetch_2.phpt b/ext/pdo_oci/tests/pdo_oci_attr_prefetch_2.phpt new file mode 100644 index 0000000..4b8fd80 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_prefetch_2.phpt @@ -0,0 +1,47 @@ +--TEST-- +PDO_OCI: Attribute: prefetch on statements +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +$dbh = PDOTest::factory(); + +$s = $dbh->prepare("select '' as myempty, null as mynull from dual", array(PDO::ATTR_PREFETCH => 101)); + +echo "Test 1: Can't set prefetch after prepare\n"; +var_dump($s->setAttribute(PDO::ATTR_PREFETCH, 102)); + +// Verify can fetch +$s = $dbh->prepare("select dummy from dual" ); +$s->execute(); +while ($r = $s->fetch()) { + echo $r[0] . "\n"; +} + +echo "Test 2: Turn off prefetching\n"; +$s = $dbh->prepare("select '' as myempty, null as mynull from dual", array(PDO::ATTR_PREFETCH => 0)); +$s = $dbh->prepare("select dummy from dual" ); +$s->execute(); +while ($r = $s->fetch()) { + echo $r[0] . "\n"; +} + +echo "Done\n"; + +?> +--EXPECTF-- +Test 1: Can't set prefetch after prepare + +Warning: PDOStatement::setAttribute(): SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support setting attributes in %s on line %d +bool(false) +X +Test 2: Turn off prefetching +X +Done diff --git a/ext/pdo_oci/tests/pdo_oci_attr_server.phpt b/ext/pdo_oci/tests/pdo_oci_attr_server.phpt new file mode 100644 index 0000000..dba5a19 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_attr_server.phpt @@ -0,0 +1,40 @@ +--TEST-- +PDO_OCI: Attribute: Server version and info +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +$dbh = PDOTest::factory(); + +echo "Test 1\n"; +echo "ATTR_SERVER_VERSION: "; +var_dump($dbh->getAttribute(PDO::ATTR_SERVER_VERSION)); + +echo "Test 2\n"; +echo "ATTR_SERVER_INFO\n"; +$si = $dbh->getAttribute(PDO::ATTR_SERVER_INFO); +$pos = strpos($si, "Oracle"); +if ($pos === 0) { + echo "Found 'Oracle' at position $pos as expected\n"; +} else { + echo "Unexpected result. Server info was:\n"; + var_dump($si); +} + +echo "Done\n"; + +?> +--EXPECTF-- +Test 1 +ATTR_SERVER_VERSION: string(%d) "%d.%d.%d.%d.%d" +Test 2 +ATTR_SERVER_INFO +Found 'Oracle' at position 0 as expected +Done diff --git a/ext/pdo_oci/tests/pdo_oci_fread_1.phpt b/ext/pdo_oci/tests/pdo_oci_fread_1.phpt new file mode 100644 index 0000000..26a92f0 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_fread_1.phpt @@ -0,0 +1,60 @@ +--TEST-- +PDO_OCI: check fread() EOF +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +if (!strpos(strtolower(getenv('PDOTEST_DSN')), 'charset=we8mswin1252')) die('skip expected output valid for WE8MSWIN1252 character set'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +$dbh = PDOTest::factory(); + +$dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + +// Initialization +$stmtarray = array( + "begin execute immediate 'drop table pdo_oci_fread_tab'; exception when others then null; end;", + "create table pdo_oci_fread_tab (id number, data clob)", + "declare + lob1 clob := 'abc' || lpad('j',4020,'j') || 'xyz'; + begin + insert into pdo_oci_fread_tab (id,data) values (1, lob1); + end;" +); + +foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); +} + +echo "Test 1\n"; + +$s = $dbh->query("select data from pdo_oci_fread_tab where id = 1"); +$r = $s->fetch(); +$sh = $r['data']; + +while (!feof($sh)) { + $buffer = fread($sh,1024); + echo '*'.$buffer.'*'; +} +echo "\n"; +fclose($sh); + +// Clean up + +$stmtarray = array( + "drop table pdo_oci_fread_tab" +); + +foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); +} + +?> +--EXPECTF-- +Test 1 +*abcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjxyz* diff --git a/ext/pdo_oci/tests/pdo_oci_quote1.phpt b/ext/pdo_oci/tests/pdo_oci_quote1.phpt new file mode 100644 index 0000000..d92317f --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_quote1.phpt @@ -0,0 +1,163 @@ +--TEST-- +Test PDO->quote() for PDO_OCI +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'; +$db = PDOTest::factory(); + +@$db->exec("drop table poq_tab"); +$db->query("create table poq_tab (t varchar2(100))"); +$stmt = $db->prepare('select * from poq_tab'); + +// The intent is that the fetched data be identical to the unquoted string. +// Remember!: use bind variables instead of PDO->quote() + +$a = array(null, "", "a", "ab", "abc", "ab'cd", "a\b\n", "'", "''", "a'", "'z", "a''b", '"'); +foreach ($a as $u) { + $q = $db->quote($u); + echo "Unquoted : "; + var_dump($u); + echo "Quoted : "; + var_dump($q); + + $db->exec("delete from poq_tab"); + + $db->query("insert into poq_tab (t) values($q)"); + $stmt->execute(); + var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); +} + +echo "Done\n"; + +@$db->exec("drop table poq_tab"); + +?> +--EXPECTF-- +Unquoted : NULL +Quoted : string(2) "''" +array(1) { + [0]=> + array(1) { + ["t"]=> + NULL + } +} +Unquoted : string(0) "" +Quoted : string(2) "''" +array(1) { + [0]=> + array(1) { + ["t"]=> + NULL + } +} +Unquoted : string(1) "a" +Quoted : string(3) "'a'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) "a" + } +} +Unquoted : string(2) "ab" +Quoted : string(4) "'ab'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "ab" + } +} +Unquoted : string(3) "abc" +Quoted : string(5) "'abc'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(3) "abc" + } +} +Unquoted : string(5) "ab'cd" +Quoted : string(8) "'ab''cd'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(5) "ab'cd" + } +} +Unquoted : string(4) "a\b +" +Quoted : string(6) "'a\b +'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(4) "a\b +" + } +} +Unquoted : string(1) "'" +Quoted : string(4) "''''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) "'" + } +} +Unquoted : string(2) "''" +Quoted : string(6) "''''''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "''" + } +} +Unquoted : string(2) "a'" +Quoted : string(5) "'a'''" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "a'" + } +} +Unquoted : string(2) "'z" +Quoted : string(5) "'''z'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(2) "'z" + } +} +Unquoted : string(4) "a''b" +Quoted : string(8) "'a''''b'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(4) "a''b" + } +} +Unquoted : string(1) """ +Quoted : string(3) "'"'" +array(1) { + [0]=> + array(1) { + ["t"]=> + string(1) """ + } +} +Done diff --git a/ext/pdo_oci/tests/pdo_oci_stream_1.phpt b/ext/pdo_oci/tests/pdo_oci_stream_1.phpt new file mode 100644 index 0000000..27a28b9 --- /dev/null +++ b/ext/pdo_oci/tests/pdo_oci_stream_1.phpt @@ -0,0 +1,113 @@ +--TEST-- +PDO_OCI: stream_get_contents length & offset test +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +if (!strpos(strtolower(getenv('PDOTEST_DSN')), 'charset=we8mswin1252')) die('skip expected output valid for WE8MSWIN1252 character set'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require(dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'); + +$dbh = PDOTest::factory(); + +$dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + +// Initialization +@$dbh->exec("drop table pdo_oci_stream_1_tab"); + +$stmtarray = array( + "create table pdo_oci_stream_1_tab (id number, data clob)", +); + +foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); +} + +$dbh->exec(" + declare + lob1 clob := 'abc' || lpad('j',30000,'j') || 'xyz'; + begin + insert into pdo_oci_stream_1_tab (id,data) values (1, 'abcdefghijklmnopqrstuvwxyz'); + insert into pdo_oci_stream_1_tab (id,data) values (2, lob1); + end;"); + +echo "Test 1\n"; + +$s = $dbh->prepare("select data from pdo_oci_stream_1_tab where id = 1"); +$s->execute(); +$r = $s->fetch(); + +// stream_get_contents ( resource $handle [, int $maxlength = -1 [, int $offset = -1 ]] ) +echo 'Read '.stream_get_contents($r['data'], 1, 1)."$\n"; // b +echo 'Read '.stream_get_contents($r['data'], 2, 1)."$\n"; // cd +echo 'Read '.stream_get_contents($r['data'], 2, 0)."$\n"; // ab +echo 'Read '.stream_get_contents($r['data'], 26, 0)."$\n"; // abcdefghijklmnopqrstuvwxyz +echo 'Read '.stream_get_contents($r['data'], 27, 0)."$\n"; // abcdefghijklmnopqrstuvwxyz +echo 'Read '.stream_get_contents($r['data'], 27, 1)."$\n"; // bcdefghijklmnopqrstuvwxyz +echo 'Read '.stream_get_contents($r['data'], 1, 20)."$\n"; // u +echo 'Read '.stream_get_contents($r['data'], 1, 25)."$\n"; // z +echo 'Read '.stream_get_contents($r['data'], 1, 26)."$\n"; // <blank> +echo 'Read '.stream_get_contents($r['data'], 1, 0)."$\n"; // a + +echo "\nTest 2\n"; + +$s = $dbh->prepare("select data from pdo_oci_stream_1_tab where id = 2"); +$s->execute(); +$r = $s->fetch(); + +echo 'Read '.stream_get_contents($r['data'], 5, 0)."\n"; // abcjj +echo 'Read '.stream_get_contents($r['data'], 5, 2)."\n"; // cjjjj +echo 'Read '.stream_get_contents($r['data'], 6, 1)."\n"; // bcjjjj +echo 'Read '.strlen(stream_get_contents($r['data'], -1,0))."\n"; // 30006 +echo 'Read '.strlen(stream_get_contents($r['data'], 0,0))."\n"; // 0 +echo 'Read '.strlen(stream_get_contents($r['data'], 0,1))."\n"; // 0 +echo 'Read '.strlen(stream_get_contents($r['data'], 10,100))."\n"; // 10 +echo 'Read '.stream_get_contents($r['data'], 6, 30000)."\n"; // jjjxyz +echo 'Read '.stream_get_contents($r['data'], 7, 30000)."\n"; // jjjxyz +echo 'Read '.strlen(stream_get_contents($r['data']))."\n"; // 0 +echo 'Read '.strlen(stream_get_contents($r['data'], 0))."\n"; // 0 +echo 'Read '.strlen(stream_get_contents($r['data'], -1))."\n"; // 0 +echo 'Read '.stream_get_contents($r['data'], -1, 30000)."\n"; // jjjxyz + +// Clean up + +$stmtarray = array( + "drop table pdo_oci_stream_1_tab" +); + +foreach ($stmtarray as $stmt) { + $dbh->exec($stmt); +} + +?> +--EXPECTF-- +Test 1 +Read b$ +Read cd$ +Read ab$ +Read abcdefghijklmnopqrstuvwxyz$ +Read abcdefghijklmnopqrstuvwxyz$ +Read bcdefghijklmnopqrstuvwxyz$ +Read u$ +Read z$ +Read $ +Read a$ + +Test 2 +Read abcjj +Read cjjjj +Read bcjjjj +Read 30006 +Read 0 +Read 0 +Read 10 +Read jjjxyz +Read jjjxyz +Read 0 +Read 0 +Read 0 +Read jjjxyz
\ No newline at end of file diff --git a/ext/pdo_oci/tests/pecl_bug_11345.phpt b/ext/pdo_oci/tests/pecl_bug_11345.phpt new file mode 100644 index 0000000..b80773a --- /dev/null +++ b/ext/pdo_oci/tests/pecl_bug_11345.phpt @@ -0,0 +1,29 @@ +--TEST-- +PECL PDO_OCI Bug #11345 (Test invalid character set name) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +// This tests only part of PECL bug 11345. The other part - testing +// when the National Language Support (NLS) environment can't be +// initialized - is very difficult to test portably. + +try { + $dbh = new PDO('oci:dbname=xxx;charset=yyy', 'abc', 'def'); +} +catch (PDOException $e) { + echo 'Connection failed: ' . $e->getMessage(). "\n"; + exit; +} + +echo "Done\n"; + +?> + +--EXPECTF-- +Connection failed: SQLSTATE[HY000]: OCINlsCharSetNameToId: unknown character set name (%s)
\ No newline at end of file diff --git a/ext/pdo_oci/tests/pecl_bug_6364.phpt b/ext/pdo_oci/tests/pecl_bug_6364.phpt new file mode 100644 index 0000000..b2981ec --- /dev/null +++ b/ext/pdo_oci/tests/pecl_bug_6364.phpt @@ -0,0 +1,71 @@ +--TEST-- +PECL PDO_OCI Bug #6364 (segmentation fault on stored procedure call with OUT binds) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require dirname(__FILE__) . '/../../pdo/tests/pdo_test.inc'; +$dbh = PDOTest::factory(); + +@$dbh->exec ("drop table bug_6364_t"); + +$dbh->exec ("create table bug_6364_t (c1 varchar2(10), c2 varchar2(10), c3 varchar2(10), c4 varchar2(10), c5 varchar2(10))"); + +$dbh->exec ("create or replace procedure bug_6364_sp(p1 IN varchar2, p2 IN varchar2, p3 IN varchar2, p4 OUT varchar2, p5 OUT varchar2) as begin insert into bug_6364_t (c1, c2, c3) values (p1, p2, p3); p4 := 'val4'; p5 := 'val5'; end;"); + +$stmt = $dbh->prepare("call bug_6364_sp('p1','p2','p3',?,?)"); + +$out_param1 = "a"; +$out_param2 = "a"; + +$stmt->bindParam(1, $out_param1,PDO::PARAM_STR, 1024); +$stmt->bindParam(2, $out_param2,PDO::PARAM_STR, 1024); + +$stmt->execute() or die ("Execution error: " . var_dump($dbh->errorInfo())); + +var_dump($out_param1); +var_dump($out_param2); + +foreach ($dbh->query("select * from bug_6364_t") as $row) { + var_dump($row); +} + +print "Done\n"; + +// Cleanup +$dbh->exec ("drop procedure bug_6364_sp"); +$dbh->exec ("drop table bug_6364_t"); + +?> + +--EXPECTF-- +string(4) "val4" +string(4) "val5" +array(10) { + ["c1"]=> + string(2) "p1" + [0]=> + string(2) "p1" + ["c2"]=> + string(2) "p2" + [1]=> + string(2) "p2" + ["c3"]=> + string(2) "p3" + [2]=> + string(2) "p3" + ["c4"]=> + NULL + [3]=> + NULL + ["c5"]=> + NULL + [4]=> + NULL +} +Done |