xref: /PHP-7.4/ext/pdo_oci/oci_statement.c (revision d59aac58)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) The PHP Group                                          |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Wez Furlong <wez@php.net>                                    |
16   +----------------------------------------------------------------------+
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "php.h"
24 #include "php_ini.h"
25 #include "ext/standard/info.h"
26 #include "pdo/php_pdo.h"
27 #include "pdo/php_pdo_driver.h"
28 #include "php_pdo_oci.h"
29 #include "php_pdo_oci_int.h"
30 #include "Zend/zend_extensions.h"
31 
32 #define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */
33 
34 #define STMT_CALL(name, params)											\
35 	do {																\
36 		S->last_err = name params;										\
37 		S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \
38 		if (S->last_err) {												\
39 			return 0;													\
40 		}																\
41 	} while(0)
42 
43 #define STMT_CALL_MSG(name, msg, params)								\
44 	do { 																\
45 		S->last_err = name params;										\
46 		S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \
47 		if (S->last_err) {												\
48 			return 0;													\
49 		}																\
50 	} while(0)
51 
52 static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob);
53 
54 #define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob)				\
55 	do															\
56 	{															\
57 		boolean isTempLOB;										\
58 		OCILobIsTemporary(envhp, errhp, lob, &isTempLOB);		\
59 		if (isTempLOB)											\
60 			OCILobFreeTemporary(svchp, errhp, lob);				\
61 	} while(0)
62 
oci_stmt_dtor(pdo_stmt_t * stmt)63 static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
64 {
65 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
66 	HashTable *BC = stmt->bound_columns;
67 	HashTable *BP = stmt->bound_params;
68 
69 	int i;
70 
71 	if (S->stmt) {
72 		/* cancel server side resources for the statement if we didn't
73 		 * fetch it all */
74 		OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
75 
76 		/* free the handle */
77 		OCIHandleFree(S->stmt, OCI_HTYPE_STMT);
78 		S->stmt = NULL;
79 	}
80 	if (S->err) {
81 		OCIHandleFree(S->err, OCI_HTYPE_ERROR);
82 		S->err = NULL;
83 	}
84 
85 	/* need to ensure these go away now */
86 	if (BC) {
87 		zend_hash_destroy(BC);
88 		FREE_HASHTABLE(stmt->bound_columns);
89 		stmt->bound_columns = NULL;
90 	}
91 
92 	if (BP) {
93 		zend_hash_destroy(BP);
94 		FREE_HASHTABLE(stmt->bound_params);
95 		stmt->bound_params = NULL;
96 	}
97 
98 	if (S->einfo.errmsg) {
99 		pefree(S->einfo.errmsg, stmt->dbh->is_persistent);
100 		S->einfo.errmsg = NULL;
101 	}
102 
103 	if (S->cols) {
104 		for (i = 0; i < stmt->column_count; i++) {
105 			if (S->cols[i].data) {
106 				switch (S->cols[i].dtype) {
107 					case SQLT_BLOB:
108 					case SQLT_CLOB:
109 						OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err,
110 							(OCILobLocator *) S->cols[i].data);
111 						OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB);
112 						break;
113 					default:
114 						efree(S->cols[i].data);
115 				}
116 			}
117 		}
118 		efree(S->cols);
119 		S->cols = NULL;
120 	}
121 	efree(S);
122 
123 	stmt->driver_data = NULL;
124 
125 	return 1;
126 } /* }}} */
127 
oci_stmt_execute(pdo_stmt_t * stmt)128 static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
129 {
130 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
131 	ub4 rowcount;
132 	b4 mode;
133 
134 	if (!S->stmt_type) {
135 		STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_STMT_TYPE",
136 				(S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err));
137 	}
138 
139 	if (stmt->executed) {
140 		/* ensure that we cancel the cursor from a previous fetch */
141 		OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
142 	}
143 
144 #ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */
145 	if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) {
146 		mode = OCI_STMT_SCROLLABLE_READONLY;
147 	} else
148 #endif
149 	if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) {
150 		mode = OCI_COMMIT_ON_SUCCESS;
151 	} else {
152 		mode = OCI_DEFAULT;
153 	}
154 
155 	STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err,
156 				(S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL,
157 				mode));
158 
159 	if (!stmt->executed) {
160 		ub4 colcount;
161 		/* do first-time-only definition of bind/mapping stuff */
162 
163 		/* how many columns do we have ? */
164 		STMT_CALL_MSG(OCIAttrGet, "ATTR_PARAM_COUNT",
165 				(S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err));
166 
167 		stmt->column_count = (int)colcount;
168 
169 		if (S->cols) {
170 			int i;
171 			for (i = 0; i < stmt->column_count; i++) {
172 				if (S->cols[i].data) {
173 					switch (S->cols[i].dtype) {
174 						case SQLT_BLOB:
175 						case SQLT_CLOB:
176 							/* do nothing */
177 							break;
178 						default:
179 							efree(S->cols[i].data);
180 					}
181 				}
182 			}
183 			efree(S->cols);
184 		}
185 
186 		S->cols = ecalloc(colcount, sizeof(pdo_oci_column));
187 	}
188 
189 	STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT",
190 			(S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err));
191 	stmt->row_count = (long)rowcount;
192 
193 	return 1;
194 } /* }}} */
195 
oci_bind_input_cb(dvoid * ctx,OCIBind * bindp,ub4 iter,ub4 index,dvoid ** bufpp,ub4 * alenp,ub1 * piecep,dvoid ** indpp)196 static sb4 oci_bind_input_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */
197 {
198 	struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
199 	pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
200 	zval *parameter;
201 
202 	if (!param) {
203 		php_error_docref(NULL, E_WARNING, "param is NULL in oci_bind_input_cb; this should not happen");
204 		return OCI_ERROR;
205 	}
206 
207 	*indpp = &P->indicator;
208 
209     if (Z_ISREF(param->parameter))
210 		parameter = Z_REFVAL(param->parameter);
211 	else
212 		parameter = &param->parameter;
213 
214 	if (P->thing) {
215 		*bufpp = P->thing;
216 		*alenp = sizeof(void*);
217 	} else if (ZVAL_IS_NULL(parameter)) {
218 		/* insert a NULL value into the column */
219 		P->indicator = -1; /* NULL */
220 		*bufpp = 0;
221 		*alenp = -1;
222 	} else if (!P->thing) {
223 		/* regular string bind */
224 		if (!try_convert_to_string(parameter)) {
225 			return OCI_ERROR;
226 		}
227 		*bufpp = Z_STRVAL_P(parameter);
228 		*alenp = (ub4) Z_STRLEN_P(parameter);
229 	}
230 
231 	*piecep = OCI_ONE_PIECE;
232 	return OCI_CONTINUE;
233 } /* }}} */
234 
oci_bind_output_cb(dvoid * ctx,OCIBind * bindp,ub4 iter,ub4 index,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcodepp)235 static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) /* {{{ */
236 {
237 	struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
238 	pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
239 	zval *parameter;
240 
241 	if (!param) {
242 		php_error_docref(NULL, E_WARNING, "param is NULL in oci_bind_output_cb; this should not happen");
243 		return OCI_ERROR;
244 	}
245 
246 	if (Z_ISREF(param->parameter))
247         parameter = Z_REFVAL(param->parameter);
248     else
249         parameter = &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 	zval_ptr_dtor(parameter);
266 
267 	Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1);
268 	P->used_for_output = 1;
269 
270 	P->actual_len = (ub4) Z_STRLEN_P(parameter);
271 	*alenpp = &P->actual_len;
272 	*bufpp = (Z_STR_P(parameter))->val;
273 	*piecep = OCI_ONE_PIECE;
274 	*rcodepp = &P->retcode;
275 	*indpp = &P->indicator;
276 
277 	return OCI_CONTINUE;
278 } /* }}} */
279 
oci_stmt_param_hook(pdo_stmt_t * stmt,struct pdo_bound_param_data * param,enum pdo_param_event event_type)280 static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) /* {{{ */
281 {
282 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
283 
284 	/* we're only interested in parameters for prepared SQL right now */
285 	if (param->is_param) {
286 		pdo_oci_bound_param *P;
287 		sb4 value_sz = -1;
288 		zval *parameter;
289 
290 		if (Z_ISREF(param->parameter))
291 			parameter = Z_REFVAL(param->parameter);
292 		else
293 			parameter = &param->parameter;
294 
295 		P = (pdo_oci_bound_param*)param->driver_data;
296 
297 		switch (event_type) {
298 			case PDO_PARAM_EVT_FETCH_PRE:
299 			case PDO_PARAM_EVT_FETCH_POST:
300 			case PDO_PARAM_EVT_NORMALIZE:
301 				/* Do nothing */
302 				break;
303 
304 			case PDO_PARAM_EVT_FREE:
305 				P = param->driver_data;
306 				if (P && P->thing) {
307 					OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
308 					OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
309 					P->thing = NULL;
310 					efree(P);
311 				}
312 				else if (P) {
313 					efree(P);
314 				}
315 				break;
316 
317 			case PDO_PARAM_EVT_ALLOC:
318 				P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param));
319 				param->driver_data = P;
320 
321 				/* figure out what we're doing */
322 				switch (PDO_PARAM_TYPE(param->param_type)) {
323 					case PDO_PARAM_STMT:
324 						return 0;
325 
326 					case PDO_PARAM_LOB:
327 						/* P->thing is now an OCILobLocator * */
328 						P->oci_type = SQLT_BLOB;
329 						value_sz = (sb4) sizeof(OCILobLocator*);
330 						break;
331 
332 					case PDO_PARAM_STR:
333 					default:
334 						P->oci_type = SQLT_CHR;
335 						value_sz = (sb4) param->max_value_len;
336 						if (param->max_value_len == 0) {
337 							value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */
338 						}
339 
340 				}
341 
342 				if (param->name) {
343 					STMT_CALL(OCIBindByName, (S->stmt,
344 							&P->bind, S->err, (text*)param->name->val,
345 							(sb4) param->name->len, 0, value_sz, P->oci_type,
346 							&P->indicator, 0, &P->retcode, 0, 0,
347 							OCI_DATA_AT_EXEC));
348 				} else {
349 					STMT_CALL(OCIBindByPos, (S->stmt,
350 							&P->bind, S->err, ((ub4)param->paramno)+1,
351 							0, value_sz, P->oci_type,
352 							&P->indicator, 0, &P->retcode, 0, 0,
353 							OCI_DATA_AT_EXEC));
354 				}
355 
356 				STMT_CALL(OCIBindDynamic, (P->bind,
357 							S->err,
358 							param, oci_bind_input_cb,
359 							param, oci_bind_output_cb));
360 
361 				return 1;
362 
363 			case PDO_PARAM_EVT_EXEC_PRE:
364 				P->indicator = 0;
365 				P->used_for_output = 0;
366 				if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
367 					ub4 empty = 0;
368 					STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL));
369 					STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err));
370 					S->have_blobs = 1;
371 				}
372 				return 1;
373 
374 			case PDO_PARAM_EVT_EXEC_POST:
375 				/* fixup stuff set in motion in oci_bind_output_cb */
376 				if (P->used_for_output) {
377 					if (P->indicator == -1) {
378 						/* set up a NULL value */
379 						if (Z_TYPE_P(parameter) == IS_STRING) {
380 							/* OCI likes to stick non-terminated strings in things */
381 							*Z_STRVAL_P(parameter) = '\0';
382 						}
383 						zval_ptr_dtor_str(parameter);
384 						ZVAL_UNDEF(parameter);
385 					} else if (Z_TYPE_P(parameter) == IS_STRING) {
386 						Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1);
387 					}
388 				} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) {
389 					php_stream *stm;
390 
391 					if (Z_TYPE_P(parameter) == IS_NULL) {
392 						/* if the param is NULL, then we assume that they
393 						 * wanted to bind a lob locator into it from the query
394 						 * */
395 
396 						stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator*)P->thing);
397 						if (stm) {
398 							OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
399 							php_stream_to_zval(stm, parameter);
400 						}
401 					} else {
402 						/* we're a LOB being used for insert; transfer the data now */
403 						size_t n;
404 						ub4 amt, offset = 1;
405 						char *consume;
406 
407 						php_stream_from_zval_no_verify(stm, parameter);
408 						if (stm) {
409 							OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
410 							do {
411 								char buf[8192];
412 								n = php_stream_read(stm, buf, sizeof(buf));
413 								if ((int)n <= 0) {
414 									break;
415 								}
416 								consume = buf;
417 								do {
418 									amt = (ub4) n;
419 									OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
420 											&amt, offset, consume, (ub4) n,
421 											OCI_ONE_PIECE,
422 											NULL, NULL, 0, SQLCS_IMPLICIT);
423 									offset += amt;
424 									n -= amt;
425 									consume += amt;
426 								} while (n);
427 							} while (1);
428 							OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
429 							OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0);
430 						} else if (Z_TYPE_P(parameter) == IS_STRING) {
431 							/* stick the string into the LOB */
432 							consume = Z_STRVAL_P(parameter);
433 							n = Z_STRLEN_P(parameter);
434 							if (n) {
435 								OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
436 								while (n) {
437 									amt = (ub4) n;
438 									OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
439 											&amt, offset, consume, (ub4) n,
440 											OCI_ONE_PIECE,
441 											NULL, NULL, 0, SQLCS_IMPLICIT);
442 									consume += amt;
443 									n -= amt;
444 								}
445 								OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
446 							}
447 						}
448 						OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
449 						OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
450 						P->thing = NULL;
451 					}
452 				}
453 
454 				return 1;
455 		}
456 	}
457 
458 	return 1;
459 } /* }}} */
460 
oci_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)461 static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,	zend_long offset) /* {{{ */
462 {
463 #if HAVE_OCISTMTFETCH2
464 	ub4 ociori;
465 #endif
466 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
467 
468 #if HAVE_OCISTMTFETCH2
469 	switch (ori) {
470 		case PDO_FETCH_ORI_NEXT:	ociori = OCI_FETCH_NEXT; break;
471 		case PDO_FETCH_ORI_PRIOR:	ociori = OCI_FETCH_PRIOR; break;
472 		case PDO_FETCH_ORI_FIRST:	ociori = OCI_FETCH_FIRST; break;
473 		case PDO_FETCH_ORI_LAST:	ociori = OCI_FETCH_LAST; break;
474 		case PDO_FETCH_ORI_ABS:		ociori = OCI_FETCH_ABSOLUTE; break;
475 		case PDO_FETCH_ORI_REL:		ociori = OCI_FETCH_RELATIVE; break;
476 	}
477 	S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT);
478 #else
479 	S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
480 #endif
481 
482 	if (S->last_err == OCI_NO_DATA) {
483 		/* no (more) data */
484 		return 0;
485 	}
486 
487 	if (S->last_err == OCI_NEED_DATA) {
488 		oci_stmt_error("OCI_NEED_DATA");
489 		return 0;
490 	}
491 
492 	if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) {
493 		return 1;
494 	}
495 
496 	oci_stmt_error("OCIStmtFetch");
497 
498 	return 0;
499 } /* }}} */
500 
oci_define_callback(dvoid * octxp,OCIDefine * define,ub4 iter,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcodepp)501 static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp,
502 		ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp)
503 {
504 	pdo_oci_column *col = (pdo_oci_column*)octxp;
505 
506 	switch (col->dtype) {
507 		case SQLT_BLOB:
508 		case SQLT_CLOB:
509 			*piecep = OCI_ONE_PIECE;
510 			*bufpp = col->data;
511 			*alenpp = &col->datalen;
512 			*indpp = (dvoid *)&col->indicator;
513 			break;
514 
515 		default:
516 			php_error_docref(NULL, E_WARNING,
517 				"unhandled datatype in oci_define_callback; this should not happen");
518 			return OCI_ERROR;
519 	}
520 
521 	return OCI_CONTINUE;
522 }
523 
oci_stmt_describe(pdo_stmt_t * stmt,int colno)524 static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
525 {
526 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
527 	OCIParam *param = NULL;
528 	text *colname;
529 	ub2 dtype, data_size, precis;
530 	ub4 namelen;
531 	struct pdo_column_data *col = &stmt->columns[colno];
532 	zend_bool dyn = FALSE;
533 
534 	/* describe the column */
535 	STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)&param, colno+1));
536 
537 	/* what type ? */
538 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
539 			(param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
540 
541 	/* how big ? */
542 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE",
543 			(param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));
544 
545 	/* precision ? */
546 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
547 			(param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
548 
549 	/* name ? */
550 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME",
551 			(param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));
552 
553 	col->precision = precis;
554 	col->maxlen = data_size;
555 	col->name = zend_string_init((char *)colname, namelen, 0);
556 
557 	S->cols[colno].dtype = dtype;
558 
559 	/* how much room do we need to store the field */
560 	switch (dtype) {
561 		case SQLT_LBI:
562 		case SQLT_LNG:
563 			if (dtype == SQLT_LBI) {
564 				dtype = SQLT_BIN;
565 			} else {
566 				dtype = SQLT_CHR;
567 			}
568 			S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */
569 			S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
570 			col->param_type = PDO_PARAM_STR;
571 			break;
572 
573 		case SQLT_BLOB:
574 		case SQLT_CLOB:
575 			col->param_type = PDO_PARAM_LOB;
576 			STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL));
577 			S->cols[colno].datalen = sizeof(OCILobLocator*);
578 			dyn = TRUE;
579 			break;
580 
581 		case SQLT_BIN:
582 		default:
583 			if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD
584 #ifdef SQLT_TIMESTAMP
585 					|| dtype == SQLT_TIMESTAMP
586 #endif
587 #ifdef SQLT_TIMESTAMP_TZ
588 					|| dtype == SQLT_TIMESTAMP_TZ
589 #endif
590 					) {
591 				/* should be big enough for most date formats and numbers */
592 				S->cols[colno].datalen = 512;
593 #if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)
594 			} else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) {
595 				S->cols[colno].datalen = 1024;
596 #endif
597 			} else if (dtype == SQLT_BIN) {
598 				S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */
599 			} else {
600 				S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width);
601 			}
602 
603 			S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
604 			dtype = SQLT_CHR;
605 
606 			/* returning data as a string */
607 			col->param_type = PDO_PARAM_STR;
608 	}
609 
610 	STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1,
611 				S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator,
612 				&S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT));
613 
614 	if (dyn) {
615 		STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno],
616 				oci_define_callback));
617 	}
618 
619 	return 1;
620 } /* }}} */
621 
622 struct _oci_lob_env {
623 	OCISvcCtx *svc;
624 	OCIError  *err;
625 };
626 typedef struct _oci_lob_env oci_lob_env;
627 
628 struct oci_lob_self {
629 	zval dbh;
630 	pdo_stmt_t *stmt;
631 	pdo_oci_stmt *S;
632 	OCILobLocator *lob;
633 	oci_lob_env   *E;
634 	ub4 offset;
635 };
636 
oci_blob_write(php_stream * stream,const char * buf,size_t count)637 static ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
638 {
639 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
640 	ub4 amt;
641 	sword r;
642 
643 	amt = (ub4) count;
644 	r = OCILobWrite(self->E->svc, self->E->err, self->lob,
645 		&amt, self->offset, (char*)buf, (ub4) count,
646 		OCI_ONE_PIECE,
647 		NULL, NULL, 0, SQLCS_IMPLICIT);
648 
649 	if (r != OCI_SUCCESS) {
650 		return (ssize_t)-1;
651 	}
652 
653 	self->offset += amt;
654 	return amt;
655 }
656 
oci_blob_read(php_stream * stream,char * buf,size_t count)657 static size_t oci_blob_read(php_stream *stream, char *buf, size_t count)
658 {
659 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
660 	ub4 amt;
661 	sword r;
662 
663 	amt = (ub4) count;
664 	r = OCILobRead(self->E->svc, self->E->err, self->lob,
665 		&amt, self->offset, buf, (ub4) count,
666 		NULL, NULL, 0, SQLCS_IMPLICIT);
667 
668 	if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
669 		return (size_t)-1;
670 	}
671 
672 	self->offset += amt;
673 	if (amt < count) {
674 		stream->eof = 1;
675 	}
676 	return amt;
677 }
678 
oci_blob_close(php_stream * stream,int close_handle)679 static int oci_blob_close(php_stream *stream, int close_handle)
680 {
681 	struct oci_lob_self *self = (struct oci_lob_self *)stream->abstract;
682 	pdo_stmt_t *stmt = self->stmt;
683 
684 	if (close_handle) {
685 		zend_object *obj = &stmt->std;
686 
687 		OCILobClose(self->E->svc, self->E->err, self->lob);
688 		zval_ptr_dtor(&self->dbh);
689 		GC_DELREF(obj);
690 		efree(self->E);
691 		efree(self);
692 	}
693 
694 	/* php_pdo_free_statement(stmt); */
695 	return 0;
696 }
697 
oci_blob_flush(php_stream * stream)698 static int oci_blob_flush(php_stream *stream)
699 {
700 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
701 	OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0);
702 	return 0;
703 }
704 
oci_blob_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffset)705 static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
706 {
707 	struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
708 
709 	if (offset >= PDO_OCI_LOBMAXSIZE) {
710 		return -1;
711 	} else {
712 		self->offset = (ub4) offset + 1;  /* Oracle LOBS are 1-based, but PHP is 0-based */
713 		return 0;
714 	}
715 }
716 
717 static const php_stream_ops oci_blob_stream_ops = {
718 	oci_blob_write,
719 	oci_blob_read,
720 	oci_blob_close,
721 	oci_blob_flush,
722 	"pdo_oci blob stream",
723 	oci_blob_seek,
724 	NULL,
725 	NULL,
726 	NULL
727 };
728 
oci_create_lob_stream(zval * dbh,pdo_stmt_t * stmt,OCILobLocator * lob)729 static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob)
730 {
731 	php_stream *stm;
732 	struct oci_lob_self *self = ecalloc(1, sizeof(*self));
733 
734 	ZVAL_COPY_VALUE(&self->dbh, dbh);
735 	self->lob = lob;
736 	self->offset = 1; /* 1-based */
737 	self->stmt = stmt;
738 	self->S = (pdo_oci_stmt*)stmt->driver_data;
739 	self->E = ecalloc(1, sizeof(oci_lob_env));
740 	self->E->svc = self->S->H->svc;
741 	self->E->err = self->S->err;
742 
743 	stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
744 
745 	if (stm) {
746 		zend_object *obj;
747 		obj = &stmt->std;
748 		Z_ADDREF(self->dbh);
749 		GC_ADDREF(obj);
750 		return stm;
751 	}
752 
753 	efree(self);
754 	return NULL;
755 }
756 
oci_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,size_t * len,int * caller_frees)757 static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) /* {{{ */
758 {
759 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
760 	pdo_oci_column *C = &S->cols[colno];
761 
762 	/* check the indicator to ensure that the data is intact */
763 	if (C->indicator == -1) {
764 		/* A NULL value */
765 		*ptr = NULL;
766 		*len = 0;
767 		return 1;
768 	} else if (C->indicator == 0) {
769 		/* it was stored perfectly */
770 
771 		if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) {
772 			if (C->data) {
773 				*ptr = (char*)oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator*)C->data);
774 				OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY);
775 			}
776 			*len = (size_t) 0;
777 			return *ptr ? 1 : 0;
778 		}
779 
780 		*ptr = C->data;
781 		*len = (size_t) C->fetched_len;
782 		return 1;
783 	} else {
784 		/* it was truncated */
785 		php_error_docref(NULL, E_WARNING, "column %d data was too large for buffer and was truncated to fit it", colno);
786 
787 		*ptr = C->data;
788 		*len = (size_t) C->fetched_len;
789 		return 1;
790 	}
791 } /* }}} */
792 
793 
oci_stmt_col_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)794 static int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */
795 {
796 	pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
797 	OCIParam *param = NULL;
798 	ub2 dtype, precis;
799 	sb1 scale;
800 	zval flags;
801 	ub1 isnull, charset_form;
802 	if (!S->stmt) {
803 		return FAILURE;
804 	}
805 	if (colno >= stmt->column_count) {
806 		/* error invalid column */
807 		return FAILURE;
808 	}
809 
810 	array_init(return_value);
811 	array_init(&flags);
812 
813 	/* describe the column */
814 	STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)&param, colno+1));
815 
816 	/* column data type */
817 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
818 			(param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
819 
820 	/* column precision */
821 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
822 			(param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
823 
824 	/* column scale */
825 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
826 			(param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
827 
828 	/* string column charset form */
829 	if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) {
830 		STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_CHARSET_FORM",
831 			(param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err));
832 	}
833 
834 
835 	if (dtype) {
836 	/* if there is a declared type */
837 		switch (dtype) {
838 #ifdef SQLT_TIMESTAMP
839 		case SQLT_TIMESTAMP:
840 			add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP");
841 			add_assoc_string(return_value, "native_type", "TIMESTAMP");
842 			break;
843 #endif
844 #ifdef SQLT_TIMESTAMP_TZ
845 		case SQLT_TIMESTAMP_TZ:
846 			add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH TIMEZONE");
847 			add_assoc_string(return_value, "native_type", "TIMESTAMP WITH TIMEZONE");
848 			break;
849 #endif
850 #ifdef SQLT_TIMESTAMP_LTZ
851 		case SQLT_TIMESTAMP_LTZ:
852 			add_assoc_string(return_value, "oci:decl_type", "TIMESTAMP WITH LOCAL TIMEZONE");
853 			add_assoc_string(return_value, "native_type", "TIMESTAMP WITH LOCAL TIMEZONE");
854 			break;
855 #endif
856 #ifdef SQLT_INTERVAL_YM
857 		case SQLT_INTERVAL_YM:
858 			add_assoc_string(return_value, "oci:decl_type", "INTERVAL YEAR TO MONTH");
859 			add_assoc_string(return_value, "native_type", "INTERVAL YEAR TO MONTH");
860 			break;
861 #endif
862 #ifdef SQLT_INTERVAL_DS
863 		case SQLT_INTERVAL_DS:
864 			add_assoc_string(return_value, "oci:decl_type", "INTERVAL DAY TO SECOND");
865 			add_assoc_string(return_value, "native_type", "INTERVAL DAY TO SECOND");
866 			break;
867 #endif
868 		case SQLT_DAT:
869 			add_assoc_string(return_value, "oci:decl_type", "DATE");
870 			add_assoc_string(return_value, "native_type", "DATE");
871 			break;
872 		case SQLT_FLT :
873 		case SQLT_NUM:
874 			/* if the precision is nonzero and scale is -127 then it is a FLOAT */
875 			if (scale == -127 && precis != 0) {
876 				add_assoc_string(return_value, "oci:decl_type", "FLOAT");
877 				add_assoc_string(return_value, "native_type", "FLOAT");
878 			} else {
879 				add_assoc_string(return_value, "oci:decl_type", "NUMBER");
880 				add_assoc_string(return_value, "native_type", "NUMBER");
881 			}
882 			break;
883 		case SQLT_LNG:
884 			add_assoc_string(return_value, "oci:decl_type", "LONG");
885 			add_assoc_string(return_value, "native_type", "LONG");
886 			break;
887 		case SQLT_BIN:
888 			add_assoc_string(return_value, "oci:decl_type", "RAW");
889 			add_assoc_string(return_value, "native_type", "RAW");
890 			break;
891 		case SQLT_LBI:
892 			add_assoc_string(return_value, "oci:decl_type", "LONG RAW");
893 			add_assoc_string(return_value, "native_type", "LONG RAW");
894 			break;
895 		case SQLT_CHR:
896 		case SQLT_VCS:
897 			if (charset_form == SQLCS_NCHAR) {
898 				add_assoc_string(return_value, "oci:decl_type", "NVARCHAR2");
899 				add_assoc_string(return_value, "native_type", "NVARCHAR2");
900 			} else {
901 				add_assoc_string(return_value, "oci:decl_type", "VARCHAR2");
902 				add_assoc_string(return_value, "native_type", "VARCHAR2");
903 			}
904 			break;
905 		case SQLT_AFC:
906 			if (charset_form == SQLCS_NCHAR) {
907 				add_assoc_string(return_value, "oci:decl_type", "NCHAR");
908 				add_assoc_string(return_value, "native_type", "NCHAR");
909 			} else {
910 				add_assoc_string(return_value, "oci:decl_type", "CHAR");
911 				add_assoc_string(return_value, "native_type", "CHAR");
912 			}
913 			break;
914 		case SQLT_BLOB:
915 			add_assoc_string(return_value, "oci:decl_type", "BLOB");
916 			add_next_index_string(&flags, "blob");
917 			add_assoc_string(return_value, "native_type", "BLOB");
918 			break;
919 		case SQLT_CLOB:
920 			if (charset_form == SQLCS_NCHAR) {
921 				add_assoc_string(return_value, "oci:decl_type", "NCLOB");
922 				add_assoc_string(return_value, "native_type", "NCLOB");
923 			} else {
924 				add_assoc_string(return_value, "oci:decl_type", "CLOB");
925 				add_assoc_string(return_value, "native_type", "CLOB");
926 			}
927 			add_next_index_string(&flags, "blob");
928 			break;
929 		case SQLT_BFILE:
930 			add_assoc_string(return_value, "oci:decl_type", "BFILE");
931 			add_next_index_string(&flags, "blob");
932 			add_assoc_string(return_value, "native_type", "BFILE");
933 			break;
934 		case SQLT_RDD:
935 			add_assoc_string(return_value, "oci:decl_type", "ROWID");
936 			add_assoc_string(return_value, "native_type", "ROWID");
937 			break;
938 		case SQLT_BFLOAT:
939 		case SQLT_IBFLOAT:
940 			add_assoc_string(return_value, "oci:decl_type", "BINARY_FLOAT");
941 			add_assoc_string(return_value, "native_type", "BINARY_FLOAT");
942 			break;
943 		case SQLT_BDOUBLE:
944 		case SQLT_IBDOUBLE:
945 			add_assoc_string(return_value, "oci:decl_type", "BINARY_DOUBLE");
946 			add_assoc_string(return_value, "native_type", "BINARY_DOUBLE");
947 			break;
948 		default:
949 			add_assoc_long(return_value, "oci:decl_type", dtype);
950 			add_assoc_string(return_value, "native_type", "UNKNOWN");
951 		}
952 	} else {
953 		/* if the column is NULL */
954 		add_assoc_long(return_value, "oci:decl_type", 0);
955 		add_assoc_string(return_value, "native_type", "NULL");
956 	}
957 
958 	/* column can be null */
959 	STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_IS_NULL",
960 			(param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err));
961 
962 	if (isnull) {
963 		add_next_index_string(&flags, "nullable");
964 	} else {
965 		add_next_index_string(&flags, "not_null");
966 	}
967 
968 	/* PDO type */
969 	switch (dtype) {
970 		case SQLT_BFILE:
971 		case SQLT_BLOB:
972 		case SQLT_CLOB:
973 			add_assoc_long(return_value, "pdo_type", PDO_PARAM_LOB);
974 			break;
975 		default:
976 			add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
977 	}
978 
979 	add_assoc_long(return_value, "scale", scale);
980 	add_assoc_zval(return_value, "flags", &flags);
981 
982 	OCIDescriptorFree(param, OCI_DTYPE_PARAM);
983 	return SUCCESS;
984 } /* }}} */
985 
986 const struct pdo_stmt_methods oci_stmt_methods = {
987 	oci_stmt_dtor,
988 	oci_stmt_execute,
989 	oci_stmt_fetch,
990 	oci_stmt_describe,
991 	oci_stmt_get_col,
992 	oci_stmt_param_hook,
993 	NULL, /* set_attr */
994 	NULL, /* get_attr */
995 	oci_stmt_col_meta
996 };
997