php-src/ext/pdo_oci/oci_statement.c
Wez Furlong 459b4f85d3 Add support for scrollable cursors.
Enable PDO_ATTR_PREFETCH and default it to 100Kb of prefetch buffer.
2005-01-12 05:47:03 +00:00

446 lines
12 KiB
C
Executable File

/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2005 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"
#include "Zend/zend_extensions.h"
#define STMT_CALL(name, params) \
S->last_err = name params; \
S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, __FILE__, __LINE__ TSRMLS_CC); \
if (S->last_err) { \
return 0; \
}
#define STMT_CALL_MSG(name, msg, params) \
S->last_err = name params; \
S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, __FILE__, __LINE__ TSRMLS_CC); \
if (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;
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;
}
if (BC) {
zend_hash_destroy(BC);
efree(stmt->bound_columns);
}
if (BP) {
zend_hash_destroy(BP);
efree(stmt->bound_params);
}
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 : S->exec_type));
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*)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 (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*)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 (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
#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';
}
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 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*)&param, 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;
}
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;
}
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));
}
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
*/