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