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