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