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