diff options
Diffstat (limited to 'ext/pdo_oci/oci_statement.c')
-rw-r--r-- | ext/pdo_oci/oci_statement.c | 767 |
1 files changed, 767 insertions, 0 deletions
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 + */ |