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