mbeccati Tue May 12 21:35:22 2009 UTC Removed files: /php-src/ext/pdo_oci package.xml
Modified files: /php-src/ext/pdo_oci package2.xml oci_driver.c oci_statement.c pdo_oci.c php_pdo_oci.h php_pdo_oci_int.h /php-src/ext/pdo_oci/tests bug_33707.phpt bug41996.phpt pdo_oci_attr_case.phpt pdo_oci_attr_client.phpt pdo_oci_attr_drivername.phpt pdo_oci_attr_nulls_1.phpt pdo_oci_attr_server.phpt pdo_oci_quote1.phpt pecl_bug_6364.phpt Log: MFB - Updated PDO_OCI to the latest version (from PHP_5_3 branch) # This commit reverts some changes to the tests. Given the very high # failure ratio of the current tests on HEAD I'm not going to fix them # now. Keeping ext/pdo_oci in sync with 5.3 will be much more helpful when # merging. The effort to fix the tests can start when 5.3 is out.
http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/package2.xml?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/pdo_oci/package2.xml diff -u /dev/null php-src/ext/pdo_oci/package2.xml:1.2 --- /dev/null Tue May 12 21:35:21 2009 +++ php-src/ext/pdo_oci/package2.xml Tue May 12 21:35:20 2009 @@ -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>w...@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> http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/oci_driver.c?r1=1.39&r2=1.40&diff_format=u Index: php-src/ext/pdo_oci/oci_driver.c diff -u php-src/ext/pdo_oci/oci_driver.c:1.39 php-src/ext/pdo_oci/oci_driver.c:1.40 --- php-src/ext/pdo_oci/oci_driver.c:1.39 Tue Mar 10 23:39:28 2009 +++ php-src/ext/pdo_oci/oci_driver.c Tue May 12 21:35:20 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: oci_driver.c,v 1.39 2009/03/10 23:39:28 helly Exp $ */ +/* $Id: oci_driver.c,v 1.40 2009/05/12 21:35:20 mbeccati Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -65,7 +65,7 @@ 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; @@ -80,12 +80,12 @@ 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); @@ -98,45 +98,52 @@ break; case OCI_ERROR: OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); - spprintf(&einfo->errmsg, 0, "%s: %s (%s:%d)", what, errbuf, file, line); + 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); - spprintf(&einfo->errmsg, 0, "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); + 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: - spprintf(&einfo->errmsg, 0, "%s: OCI_NEED_DATA (%s:%d)", what, file, line); + 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: - spprintf(&einfo->errmsg, 0, "%s: OCI_NO_DATA (%s:%d)", what, file, line); + 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: - spprintf(&einfo->errmsg, 0, "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); + 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: - spprintf(&einfo->errmsg, 0, "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); + 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: - spprintf(&einfo->errmsg, 0, "%s: OCI_CONTINUE (%s:%d)", what, file, line); + 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 1012: /* ORA-01012: */ case 3113: /* ORA-03133: end of file on communication channel */ @@ -147,12 +154,12 @@ 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 @@ -168,11 +175,7 @@ /* little mini hack so that we can use this code from the dbh ctor */ if (!dbh->methods) { -#if PHP_VERSION_ID > 50200 zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); -#else - zend_throw_exception_ex(php_pdo_get_exception(TSRMLS_C), 0 TSRMLS_CC, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); -#endif } return einfo->errcode; @@ -182,7 +185,7 @@ 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); @@ -197,7 +200,7 @@ 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) { @@ -218,12 +221,12 @@ 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; @@ -261,7 +264,7 @@ efree(S); return 0; } - + /* create an OCI statement handle */ OCIHandleAlloc(H->env, (dvoid*)&S->stmt, OCI_HTYPE_STMT, 0, NULL); @@ -287,11 +290,11 @@ 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); + 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); + OCI_ATTR_PREFETCH_MEMORY, H->err); } } @@ -301,7 +304,7 @@ efree(nsql); nsql = NULL; } - + return 1; } /* }}} */ @@ -322,7 +325,7 @@ 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) { @@ -345,7 +348,7 @@ } OCIHandleFree(stmt, OCI_HTYPE_STMT); - + return ret; } /* }}} */ @@ -384,7 +387,6 @@ (*quoted)[*quotedlen] = '\0'; return 1; - } /* }}} */ @@ -542,10 +544,10 @@ }; 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) { @@ -568,10 +570,10 @@ /* 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); @@ -625,7 +627,7 @@ } /* Now fire up the session */ - H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); + 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; @@ -637,13 +639,13 @@ 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) { http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/oci_statement.c?r1=1.29&r2=1.30&diff_format=u Index: php-src/ext/pdo_oci/oci_statement.c diff -u php-src/ext/pdo_oci/oci_statement.c:1.29 php-src/ext/pdo_oci/oci_statement.c:1.30 --- php-src/ext/pdo_oci/oci_statement.c:1.29 Tue Mar 10 23:39:28 2009 +++ php-src/ext/pdo_oci/oci_statement.c Tue May 12 21:35:20 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: oci_statement.c,v 1.29 2009/03/10 23:39:28 helly Exp $ */ +/* $Id: oci_statement.c,v 1.30 2009/05/12 21:35:20 mbeccati Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -41,7 +41,7 @@ } while(0) #define STMT_CALL_MSG(name, msg, params) \ - do { \ + 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) { \ @@ -49,13 +49,14 @@ } \ } 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) { @@ -78,22 +79,29 @@ 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) { - efree(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); @@ -134,7 +142,7 @@ } STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err, - S->stmt_type == OCI_STMT_SELECT ? 0 : 1, 0, NULL, NULL, + (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode)); if (!stmt->executed) { @@ -147,9 +155,26 @@ 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; @@ -167,10 +192,13 @@ 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)) { + 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; @@ -180,9 +208,6 @@ 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; @@ -199,7 +224,17 @@ 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; } @@ -211,7 +246,7 @@ 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); + P->actual_len = Z_STRLEN_P(param->parameter); *alenpp = &P->actual_len; *bufpp = Z_STRVAL_P(param->parameter); *piecep = OCI_ONE_PIECE; @@ -229,7 +264,7 @@ if (param->is_param) { pdo_oci_bound_param *P; sb4 value_sz = -1; - + P = (pdo_oci_bound_param*)param->driver_data; switch (event_type) { @@ -243,23 +278,28 @@ 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_LOB: 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 + 1; + value_sz = param->max_value_len; if (param->max_value_len == 0) { - value_sz = 4000; /* maximum size before value is interpreted as a LONG value */ + 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, @@ -273,7 +313,7 @@ &P->indicator, 0, &P->retcode, 0, 0, OCI_DATA_AT_EXEC)); } - + STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, @@ -284,6 +324,12 @@ 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: @@ -310,12 +356,76 @@ 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; } /* }}} */ @@ -353,12 +463,36 @@ 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; @@ -397,11 +531,12 @@ 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: - dyn = FALSE; if (dtype == SQLT_LBI) { dtype = SQLT_BIN; } else { @@ -411,10 +546,17 @@ 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: - dyn = FALSE; if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD #ifdef SQLT_TIMESTAMP || dtype == SQLT_TIMESTAMP @@ -442,15 +584,127 @@ 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, OCI_DEFAULT)); + 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) { + return (size_t)-1; + } + + self->offset += amt; + 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); + OCIDescriptorFree(self->lob, OCI_DTYPE_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; +} + +/* TODO: implement +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; + + return -1; +} +*/ + +static php_stream_ops oci_blob_stream_ops = { + oci_blob_write, + oci_blob_read, + oci_blob_close, + oci_blob_flush, + "pdo_oci blob stream", + NULL, /*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; @@ -464,6 +718,16 @@ 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; http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/pdo_oci.c?r1=1.13&r2=1.14&diff_format=u Index: php-src/ext/pdo_oci/pdo_oci.c diff -u php-src/ext/pdo_oci/pdo_oci.c:1.13 php-src/ext/pdo_oci/pdo_oci.c:1.14 --- php-src/ext/pdo_oci/pdo_oci.c:1.13 Tue Mar 10 23:39:28 2009 +++ php-src/ext/pdo_oci/pdo_oci.c Tue May 12 21:35:20 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_oci.c,v 1.13 2009/03/10 23:39:28 helly Exp $ */ +/* $Id: pdo_oci.c,v 1.14 2009/05/12 21:35:20 mbeccati Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -59,7 +59,7 @@ NULL, NULL, PHP_MINFO(pdo_oci), - "0.9", + "1.0.1", STANDARD_MODULE_PROPERTIES }; /* }}} */ http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/php_pdo_oci.h?r1=1.8&r2=1.9&diff_format=u Index: php-src/ext/pdo_oci/php_pdo_oci.h diff -u php-src/ext/pdo_oci/php_pdo_oci.h:1.8 php-src/ext/pdo_oci/php_pdo_oci.h:1.9 --- php-src/ext/pdo_oci/php_pdo_oci.h:1.8 Tue Mar 10 23:39:28 2009 +++ php-src/ext/pdo_oci/php_pdo_oci.h Tue May 12 21:35:20 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_pdo_oci.h,v 1.8 2009/03/10 23:39:28 helly Exp $ */ +/* $Id: php_pdo_oci.h,v 1.9 2009/05/12 21:35:20 mbeccati Exp $ */ #ifndef PHP_PDO_OCI_H #define PHP_PDO_OCI_H http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/php_pdo_oci_int.h?r1=1.12&r2=1.13&diff_format=u Index: php-src/ext/pdo_oci/php_pdo_oci_int.h diff -u php-src/ext/pdo_oci/php_pdo_oci_int.h:1.12 php-src/ext/pdo_oci/php_pdo_oci_int.h:1.13 --- php-src/ext/pdo_oci/php_pdo_oci_int.h:1.12 Tue Mar 10 23:39:28 2009 +++ php-src/ext/pdo_oci/php_pdo_oci_int.h Tue May 12 21:35:20 2009 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_pdo_oci_int.h,v 1.12 2009/03/10 23:39:28 helly Exp $ */ +/* $Id: php_pdo_oci_int.h,v 1.13 2009/05/12 21:35:20 mbeccati Exp $ */ #include <oci.h> @@ -38,8 +38,8 @@ ub2 charset; sword last_err; - unsigned attached:1; - unsigned _reserved:31; + unsigned attached:1; + unsigned _reserved:31; pdo_oci_error_info einfo; } pdo_oci_db_handle; @@ -51,7 +51,9 @@ sb2 indicator; char *data; - unsigned long datalen; + ub4 datalen; + + ub2 dtype; } pdo_oci_column; @@ -60,10 +62,11 @@ OCIStmt *stmt; OCIError *err; sword last_err; - ub2 stmt_type; - ub4 exec_type; + 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 { http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/bug_33707.phpt?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/pdo_oci/tests/bug_33707.phpt diff -u /dev/null php-src/ext/pdo_oci/tests/bug_33707.phpt:1.2 --- /dev/null Tue May 12 21:35:21 2009 +++ php-src/ext/pdo_oci/tests/bug_33707.phpt Tue May 12 21:35:21 2009 @@ -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)" +} http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/bug41996.phpt?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/pdo_oci/tests/bug41996.phpt diff -u php-src/ext/pdo_oci/tests/bug41996.phpt:1.1 php-src/ext/pdo_oci/tests/bug41996.phpt:1.2 --- php-src/ext/pdo_oci/tests/bug41996.phpt:1.1 Mon Jul 21 17:17:58 2008 +++ php-src/ext/pdo_oci/tests/bug41996.phpt Tue May 12 21:35:21 2009 @@ -2,7 +2,7 @@ PDO OCI Bug #41996 (Problem accessing Oracle ROWID) --SKIPIF-- <?php -/* $Id: bug41996.phpt,v 1.1 2008/07/21 17:17:58 pajoye Exp $ */ +/* $Id: bug41996.phpt,v 1.2 2009/05/12 21:35:21 mbeccati Exp $ */ if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); require dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'; PDOTest::skip(); http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/pdo_oci_attr_case.phpt?r1=1.3&r2=1.4&diff_format=u Index: php-src/ext/pdo_oci/tests/pdo_oci_attr_case.phpt diff -u php-src/ext/pdo_oci/tests/pdo_oci_attr_case.phpt:1.3 php-src/ext/pdo_oci/tests/pdo_oci_attr_case.phpt:1.4 --- php-src/ext/pdo_oci/tests/pdo_oci_attr_case.phpt:1.3 Tue May 27 13:16:27 2008 +++ php-src/ext/pdo_oci/tests/pdo_oci_attr_case.phpt Tue May 12 21:35:21 2009 @@ -57,25 +57,25 @@ Test 1 - Force column names to lower case int(2) array(1) { - [u"dummy"]=> - unicode(1) "X" + ["dummy"]=> + string(1) "X" } Test 2 - Leave column names as returned by the database driver int(0) array(1) { - [u"DUMMY"]=> - unicode(1) "X" + ["DUMMY"]=> + string(1) "X" } Test 3 - Force column names to upper case int(1) array(1) { - [u"DUMMY"]=> - unicode(1) "X" + ["DUMMY"]=> + string(1) "X" } Test 4 - Setting on statement has no effect. Attempt lower case but get upper Mode desired is 2 array(1) { - [u"DUMMY"]=> - unicode(1) "X" + ["DUMMY"]=> + string(1) "X" } Done http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/pdo_oci_attr_client.phpt?r1=1.3&r2=1.4&diff_format=u Index: php-src/ext/pdo_oci/tests/pdo_oci_attr_client.phpt diff -u php-src/ext/pdo_oci/tests/pdo_oci_attr_client.phpt:1.3 php-src/ext/pdo_oci/tests/pdo_oci_attr_client.phpt:1.4 --- php-src/ext/pdo_oci/tests/pdo_oci_attr_client.phpt:1.3 Tue May 27 13:16:27 2008 +++ php-src/ext/pdo_oci/tests/pdo_oci_attr_client.phpt Tue May 12 21:35:21 2009 @@ -38,6 +38,6 @@ ?> --EXPECTF-- -ATTR_CLIENT_VERSION: unicode(%d) "%d.%s" +ATTR_CLIENT_VERSION: string(%d) "%d.%s" Version OK, so far as can be portably checked Done http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt?r1=1.3&r2=1.4&diff_format=u Index: php-src/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt diff -u php-src/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt:1.3 php-src/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt:1.4 --- php-src/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt:1.3 Tue May 27 13:16:27 2008 +++ php-src/ext/pdo_oci/tests/pdo_oci_attr_drivername.phpt Tue May 12 21:35:21 2009 @@ -17,5 +17,5 @@ echo "Done\n"; ?> --EXPECT-- -unicode(3) "oci" +string(3) "oci" Done http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt?r1=1.3&r2=1.4&diff_format=u Index: php-src/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt diff -u php-src/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt:1.3 php-src/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt:1.4 --- php-src/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt:1.3 Tue May 27 13:16:27 2008 +++ php-src/ext/pdo_oci/tests/pdo_oci_attr_nulls_1.phpt Tue May 12 21:35:21 2009 @@ -56,6 +56,6 @@ NULL NULL PDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: int(2) -unicode(0) "" -unicode(0) "" -Done +string(0) "" +string(0) "" +Done \ No newline at end of file http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/pdo_oci_attr_server.phpt?r1=1.3&r2=1.4&diff_format=u Index: php-src/ext/pdo_oci/tests/pdo_oci_attr_server.phpt diff -u php-src/ext/pdo_oci/tests/pdo_oci_attr_server.phpt:1.3 php-src/ext/pdo_oci/tests/pdo_oci_attr_server.phpt:1.4 --- php-src/ext/pdo_oci/tests/pdo_oci_attr_server.phpt:1.3 Tue May 27 13:16:27 2008 +++ php-src/ext/pdo_oci/tests/pdo_oci_attr_server.phpt Tue May 12 21:35:21 2009 @@ -33,7 +33,7 @@ ?> --EXPECTF-- Test 1 -ATTR_SERVER_VERSION: unicode(%d) "%d.%d.%d.%d.%d" +ATTR_SERVER_VERSION: string(%d) "%d.%d.%d.%d.%d" Test 2 ATTR_SERVER_INFO Found 'Oracle' at position 0 as expected http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/pdo_oci_quote1.phpt?r1=1.3&r2=1.4&diff_format=u Index: php-src/ext/pdo_oci/tests/pdo_oci_quote1.phpt diff -u php-src/ext/pdo_oci/tests/pdo_oci_quote1.phpt:1.3 php-src/ext/pdo_oci/tests/pdo_oci_quote1.phpt:1.4 --- php-src/ext/pdo_oci/tests/pdo_oci_quote1.phpt:1.3 Tue May 27 13:16:27 2008 +++ php-src/ext/pdo_oci/tests/pdo_oci_quote1.phpt Tue May 12 21:35:21 2009 @@ -2,7 +2,6 @@ Test PDO->quote() for PDO_OCI --SKIPIF-- <?php -die('skip triggers query errors'); if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); PDOTest::skip(); @@ -42,7 +41,7 @@ ?> --EXPECTF-- Unquoted : NULL -Quoted : unicode(2) "''" +Quoted : string(2) "''" array(1) { [0]=> array(1) { @@ -50,8 +49,8 @@ NULL } } -Unquoted : unicode(0) "" -Quoted : unicode(2) "''" +Unquoted : string(0) "" +Quoted : string(2) "''" array(1) { [0]=> array(1) { @@ -59,106 +58,106 @@ NULL } } -Unquoted : unicode(1) "a" -Quoted : unicode(3) "'a'" +Unquoted : string(1) "a" +Quoted : string(3) "'a'" array(1) { [0]=> array(1) { ["t"]=> - unicode(1) "a" + string(1) "a" } } -Unquoted : unicode(2) "ab" -Quoted : unicode(4) "'ab'" +Unquoted : string(2) "ab" +Quoted : string(4) "'ab'" array(1) { [0]=> array(1) { ["t"]=> - unicode(2) "ab" + string(2) "ab" } } -Unquoted : unicode(3) "abc" -Quoted : unicode(5) "'abc'" +Unquoted : string(3) "abc" +Quoted : string(5) "'abc'" array(1) { [0]=> array(1) { ["t"]=> - unicode(3) "abc" + string(3) "abc" } } -Unquoted : unicode(5) "ab'cd" -Quoted : unicode(8) "'ab''cd'" +Unquoted : string(5) "ab'cd" +Quoted : string(8) "'ab''cd'" array(1) { [0]=> array(1) { ["t"]=> - unicode(5) "ab'cd" + string(5) "ab'cd" } } -Unquoted : unicode(4) "a\b +Unquoted : string(4) "a\b " -Quoted : unicode(6) "'a\b +Quoted : string(6) "'a\b '" array(1) { [0]=> array(1) { ["t"]=> - unicode(4) "a\b + string(4) "a\b " } } -Unquoted : unicode(1) "'" -Quoted : unicode(4) "''''" +Unquoted : string(1) "'" +Quoted : string(4) "''''" array(1) { [0]=> array(1) { ["t"]=> - unicode(1) "'" + string(1) "'" } } -Unquoted : unicode(2) "''" -Quoted : unicode(6) "''''''" +Unquoted : string(2) "''" +Quoted : string(6) "''''''" array(1) { [0]=> array(1) { ["t"]=> - unicode(2) "''" + string(2) "''" } } -Unquoted : unicode(2) "a'" -Quoted : unicode(5) "'a'''" +Unquoted : string(2) "a'" +Quoted : string(5) "'a'''" array(1) { [0]=> array(1) { ["t"]=> - unicode(2) "a'" + string(2) "a'" } } -Unquoted : unicode(2) "'z" -Quoted : unicode(5) "'''z'" +Unquoted : string(2) "'z" +Quoted : string(5) "'''z'" array(1) { [0]=> array(1) { ["t"]=> - unicode(2) "'z" + string(2) "'z" } } -Unquoted : unicode(4) "a''b" -Quoted : unicode(8) "'a''''b'" +Unquoted : string(4) "a''b" +Quoted : string(8) "'a''''b'" array(1) { [0]=> array(1) { ["t"]=> - unicode(4) "a''b" + string(4) "a''b" } } -Unquoted : unicode(1) """ -Quoted : unicode(3) "'"'" +Unquoted : string(1) """ +Quoted : string(3) "'"'" array(1) { [0]=> array(1) { ["t"]=> - unicode(1) """ + string(1) """ } } Done http://cvs.php.net/viewvc.cgi/php-src/ext/pdo_oci/tests/pecl_bug_6364.phpt?r1=1.3&r2=1.4&diff_format=u Index: php-src/ext/pdo_oci/tests/pecl_bug_6364.phpt diff -u php-src/ext/pdo_oci/tests/pecl_bug_6364.phpt:1.3 php-src/ext/pdo_oci/tests/pecl_bug_6364.phpt:1.4 --- php-src/ext/pdo_oci/tests/pecl_bug_6364.phpt:1.3 Tue May 27 13:16:27 2008 +++ php-src/ext/pdo_oci/tests/pecl_bug_6364.phpt Tue May 12 21:35:21 2009 @@ -44,21 +44,21 @@ ?> --EXPECTF-- -unicode(4) "val4" -unicode(4) "val5" +string(4) "val4" +string(4) "val5" array(10) { ["c1"]=> - unicode(2) "p1" + string(2) "p1" [0]=> - unicode(2) "p1" + string(2) "p1" ["c2"]=> - unicode(2) "p2" + string(2) "p2" [1]=> - unicode(2) "p2" + string(2) "p2" ["c3"]=> - unicode(2) "p3" + string(2) "p3" [2]=> - unicode(2) "p3" + string(2) "p3" ["c4"]=> NULL [3]=>
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php