1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Author: Wez Furlong <wez@php.net> | 16 +----------------------------------------------------------------------+ 17*/ 18 19/* $Id$ */ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#endif 24 25#include "php.h" 26#include "php_ini.h" 27#include "ext/standard/info.h" 28#include "pdo/php_pdo.h" 29#include "pdo/php_pdo_driver.h" 30#include "php_pdo_oci.h" 31#include "php_pdo_oci_int.h" 32#include "Zend/zend_extensions.h" 33 34#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */ 35 36#define STMT_CALL(name, params) \ 37 do { \ 38 S->last_err = name params; \ 39 S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__ TSRMLS_CC); \ 40 if (S->last_err) { \ 41 return 0; \ 42 } \ 43 } while(0) 44 45#define STMT_CALL_MSG(name, msg, params) \ 46 do { \ 47 S->last_err = name params; \ 48 S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__ TSRMLS_CC); \ 49 if (S->last_err) { \ 50 return 0; \ 51 } \ 52 } while(0) 53 54static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob TSRMLS_DC); 55 56static int oci_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ 57{ 58 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; 59 HashTable *BC = stmt->bound_columns; 60 HashTable *BP = stmt->bound_params; 61 62 int i; 63 64 if (S->stmt) { 65 /* cancel server side resources for the statement if we didn't 66 * fetch it all */ 67 OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); 68 69 /* free the handle */ 70 OCIHandleFree(S->stmt, OCI_HTYPE_STMT); 71 S->stmt = NULL; 72 } 73 if (S->err) { 74 OCIHandleFree(S->err, OCI_HTYPE_ERROR); 75 S->err = NULL; 76 } 77 78 /* need to ensure these go away now */ 79 if (BC) { 80 zend_hash_destroy(BC); 81 FREE_HASHTABLE(stmt->bound_columns); 82 stmt->bound_columns = NULL; 83 } 84 85 if (BP) { 86 zend_hash_destroy(BP); 87 FREE_HASHTABLE(stmt->bound_params); 88 stmt->bound_params = NULL; 89 } 90 91 if (S->einfo.errmsg) { 92 pefree(S->einfo.errmsg, stmt->dbh->is_persistent); 93 S->einfo.errmsg = NULL; 94 } 95 96 if (S->cols) { 97 for (i = 0; i < stmt->column_count; i++) { 98 if (S->cols[i].data) { 99 switch (S->cols[i].dtype) { 100 case SQLT_BLOB: 101 case SQLT_CLOB: 102 OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); 103 break; 104 default: 105 efree(S->cols[i].data); 106 } 107 } 108 } 109 efree(S->cols); 110 S->cols = NULL; 111 } 112 efree(S); 113 114 stmt->driver_data = NULL; 115 116 return 1; 117} /* }}} */ 118 119static int oci_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ 120{ 121 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; 122 ub4 rowcount; 123 b4 mode; 124 125 if (!S->stmt_type) { 126 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_STMT_TYPE", 127 (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err)); 128 } 129 130 if (stmt->executed) { 131 /* ensure that we cancel the cursor from a previous fetch */ 132 OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT); 133 } 134 135#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */ 136 if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) { 137 mode = OCI_STMT_SCROLLABLE_READONLY; 138 } else 139#endif 140 if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) { 141 mode = OCI_COMMIT_ON_SUCCESS; 142 } else { 143 mode = OCI_DEFAULT; 144 } 145 146 STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err, 147 (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, 148 mode)); 149 150 if (!stmt->executed) { 151 ub4 colcount; 152 /* do first-time-only definition of bind/mapping stuff */ 153 154 /* how many columns do we have ? */ 155 STMT_CALL_MSG(OCIAttrGet, "ATTR_PARAM_COUNT", 156 (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err)); 157 158 stmt->column_count = (int)colcount; 159 160 if (S->cols) { 161 int i; 162 for (i = 0; i < stmt->column_count; i++) { 163 if (S->cols[i].data) { 164 switch (S->cols[i].dtype) { 165 case SQLT_BLOB: 166 case SQLT_CLOB: 167 /* do nothing */ 168 break; 169 default: 170 efree(S->cols[i].data); 171 } 172 } 173 } 174 efree(S->cols); 175 } 176 177 S->cols = ecalloc(colcount, sizeof(pdo_oci_column)); 178 } 179 180 STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT", 181 (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err)); 182 stmt->row_count = (long)rowcount; 183 184 return 1; 185} /* }}} */ 186 187static sb4 oci_bind_input_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */ 188{ 189 struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx; 190 pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data; 191 TSRMLS_FETCH(); 192 193 if (!param || !param->parameter) { 194 php_error_docref(NULL TSRMLS_CC, E_WARNING, "param is NULL in oci_bind_input_cb; this should not happen"); 195 return OCI_ERROR; 196 } 197 198 *indpp = &P->indicator; 199 200 if (P->thing) { 201 *bufpp = P->thing; 202 *alenp = sizeof(void*); 203 } else if (ZVAL_IS_NULL(param->parameter)) { 204 /* insert a NULL value into the column */ 205 P->indicator = -1; /* NULL */ 206 *bufpp = 0; 207 *alenp = -1; 208 } else if (!P->thing) { 209 /* regular string bind */ 210 convert_to_string(param->parameter); 211 *bufpp = Z_STRVAL_P(param->parameter); 212 *alenp = Z_STRLEN_P(param->parameter); 213 } 214 215 *piecep = OCI_ONE_PIECE; 216 return OCI_CONTINUE; 217} /* }}} */ 218 219static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) /* {{{ */ 220{ 221 struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx; 222 pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data; 223 TSRMLS_FETCH(); 224 225 if (!param || !param->parameter) { 226 php_error_docref(NULL TSRMLS_CC, E_WARNING, "param is NULL in oci_bind_output_cb; this should not happen"); 227 return OCI_ERROR; 228 } 229 230 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { 231 P->actual_len = sizeof(OCILobLocator*); 232 *bufpp = P->thing; 233 *alenpp = &P->actual_len; 234 *piecep = OCI_ONE_PIECE; 235 *rcodepp = &P->retcode; 236 *indpp = &P->indicator; 237 return OCI_CONTINUE; 238 } 239 240 if (Z_TYPE_P(param->parameter) == IS_OBJECT || Z_TYPE_P(param->parameter) == IS_RESOURCE) { 241 return OCI_CONTINUE; 242 } 243 244 convert_to_string(param->parameter); 245 zval_dtor(param->parameter); 246 247 Z_STRLEN_P(param->parameter) = param->max_value_len; 248 Z_STRVAL_P(param->parameter) = ecalloc(1, Z_STRLEN_P(param->parameter)+1); 249 P->used_for_output = 1; 250 251 P->actual_len = Z_STRLEN_P(param->parameter); 252 *alenpp = &P->actual_len; 253 *bufpp = Z_STRVAL_P(param->parameter); 254 *piecep = OCI_ONE_PIECE; 255 *rcodepp = &P->retcode; 256 *indpp = &P->indicator; 257 258 return OCI_CONTINUE; 259} /* }}} */ 260 261static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type TSRMLS_DC) /* {{{ */ 262{ 263 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; 264 265 /* we're only interested in parameters for prepared SQL right now */ 266 if (param->is_param) { 267 pdo_oci_bound_param *P; 268 sb4 value_sz = -1; 269 270 P = (pdo_oci_bound_param*)param->driver_data; 271 272 switch (event_type) { 273 case PDO_PARAM_EVT_FREE: 274 P = param->driver_data; 275 if (P) { 276 efree(P); 277 } 278 break; 279 280 case PDO_PARAM_EVT_ALLOC: 281 P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param)); 282 param->driver_data = P; 283 284 /* figure out what we're doing */ 285 switch (PDO_PARAM_TYPE(param->param_type)) { 286 case PDO_PARAM_STMT: 287 return 0; 288 289 case PDO_PARAM_LOB: 290 /* P->thing is now an OCILobLocator * */ 291 P->oci_type = SQLT_BLOB; 292 value_sz = sizeof(OCILobLocator*); 293 break; 294 295 case PDO_PARAM_STR: 296 default: 297 P->oci_type = SQLT_CHR; 298 value_sz = param->max_value_len; 299 if (param->max_value_len == 0) { 300 value_sz = 1332; /* maximum size before value is interpreted as a LONG value */ 301 } 302 303 } 304 305 if (param->name) { 306 STMT_CALL(OCIBindByName, (S->stmt, 307 &P->bind, S->err, (text*)param->name, 308 param->namelen, 0, value_sz, P->oci_type, 309 &P->indicator, 0, &P->retcode, 0, 0, 310 OCI_DATA_AT_EXEC)); 311 } else { 312 STMT_CALL(OCIBindByPos, (S->stmt, 313 &P->bind, S->err, param->paramno+1, 314 0, value_sz, P->oci_type, 315 &P->indicator, 0, &P->retcode, 0, 0, 316 OCI_DATA_AT_EXEC)); 317 } 318 319 STMT_CALL(OCIBindDynamic, (P->bind, 320 S->err, 321 param, oci_bind_input_cb, 322 param, oci_bind_output_cb)); 323 324 return 1; 325 326 case PDO_PARAM_EVT_EXEC_PRE: 327 P->indicator = 0; 328 P->used_for_output = 0; 329 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { 330 ub4 empty = 0; 331 STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL)); 332 STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err)); 333 S->have_blobs = 1; 334 } 335 return 1; 336 337 case PDO_PARAM_EVT_EXEC_POST: 338 /* fixup stuff set in motion in oci_bind_output_cb */ 339 if (P->used_for_output) { 340 if (P->indicator == -1) { 341 /* set up a NULL value */ 342 if (Z_TYPE_P(param->parameter) == IS_STRING 343#if ZEND_EXTENSION_API_NO < 220040718 344 && Z_STRVAL_P(param->parameter) != empty_string 345#endif 346 ) { 347 /* OCI likes to stick non-terminated strings in things */ 348 *Z_STRVAL_P(param->parameter) = '\0'; 349 } 350 zval_dtor(param->parameter); 351 ZVAL_NULL(param->parameter); 352 } else if (Z_TYPE_P(param->parameter) == IS_STRING 353#if ZEND_EXTENSION_API_NO < 220040718 354 && Z_STRVAL_P(param->parameter) != empty_string 355#endif 356 ) { 357 Z_STRLEN_P(param->parameter) = P->actual_len; 358 Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), P->actual_len+1); 359 Z_STRVAL_P(param->parameter)[P->actual_len] = '\0'; 360 } 361 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) { 362 php_stream *stm; 363 364 if (Z_TYPE_P(param->parameter) == IS_NULL) { 365 /* if the param is NULL, then we assume that they 366 * wanted to bind a lob locator into it from the query 367 * */ 368 369 stm = oci_create_lob_stream(stmt, (OCILobLocator*)P->thing TSRMLS_CC); 370 if (stm) { 371 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE); 372 php_stream_to_zval(stm, param->parameter); 373 P->thing = NULL; 374 } 375 } else { 376 /* we're a LOB being used for insert; transfer the data now */ 377 size_t n; 378 ub4 amt, offset = 1; 379 char *consume; 380 381 php_stream_from_zval_no_verify(stm, ¶m->parameter); 382 if (stm) { 383 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE); 384 do { 385 char buf[8192]; 386 n = php_stream_read(stm, buf, sizeof(buf)); 387 if ((int)n <= 0) { 388 break; 389 } 390 consume = buf; 391 do { 392 amt = n; 393 OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing, 394 &amt, offset, consume, n, 395 OCI_ONE_PIECE, 396 NULL, NULL, 0, SQLCS_IMPLICIT); 397 offset += amt; 398 n -= amt; 399 consume += amt; 400 } while (n); 401 } while (1); 402 OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing); 403 OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0); 404 } else if (Z_TYPE_P(param->parameter) == IS_STRING) { 405 /* stick the string into the LOB */ 406 consume = Z_STRVAL_P(param->parameter); 407 n = Z_STRLEN_P(param->parameter); 408 if (n) { 409 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE); 410 while (n) { 411 amt = n; 412 OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing, 413 &amt, offset, consume, n, 414 OCI_ONE_PIECE, 415 NULL, NULL, 0, SQLCS_IMPLICIT); 416 consume += amt; 417 n -= amt; 418 } 419 OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing); 420 } 421 } 422 OCIDescriptorFree(P->thing, OCI_DTYPE_LOB); 423 P->thing = NULL; 424 } 425 } 426 427 return 1; 428 } 429 } 430 431 return 1; 432} /* }}} */ 433 434static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, long offset TSRMLS_DC) /* {{{ */ 435{ 436#if HAVE_OCISTMTFETCH2 437 ub4 ociori; 438#endif 439 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; 440 441#if HAVE_OCISTMTFETCH2 442 switch (ori) { 443 case PDO_FETCH_ORI_NEXT: ociori = OCI_FETCH_NEXT; break; 444 case PDO_FETCH_ORI_PRIOR: ociori = OCI_FETCH_PRIOR; break; 445 case PDO_FETCH_ORI_FIRST: ociori = OCI_FETCH_FIRST; break; 446 case PDO_FETCH_ORI_LAST: ociori = OCI_FETCH_LAST; break; 447 case PDO_FETCH_ORI_ABS: ociori = OCI_FETCH_ABSOLUTE; break; 448 case PDO_FETCH_ORI_REL: ociori = OCI_FETCH_RELATIVE; break; 449 } 450 S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, offset, OCI_DEFAULT); 451#else 452 S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT); 453#endif 454 455 if (S->last_err == OCI_NO_DATA) { 456 /* no (more) data */ 457 return 0; 458 } 459 460 if (S->last_err == OCI_NEED_DATA) { 461 oci_stmt_error("OCI_NEED_DATA"); 462 return 0; 463 } 464 465 if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) { 466 return 1; 467 } 468 469 oci_stmt_error("OCIStmtFetch"); 470 471 return 0; 472} /* }}} */ 473 474static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, 475 ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) 476{ 477 pdo_oci_column *col = (pdo_oci_column*)octxp; 478 TSRMLS_FETCH(); 479 480 switch (col->dtype) { 481 case SQLT_BLOB: 482 case SQLT_CLOB: 483 *piecep = OCI_ONE_PIECE; 484 *bufpp = col->data; 485 *alenpp = &col->datalen; 486 *indpp = (dvoid *)&col->indicator; 487 break; 488 489 default: 490 php_error_docref(NULL TSRMLS_CC, E_WARNING, 491 "unhandled datatype in oci_define_callback; this should not happen"); 492 return OCI_ERROR; 493 } 494 495 return OCI_CONTINUE; 496} 497 498static int oci_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) /* {{{ */ 499{ 500 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; 501 OCIParam *param = NULL; 502 text *colname; 503 ub2 dtype, data_size, scale, precis; 504 ub4 namelen; 505 struct pdo_column_data *col = &stmt->columns[colno]; 506 zend_bool dyn = FALSE; 507 508 /* describe the column */ 509 STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1)); 510 511 /* what type ? */ 512 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE", 513 (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err)); 514 515 /* how big ? */ 516 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE", 517 (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err)); 518 519 /* scale ? */ 520 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE", 521 (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err)); 522 523 /* precision ? */ 524 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION", 525 (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err)); 526 527 /* name ? */ 528 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME", 529 (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err)); 530 531 col->precision = scale; 532 col->maxlen = data_size; 533 col->namelen = namelen; 534 col->name = estrndup((char *)colname, namelen); 535 536 S->cols[colno].dtype = dtype; 537 538 /* how much room do we need to store the field */ 539 switch (dtype) { 540 case SQLT_LBI: 541 case SQLT_LNG: 542 if (dtype == SQLT_LBI) { 543 dtype = SQLT_BIN; 544 } else { 545 dtype = SQLT_CHR; 546 } 547 S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */ 548 S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); 549 col->param_type = PDO_PARAM_STR; 550 break; 551 552 case SQLT_BLOB: 553 case SQLT_CLOB: 554 col->param_type = PDO_PARAM_LOB; 555 STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL)); 556 S->cols[colno].datalen = sizeof(OCILobLocator*); 557 dyn = TRUE; 558 break; 559 560 case SQLT_BIN: 561 default: 562 if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD 563#ifdef SQLT_TIMESTAMP 564 || dtype == SQLT_TIMESTAMP 565#endif 566#ifdef SQLT_TIMESTAMP_TZ 567 || dtype == SQLT_TIMESTAMP_TZ 568#endif 569 ) { 570 /* should be big enough for most date formats and numbers */ 571 S->cols[colno].datalen = 512; 572#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE) 573 } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) { 574 S->cols[colno].datalen = 1024; 575#endif 576 } else { 577 S->cols[colno].datalen = col->maxlen; 578 } 579 if (dtype == SQLT_BIN) { 580 S->cols[colno].datalen *= 3; 581 } 582 S->cols[colno].data = emalloc(S->cols[colno].datalen + 1); 583 dtype = SQLT_CHR; 584 585 /* returning data as a string */ 586 col->param_type = PDO_PARAM_STR; 587 } 588 589 STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1, 590 S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator, 591 &S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT)); 592 593 if (dyn) { 594 STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], 595 oci_define_callback)); 596 } 597 598 return 1; 599} /* }}} */ 600 601struct oci_lob_self { 602 pdo_stmt_t *stmt; 603 pdo_oci_stmt *S; 604 OCILobLocator *lob; 605 ub4 offset; 606}; 607 608static size_t oci_blob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) 609{ 610 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; 611 ub4 amt; 612 sword r; 613 614 amt = count; 615 r = OCILobWrite(self->S->H->svc, self->S->err, self->lob, 616 &amt, self->offset, (char*)buf, count, 617 OCI_ONE_PIECE, 618 NULL, NULL, 0, SQLCS_IMPLICIT); 619 620 if (r != OCI_SUCCESS) { 621 return (size_t)-1; 622 } 623 624 self->offset += amt; 625 return amt; 626} 627 628static size_t oci_blob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) 629{ 630 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; 631 ub4 amt; 632 sword r; 633 634 amt = count; 635 r = OCILobRead(self->S->H->svc, self->S->err, self->lob, 636 &amt, self->offset, buf, count, 637 NULL, NULL, 0, SQLCS_IMPLICIT); 638 639 if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { 640 return (size_t)-1; 641 } 642 643 self->offset += amt; 644 if (amt < count) { 645 stream->eof = 1; 646 } 647 return amt; 648} 649 650static int oci_blob_close(php_stream *stream, int close_handle TSRMLS_DC) 651{ 652 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; 653 pdo_stmt_t *stmt = self->stmt; 654 655 if (close_handle) { 656 OCILobClose(self->S->H->svc, self->S->err, self->lob); 657 efree(self); 658 } 659 660 php_pdo_stmt_delref(stmt TSRMLS_CC); 661 return 0; 662} 663 664static int oci_blob_flush(php_stream *stream TSRMLS_DC) 665{ 666 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; 667 OCILobFlushBuffer(self->S->H->svc, self->S->err, self->lob, 0); 668 return 0; 669} 670 671static int oci_blob_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) 672{ 673 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; 674 675 if (offset >= PDO_OCI_LOBMAXSIZE) { 676 return -1; 677 } else { 678 self->offset = offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */ 679 return 0; 680 } 681} 682 683static php_stream_ops oci_blob_stream_ops = { 684 oci_blob_write, 685 oci_blob_read, 686 oci_blob_close, 687 oci_blob_flush, 688 "pdo_oci blob stream", 689 oci_blob_seek, 690 NULL, 691 NULL, 692 NULL 693}; 694 695static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob TSRMLS_DC) 696{ 697 php_stream *stm; 698 struct oci_lob_self *self = ecalloc(1, sizeof(*self)); 699 self->lob = lob; 700 self->offset = 1; /* 1-based */ 701 self->stmt = stmt; 702 self->S = (pdo_oci_stmt*)stmt->driver_data; 703 704 stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b"); 705 706 if (stm) { 707 php_pdo_stmt_addref(stmt TSRMLS_CC); 708 return stm; 709 } 710 711 efree(self); 712 return NULL; 713} 714 715static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) /* {{{ */ 716{ 717 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; 718 pdo_oci_column *C = &S->cols[colno]; 719 720 /* check the indicator to ensure that the data is intact */ 721 if (C->indicator == -1) { 722 /* A NULL value */ 723 *ptr = NULL; 724 *len = 0; 725 return 1; 726 } else if (C->indicator == 0) { 727 /* it was stored perfectly */ 728 729 if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) { 730 if (C->data) { 731 *ptr = (char*)oci_create_lob_stream(stmt, (OCILobLocator*)C->data TSRMLS_CC); 732 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY); 733 } 734 *len = 0; 735 return *ptr ? 1 : 0; 736 } 737 738 *ptr = C->data; 739 *len = C->fetched_len; 740 return 1; 741 } else { 742 /* it was truncated */ 743 php_error_docref(NULL TSRMLS_CC, E_WARNING, "column %d data was too large for buffer and was truncated to fit it", colno); 744 745 *ptr = C->data; 746 *len = C->fetched_len; 747 return 1; 748 } 749} /* }}} */ 750 751struct pdo_stmt_methods oci_stmt_methods = { 752 oci_stmt_dtor, 753 oci_stmt_execute, 754 oci_stmt_fetch, 755 oci_stmt_describe, 756 oci_stmt_get_col, 757 oci_stmt_param_hook 758}; 759 760/* 761 * Local variables: 762 * tab-width: 4 763 * c-basic-offset: 4 764 * End: 765 * vim600: noet sw=4 ts=4 fdm=marker 766 * vim<600: noet sw=4 ts=4 767 */ 768