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