xref: /PHP-8.0/ext/oci8/oci8_statement.c (revision 0d146cb3)
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    | http://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    | Authors: Stig Sæther Bakken <ssb@php.net>                            |
14    |          Thies C. Arntzen <thies@thieso.net>                         |
15    |                                                                      |
16    | Collection support by Andy Sautins <asautins@veripost.net>           |
17    | Temporary LOB support by David Benson <dbenson@mancala.com>          |
18    | ZTS per process OCIPLogon by Harald Radi <harald.radi@nme.at>        |
19    |                                                                      |
20    | Redesigned by: Antony Dovgal <antony@zend.com>                       |
21    |                Andi Gutmans <andi@php.net>                           |
22    |                Wez Furlong <wez@omniti.com>                          |
23    +----------------------------------------------------------------------+
24 */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include "php.h"
31 #include "ext/standard/info.h"
32 #include "php_ini.h"
33 
34 #ifdef HAVE_OCI8
35 
36 #include "php_oci8.h"
37 #include "php_oci8_int.h"
38 
39 #if defined(OCI_MAJOR_VERSION) && (OCI_MAJOR_VERSION > 10) && \
40 	(defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64))
41 typedef ub8 oci_phpsized_int;
42 #else
43 typedef ub4 oci_phpsized_int;
44 #endif
45 
46 /* {{{ php_oci_statement_create()
47  Create statemend handle and allocate necessary resources */
php_oci_statement_create(php_oci_connection * connection,char * query,int query_len)48 php_oci_statement *php_oci_statement_create(php_oci_connection *connection, char *query, int query_len)
49 {
50 	php_oci_statement *statement;
51 	sword errstatus;
52 
53 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
54 
55 	statement = ecalloc(1,sizeof(php_oci_statement));
56 
57 	if (!query_len) {
58 		/* do not allocate stmt handle for refcursors, we'll get it from OCIStmtPrepare2() */
59 		PHP_OCI_CALL(OCIHandleAlloc, (connection->env, (dvoid **)&(statement->stmt), OCI_HTYPE_STMT, 0, NULL));
60 	}
61 
62 	PHP_OCI_CALL(OCIHandleAlloc, (connection->env, (dvoid **)&(statement->err), OCI_HTYPE_ERROR, 0, NULL));
63 
64 	if (query_len > 0) {
65 		PHP_OCI_CALL_RETURN(errstatus, OCIStmtPrepare2,
66 				(
67 				 connection->svc,
68 				 &(statement->stmt),
69 				 connection->err,
70 				 (text *)query,
71 				 query_len,
72 				 NULL,
73 				 0,
74 				 OCI_NTV_SYNTAX,
75 				 OCI_DEFAULT
76 				)
77 		);
78 #ifdef HAVE_OCI8_DTRACE
79 		if (DTRACE_OCI8_SQLTEXT_ENABLED()) {
80 			DTRACE_OCI8_SQLTEXT(connection, connection->client_id, statement, query);
81 		}
82 #endif /* HAVE_OCI8_DTRACE */
83 
84 		if (errstatus != OCI_SUCCESS) {
85 			connection->errcode = php_oci_error(connection->err, errstatus);
86 
87 			PHP_OCI_CALL(OCIStmtRelease, (statement->stmt, statement->err, NULL, 0, OCI_STRLS_CACHE_DELETE));
88 			PHP_OCI_CALL(OCIHandleFree,(statement->err, OCI_HTYPE_ERROR));
89 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
90 			efree(statement);
91 			return NULL;
92 		}
93 	}
94 
95 	if (query && query_len) {
96 		statement->last_query = ecalloc(1, query_len + 1);
97 		memcpy(statement->last_query, query, query_len);
98 		statement->last_query_len = query_len;
99 	}
100 	else {
101 		statement->last_query = NULL;
102 		statement->last_query_len = 0;
103 	}
104 
105 	statement->connection = connection;
106 	statement->has_data = 0;
107 	statement->has_descr = 0;
108 	statement->parent_stmtid = 0;
109 	statement->impres_child_stmt = NULL;
110 	statement->impres_count = 0;
111 	statement->impres_flag = PHP_OCI_IMPRES_UNKNOWN;  /* may or may not have Implicit Result Set children */
112 	GC_ADDREF(statement->connection->id);
113 
114 	if (OCI_G(default_prefetch) >= 0) {
115 		php_oci_statement_set_prefetch(statement, (ub4)OCI_G(default_prefetch));
116 	} else {
117 		php_oci_statement_set_prefetch(statement, (ub4)100); /* semi-arbitrary, "sensible default" */
118 	}
119 
120 	PHP_OCI_REGISTER_RESOURCE(statement, le_statement);
121 
122 	OCI_G(num_statements)++;
123 
124 	return statement;
125 }
126 /* }}} */
127 
128 /* {{{ php_oci_get_implicit_resultset()
129    Fetch implicit result set statement resource */
php_oci_get_implicit_resultset(php_oci_statement * statement)130 php_oci_statement *php_oci_get_implicit_resultset(php_oci_statement *statement)
131 {
132 #if (OCI_MAJOR_VERSION < 12)
133 	php_error_docref(NULL, E_WARNING, "Implicit results are available in Oracle Database 12c onwards");
134 	return NULL;
135 #else
136 	void *result;
137 	ub4   rtype;
138 	php_oci_statement *statement2;  /* implicit result set statement handle */
139 	sword errstatus;
140 
141 	PHP_OCI_CALL_RETURN(errstatus, OCIStmtGetNextResult, (statement->stmt, statement->err, &result, &rtype, OCI_DEFAULT));
142 	if (errstatus == OCI_NO_DATA) {
143 		return NULL;
144 	}
145 
146 	if (rtype != OCI_RESULT_TYPE_SELECT) {
147 		/* Only OCI_RESULT_TYPE_SELECT is supported by Oracle DB 12cR1 */
148 		php_error_docref(NULL, E_WARNING, "Unexpected implicit result type returned from Oracle Database");
149 		return NULL;
150 	} else {
151 		statement2 = ecalloc(1,sizeof(php_oci_statement));
152 
153 		PHP_OCI_CALL(OCIHandleAlloc, (statement->connection->env, (dvoid **)&(statement2->err), OCI_HTYPE_ERROR, 0, NULL));
154 		statement2->stmt = (OCIStmt *)result;
155 		statement2->parent_stmtid = statement->id;
156 		statement2->impres_child_stmt = NULL;
157 		statement2->impres_count = 0;
158 		statement2->impres_flag = PHP_OCI_IMPRES_IS_CHILD;
159 		statement2->connection = statement->connection;
160 		statement2->errcode = 0;
161 		statement2->last_query = NULL;
162 		statement2->last_query_len = 0;
163 		statement2->columns = NULL;
164 		statement2->binds = NULL;
165 		statement2->defines = NULL;
166 		statement2->ncolumns = 0;
167 		statement2->executed = 0;
168 		statement2->has_data = 0;
169 		statement2->has_descr = 0;
170 		statement2->stmttype = 0;
171 
172 		GC_ADDREF(statement->id);
173 		GC_ADDREF(statement2->connection->id);
174 
175 		php_oci_statement_set_prefetch(statement2, statement->prefetch_count);
176 
177 		PHP_OCI_REGISTER_RESOURCE(statement2, le_statement);
178 
179 		OCI_G(num_statements)++;
180 
181 		return statement2;
182 	}
183 #endif /* OCI_MAJOR_VERSION < 12 */
184 }
185 /* }}} */
186 
187 /* {{{ php_oci_statement_set_prefetch()
188  Set prefetch buffer size for the statement */
php_oci_statement_set_prefetch(php_oci_statement * statement,ub4 prefetch)189 int php_oci_statement_set_prefetch(php_oci_statement *statement, ub4 prefetch )
190 {
191 	sword errstatus;
192 
193 	if (prefetch > 20000) {
194 		prefetch = 20000;		/* keep it somewhat sane */
195 	}
196 
197 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (statement->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, statement->err));
198 
199 	if (errstatus != OCI_SUCCESS) {
200 		statement->errcode = php_oci_error(statement->err, errstatus);
201 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
202 		statement->prefetch_count = 0;
203 		return 1;
204 	}
205 	statement->prefetch_count = prefetch;
206 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
207 	return 0;
208 }
209 /* }}} */
210 
211 /* {{{ php_oci_cleanup_pre_fetch()
212    Helper function to cleanup ref-cursors and descriptors from the previous row */
php_oci_cleanup_pre_fetch(zval * data)213 int php_oci_cleanup_pre_fetch(zval *data)
214 {
215 	php_oci_out_column *outcol = (php_oci_out_column*) Z_PTR_P(data);
216 
217 	if (!outcol->is_descr && !outcol->is_cursor)
218 		return ZEND_HASH_APPLY_KEEP;
219 
220 	switch(outcol->data_type) {
221 		case SQLT_CLOB:
222 		case SQLT_BLOB:
223 		case SQLT_RDD:
224 		case SQLT_BFILE:
225 			if (outcol->descid) {
226 				zend_list_delete(outcol->descid);
227 				outcol->descid = 0;
228 			}
229 			break;
230 		case SQLT_RSET:
231 			if (outcol->stmtid) {
232 				zend_list_delete(outcol->stmtid);
233 				outcol->stmtid = 0;
234 				outcol->nested_statement = NULL;
235 			}
236 			break;
237 		default:
238 			break;
239 	}
240 	return ZEND_HASH_APPLY_KEEP;
241 
242 }
243 /* }}} */
244 
245 /* {{{ php_oci_statement_fetch()
246  Fetch a row from the statement */
php_oci_statement_fetch(php_oci_statement * statement,ub4 nrows)247 int php_oci_statement_fetch(php_oci_statement *statement, ub4 nrows)
248 {
249 	int i;
250 	void *handlepp;
251 	ub4 typep, iterp, idxp;
252 	ub1 in_outp, piecep;
253 	zend_bool piecewisecols = 0;
254 	php_oci_out_column *column;
255 	sword errstatus;
256 
257 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
258 
259 	if (statement->has_descr && statement->columns) {
260 		zend_hash_apply(statement->columns, php_oci_cleanup_pre_fetch);
261     }
262 
263 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
264 	PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch2, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, 0, OCI_DEFAULT));
265 #else
266 	PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, OCI_DEFAULT));
267 #endif
268 
269 	if (errstatus == OCI_NO_DATA || nrows == 0) {
270 		if (statement->last_query == NULL) {
271 			/* reset define-list for refcursors */
272 			if (statement->columns) {
273 				zend_hash_destroy(statement->columns);
274 				efree(statement->columns);
275 				statement->columns = NULL;
276 				statement->ncolumns = 0;
277 			}
278 			statement->executed = 0;
279 		}
280 
281 		statement->has_data = 0;
282 
283 		if (nrows == 0) {
284 			/* this is exactly what we requested */
285 			return 0;
286 		}
287 		return 1;
288 	}
289 
290 	/* reset length for all piecewise columns */
291 	for (i = 0; i < statement->ncolumns; i++) {
292 		column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
293 		if (column && column->piecewise) {
294 			column->retlen4 = 0;
295 			piecewisecols = 1;
296 		}
297 	}
298 
299 	while (errstatus == OCI_NEED_DATA) {
300 		if (piecewisecols) {
301 			PHP_OCI_CALL_RETURN(errstatus,
302 				OCIStmtGetPieceInfo,
303 				   (
304 					statement->stmt,
305 					statement->err,
306 					&handlepp,
307 					&typep,
308 					&in_outp,
309 					&iterp,
310 					&idxp,
311 					&piecep
312 				   )
313 			);
314 
315 			/* scan through our columns for a piecewise column with a matching handle */
316 			for (i = 0; i < statement->ncolumns; i++) {
317 				column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
318 				if (column && column->piecewise && handlepp == column->oci_define)   {
319 					if (!column->data) {
320 						column->data = (text *) ecalloc(1, PHP_OCI_PIECE_SIZE + 1);
321 					} else {
322 						column->data = erealloc(column->data, column->retlen4 + PHP_OCI_PIECE_SIZE + 1);
323 					}
324 					column->cb_retlen = PHP_OCI_PIECE_SIZE;
325 
326 					/* and instruct fetch to fetch waiting piece into our buffer */
327 					PHP_OCI_CALL(OCIStmtSetPieceInfo,
328 						   (
329 							(void *) column->oci_define,
330 							OCI_HTYPE_DEFINE,
331 							statement->err,
332 							((char*)column->data) + column->retlen4,
333 							&(column->cb_retlen),
334 							piecep,
335 							&column->indicator,
336 							&column->retcode
337 						   )
338 					);
339 				}
340 			}
341 		}
342 
343 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
344 		PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch2, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, 0, OCI_DEFAULT));
345 #else
346 		PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, OCI_DEFAULT));
347 #endif
348 
349 		if (piecewisecols) {
350 			for (i = 0; i < statement->ncolumns; i++) {
351 				column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
352 				if (column && column->piecewise && handlepp == column->oci_define)	{
353 					column->retlen4 += column->cb_retlen;
354 				}
355 			}
356 		}
357 	}
358 
359 	if (errstatus == OCI_SUCCESS_WITH_INFO || errstatus == OCI_SUCCESS) {
360 		statement->has_data = 1;
361 
362 		/* do the stuff needed for OCIDefineByName */
363 		for (i = 0; i < statement->ncolumns; i++) {
364 			column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
365 			if (column == NULL) {
366 				continue;
367 			}
368 
369 			if (!column->define) {
370 				continue;
371 			}
372 
373 			ZEND_ASSERT(Z_ISREF(column->define->val));
374 			zval_ptr_dtor(Z_REFVAL(column->define->val));
375 			ZVAL_NULL(Z_REFVAL(column->define->val));
376 			php_oci_column_to_zval(column, Z_REFVAL(column->define->val), 0);
377 		}
378 
379 		return 0;
380 	}
381 
382 	statement->errcode = php_oci_error(statement->err, errstatus);
383 	PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
384 
385 	statement->has_data = 0;
386 
387 	return 1;
388 }
389 /* }}} */
390 
391 /* {{{ php_oci_statement_get_column()
392  Get column from the result set */
php_oci_statement_get_column(php_oci_statement * statement,zend_long column_index,char * column_name,int column_name_len)393 php_oci_out_column *php_oci_statement_get_column(php_oci_statement *statement, zend_long column_index, char *column_name, int column_name_len)
394 {
395 	php_oci_out_column *column = NULL;
396 	int i;
397 
398 	if (statement->columns == NULL) { /* we release the columns at the end of a fetch */
399 		return NULL;
400 	}
401 
402 	if (column_name) {
403 		for (i = 0; i < statement->ncolumns; i++) {
404 			column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
405 			if (column == NULL) {
406 				continue;
407 			} else if (((int) column->name_len == column_name_len) && (!strncmp(column->name, column_name, column_name_len))) {
408 				return column;
409 			}
410 		}
411 	} else if (column_index != -1) {
412 		if ((column = zend_hash_index_find_ptr(statement->columns, column_index)) == NULL) {
413 			return NULL;
414 		}
415 		return column;
416 	}
417 
418 	return NULL;
419 }
420 /* }}} */
421 
422 /* {{{ php_oci_define_callback() */
php_oci_define_callback(dvoid * ctx,OCIDefine * define,ub4 iter,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcpp)423 sb4 php_oci_define_callback(dvoid *ctx, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcpp)
424 {
425 	php_oci_out_column *outcol = (php_oci_out_column *)ctx;
426 
427 	if (!outcol) {
428 
429 		php_error_docref(NULL, E_WARNING, "Invalid context pointer value");
430 		return OCI_ERROR;
431 	}
432 
433 	switch(outcol->data_type) {
434 		case SQLT_RSET: {
435 				php_oci_statement *nested_stmt;
436 
437 				nested_stmt = php_oci_statement_create(outcol->statement->connection, NULL, 0);
438 				if (!nested_stmt) {
439 					return OCI_ERROR;
440 				}
441 				nested_stmt->parent_stmtid = outcol->statement->id;
442 				GC_ADDREF(outcol->statement->id);
443 				outcol->nested_statement = nested_stmt;
444 				outcol->stmtid = nested_stmt->id;
445 
446 				*bufpp = nested_stmt->stmt;
447 				*alenpp = &(outcol->retlen4);
448 				*piecep = OCI_ONE_PIECE;
449 				*indpp = &(outcol->indicator);
450 				*rcpp = &(outcol->retcode);
451 				return OCI_CONTINUE;
452 			}
453 			break;
454 		case SQLT_RDD:
455 		case SQLT_BLOB:
456 		case SQLT_CLOB:
457 		case SQLT_BFILE: {
458 				php_oci_descriptor *descr;
459 				int dtype;
460 
461 				if (outcol->data_type == SQLT_BFILE) {
462 					dtype = OCI_DTYPE_FILE;
463 				} else if (outcol->data_type == SQLT_RDD ) {
464 					dtype = OCI_DTYPE_ROWID;
465 				} else {
466 					dtype = OCI_DTYPE_LOB;
467 				}
468 
469 				descr = php_oci_lob_create(outcol->statement->connection, dtype);
470 				if (!descr) {
471 					return OCI_ERROR;
472 				}
473 				outcol->descid = descr->id;
474 				descr->charset_form = outcol->charset_form;
475 
476 				*bufpp = descr->descriptor;
477 				*alenpp = &(outcol->retlen4);
478 				*piecep = OCI_ONE_PIECE;
479 				*indpp = &(outcol->indicator);
480 				*rcpp = &(outcol->retcode);
481 
482 				return OCI_CONTINUE;
483 			}
484 			break;
485 	}
486 	return OCI_ERROR;
487 }
488 /* }}} */
489 
490 /* {{{ php_oci_statement_execute()
491  Execute statement */
php_oci_statement_execute(php_oci_statement * statement,ub4 mode)492 int php_oci_statement_execute(php_oci_statement *statement, ub4 mode)
493 {
494 	php_oci_out_column *outcol;
495 	OCIParam *param = NULL;
496 	text *colname;
497 	ub4 counter;
498 	ub2 define_type;
499 	ub4 iters;
500 	ub4 colcount;
501 	ub2 dynamic;
502 	dvoid *buf;
503 	sword errstatus;
504 
505 	switch (mode) {
506 		case OCI_COMMIT_ON_SUCCESS:
507 		case OCI_DESCRIBE_ONLY:
508 		case OCI_DEFAULT:
509 			/* only these are allowed */
510 #ifdef HAVE_OCI8_DTRACE
511 			if (DTRACE_OCI8_EXECUTE_MODE_ENABLED()) {
512 				DTRACE_OCI8_EXECUTE_MODE(statement->connection, statement->connection->client_id, statement, mode);
513 			}
514 #endif /* HAVE_OCI8_DTRACE */
515 			break;
516 		default:
517 			php_error_docref(NULL, E_WARNING, "Invalid execute mode given: %d", mode);
518 			return 1;
519 			break;
520 	}
521 
522 	if (!statement->stmttype) {
523 		/* get statement type */
524 		PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub2 *)&statement->stmttype, (ub4 *)0, OCI_ATTR_STMT_TYPE, statement->err));
525 
526 		if (errstatus != OCI_SUCCESS) {
527 			statement->errcode = php_oci_error(statement->err, errstatus);
528 			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
529 			return 1;
530 		} else {
531 			statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
532 		}
533 	}
534 
535 	if (statement->stmttype == OCI_STMT_SELECT) {
536 		iters = 0;
537 	} else {
538 		iters = 1;
539 	}
540 
541 	if (statement->last_query) { /* Don't execute REFCURSORS or Implicit Result Set handles */
542 
543 		if (statement->binds) {
544 			int result = 0;
545 			zend_hash_apply_with_argument(statement->binds, php_oci_bind_pre_exec, (void *)&result);
546 			if (result) {
547 				return 1;
548 			}
549 		}
550 
551 		/* execute statement */
552 		PHP_OCI_CALL_RETURN(errstatus, OCIStmtExecute, (statement->connection->svc, statement->stmt, statement->err, iters, 0, NULL, NULL, mode));
553 
554 		if (errstatus != OCI_SUCCESS) {
555 			statement->errcode = php_oci_error(statement->err, errstatus);
556 			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
557 			return 1;
558 		}
559 
560 		if (statement->binds) {
561 			zend_hash_apply(statement->binds, php_oci_bind_post_exec);
562 		}
563 
564 		if (mode & OCI_COMMIT_ON_SUCCESS) {
565 			/* No need to rollback on disconnect */
566 			statement->connection->rb_on_disconnect = 0;
567 		} else if (statement->stmttype != OCI_STMT_SELECT) {
568 			/* Assume some uncommitted DML occurred */
569 			statement->connection->rb_on_disconnect = 1;
570 		}
571 		/* else for SELECT with OCI_NO_AUTO_COMMIT, leave
572 		 * "rb_on_disconnect" at its previous value.  SELECT can't
573 		 * initiate uncommitted DML. (An AUTONOMOUS_TRANSACTION in
574 		 * invoked PL/SQL must explicitly rollback/commit else the
575 		 * SELECT fails).
576 		 */
577 
578 		statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
579 	}
580 
581 	if (statement->stmttype == OCI_STMT_SELECT && statement->executed == 0) {
582 		/* we only need to do the define step is this very statement is executed the first time! */
583 		statement->executed = 1;
584 
585 		ALLOC_HASHTABLE(statement->columns);
586 		zend_hash_init(statement->columns, 13, NULL, php_oci_column_hash_dtor, 0);
587 
588 		counter = 1;
589 
590 		/* get number of columns */
591 		PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (dvoid *)&colcount, (ub4 *)0, OCI_ATTR_PARAM_COUNT, statement->err));
592 
593 		if (errstatus != OCI_SUCCESS) {
594 			statement->errcode = php_oci_error(statement->err, errstatus);
595 			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
596 			return 1;
597 		}
598 
599 		statement->ncolumns = colcount;
600 
601 		for (counter = 1; counter <= colcount; counter++) {
602 			outcol = (php_oci_out_column *) ecalloc(1, sizeof(php_oci_out_column));
603 
604 			outcol = zend_hash_index_update_ptr(statement->columns, counter, outcol);
605 
606 			/* get column */
607 			PHP_OCI_CALL_RETURN(errstatus, OCIParamGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, statement->err, (dvoid**)&param, counter));
608 
609 			if (errstatus != OCI_SUCCESS) {
610 				statement->errcode = php_oci_error(statement->err, errstatus);
611 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
612 				return 1;
613 			}
614 
615 			/* get column datatype */
616 			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->data_type, (ub4 *)0, OCI_ATTR_DATA_TYPE, statement->err));
617 
618 			if (errstatus != OCI_SUCCESS) {
619 				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
620 				statement->errcode = php_oci_error(statement->err, errstatus);
621 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
622 				return 1;
623 			}
624 
625 			/* get character set form  */
626 			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->charset_form, (ub4 *)0, OCI_ATTR_CHARSET_FORM, statement->err));
627 
628 			if (errstatus != OCI_SUCCESS) {
629 				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
630 				statement->errcode = php_oci_error(statement->err, errstatus);
631 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
632 				return 1;
633 			}
634 
635 			/* get character set id	 */
636 			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->charset_id, (ub4 *)0, OCI_ATTR_CHARSET_ID, statement->err));
637 
638 			if (errstatus != OCI_SUCCESS) {
639 				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
640 				statement->errcode = php_oci_error(statement->err, errstatus);
641 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
642 				return 1;
643 			}
644 
645 			/* get size of the column */
646 			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->data_size, (dvoid *)0, OCI_ATTR_DATA_SIZE, statement->err));
647 
648 			if (errstatus != OCI_SUCCESS) {
649 				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
650 				statement->errcode = php_oci_error(statement->err, errstatus);
651 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
652 				return 1;
653 			}
654 
655 			outcol->storage_size4 = outcol->data_size;
656 			outcol->retlen = outcol->data_size;
657 
658 			/* get scale of the column */
659 			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->scale, (dvoid *)0, OCI_ATTR_SCALE, statement->err));
660 
661 			if (errstatus != OCI_SUCCESS) {
662 				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
663 				statement->errcode = php_oci_error(statement->err, errstatus);
664 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
665 				return 1;
666 			}
667 
668 			/* get precision of the column */
669 			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->precision, (dvoid *)0, OCI_ATTR_PRECISION, statement->err));
670 
671 			if (errstatus != OCI_SUCCESS) {
672 				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
673 				statement->errcode = php_oci_error(statement->err, errstatus);
674 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
675 				return 1;
676 			}
677 
678 			/* get name of the column */
679 			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid **)&colname, (ub4 *)&outcol->name_len, (ub4)OCI_ATTR_NAME, statement->err));
680 
681 			if (errstatus != OCI_SUCCESS) {
682 				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
683 				statement->errcode = php_oci_error(statement->err, errstatus);
684 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
685 				return 1;
686 			}
687 			PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
688 
689 			outcol->name = ecalloc(1, outcol->name_len + 1);
690 			memcpy(outcol->name, colname, outcol->name_len);
691 
692 			/* find a user-set define */
693 			if (statement->defines) {
694 				if ((outcol->define = zend_hash_str_find_ptr(statement->defines, outcol->name, outcol->name_len)) != NULL) {
695 					if (outcol->define->type) {
696 						outcol->data_type = outcol->define->type;
697 					}
698 				}
699 			}
700 
701 			buf = 0;
702 			switch (outcol->data_type) {
703 				case SQLT_RSET:
704 					outcol->statement = statement; /* parent handle */
705 
706 					define_type = SQLT_RSET;
707 					outcol->is_cursor = 1;
708 					outcol->statement->has_descr = 1;
709 					outcol->storage_size4 = -1;
710 					outcol->retlen = -1;
711 					dynamic = OCI_DYNAMIC_FETCH;
712 					break;
713 
714 				case SQLT_RDD:	 /* ROWID */
715 				case SQLT_BLOB:	 /* binary LOB */
716 				case SQLT_CLOB:	 /* character LOB */
717 				case SQLT_BFILE: /* binary file LOB */
718 					outcol->statement = statement; /* parent handle */
719 
720 					define_type = outcol->data_type;
721 					outcol->is_descr = 1;
722 					outcol->statement->has_descr = 1;
723 					outcol->storage_size4 = -1;
724 					outcol->chunk_size = 0;
725 					dynamic = OCI_DYNAMIC_FETCH;
726 					break;
727 
728 				case SQLT_LNG:
729 				case SQLT_LBI:
730 					if (outcol->data_type == SQLT_LBI) {
731 						define_type = SQLT_BIN;
732 					} else {
733 						define_type = SQLT_CHR;
734 					}
735 					outcol->storage_size4 = PHP_OCI_MAX_DATA_SIZE;
736 					outcol->piecewise = 1;
737 					dynamic = OCI_DYNAMIC_FETCH;
738 					break;
739 
740 				case SQLT_BIN:
741 				default:
742 					define_type = SQLT_CHR;
743 					if (outcol->data_type == SQLT_BIN) {
744 						define_type = SQLT_BIN;
745 					}
746 					if ((outcol->data_type == SQLT_DAT) || (outcol->data_type == SQLT_NUM)
747 #ifdef SQLT_TIMESTAMP
748 						|| (outcol->data_type == SQLT_TIMESTAMP)
749 #endif
750 #ifdef SQLT_TIMESTAMP_TZ
751 						|| (outcol->data_type == SQLT_TIMESTAMP_TZ)
752 #endif
753 #ifdef SQLT_TIMESTAMP_LTZ
754 						|| (outcol->data_type == SQLT_TIMESTAMP_LTZ)
755 #endif
756 #ifdef SQLT_INTERVAL_YM
757 						|| (outcol->data_type == SQLT_INTERVAL_YM)
758 #endif
759 #ifdef SQLT_INTERVAL_DS
760 						|| (outcol->data_type == SQLT_INTERVAL_DS)
761 #endif
762 						) {
763 						outcol->storage_size4 = 512; /* XXX this should fit "most" NLS date-formats and Numbers */
764 #if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)
765 					} else if (outcol->data_type == SQLT_IBFLOAT || outcol->data_type == SQLT_IBDOUBLE) {
766 						outcol->storage_size4 = 1024;
767 #endif
768 					} else {
769 						outcol->storage_size4++; /* add one for string terminator */
770 					}
771 
772 					outcol->storage_size4 *= 3;
773 
774 					dynamic = OCI_DEFAULT;
775 					buf = outcol->data = (text *) safe_emalloc(1, outcol->storage_size4, 0);
776 					memset(buf, 0, outcol->storage_size4);
777 					break;
778 			}
779 
780 			if (dynamic == OCI_DYNAMIC_FETCH) {
781 				PHP_OCI_CALL_RETURN(errstatus,
782 					OCIDefineByPos,
783 					(
784 						statement->stmt,							/* IN/OUT handle to the requested SQL query */
785 						(OCIDefine **)&outcol->oci_define,			/* IN/OUT pointer to a pointer to a define handle */
786 						statement->err,								/* IN/OUT An error handle  */
787 						counter,									/* IN	  position in the select list */
788 						(dvoid *)NULL,								/* IN/OUT pointer to a buffer */
789 						outcol->storage_size4,						/* IN	  The size of each valuep buffer in bytes */
790 						define_type,								/* IN	  The data type */
791 						(dvoid *)&outcol->indicator,				/* IN	  pointer to an indicator variable or arr */
792 						(ub2 *)NULL,								/* IN/OUT Pointer to array of length of data fetched */
793 						(ub2 *)NULL,								/* OUT	  Pointer to array of column-level return codes */
794 						OCI_DYNAMIC_FETCH							/* IN	  mode (OCI_DEFAULT, OCI_DYNAMIC_FETCH) */
795 					)
796 				);
797 
798 			} else {
799 				PHP_OCI_CALL_RETURN(errstatus,
800 					OCIDefineByPos,
801 					(
802 						statement->stmt,							/* IN/OUT handle to the requested SQL query */
803 						(OCIDefine **)&outcol->oci_define,			/* IN/OUT pointer to a pointer to a define handle */
804 						statement->err,								/* IN/OUT An error handle  */
805 						counter,									/* IN	  position in the select list */
806 						(dvoid *)buf,								/* IN/OUT pointer to a buffer */
807 						outcol->storage_size4,						/* IN	  The size of each valuep buffer in bytes */
808 						define_type,								/* IN	  The data type */
809 						(dvoid *)&outcol->indicator,				/* IN	  pointer to an indicator variable or arr */
810 						(ub2 *)&outcol->retlen,						/* IN/OUT Pointer to array of length of data fetched */
811 						(ub2 *)&outcol->retcode,					/* OUT	  Pointer to array of column-level return codes */
812 						OCI_DEFAULT									/* IN	  mode (OCI_DEFAULT, OCI_DYNAMIC_FETCH) */
813 					)
814 				);
815 
816 			}
817 
818 			if (errstatus != OCI_SUCCESS) {
819 				statement->errcode = php_oci_error(statement->err, errstatus);
820 				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
821 				return 1;
822 			}
823 
824 			/* additional OCIDefineDynamic() call */
825 			switch (outcol->data_type) {
826 				case SQLT_RSET:
827 				case SQLT_RDD:
828 				case SQLT_BLOB:
829 				case SQLT_CLOB:
830 				case SQLT_BFILE:
831 					PHP_OCI_CALL_RETURN(errstatus,
832 						OCIDefineDynamic,
833 						(
834 							outcol->oci_define,
835 							statement->err,
836 							(dvoid *)outcol,
837 							php_oci_define_callback
838 						)
839 					);
840 
841 					if (errstatus != OCI_SUCCESS) {
842 						statement->errcode = php_oci_error(statement->err, errstatus);
843 						PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
844 						return 1;
845 					}
846 					break;
847 			}
848 		}
849 		statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
850 	}
851 
852 	return 0;
853 }
854 /* }}} */
855 
856 /* {{{ php_oci_statement_cancel()
857  Cancel statement */
php_oci_statement_cancel(php_oci_statement * statement)858 int php_oci_statement_cancel(php_oci_statement *statement)
859 {
860 	return php_oci_statement_fetch(statement, 0);
861 }
862 /* }}} */
863 
864 /* {{{ php_oci_statement_free()
865  Destroy statement handle and free associated resources */
php_oci_statement_free(php_oci_statement * statement)866 void php_oci_statement_free(php_oci_statement *statement)
867 {
868 	if (statement->stmt) {
869 		if (statement->last_query_len) { /* FIXME: magical */
870 			PHP_OCI_CALL(OCIStmtRelease, (statement->stmt, statement->err, NULL, 0, statement->errcode ? OCI_STRLS_CACHE_DELETE : OCI_DEFAULT));
871 		} else if (statement->impres_flag != PHP_OCI_IMPRES_IS_CHILD) {  /* Oracle doc says don't free Implicit Result Set handles */
872 			PHP_OCI_CALL(OCIHandleFree, (statement->stmt, OCI_HTYPE_STMT));
873 		}
874 		statement->stmt = NULL;
875 	}
876 
877 	if (statement->err) {
878 		PHP_OCI_CALL(OCIHandleFree, (statement->err, OCI_HTYPE_ERROR));
879 		statement->err = NULL;
880 	}
881 
882 	if (statement->last_query) {
883 		efree(statement->last_query);
884 	}
885 
886 	if (statement->binds) {
887 		zend_hash_destroy(statement->binds);
888 		efree(statement->binds);
889 	}
890 
891 	if (statement->defines) {
892 		zend_hash_destroy(statement->defines);
893 		efree(statement->defines);
894 	}
895 
896 	if (statement->columns) {
897 		zend_hash_destroy(statement->columns);
898 		efree(statement->columns);
899 	}
900 
901 	if (statement->parent_stmtid) {
902 		zend_list_delete(statement->parent_stmtid);
903 	}
904 
905 	zend_list_delete(statement->connection->id);
906 	efree(statement);
907 
908 	OCI_G(num_statements)--;
909 }
910 /* }}} */
911 
912 /* {{{ php_oci_bind_pre_exec()
913  Helper function */
php_oci_bind_pre_exec(zval * data,void * result)914 int php_oci_bind_pre_exec(zval *data, void *result)
915 {
916 	php_oci_bind *bind = (php_oci_bind *) Z_PTR_P(data);
917 	zval *zv = &bind->val;
918 
919 	*(int *)result = 0;
920 
921 	ZVAL_DEREF(zv);
922 	if (Z_TYPE_P(zv) == IS_ARRAY) {
923 		/* These checks are currently valid for oci_bind_by_name, not
924 		 * oci_bind_array_by_name.  Also bind->type and
925 		 * bind->indicator are not used for oci_bind_array_by_name.
926 		 */
927 		return 0;
928 	}
929 	switch (bind->type) {
930 		case SQLT_NTY:
931 		case SQLT_BFILEE:
932 		case SQLT_CFILEE:
933 		case SQLT_CLOB:
934 		case SQLT_BLOB:
935 		case SQLT_RDD:
936 			if (Z_TYPE_P(zv) != IS_OBJECT) {
937 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
938 				*(int *)result = 1;
939 			}
940 			break;
941 
942 		case SQLT_CHR:
943 		case SQLT_AFC:
944 		case SQLT_INT:
945 		case SQLT_NUM:
946 #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
947 		case SQLT_BOL:
948 #endif
949 		case SQLT_LBI:
950 		case SQLT_BIN:
951 		case SQLT_LNG:
952 			if (Z_TYPE_P(zv) == IS_RESOURCE || Z_TYPE_P(zv) == IS_OBJECT) {
953 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
954 				*(int *)result = 1;
955 			}
956 			break;
957 
958 		case SQLT_RSET:
959 			if (Z_TYPE_P(zv) != IS_RESOURCE) {
960 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
961 				*(int *)result = 1;
962 			}
963 			break;
964 	}
965 
966 	/* reset all bind stuff to a normal state... */
967 	bind->indicator = 0;
968 
969 	return 0;
970 }
971 /* }}} */
972 
973 /* {{{ php_oci_bind_post_exec()
974  Helper function */
php_oci_bind_post_exec(zval * data)975 int php_oci_bind_post_exec(zval *data)
976 {
977 	php_oci_bind *bind = (php_oci_bind *) Z_PTR_P(data);
978 	php_oci_connection *connection = bind->parent_statement->connection;
979 	sword errstatus;
980 	zval *zv = &bind->val;
981 
982 	ZVAL_DEREF(zv);
983 	if (bind->indicator == -1) { /* NULL */
984 		if (Z_TYPE_P(zv) == IS_STRING) {
985 			*Z_STRVAL_P(zv) = '\0'; /* XXX avoid warning in debug mode */
986 		}
987 		zval_ptr_dtor(zv);
988 		ZVAL_NULL(zv);
989 	} else if (Z_TYPE_P(zv) == IS_STRING
990 			   && Z_STRLEN_P(zv) > 0
991 			   && Z_STRVAL_P(zv)[ Z_STRLEN_P(zv) ] != '\0') {
992 		/* The post- PHP 5.3 feature for "interned" strings disallows
993 		 * their reallocation but (i) any IN binds either interned or
994 		 * not should already be null terminated and (ii) for OUT
995 		 * binds, php_oci_bind_out_callback() should have allocated a
996 		 * new string that we can modify here.
997 		 */
998 		ZVAL_NEW_STR(zv, zend_string_extend(Z_STR_P(zv), Z_STRLEN_P(zv)+1, 0));
999 		Z_STRVAL_P(zv)[ Z_STRLEN_P(zv) ] = '\0';
1000 	} else if (Z_TYPE_P(zv) == IS_ARRAY) {
1001 		int i;
1002 		zval *entry = NULL;
1003 		HashTable *hash;
1004 
1005 		SEPARATE_ARRAY(zv);
1006 		hash = Z_ARRVAL_P(zv);
1007 		zend_hash_internal_pointer_reset(hash);
1008 
1009 		switch (bind->array.type) {
1010 			case SQLT_NUM:
1011 			case SQLT_INT:
1012 			case SQLT_LNG:
1013 				for (i = 0; i < (int) bind->array.current_length; i++) {
1014 					if ((i < (int) bind->array.old_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1015 						zval_ptr_dtor(entry);
1016 						ZVAL_LONG(entry, ((oci_phpsized_int *)(bind->array.elements))[i]);
1017 						zend_hash_move_forward(hash);
1018 					} else {
1019 						add_next_index_long(zv, ((oci_phpsized_int *)(bind->array.elements))[i]);
1020 					}
1021 				}
1022 				break;
1023 			case SQLT_FLT:
1024 				for (i = 0; i < (int) bind->array.current_length; i++) {
1025 					if ((i < (int) bind->array.old_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1026 						zval_ptr_dtor(entry);
1027 						ZVAL_DOUBLE(entry, ((double *)(bind->array.elements))[i]);
1028 						zend_hash_move_forward(hash);
1029 					} else {
1030 						add_next_index_double(zv, ((double *)(bind->array.elements))[i]);
1031 					}
1032 				}
1033 				break;
1034 			case SQLT_ODT:
1035 				for (i = 0; i < (int) bind->array.current_length; i++) {
1036 					oratext buff[1024];
1037 					ub4 buff_len = 1024;
1038 
1039 					memset((void*)buff,0,sizeof(buff));
1040 
1041 					if ((i < (int) bind->array.old_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1042 						PHP_OCI_CALL_RETURN(errstatus, OCIDateToText, (connection->err, &(((OCIDate *)(bind->array.elements))[i]), 0, 0, 0, 0, &buff_len, buff));
1043 						zval_ptr_dtor(entry);
1044 
1045 						if (errstatus != OCI_SUCCESS) {
1046 							connection->errcode = php_oci_error(connection->err, errstatus);
1047 							PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1048 							ZVAL_NULL(entry);
1049 						} else {
1050 							connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1051 							ZVAL_STRINGL(entry, (char *)buff, buff_len);
1052 						}
1053 						zend_hash_move_forward(hash);
1054 					} else {
1055 						PHP_OCI_CALL_RETURN(errstatus, OCIDateToText, (connection->err, &(((OCIDate *)(bind->array.elements))[i]), 0, 0, 0, 0, &buff_len, buff));
1056 						if (errstatus != OCI_SUCCESS) {
1057 							connection->errcode = php_oci_error(connection->err, errstatus);
1058 							PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1059 							add_next_index_null(zv);
1060 						} else {
1061 							connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1062 							add_next_index_stringl(zv, (char *)buff, buff_len);
1063 						}
1064 					}
1065 				}
1066 				break;
1067 
1068 			case SQLT_AFC:
1069 			case SQLT_CHR:
1070 			case SQLT_VCS:
1071 			case SQLT_AVC:
1072 			case SQLT_STR:
1073 			case SQLT_LVC:
1074 				for (i = 0; i < (int) bind->array.current_length; i++) {
1075 					/* int curr_element_length = strlen(((text *)bind->array.elements)+i*bind->array.max_length); */
1076 					int curr_element_length = bind->array.element_lengths[i];
1077 					if ((i < (int) bind->array.old_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1078 						zval_ptr_dtor(entry);
1079 						ZVAL_STRINGL(entry, (char *)(((text *)bind->array.elements)+i*bind->array.max_length), curr_element_length);
1080 						zend_hash_move_forward(hash);
1081 					} else {
1082 						add_next_index_stringl(zv, (char *)(((text *)bind->array.elements)+i*bind->array.max_length), curr_element_length);
1083 					}
1084 				}
1085 				break;
1086 		}
1087 	} else if ((Z_TYPE_P(zv) == IS_TRUE) || (Z_TYPE_P(zv) == IS_FALSE)) {
1088 		/* This convetrsion is done on purpose (ext/oci8 uses LVAL as a temporary value) */
1089 		if (Z_LVAL_P(zv) == 0)
1090 			ZVAL_BOOL(zv, FALSE);
1091 		else if (Z_LVAL_P(zv) == 1)
1092 			ZVAL_BOOL(zv, TRUE);
1093 	}
1094 
1095 	return 0;
1096 }
1097 /* }}} */
1098 
1099 /* {{{ php_oci_bind_by_name()
1100  Bind zval to the given placeholder */
php_oci_bind_by_name(php_oci_statement * statement,char * name,size_t name_len,zval * var,zend_long maxlength,ub2 type)1101 int php_oci_bind_by_name(php_oci_statement *statement, char *name, size_t name_len, zval *var, zend_long maxlength, ub2 type)
1102 {
1103 	php_oci_collection *bind_collection = NULL;
1104 	php_oci_descriptor *bind_descriptor = NULL;
1105 	php_oci_statement  *bind_statement	= NULL;
1106 	dvoid *oci_desc					= NULL;
1107 	/* dvoid *php_oci_collection		   = NULL; */
1108 	OCIStmt *oci_stmt				= NULL;
1109 	dvoid *bind_data				= NULL;
1110 	php_oci_bind *old_bind, *bindp;
1111 	int mode = OCI_DATA_AT_EXEC;
1112 	sb4 value_sz = -1;
1113 	sword errstatus;
1114 	zval *param = NULL;
1115 
1116 	ZEND_ASSERT(Z_ISREF_P(var));
1117 	param = Z_REFVAL_P(var);
1118 
1119 	switch (type) {
1120 		case SQLT_NTY:
1121 		{
1122 			zval *tmp;
1123 
1124 			if (Z_TYPE_P(param) != IS_OBJECT || (tmp = zend_hash_str_find(Z_OBJPROP_P(param), "collection", sizeof("collection")-1)) == NULL) {
1125 				php_error_docref(NULL, E_WARNING, "Unable to find collection property");
1126 				return 1;
1127 			}
1128 
1129 			PHP_OCI_ZVAL_TO_COLLECTION_EX(tmp, bind_collection);
1130 			value_sz = sizeof(void*);
1131 			mode = OCI_DEFAULT;
1132 
1133 			if (!bind_collection->collection) {
1134 				return 1;
1135 			}
1136 		}
1137 			break;
1138 		case SQLT_BFILEE:
1139 		case SQLT_CFILEE:
1140 		case SQLT_CLOB:
1141 		case SQLT_BLOB:
1142 		case SQLT_RDD:
1143 		{
1144 			zval *tmp;
1145 
1146 			if (Z_TYPE_P(param) != IS_OBJECT || (tmp = zend_hash_str_find(Z_OBJPROP_P(param), "descriptor", sizeof("descriptor")-1)) == NULL) {
1147 				php_error_docref(NULL, E_WARNING, "Unable to find descriptor property");
1148 				return 1;
1149 			}
1150 
1151 			PHP_OCI_ZVAL_TO_DESCRIPTOR_EX(tmp, bind_descriptor);
1152 
1153 			value_sz = sizeof(void*);
1154 
1155 			oci_desc = bind_descriptor->descriptor;
1156 
1157 			if (!oci_desc) {
1158 				return 1;
1159 			}
1160 		}
1161 			break;
1162 
1163 		case SQLT_INT:
1164 		case SQLT_NUM:
1165 			if (Z_TYPE_P(param) == IS_RESOURCE || Z_TYPE_P(param) == IS_OBJECT) {
1166 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1167 				return 1;
1168 			}
1169 			convert_to_long(param);
1170 			bind_data = (oci_phpsized_int *)&Z_LVAL_P(param);
1171 			value_sz = sizeof(oci_phpsized_int);
1172 			mode = OCI_DEFAULT;
1173 			break;
1174 
1175 		case SQLT_LBI:
1176 		case SQLT_BIN:
1177 		case SQLT_LNG:
1178 		case SQLT_AFC:
1179 		case SQLT_CHR: /* SQLT_CHR is the default value when type was not specified */
1180 			if (Z_TYPE_P(param) == IS_RESOURCE || Z_TYPE_P(param) == IS_OBJECT) {
1181 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1182 				return 1;
1183 			}
1184 			if (Z_TYPE_P(param) != IS_NULL) {
1185 				if (!try_convert_to_string(param)) {
1186 					return 1;
1187 				}
1188 			}
1189 			if ((maxlength == -1) || (maxlength == 0)) {
1190 				if (type == SQLT_LNG) {
1191 					value_sz = SB4MAXVAL;
1192 				} else if (Z_TYPE_P(param) == IS_STRING) {
1193 					value_sz = (sb4) Z_STRLEN_P(param);
1194 				} else {
1195 					/* Bug-72524: revert value_sz from PHP_OCI_PIECE_SIZE to 0. This restores PHP 5.6 behavior  */
1196 					value_sz = 0;
1197 				}
1198 			} else {
1199 				value_sz = (sb4) maxlength;
1200 			}
1201 			break;
1202 
1203 		case SQLT_RSET:
1204 			if (Z_TYPE_P(param) != IS_RESOURCE) {
1205 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1206 				return 1;
1207 			}
1208 			PHP_OCI_ZVAL_TO_STATEMENT_EX(param, bind_statement);
1209 			value_sz = sizeof(void*);
1210 
1211 			oci_stmt = bind_statement->stmt;
1212 
1213 			if (!oci_stmt) {
1214 				return 1;
1215 			}
1216 			break;
1217 
1218 #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
1219 		case SQLT_BOL:
1220 			if (Z_TYPE_P(param) == IS_RESOURCE || Z_TYPE_P(param) == IS_OBJECT) {
1221 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1222 				return 1;
1223 			}
1224 			convert_to_boolean(param);
1225 			bind_data = (zend_long *)&Z_LVAL_P(param);
1226 			if (Z_TYPE_P(param) == IS_TRUE)
1227 				*(zend_long *)bind_data = 1;
1228 			else if (Z_TYPE_P(param) == IS_FALSE)
1229 				*(zend_long *)bind_data = 0;
1230 			else {
1231 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1232 				return 1;
1233 			}
1234 
1235 			value_sz = sizeof(zend_long);
1236 
1237 			mode = OCI_DEFAULT;
1238 			break;
1239 #endif
1240 
1241 		default:
1242 			php_error_docref(NULL, E_WARNING, "Unknown or unsupported datatype given: %d", (int)type);
1243 			return 1;
1244 			break;
1245 	}
1246 
1247 	if (!statement->binds) {
1248 		ALLOC_HASHTABLE(statement->binds);
1249 		zend_hash_init(statement->binds, 13, NULL, php_oci_bind_hash_dtor, 0);
1250 	}
1251 
1252 	if ((old_bind = zend_hash_str_find_ptr(statement->binds, name, name_len)) != NULL) {
1253 		bindp = old_bind;
1254 		if (!Z_ISUNDEF(bindp->val)) {
1255 			zval_ptr_dtor(&bindp->val);
1256 			ZVAL_UNDEF(&bindp->val);
1257 		}
1258 	} else {
1259 		zend_string *zvtmp;
1260 		zvtmp = zend_string_init(name, name_len, 0);
1261 		bindp = (php_oci_bind *) ecalloc(1, sizeof(php_oci_bind));
1262 		bindp = zend_hash_update_ptr(statement->binds, zvtmp, bindp);
1263 		zend_string_release_ex(zvtmp, 0);
1264 	}
1265 
1266 	/* Make sure the minimum of value_sz is 1 to avoid ORA-3149
1267 	 * when both in/out parameters are bound with empty strings
1268 	 */
1269 	if (value_sz == 0)
1270 		value_sz = 1;
1271 
1272 	bindp->descriptor = oci_desc;
1273 	bindp->statement = oci_stmt;
1274 	bindp->parent_statement = statement;
1275 	ZVAL_COPY(&bindp->val, var);
1276 	bindp->type = type;
1277 	/* Storing max length set in OCIBindByName() to check it later in
1278 	 * php_oci_bind_in_callback() function to avoid ORA-1406 error while
1279 	 * executing OCIStmtExecute()
1280      */
1281 	bindp->dummy_len = value_sz;
1282 
1283 	PHP_OCI_CALL_RETURN(errstatus,
1284 		OCIBindByName,
1285 		(
1286 			statement->stmt,				 /* statement handle */
1287 			(OCIBind **)&bindp->bind,		 /* bind hdl (will alloc) */
1288 			statement->err,				  	 /* error handle */
1289 			(text*) name,					 /* placeholder name */
1290 			(sb4) name_len,					 /* placeholder length */
1291 			(dvoid *)bind_data,				 /* in/out data */
1292 			value_sz, /* PHP_OCI_MAX_DATA_SIZE, */ /* max size of input/output data */
1293 			type,							 /* in/out data type */
1294 			(dvoid *)&bindp->indicator,		 /* indicator (ignored) */
1295 			(ub2 *)0,						 /* size array (ignored) */
1296 			(ub2 *)&bindp->retcode,			 /* return code (ignored) */
1297 			(ub4)0,							 /* maxarr_len (PL/SQL only?) */
1298 			(ub4 *)0,						 /* actual array size (PL/SQL only?) */
1299 			mode							 /* mode */
1300 		)
1301 	);
1302 
1303 	if (errstatus != OCI_SUCCESS) {
1304 		statement->errcode = php_oci_error(statement->err, errstatus);
1305 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1306 		return 1;
1307 	}
1308 
1309 	if (mode == OCI_DATA_AT_EXEC) {
1310 		PHP_OCI_CALL_RETURN(errstatus, OCIBindDynamic,
1311 				(
1312 				 bindp->bind,
1313 				 statement->err,
1314 				 (dvoid *)bindp,
1315 				 php_oci_bind_in_callback,
1316 				 (dvoid *)bindp,
1317 				 php_oci_bind_out_callback
1318 				)
1319 		);
1320 
1321 		if (errstatus != OCI_SUCCESS) {
1322 			statement->errcode = php_oci_error(statement->err, errstatus);
1323 			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1324 			return 1;
1325 		}
1326 	}
1327 
1328 	if (type == SQLT_NTY) {
1329 		/* Bind object */
1330 		PHP_OCI_CALL_RETURN(errstatus, OCIBindObject,
1331 				(
1332 				 bindp->bind,
1333 				 statement->err,
1334 				 bind_collection->tdo,
1335 				 (dvoid **) &(bind_collection->collection),
1336 				 (ub4 *) 0,
1337 				 (dvoid **) 0,
1338 				 (ub4 *) 0
1339 				)
1340 		);
1341 
1342 		if (errstatus) {
1343 			statement->errcode = php_oci_error(statement->err, errstatus);
1344 			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1345 			return 1;
1346 		}
1347 	}
1348 
1349 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1350 	return 0;
1351 }
1352 /* }}} */
1353 
1354 /* {{{ php_oci_bind_in_callback()
1355  Callback used when binding LOBs and VARCHARs */
php_oci_bind_in_callback(dvoid * ictxp,OCIBind * bindp,ub4 iter,ub4 index,dvoid ** bufpp,ub4 * alenp,ub1 * piecep,dvoid ** indpp)1356 sb4 php_oci_bind_in_callback(
1357 					dvoid *ictxp,	  /* context pointer */
1358 					OCIBind *bindp,	  /* bind handle */
1359 					ub4 iter,		  /* 0-based execute iteration value */
1360 					ub4 index,		  /* index of current array for PL/SQL or row index for SQL */
1361 					dvoid **bufpp,	  /* pointer to data */
1362 					ub4 *alenp,		  /* size after value/piece has been read */
1363 					ub1 *piecep,	  /* which piece */
1364 					dvoid **indpp)	  /* indicator value */
1365 {
1366 	php_oci_bind *phpbind;
1367 	zval *val;
1368 
1369 	if (!(phpbind=(php_oci_bind *)ictxp) || Z_ISUNDEF(phpbind->val)) {
1370 		php_error_docref(NULL, E_WARNING, "Invalid phpbind pointer value");
1371 		return OCI_ERROR;
1372 	}
1373 
1374 	val = &phpbind->val;
1375 	ZVAL_DEREF(val);
1376 
1377 	if (Z_ISNULL_P(val)) {
1378 		/* we're going to insert a NULL column */
1379 		phpbind->indicator = -1;
1380 		*bufpp = 0;
1381 		*alenp = -1;
1382 		*indpp = (dvoid *)&phpbind->indicator;
1383 	} else	if ((phpbind->descriptor == 0) && (phpbind->statement == 0)) {
1384 		/* "normal" string bind */
1385 		if (!try_convert_to_string(val)) {
1386 			return OCI_ERROR;
1387 		}
1388 
1389 		*bufpp = Z_STRVAL_P(val);
1390 		*alenp = (ub4) Z_STRLEN_P(val);
1391 		/*
1392 		 * bind_char_1: If max length set in OCIBindByName is less than the
1393 		 * actual length of input string, then we have to overwrite alenp with
1394 		 * max value set in OCIBindByName (dummy_len). Or else it will cause
1395 		 * ORA-1406 error in OCIStmtExecute
1396 		 */
1397 		if ((phpbind->dummy_len > 0) && (phpbind->dummy_len < *alenp))
1398 			*alenp = phpbind->dummy_len;
1399 		*indpp = (dvoid *)&phpbind->indicator;
1400 	} else if (phpbind->statement != 0) {
1401 		/* RSET */
1402 		*bufpp = phpbind->statement;
1403 		*alenp = -1;		/* seems to be alright */
1404 		*indpp = (dvoid *)&phpbind->indicator;
1405 	} else {
1406 		/* descriptor bind */
1407 		*bufpp = phpbind->descriptor;
1408 		*alenp = -1;		/* seems to be alright */
1409 		*indpp = (dvoid *)&phpbind->indicator;
1410 	}
1411 
1412 	*piecep = OCI_ONE_PIECE; /* pass all data in one go */
1413 
1414 	return OCI_CONTINUE;
1415 }
1416 /* }}} */
1417 
1418 /* {{{ php_oci_bind_out_callback()
1419  Callback used when binding LOBs and VARCHARs */
php_oci_bind_out_callback(dvoid * octxp,OCIBind * bindp,ub4 iter,ub4 index,dvoid ** bufpp,ub4 ** alenpp,ub1 * piecep,dvoid ** indpp,ub2 ** rcodepp)1420 sb4 php_oci_bind_out_callback(
1421 					dvoid *octxp,	   /* context pointer */
1422 					OCIBind *bindp,	   /* bind handle */
1423 					ub4 iter,		   /* 0-based execute iteration value */
1424 					ub4 index,		   /* index of current array for PL/SQL or row index for SQL */
1425 					dvoid **bufpp,	   /* pointer to data */
1426 					ub4 **alenpp,	   /* size after value/piece has been read */
1427 					ub1 *piecep,	   /* which piece */
1428 					dvoid **indpp,	   /* indicator value */
1429 					ub2 **rcodepp)	   /* return code */
1430 {
1431 	php_oci_bind *phpbind;
1432 	zval *val;
1433 	sb4 retval = OCI_ERROR;
1434 
1435 	if (!(phpbind=(php_oci_bind *)octxp) || Z_ISUNDEF(phpbind->val)) {
1436 		php_error_docref(NULL, E_WARNING, "Invalid phpbind pointer value");
1437 		return retval;
1438 	}
1439 
1440 	val = &phpbind->val;
1441 	ZVAL_DEREF(val);
1442 
1443 	if (Z_TYPE_P(val) == IS_RESOURCE) {
1444 		/* Processing for ref-cursor out binds */
1445 		if (phpbind->statement != NULL) {
1446 			*bufpp = phpbind->statement;
1447 			*alenpp = &phpbind->dummy_len;
1448 			*piecep = OCI_ONE_PIECE;
1449 			*rcodepp = &phpbind->retcode;
1450 			*indpp = &phpbind->indicator;
1451 		}
1452 		retval = OCI_CONTINUE;
1453 	} else if (Z_TYPE_P(val) == IS_OBJECT) {
1454 		zval *tmp;
1455 		php_oci_descriptor *desc;
1456 
1457 		if (!phpbind->descriptor) {
1458 			return OCI_ERROR;
1459 		}
1460 
1461 		/* Do not use the cached lob size if the descriptor is an
1462 		 * out-bind as the contents would have been changed for in/out
1463 		 * binds (Bug #46994).
1464 		 */
1465 		if ((tmp = zend_hash_str_find(Z_OBJPROP_P(val), "descriptor", sizeof("descriptor")-1)) == NULL) {
1466 			php_error_docref(NULL, E_WARNING, "Unable to find object outbind descriptor property");
1467 			return OCI_ERROR;
1468 		}
1469 		PHP_OCI_ZVAL_TO_DESCRIPTOR_EX(tmp, desc);
1470 		desc->lob_size = -1;	/* force OCI8 to update cached size */
1471 
1472 		*alenpp = &phpbind->dummy_len;
1473 		*bufpp = phpbind->descriptor;
1474 		*piecep = OCI_ONE_PIECE;
1475 		*rcodepp = &phpbind->retcode;
1476 		*indpp = &phpbind->indicator;
1477 		retval = OCI_CONTINUE;
1478 	} else {
1479 		zval_ptr_dtor(val);
1480 
1481 		{
1482 			char *p = ecalloc(1, PHP_OCI_PIECE_SIZE);
1483 			ZVAL_STRINGL(val, p, PHP_OCI_PIECE_SIZE);
1484 			efree(p);
1485 		}
1486 #if 0
1487 		Z_STRLEN_P(val) = PHP_OCI_PIECE_SIZE; /* 64K-1 is max XXX */
1488 		Z_STRVAL_P(val) = ecalloc(1, Z_STRLEN_P(val) + 1);
1489 		/* XXX is this right? */
1490 		ZVAL_STRINGL(val, NULL, Z_STRLEN(val) + 1);
1491 #endif
1492 
1493 		/* XXX we assume that zend-zval len has 4 bytes */
1494 		*alenpp = (ub4*) &Z_STRLEN_P(val);
1495 		*bufpp = Z_STRVAL_P(val);
1496 		*piecep = OCI_ONE_PIECE;
1497 		*rcodepp = &phpbind->retcode;
1498 		*indpp = &phpbind->indicator;
1499 		retval = OCI_CONTINUE;
1500 	}
1501 
1502 	return retval;
1503 }
1504 /* }}} */
1505 
1506 /* {{{ php_oci_statement_get_column_helper()
1507  Helper function to get column by name and index */
php_oci_statement_get_column_helper(INTERNAL_FUNCTION_PARAMETERS,int need_data)1508 php_oci_out_column *php_oci_statement_get_column_helper(INTERNAL_FUNCTION_PARAMETERS, int need_data)
1509 {
1510 	zval *z_statement;
1511 	zend_string *column_index_string;
1512 	zend_long column_index_int;
1513 
1514 	php_oci_statement *statement;
1515 	php_oci_out_column *column;
1516 
1517 	ZEND_PARSE_PARAMETERS_START(2, 2)
1518 		Z_PARAM_RESOURCE(z_statement)
1519 		Z_PARAM_STR_OR_LONG(column_index_string, column_index_int)
1520 	ZEND_PARSE_PARAMETERS_END_EX(return NULL);
1521 
1522 	statement = (php_oci_statement *) zend_fetch_resource_ex(z_statement, "oci8 statement", le_statement);
1523 
1524 	if (!statement) {
1525 		return NULL;
1526 	}
1527 
1528 	if (need_data && !statement->has_data) {
1529 		return NULL;
1530 	}
1531 
1532 	if (column_index_string != NULL) {
1533 		column = php_oci_statement_get_column(statement, -1, ZSTR_VAL(column_index_string), (int) ZSTR_LEN(column_index_string));
1534 		if (!column) {
1535 			php_error_docref(NULL, E_WARNING, "Invalid column name \"%s\"", ZSTR_VAL(column_index_string));
1536 			return NULL;
1537 		}
1538 	} else {
1539 		column = php_oci_statement_get_column(statement, column_index_int, NULL, 0);
1540 
1541 		if (!column) {
1542 			php_error_docref(NULL, E_WARNING, "Invalid column index \"" ZEND_LONG_FMT "\"", column_index_int);
1543 			return NULL;
1544 		}
1545 	}
1546 	return column;
1547 }
1548 /* }}} */
1549 
1550 /* {{{ php_oci_statement_get_type()
1551  Return type of the statement */
php_oci_statement_get_type(php_oci_statement * statement,ub2 * type)1552 int php_oci_statement_get_type(php_oci_statement *statement, ub2 *type)
1553 {
1554 	ub2 statement_type;
1555 	sword errstatus;
1556 
1557 	*type = 0;
1558 
1559 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub2 *)&statement_type, (ub4 *)0, OCI_ATTR_STMT_TYPE, statement->err));
1560 
1561 	if (errstatus != OCI_SUCCESS) {
1562 		statement->errcode = php_oci_error(statement->err, errstatus);
1563 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1564 		return 1;
1565 	}
1566 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1567 	*type = statement_type;
1568 
1569 	return 0;
1570 }
1571 /* }}} */
1572 
1573 /* {{{ php_oci_statement_get_numrows()
1574  Get the number of rows fetched to the clientside (NOT the number of rows in the result set) */
php_oci_statement_get_numrows(php_oci_statement * statement,ub4 * numrows)1575 int php_oci_statement_get_numrows(php_oci_statement *statement, ub4 *numrows)
1576 {
1577 	ub4 statement_numrows;
1578 	sword errstatus;
1579 
1580 	*numrows = 0;
1581 
1582 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub4 *)&statement_numrows, (ub4 *)0, OCI_ATTR_ROW_COUNT, statement->err));
1583 
1584 	if (errstatus != OCI_SUCCESS) {
1585 		statement->errcode = php_oci_error(statement->err, errstatus);
1586 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1587 		return 1;
1588 	}
1589 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1590 	*numrows = statement_numrows;
1591 
1592 	return 0;
1593 }
1594 /* }}} */
1595 
1596 /* {{{ php_oci_bind_array_by_name()
1597  Bind arrays to PL/SQL types */
php_oci_bind_array_by_name(php_oci_statement * statement,char * name,size_t name_len,zval * var,zend_long max_table_length,zend_long maxlength,zend_long type)1598 int php_oci_bind_array_by_name(php_oci_statement *statement, char *name, size_t name_len, zval *var, zend_long max_table_length, zend_long maxlength, zend_long type)
1599 {
1600 	php_oci_bind *bind;
1601 	sword errstatus;
1602 	zend_string *zvtmp;
1603 	zval *val;
1604 
1605 	ZEND_ASSERT(Z_ISREF_P(var));
1606 	val = Z_REFVAL_P(var);
1607 	convert_to_array(val);
1608 	SEPARATE_ARRAY(val);
1609 
1610 	if (maxlength < -1) {
1611 		php_error_docref(NULL, E_WARNING, "Invalid max length value (" ZEND_LONG_FMT ")", maxlength);
1612 		return 1;
1613 	}
1614 
1615 	switch(type) {
1616 		case SQLT_NUM:
1617 		case SQLT_INT:
1618 		case SQLT_LNG:
1619 			bind = php_oci_bind_array_helper_number(val, max_table_length);
1620 			break;
1621 
1622 		case SQLT_FLT:
1623 			bind = php_oci_bind_array_helper_double(val, max_table_length);
1624 			break;
1625 
1626 		case SQLT_AFC:
1627 		case SQLT_CHR:
1628 		case SQLT_VCS:
1629 		case SQLT_AVC:
1630 		case SQLT_STR:
1631 		case SQLT_LVC:
1632 			if (maxlength == -1 && zend_hash_num_elements(Z_ARRVAL_P(val)) == 0) {
1633 				php_error_docref(NULL, E_WARNING, "You must provide max length value for empty arrays");
1634 				return 1;
1635 			}
1636 			bind = php_oci_bind_array_helper_string(val, max_table_length, maxlength);
1637 			break;
1638 		case SQLT_ODT:
1639 			bind = php_oci_bind_array_helper_date(val, max_table_length, statement->connection);
1640 			break;
1641 		default:
1642 			php_error_docref(NULL, E_WARNING, "Unknown or unsupported datatype given: " ZEND_LONG_FMT, type);
1643 			return 1;
1644 			break;
1645 	}
1646 
1647 	if (bind == NULL) {
1648 		/* failed to generate bind struct */
1649 		return 1;
1650 	}
1651 
1652 	bind->descriptor = NULL;
1653 	bind->statement = NULL;
1654 	bind->parent_statement = statement;
1655 	bind->bind = NULL;
1656 	ZVAL_COPY(&bind->val, var);
1657 	bind->array.type = type;
1658 	bind->indicator = 0;  		/* not used for array binds */
1659 	bind->type = 0; 			/* not used for array binds */
1660 
1661 	PHP_OCI_CALL_RETURN(errstatus,
1662 							OCIBindByName,
1663 							(
1664 								statement->stmt,
1665 								(OCIBind **)&bind->bind,
1666 								statement->err,
1667 								(text *)name,
1668 								(sb4) name_len,
1669 								(dvoid *) bind->array.elements,
1670 								(sb4) bind->array.max_length,
1671 								(ub2)type,
1672 								(dvoid *)bind->array.indicators,
1673 								(ub2 *)bind->array.element_lengths,
1674 								(ub2 *)0, /* bindp->array.retcodes, */
1675 								(ub4) max_table_length,
1676 								(ub4 *) &(bind->array.current_length),
1677 								(ub4) OCI_DEFAULT
1678 							)
1679 						);
1680 
1681 
1682 	if (errstatus != OCI_SUCCESS) {
1683 		if (bind->array.elements) {
1684 			efree(bind->array.elements);
1685 		}
1686 
1687 		if (bind->array.element_lengths) {
1688 			efree(bind->array.element_lengths);
1689 		}
1690 
1691 		if (bind->array.indicators) {
1692 			efree(bind->array.indicators);
1693 		}
1694 
1695 		zval_ptr_dtor(&bind->val);
1696 
1697 		efree(bind);
1698 
1699 		statement->errcode = php_oci_error(statement->err, errstatus);
1700 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1701 		return 1;
1702 	}
1703 
1704 	if (!statement->binds) {
1705 		ALLOC_HASHTABLE(statement->binds);
1706 		zend_hash_init(statement->binds, 13, NULL, php_oci_bind_hash_dtor, 0);
1707 	}
1708 
1709 	zvtmp = zend_string_init(name, name_len, 0);
1710 	zend_hash_update_ptr(statement->binds, zvtmp, bind);
1711 	zend_string_release_ex(zvtmp, 0);
1712 
1713 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1714 	return 0;
1715 }
1716 /* }}} */
1717 
1718 /* {{{ php_oci_bind_array_helper_string()
1719  Bind arrays to PL/SQL types */
php_oci_bind_array_helper_string(zval * var,zend_long max_table_length,zend_long maxlength)1720 php_oci_bind *php_oci_bind_array_helper_string(zval *var, zend_long max_table_length, zend_long maxlength)
1721 {
1722 	php_oci_bind *bind;
1723 	ub4 i;
1724 	HashTable *hash;
1725 	zval *entry;
1726 
1727 	SEPARATE_ARRAY(var); /* TODO: may be use new HashTable iteration and prevent inplace modification */
1728 	hash = Z_ARRVAL_P(var);
1729 
1730 	if (maxlength == -1) {
1731 		zend_hash_internal_pointer_reset(hash);
1732 		while ((entry = zend_hash_get_current_data(hash)) != NULL) {
1733 			if (!try_convert_to_string(entry)) {
1734 				return NULL;
1735 			}
1736 
1737 			if (maxlength == -1 || Z_STRLEN_P(entry) > (size_t) maxlength) {
1738 				maxlength = Z_STRLEN_P(entry) + 1;
1739 			}
1740 
1741 			zend_hash_move_forward(hash);
1742 		}
1743 	}
1744 
1745 	bind = emalloc(sizeof(php_oci_bind));
1746 	ZVAL_UNDEF(&bind->val);
1747 	bind->array.elements		= (text *)safe_emalloc(max_table_length * (maxlength + 1), sizeof(text), 0);
1748 	memset(bind->array.elements, 0, max_table_length * (maxlength + 1) * sizeof(text));
1749 	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1750 	bind->array.old_length		= bind->array.current_length;
1751 	bind->array.max_length		= (ub4) maxlength;
1752 	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1753 	memset(bind->array.element_lengths, 0, max_table_length*sizeof(ub2));
1754 	bind->array.indicators		= safe_emalloc(max_table_length, sizeof(sb2), 0);
1755 	memset(bind->array.indicators, 0, max_table_length*sizeof(sb2));
1756 
1757 	zend_hash_internal_pointer_reset(hash);
1758 
1759 	for (i = 0; i < bind->array.current_length; i++) {
1760 		if ((entry = zend_hash_get_current_data(hash)) != NULL) {
1761 			if (!try_convert_to_string(entry)) {
1762 				efree(bind->array.elements);
1763 				efree(bind->array.element_lengths);
1764 				efree(bind->array.indicators);
1765 				efree(bind);
1766 				return NULL;
1767 			}
1768 
1769 			bind->array.element_lengths[i] = (ub2) Z_STRLEN_P(entry);
1770 			if (Z_STRLEN_P(entry) == 0) {
1771 				bind->array.indicators[i] = -1;
1772 			}
1773 			zend_hash_move_forward(hash);
1774 		} else {
1775 			break;
1776 		}
1777 	}
1778 
1779 	zend_hash_internal_pointer_reset(hash);
1780 	for (i = 0; i < max_table_length; i++) {
1781 		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1782 			int element_length;
1783 			if (!try_convert_to_string(entry)) {
1784 				efree(bind->array.elements);
1785 				efree(bind->array.element_lengths);
1786 				efree(bind->array.indicators);
1787 				efree(bind);
1788 				return NULL;
1789 			}
1790 
1791 			element_length = ((size_t) maxlength > Z_STRLEN_P(entry)) ? (int) Z_STRLEN_P(entry) : (int) maxlength;
1792 
1793 			memcpy((text *)bind->array.elements + i*maxlength, Z_STRVAL_P(entry), element_length);
1794 			((text *)bind->array.elements)[i*maxlength + element_length] = '\0';
1795 
1796 			zend_hash_move_forward(hash);
1797 		} else {
1798 			((text *)bind->array.elements)[i*maxlength] = '\0';
1799 		}
1800 	}
1801 	zend_hash_internal_pointer_reset(hash);
1802 
1803 	return bind;
1804 }
1805 /* }}} */
1806 
1807 /* {{{ php_oci_bind_array_helper_number()
1808  Bind arrays to PL/SQL types */
php_oci_bind_array_helper_number(zval * var,zend_long max_table_length)1809 php_oci_bind *php_oci_bind_array_helper_number(zval *var, zend_long max_table_length)
1810 {
1811 	php_oci_bind *bind;
1812 	ub4 i;
1813 	HashTable *hash;
1814 	zval *entry;
1815 
1816 	SEPARATE_ARRAY(var); /* TODO: may be use new HashTable iteration and prevent inplace modification */
1817 	hash = Z_ARRVAL_P(var);
1818 
1819 	bind = emalloc(sizeof(php_oci_bind));
1820 	ZVAL_UNDEF(&bind->val);
1821 	bind->array.elements		= (oci_phpsized_int *)safe_emalloc(max_table_length, sizeof(oci_phpsized_int), 0);
1822 	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1823 	bind->array.old_length		= bind->array.current_length;
1824 	bind->array.max_length		= sizeof(oci_phpsized_int);
1825 	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1826 	memset(bind->array.element_lengths, 0, max_table_length * sizeof(ub2));
1827 	bind->array.indicators		= NULL;
1828 
1829 	zend_hash_internal_pointer_reset(hash);
1830 	for (i = 0; i < max_table_length; i++) {
1831 		if (i < bind->array.current_length) {
1832 			bind->array.element_lengths[i] = sizeof(oci_phpsized_int);
1833 		}
1834 		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1835 			convert_to_long_ex(entry);
1836 			((oci_phpsized_int *)bind->array.elements)[i] = (oci_phpsized_int) Z_LVAL_P(entry);
1837 			zend_hash_move_forward(hash);
1838 		} else {
1839 			((oci_phpsized_int *)bind->array.elements)[i] = 0;
1840 		}
1841 	}
1842 	zend_hash_internal_pointer_reset(hash);
1843 
1844 	return bind;
1845 }
1846 /* }}} */
1847 
1848 /* {{{ php_oci_bind_array_helper_double()
1849  Bind arrays to PL/SQL types */
php_oci_bind_array_helper_double(zval * var,zend_long max_table_length)1850 php_oci_bind *php_oci_bind_array_helper_double(zval *var, zend_long max_table_length)
1851 {
1852 	php_oci_bind *bind;
1853 	ub4 i;
1854 	HashTable *hash;
1855 	zval *entry;
1856 
1857 	SEPARATE_ARRAY(var); /* TODO: may be use new HashTable iteration and prevent inplace modification */
1858 	hash = Z_ARRVAL_P(var);
1859 
1860 	bind = emalloc(sizeof(php_oci_bind));
1861 	ZVAL_UNDEF(&bind->val);
1862 	bind->array.elements		= (double *)safe_emalloc(max_table_length, sizeof(double), 0);
1863 	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1864 	bind->array.old_length		= bind->array.current_length;
1865 	bind->array.max_length		= sizeof(double);
1866 	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1867 	memset(bind->array.element_lengths, 0, max_table_length * sizeof(ub2));
1868 	bind->array.indicators		= NULL;
1869 
1870 	zend_hash_internal_pointer_reset(hash);
1871 	for (i = 0; i < max_table_length; i++) {
1872 		if (i < bind->array.current_length) {
1873 			bind->array.element_lengths[i] = sizeof(double);
1874 		}
1875 		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1876 			convert_to_double_ex(entry);
1877 			((double *)bind->array.elements)[i] = (double) Z_DVAL_P(entry);
1878 			zend_hash_move_forward(hash);
1879 		} else {
1880 			((double *)bind->array.elements)[i] = 0;
1881 		}
1882 	}
1883 	zend_hash_internal_pointer_reset(hash);
1884 
1885 	return bind;
1886 }
1887 /* }}} */
1888 
1889 /* {{{ php_oci_bind_array_helper_date()
1890  Bind arrays to PL/SQL types */
php_oci_bind_array_helper_date(zval * var,zend_long max_table_length,php_oci_connection * connection)1891 php_oci_bind *php_oci_bind_array_helper_date(zval *var, zend_long max_table_length, php_oci_connection *connection)
1892 {
1893 	php_oci_bind *bind;
1894 	ub4 i;
1895 	HashTable *hash;
1896 	zval *entry;
1897 	sword errstatus;
1898 
1899 	SEPARATE_ARRAY(var); /* TODO: may be use new HashTable iteration and prevent inplace modification */
1900 	hash = Z_ARRVAL_P(var);
1901 
1902 	bind = emalloc(sizeof(php_oci_bind));
1903 	ZVAL_UNDEF(&bind->val);
1904 	bind->array.elements		= (OCIDate *)safe_emalloc(max_table_length, sizeof(OCIDate), 0);
1905 	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1906 	bind->array.old_length		= bind->array.current_length;
1907 	bind->array.max_length		= sizeof(OCIDate);
1908 	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1909 	memset(bind->array.element_lengths, 0, max_table_length * sizeof(ub2));
1910 	bind->array.indicators		= NULL;
1911 
1912 	zend_hash_internal_pointer_reset(hash);
1913 	for (i = 0; i < max_table_length; i++) {
1914 		OCIDate oci_date;
1915 		if (i < bind->array.current_length) {
1916 			bind->array.element_lengths[i] = sizeof(OCIDate);
1917 		}
1918 		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1919 			zend_string *entry_str = zval_try_get_string(entry);
1920 			if (UNEXPECTED(!entry_str)) {
1921 				efree(bind->array.element_lengths);
1922 				efree(bind->array.elements);
1923 				efree(bind);
1924 				return NULL;
1925 			}
1926 
1927 			PHP_OCI_CALL_RETURN(errstatus, OCIDateFromText, (connection->err, (CONST text *)ZSTR_VAL(entry_str), (ub4) ZSTR_LEN(entry_str), NULL, 0, NULL, 0, &oci_date));
1928 			zend_string_release(entry_str);
1929 
1930 			if (errstatus != OCI_SUCCESS) {
1931 				/* failed to convert string to date */
1932 				efree(bind->array.element_lengths);
1933 				efree(bind->array.elements);
1934 				efree(bind);
1935 				connection->errcode = php_oci_error(connection->err, errstatus);
1936 				PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1937 				return NULL;
1938 			}
1939 
1940 			((OCIDate *)bind->array.elements)[i] = oci_date;
1941 			zend_hash_move_forward(hash);
1942 		} else {
1943 			PHP_OCI_CALL_RETURN(errstatus, OCIDateFromText, (connection->err, (CONST text *)"01-JAN-00", sizeof("01-JAN-00")-1, NULL, 0, NULL, 0, &oci_date));
1944 
1945 			if (errstatus != OCI_SUCCESS) {
1946 				/* failed to convert string to date */
1947 				efree(bind->array.element_lengths);
1948 				efree(bind->array.elements);
1949 				efree(bind);
1950 				connection->errcode = php_oci_error(connection->err, errstatus);
1951 				PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1952 				return NULL;
1953 			}
1954 
1955 			((OCIDate *)bind->array.elements)[i] = oci_date;
1956 		}
1957 		connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1958 	}
1959 	zend_hash_internal_pointer_reset(hash);
1960 
1961 	return bind;
1962 }
1963 /* }}} */
1964 
1965 #endif /* HAVE_OCI8 */
1966