1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 if (!try_convert_to_string(parameter)) {
225 return OCI_ERROR;
226 }
227 *bufpp = Z_STRVAL_P(parameter);
228 *alenp = (ub4) Z_STRLEN_P(parameter);
229 }
230
231 *piecep = OCI_ONE_PIECE;
232 return OCI_CONTINUE;
233 } /* }}} */
234
oci_bind_output_cb(dvoid * ctx,OCIBind * bindp,ub4 iter,ub4 index,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcodepp)235 static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) /* {{{ */
236 {
237 struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
238 pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
239 zval *parameter;
240
241 if (!param) {
242 php_error_docref(NULL, E_WARNING, "param is NULL in oci_bind_output_cb; this should not happen");
243 return OCI_ERROR;
244 }
245
246 if (Z_ISREF(param->parameter))
247 parameter = Z_REFVAL(param->parameter);
248 else
249 parameter = ¶m->parameter;
250
251 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
252 P->actual_len = sizeof(OCILobLocator*);
253 *bufpp = P->thing;
254 *alenpp = &P->actual_len;
255 *piecep = OCI_ONE_PIECE;
256 *rcodepp = &P->retcode;
257 *indpp = &P->indicator;
258 return OCI_CONTINUE;
259 }
260
261 if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) {
262 return OCI_CONTINUE;
263 }
264
265 zval_ptr_dtor(parameter);
266
267 Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1);
268 P->used_for_output = 1;
269
270 P->actual_len = (ub4) Z_STRLEN_P(parameter);
271 *alenpp = &P->actual_len;
272 *bufpp = (Z_STR_P(parameter))->val;
273 *piecep = OCI_ONE_PIECE;
274 *rcodepp = &P->retcode;
275 *indpp = &P->indicator;
276
277 return OCI_CONTINUE;
278 } /* }}} */
279
oci_stmt_param_hook(pdo_stmt_t * stmt,struct pdo_bound_param_data * param,enum pdo_param_event event_type)280 static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) /* {{{ */
281 {
282 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
283
284 /* we're only interested in parameters for prepared SQL right now */
285 if (param->is_param) {
286 pdo_oci_bound_param *P;
287 sb4 value_sz = -1;
288 zval *parameter;
289
290 if (Z_ISREF(param->parameter))
291 parameter = Z_REFVAL(param->parameter);
292 else
293 parameter = ¶m->parameter;
294
295 P = (pdo_oci_bound_param*)param->driver_data;
296
297 switch (event_type) {
298 case PDO_PARAM_EVT_FETCH_PRE:
299 case PDO_PARAM_EVT_FETCH_POST:
300 case PDO_PARAM_EVT_NORMALIZE:
301 /* Do nothing */
302 break;
303
304 case PDO_PARAM_EVT_FREE:
305 P = param->driver_data;
306 if (P && P->thing) {
307 OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
308 OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
309 P->thing = NULL;
310 efree(P);
311 }
312 else if (P) {
313 efree(P);
314 }
315 break;
316
317 case PDO_PARAM_EVT_ALLOC:
318 P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param));
319 param->driver_data = P;
320
321 /* figure out what we're doing */
322 switch (PDO_PARAM_TYPE(param->param_type)) {
323 case PDO_PARAM_STMT:
324 return 0;
325
326 case PDO_PARAM_LOB:
327 /* P->thing is now an OCILobLocator * */
328 P->oci_type = SQLT_BLOB;
329 value_sz = (sb4) sizeof(OCILobLocator*);
330 break;
331
332 case PDO_PARAM_STR:
333 default:
334 P->oci_type = SQLT_CHR;
335 value_sz = (sb4) param->max_value_len;
336 if (param->max_value_len == 0) {
337 value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */
338 }
339
340 }
341
342 if (param->name) {
343 STMT_CALL(OCIBindByName, (S->stmt,
344 &P->bind, S->err, (text*)param->name->val,
345 (sb4) param->name->len, 0, value_sz, P->oci_type,
346 &P->indicator, 0, &P->retcode, 0, 0,
347 OCI_DATA_AT_EXEC));
348 } else {
349 STMT_CALL(OCIBindByPos, (S->stmt,
350 &P->bind, S->err, ((ub4)param->paramno)+1,
351 0, value_sz, P->oci_type,
352 &P->indicator, 0, &P->retcode, 0, 0,
353 OCI_DATA_AT_EXEC));
354 }
355
356 STMT_CALL(OCIBindDynamic, (P->bind,
357 S->err,
358 param, oci_bind_input_cb,
359 param, oci_bind_output_cb));
360
361 return 1;
362
363 case PDO_PARAM_EVT_EXEC_PRE:
364 P->indicator = 0;
365 P->used_for_output = 0;
366 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
367 ub4 empty = 0;
368 STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL));
369 STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err));
370 S->have_blobs = 1;
371 }
372 return 1;
373
374 case PDO_PARAM_EVT_EXEC_POST:
375 /* fixup stuff set in motion in oci_bind_output_cb */
376 if (P->used_for_output) {
377 if (P->indicator == -1) {
378 /* set up a NULL value */
379 if (Z_TYPE_P(parameter) == IS_STRING) {
380 /* OCI likes to stick non-terminated strings in things */
381 *Z_STRVAL_P(parameter) = '\0';
382 }
383 zval_ptr_dtor_str(parameter);
384 ZVAL_UNDEF(parameter);
385 } else if (Z_TYPE_P(parameter) == IS_STRING) {
386 Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1);
387 }
388 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) {
389 php_stream *stm;
390
391 if (Z_TYPE_P(parameter) == IS_NULL) {
392 /* if the param is NULL, then we assume that they
393 * wanted to bind a lob locator into it from the query
394 * */
395
396 stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator*)P->thing);
397 if (stm) {
398 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
399 php_stream_to_zval(stm, parameter);
400 }
401 } else {
402 /* we're a LOB being used for insert; transfer the data now */
403 size_t n;
404 ub4 amt, offset = 1;
405 char *consume;
406
407 php_stream_from_zval_no_verify(stm, parameter);
408 if (stm) {
409 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
410 do {
411 char buf[8192];
412 n = php_stream_read(stm, buf, sizeof(buf));
413 if ((int)n <= 0) {
414 break;
415 }
416 consume = buf;
417 do {
418 amt = (ub4) n;
419 OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
420 &amt, offset, consume, (ub4) n,
421 OCI_ONE_PIECE,
422 NULL, NULL, 0, SQLCS_IMPLICIT);
423 offset += amt;
424 n -= amt;
425 consume += amt;
426 } while (n);
427 } while (1);
428 OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
429 OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0);
430 } else if (Z_TYPE_P(parameter) == IS_STRING) {
431 /* stick the string into the LOB */
432 consume = Z_STRVAL_P(parameter);
433 n = Z_STRLEN_P(parameter);
434 if (n) {
435 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
436 while (n) {
437 amt = (ub4) n;
438 OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
439 &amt, offset, consume, (ub4) n,
440 OCI_ONE_PIECE,
441 NULL, NULL, 0, SQLCS_IMPLICIT);
442 consume += amt;
443 n -= amt;
444 }
445 OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
446 }
447 }
448 OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
449 OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
450 P->thing = NULL;
451 }
452 }
453
454 return 1;
455 }
456 }
457
458 return 1;
459 } /* }}} */
460
oci_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)461 static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */
462 {
463 #if HAVE_OCISTMTFETCH2
464 ub4 ociori;
465 #endif
466 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
467
468 #if HAVE_OCISTMTFETCH2
469 switch (ori) {
470 case PDO_FETCH_ORI_NEXT: ociori = OCI_FETCH_NEXT; break;
471 case PDO_FETCH_ORI_PRIOR: ociori = OCI_FETCH_PRIOR; break;
472 case PDO_FETCH_ORI_FIRST: ociori = OCI_FETCH_FIRST; break;
473 case PDO_FETCH_ORI_LAST: ociori = OCI_FETCH_LAST; break;
474 case PDO_FETCH_ORI_ABS: ociori = OCI_FETCH_ABSOLUTE; break;
475 case PDO_FETCH_ORI_REL: ociori = OCI_FETCH_RELATIVE; break;
476 }
477 S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT);
478 #else
479 S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
480 #endif
481
482 if (S->last_err == OCI_NO_DATA) {
483 /* no (more) data */
484 return 0;
485 }
486
487 if (S->last_err == OCI_NEED_DATA) {
488 oci_stmt_error("OCI_NEED_DATA");
489 return 0;
490 }
491
492 if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) {
493 return 1;
494 }
495
496 oci_stmt_error("OCIStmtFetch");
497
498 return 0;
499 } /* }}} */
500
oci_define_callback(dvoid * octxp,OCIDefine * define,ub4 iter,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcodepp)501 static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp,
502 ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp)
503 {
504 pdo_oci_column *col = (pdo_oci_column*)octxp;
505
506 switch (col->dtype) {
507 case SQLT_BLOB:
508 case SQLT_CLOB:
509 *piecep = OCI_ONE_PIECE;
510 *bufpp = col->data;
511 *alenpp = &col->datalen;
512 *indpp = (dvoid *)&col->indicator;
513 break;
514
515 default:
516 php_error_docref(NULL, E_WARNING,
517 "unhandled datatype in oci_define_callback; this should not happen");
518 return OCI_ERROR;
519 }
520
521 return OCI_CONTINUE;
522 }
523
oci_stmt_describe(pdo_stmt_t * stmt,int colno)524 static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
525 {
526 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
527 OCIParam *param = NULL;
528 text *colname;
529 ub2 dtype, data_size, precis;
530 ub4 namelen;
531 struct pdo_column_data *col = &stmt->columns[colno];
532 zend_bool dyn = FALSE;
533
534 /* describe the column */
535 STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1));
536
537 /* what type ? */
538 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
539 (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
540
541 /* how big ? */
542 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE",
543 (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));
544
545 /* precision ? */
546 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
547 (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
548
549 /* name ? */
550 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME",
551 (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));
552
553 col->precision = precis;
554 col->maxlen = data_size;
555 col->name = zend_string_init((char *)colname, namelen, 0);
556
557 S->cols[colno].dtype = dtype;
558
559 /* how much room do we need to store the field */
560 switch (dtype) {
561 case SQLT_LBI:
562 case SQLT_LNG:
563 if (dtype == SQLT_LBI) {
564 dtype = SQLT_BIN;
565 } else {
566 dtype = SQLT_CHR;
567 }
568 S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */
569 S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
570 col->param_type = PDO_PARAM_STR;
571 break;
572
573 case SQLT_BLOB:
574 case SQLT_CLOB:
575 col->param_type = PDO_PARAM_LOB;
576 STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL));
577 S->cols[colno].datalen = sizeof(OCILobLocator*);
578 dyn = TRUE;
579 break;
580
581 case SQLT_BIN:
582 default:
583 if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD
584 #ifdef SQLT_TIMESTAMP
585 || dtype == SQLT_TIMESTAMP
586 #endif
587 #ifdef SQLT_TIMESTAMP_TZ
588 || dtype == SQLT_TIMESTAMP_TZ
589 #endif
590 ) {
591 /* should be big enough for most date formats and numbers */
592 S->cols[colno].datalen = 512;
593 #if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)
594 } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) {
595 S->cols[colno].datalen = 1024;
596 #endif
597 } else if (dtype == SQLT_BIN) {
598 S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */
599 } else {
600 S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width);
601 }
602
603 S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
604 dtype = SQLT_CHR;
605
606 /* returning data as a string */
607 col->param_type = PDO_PARAM_STR;
608 }
609
610 STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1,
611 S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator,
612 &S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT));
613
614 if (dyn) {
615 STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno],
616 oci_define_callback));
617 }
618
619 return 1;
620 } /* }}} */
621
622 struct _oci_lob_env {
623 OCISvcCtx *svc;
624 OCIError *err;
625 };
626 typedef struct _oci_lob_env oci_lob_env;
627
628 struct oci_lob_self {
629 zval dbh;
630 pdo_stmt_t *stmt;
631 pdo_oci_stmt *S;
632 OCILobLocator *lob;
633 oci_lob_env *E;
634 ub4 offset;
635 };
636
oci_blob_write(php_stream * stream,const char * buf,size_t count)637 static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
638 {
639 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
640 ub4 amt;
641 sword r;
642
643 amt = (ub4) count;
644 r = OCILobWrite(self->E->svc, self->E->err, self->lob,
645 &amt, self->offset, (char*)buf, (ub4) count,
646 OCI_ONE_PIECE,
647 NULL, NULL, 0, SQLCS_IMPLICIT);
648
649 if (r != OCI_SUCCESS) {
650 return (ssize_t)-1;
651 }
652
653 self->offset += amt;
654 return amt;
655 }
656
oci_blob_read(php_stream * stream,char * buf,size_t count)657 static size_t oci_blob_read(php_stream *stream, char *buf, size_t count)
658 {
659 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
660 ub4 amt;
661 sword r;
662
663 amt = (ub4) count;
664 r = OCILobRead(self->E->svc, self->E->err, self->lob,
665 &amt, self->offset, buf, (ub4) count,
666 NULL, NULL, 0, SQLCS_IMPLICIT);
667
668 if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
669 return (size_t)-1;
670 }
671
672 self->offset += amt;
673 if (amt < count) {
674 stream->eof = 1;
675 }
676 return amt;
677 }
678
oci_blob_close(php_stream * stream,int close_handle)679 static int oci_blob_close(php_stream *stream, int close_handle)
680 {
681 struct oci_lob_self *self = (struct oci_lob_self *)stream->abstract;
682 pdo_stmt_t *stmt = self->stmt;
683
684 if (close_handle) {
685 zend_object *obj = &stmt->std;
686
687 OCILobClose(self->E->svc, self->E->err, self->lob);
688 zval_ptr_dtor(&self->dbh);
689 GC_DELREF(obj);
690 efree(self->E);
691 efree(self);
692 }
693
694 /* php_pdo_free_statement(stmt); */
695 return 0;
696 }
697
oci_blob_flush(php_stream * stream)698 static int oci_blob_flush(php_stream *stream)
699 {
700 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
701 OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0);
702 return 0;
703 }
704
oci_blob_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffset)705 static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
706 {
707 struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
708
709 if (offset >= PDO_OCI_LOBMAXSIZE) {
710 return -1;
711 } else {
712 self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */
713 return 0;
714 }
715 }
716
717 static const php_stream_ops oci_blob_stream_ops = {
718 oci_blob_write,
719 oci_blob_read,
720 oci_blob_close,
721 oci_blob_flush,
722 "pdo_oci blob stream",
723 oci_blob_seek,
724 NULL,
725 NULL,
726 NULL
727 };
728
oci_create_lob_stream(zval * dbh,pdo_stmt_t * stmt,OCILobLocator * lob)729 static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob)
730 {
731 php_stream *stm;
732 struct oci_lob_self *self = ecalloc(1, sizeof(*self));
733
734 ZVAL_COPY_VALUE(&self->dbh, dbh);
735 self->lob = lob;
736 self->offset = 1; /* 1-based */
737 self->stmt = stmt;
738 self->S = (pdo_oci_stmt*)stmt->driver_data;
739 self->E = ecalloc(1, sizeof(oci_lob_env));
740 self->E->svc = self->S->H->svc;
741 self->E->err = self->S->err;
742
743 stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
744
745 if (stm) {
746 zend_object *obj;
747 obj = &stmt->std;
748 Z_ADDREF(self->dbh);
749 GC_ADDREF(obj);
750 return stm;
751 }
752
753 efree(self);
754 return NULL;
755 }
756
oci_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,size_t * len,int * caller_frees)757 static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) /* {{{ */
758 {
759 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
760 pdo_oci_column *C = &S->cols[colno];
761
762 /* check the indicator to ensure that the data is intact */
763 if (C->indicator == -1) {
764 /* A NULL value */
765 *ptr = NULL;
766 *len = 0;
767 return 1;
768 } else if (C->indicator == 0) {
769 /* it was stored perfectly */
770
771 if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) {
772 if (C->data) {
773 *ptr = (char*)oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator*)C->data);
774 OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY);
775 }
776 *len = (size_t) 0;
777 return *ptr ? 1 : 0;
778 }
779
780 *ptr = C->data;
781 *len = (size_t) C->fetched_len;
782 return 1;
783 } else {
784 /* it was truncated */
785 php_error_docref(NULL, E_WARNING, "column %d data was too large for buffer and was truncated to fit it", colno);
786
787 *ptr = C->data;
788 *len = (size_t) C->fetched_len;
789 return 1;
790 }
791 } /* }}} */
792
793
oci_stmt_col_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)794 static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */
795 {
796 pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
797 OCIParam *param = NULL;
798 ub2 dtype, precis;
799 sb1 scale;
800 zval flags;
801 ub1 isnull, charset_form;
802 if (!S->stmt) {
803 return FAILURE;
804 }
805 if (colno >= stmt->column_count) {
806 /* error invalid column */
807 return FAILURE;
808 }
809
810 array_init(return_value);
811 array_init(&flags);
812
813 /* describe the column */
814 STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)¶m, colno+1));
815
816 /* column data type */
817 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
818 (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
819
820 /* column precision */
821 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
822 (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
823
824 /* column scale */
825 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
826 (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
827
828 /* string column charset form */
829 if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) {
830 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_CHARSET_FORM",
831 (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err));
832 }
833
834
835 if (dtype) {
836 /* if there is a declared type */
837 switch (dtype) {
838 #ifdef SQLT_TIMESTAMP
839 case SQLT_TIMESTAMP:
840 add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP");
841 add_assoc_string(return_value, "native_type", "TIMESTAMP");
842 break;
843 #endif
844 #ifdef SQLT_TIMESTAMP_TZ
845 case SQLT_TIMESTAMP_TZ:
846 add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE");
847 add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE");
848 break;
849 #endif
850 #ifdef SQLT_TIMESTAMP_LTZ
851 case SQLT_TIMESTAMP_LTZ:
852 add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE");
853 add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE");
854 break;
855 #endif
856 #ifdef SQLT_INTERVAL_YM
857 case SQLT_INTERVAL_YM:
858 add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH");
859 add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH");
860 break;
861 #endif
862 #ifdef SQLT_INTERVAL_DS
863 case SQLT_INTERVAL_DS:
864 add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND");
865 add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND");
866 break;
867 #endif
868 case SQLT_DAT:
869 add_assoc_string(return_value, "oci:decl_type", "DATE");
870 add_assoc_string(return_value, "native_type", "DATE");
871 break;
872 case SQLT_FLT :
873 case SQLT_NUM:
874 /* if the precision is nonzero and scale is -127 then it is a FLOAT */
875 if (scale == -127 && precis != 0) {
876 add_assoc_string(return_value, "oci:decl_type", "FLOAT");
877 add_assoc_string(return_value, "native_type", "FLOAT");
878 } else {
879 add_assoc_string(return_value, "oci:decl_type", "NUMBER");
880 add_assoc_string(return_value, "native_type", "NUMBER");
881 }
882 break;
883 case SQLT_LNG:
884 add_assoc_string(return_value, "oci:decl_type", "LONG");
885 add_assoc_string(return_value, "native_type", "LONG");
886 break;
887 case SQLT_BIN:
888 add_assoc_string(return_value, "oci:decl_type", "RAW");
889 add_assoc_string(return_value, "native_type", "RAW");
890 break;
891 case SQLT_LBI:
892 add_assoc_string(return_value, "oci:decl_type", "LONG RAW");
893 add_assoc_string(return_value, "native_type", "LONG RAW");
894 break;
895 case SQLT_CHR:
896 case SQLT_VCS:
897 if (charset_form == SQLCS_NCHAR) {
898 add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2");
899 add_assoc_string(return_value, "native_type", "NVARCHAR2");
900 } else {
901 add_assoc_string(return_value, "oci:decl_type", "VARCHAR2");
902 add_assoc_string(return_value, "native_type", "VARCHAR2");
903 }
904 break;
905 case SQLT_AFC:
906 if (charset_form == SQLCS_NCHAR) {
907 add_assoc_string(return_value, "oci:decl_type", "NCHAR");
908 add_assoc_string(return_value, "native_type", "NCHAR");
909 } else {
910 add_assoc_string(return_value, "oci:decl_type", "CHAR");
911 add_assoc_string(return_value, "native_type", "CHAR");
912 }
913 break;
914 case SQLT_BLOB:
915 add_assoc_string(return_value, "oci:decl_type", "BLOB");
916 add_next_index_string(&flags, "blob");
917 add_assoc_string(return_value, "native_type", "BLOB");
918 break;
919 case SQLT_CLOB:
920 if (charset_form == SQLCS_NCHAR) {
921 add_assoc_string(return_value, "oci:decl_type", "NCLOB");
922 add_assoc_string(return_value, "native_type", "NCLOB");
923 } else {
924 add_assoc_string(return_value, "oci:decl_type", "CLOB");
925 add_assoc_string(return_value, "native_type", "CLOB");
926 }
927 add_next_index_string(&flags, "blob");
928 break;
929 case SQLT_BFILE:
930 add_assoc_string(return_value, "oci:decl_type", "BFILE");
931 add_next_index_string(&flags, "blob");
932 add_assoc_string(return_value, "native_type", "BFILE");
933 break;
934 case SQLT_RDD:
935 add_assoc_string(return_value, "oci:decl_type", "ROWID");
936 add_assoc_string(return_value, "native_type", "ROWID");
937 break;
938 case SQLT_BFLOAT:
939 case SQLT_IBFLOAT:
940 add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT");
941 add_assoc_string(return_value, "native_type", "BINARY_FLOAT");
942 break;
943 case SQLT_BDOUBLE:
944 case SQLT_IBDOUBLE:
945 add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE");
946 add_assoc_string(return_value, "native_type", "BINARY_DOUBLE");
947 break;
948 default:
949 add_assoc_long(return_value, "oci:decl_type", dtype);
950 add_assoc_string(return_value, "native_type", "UNKNOWN");
951 }
952 } else {
953 /* if the column is NULL */
954 add_assoc_long(return_value, "oci:decl_type", 0);
955 add_assoc_string(return_value, "native_type", "NULL");
956 }
957
958 /* column can be null */
959 STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL",
960 (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err));
961
962 if (isnull) {
963 add_next_index_string(&flags, "nullable");
964 } else {
965 add_next_index_string(&flags, "not_null");
966 }
967
968 /* PDO type */
969 switch (dtype) {
970 case SQLT_BFILE:
971 case SQLT_BLOB:
972 case SQLT_CLOB:
973 add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB);
974 break;
975 default:
976 add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
977 }
978
979 add_assoc_long(return_value, "scale", scale);
980 add_assoc_zval(return_value, "flags", &flags);
981
982 OCIDescriptorFree(param, OCI_DTYPE_PARAM);
983 return SUCCESS;
984 } /* }}} */
985
986 const struct pdo_stmt_methods oci_stmt_methods = {
987 oci_stmt_dtor,
988 oci_stmt_execute,
989 oci_stmt_fetch,
990 oci_stmt_describe,
991 oci_stmt_get_col,
992 oci_stmt_param_hook,
993 NULL, /* set_attr */
994 NULL, /* get_attr */
995 oci_stmt_col_meta
996 };
997