xref: /PHP-7.3/ext/oci8/oci8_statement.c (revision e2ecd60f)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 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 				convert_to_string(param);
1193 			}
1194 			if ((maxlength == -1) || (maxlength == 0)) {
1195 				if (type == SQLT_LNG) {
1196 					value_sz = SB4MAXVAL;
1197 				} else if (Z_TYPE_P(param) == IS_STRING) {
1198 					value_sz = (sb4) Z_STRLEN_P(param);
1199 				} else {
1200 					/* Bug-72524: revert value_sz from PHP_OCI_PIECE_SIZE to 0. This restores PHP 5.6 behavior  */
1201 					value_sz = 0;
1202 				}
1203 			} else {
1204 				value_sz = (sb4) maxlength;
1205 			}
1206 			break;
1207 
1208 		case SQLT_RSET:
1209 			if (Z_TYPE_P(param) != IS_RESOURCE) {
1210 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1211 				return 1;
1212 			}
1213 			PHP_OCI_ZVAL_TO_STATEMENT_EX(param, bind_statement);
1214 			value_sz = sizeof(void*);
1215 
1216 			oci_stmt = bind_statement->stmt;
1217 
1218 			if (!oci_stmt) {
1219 				return 1;
1220 			}
1221 			break;
1222 
1223 #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
1224 		case SQLT_BOL:
1225 			if (Z_TYPE_P(param) == IS_RESOURCE || Z_TYPE_P(param) == IS_OBJECT) {
1226 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1227 				return 1;
1228 			}
1229 			convert_to_boolean(param);
1230 			bind_data = (zend_long *)&Z_LVAL_P(param);
1231 			if (Z_TYPE_P(param) == IS_TRUE)
1232 				*(zend_long *)bind_data = 1;
1233 			else if (Z_TYPE_P(param) == IS_FALSE)
1234 				*(zend_long *)bind_data = 0;
1235 			else {
1236 				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1237 				return 1;
1238 			}
1239 
1240 			value_sz = sizeof(zend_long);
1241 
1242 			mode = OCI_DEFAULT;
1243 			break;
1244 #endif
1245 
1246 		default:
1247 			php_error_docref(NULL, E_WARNING, "Unknown or unsupported datatype given: %d", (int)type);
1248 			return 1;
1249 			break;
1250 	}
1251 
1252 	if (!statement->binds) {
1253 		ALLOC_HASHTABLE(statement->binds);
1254 		zend_hash_init(statement->binds, 13, NULL, php_oci_bind_hash_dtor, 0);
1255 	}
1256 
1257 	if ((old_bind = zend_hash_str_find_ptr(statement->binds, name, name_len)) != NULL) {
1258 		bindp = old_bind;
1259 		if (!Z_ISUNDEF(bindp->val)) {
1260 			zval_ptr_dtor(&bindp->val);
1261 			ZVAL_UNDEF(&bindp->val);
1262 		}
1263 	} else {
1264 		zend_string *zvtmp;
1265 		zvtmp = zend_string_init(name, name_len, 0);
1266 		bindp = (php_oci_bind *) ecalloc(1, sizeof(php_oci_bind));
1267 		bindp = zend_hash_update_ptr(statement->binds, zvtmp, bindp);
1268 #if PHP_VERSION_ID < 70300
1269 		zend_string_release(zvtmp);
1270 #else
1271 		zend_string_release_ex(zvtmp, 0);
1272 #endif
1273 	}
1274 
1275 	/* Make sure the minimum of value_sz is 1 to avoid ORA-3149
1276 	 * when both in/out parameters are bound with empty strings
1277 	 */
1278 	if (value_sz == 0)
1279 		value_sz = 1;
1280 
1281 	bindp->descriptor = oci_desc;
1282 	bindp->statement = oci_stmt;
1283 	bindp->parent_statement = statement;
1284 	ZVAL_COPY(&bindp->val, var);
1285 	bindp->type = type;
1286 	/* Storing max length set in OCIBindByName() to check it later in
1287 	 * php_oci_bind_in_callback() function to avoid ORA-1406 error while
1288 	 * executing OCIStmtExecute()
1289      */
1290 	bindp->dummy_len = value_sz;
1291 
1292 	PHP_OCI_CALL_RETURN(errstatus,
1293 		OCIBindByName,
1294 		(
1295 			statement->stmt,				 /* statement handle */
1296 			(OCIBind **)&bindp->bind,		 /* bind hdl (will alloc) */
1297 			statement->err,				  	 /* error handle */
1298 			(text*) name,					 /* placeholder name */
1299 			(sb4) name_len,					 /* placeholder length */
1300 			(dvoid *)bind_data,				 /* in/out data */
1301 			value_sz, /* PHP_OCI_MAX_DATA_SIZE, */ /* max size of input/output data */
1302 			type,							 /* in/out data type */
1303 			(dvoid *)&bindp->indicator,		 /* indicator (ignored) */
1304 			(ub2 *)0,						 /* size array (ignored) */
1305 			(ub2 *)&bindp->retcode,			 /* return code (ignored) */
1306 			(ub4)0,							 /* maxarr_len (PL/SQL only?) */
1307 			(ub4 *)0,						 /* actual array size (PL/SQL only?) */
1308 			mode							 /* mode */
1309 		)
1310 	);
1311 
1312 	if (errstatus != OCI_SUCCESS) {
1313 		statement->errcode = php_oci_error(statement->err, errstatus);
1314 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1315 		return 1;
1316 	}
1317 
1318 	if (mode == OCI_DATA_AT_EXEC) {
1319 		PHP_OCI_CALL_RETURN(errstatus, OCIBindDynamic,
1320 				(
1321 				 bindp->bind,
1322 				 statement->err,
1323 				 (dvoid *)bindp,
1324 				 php_oci_bind_in_callback,
1325 				 (dvoid *)bindp,
1326 				 php_oci_bind_out_callback
1327 				)
1328 		);
1329 
1330 		if (errstatus != OCI_SUCCESS) {
1331 			statement->errcode = php_oci_error(statement->err, errstatus);
1332 			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1333 			return 1;
1334 		}
1335 	}
1336 
1337 	if (type == SQLT_NTY) {
1338 		/* Bind object */
1339 		PHP_OCI_CALL_RETURN(errstatus, OCIBindObject,
1340 				(
1341 				 bindp->bind,
1342 				 statement->err,
1343 				 bind_collection->tdo,
1344 				 (dvoid **) &(bind_collection->collection),
1345 				 (ub4 *) 0,
1346 				 (dvoid **) 0,
1347 				 (ub4 *) 0
1348 				)
1349 		);
1350 
1351 		if (errstatus) {
1352 			statement->errcode = php_oci_error(statement->err, errstatus);
1353 			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1354 			return 1;
1355 		}
1356 	}
1357 
1358 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1359 	return 0;
1360 }
1361 /* }}} */
1362 
1363 /* {{{ php_oci_bind_in_callback()
1364  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)1365 sb4 php_oci_bind_in_callback(
1366 					dvoid *ictxp,	  /* context pointer */
1367 					OCIBind *bindp,	  /* bind handle */
1368 					ub4 iter,		  /* 0-based execute iteration value */
1369 					ub4 index,		  /* index of current array for PL/SQL or row index for SQL */
1370 					dvoid **bufpp,	  /* pointer to data */
1371 					ub4 *alenp,		  /* size after value/piece has been read */
1372 					ub1 *piecep,	  /* which piece */
1373 					dvoid **indpp)	  /* indicator value */
1374 {
1375 	php_oci_bind *phpbind;
1376 	zval *val;
1377 
1378 	if (!(phpbind=(php_oci_bind *)ictxp) || Z_ISUNDEF(phpbind->val)) {
1379 		php_error_docref(NULL, E_WARNING, "Invalid phpbind pointer value");
1380 		return OCI_ERROR;
1381 	}
1382 
1383 	val = &phpbind->val;
1384 	ZVAL_DEREF(val);
1385 
1386 	if (Z_ISNULL_P(val)) {
1387 		/* we're going to insert a NULL column */
1388 		phpbind->indicator = -1;
1389 		*bufpp = 0;
1390 		*alenp = -1;
1391 		*indpp = (dvoid *)&phpbind->indicator;
1392 	} else	if ((phpbind->descriptor == 0) && (phpbind->statement == 0)) {
1393 		/* "normal string bind */
1394 		convert_to_string(val);
1395 
1396 		*bufpp = Z_STRVAL_P(val);
1397 		*alenp = (ub4) Z_STRLEN_P(val);
1398 		/*
1399 		 * bind_char_1: If max length set in OCIBindByName is less than the
1400 		 * actual length of input string, then we have to overwrite alenp with
1401 		 * max value set in OCIBindByName (dummy_len). Or else it will cause
1402 		 * ORA-1406 error in OCIStmtExecute
1403 		 */
1404 		if ((phpbind->dummy_len > 0) && (phpbind->dummy_len < *alenp))
1405 			*alenp = phpbind->dummy_len;
1406 		*indpp = (dvoid *)&phpbind->indicator;
1407 	} else if (phpbind->statement != 0) {
1408 		/* RSET */
1409 		*bufpp = phpbind->statement;
1410 		*alenp = -1;		/* seems to be allright */
1411 		*indpp = (dvoid *)&phpbind->indicator;
1412 	} else {
1413 		/* descriptor bind */
1414 		*bufpp = phpbind->descriptor;
1415 		*alenp = -1;		/* seems to be allright */
1416 		*indpp = (dvoid *)&phpbind->indicator;
1417 	}
1418 
1419 	*piecep = OCI_ONE_PIECE; /* pass all data in one go */
1420 
1421 	return OCI_CONTINUE;
1422 }
1423 /* }}} */
1424 
1425 /* {{{ php_oci_bind_out_callback()
1426  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)1427 sb4 php_oci_bind_out_callback(
1428 					dvoid *octxp,	   /* context pointer */
1429 					OCIBind *bindp,	   /* bind handle */
1430 					ub4 iter,		   /* 0-based execute iteration value */
1431 					ub4 index,		   /* index of current array for PL/SQL or row index for SQL */
1432 					dvoid **bufpp,	   /* pointer to data */
1433 					ub4 **alenpp,	   /* size after value/piece has been read */
1434 					ub1 *piecep,	   /* which piece */
1435 					dvoid **indpp,	   /* indicator value */
1436 					ub2 **rcodepp)	   /* return code */
1437 {
1438 	php_oci_bind *phpbind;
1439 	zval *val;
1440 	sb4 retval = OCI_ERROR;
1441 
1442 	if (!(phpbind=(php_oci_bind *)octxp) || Z_ISUNDEF(phpbind->val)) {
1443 		php_error_docref(NULL, E_WARNING, "Invalid phpbind pointer value");
1444 		return retval;
1445 	}
1446 
1447 	val = &phpbind->val;
1448 	ZVAL_DEREF(val);
1449 
1450 	if (Z_TYPE_P(val) == IS_RESOURCE) {
1451 		/* Processing for ref-cursor out binds */
1452 		if (phpbind->statement != NULL) {
1453 			*bufpp = phpbind->statement;
1454 			*alenpp = &phpbind->dummy_len;
1455 			*piecep = OCI_ONE_PIECE;
1456 			*rcodepp = &phpbind->retcode;
1457 			*indpp = &phpbind->indicator;
1458 		}
1459 		retval = OCI_CONTINUE;
1460 	} else if (Z_TYPE_P(val) == IS_OBJECT) {
1461 		zval *tmp;
1462 		php_oci_descriptor *desc;
1463 
1464 		if (!phpbind->descriptor) {
1465 			return OCI_ERROR;
1466 		}
1467 
1468 		/* Do not use the cached lob size if the descriptor is an
1469 		 * out-bind as the contents would have been changed for in/out
1470 		 * binds (Bug #46994).
1471 		 */
1472 		if ((tmp = zend_hash_str_find(Z_OBJPROP_P(val), "descriptor", sizeof("descriptor")-1)) == NULL) {
1473 			php_error_docref(NULL, E_WARNING, "Unable to find object outbind descriptor property");
1474 			return OCI_ERROR;
1475 		}
1476 		PHP_OCI_ZVAL_TO_DESCRIPTOR_EX(tmp, desc);
1477 		desc->lob_size = -1;	/* force OCI8 to update cached size */
1478 
1479 		*alenpp = &phpbind->dummy_len;
1480 		*bufpp = phpbind->descriptor;
1481 		*piecep = OCI_ONE_PIECE;
1482 		*rcodepp = &phpbind->retcode;
1483 		*indpp = &phpbind->indicator;
1484 		retval = OCI_CONTINUE;
1485 	} else {
1486 		convert_to_string(val);
1487 		zval_ptr_dtor(val);
1488 
1489 		{
1490 			char *p = ecalloc(1, PHP_OCI_PIECE_SIZE);
1491 			ZVAL_STRINGL(val, p, PHP_OCI_PIECE_SIZE);
1492 			efree(p);
1493 		}
1494 #if 0
1495 		Z_STRLEN_P(val) = PHP_OCI_PIECE_SIZE; /* 64K-1 is max XXX */
1496 		Z_STRVAL_P(val) = ecalloc(1, Z_STRLEN_P(val) + 1);
1497 		/* XXX is this right? */
1498 		ZVAL_STRINGL(val, NULL, Z_STRLEN(val) + 1);
1499 #endif
1500 
1501 		/* XXX we assume that zend-zval len has 4 bytes */
1502 		*alenpp = (ub4*) &Z_STRLEN_P(val);
1503 		*bufpp = Z_STRVAL_P(val);
1504 		*piecep = OCI_ONE_PIECE;
1505 		*rcodepp = &phpbind->retcode;
1506 		*indpp = &phpbind->indicator;
1507 		retval = OCI_CONTINUE;
1508 	}
1509 
1510 	return retval;
1511 }
1512 /* }}} */
1513 
1514 /* {{{ php_oci_statement_get_column_helper()
1515  Helper function to get column by name and index */
php_oci_statement_get_column_helper(INTERNAL_FUNCTION_PARAMETERS,int need_data)1516 php_oci_out_column *php_oci_statement_get_column_helper(INTERNAL_FUNCTION_PARAMETERS, int need_data)
1517 {
1518 	zval *z_statement, *column_index;
1519 	php_oci_statement *statement;
1520 	php_oci_out_column *column;
1521 
1522 	ZEND_PARSE_PARAMETERS_START(2, 2)
1523 		Z_PARAM_RESOURCE(z_statement)
1524 		Z_PARAM_ZVAL(column_index)
1525 	ZEND_PARSE_PARAMETERS_END_EX(return NULL);
1526 
1527 	statement = (php_oci_statement *) zend_fetch_resource_ex(z_statement, "oci8 statement", le_statement);
1528 
1529 	if (!statement) {
1530 		return NULL;
1531 	}
1532 
1533 	if (need_data && !statement->has_data) {
1534 		return NULL;
1535 	}
1536 
1537 	if (Z_TYPE_P(column_index) == IS_STRING) {
1538 		column = php_oci_statement_get_column(statement, -1, Z_STRVAL_P(column_index), (int) Z_STRLEN_P(column_index));
1539 		if (!column) {
1540 			php_error_docref(NULL, E_WARNING, "Invalid column name \"%s\"", Z_STRVAL_P(column_index));
1541 			return NULL;
1542 		}
1543 	} else {
1544 		zend_long tmp;
1545 
1546 		tmp = zval_get_long(column_index);
1547 		column = php_oci_statement_get_column(statement, tmp, NULL, 0);
1548 		if (!column) {
1549 			php_error_docref(NULL, E_WARNING, "Invalid column index \"" ZEND_LONG_FMT "\"", tmp);
1550 			return NULL;
1551 		}
1552 	}
1553 	return column;
1554 }
1555 /* }}} */
1556 
1557 /* {{{ php_oci_statement_get_type()
1558  Return type of the statement */
php_oci_statement_get_type(php_oci_statement * statement,ub2 * type)1559 int php_oci_statement_get_type(php_oci_statement *statement, ub2 *type)
1560 {
1561 	ub2 statement_type;
1562 	sword errstatus;
1563 
1564 	*type = 0;
1565 
1566 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub2 *)&statement_type, (ub4 *)0, OCI_ATTR_STMT_TYPE, statement->err));
1567 
1568 	if (errstatus != OCI_SUCCESS) {
1569 		statement->errcode = php_oci_error(statement->err, errstatus);
1570 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1571 		return 1;
1572 	}
1573 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1574 	*type = statement_type;
1575 
1576 	return 0;
1577 }
1578 /* }}} */
1579 
1580 /* {{{ php_oci_statement_get_numrows()
1581  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)1582 int php_oci_statement_get_numrows(php_oci_statement *statement, ub4 *numrows)
1583 {
1584 	ub4 statement_numrows;
1585 	sword errstatus;
1586 
1587 	*numrows = 0;
1588 
1589 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub4 *)&statement_numrows, (ub4 *)0, OCI_ATTR_ROW_COUNT, statement->err));
1590 
1591 	if (errstatus != OCI_SUCCESS) {
1592 		statement->errcode = php_oci_error(statement->err, errstatus);
1593 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1594 		return 1;
1595 	}
1596 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1597 	*numrows = statement_numrows;
1598 
1599 	return 0;
1600 }
1601 /* }}} */
1602 
1603 /* {{{ php_oci_bind_array_by_name()
1604  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)1605 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)
1606 {
1607 	php_oci_bind *bind;
1608 	sword errstatus;
1609 	zend_string *zvtmp;
1610 	zval *val;
1611 
1612 	ZEND_ASSERT(Z_ISREF_P(var));
1613 	val = Z_REFVAL_P(var);
1614 	convert_to_array(val);
1615 	SEPARATE_ARRAY(val);
1616 
1617 	if (maxlength < -1) {
1618 		php_error_docref(NULL, E_WARNING, "Invalid max length value (" ZEND_LONG_FMT ")", maxlength);
1619 		return 1;
1620 	}
1621 
1622 	switch(type) {
1623 		case SQLT_NUM:
1624 		case SQLT_INT:
1625 		case SQLT_LNG:
1626 			bind = php_oci_bind_array_helper_number(val, max_table_length);
1627 			break;
1628 
1629 		case SQLT_FLT:
1630 			bind = php_oci_bind_array_helper_double(val, max_table_length);
1631 			break;
1632 
1633 		case SQLT_AFC:
1634 		case SQLT_CHR:
1635 		case SQLT_VCS:
1636 		case SQLT_AVC:
1637 		case SQLT_STR:
1638 		case SQLT_LVC:
1639 			if (maxlength == -1 && zend_hash_num_elements(Z_ARRVAL_P(val)) == 0) {
1640 				php_error_docref(NULL, E_WARNING, "You must provide max length value for empty arrays");
1641 				return 1;
1642 			}
1643 			bind = php_oci_bind_array_helper_string(val, max_table_length, maxlength);
1644 			break;
1645 		case SQLT_ODT:
1646 			bind = php_oci_bind_array_helper_date(val, max_table_length, statement->connection);
1647 			break;
1648 		default:
1649 			php_error_docref(NULL, E_WARNING, "Unknown or unsupported datatype given: " ZEND_LONG_FMT, type);
1650 			return 1;
1651 			break;
1652 	}
1653 
1654 	if (bind == NULL) {
1655 		/* failed to generate bind struct */
1656 		return 1;
1657 	}
1658 
1659 	bind->descriptor = NULL;
1660 	bind->statement = NULL;
1661 	bind->parent_statement = statement;
1662 	bind->bind = NULL;
1663 	ZVAL_COPY(&bind->val, var);
1664 	bind->array.type = type;
1665 	bind->indicator = 0;  		/* not used for array binds */
1666 	bind->type = 0; 			/* not used for array binds */
1667 
1668 	PHP_OCI_CALL_RETURN(errstatus,
1669 							OCIBindByName,
1670 							(
1671 								statement->stmt,
1672 								(OCIBind **)&bind->bind,
1673 								statement->err,
1674 								(text *)name,
1675 								(sb4) name_len,
1676 								(dvoid *) bind->array.elements,
1677 								(sb4) bind->array.max_length,
1678 								(ub2)type,
1679 								(dvoid *)bind->array.indicators,
1680 								(ub2 *)bind->array.element_lengths,
1681 								(ub2 *)0, /* bindp->array.retcodes, */
1682 								(ub4) max_table_length,
1683 								(ub4 *) &(bind->array.current_length),
1684 								(ub4) OCI_DEFAULT
1685 							)
1686 						);
1687 
1688 
1689 	if (errstatus != OCI_SUCCESS) {
1690 		if (bind->array.elements) {
1691 			efree(bind->array.elements);
1692 		}
1693 
1694 		if (bind->array.element_lengths) {
1695 			efree(bind->array.element_lengths);
1696 		}
1697 
1698 		if (bind->array.indicators) {
1699 			efree(bind->array.indicators);
1700 		}
1701 
1702 		zval_ptr_dtor(&bind->val);
1703 
1704 		efree(bind);
1705 
1706 		statement->errcode = php_oci_error(statement->err, errstatus);
1707 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1708 		return 1;
1709 	}
1710 
1711 	if (!statement->binds) {
1712 		ALLOC_HASHTABLE(statement->binds);
1713 		zend_hash_init(statement->binds, 13, NULL, php_oci_bind_hash_dtor, 0);
1714 	}
1715 
1716 	zvtmp = zend_string_init(name, name_len, 0);
1717 	zend_hash_update_ptr(statement->binds, zvtmp, bind);
1718 #if PHP_VERSION_ID < 70300
1719 	zend_string_release(zvtmp);
1720 #else
1721 	zend_string_release_ex(zvtmp, 0);
1722 #endif
1723 
1724 	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1725 	return 0;
1726 }
1727 /* }}} */
1728 
1729 /* {{{ php_oci_bind_array_helper_string()
1730  Bind arrays to PL/SQL types */
php_oci_bind_array_helper_string(zval * var,zend_long max_table_length,zend_long maxlength)1731 php_oci_bind *php_oci_bind_array_helper_string(zval *var, zend_long max_table_length, zend_long maxlength)
1732 {
1733 	php_oci_bind *bind;
1734 	ub4 i;
1735 	HashTable *hash;
1736 	zval *entry;
1737 
1738 	SEPARATE_ARRAY(var); /* TODO: may be use new HashTable iteration and prevent inplace modification */
1739 	hash = Z_ARRVAL_P(var);
1740 
1741 	if (maxlength == -1) {
1742 		zend_hash_internal_pointer_reset(hash);
1743 		while ((entry = zend_hash_get_current_data(hash)) != NULL) {
1744 			convert_to_string_ex(entry);
1745 
1746 			if (maxlength == -1 || Z_STRLEN_P(entry) > (size_t) maxlength) {
1747 				maxlength = Z_STRLEN_P(entry) + 1;
1748 			}
1749 
1750 			zend_hash_move_forward(hash);
1751 		}
1752 	}
1753 
1754 	bind = emalloc(sizeof(php_oci_bind));
1755 	ZVAL_UNDEF(&bind->val);
1756 	bind->array.elements		= (text *)safe_emalloc(max_table_length * (maxlength + 1), sizeof(text), 0);
1757 	memset(bind->array.elements, 0, max_table_length * (maxlength + 1) * sizeof(text));
1758 	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1759 	bind->array.old_length		= bind->array.current_length;
1760 	bind->array.max_length		= (ub4) maxlength;
1761 	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1762 	memset(bind->array.element_lengths, 0, max_table_length*sizeof(ub2));
1763 	bind->array.indicators		= safe_emalloc(max_table_length, sizeof(sb2), 0);
1764 	memset(bind->array.indicators, 0, max_table_length*sizeof(sb2));
1765 
1766 	zend_hash_internal_pointer_reset(hash);
1767 
1768 	for (i = 0; i < bind->array.current_length; i++) {
1769 		if ((entry = zend_hash_get_current_data(hash)) != NULL) {
1770 			convert_to_string_ex(entry);
1771 			bind->array.element_lengths[i] = (ub2) Z_STRLEN_P(entry);
1772 			if (Z_STRLEN_P(entry) == 0) {
1773 				bind->array.indicators[i] = -1;
1774 			}
1775 			zend_hash_move_forward(hash);
1776 		} else {
1777 			break;
1778 		}
1779 	}
1780 
1781 	zend_hash_internal_pointer_reset(hash);
1782 	for (i = 0; i < max_table_length; i++) {
1783 		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1784 			int element_length;
1785 
1786 			convert_to_string_ex(entry);
1787 			element_length = ((size_t) maxlength > Z_STRLEN_P(entry)) ? (int) Z_STRLEN_P(entry) : (int) maxlength;
1788 
1789 			memcpy((text *)bind->array.elements + i*maxlength, Z_STRVAL_P(entry), element_length);
1790 			((text *)bind->array.elements)[i*maxlength + element_length] = '\0';
1791 
1792 			zend_hash_move_forward(hash);
1793 		} else {
1794 			((text *)bind->array.elements)[i*maxlength] = '\0';
1795 		}
1796 	}
1797 	zend_hash_internal_pointer_reset(hash);
1798 
1799 	return bind;
1800 }
1801 /* }}} */
1802 
1803 /* {{{ php_oci_bind_array_helper_number()
1804  Bind arrays to PL/SQL types */
php_oci_bind_array_helper_number(zval * var,zend_long max_table_length)1805 php_oci_bind *php_oci_bind_array_helper_number(zval *var, zend_long max_table_length)
1806 {
1807 	php_oci_bind *bind;
1808 	ub4 i;
1809 	HashTable *hash;
1810 	zval *entry;
1811 
1812 	SEPARATE_ARRAY(var); /* TODO: may be use new HashTable iteration and prevent inplace modification */
1813 	hash = Z_ARRVAL_P(var);
1814 
1815 	bind = emalloc(sizeof(php_oci_bind));
1816 	ZVAL_UNDEF(&bind->val);
1817 	bind->array.elements		= (oci_phpsized_int *)safe_emalloc(max_table_length, sizeof(oci_phpsized_int), 0);
1818 	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1819 	bind->array.old_length		= bind->array.current_length;
1820 	bind->array.max_length		= sizeof(oci_phpsized_int);
1821 	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1822 	memset(bind->array.element_lengths, 0, max_table_length * sizeof(ub2));
1823 	bind->array.indicators		= NULL;
1824 
1825 	zend_hash_internal_pointer_reset(hash);
1826 	for (i = 0; i < max_table_length; i++) {
1827 		if (i < bind->array.current_length) {
1828 			bind->array.element_lengths[i] = sizeof(oci_phpsized_int);
1829 		}
1830 		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1831 			convert_to_long_ex(entry);
1832 			((oci_phpsized_int *)bind->array.elements)[i] = (oci_phpsized_int) Z_LVAL_P(entry);
1833 			zend_hash_move_forward(hash);
1834 		} else {
1835 			((oci_phpsized_int *)bind->array.elements)[i] = 0;
1836 		}
1837 	}
1838 	zend_hash_internal_pointer_reset(hash);
1839 
1840 	return bind;
1841 }
1842 /* }}} */
1843 
1844 /* {{{ php_oci_bind_array_helper_double()
1845  Bind arrays to PL/SQL types */
php_oci_bind_array_helper_double(zval * var,zend_long max_table_length)1846 php_oci_bind *php_oci_bind_array_helper_double(zval *var, zend_long max_table_length)
1847 {
1848 	php_oci_bind *bind;
1849 	ub4 i;
1850 	HashTable *hash;
1851 	zval *entry;
1852 
1853 	SEPARATE_ARRAY(var); /* TODO: may be use new HashTable iteration and prevent inplace modification */
1854 	hash = Z_ARRVAL_P(var);
1855 
1856 	bind = emalloc(sizeof(php_oci_bind));
1857 	ZVAL_UNDEF(&bind->val);
1858 	bind->array.elements		= (double *)safe_emalloc(max_table_length, sizeof(double), 0);
1859 	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1860 	bind->array.old_length		= bind->array.current_length;
1861 	bind->array.max_length		= sizeof(double);
1862 	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1863 	memset(bind->array.element_lengths, 0, max_table_length * sizeof(ub2));
1864 	bind->array.indicators		= NULL;
1865 
1866 	zend_hash_internal_pointer_reset(hash);
1867 	for (i = 0; i < max_table_length; i++) {
1868 		if (i < bind->array.current_length) {
1869 			bind->array.element_lengths[i] = sizeof(double);
1870 		}
1871 		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1872 			convert_to_double_ex(entry);
1873 			((double *)bind->array.elements)[i] = (double) Z_DVAL_P(entry);
1874 			zend_hash_move_forward(hash);
1875 		} else {
1876 			((double *)bind->array.elements)[i] = 0;
1877 		}
1878 	}
1879 	zend_hash_internal_pointer_reset(hash);
1880 
1881 	return bind;
1882 }
1883 /* }}} */
1884 
1885 /* {{{ php_oci_bind_array_helper_date()
1886  Bind arrays to PL/SQL types */
php_oci_bind_array_helper_date(zval * var,zend_long max_table_length,php_oci_connection * connection)1887 php_oci_bind *php_oci_bind_array_helper_date(zval *var, zend_long max_table_length, php_oci_connection *connection)
1888 {
1889 	php_oci_bind *bind;
1890 	ub4 i;
1891 	HashTable *hash;
1892 	zval *entry;
1893 	sword errstatus;
1894 
1895 	SEPARATE_ARRAY(var); /* TODO: may be use new HashTable iteration and prevent inplace modification */
1896 	hash = Z_ARRVAL_P(var);
1897 
1898 	bind = emalloc(sizeof(php_oci_bind));
1899 	ZVAL_UNDEF(&bind->val);
1900 	bind->array.elements		= (OCIDate *)safe_emalloc(max_table_length, sizeof(OCIDate), 0);
1901 	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1902 	bind->array.old_length		= bind->array.current_length;
1903 	bind->array.max_length		= sizeof(OCIDate);
1904 	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1905 	memset(bind->array.element_lengths, 0, max_table_length * sizeof(ub2));
1906 	bind->array.indicators		= NULL;
1907 
1908 	zend_hash_internal_pointer_reset(hash);
1909 	for (i = 0; i < max_table_length; i++) {
1910 		OCIDate oci_date;
1911 		if (i < bind->array.current_length) {
1912 			bind->array.element_lengths[i] = sizeof(OCIDate);
1913 		}
1914 		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1915 
1916 			convert_to_string_ex(entry);
1917 			PHP_OCI_CALL_RETURN(errstatus, OCIDateFromText, (connection->err, (CONST text *)Z_STRVAL_P(entry), (ub4) Z_STRLEN_P(entry), NULL, 0, NULL, 0, &oci_date));
1918 
1919 			if (errstatus != OCI_SUCCESS) {
1920 				/* failed to convert string to date */
1921 				efree(bind->array.element_lengths);
1922 				efree(bind->array.elements);
1923 				efree(bind);
1924 				connection->errcode = php_oci_error(connection->err, errstatus);
1925 				PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1926 				return NULL;
1927 			}
1928 
1929 			((OCIDate *)bind->array.elements)[i] = oci_date;
1930 			zend_hash_move_forward(hash);
1931 		} else {
1932 			PHP_OCI_CALL_RETURN(errstatus, OCIDateFromText, (connection->err, (CONST text *)"01-JAN-00", sizeof("01-JAN-00")-1, NULL, 0, NULL, 0, &oci_date));
1933 
1934 			if (errstatus != OCI_SUCCESS) {
1935 				/* failed to convert string to date */
1936 				efree(bind->array.element_lengths);
1937 				efree(bind->array.elements);
1938 				efree(bind);
1939 				connection->errcode = php_oci_error(connection->err, errstatus);
1940 				PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1941 				return NULL;
1942 			}
1943 
1944 			((OCIDate *)bind->array.elements)[i] = oci_date;
1945 		}
1946 		connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1947 	}
1948 	zend_hash_internal_pointer_reset(hash);
1949 
1950 	return bind;
1951 }
1952 /* }}} */
1953 
1954 #endif /* HAVE_OCI8 */
1955 
1956 /*
1957  * Local variables:
1958  * tab-width: 4
1959  * c-basic-offset: 4
1960  * End:
1961  * vim600: noet sw=4 ts=4 fdm=marker
1962  * vim<600: noet sw=4 ts=4
1963  */
1964