/* TODO list:
*
- * - better oracle-error handling.
- * - Commit/Rollback testing.....
* - php.ini flags
* - Error mode (print or shut up?)
- * - returning refcursors as statement handles
* - OCIPasswordChange()
* - Prefetching control
- * - LONG, LONG RAW, RAW is now limited to 2MB (should be user-settable);
* - binding of arrays
* - Truncate input values to the bind size
* - Character sets for NCLOBS
- * - piecewise operation for longs, lobs etc
* - split the module into an upper (php-callable) and lower (c-callable) layer!
* - make_pval needs some cleanup....
* - NULLS (retcode/indicator) needs some more work for describing & binding
/* }}} */
/* {{{ startup/shutdown/info/internal function prototypes */
+#ifndef ZEND_MODULE_INFO_FUNC_ARGS
+#define ZEND_MODULE_INFO_FUNC_ARGS void
+#endif
+
int php3_minit_oci8(INIT_FUNC_ARGS);
int php3_rinit_oci8(INIT_FUNC_ARGS);
int php3_mshutdown_oci8(SHUTDOWN_FUNC_ARGS);
static oci8_statement *oci8_get_stmt(int, const char *, HashTable *);
static oci8_out_column *oci8_get_col(oci8_statement *, int, pval *, char *);
-static int oci8_make_pval(pval *,oci8_statement *,oci8_out_column *, char *,HashTable *, int mode);
+static int oci8_make_pval(pval *,oci8_statement *,oci8_out_column *, char *, int mode);
static int oci8_parse(oci8_connection *, char *, int, HashTable *);
static int oci8_execute(oci8_statement *, char *,ub4 mode,HashTable *);
static int oci8_fetch(oci8_statement *, ub4, char *);
REGISTER_LONG_CONSTANT("OCI_B_BLOB",SQLT_BLOB, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OCI_B_ROWID",SQLT_RDD, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("OCI_B_CURSOR",SQLT_RSET, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("OCI_B_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT);
/* for OCIFetchInto & OCIResult */
REGISTER_LONG_CONSTANT("OCI_ASSOC",OCI_ASSOC, CONST_CS | CONST_PERSISTENT);
return SUCCESS;
}
+static int _stmt_cleanup(list_entry *le)
+{
+ oci8_statement *statement;
+ OCI8_TLS_VARS;
+
+ if (le->type == OCI8_GLOBAL(php3_oci8_module).le_stmt) {
+ statement = (oci8_statement *) le->ptr;
+
+ if (!statement->conn->open) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static int _user_cleanup(oci8_session *session)
{
if (session->persistent)
if (column->is_descr) {
_php3_hash_index_del(column->statement->conn->descriptors,(int) column->data);
} else {
- if ((! column->define) && column->data) {
+ if (column->data) {
efree(column->data);
}
}
oci8_error(OCIError *err_p, char *what, sword status)
{
text errbuf[512];
- ub4 errcode = 0;
+ sb4 errcode = 0;
switch (status) {
case OCI_SUCCESS:
/************************* INTERNAL FUNCTIONS *************************/
/* {{{ oci8_debugcol() */
-#if 0
static void oci8_debugcol(oci8_out_column *column,const char *format,...)
{
OCI8_TLS_VARS;
}
}
}
-#endif
/* }}} */
/* {{{ oci8_debug() */
int i;
OCI8_TLS_VARS;
+ if (statement->columns == 0) { /* we release the columns at the end of a fetch */
+ return NULL;
+ }
+
if (pval) {
if (pval->type == IS_STRING) {
for (i = 0; i < statement->ncolumns; i++) {
/* {{{ oci8_make_pval() */
static int
-oci8_make_pval(pval *value,oci8_statement *statement,oci8_out_column *column, char *func,HashTable *list, int mode)
+oci8_make_pval(pval *value,oci8_statement *statement,oci8_out_column *column, char *func, int mode)
{
size_t size;
oci8_descriptor *descr;
ub4 loblen;
char *buffer;
- /*
- oci8_debug("oci8_make_pval: %16s,rlen = %4d,storage_size4 = %4d,size2 = %4d,indicator %4d, retcode = %4d",
- column->name,column->rlen,column->storage_size4,column->size2,column->indicator,column->retcode);
- */
+ if (column->indicator || column->retcode)
+ if ((column->indicator != -1) && (column->retcode != 1405)) /* "normal" NULL column -> no interest for us! */
+ oci8_debug("oci8_make_pval: %16s,rlen = %4d,storage_size4 = %4d,size2 = %4d,indicator %4d, retcode = %4d",
+ column->name,column->rlen,column->storage_size4,column->size2,column->indicator,column->retcode);
memset(value,0,sizeof(pval));
value->type = IS_STRING;
value->value.str.len = loblen;
value->value.str.val = buffer;
-#if PHP_API_VERSION >= 19990421
- value->refcount = 1;
- value->is_ref = 0;
-#endif
} else {
var_reset(value);
}
} else {
switch (column->retcode) {
case 1406: /* ORA-01406 XXX truncated value */
+
/* this seems to be a BUG in oracle with 1-digit numbers */
- /*
- oci8_debugcol(column,"truncated");
- */
- size = column->indicator - 1; /* when value is truncated indicator contains the lenght */
+
+ if (column->rlen <= 0) {
+ if (column->indicator > 0) { /* XXX this is our "oci-bug" */
+ /*
+ size = column->indicator;
+ */
+
+ size = 1;
+ } else {
+ size = 1;
+ }
+ } else {
+ if (column->piecewise) {
+ size = (column->cursize - column->piecesize) + column->rlen;
+ } else {
+ size = column->rlen;
+ }
+ }
break;
case 0: /* intact value */
/*
oci8_debugcol(column,"OK");
*/
- size = column->rlen;
+ if (column->piecewise) {
+ size = (column->cursize - column->piecesize) + column->rlen;
+ } else {
+ size = column->rlen;
+ }
break;
default: /* XXX we SHOULD maybe have a different behaviour for unknown results! */
value->type = IS_STRING;
value->value.str.len = size;
value->value.str.val = estrndup(column->data,size);
-#if PHP_API_VERSION >= 19990421
- value->refcount = 1;
- value->is_ref = 0;
-#endif
-
}
return 0;
"OCIParse",
OCIStmtPrepare(statement->pStmt,
connection->pError,
- query,
+ (text*)query,
len,
OCI_NTV_SYNTAX,
OCI_DEFAULT));
#if 0 /* testing */
{
- ub4 prefetch = 5*1024;
+ ub4 prefetch = 512*1024;
statement->error =
oci8_error(statement->pError,
- "OCIAttrSet OCI_ATTR_PREFETCH_ROWS",
+ "OCIAttrSet OCI_ATTR_PREFETCH_MEMORY",
OCIAttrSet(statement->pStmt,
OCI_HTYPE_STMT,
- &prefetch,
- 0,
+ 512*1024, //&prefetch,
+ 0,
OCI_ATTR_PREFETCH_MEMORY,
- &statement->pError));
+ statement->pError));
}
#endif
return 0; /* XXX we loose memory!!! */
}
+
statement->error =
oci8_error(statement->pError,
"OCIAttrGet OCI_DTYPE_PARAM/OCI_ATTR_DATA_SIZE",
return 0; /* XXX we loose memory!!! */
}
- outcol->name = estrndup(colname,outcol->name_len);
+ outcol->name = estrndup((char*) colname,outcol->name_len);
/* Remember the size Oracle told us, we have to increase the
* storage size for some types.
break;
case SQLT_LBI:
- case SQLT_LNG:
case SQLT_BIN:
+ define_type = SQLT_BIN;
+ outcol->storage_size4 = OCI8_MAX_DATA_SIZE;
+ outcol->piecewise = 1;
+ outcol->piecesize = OCI8_PIECE_SIZE;
+ break;
+
+ case SQLT_LNG:
define_type = SQLT_STR;
- /* XXX this should be user-settable!! */
- outcol->storage_size4 = OCI8_MAX_DATA_SIZE; /* 2MB */
+ outcol->storage_size4 = OCI8_MAX_DATA_SIZE;
+ outcol->piecewise = 1;
+ outcol->piecesize = OCI8_PIECE_SIZE;
break;
default:
oci8_error(statement->pError,
"OCIDefineByPos",
OCIDefineByPos(statement->pStmt, /* IN/OUT handle to the requested SQL query */
- (OCIDefine **)&outcol->pDefine, /* IN/OUT pointer to a pointer to a define handle */
- statement->pError, /* IN/OUT An error handle */
+ (OCIDefine **)&outcol->pDefine,/* IN/OUT pointer to a pointer to a define handle */
+ statement->pError, /* IN/OUT An error handle */
counter, /* IN position in the select list */
- (dvoid *)0, /* IN/OUT pointer to a buffer */
- outcol->storage_size4, /* IN The size of each valuep buffer in bytes */
- define_type, /* IN The data type */
+ (dvoid *)0, /* IN/OUT pointer to a buffer */
+ outcol->storage_size4, /* IN The size of each valuep buffer in bytes */
+ define_type, /* IN The data type */
(dvoid *)&outcol->indicator, /* IN pointer to an indicator variable or arr */
- (ub2 *)&outcol->size2, /* IN/OUT Pointer to array of length of data fetched */
+ (ub2 *)&outcol->size2, /* IN/OUT Pointer to array of length of data fetched */
(ub2 *)&outcol->retcode, /* OUT Pointer to array of column-level return codes */
OCI_DYNAMIC_FETCH)); /* IN mode (OCI_DEFAULT, OCI_DYNAMIC_FETCH) */
if (statement->error) {
"OCIDefineDynamic",
OCIDefineDynamic(outcol->pDefine,
statement->pError,
- outcol,
- oci8_define_callback));
+ (dvoid *) outcol,
+ (OCICallbackDefine)oci8_define_callback));
+
if (statement->error) {
return 0; /* XXX we loose memory!!! */
}
{
int i;
oci8_out_column *column;
- pval *pval;
OCI8_TLS_VARS;
+ /* first we need to reset our piecewise fetch-infos */
+ for (i = 0; i < statement->ncolumns; i++) {
+ column = oci8_get_col(statement, i + 1, 0, "OCIFetch");
+ if (column == NULL) { /* should not happen... */
+ continue;
+ }
+
+ if (column->piecewise) {
+ if (column->curoffs) {
+ column->cursize = column->piecesize;
+ column->curoffs = 0;
+ }
+ }
+ }
+
statement->error =
OCIStmtFetch(statement->pStmt,
statement->pError, nrows,
if (column == NULL) { /* should not happen... */
continue;
}
-
- if ((column->define) && (! column->is_descr)) {
- pval = column->define->pval;
- if (! column->define->data) { /* oracle has NOT called our define_callback (column value is NULL) */
- pval->value.str.val = emalloc(column->storage_size4);
- column->define->data = pval->value.str.val;
- }
+ if (! column->define) {
+ continue;
+ }
+
+#if PHP_API_VERSION < 19990421
+ php3tls_pval_destructor(column->define->pval);
+#else
+ pval_destructor(column->define->pval);
+#endif
- if (column->indicator == -1) { /* NULL */
- pval->value.str.len = 0;
- } else {
- if (column->retcode == 1406) { /*XXX ORA-01406 truncated value */
- /* this seems to be a BUG in oracle with 1-digit numbers */
- /*
- oci8_debugcol(column,"truncated");
- */
- pval->value.str.len = column->indicator - 1; /* when value is truncated indicator contains the lenght */
- } else {
- pval->value.str.len = column->rlen;
- }
- }
- pval->value.str.val[ pval->value.str.len ] = 0;
- }
+ oci8_make_pval(column->define->pval,statement,column,"OCIFetch",0);
}
return 1;
ub2 **rcodep)
{
oci8_out_column *outcol;
- oci8_define *define;
- pval *pval, *tmp;
oci8_descriptor *pdescr, descr;
outcol = (oci8_out_column *)octxp;
- define = outcol->define;
if (outcol->is_cursor) { /* REFCURSOR */
outcol->rlen = -1;
*bufpp = outcol->pstmt->pStmt;
} else if (outcol->is_descr) { /* LOB or FILE */
- if (define && (! outcol->pdescr)) { /* column has been user-defined */
- if (_php3_hash_find(define->pval->value.ht, "descriptor", sizeof("descriptor"), (void **)&tmp) == FAILURE) {
- php3_error(E_WARNING, "unable to find my descriptor property");
- return OCI_ERROR;
- } else if (_php3_hash_index_find(outcol->statement->conn->descriptors, tmp->value.lval, (void **)&pdescr) == FAILURE) {
- php3_error(E_WARNING, "unable to find my descriptor");
- return OCI_ERROR;
- }
- outcol->pdescr = pdescr;
- } else if (! outcol->pdescr) { /* we got no define value and the descriptor hasn't been allocated yet */
+ if (! outcol->pdescr) { /* we got no define value and the descriptor hasn't been allocated yet */
if (outcol->type == SQLT_BFILE) {
descr.type = OCI_DTYPE_FILE;
} else if (outcol->type == SQLT_RDD ) {
outcol->rlen = -1;
*bufpp = outcol->pdescr->ocidescr;
} else { /* "normal variable" */
- if (define) {
- pval = define->pval;
- convert_to_string(pval);
-
- if (pval->value.str.val) {
- if ((pval->value.str.val==undefined_variable_string) || (pval->value.str.val==empty_string)) {
- /* value was empty -> allocate it... */
- pval->value.str.val = 0;
- } else if (pval->value.str.val != define->data) {
- /* value has changed - and is maybe too small -> reallocate it! */
- efree(pval->value.str.val);
- pval->value.str.val = 0;
- }
- }
-
- if (pval->value.str.val == 0) {
- pval->value.str.val = emalloc(outcol->storage_size4);
- define->data = pval->value.str.val;
+ if (! outcol->data) {
+ if (outcol->piecewise) {
+ outcol->data = (text *) emalloc(outcol->piecesize);
+ outcol->cursize = outcol->piecesize;
+ outcol->curoffs = 0;
+ } else {
+ outcol->data = (text *) emalloc(outcol->storage_size4);
+ outcol->cursize = outcol->storage_size4;
}
- outcol->data = pval->value.str.val;
- } else if (! outcol->data) {
- outcol->data = (text *) emalloc(outcol->storage_size4);
}
-
+
if (! outcol->data) {
php3_error(E_WARNING, "OCIFetch: cannot allocate %d bytes!",outcol->storage_size4);
return OCI_ERROR;
}
- outcol->rlen = outcol->storage_size4;
- *bufpp = outcol->data;
+ if (outcol->piecewise) {
+ if ((outcol->curoffs + outcol->piecesize) > outcol->cursize) {
+ outcol->cursize += outcol->piecesize;
+ outcol->data = erealloc(outcol->data,outcol->cursize);
+ }
+ outcol->rlen = outcol->piecesize;
+ *bufpp = ((char*)outcol->data) + outcol->curoffs;
+ outcol->curoffs += outcol->piecesize;
+ } else {
+ outcol->rlen = outcol->storage_size4;
+ *bufpp = outcol->data;
+ }
}
outcol->indicator = 0;
*rcodep = &outcol->retcode;
*piecep = OCI_ONE_PIECE;
-/*
+ /*
oci8_debug("oci8_define_callback: %s,*bufpp = %x,**alenp = %d,**indpp = %d, **rcodep= %d, *piecep = %d",
outcol->name,*bufpp,**alenp,**(ub2**)indpp,**rcodep,*piecep);
-*/
+ */
return OCI_CONTINUE;
}
OCI8_GLOBAL(php3_oci8_module).error =
OCIServerAttach(server->pServer,
OCI8_GLOBAL(php3_oci8_module).pError,
- dbname,
+ (text*)dbname,
strlen(dbname),
(ub4) OCI_DEFAULT);
*/
static void oci8_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent,int exclusive)
{
- text *username, *password, *dbname;
+ char *username, *password, *dbname;
pval *userParam, *passParam, *dbParam;
oci8_server *server = 0;
oci8_session *session = 0;
username = userParam->value.str.val;
password = passParam->value.str.val;
- dbname = (text *)"";
+ dbname = "";
} else {
WRONG_PARAM_COUNT;
}
RETURN_FALSE;
}
- define->name = estrndup(name->value.str.val,name->value.str.len);
+ define->name = (text*) estrndup(name->value.str.val,name->value.str.len);
define->name_len = name->value.str.len;
define->type = ocitype;
define->pval = var;
if (var->value.str.len == 0) {
php3_error(E_WARNING, "OCIBindByName bindlength is 0");
}
- ocimaxlen = var->value.str.len + 1;
- /* SQLT_STR needs a trailing 0 - maybe we need to resize the var buffers????? */
+
+ if (ocitype == SQLT_BIN) {
+ ocimaxlen = var->value.str.len;
+ } else {
+ ocimaxlen = var->value.str.len + 1;
+ /* SQLT_STR needs a trailing 0 - maybe we need to resize the var buffers????? */
+ }
}
}
break;
OCIBindByName(statement->pStmt, /* statement handle */
(OCIBind **)&bind->pBind, /* bind hdl (will alloc) */
statement->pError, /* error handle */
- name->value.str.val, /* placeholder name */
+ (text*) name->value.str.val, /* placeholder name */
name->value.str.len, /* placeholder length */
(dvoid *)0, /* in/out data */
ocimaxlen, /* max size of input/output data */
OCI8_TLS_VARS;
+#if PHP_API_VERSION < 19990421
if (getThis(&id) == SUCCESS) {
+#else
+ if (id = getThis()) {
+#endif
if (_php3_hash_find(id->value.ht, "connection", sizeof("connection"), (void **)&conn) == FAILURE) {
php3_error(E_WARNING, "unable to find my statement property");
RETURN_FALSE;
OCI8_TLS_VARS;
+#if PHP_API_VERSION < 19990421
if (getThis(&id) == SUCCESS) {
+#else
+ if (id = getThis()) {
+#endif
if (_php3_hash_find(id->value.ht, "connection", sizeof("connection"), (void **)&conn) == FAILURE) {
php3_error(E_WARNING, "unable to find my statement property");
RETURN_FALSE;
OCI8_TLS_VARS;
+#if PHP_API_VERSION < 19990421
if (getThis(&id) == SUCCESS) {
+#else
+ if (id = getThis()) {
+#endif
if (_php3_hash_find(id->value.ht, "connection", sizeof("connection"), (void **)&conn) == FAILURE) {
php3_error(E_WARNING, "unable to find my statement property");
RETURN_FALSE;
#endif
if ((mode & OCI_NUM) || (! (mode & OCI_ASSOC))) { /* OCI_NUM is default */
- oci8_make_pval(element,statement,column, "OCIFetchInto",list,mode);
+ oci8_make_pval(element,statement,column, "OCIFetchInto",mode);
#if PHP_API_VERSION >= 19990421
_php3_hash_index_update(array->value.ht, i, (void *)&element, sizeof(pval*), NULL);
#else
}
if (mode & OCI_ASSOC) {
- oci8_make_pval(element,statement,column, "OCIFetchInto",list,mode);
+ oci8_make_pval(element,statement,column, "OCIFetchInto",mode);
#if PHP_API_VERSION >= 19990421
_php3_hash_update(array->value.ht, column->name, column->name_len+1, (void *)&element, sizeof(pval*), NULL);
PHP_FUNCTION(oci8_fetchstatement)
{
- pval *stmt, *array, element, *fmode;
+ pval *stmt, *array, *element, *fmode, *tmp;
oci8_statement *statement;
oci8_out_column **columns;
+#if PHP_API_VERSION < 19990421
pval **outarrs;
- pval tmp;
+#else
+ pval ***outarrs;
+#endif
ub4 nrows = 1;
int i;
int mode = OCI_NUM;
}
columns = emalloc(statement->ncolumns * sizeof(oci8_out_column *));
+#if PHP_API_VERSION < 19990421
outarrs = emalloc(statement->ncolumns * sizeof(pval));
+#else
+ outarrs = emalloc(statement->ncolumns * sizeof(pval*));
+#endif
+
+#if PHP_API_VERSION < 19990421
+ tmp = emalloc(sizeof(pval));
+#endif
for (i = 0; i < statement->ncolumns; i++) {
columns[ i ] = oci8_get_col(statement, i + 1, 0, "OCIFetchStatement");
- array_init(&tmp);
+#if PHP_API_VERSION >= 19990421
+ tmp = emalloc(sizeof(pval));
+ tmp->is_ref = 0;
+ tmp->refcount = 1;
+#endif
+
+ array_init(tmp);
memcpy(namebuf,columns[ i ]->name, columns[ i ]->name_len);
namebuf[ columns[ i ]->name_len ] = 0;
- _php3_hash_update(array->value.ht, namebuf, columns[ i ]->name_len+1, (void *) &tmp, sizeof(pval), (void **) &(outarrs[ i ]));
+#if PHP_API_VERSION < 19990421
+ _php3_hash_update(array->value.ht, namebuf, columns[ i ]->name_len+1, (void *) tmp, sizeof(pval), (void **) &(outarrs[ i ]));
+#else
+ _php3_hash_update(array->value.ht, namebuf, columns[ i ]->name_len+1, (void *) &tmp, sizeof(pval*), (void **) &(outarrs[ i ]));
+#endif
}
+#if PHP_API_VERSION < 19990421
+ efree(tmp);
+#endif
+
+#if PHP_API_VERSION < 19990421
+ element = emalloc(sizeof(pval));
+#endif
+
while (oci8_fetch(statement, nrows, "OCIFetchStatement")) {
for (i = 0; i < statement->ncolumns; i++) {
- oci8_make_pval(&element,statement,columns[ i ], "OCIFetchStatement",list,OCI_RETURN_LOBS);
- _php3_hash_index_update(outarrs[ i ]->value.ht, rows, (void *)&element, sizeof(pval), NULL);
+#if PHP_API_VERSION >= 19990421
+ element = emalloc(sizeof(pval));
+#endif
+
+ oci8_make_pval(element,statement,columns[ i ], "OCIFetchStatement",OCI_RETURN_LOBS);
+
+#if PHP_API_VERSION < 19990421
+ _php3_hash_index_update(outarrs[ i ]->value.ht, rows, (void *)element, sizeof(pval), NULL);
+#else
+ element->refcount = 1;
+ element->is_ref = 0;
+ _php3_hash_index_update((*(outarrs[ i ]))->value.ht, rows, (void *)&element, sizeof(pval*), NULL);
+#endif
}
rows++;
}
+#if PHP_API_VERSION < 19990421
+ efree(element);
+#endif
+
efree(columns);
efree(outarrs);
RETURN_FALSE;
}
+ connection->open = 0;
+
+ _php3_hash_apply(list,(int (*)(void *))_stmt_cleanup);
+
if (php3_list_delete(index) == SUCCESS) {
RETURN_TRUE;
} else {
oci8_statement *statement;
oci8_connection *connection;
text errbuf[512];
- ub4 errcode = 0;
+ sb4 errcode = 0;
int type;
sword error = 0;
dvoid *errh = NULL;
if (errcode) {
array_init(return_value);
add_assoc_long(return_value, "code", errcode);
- add_assoc_string(return_value, "message", errbuf, 1);
+ add_assoc_string(return_value, "message", (char*) errbuf, 1);
} else {
RETURN_FALSE;
}
RETURN_FALSE;
}
- oci8_make_pval(return_value,statement,outcol, "OCIResult",list,0);
+ oci8_make_pval(return_value,statement,outcol, "OCIResult",0);
}
/* }}} */
connection->error =
OCIServerVersion(connection->pServiceContext,
connection->pError,
- version,
+ (text*)version,
sizeof(version),
OCI_HTYPE_SVCCTX);
if (connection->error != OCI_SUCCESS) {
/* {{{ proto int OCIStatementType(int stmt)
Return the query type of an OCI statement.
*/
-
+
/* XXX it would be better with a general interface to OCIAttrGet() */
PHP_FUNCTION(oci8_statementtype)