1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22 #include "php_ini.h"
23 #include "ext/standard/info.h"
24 #include "pdo/php_pdo.h"
25 #include "pdo/php_pdo_driver.h"
26 #include "php_pdo_oci.h"
27 #include "php_pdo_oci_int.h"
28 #include "Zend/zend_extensions.h"
29
30 #define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */
31
32 #define STMT_CALL(name, params) \
33 do { \
34 S->last_err = name params; \
35 S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \
36 if (S->last_err) { \
37 return 0; \
38 } \
39 } while(0)
40
41 #define STMT_CALL_MSG(name, msg, params) \
42 do { \
43 S->last_err = name params; \
44 S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \
45 if (S->last_err) { \
46 return 0; \
47 } \
48 } while(0)
49
50 static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob);
51
52 #define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob) \
53 do \
54 { \
55 boolean isTempLOB; \
56 OCILobIsTemporary(envhp, errhp, lob, &isTempLOB); \
57 if (isTempLOB) \
58 OCILobFreeTemporary(svchp, errhp, lob); \
59 } while(0)
60
oci_stmt_dtor(pdo_stmt_t * stmt)61 static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
62 {
63 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
64 HashTable *BC = stmt->bound_columns;
65 HashTable *BP = stmt->bound_params;
66
67 int i;
68
69 if (S->stmt) {
70 /* cancel server side resources for the statement if we didn't
71 * fetch it all */
72 OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
73
74 /* free the handle */
75 OCIHandleFree(S->stmt, OCI_HTYPE_STMT);
76 S->stmt = NULL;
77 }
78 if (S->err) {
79 OCIHandleFree(S->err, OCI_HTYPE_ERROR);
80 S->err = NULL;
81 }
82
83 /* need to ensure these go away now */
84 if (BC) {
85 zend_hash_destroy(BC);
86 FREE_HASHTABLE(stmt->bound_columns);
87 stmt->bound_columns = NULL;
88 }
89
90 if (BP) {
91 zend_hash_destroy(BP);
92 FREE_HASHTABLE(stmt->bound_params);
93 stmt->bound_params = NULL;
94 }
95
96 if (S->einfo.errmsg) {
97 pefree(S->einfo.errmsg, stmt->dbh->is_persistent);
98 S->einfo.errmsg = NULL;
99 }
100
101 if (S->cols) {
102 for (i = 0; i < stmt->column_count; i++) {
103 if (S->cols[i].data) {
104 switch (S->cols[i].dtype) {
105 case SQLT_BLOB:
106 case SQLT_CLOB:
107 OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err,
108 (OCILobLocator *) S->cols[i].data);
109 OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB);
110 break;
111 default:
112 efree(S->cols[i].data);
113 }
114 }
115 }
116 efree(S->cols);
117 S->cols = NULL;
118 }
119 efree(S);
120
121 stmt->driver_data = NULL;
122
123 return 1;
124 } /* }}} */
125
oci_stmt_execute(pdo_stmt_t * stmt)126 static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
127 {
128 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
129 ub4 rowcount;
130 b4 mode;
131
132 if (!S->stmt_type) {
133 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_STMT_TYPE",
134 (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err));
135 }
136
137 if (stmt->executed) {
138 /* ensure that we cancel the cursor from a previous fetch */
139 OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
140 }
141
142 #ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */
143 if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) {
144 mode = OCI_STMT_SCROLLABLE_READONLY;
145 } else
146 #endif
147 if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) {
148 mode = OCI_COMMIT_ON_SUCCESS;
149 } else {
150 mode = OCI_DEFAULT;
151 }
152
153 STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err,
154 (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL,
155 mode));
156
157 if (!stmt->executed) {
158 ub4 colcount;
159 /* do first-time-only definition of bind/mapping stuff */
160
161 /* how many columns do we have ? */
162 STMT_CALL_MSG(OCIAttrGet, "ATTR_PARAM_COUNT",
163 (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err));
164
165 stmt->column_count = (int)colcount;
166
167 if (S->cols) {
168 int i;
169 for (i = 0; i < stmt->column_count; i++) {
170 if (S->cols[i].data) {
171 switch (S->cols[i].dtype) {
172 case SQLT_BLOB:
173 case SQLT_CLOB:
174 /* do nothing */
175 break;
176 default:
177 efree(S->cols[i].data);
178 }
179 }
180 }
181 efree(S->cols);
182 }
183
184 S->cols = ecalloc(colcount, sizeof(pdo_oci_column));
185 }
186
187 STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT",
188 (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err));
189 stmt->row_count = (long)rowcount;
190
191 return 1;
192 } /* }}} */
193
oci_bind_input_cb(dvoid * ctx,OCIBind * bindp,ub4 iter,ub4 index,dvoid ** bufpp,ub4 * alenp,ub1 * piecep,dvoid ** indpp)194 static sb4 oci_bind_input_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */
195 {
196 struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
197 pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
198 zval *parameter;
199
200 ZEND_ASSERT(param);
201
202 *indpp = &P->indicator;
203
204 if (Z_ISREF(param->parameter))
205 parameter = Z_REFVAL(param->parameter);
206 else
207 parameter = ¶m->parameter;
208
209 if (P->thing) {
210 *bufpp = P->thing;
211 *alenp = sizeof(void*);
212 } else if (ZVAL_IS_NULL(parameter)) {
213 /* insert a NULL value into the column */
214 P->indicator = -1; /* NULL */
215 *bufpp = 0;
216 *alenp = -1;
217 } else if (!P->thing) {
218 /* regular string bind */
219 if (!try_convert_to_string(parameter)) {
220 return OCI_ERROR;
221 }
222 *bufpp = Z_STRVAL_P(parameter);
223 *alenp = (ub4) Z_STRLEN_P(parameter);
224 }
225
226 *piecep = OCI_ONE_PIECE;
227 return OCI_CONTINUE;
228 } /* }}} */
229
oci_bind_output_cb(dvoid * ctx,OCIBind * bindp,ub4 iter,ub4 index,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcodepp)230 static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) /* {{{ */
231 {
232 struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
233 pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
234 zval *parameter;
235
236 ZEND_ASSERT(param);
237
238 if (Z_ISREF(param->parameter)) {
239 parameter = Z_REFVAL(param->parameter);
240 } else {
241 parameter = ¶m->parameter;
242 }
243
244 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
245 P->actual_len = sizeof(OCILobLocator*);
246 *bufpp = P->thing;
247 *alenpp = &P->actual_len;
248 *piecep = OCI_ONE_PIECE;
249 *rcodepp = &P->retcode;
250 *indpp = &P->indicator;
251 return OCI_CONTINUE;
252 }
253
254 if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) {
255 return OCI_CONTINUE;
256 }
257
258 zval_ptr_dtor(parameter);
259
260 Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1);
261 P->used_for_output = 1;
262
263 P->actual_len = (ub4) Z_STRLEN_P(parameter);
264 *alenpp = &P->actual_len;
265 *bufpp = (Z_STR_P(parameter))->val;
266 *piecep = OCI_ONE_PIECE;
267 *rcodepp = &P->retcode;
268 *indpp = &P->indicator;
269
270 return OCI_CONTINUE;
271 } /* }}} */
272
oci_stmt_param_hook(pdo_stmt_t * stmt,struct pdo_bound_param_data * param,enum pdo_param_event event_type)273 static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) /* {{{ */
274 {
275 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
276
277 /* we're only interested in parameters for prepared SQL right now */
278 if (param->is_param) {
279 pdo_oci_bound_param *P;
280 sb4 value_sz = -1;
281 zval *parameter;
282
283 if (Z_ISREF(param->parameter))
284 parameter = Z_REFVAL(param->parameter);
285 else
286 parameter = ¶m->parameter;
287
288 P = (pdo_oci_bound_param*)param->driver_data;
289
290 switch (event_type) {
291 case PDO_PARAM_EVT_FETCH_PRE:
292 case PDO_PARAM_EVT_FETCH_POST:
293 case PDO_PARAM_EVT_NORMALIZE:
294 /* Do nothing */
295 break;
296
297 case PDO_PARAM_EVT_FREE:
298 P = param->driver_data;
299 if (P && P->thing) {
300 OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
301 OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
302 P->thing = NULL;
303 efree(P);
304 }
305 else if (P) {
306 efree(P);
307 }
308 break;
309
310 case PDO_PARAM_EVT_ALLOC:
311 P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param));
312 param->driver_data = P;
313
314 /* figure out what we're doing */
315 switch (PDO_PARAM_TYPE(param->param_type)) {
316 case PDO_PARAM_STMT:
317 return 0;
318
319 case PDO_PARAM_LOB:
320 /* P->thing is now an OCILobLocator * */
321 P->oci_type = SQLT_BLOB;
322 value_sz = (sb4) sizeof(OCILobLocator*);
323 break;
324
325 case PDO_PARAM_STR:
326 default:
327 P->oci_type = SQLT_CHR;
328 value_sz = (sb4) param->max_value_len;
329 if (param->max_value_len == 0) {
330 value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */
331 }
332
333 }
334
335 if (param->name) {
336 STMT_CALL(OCIBindByName, (S->stmt,
337 &P->bind, S->err, (text*)param->name->val,
338 (sb4) param->name->len, 0, value_sz, P->oci_type,
339 &P->indicator, 0, &P->retcode, 0, 0,
340 OCI_DATA_AT_EXEC));
341 } else {
342 STMT_CALL(OCIBindByPos, (S->stmt,
343 &P->bind, S->err, ((ub4)param->paramno)+1,
344 0, value_sz, P->oci_type,
345 &P->indicator, 0, &P->retcode, 0, 0,
346 OCI_DATA_AT_EXEC));
347 }
348
349 STMT_CALL(OCIBindDynamic, (P->bind,
350 S->err,
351 param, oci_bind_input_cb,
352 param, oci_bind_output_cb));
353
354 return 1;
355
356 case PDO_PARAM_EVT_EXEC_PRE:
357 P->indicator = 0;
358 P->used_for_output = 0;
359 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
360 ub4 empty = 0;
361 STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL));
362 STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err));
363 S->have_blobs = 1;
364 }
365 return 1;
366
367 case PDO_PARAM_EVT_EXEC_POST:
368 /* fixup stuff set in motion in oci_bind_output_cb */
369 if (P->used_for_output) {
370 if (P->indicator == -1) {
371 /* set up a NULL value */
372 if (Z_TYPE_P(parameter) == IS_STRING) {
373 /* OCI likes to stick non-terminated strings in things */
374 *Z_STRVAL_P(parameter) = '\0';
375 }
376 zval_ptr_dtor_str(parameter);
377 ZVAL_UNDEF(parameter);
378 } else if (Z_TYPE_P(parameter) == IS_STRING) {
379 Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1);
380 }
381 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) {
382 php_stream *stm;
383
384 if (Z_TYPE_P(parameter) == IS_NULL) {
385 /* if the param is NULL, then we assume that they
386 * wanted to bind a lob locator into it from the query
387 * */
388
389 stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator*)P->thing);
390 if (stm) {
391 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
392 php_stream_to_zval(stm, parameter);
393 }
394 } else {
395 /* we're a LOB being used for insert; transfer the data now */
396 size_t n;
397 ub4 amt, offset = 1;
398 char *consume;
399
400 php_stream_from_zval_no_verify(stm, parameter);
401 if (stm) {
402 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
403 do {
404 char buf[8192];
405 n = php_stream_read(stm, buf, sizeof(buf));
406 if ((int)n <= 0) {
407 break;
408 }
409 consume = buf;
410 do {
411 amt = (ub4) n;
412 OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
413 &amt, offset, consume, (ub4) n,
414 OCI_ONE_PIECE,
415 NULL, NULL, 0, SQLCS_IMPLICIT);
416 offset += amt;
417 n -= amt;
418 consume += amt;
419 } while (n);
420 } while (1);
421 OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
422 OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0);
423 } else if (Z_TYPE_P(parameter) == IS_STRING) {
424 /* stick the string into the LOB */
425 consume = Z_STRVAL_P(parameter);
426 n = Z_STRLEN_P(parameter);
427 if (n) {
428 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
429 while (n) {
430 amt = (ub4) n;
431 OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
432 &amt, offset, consume, (ub4) n,
433 OCI_ONE_PIECE,
434 NULL, NULL, 0, SQLCS_IMPLICIT);
435 consume += amt;
436 n -= amt;
437 }
438 OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
439 }
440 }
441 OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
442 OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
443 P->thing = NULL;
444 }
445 }
446
447 return 1;
448 }
449 }
450
451 return 1;
452 } /* }}} */
453
oci_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)454 static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */
455 {
456 #ifdef HAVE_OCISTMTFETCH2
457 ub4 ociori = OCI_FETCH_NEXT;
458 #endif
459 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
460
461 #ifdef HAVE_OCISTMTFETCH2
462 switch (ori) {
463 case PDO_FETCH_ORI_NEXT: ociori = OCI_FETCH_NEXT; break;
464 case PDO_FETCH_ORI_PRIOR: ociori = OCI_FETCH_PRIOR; break;
465 case PDO_FETCH_ORI_FIRST: ociori = OCI_FETCH_FIRST; break;
466 case PDO_FETCH_ORI_LAST: ociori = OCI_FETCH_LAST; break;
467 case PDO_FETCH_ORI_ABS: ociori = OCI_FETCH_ABSOLUTE; break;
468 case PDO_FETCH_ORI_REL: ociori = OCI_FETCH_RELATIVE; break;
469 }
470 S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT);
471 #else
472 S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
473 #endif
474
475 if (S->last_err == OCI_NO_DATA) {
476 /* no (more) data */
477 return 0;
478 }
479
480 if (S->last_err == OCI_NEED_DATA) {
481 oci_stmt_error("OCI_NEED_DATA");
482 return 0;
483 }
484
485 if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) {
486 return 1;
487 }
488
489 oci_stmt_error("OCIStmtFetch");
490
491 return 0;
492 } /* }}} */
493
oci_define_callback(dvoid * octxp,OCIDefine * define,ub4 iter,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcodepp)494 static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp,
495 ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp)
496 {
497 pdo_oci_column *col = (pdo_oci_column*)octxp;
498
499 switch (col->dtype) {
500 case SQLT_BLOB:
501 case SQLT_CLOB:
502 *piecep = OCI_ONE_PIECE;
503 *bufpp = col->data;
504 *alenpp = &col->datalen;
505 *indpp = (dvoid *)&col->indicator;
506 break;
507 EMPTY_SWITCH_DEFAULT_CASE();
508 }
509
510 return OCI_CONTINUE;
511 }
512
oci_stmt_describe(pdo_stmt_t * stmt,int colno)513 static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
514 {
515 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
516 OCIParam *param = NULL;
517 text *colname;
518 ub2 dtype, data_size, precis;
519 ub4 namelen;
520 struct pdo_column_data *col = &stmt->columns[colno];
521 bool dyn = FALSE;
522
523 /* describe the column */
524 STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1));
525
526 /* what type ? */
527 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
528 (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
529
530 /* how big ? */
531 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE",
532 (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));
533
534 /* precision ? */
535 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
536 (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
537
538 /* name ? */
539 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME",
540 (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));
541
542 col->precision = precis;
543 col->maxlen = data_size;
544 col->name = zend_string_init((char *)colname, namelen, 0);
545
546 S->cols[colno].dtype = dtype;
547
548 /* how much room do we need to store the field */
549 switch (dtype) {
550 case SQLT_LBI:
551 case SQLT_LNG:
552 if (dtype == SQLT_LBI) {
553 dtype = SQLT_BIN;
554 } else {
555 dtype = SQLT_CHR;
556 }
557 S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */
558 S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
559 break;
560
561 case SQLT_BLOB:
562 case SQLT_CLOB:
563 STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL));
564 S->cols[colno].datalen = sizeof(OCILobLocator*);
565 dyn = TRUE;
566 break;
567
568 case SQLT_BIN:
569 default:
570 if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD
571 #ifdef SQLT_TIMESTAMP
572 || dtype == SQLT_TIMESTAMP
573 #endif
574 #ifdef SQLT_TIMESTAMP_TZ
575 || dtype == SQLT_TIMESTAMP_TZ
576 #endif
577 ) {
578 /* should be big enough for most date formats and numbers */
579 S->cols[colno].datalen = 512;
580 #if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)
581 } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) {
582 S->cols[colno].datalen = 1024;
583 #endif
584 } else if (dtype == SQLT_BIN) {
585 S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */
586 } else {
587 S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width);
588 }
589
590 S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
591 dtype = SQLT_CHR;
592 }
593
594 STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1,
595 S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator,
596 &S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT));
597
598 if (dyn) {
599 STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno],
600 oci_define_callback));
601 }
602
603 return 1;
604 } /* }}} */
605
606 struct _oci_lob_env {
607 OCISvcCtx *svc;
608 OCIError *err;
609 };
610 typedef struct _oci_lob_env oci_lob_env;
611
612 struct oci_lob_self {
613 zval dbh;
614 pdo_stmt_t *stmt;
615 pdo_oci_stmt *S;
616 OCILobLocator *lob;
617 oci_lob_env *E;
618 ub4 offset;
619 ub1 csfrm;
620 };
621
oci_blob_write(php_stream * stream,const char * buf,size_t count)622 static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
623 {
624 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
625 ub4 amt;
626 sword r;
627
628 amt = (ub4) count;
629 r = OCILobWrite(self->E->svc, self->E->err, self->lob,
630 &amt, self->offset, (char*)buf, (ub4) count,
631 OCI_ONE_PIECE,
632 NULL, NULL, 0, SQLCS_IMPLICIT);
633
634 if (r != OCI_SUCCESS) {
635 return (ssize_t)-1;
636 }
637
638 self->offset += amt;
639 return amt;
640 }
641
oci_blob_read(php_stream * stream,char * buf,size_t count)642 static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count)
643 {
644 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
645 #if HAVE_OCILOBREAD2
646 oraub8 byte_amt = (oraub8) count;
647 oraub8 char_amt = 0;
648
649 sword r = OCILobRead2(self->E->svc, self->E->err, self->lob,
650 &byte_amt, &char_amt, (oraub8) self->offset, buf, (oraub8) count,
651 OCI_ONE_PIECE, NULL, NULL, 0, self->csfrm);
652 #else
653 ub4 byte_amt = (ub4) count;
654
655 sword r = OCILobRead(self->E->svc, self->E->err, self->lob,
656 &byte_amt, self->offset, buf, (ub4) count,
657 NULL, NULL, 0, SQLCS_IMPLICIT);
658 #endif
659
660 if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
661 return (ssize_t)-1;
662 }
663
664 #if HAVE_OCILOBREAD2
665 self->offset += self->csfrm == 0 ? byte_amt : char_amt;
666 #else
667 self->offset += byte_amt;
668 #endif
669 if (byte_amt < count) {
670 stream->eof = 1;
671 }
672 return byte_amt;
673 }
674
oci_blob_close(php_stream * stream,int close_handle)675 static int oci_blob_close(php_stream *stream, int close_handle)
676 {
677 struct oci_lob_self *self = (struct oci_lob_self *)stream->abstract;
678 pdo_stmt_t *stmt = self->stmt;
679
680 if (close_handle) {
681 zend_object *obj = &stmt->std;
682
683 OCILobClose(self->E->svc, self->E->err, self->lob);
684 zval_ptr_dtor(&self->dbh);
685 GC_DELREF(obj);
686 efree(self->E);
687 efree(self);
688 }
689
690 /* php_pdo_free_statement(stmt); */
691 return 0;
692 }
693
oci_blob_flush(php_stream * stream)694 static int oci_blob_flush(php_stream *stream)
695 {
696 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
697 OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0);
698 return 0;
699 }
700
oci_blob_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffset)701 static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
702 {
703 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
704
705 if (offset >= PDO_OCI_LOBMAXSIZE) {
706 return -1;
707 } else {
708 self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */
709 return 0;
710 }
711 }
712
713 static const php_stream_ops oci_blob_stream_ops = {
714 oci_blob_write,
715 oci_blob_read,
716 oci_blob_close,
717 oci_blob_flush,
718 "pdo_oci blob stream",
719 oci_blob_seek,
720 NULL,
721 NULL,
722 NULL
723 };
724
oci_create_lob_stream(zval * dbh,pdo_stmt_t * stmt,OCILobLocator * lob)725 static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob)
726 {
727 php_stream *stm;
728 struct oci_lob_self *self = ecalloc(1, sizeof(*self));
729
730 ZVAL_COPY_VALUE(&self->dbh, dbh);
731 self->lob = lob;
732 self->offset = 1; /* 1-based */
733 self->stmt = stmt;
734 self->S = (pdo_oci_stmt*)stmt->driver_data;
735 self->E = ecalloc(1, sizeof(oci_lob_env));
736 self->E->svc = self->S->H->svc;
737 self->E->err = self->S->err;
738
739 OCILobCharSetForm(self->S->H->env, self->S->err, self->lob, &self->csfrm);
740
741 stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
742
743 if (stm) {
744 zend_object *obj;
745 obj = &stmt->std;
746 Z_ADDREF(self->dbh);
747 GC_ADDREF(obj);
748 return stm;
749 }
750
751 efree(self);
752 return NULL;
753 }
754
oci_stmt_get_col(pdo_stmt_t * stmt,int colno,zval * result,enum pdo_param_type * type)755 static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) /* {{{ */
756 {
757 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
758 pdo_oci_column *C = &S->cols[colno];
759
760 /* check the indicator to ensure that the data is intact */
761 if (C->indicator == -1) {
762 /* A NULL value */
763 ZVAL_NULL(result);
764 return 1;
765 } else if (C->indicator == 0) {
766 /* it was stored perfectly */
767
768 if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) {
769 if (C->data) {
770 php_stream *stream = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator*)C->data);
771 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY);
772 php_stream_to_zval(stream, result);
773 return 1;
774 }
775 return 0;
776 }
777
778 ZVAL_STRINGL_FAST(result, C->data, C->fetched_len);
779 return 1;
780 } else {
781 /* it was truncated */
782 php_error_docref(NULL, E_WARNING, "Column %d data was too large for buffer and was truncated to fit it", colno);
783 ZVAL_STRINGL(result, C->data, C->fetched_len);
784 return 1;
785 }
786 } /* }}} */
787
788
oci_stmt_col_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)789 static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */
790 {
791 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
792 OCIParam *param = NULL;
793 ub2 dtype, precis;
794 sb1 scale;
795 zval flags;
796 ub1 isnull, charset_form;
797 if (!S->stmt) {
798 return FAILURE;
799 }
800 if (colno >= stmt->column_count) {
801 /* error invalid column */
802 return FAILURE;
803 }
804
805 array_init(return_value);
806 array_init(&flags);
807
808 /* describe the column */
809 STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1));
810
811 /* column data type */
812 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
813 (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
814
815 /* column precision */
816 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
817 (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
818
819 /* column scale */
820 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
821 (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
822
823 /* string column charset form */
824 if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) {
825 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_CHARSET_FORM",
826 (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err));
827 }
828
829
830 if (dtype) {
831 /* if there is a declared type */
832 switch (dtype) {
833 #ifdef SQLT_TIMESTAMP
834 case SQLT_TIMESTAMP:
835 add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP");
836 add_assoc_string(return_value, "native_type", "TIMESTAMP");
837 break;
838 #endif
839 #ifdef SQLT_TIMESTAMP_TZ
840 case SQLT_TIMESTAMP_TZ:
841 add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE");
842 add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE");
843 break;
844 #endif
845 #ifdef SQLT_TIMESTAMP_LTZ
846 case SQLT_TIMESTAMP_LTZ:
847 add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE");
848 add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE");
849 break;
850 #endif
851 #ifdef SQLT_INTERVAL_YM
852 case SQLT_INTERVAL_YM:
853 add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH");
854 add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH");
855 break;
856 #endif
857 #ifdef SQLT_INTERVAL_DS
858 case SQLT_INTERVAL_DS:
859 add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND");
860 add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND");
861 break;
862 #endif
863 case SQLT_DAT:
864 add_assoc_string(return_value, "oci:decl_type", "DATE");
865 add_assoc_string(return_value, "native_type", "DATE");
866 break;
867 case SQLT_FLT :
868 case SQLT_NUM:
869 /* if the precision is nonzero and scale is -127 then it is a FLOAT */
870 if (scale == -127 && precis != 0) {
871 add_assoc_string(return_value, "oci:decl_type", "FLOAT");
872 add_assoc_string(return_value, "native_type", "FLOAT");
873 } else {
874 add_assoc_string(return_value, "oci:decl_type", "NUMBER");
875 add_assoc_string(return_value, "native_type", "NUMBER");
876 }
877 break;
878 case SQLT_LNG:
879 add_assoc_string(return_value, "oci:decl_type", "LONG");
880 add_assoc_string(return_value, "native_type", "LONG");
881 break;
882 case SQLT_BIN:
883 add_assoc_string(return_value, "oci:decl_type", "RAW");
884 add_assoc_string(return_value, "native_type", "RAW");
885 break;
886 case SQLT_LBI:
887 add_assoc_string(return_value, "oci:decl_type", "LONG RAW");
888 add_assoc_string(return_value, "native_type", "LONG RAW");
889 break;
890 case SQLT_CHR:
891 case SQLT_VCS:
892 if (charset_form == SQLCS_NCHAR) {
893 add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2");
894 add_assoc_string(return_value, "native_type", "NVARCHAR2");
895 } else {
896 add_assoc_string(return_value, "oci:decl_type", "VARCHAR2");
897 add_assoc_string(return_value, "native_type", "VARCHAR2");
898 }
899 break;
900 case SQLT_AFC:
901 if (charset_form == SQLCS_NCHAR) {
902 add_assoc_string(return_value, "oci:decl_type", "NCHAR");
903 add_assoc_string(return_value, "native_type", "NCHAR");
904 } else {
905 add_assoc_string(return_value, "oci:decl_type", "CHAR");
906 add_assoc_string(return_value, "native_type", "CHAR");
907 }
908 break;
909 case SQLT_BLOB:
910 add_assoc_string(return_value, "oci:decl_type", "BLOB");
911 add_next_index_string(&flags, "blob");
912 add_assoc_string(return_value, "native_type", "BLOB");
913 break;
914 case SQLT_CLOB:
915 if (charset_form == SQLCS_NCHAR) {
916 add_assoc_string(return_value, "oci:decl_type", "NCLOB");
917 add_assoc_string(return_value, "native_type", "NCLOB");
918 } else {
919 add_assoc_string(return_value, "oci:decl_type", "CLOB");
920 add_assoc_string(return_value, "native_type", "CLOB");
921 }
922 add_next_index_string(&flags, "blob");
923 break;
924 case SQLT_BFILE:
925 add_assoc_string(return_value, "oci:decl_type", "BFILE");
926 add_next_index_string(&flags, "blob");
927 add_assoc_string(return_value, "native_type", "BFILE");
928 break;
929 case SQLT_RDD:
930 add_assoc_string(return_value, "oci:decl_type", "ROWID");
931 add_assoc_string(return_value, "native_type", "ROWID");
932 break;
933 case SQLT_BFLOAT:
934 case SQLT_IBFLOAT:
935 add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT");
936 add_assoc_string(return_value, "native_type", "BINARY_FLOAT");
937 break;
938 case SQLT_BDOUBLE:
939 case SQLT_IBDOUBLE:
940 add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE");
941 add_assoc_string(return_value, "native_type", "BINARY_DOUBLE");
942 break;
943 default:
944 add_assoc_long(return_value, "oci:decl_type", dtype);
945 add_assoc_string(return_value, "native_type", "UNKNOWN");
946 }
947 } else {
948 /* if the column is NULL */
949 add_assoc_long(return_value, "oci:decl_type", 0);
950 add_assoc_string(return_value, "native_type", "NULL");
951 }
952
953 switch (dtype) {
954 case SQLT_BLOB:
955 case SQLT_CLOB:
956 add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB);
957 break;
958 default:
959 add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
960 break;
961 }
962
963 /* column can be null */
964 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL",
965 (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err));
966
967 if (isnull) {
968 add_next_index_string(&flags, "nullable");
969 } else {
970 add_next_index_string(&flags, "not_null");
971 }
972
973 /* PDO type */
974 switch (dtype) {
975 case SQLT_BFILE:
976 case SQLT_BLOB:
977 case SQLT_CLOB:
978 add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB);
979 break;
980 default:
981 add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
982 }
983
984 add_assoc_long(return_value, "scale", scale);
985 add_assoc_zval(return_value, "flags", &flags);
986
987 OCIDescriptorFree(param, OCI_DTYPE_PARAM);
988 return SUCCESS;
989 } /* }}} */
990
991 const struct pdo_stmt_methods oci_stmt_methods = {
992 oci_stmt_dtor,
993 oci_stmt_execute,
994 oci_stmt_fetch,
995 oci_stmt_describe,
996 oci_stmt_get_col,
997 oci_stmt_param_hook,
998 NULL, /* set_attr */
999 NULL, /* get_attr */
1000 oci_stmt_col_meta,
1001 NULL,
1002 NULL
1003 };
1004