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