xref: /PHP-5.5/ext/pdo_oci/oci_statement.c (revision 73c1be26)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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 						OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB);
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_FETCH_PRE:
274 			case PDO_PARAM_EVT_FETCH_POST:
275 			case PDO_PARAM_EVT_NORMALIZE:
276 				/* Do nothing */
277 				break;
278 
279 			case PDO_PARAM_EVT_FREE:
280 				P = param->driver_data;
281 				if (P) {
282 					efree(P);
283 				}
284 				break;
285 
286 			case PDO_PARAM_EVT_ALLOC:
287 				P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param));
288 				param->driver_data = P;
289 
290 				/* figure out what we're doing */
291 				switch (PDO_PARAM_TYPE(param->param_type)) {
292 					case PDO_PARAM_STMT:
293 						return 0;
294 
295 					case PDO_PARAM_LOB:
296 						/* P->thing is now an OCILobLocator * */
297 						P->oci_type = SQLT_BLOB;
298 						value_sz = sizeof(OCILobLocator*);
299 						break;
300 
301 					case PDO_PARAM_STR:
302 					default:
303 						P->oci_type = SQLT_CHR;
304 						value_sz = param->max_value_len;
305 						if (param->max_value_len == 0) {
306 							value_sz = 1332; /* maximum size before value is interpreted as a LONG value */
307 						}
308 
309 				}
310 
311 				if (param->name) {
312 					STMT_CALL(OCIBindByName, (S->stmt,
313 							&P->bind, S->err, (text*)param->name,
314 							param->namelen, 0, value_sz, P->oci_type,
315 							&P->indicator, 0, &P->retcode, 0, 0,
316 							OCI_DATA_AT_EXEC));
317 				} else {
318 					STMT_CALL(OCIBindByPos, (S->stmt,
319 							&P->bind, S->err, param->paramno+1,
320 							0, value_sz, P->oci_type,
321 							&P->indicator, 0, &P->retcode, 0, 0,
322 							OCI_DATA_AT_EXEC));
323 				}
324 
325 				STMT_CALL(OCIBindDynamic, (P->bind,
326 							S->err,
327 							param, oci_bind_input_cb,
328 							param, oci_bind_output_cb));
329 
330 				return 1;
331 
332 			case PDO_PARAM_EVT_EXEC_PRE:
333 				P->indicator = 0;
334 				P->used_for_output = 0;
335 				if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
336 					ub4 empty = 0;
337 					STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL));
338 					STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err));
339 					S->have_blobs = 1;
340 				}
341 				return 1;
342 
343 			case PDO_PARAM_EVT_EXEC_POST:
344 				/* fixup stuff set in motion in oci_bind_output_cb */
345 				if (P->used_for_output) {
346 					if (P->indicator == -1) {
347 						/* set up a NULL value */
348 						if (Z_TYPE_P(param->parameter) == IS_STRING
349 #if ZEND_EXTENSION_API_NO < 220040718
350 								&& Z_STRVAL_P(param->parameter) != empty_string
351 #endif
352 						   ) {
353 							/* OCI likes to stick non-terminated strings in things */
354 							*Z_STRVAL_P(param->parameter) = '\0';
355 						}
356 						zval_dtor(param->parameter);
357 						ZVAL_NULL(param->parameter);
358 					} else if (Z_TYPE_P(param->parameter) == IS_STRING
359 #if ZEND_EXTENSION_API_NO < 220040718
360 							&& Z_STRVAL_P(param->parameter) != empty_string
361 #endif
362 							) {
363 						Z_STRLEN_P(param->parameter) = P->actual_len;
364 						Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), P->actual_len+1);
365 						Z_STRVAL_P(param->parameter)[P->actual_len] = '\0';
366 					}
367 				} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) {
368 					php_stream *stm;
369 
370 					if (Z_TYPE_P(param->parameter) == IS_NULL) {
371 						/* if the param is NULL, then we assume that they
372 						 * wanted to bind a lob locator into it from the query
373 						 * */
374 
375 						stm = oci_create_lob_stream(stmt, (OCILobLocator*)P->thing TSRMLS_CC);
376 						if (stm) {
377 							OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
378 							php_stream_to_zval(stm, param->parameter);
379 							P->thing = NULL;
380 						}
381 					} else {
382 						/* we're a LOB being used for insert; transfer the data now */
383 						size_t n;
384 						ub4 amt, offset = 1;
385 						char *consume;
386 
387 						php_stream_from_zval_no_verify(stm, &param->parameter);
388 						if (stm) {
389 							OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
390 							do {
391 								char buf[8192];
392 								n = php_stream_read(stm, buf, sizeof(buf));
393 								if ((int)n <= 0) {
394 									break;
395 								}
396 								consume = buf;
397 								do {
398 									amt = n;
399 									OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
400 											&amt, offset, consume, n,
401 											OCI_ONE_PIECE,
402 											NULL, NULL, 0, SQLCS_IMPLICIT);
403 									offset += amt;
404 									n -= amt;
405 									consume += amt;
406 								} while (n);
407 							} while (1);
408 							OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
409 							OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0);
410 						} else if (Z_TYPE_P(param->parameter) == IS_STRING) {
411 							/* stick the string into the LOB */
412 							consume = Z_STRVAL_P(param->parameter);
413 							n = Z_STRLEN_P(param->parameter);
414 							if (n) {
415 								OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
416 								while (n) {
417 									amt = n;
418 									OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
419 											&amt, offset, consume, n,
420 											OCI_ONE_PIECE,
421 											NULL, NULL, 0, SQLCS_IMPLICIT);
422 									consume += amt;
423 									n -= amt;
424 								}
425 								OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
426 							}
427 						}
428 						OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
429 						P->thing = NULL;
430 					}
431 				}
432 
433 				return 1;
434 		}
435 	}
436 
437 	return 1;
438 } /* }}} */
439 
oci_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,long offset TSRMLS_DC)440 static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,	long offset TSRMLS_DC) /* {{{ */
441 {
442 #if HAVE_OCISTMTFETCH2
443 	ub4 ociori;
444 #endif
445 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
446 
447 #if HAVE_OCISTMTFETCH2
448 	switch (ori) {
449 		case PDO_FETCH_ORI_NEXT:	ociori = OCI_FETCH_NEXT; break;
450 		case PDO_FETCH_ORI_PRIOR:	ociori = OCI_FETCH_PRIOR; break;
451 		case PDO_FETCH_ORI_FIRST:	ociori = OCI_FETCH_FIRST; break;
452 		case PDO_FETCH_ORI_LAST:	ociori = OCI_FETCH_LAST; break;
453 		case PDO_FETCH_ORI_ABS:		ociori = OCI_FETCH_ABSOLUTE; break;
454 		case PDO_FETCH_ORI_REL:		ociori = OCI_FETCH_RELATIVE; break;
455 	}
456 	S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, offset, OCI_DEFAULT);
457 #else
458 	S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
459 #endif
460 
461 	if (S->last_err == OCI_NO_DATA) {
462 		/* no (more) data */
463 		return 0;
464 	}
465 
466 	if (S->last_err == OCI_NEED_DATA) {
467 		oci_stmt_error("OCI_NEED_DATA");
468 		return 0;
469 	}
470 
471 	if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) {
472 		return 1;
473 	}
474 
475 	oci_stmt_error("OCIStmtFetch");
476 
477 	return 0;
478 } /* }}} */
479 
oci_define_callback(dvoid * octxp,OCIDefine * define,ub4 iter,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcodepp)480 static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp,
481 		ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp)
482 {
483 	pdo_oci_column *col = (pdo_oci_column*)octxp;
484 	TSRMLS_FETCH();
485 
486 	switch (col->dtype) {
487 		case SQLT_BLOB:
488 		case SQLT_CLOB:
489 			*piecep = OCI_ONE_PIECE;
490 			*bufpp = col->data;
491 			*alenpp = &col->datalen;
492 			*indpp = (dvoid *)&col->indicator;
493 			break;
494 
495 		default:
496 			php_error_docref(NULL TSRMLS_CC, E_WARNING,
497 				"unhandled datatype in oci_define_callback; this should not happen");
498 			return OCI_ERROR;
499 	}
500 
501 	return OCI_CONTINUE;
502 }
503 
oci_stmt_describe(pdo_stmt_t * stmt,int colno TSRMLS_DC)504 static int oci_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) /* {{{ */
505 {
506 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
507 	OCIParam *param = NULL;
508 	text *colname;
509 	ub2 dtype, data_size, scale, precis;
510 	ub4 namelen;
511 	struct pdo_column_data *col = &stmt->columns[colno];
512 	zend_bool dyn = FALSE;
513 
514 	/* describe the column */
515 	STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)&param, colno+1));
516 
517 	/* what type ? */
518 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
519 			(param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
520 
521 	/* how big ? */
522 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE",
523 			(param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));
524 
525 	/* scale ? */
526 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
527 			(param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
528 
529 	/* precision ? */
530 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
531 			(param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
532 
533 	/* name ? */
534 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME",
535 			(param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));
536 
537 	col->precision = scale;
538 	col->maxlen = data_size;
539 	col->namelen = namelen;
540 	col->name = estrndup((char *)colname, namelen);
541 
542 	S->cols[colno].dtype = dtype;
543 
544 	/* how much room do we need to store the field */
545 	switch (dtype) {
546 		case SQLT_LBI:
547 		case SQLT_LNG:
548 			if (dtype == SQLT_LBI) {
549 				dtype = SQLT_BIN;
550 			} else {
551 				dtype = SQLT_CHR;
552 			}
553 			S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */
554 			S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
555 			col->param_type = PDO_PARAM_STR;
556 			break;
557 
558 		case SQLT_BLOB:
559 		case SQLT_CLOB:
560 			col->param_type = PDO_PARAM_LOB;
561 			STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL));
562 			S->cols[colno].datalen = sizeof(OCILobLocator*);
563 			dyn = TRUE;
564 			break;
565 
566 		case SQLT_BIN:
567 		default:
568 			if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD
569 #ifdef SQLT_TIMESTAMP
570 					|| dtype == SQLT_TIMESTAMP
571 #endif
572 #ifdef SQLT_TIMESTAMP_TZ
573 					|| dtype == SQLT_TIMESTAMP_TZ
574 #endif
575 					) {
576 				/* should be big enough for most date formats and numbers */
577 				S->cols[colno].datalen = 512;
578 #if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)
579 			} else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) {
580 				S->cols[colno].datalen = 1024;
581 #endif
582 			} else {
583 				S->cols[colno].datalen = col->maxlen;
584 			}
585 			if (dtype == SQLT_BIN) {
586 				S->cols[colno].datalen *= 3;
587 			}
588 			S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
589 			dtype = SQLT_CHR;
590 
591 			/* returning data as a string */
592 			col->param_type = PDO_PARAM_STR;
593 	}
594 
595 	STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1,
596 				S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator,
597 				&S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT));
598 
599 	if (dyn) {
600 		STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno],
601 				oci_define_callback));
602 	}
603 
604 	return 1;
605 } /* }}} */
606 
607 struct oci_lob_self {
608 	pdo_stmt_t *stmt;
609 	pdo_oci_stmt *S;
610 	OCILobLocator *lob;
611 	ub4 offset;
612 };
613 
oci_blob_write(php_stream * stream,const char * buf,size_t count TSRMLS_DC)614 static size_t oci_blob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
615 {
616 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
617 	ub4 amt;
618 	sword r;
619 
620 	amt = count;
621 	r = OCILobWrite(self->S->H->svc, self->S->err, self->lob,
622 		&amt, self->offset, (char*)buf, count,
623 		OCI_ONE_PIECE,
624 		NULL, NULL, 0, SQLCS_IMPLICIT);
625 
626 	if (r != OCI_SUCCESS) {
627 		return (size_t)-1;
628 	}
629 
630 	self->offset += amt;
631 	return amt;
632 }
633 
oci_blob_read(php_stream * stream,char * buf,size_t count TSRMLS_DC)634 static size_t oci_blob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
635 {
636 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
637 	ub4 amt;
638 	sword r;
639 
640 	amt = count;
641 	r = OCILobRead(self->S->H->svc, self->S->err, self->lob,
642 		&amt, self->offset, buf, count,
643 		NULL, NULL, 0, SQLCS_IMPLICIT);
644 
645 	if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
646 		return (size_t)-1;
647 	}
648 
649 	self->offset += amt;
650 	if (amt < count) {
651 		stream->eof = 1;
652 	}
653 	return amt;
654 }
655 
oci_blob_close(php_stream * stream,int close_handle TSRMLS_DC)656 static int oci_blob_close(php_stream *stream, int close_handle TSRMLS_DC)
657 {
658 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
659 	pdo_stmt_t *stmt = self->stmt;
660 
661 	if (close_handle) {
662 		OCILobClose(self->S->H->svc, self->S->err, self->lob);
663 		efree(self);
664 	}
665 
666 	php_pdo_stmt_delref(stmt TSRMLS_CC);
667 	return 0;
668 }
669 
oci_blob_flush(php_stream * stream TSRMLS_DC)670 static int oci_blob_flush(php_stream *stream TSRMLS_DC)
671 {
672 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
673 	OCILobFlushBuffer(self->S->H->svc, self->S->err, self->lob, 0);
674 	return 0;
675 }
676 
oci_blob_seek(php_stream * stream,off_t offset,int whence,off_t * newoffset TSRMLS_DC)677 static int oci_blob_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC)
678 {
679 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
680 
681 	if (offset >= PDO_OCI_LOBMAXSIZE) {
682 		return -1;
683 	} else {
684 		self->offset = offset + 1;  /* Oracle LOBS are 1-based, but PHP is 0-based */
685 		return 0;
686 	}
687 }
688 
689 static php_stream_ops oci_blob_stream_ops = {
690 	oci_blob_write,
691 	oci_blob_read,
692 	oci_blob_close,
693 	oci_blob_flush,
694 	"pdo_oci blob stream",
695 	oci_blob_seek,
696 	NULL,
697 	NULL,
698 	NULL
699 };
700 
oci_create_lob_stream(pdo_stmt_t * stmt,OCILobLocator * lob TSRMLS_DC)701 static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob TSRMLS_DC)
702 {
703 	php_stream *stm;
704 	struct oci_lob_self *self = ecalloc(1, sizeof(*self));
705 	self->lob = lob;
706 	self->offset = 1; /* 1-based */
707 	self->stmt = stmt;
708 	self->S = (pdo_oci_stmt*)stmt->driver_data;
709 
710 	stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
711 
712 	if (stm) {
713 		php_pdo_stmt_addref(stmt TSRMLS_CC);
714 		return stm;
715 	}
716 
717 	efree(self);
718 	return NULL;
719 }
720 
oci_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,unsigned long * len,int * caller_frees TSRMLS_DC)721 static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) /* {{{ */
722 {
723 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
724 	pdo_oci_column *C = &S->cols[colno];
725 
726 	/* check the indicator to ensure that the data is intact */
727 	if (C->indicator == -1) {
728 		/* A NULL value */
729 		*ptr = NULL;
730 		*len = 0;
731 		return 1;
732 	} else if (C->indicator == 0) {
733 		/* it was stored perfectly */
734 
735 		if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) {
736 			if (C->data) {
737 				*ptr = (char*)oci_create_lob_stream(stmt, (OCILobLocator*)C->data TSRMLS_CC);
738 				OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY);
739 			}
740 			*len = 0;
741 			return *ptr ? 1 : 0;
742 		}
743 
744 		*ptr = C->data;
745 		*len = C->fetched_len;
746 		return 1;
747 	} else {
748 		/* it was truncated */
749 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "column %d data was too large for buffer and was truncated to fit it", colno);
750 
751 		*ptr = C->data;
752 		*len = C->fetched_len;
753 		return 1;
754 	}
755 } /* }}} */
756 
757 struct pdo_stmt_methods oci_stmt_methods = {
758 	oci_stmt_dtor,
759 	oci_stmt_execute,
760 	oci_stmt_fetch,
761 	oci_stmt_describe,
762 	oci_stmt_get_col,
763 	oci_stmt_param_hook
764 };
765 
766 /*
767  * Local variables:
768  * tab-width: 4
769  * c-basic-offset: 4
770  * End:
771  * vim600: noet sw=4 ts=4 fdm=marker
772  * vim<600: noet sw=4 ts=4
773  */
774