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