diff options
author | Wez Furlong <wez@php.net> | 2004-05-17 15:42:33 +0000 |
---|---|---|
committer | Wez Furlong <wez@php.net> | 2004-05-17 15:42:33 +0000 |
commit | 1b1ced539b959b1122abee2f206a3c1bf3970919 (patch) | |
tree | f6bd33d225d77871a2005b72f7972b880d67b6d0 /ext/pdo_oci | |
parent | 684be9cf36c48893d98f980eafc4fe08ac98dbfa (diff) | |
download | php-git-1b1ced539b959b1122abee2f206a3c1bf3970919.tar.gz |
OCI driver for PDO, supporting bound input and output parameters.
Will not build under unix yet.
Diffstat (limited to 'ext/pdo_oci')
-rwxr-xr-x | ext/pdo_oci/CREDITS | 2 | ||||
-rw-r--r-- | ext/pdo_oci/EXPERIMENTAL | 0 | ||||
-rwxr-xr-x | ext/pdo_oci/config.m4 | 169 | ||||
-rwxr-xr-x | ext/pdo_oci/config.w32 | 45 | ||||
-rwxr-xr-x | ext/pdo_oci/oci_driver.c | 379 | ||||
-rwxr-xr-x | ext/pdo_oci/oci_statement.c | 410 | ||||
-rwxr-xr-x | ext/pdo_oci/pdo_oci.c | 120 | ||||
-rwxr-xr-x | ext/pdo_oci/php_pdo_oci.h | 53 | ||||
-rwxr-xr-x | ext/pdo_oci/php_pdo_oci_int.h | 80 |
9 files changed, 1258 insertions, 0 deletions
diff --git a/ext/pdo_oci/CREDITS b/ext/pdo_oci/CREDITS new file mode 100755 index 0000000000..63e863a22d --- /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 0000000000..e69de29bb2 --- /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 0000000000..df8f05d303 --- /dev/null +++ b/ext/pdo_oci/config.m4 @@ -0,0 +1,169 @@ +dnl +dnl $Id$ +dnl + +dnl This file is more or less a straight copy from oci8; it won't work with pdo_oci yet! + +AC_DEFUN(PHP_OCI_IF_DEFINED,[ + old_CPPFLAGS=$CPPFLAGS + CPPFLAGS=$3 + AC_EGREP_CPP(yes,[ +#include <oci.h> +#if defined($1) + yes +#endif + ],[ + CPPFLAGS=$old_CPPFLAGS + $2 + ],[ + CPPFLAGS=$old_CPPFLAGS + ]) +]) + +AC_DEFUN(AC_OCI_VERSION,[ + AC_MSG_CHECKING([Oracle version]) + if test -s "$OCI_DIR/orainst/unix.rgs"; then + OCI_VERSION=`grep '"ocommon"' $OCI_DIR/orainst/unix.rgs | sed 's/[ ][ ]*/:/g' | cut -d: -f 6 | cut -c 2-4` + test -z "$OCI_VERSION" && OCI_VERSION=7.3 + elif test -f $OCI_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME.10.1; then + OCI_VERSION=10.1 + elif test -f $OCI_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME.9.0; then + OCI_VERSION=9.0 + elif test -f $OCI_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME.8.0; then + OCI_VERSION=8.1 + elif test -f $OCI_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME.1.0; then + OCI_VERSION=8.0 + elif test -f $OCI_DIR/lib/libclntsh.a; then + if test -f $OCI_DIR/lib/libcore4.a; then + OCI_VERSION=8.0 + else + OCI_VERSION=8.1 + fi + else + AC_MSG_ERROR(Oracle-OCI needed libraries not found) + fi + AC_MSG_RESULT($OCI_VERSION) +]) + +PHP_ARG_WITH(oci, for Oracle-OCI support, +[ --with-oci[=DIR] Include Oracle-oci support. Default DIR is ORACLE_HOME.]) + +if test "$PHP_OCI" != "no"; then + AC_MSG_CHECKING([Oracle Install-Dir]) + if test "$PHP_OCI" = "yes"; then + OCI_DIR=$ORACLE_HOME + else + OCI_DIR=$PHP_OCI + fi + AC_MSG_RESULT($OCI_DIR) + + if test -d "$OCI_DIR/rdbms/public"; then + PHP_ADD_INCLUDE($OCI_DIR/rdbms/public) + OCI_INCLUDES="$OCI_INCLUDES -I$OCI_DIR/rdbms/public" + fi + if test -d "$OCI_DIR/rdbms/demo"; then + PHP_ADD_INCLUDE($OCI_DIR/rdbms/demo) + OCI_INCLUDES="$OCI_INCLUDES -I$OCI_DIR/rdbms/demo" + fi + if test -d "$OCI_DIR/network/public"; then + PHP_ADD_INCLUDE($OCI_DIR/network/public) + OCI_INCLUDES="$OCI_INCLUDES -I$OCI_DIR/network/public" + fi + if test -d "$OCI_DIR/plsql/public"; then + PHP_ADD_INCLUDE($OCI_DIR/plsql/public) + OCI_INCLUDES="$OCI_INCLUDES -I$OCI_DIR/plsql/public" + fi + + if test -f "$OCI_DIR/lib/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $OCI_DIR/lib/sysliblist`, OCI_SYSLIB) + elif test -f "$OCI_DIR/rdbms/lib/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $OCI_DIR/rdbms/lib/sysliblist`, OCI_SYSLIB) + fi + + AC_OCI_VERSION($OCI_DIR) + case $OCI_VERSION in + 8.0) + PHP_ADD_LIBRARY_WITH_PATH(nlsrtl3, "", OCI_SHARED_LIBADD) + PHP_ADD_LIBRARY_WITH_PATH(core4, "", OCI_SHARED_LIBADD) + PHP_ADD_LIBRARY_WITH_PATH(psa, "", OCI_SHARED_LIBADD) + PHP_ADD_LIBRARY_WITH_PATH(clntsh, $OCI_DIR/lib, OCI_SHARED_LIBADD) + ;; + + 8.1) + PHP_ADD_LIBRARY(clntsh, 1, OCI_SHARED_LIBADD) + PHP_ADD_LIBPATH($OCI_DIR/lib, OCI_SHARED_LIBADD) + + dnl + dnl OCI_ATTR_STATEMENT is not available in all 8.1.x versions + dnl + PHP_OCI_IF_DEFINED(OCI_ATTR_STATEMENT, [AC_DEFINE(HAVE_OCI_ATTR_STATEMENT,1,[ ])], $OCI_INCLUDES) + ;; + + 9.0) + PHP_ADD_LIBRARY(clntsh, 1, OCI_SHARED_LIBADD) + PHP_ADD_LIBPATH($OCI_DIR/lib, OCI_SHARED_LIBADD) + AC_DEFINE(HAVE_OCI_ATTR_STATEMENT,1,[ ]) + + dnl These functions are only available in version >= 9.2 + PHP_CHECK_LIBRARY(clntsh, OCIEnvNlsCreate, + [ + PHP_CHECK_LIBRARY(clntsh, OCINlsCharSetNameToId, + [ + AC_DEFINE(HAVE_OCI_9_2,1,[ ]) + OCI_VERSION=9.2 + ], [], [ + -L$OCI_DIR/lib $OCI_SHARED_LIBADD + ]) + ], [], [ + -L$OCI_DIR/lib $OCI_SHARED_LIBADD + ]) + ;; + + 10.1) + PHP_ADD_LIBRARY(clntsh, 1, OCI_SHARED_LIBADD) + PHP_ADD_LIBPATH($OCI_DIR/lib, OCI_SHARED_LIBADD) + AC_DEFINE(HAVE_OCI_ATTR_STATEMENT,1,[ ]) + AC_DEFINE(HAVE_OCI_9_2,1,[ ]) + ;; + *) + AC_MSG_ERROR(Unsupported Oracle version!) + ;; + esac + + dnl + dnl Check if we need to add -locijdbc8 + dnl + PHP_CHECK_LIBRARY(clntsh, OCILobIsTemporary, + [ + AC_DEFINE(HAVE_OCI_TEMP_LOB,1,[ ]) + ], [ + PHP_CHECK_LIBRARY(ocijdbc8, OCILobIsTemporary, + [ + PHP_ADD_LIBRARY(ocijdbc8, 1, OCI_SHARED_LIBADD) + AC_DEFINE(HAVE_OCI_TEMP_LOB,1,[ ]) + ], [], [ + -L$OCI_DIR/lib $OCI_SHARED_LIBADD + ]) + ], [ + -L$OCI_DIR/lib $OCI_SHARED_LIBADD + ]) + + dnl + dnl Check if we have collections + dnl + PHP_CHECK_LIBRARY(clntsh, OCICollAssign, + [ + AC_DEFINE(PHP_OCI_HAVE_COLLECTIONS,1,[ ]) + ], [], [ + -L$OCI_DIR/lib $OCI_SHARED_LIBADD + ]) + + + PHP_NEW_EXTENSION(pdo_oci, pdo_oci.c oci_driver.c oci_statement.c, $ext_shared) + AC_DEFINE(HAVE_OCI,1,[ ]) + + PHP_SUBST_OLD(OCI_SHARED_LIBADD) + PHP_SUBST_OLD(OCI_DIR) + PHP_SUBST_OLD(OCI_VERSION) + +fi diff --git a/ext/pdo_oci/config.w32 b/ext/pdo_oci/config.w32 new file mode 100755 index 0000000000..7c1e4bd02c --- /dev/null +++ b/ext/pdo_oci/config.w32 @@ -0,0 +1,45 @@ +// $Id$ +// vim:ft=javascript + +ARG_WITH("pdo-oci", "Oracle OCI support for PDO", "yes,shared"); + +if (PHP_PDO_OCI != "no") { + + php_oci_dirs = new Array( + PHP_PDO_OCI, + PHP_PDO_OCI + "\\oci", + PHP_PHP_BUILD + "\\oci92", + PHP_PHP_BUILD + "\\oci805" + ); + + php_oci_lib_paths = ""; + php_oci_inc_paths = ""; + + // find the oracle install + for (i = 0; i < php_oci_dirs.length; i++) { + php_oci_lib_paths += php_oci_dirs[i] + "\\lib;"; + php_oci_lib_paths += php_oci_dirs[i] + "\\lib\\msvc;"; + php_oci_inc_paths += php_oci_dirs[i] + "\\include;"; + } + + if (CHECK_HEADER_ADD_INCLUDE("oci.h", "CFLAGS_PDO_OCI", php_oci_inc_paths) && + CHECK_LIB("oci.lib", "pdo_oci", php_oci_lib_paths) + ) { + + EXTENSION('pdo_oci', 'pdo_oci.c oci_driver.c oci_statement.c'); + ADD_FLAG('CFLAGS_PDO_OCI', "/I ..\\pecl"); + + AC_DEFINE('HAVE_OCI_TEMP_LOB', 1); + AC_DEFINE('HAVE_OCI', 1); + AC_DEFINE('HAVE_OCI_ATTR_STATEMENT', 1); + AC_DEFINE('PHP_OCI_HAVE_COLLECTIONS', 1); + + /* LOCAL HACKS: assume you have OCI 9.2 */ + AC_DEFINE('HAVE_OCIENVCREATE', 1); + AC_DEFINE('HAVE_OCIENVNLSCREATE', 1); + } 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 100755 index 0000000000..3c39a9e553 --- /dev/null +++ b/ext/pdo_oci/oci_driver.c @@ -0,0 +1,379 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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" + +ub4 _oci_error(OCIError *err, char *what, sword status, const char *file, int line TSRMLS_DC) /* {{{ */ +{ + text errbuf[1024] = "<<Unknown>>"; + sb4 errcode = 0; + + switch (status) { + case OCI_SUCCESS: + break; + case OCI_ERROR: + OCIErrorGet(err, (ub4)1, NULL, &errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: %d %s", file, line, what, errcode, errbuf); + break; + case OCI_SUCCESS_WITH_INFO: + OCIErrorGet(err, (ub4)1, NULL, &errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_SUCCESS_WITH_INFO: %s", file, line, what, errbuf); + break; + case OCI_NEED_DATA: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_NEED_DATA", file, line, what); + break; + case OCI_NO_DATA: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_NO_DATA", file, line, what); + break; + case OCI_INVALID_HANDLE: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_INVALID_HANDLE", file, line, what); + break; + case OCI_STILL_EXECUTING: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_STILL_EXECUTING", file, line, what); + break; + case OCI_CONTINUE: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_CONTINUE", file, line, what); + break; + } + return errcode; +} +/* }}} */ + +ub4 oci_handle_error(pdo_dbh_t *dbh, pdo_oci_db_handle *H, ub4 errcode) /* {{{ */ +{ + switch (errcode) { + case 1013: /* user requested cancel of current operation */ + zend_bailout(); + break; + case 22: /* ORA-00022: invalid session id */ + case 1012: /* ORA-01012: */ + case 3113: /* ORA-03133: end of file on communication channel */ + case 604: + case 1041: + /* consider the connection closed */ + dbh->is_closed = 1; + H->attached = 0; + return 1; + } + return 0; +} +/* }}} */ + +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_error(H->err, "OCIServerDetach", H->last_err); + } + H->attached = 0; + } + + if (H->server) { + OCIHandleFree(H->server, OCI_HTYPE_SERVER); + H->server = NULL; + } + + OCIHandleFree(H->err, OCI_HTYPE_ERROR); + H->err = NULL; + + if (H->charset && H->env) { + OCIHandleFree(H->env, OCI_HTYPE_ENV); + H->env = 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 TSRMLS_DC) +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + pdo_oci_stmt *S = ecalloc(1, sizeof(*S)); + + S->H = H; + + /* create an OCI statement handle */ + OCIHandleAlloc(H->env, &S->stmt, OCI_HTYPE_STMT, 0, NULL); + + /* and our own private error handle */ + OCIHandleAlloc(H->env, &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 (H->last_err) { + H->last_err = oci_error(H->err, "OCIStmtPrepare", H->last_err); + OCIHandleFree(S->stmt, OCI_HTYPE_STMT); + OCIHandleFree(S->err, OCI_HTYPE_ERROR); + efree(S); + oci_handle_error(dbh, H, H->last_err); + return 0; + } + } + + stmt->driver_data = S; + stmt->methods = &oci_stmt_methods; + + return 1; +} + +static int oci_handle_doer(pdo_dbh_t *dbh, const char *sql TSRMLS_DC) +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + + return 0; +} + +static int oci_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, char **quoted, int *quotedlen TSRMLS_DC) +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + + return 0; +} + +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_error(H->err, "OCITransCommit", H->last_err); + oci_handle_error(dbh, H, H->last_err); + 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_error(H->err, "OCITransRollback", H->last_err); + oci_handle_error(dbh, H, H->last_err); + 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 (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_error(H->err, "OCITransCommit", H->last_err); + oci_handle_error(dbh, H, H->last_err); + return 0; + } + dbh->in_txn = 0; + } + + convert_to_long(val); + + dbh->auto_commit = Z_LVAL_P(val); + + return 1; +} + +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 +}; + +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, 4); + + H = pecalloc(1, sizeof(*H), dbh->is_persistent); + + /* allocate an environment */ +#if HAVE_OCIENVNLSCREATE + if (vars[0].optval) { + H->charset = OCINlsCharSetNameToId(pdo_oci_Env, vars[0].optval); + if (H->charset) { + OCIEnvNlsCreate(&H->env, PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset); + } + } +#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_error(H->err, "pdo_oci_handle_factory", H->last_err); + 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_error(H->err, "OCIHandleAlloc: OCI_HTYPE_SVCCTX", H->last_err); + goto cleanup; + } + + H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->session, OCI_HTYPE_SESSION, 0, NULL); + if (H->last_err) { + oci_error(H->err, "OCIHandleAlloc: OCI_HTYPE_SESSION", H->last_err); + 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_error(H->err, "OCIAttrSet: OCI_ATTR_SERVER", H->last_err); + goto cleanup; + } + + /* 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_error(H->err, "OCIAttrSet: OCI_ATTR_USERNAME", H->last_err); + goto cleanup; + } + + /* 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_error(H->err, "OCIAttrSet: OCI_ATTR_PASSWORD", H->last_err); + 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_error(H->err, "OCISessionBegin:", H->last_err); + 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_error(H->err, "OCIAttrSet: OCI_ATTR_SESSION:", H->last_err); + goto cleanup; + } + + dbh->driver_data = H; + dbh->methods = &oci_methods; + dbh->alloc_own_columns = 1; + dbh->supports_placeholders = 1; + dbh->placeholders_can_be_strings = 1; + + 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 +}; + +/* + * 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 100755 index 0000000000..39aa4584b8 --- /dev/null +++ b/ext/pdo_oci/oci_statement.c @@ -0,0 +1,410 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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" + +#define STMT_CALL(name, params) \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, #name, S->last_err, __FILE__, __LINE__ TSRMLS_CC); \ + if (S->last_err) { \ + oci_handle_error(stmt->dbh, S->H, S->last_err); \ + return 0; \ + } + +#define STMT_CALL_MSG(name, msg, params) \ + S->last_err = name params; \ + S->last_err = _oci_error(S->err, #name ": " #msg, S->last_err, __FILE__, __LINE__ TSRMLS_CC); \ + if (S->last_err) { \ + oci_handle_error(stmt->dbh, S->H, S->last_err); \ + return 0; \ + } + + +static int oci_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) +{ + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + 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; + } + + if (S->cols) { + for (i = 0; i < stmt->column_count; i++) { + if (S->cols[i].data) { + efree(S->cols[i].data); + } + } + efree(S->cols); + S->cols = NULL; + } + efree(S); + + 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; + + 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); + } + + STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err, + S->stmt_type == OCI_STMT_SELECT ? 0 : 1, 0, NULL, NULL, + (stmt->dbh->auto_commit & !stmt->dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT)); + + 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; + + 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*)ecalloc(1, sizeof(pdo_oci_bound_param)); + 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 (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); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "P->thing should not be set??"); + return OCI_ERROR; + } + + *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*)ecalloc(1, sizeof(pdo_oci_bound_param)); + 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 (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) = emalloc(Z_STRLEN_P(param->parameter)+1); + + + *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_ALLOC: + P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param)); + param->driver_data = P; + + /* figure out what we're doing */ + switch (param->param_type) { + case PDO_PARAM_LOB: + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_STR: + default: + P->oci_type = SQLT_CHR; + convert_to_string(param->parameter); + value_sz = param->max_value_len + 1; + P->actual_len = Z_STRLEN_P(param->parameter); + + } + + 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; + return 1; + + case PDO_PARAM_EVT_EXEC_POST: + /* fixup stuff set in motion in oci_bind_output_cb */ + if (P->indicator == -1) { + /* set up a NULL value */ + if (Z_TYPE_P(param->parameter) == IS_STRING && Z_STRVAL_P(param->parameter) != empty_string) { + /* 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 && Z_STRVAL_P(param->parameter) != empty_string) { + 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'; + } + + return 1; + } + } + + return 1; +} + +static int oci_stmt_fetch(pdo_stmt_t *stmt TSRMLS_DC) +{ + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + + S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); + + if (S->last_err == OCI_NO_DATA) { + /* no (more) data */ + return 0; + } + + if (S->last_err == OCI_NEED_DATA) { + oci_error(S->err, "OCI_NEED_DATA", S->last_err); + return 0; + } + + if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { + return 1; + } + + oci_error(S->err, "OCIStmtFetch", S->last_err); + oci_handle_error(stmt->dbh, S->H, S->last_err); + + return 0; +} + +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, ¶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(colname, namelen); + + /* how much room do we need to store the field */ + switch (dtype) { + case SQLT_BIN: + default: + dyn = FALSE; + if (dtype == SQLT_DAT || dtype == SQLT_NUM +#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; + } else { + S->cols[colno].datalen = col->maxlen + 1; /* 1 for NUL terminator */ + } + if (dtype == SQLT_BIN) { + S->cols[colno].datalen *= 3; + } + S->cols[colno].data = emalloc(S->cols[colno].datalen); + dtype = SQLT_CHR; + + /* returning data as a string */ + col->param_type = PDO_PARAM_STR; + } + + if (!dyn) { + 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)); + } + + return 1; +} + +static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len 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 */ + *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/pdo_oci.c b/ext/pdo_oci/pdo_oci.c new file mode 100755 index 0000000000..154faa77da --- /dev/null +++ b/ext/pdo_oci/pdo_oci.c @@ -0,0 +1,120 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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[] */ +function_entry pdo_oci_functions[] = { + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ pdo_oci_module_entry */ +zend_module_entry pdo_oci_module_entry = { + STANDARD_MODULE_HEADER, + "pdo_oci", + pdo_oci_functions, + PHP_MINIT(pdo_oci), + PHP_MSHUTDOWN(pdo_oci), + NULL, + NULL, + PHP_MINFO(pdo_oci), + "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 100755 index 0000000000..fe9f24749f --- /dev/null +++ b/ext/pdo_oci/php_pdo_oci.h @@ -0,0 +1,53 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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 PHP_WIN32 +#define PHP_PDO_OCI_API __declspec(dllexport) +#else +#define PHP_PDO_OCI_API +#endif + +#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 100755 index 0000000000..4933f9bbb3 --- /dev/null +++ b/ext/pdo_oci/php_pdo_oci_int.h @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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> + +/* 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_db_handle; + +typedef struct { + OCIDefine *def; + ub2 fetched_len; + ub2 retcode; + sb2 indicator; + + char *data; + unsigned long datalen; + +} pdo_oci_column; + +typedef struct { + pdo_oci_db_handle *H; + OCIStmt *stmt; + OCIError *err; + sword last_err; + ub2 stmt_type; + + pdo_oci_column *cols; +} 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. */ +} 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, char *what, sword status, const char *file, int line TSRMLS_DC); +#define oci_error(e, w, s) _oci_error(e, w, s, __FILE__, __LINE__ TSRMLS_CC) + +ub4 oci_handle_error(pdo_dbh_t *dbh, pdo_oci_db_handle *H, ub4 errcode); + +extern struct pdo_stmt_methods oci_stmt_methods; + |