1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
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 */
46php_oci_statement *php_oci_statement_create(php_oci_connection *connection, char *query, int query_len)
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);
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 = ecalloc(1, query_len + 1);
95		memcpy(statement->last_query, query, query_len);
96		statement->last_query_len = query_len;
97	}
98	else {
99		statement->last_query = NULL;
100		statement->last_query_len = 0;
101	}
102
103	statement->connection = connection;
104	statement->has_data = 0;
105	statement->has_descr = 0;
106	statement->parent_stmtid = 0;
107	statement->impres_child_stmt = NULL;
108	statement->impres_count = 0;
109	statement->impres_flag = PHP_OCI_IMPRES_UNKNOWN;  /* may or may not have Implicit Result Set children */
110	++GC_REFCOUNT(statement->connection->id);
111
112	if (OCI_G(default_prefetch) >= 0) {
113		php_oci_statement_set_prefetch(statement, (ub4)OCI_G(default_prefetch));
114	} else {
115		php_oci_statement_set_prefetch(statement, (ub4)100); /* semi-arbitrary, "sensible default" */
116	}
117
118	PHP_OCI_REGISTER_RESOURCE(statement, le_statement);
119
120	OCI_G(num_statements)++;
121
122	return statement;
123}
124/* }}} */
125
126/* {{{ php_oci_get_implicit_resultset()
127   Fetch implicit result set statement resource */
128php_oci_statement *php_oci_get_implicit_resultset(php_oci_statement *statement)
129{
130#if (OCI_MAJOR_VERSION < 12)
131	php_error_docref(NULL, E_WARNING, "Implicit results are available in Oracle Database 12c onwards");
132	return NULL;
133#else
134	void *result;
135	ub4   rtype;
136	php_oci_statement *statement2;  /* implicit result set statement handle */
137	sword errstatus;
138
139	PHP_OCI_CALL_RETURN(errstatus, OCIStmtGetNextResult, (statement->stmt, statement->err, &result, &rtype, OCI_DEFAULT));
140	if (errstatus == OCI_NO_DATA) {
141		return NULL;
142	}
143
144	if (rtype != OCI_RESULT_TYPE_SELECT) {
145		/* Only OCI_RESULT_TYPE_SELECT is supported by Oracle DB 12cR1 */
146		php_error_docref(NULL, E_WARNING, "Unexpected implicit result type returned from Oracle Database");
147		return NULL;
148	} else {
149		statement2 = ecalloc(1,sizeof(php_oci_statement));
150
151		PHP_OCI_CALL(OCIHandleAlloc, (statement->connection->env, (dvoid **)&(statement2->err), OCI_HTYPE_ERROR, 0, NULL));
152		statement2->stmt = (OCIStmt *)result;
153		statement2->parent_stmtid = statement->id;
154		statement2->impres_child_stmt = NULL;
155		statement2->impres_count = 0;
156		statement2->impres_flag = PHP_OCI_IMPRES_IS_CHILD;
157		statement2->connection = statement->connection;
158		statement2->errcode = 0;
159		statement2->last_query = NULL;
160		statement2->last_query_len = 0;
161		statement2->columns = NULL;
162		statement2->binds = NULL;
163		statement2->defines = NULL;
164		statement2->ncolumns = 0;
165		statement2->executed = 0;
166		statement2->has_data = 0;
167		statement2->has_descr = 0;
168		statement2->stmttype = 0;
169
170		GC_REFCOUNT(statement->id)++;
171		GC_REFCOUNT(statement2->connection->id)++;
172
173		php_oci_statement_set_prefetch(statement2, statement->prefetch_count);
174
175		PHP_OCI_REGISTER_RESOURCE(statement2, le_statement);
176
177		OCI_G(num_statements)++;
178
179		return statement2;
180	}
181#endif /* OCI_MAJOR_VERSION < 12 */
182}
183/* }}} */
184
185/* {{{ php_oci_statement_set_prefetch()
186 Set prefetch buffer size for the statement */
187int php_oci_statement_set_prefetch(php_oci_statement *statement, ub4 prefetch )
188{
189	sword errstatus;
190
191	if (prefetch > 20000) {
192		prefetch = 20000;		/* keep it somewhat sane */
193	}
194
195	PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (statement->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, statement->err));
196
197	if (errstatus != OCI_SUCCESS) {
198		statement->errcode = php_oci_error(statement->err, errstatus);
199		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
200		statement->prefetch_count = 0;
201		return 1;
202	}
203	statement->prefetch_count = prefetch;
204	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
205	return 0;
206}
207/* }}} */
208
209/* {{{ php_oci_cleanup_pre_fetch()
210   Helper function to cleanup ref-cursors and descriptors from the previous row */
211int php_oci_cleanup_pre_fetch(zval *data)
212{
213	php_oci_out_column *outcol = (php_oci_out_column*) Z_PTR_P(data);
214
215	if (!outcol->is_descr && !outcol->is_cursor)
216		return ZEND_HASH_APPLY_KEEP;
217
218	switch(outcol->data_type) {
219		case SQLT_CLOB:
220		case SQLT_BLOB:
221		case SQLT_RDD:
222		case SQLT_BFILE:
223			if (outcol->descid) {
224				zend_list_delete(outcol->descid);
225				outcol->descid = 0;
226			}
227			break;
228		case SQLT_RSET:
229			if (outcol->stmtid) {
230				zend_list_delete(outcol->stmtid);
231				outcol->stmtid = 0;
232				outcol->nested_statement = NULL;
233			}
234			break;
235		default:
236			break;
237	}
238	return ZEND_HASH_APPLY_KEEP;
239
240}
241/* }}} */
242
243/* {{{ php_oci_statement_fetch()
244 Fetch a row from the statement */
245int php_oci_statement_fetch(php_oci_statement *statement, ub4 nrows)
246{
247	int i;
248	void *handlepp;
249	ub4 typep, iterp, idxp;
250	ub1 in_outp, piecep;
251	zend_bool piecewisecols = 0;
252	php_oci_out_column *column;
253	sword errstatus;
254
255	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
256
257	if (statement->has_descr && statement->columns) {
258		zend_hash_apply(statement->columns, php_oci_cleanup_pre_fetch);
259    }
260
261	PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, OCI_DEFAULT));
262
263	if (errstatus == OCI_NO_DATA || nrows == 0) {
264		if (statement->last_query == NULL) {
265			/* reset define-list for refcursors */
266			if (statement->columns) {
267				zend_hash_destroy(statement->columns);
268				efree(statement->columns);
269				statement->columns = NULL;
270				statement->ncolumns = 0;
271			}
272			statement->executed = 0;
273		}
274
275		statement->has_data = 0;
276
277		if (nrows == 0) {
278			/* this is exactly what we requested */
279			return 0;
280		}
281		return 1;
282	}
283
284	/* reset length for all piecewise columns */
285	for (i = 0; i < statement->ncolumns; i++) {
286		column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
287		if (column && column->piecewise) {
288			column->retlen4 = 0;
289			piecewisecols = 1;
290		}
291	}
292
293	while (errstatus == OCI_NEED_DATA) {
294		if (piecewisecols) {
295			PHP_OCI_CALL_RETURN(errstatus,
296				OCIStmtGetPieceInfo,
297				   (
298					statement->stmt,
299					statement->err,
300					&handlepp,
301					&typep,
302					&in_outp,
303					&iterp,
304					&idxp,
305					&piecep
306				   )
307			);
308
309			/* scan through our columns for a piecewise column with a matching handle */
310			for (i = 0; i < statement->ncolumns; i++) {
311				column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
312				if (column && column->piecewise && handlepp == column->oci_define)   {
313					if (!column->data) {
314						column->data = (text *) ecalloc(1, PHP_OCI_PIECE_SIZE + 1);
315					} else {
316						column->data = erealloc(column->data, column->retlen4 + PHP_OCI_PIECE_SIZE + 1);
317					}
318					column->cb_retlen = PHP_OCI_PIECE_SIZE;
319
320					/* and instruct fetch to fetch waiting piece into our buffer */
321					PHP_OCI_CALL(OCIStmtSetPieceInfo,
322						   (
323							(void *) column->oci_define,
324							OCI_HTYPE_DEFINE,
325							statement->err,
326							((char*)column->data) + column->retlen4,
327							&(column->cb_retlen),
328							piecep,
329							&column->indicator,
330							&column->retcode
331						   )
332					);
333				}
334			}
335		}
336
337		PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, OCI_DEFAULT));
338
339		if (piecewisecols) {
340			for (i = 0; i < statement->ncolumns; i++) {
341				column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
342				if (column && column->piecewise && handlepp == column->oci_define)	{
343					column->retlen4 += column->cb_retlen;
344				}
345			}
346		}
347	}
348
349	if (errstatus == OCI_SUCCESS_WITH_INFO || errstatus == OCI_SUCCESS) {
350		statement->has_data = 1;
351
352		/* do the stuff needed for OCIDefineByName */
353		for (i = 0; i < statement->ncolumns; i++) {
354			column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
355			if (column == NULL) {
356				continue;
357			}
358
359			if (!column->define) {
360				continue;
361			}
362
363			zval_dtor(column->define->zval);
364			php_oci_column_to_zval(column, column->define->zval, 0);
365		}
366
367		return 0;
368	}
369
370	statement->errcode = php_oci_error(statement->err, errstatus);
371	PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
372
373	statement->has_data = 0;
374
375	return 1;
376}
377/* }}} */
378
379/* {{{ php_oci_statement_get_column()
380 Get column from the result set */
381php_oci_out_column *php_oci_statement_get_column(php_oci_statement *statement, zend_long column_index, char *column_name, int column_name_len)
382{
383	php_oci_out_column *column = NULL;
384	int i;
385
386	if (statement->columns == NULL) { /* we release the columns at the end of a fetch */
387		return NULL;
388	}
389
390	if (column_name) {
391		for (i = 0; i < statement->ncolumns; i++) {
392			column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
393			if (column == NULL) {
394				continue;
395			} else if (((int) column->name_len == column_name_len) && (!strncmp(column->name, column_name, column_name_len))) {
396				return column;
397			}
398		}
399	} else if (column_index != -1) {
400		if ((column = zend_hash_index_find_ptr(statement->columns, column_index)) == NULL) {
401			return NULL;
402		}
403		return column;
404	}
405
406	return NULL;
407}
408/* }}} */
409
410/* {{{ php_oci_define_callback() */
411sb4 php_oci_define_callback(dvoid *ctx, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcpp)
412{
413	php_oci_out_column *outcol = (php_oci_out_column *)ctx;
414
415	if (!outcol) {
416
417		php_error_docref(NULL, 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);
426				if (!nested_stmt) {
427					return OCI_ERROR;
428				}
429				nested_stmt->parent_stmtid = outcol->statement->id;
430				++GC_REFCOUNT(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);
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 */
480int php_oci_statement_execute(php_oci_statement *statement, ub4 mode)
481{
482	php_oci_out_column *outcol;
483	OCIParam *param = NULL;
484	text *colname;
485	ub4 counter;
486	ub2 define_type;
487	ub4 iters;
488	ub4 colcount;
489	ub2 dynamic;
490	dvoid *buf;
491	sword errstatus;
492
493	switch (mode) {
494		case OCI_COMMIT_ON_SUCCESS:
495		case OCI_DESCRIBE_ONLY:
496		case OCI_DEFAULT:
497			/* only these are allowed */
498#ifdef HAVE_OCI8_DTRACE
499			if (DTRACE_OCI8_EXECUTE_MODE_ENABLED()) {
500				DTRACE_OCI8_EXECUTE_MODE(statement->connection, statement->connection->client_id, statement, mode);
501			}
502#endif /* HAVE_OCI8_DTRACE */
503			break;
504		default:
505			php_error_docref(NULL, E_WARNING, "Invalid execute mode given: %d", mode);
506			return 1;
507			break;
508	}
509
510	if (!statement->stmttype) {
511		/* get statement type */
512		PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub2 *)&statement->stmttype, (ub4 *)0, OCI_ATTR_STMT_TYPE, statement->err));
513
514		if (errstatus != OCI_SUCCESS) {
515			statement->errcode = php_oci_error(statement->err, errstatus);
516			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
517			return 1;
518		} else {
519			statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
520		}
521	}
522
523	if (statement->stmttype == OCI_STMT_SELECT) {
524		iters = 0;
525	} else {
526		iters = 1;
527	}
528
529	if (statement->last_query) { /* Don't execute REFCURSORS or Implicit Result Set handles */
530
531		if (statement->binds) {
532			int result = 0;
533			zend_hash_apply_with_argument(statement->binds, php_oci_bind_pre_exec, (void *)&result);
534			if (result) {
535				return 1;
536			}
537		}
538
539		/* execute statement */
540		PHP_OCI_CALL_RETURN(errstatus, OCIStmtExecute, (statement->connection->svc, statement->stmt, statement->err, iters, 0, NULL, NULL, mode));
541
542		if (errstatus != OCI_SUCCESS) {
543			statement->errcode = php_oci_error(statement->err, errstatus);
544			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
545			return 1;
546		}
547
548		if (statement->binds) {
549			zend_hash_apply(statement->binds, php_oci_bind_post_exec);
550		}
551
552		if (mode & OCI_COMMIT_ON_SUCCESS) {
553			/* No need to rollback on disconnect */
554			statement->connection->rb_on_disconnect = 0;
555		} else if (statement->stmttype != OCI_STMT_SELECT) {
556			/* Assume some uncommitted DML occurred */
557			statement->connection->rb_on_disconnect = 1;
558		}
559		/* else for SELECT with OCI_NO_AUTO_COMMIT, leave
560		 * "rb_on_disconnect" at its previous value.  SELECT can't
561		 * initiate uncommitted DML. (An AUTONOMOUS_TRANSACTION in
562		 * invoked PL/SQL must explicitly rollback/commit else the
563		 * SELECT fails).
564		 */
565
566		statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
567	}
568
569	if (statement->stmttype == OCI_STMT_SELECT && statement->executed == 0) {
570		/* we only need to do the define step is this very statement is executed the first time! */
571		statement->executed = 1;
572
573		ALLOC_HASHTABLE(statement->columns);
574		zend_hash_init(statement->columns, 13, NULL, php_oci_column_hash_dtor, 0);
575
576		counter = 1;
577
578		/* get number of columns */
579		PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (dvoid *)&colcount, (ub4 *)0, OCI_ATTR_PARAM_COUNT, statement->err));
580
581		if (errstatus != OCI_SUCCESS) {
582			statement->errcode = php_oci_error(statement->err, errstatus);
583			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
584			return 1;
585		}
586
587		statement->ncolumns = colcount;
588
589		for (counter = 1; counter <= colcount; counter++) {
590			outcol = (php_oci_out_column *) ecalloc(1, sizeof(php_oci_out_column));
591
592			if ((outcol = zend_hash_index_update_ptr(statement->columns, counter, outcol)) == NULL) {
593				FREE_HASHTABLE(statement->columns);
594				/* out of memory */
595				return 1;
596			}
597
598			/* get column */
599			PHP_OCI_CALL_RETURN(errstatus, OCIParamGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, statement->err, (dvoid**)&param, counter));
600
601			if (errstatus != OCI_SUCCESS) {
602				statement->errcode = php_oci_error(statement->err, errstatus);
603				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
604				return 1;
605			}
606
607			/* get column datatype */
608			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->data_type, (ub4 *)0, OCI_ATTR_DATA_TYPE, statement->err));
609
610			if (errstatus != OCI_SUCCESS) {
611				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
612				statement->errcode = php_oci_error(statement->err, errstatus);
613				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
614				return 1;
615			}
616
617			/* get character set form  */
618			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->charset_form, (ub4 *)0, OCI_ATTR_CHARSET_FORM, statement->err));
619
620			if (errstatus != OCI_SUCCESS) {
621				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
622				statement->errcode = php_oci_error(statement->err, errstatus);
623				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
624				return 1;
625			}
626
627			/* get character set id	 */
628			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->charset_id, (ub4 *)0, OCI_ATTR_CHARSET_ID, statement->err));
629
630			if (errstatus != OCI_SUCCESS) {
631				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
632				statement->errcode = php_oci_error(statement->err, errstatus);
633				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
634				return 1;
635			}
636
637			/* get size of the column */
638			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->data_size, (dvoid *)0, OCI_ATTR_DATA_SIZE, statement->err));
639
640			if (errstatus != OCI_SUCCESS) {
641				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
642				statement->errcode = php_oci_error(statement->err, errstatus);
643				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
644				return 1;
645			}
646
647			outcol->storage_size4 = outcol->data_size;
648			outcol->retlen = outcol->data_size;
649
650			/* get scale of the column */
651			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->scale, (dvoid *)0, OCI_ATTR_SCALE, statement->err));
652
653			if (errstatus != OCI_SUCCESS) {
654				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
655				statement->errcode = php_oci_error(statement->err, errstatus);
656				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
657				return 1;
658			}
659
660			/* get precision of the column */
661			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&outcol->precision, (dvoid *)0, OCI_ATTR_PRECISION, statement->err));
662
663			if (errstatus != OCI_SUCCESS) {
664				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
665				statement->errcode = php_oci_error(statement->err, errstatus);
666				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
667				return 1;
668			}
669
670			/* get name of the column */
671			PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)param, OCI_DTYPE_PARAM, (dvoid **)&colname, (ub4 *)&outcol->name_len, (ub4)OCI_ATTR_NAME, statement->err));
672
673			if (errstatus != OCI_SUCCESS) {
674				PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
675				statement->errcode = php_oci_error(statement->err, errstatus);
676				PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
677				return 1;
678			}
679			PHP_OCI_CALL(OCIDescriptorFree, (param, OCI_DTYPE_PARAM));
680
681			outcol->name = ecalloc(1, outcol->name_len + 1);
682			memcpy(outcol->name, colname, outcol->name_len);
683
684			/* find a user-set define */
685			if (statement->defines) {
686				if ((outcol->define = zend_hash_str_find_ptr(statement->defines, outcol->name, outcol->name_len)) != NULL) {
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);
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);
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 */
850int php_oci_statement_cancel(php_oci_statement *statement)
851{
852	return php_oci_statement_fetch(statement, 0);
853}
854/* }}} */
855
856/* {{{ php_oci_statement_free()
857 Destroy statement handle and free associated resources */
858void php_oci_statement_free(php_oci_statement *statement)
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->binds) {
879		zend_hash_destroy(statement->binds);
880		efree(statement->binds);
881	}
882
883	if (statement->defines) {
884		zend_hash_destroy(statement->defines);
885		efree(statement->defines);
886	}
887
888	if (statement->columns) {
889		zend_hash_destroy(statement->columns);
890		efree(statement->columns);
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 */
906int php_oci_bind_pre_exec(zval *data, void *result)
907{
908	php_oci_bind *bind = (php_oci_bind *) Z_PTR_P(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, 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, 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, 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 */
965int php_oci_bind_post_exec(zval *data)
966{
967	php_oci_bind *bind = (php_oci_bind *) Z_PTR_P(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_STR_P(bind->zval) = zend_string_extend(Z_STR_P(bind->zval), Z_STRLEN_P(bind->zval)+1, 0);
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 = NULL;
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 < (int) bind->array.current_length; i++) {
1001					if ((i < (int) bind->array.old_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
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 < (int) bind->array.current_length; i++) {
1012					if ((i < (int) bind->array.old_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
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 < (int) 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 < (int) bind->array.old_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
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);
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);
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);
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);
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 < (int) 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 < (int) bind->array.old_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1065						zval_dtor(entry);
1066						ZVAL_STRINGL(entry, (char *)(((text *)bind->array.elements)+i*bind->array.max_length), curr_element_length);
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);
1070					}
1071				}
1072				break;
1073		}
1074	} else if ((Z_TYPE_P(bind->zval) == IS_TRUE) || (Z_TYPE_P(bind->zval) == IS_FALSE)) {
1075		if (Z_LVAL_P(bind->zval) == 0)
1076			ZVAL_BOOL(bind->zval, FALSE);
1077		else if (Z_LVAL_P(bind->zval) == 1)
1078			ZVAL_BOOL(bind->zval, TRUE);
1079	}
1080
1081	return 0;
1082}
1083/* }}} */
1084
1085/* {{{ php_oci_bind_by_name()
1086 Bind zval to the given placeholder */
1087int php_oci_bind_by_name(php_oci_statement *statement, char *name, size_t name_len, zval *var, zend_long maxlength, ub2 type)
1088{
1089	php_oci_collection *bind_collection = NULL;
1090	php_oci_descriptor *bind_descriptor = NULL;
1091	php_oci_statement  *bind_statement	= NULL;
1092	dvoid *oci_desc					= NULL;
1093	/* dvoid *php_oci_collection		   = NULL; */
1094	OCIStmt *oci_stmt				= NULL;
1095	dvoid *bind_data				= NULL;
1096	php_oci_bind *old_bind, *bindp;
1097	int mode = OCI_DATA_AT_EXEC;
1098	sb4 value_sz = -1;
1099	sword errstatus;
1100
1101	switch (type) {
1102		case SQLT_NTY:
1103		{
1104			zval *tmp;
1105
1106			if (Z_TYPE_P(var) != IS_OBJECT || (tmp = zend_hash_str_find(Z_OBJPROP_P(var), "collection", sizeof("collection")-1)) == NULL) {
1107				php_error_docref(NULL, E_WARNING, "Unable to find collection property");
1108				return 1;
1109			}
1110
1111			PHP_OCI_ZVAL_TO_COLLECTION_EX(tmp, bind_collection);
1112			value_sz = sizeof(void*);
1113			mode = OCI_DEFAULT;
1114
1115			if (!bind_collection->collection) {
1116				return 1;
1117			}
1118		}
1119			break;
1120		case SQLT_BFILEE:
1121		case SQLT_CFILEE:
1122		case SQLT_CLOB:
1123		case SQLT_BLOB:
1124		case SQLT_RDD:
1125		{
1126			zval *tmp;
1127
1128			if (Z_TYPE_P(var) != IS_OBJECT || (tmp = zend_hash_str_find(Z_OBJPROP_P(var), "descriptor", sizeof("descriptor")-1)) == NULL) {
1129				php_error_docref(NULL, E_WARNING, "Unable to find descriptor property");
1130				return 1;
1131			}
1132
1133			PHP_OCI_ZVAL_TO_DESCRIPTOR_EX(tmp, bind_descriptor);
1134
1135			value_sz = sizeof(void*);
1136
1137			oci_desc = bind_descriptor->descriptor;
1138
1139			if (!oci_desc) {
1140				return 1;
1141			}
1142		}
1143			break;
1144
1145		case SQLT_INT:
1146		case SQLT_NUM:
1147			if (Z_TYPE_P(var) == IS_RESOURCE || Z_TYPE_P(var) == IS_OBJECT) {
1148				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1149				return 1;
1150			}
1151			convert_to_long(var);
1152#if defined(OCI_MAJOR_VERSION) && (OCI_MAJOR_VERSION > 10) &&			\
1153	(defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64))
1154			bind_data = (ub8 *)&Z_LVAL_P(var);
1155			value_sz = sizeof(ub8);
1156#else
1157			bind_data = (ub4 *)&Z_LVAL_P(var);
1158			value_sz = sizeof(ub4);
1159#endif
1160			mode = OCI_DEFAULT;
1161			break;
1162
1163		case SQLT_LBI:
1164		case SQLT_BIN:
1165		case SQLT_LNG:
1166		case SQLT_AFC:
1167		case SQLT_CHR: /* SQLT_CHR is the default value when type was not specified */
1168			if (Z_TYPE_P(var) == IS_RESOURCE || Z_TYPE_P(var) == IS_OBJECT) {
1169				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1170				return 1;
1171			}
1172			if (Z_TYPE_P(var) != IS_NULL) {
1173				convert_to_string(var);
1174			}
1175			if ((maxlength == -1) || (maxlength == 0)) {
1176				if (type == SQLT_LNG) {
1177					value_sz = SB4MAXVAL;
1178				} else if (Z_TYPE_P(var) == IS_STRING) {
1179					value_sz = (sb4) Z_STRLEN_P(var);
1180				} else {
1181					value_sz = PHP_OCI_PIECE_SIZE;
1182				}
1183			} else {
1184				value_sz = (sb4) maxlength;
1185			}
1186			break;
1187
1188		case SQLT_RSET:
1189			if (Z_TYPE_P(var) != IS_RESOURCE) {
1190				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1191				return 1;
1192			}
1193			PHP_OCI_ZVAL_TO_STATEMENT_EX(var, bind_statement);
1194			value_sz = sizeof(void*);
1195
1196			oci_stmt = bind_statement->stmt;
1197
1198			if (!oci_stmt) {
1199				return 1;
1200			}
1201			break;
1202
1203#if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
1204		case SQLT_BOL:
1205			if (Z_TYPE_P(var) == IS_RESOURCE || Z_TYPE_P(var) == IS_OBJECT) {
1206				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1207				return 1;
1208			}
1209			convert_to_boolean(var);
1210			bind_data = (zend_long *)&Z_LVAL_P(var);
1211			if (Z_TYPE_P(var) == IS_TRUE)
1212				*(zend_long *)bind_data = 1;
1213			else if (Z_TYPE_P(var) == IS_FALSE)
1214				*(zend_long *)bind_data = 0;
1215			else {
1216				php_error_docref(NULL, E_WARNING, "Invalid variable used for bind");
1217				return 1;
1218			}
1219
1220			value_sz = sizeof(zend_long);
1221
1222			mode = OCI_DEFAULT;
1223			break;
1224#endif
1225
1226		default:
1227			php_error_docref(NULL, E_WARNING, "Unknown or unsupported datatype given: %d", (int)type);
1228			return 1;
1229			break;
1230	}
1231
1232	if (!statement->binds) {
1233		ALLOC_HASHTABLE(statement->binds);
1234		zend_hash_init(statement->binds, 13, NULL, php_oci_bind_hash_dtor, 0);
1235	}
1236
1237	if ((old_bind = zend_hash_str_find_ptr(statement->binds, name, name_len)) != NULL) {
1238		bindp = old_bind;
1239	} else {
1240		zend_string *zvtmp;
1241		zvtmp = zend_string_init(name, name_len, 0);
1242		bindp = (php_oci_bind *) ecalloc(1, sizeof(php_oci_bind));
1243		bindp = zend_hash_update_ptr(statement->binds, zvtmp, bindp);
1244		zend_string_release(zvtmp);
1245	}
1246
1247	bindp->descriptor = oci_desc;
1248	bindp->statement = oci_stmt;
1249	bindp->parent_statement = statement;
1250	bindp->zval = var;
1251	bindp->type = type;
1252	/* Storing max length set in OCIBindByName() to check it later in
1253	 * php_oci_bind_in_callback() function to avoid ORA-1406 error while
1254	 * executing OCIStmtExecute()
1255     */
1256	bindp->dummy_len = value_sz;
1257
1258	PHP_OCI_CALL_RETURN(errstatus,
1259		OCIBindByName,
1260		(
1261			statement->stmt,				 /* statement handle */
1262			(OCIBind **)&bindp->bind,		 /* bind hdl (will alloc) */
1263			statement->err,				  	 /* error handle */
1264			(text*) name,					 /* placeholder name */
1265			(sb4) name_len,					 /* placeholder length */
1266			(dvoid *)bind_data,				 /* in/out data */
1267			value_sz, /* PHP_OCI_MAX_DATA_SIZE, */ /* max size of input/output data */
1268			type,							 /* in/out data type */
1269			(dvoid *)&bindp->indicator,		 /* indicator (ignored) */
1270			(ub2 *)0,						 /* size array (ignored) */
1271			(ub2 *)&bindp->retcode,			 /* return code (ignored) */
1272			(ub4)0,							 /* maxarr_len (PL/SQL only?) */
1273			(ub4 *)0,						 /* actual array size (PL/SQL only?) */
1274			mode							 /* mode */
1275		)
1276	);
1277
1278	if (errstatus != OCI_SUCCESS) {
1279		statement->errcode = php_oci_error(statement->err, errstatus);
1280		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1281		return 1;
1282	}
1283
1284	if (mode == OCI_DATA_AT_EXEC) {
1285		PHP_OCI_CALL_RETURN(errstatus, OCIBindDynamic,
1286				(
1287				 bindp->bind,
1288				 statement->err,
1289				 (dvoid *)bindp,
1290				 php_oci_bind_in_callback,
1291				 (dvoid *)bindp,
1292				 php_oci_bind_out_callback
1293				)
1294		);
1295
1296		if (errstatus != OCI_SUCCESS) {
1297			statement->errcode = php_oci_error(statement->err, errstatus);
1298			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1299			return 1;
1300		}
1301	}
1302
1303	if (type == SQLT_NTY) {
1304		/* Bind object */
1305		PHP_OCI_CALL_RETURN(errstatus, OCIBindObject,
1306				(
1307				 bindp->bind,
1308				 statement->err,
1309				 bind_collection->tdo,
1310				 (dvoid **) &(bind_collection->collection),
1311				 (ub4 *) 0,
1312				 (dvoid **) 0,
1313				 (ub4 *) 0
1314				)
1315		);
1316
1317		if (errstatus) {
1318			statement->errcode = php_oci_error(statement->err, errstatus);
1319			PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1320			return 1;
1321		}
1322	}
1323
1324	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1325	return 0;
1326}
1327/* }}} */
1328
1329/* {{{ php_oci_bind_in_callback()
1330 Callback used when binding LOBs and VARCHARs */
1331sb4 php_oci_bind_in_callback(
1332					dvoid *ictxp,	  /* context pointer */
1333					OCIBind *bindp,	  /* bind handle */
1334					ub4 iter,		  /* 0-based execute iteration value */
1335					ub4 index,		  /* index of current array for PL/SQL or row index for SQL */
1336					dvoid **bufpp,	  /* pointer to data */
1337					ub4 *alenp,		  /* size after value/piece has been read */
1338					ub1 *piecep,	  /* which piece */
1339					dvoid **indpp)	  /* indicator value */
1340{
1341	php_oci_bind *phpbind;
1342	zval *val;
1343
1344	if (!(phpbind=(php_oci_bind *)ictxp) || !(val = phpbind->zval)) {
1345		php_error_docref(NULL, E_WARNING, "Invalid phpbind pointer value");
1346		return OCI_ERROR;
1347	}
1348
1349	if (Z_ISNULL_P(val)) {
1350		/* we're going to insert a NULL column */
1351		phpbind->indicator = -1;
1352		*bufpp = 0;
1353		*alenp = -1;
1354		*indpp = (dvoid *)&phpbind->indicator;
1355	} else	if ((phpbind->descriptor == 0) && (phpbind->statement == 0)) {
1356		/* "normal string bind */
1357		convert_to_string(val);
1358
1359		*bufpp = Z_STRVAL_P(val);
1360		*alenp = (ub4) Z_STRLEN_P(val);
1361		/*
1362		 * bind_char_1: If max length set in OCIBindByName is less than the
1363		 * actual length of input string, then we have to overwrite alenp with
1364		 * max value set in OCIBindByName (dummy_len). Or else it will cause
1365		 * ORA-1406 error in OCIStmtExecute
1366		 */
1367		if ((phpbind->dummy_len > 0) && (phpbind->dummy_len < *alenp))
1368			*alenp = phpbind->dummy_len;
1369		*indpp = (dvoid *)&phpbind->indicator;
1370	} else if (phpbind->statement != 0) {
1371		/* RSET */
1372		*bufpp = phpbind->statement;
1373		*alenp = -1;		/* seems to be allright */
1374		*indpp = (dvoid *)&phpbind->indicator;
1375	} else {
1376		/* descriptor bind */
1377		*bufpp = phpbind->descriptor;
1378		*alenp = -1;		/* seems to be allright */
1379		*indpp = (dvoid *)&phpbind->indicator;
1380	}
1381
1382	*piecep = OCI_ONE_PIECE; /* pass all data in one go */
1383
1384	return OCI_CONTINUE;
1385}
1386/* }}} */
1387
1388/* {{{ php_oci_bind_out_callback()
1389 Callback used when binding LOBs and VARCHARs */
1390sb4 php_oci_bind_out_callback(
1391					dvoid *octxp,	   /* context pointer */
1392					OCIBind *bindp,	   /* bind handle */
1393					ub4 iter,		   /* 0-based execute iteration value */
1394					ub4 index,		   /* index of current array for PL/SQL or row index for SQL */
1395					dvoid **bufpp,	   /* pointer to data */
1396					ub4 **alenpp,	   /* size after value/piece has been read */
1397					ub1 *piecep,	   /* which piece */
1398					dvoid **indpp,	   /* indicator value */
1399					ub2 **rcodepp)	   /* return code */
1400{
1401	php_oci_bind *phpbind;
1402	zval *val;
1403	sb4 retval = OCI_ERROR;
1404
1405	if (!(phpbind=(php_oci_bind *)octxp) || !(val = phpbind->zval)) {
1406		php_error_docref(NULL, E_WARNING, "Invalid phpbind pointer value");
1407		return retval;
1408	}
1409
1410	if (Z_TYPE_P(val) == IS_RESOURCE) {
1411		/* Processing for ref-cursor out binds */
1412		if (phpbind->statement != NULL) {
1413			*bufpp = phpbind->statement;
1414			*alenpp = &phpbind->dummy_len;
1415			*piecep = OCI_ONE_PIECE;
1416			*rcodepp = &phpbind->retcode;
1417			*indpp = &phpbind->indicator;
1418		}
1419		retval = OCI_CONTINUE;
1420	} else if (Z_TYPE_P(val) == IS_OBJECT) {
1421		zval *tmp;
1422		php_oci_descriptor *desc;
1423
1424		if (!phpbind->descriptor) {
1425			return OCI_ERROR;
1426		}
1427
1428		/* Do not use the cached lob size if the descriptor is an
1429		 * out-bind as the contents would have been changed for in/out
1430		 * binds (Bug #46994).
1431		 */
1432		if ((tmp = zend_hash_str_find(Z_OBJPROP_P(val), "descriptor", sizeof("descriptor")-1)) == NULL) {
1433			php_error_docref(NULL, E_WARNING, "Unable to find object outbind descriptor property");
1434			return OCI_ERROR;
1435		}
1436		PHP_OCI_ZVAL_TO_DESCRIPTOR_EX(tmp, desc);
1437		desc->lob_size = -1;	/* force OCI8 to update cached size */
1438
1439		*alenpp = &phpbind->dummy_len;
1440		*bufpp = phpbind->descriptor;
1441		*piecep = OCI_ONE_PIECE;
1442		*rcodepp = &phpbind->retcode;
1443		*indpp = &phpbind->indicator;
1444		retval = OCI_CONTINUE;
1445	} else {
1446		convert_to_string(val);
1447		zval_dtor(val);
1448
1449		{
1450			char *p = ecalloc(1, PHP_OCI_PIECE_SIZE);
1451			ZVAL_STRINGL(val, p, PHP_OCI_PIECE_SIZE);
1452			efree(p);
1453		}
1454#if 0
1455		Z_STRLEN_P(val) = PHP_OCI_PIECE_SIZE; /* 64K-1 is max XXX */
1456		Z_STRVAL_P(val) = ecalloc(1, Z_STRLEN_P(val) + 1);
1457		/* XXX is this right? */
1458		ZVAL_STRINGL(val, NULL, Z_STRLEN(phpbind->zval) + 1);
1459#endif
1460
1461		/* XXX we assume that zend-zval len has 4 bytes */
1462		*alenpp = (ub4*) &Z_STRLEN_P(phpbind->zval);
1463		*bufpp = Z_STRVAL_P(phpbind->zval);
1464		*piecep = OCI_ONE_PIECE;
1465		*rcodepp = &phpbind->retcode;
1466		*indpp = &phpbind->indicator;
1467		retval = OCI_CONTINUE;
1468	}
1469
1470	return retval;
1471}
1472/* }}} */
1473
1474/* {{{ php_oci_statement_get_column_helper()
1475 Helper function to get column by name and index */
1476php_oci_out_column *php_oci_statement_get_column_helper(INTERNAL_FUNCTION_PARAMETERS, int need_data)
1477{
1478	zval *z_statement, *column_index;
1479	php_oci_statement *statement;
1480	php_oci_out_column *column;
1481
1482	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz", &z_statement, &column_index) == FAILURE) {
1483		return NULL;
1484	}
1485
1486	statement = (php_oci_statement *) zend_fetch_resource_ex(z_statement, "oci8 statement", le_statement);
1487
1488	if (!statement) {
1489		return NULL;
1490	}
1491
1492	if (need_data && !statement->has_data) {
1493		return NULL;
1494	}
1495
1496	if (Z_TYPE_P(column_index) == IS_STRING) {
1497		column = php_oci_statement_get_column(statement, -1, Z_STRVAL_P(column_index), (int) Z_STRLEN_P(column_index));
1498		if (!column) {
1499			php_error_docref(NULL, E_WARNING, "Invalid column name \"%s\"", Z_STRVAL_P(column_index));
1500			return NULL;
1501		}
1502	} else {
1503		zval tmp;
1504		/* NB: for PHP4 compat only, it should be using 'Z' instead */
1505		tmp = *column_index;
1506		zval_copy_ctor(&tmp);
1507		convert_to_long(&tmp);
1508		column = php_oci_statement_get_column(statement, Z_LVAL(tmp), NULL, 0);
1509		if (!column) {
1510			php_error_docref(NULL, E_WARNING, "Invalid column index \"%pd\"", Z_LVAL(tmp));
1511			zval_dtor(&tmp);
1512			return NULL;
1513		}
1514		zval_dtor(&tmp);
1515	}
1516	return column;
1517}
1518/* }}} */
1519
1520/* {{{ php_oci_statement_get_type()
1521 Return type of the statement */
1522int php_oci_statement_get_type(php_oci_statement *statement, ub2 *type)
1523{
1524	ub2 statement_type;
1525	sword errstatus;
1526
1527	*type = 0;
1528
1529	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub2 *)&statement_type, (ub4 *)0, OCI_ATTR_STMT_TYPE, statement->err));
1530
1531	if (errstatus != OCI_SUCCESS) {
1532		statement->errcode = php_oci_error(statement->err, errstatus);
1533		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1534		return 1;
1535	}
1536	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1537	*type = statement_type;
1538
1539	return 0;
1540}
1541/* }}} */
1542
1543/* {{{ php_oci_statement_get_numrows()
1544 Get the number of rows fetched to the clientside (NOT the number of rows in the result set) */
1545int php_oci_statement_get_numrows(php_oci_statement *statement, ub4 *numrows)
1546{
1547	ub4 statement_numrows;
1548	sword errstatus;
1549
1550	*numrows = 0;
1551
1552	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub4 *)&statement_numrows, (ub4 *)0, OCI_ATTR_ROW_COUNT, statement->err));
1553
1554	if (errstatus != OCI_SUCCESS) {
1555		statement->errcode = php_oci_error(statement->err, errstatus);
1556		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1557		return 1;
1558	}
1559	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1560	*numrows = statement_numrows;
1561
1562	return 0;
1563}
1564/* }}} */
1565
1566/* {{{ php_oci_bind_array_by_name()
1567 Bind arrays to PL/SQL types */
1568int 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)
1569{
1570	php_oci_bind *bind, *bindp;
1571	sword errstatus;
1572	zend_string *zvtmp;
1573
1574	convert_to_array(var);
1575
1576	if (maxlength < -1) {
1577		php_error_docref(NULL, E_WARNING, "Invalid max length value (%pd)", maxlength);
1578		return 1;
1579	}
1580
1581	switch(type) {
1582		case SQLT_NUM:
1583		case SQLT_INT:
1584		case SQLT_LNG:
1585			bind = php_oci_bind_array_helper_number(var, max_table_length);
1586			break;
1587
1588		case SQLT_FLT:
1589			bind = php_oci_bind_array_helper_double(var, max_table_length);
1590			break;
1591
1592		case SQLT_AFC:
1593		case SQLT_CHR:
1594		case SQLT_VCS:
1595		case SQLT_AVC:
1596		case SQLT_STR:
1597		case SQLT_LVC:
1598			if (maxlength == -1 && zend_hash_num_elements(Z_ARRVAL_P(var)) == 0) {
1599				php_error_docref(NULL, E_WARNING, "You must provide max length value for empty arrays");
1600				return 1;
1601			}
1602			bind = php_oci_bind_array_helper_string(var, max_table_length, maxlength);
1603			break;
1604		case SQLT_ODT:
1605			bind = php_oci_bind_array_helper_date(var, max_table_length, statement->connection);
1606			break;
1607		default:
1608			php_error_docref(NULL, E_WARNING, "Unknown or unsupported datatype given: %pd", type);
1609			return 1;
1610			break;
1611	}
1612
1613	if (bind == NULL) {
1614		/* failed to generate bind struct */
1615		return 1;
1616	}
1617
1618	bind->descriptor = NULL;
1619	bind->statement = NULL;
1620	bind->parent_statement = statement;
1621	bind->bind = NULL;
1622	bind->zval = var;
1623	bind->array.type = type;
1624	bind->indicator = 0;  		/* not used for array binds */
1625	bind->type = 0; 			/* not used for array binds */
1626
1627	PHP_OCI_CALL_RETURN(errstatus,
1628							OCIBindByName,
1629							(
1630								statement->stmt,
1631								(OCIBind **)&bind->bind,
1632								statement->err,
1633								(text *)name,
1634								(sb4) name_len,
1635								(dvoid *) bind->array.elements,
1636								(sb4) bind->array.max_length,
1637								(ub2)type,
1638								(dvoid *)bind->array.indicators,
1639								(ub2 *)bind->array.element_lengths,
1640								(ub2 *)0, /* bindp->array.retcodes, */
1641								(ub4) max_table_length,
1642								(ub4 *) &(bind->array.current_length),
1643								(ub4) OCI_DEFAULT
1644							)
1645						);
1646
1647
1648	if (errstatus != OCI_SUCCESS) {
1649		if (bind->array.elements) {
1650			efree(bind->array.elements);
1651		}
1652
1653		if (bind->array.element_lengths) {
1654			efree(bind->array.element_lengths);
1655		}
1656
1657		if (bind->array.indicators) {
1658			efree(bind->array.indicators);
1659		}
1660
1661		efree(bind);
1662
1663		statement->errcode = php_oci_error(statement->err, errstatus);
1664		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
1665		return 1;
1666	}
1667
1668	if (!statement->binds) {
1669		ALLOC_HASHTABLE(statement->binds);
1670		zend_hash_init(statement->binds, 13, NULL, php_oci_bind_hash_dtor, 0);
1671	}
1672
1673	zvtmp = zend_string_init(name, name_len, 0);
1674	bindp = zend_hash_update_ptr(statement->binds, zvtmp, bind);
1675	zend_string_release(zvtmp);
1676
1677	statement->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1678	return 0;
1679}
1680/* }}} */
1681
1682/* {{{ php_oci_bind_array_helper_string()
1683 Bind arrays to PL/SQL types */
1684php_oci_bind *php_oci_bind_array_helper_string(zval *var, zend_long max_table_length, zend_long maxlength)
1685{
1686	php_oci_bind *bind;
1687	ub4 i;
1688	HashTable *hash;
1689	zval *entry;
1690
1691	hash = HASH_OF(var);
1692
1693	if (maxlength == -1) {
1694		zend_hash_internal_pointer_reset(hash);
1695		while ((entry = zend_hash_get_current_data(hash)) != NULL) {
1696			convert_to_string_ex(entry);
1697
1698			if (maxlength == -1 || Z_STRLEN_P(entry) > (size_t) maxlength) {
1699				maxlength = Z_STRLEN_P(entry) + 1;
1700			}
1701
1702			zend_hash_move_forward(hash);
1703		}
1704	}
1705
1706	bind = emalloc(sizeof(php_oci_bind));
1707	bind->array.elements		= (text *)safe_emalloc(max_table_length * (maxlength + 1), sizeof(text), 0);
1708	memset(bind->array.elements, 0, max_table_length * (maxlength + 1) * sizeof(text));
1709	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1710	bind->array.old_length		= bind->array.current_length;
1711	bind->array.max_length		= (ub4) maxlength;
1712	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1713	memset(bind->array.element_lengths, 0, max_table_length*sizeof(ub2));
1714	bind->array.indicators		= safe_emalloc(max_table_length, sizeof(sb2), 0);
1715	memset(bind->array.indicators, 0, max_table_length*sizeof(sb2));
1716
1717	zend_hash_internal_pointer_reset(hash);
1718
1719	for (i = 0; i < bind->array.current_length; i++) {
1720		if ((entry = zend_hash_get_current_data(hash)) != NULL) {
1721			convert_to_string_ex(entry);
1722			bind->array.element_lengths[i] = (ub2) Z_STRLEN_P(entry);
1723			if (Z_STRLEN_P(entry) == 0) {
1724				bind->array.indicators[i] = -1;
1725			}
1726			zend_hash_move_forward(hash);
1727		} else {
1728			break;
1729		}
1730	}
1731
1732	zend_hash_internal_pointer_reset(hash);
1733	for (i = 0; i < max_table_length; i++) {
1734		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1735			int element_length;
1736
1737			convert_to_string_ex(entry);
1738			element_length = ((size_t) maxlength > Z_STRLEN_P(entry)) ? (int) Z_STRLEN_P(entry) : (int) maxlength;
1739
1740			memcpy((text *)bind->array.elements + i*maxlength, Z_STRVAL_P(entry), element_length);
1741			((text *)bind->array.elements)[i*maxlength + element_length] = '\0';
1742
1743			zend_hash_move_forward(hash);
1744		} else {
1745			((text *)bind->array.elements)[i*maxlength] = '\0';
1746		}
1747	}
1748	zend_hash_internal_pointer_reset(hash);
1749
1750	return bind;
1751}
1752/* }}} */
1753
1754/* {{{ php_oci_bind_array_helper_number()
1755 Bind arrays to PL/SQL types */
1756php_oci_bind *php_oci_bind_array_helper_number(zval *var, zend_long max_table_length)
1757{
1758	php_oci_bind *bind;
1759	ub4 i;
1760	HashTable *hash;
1761	zval *entry;
1762
1763	hash = HASH_OF(var);
1764
1765	bind = emalloc(sizeof(php_oci_bind));
1766	bind->array.elements		= (ub4 *)safe_emalloc(max_table_length, sizeof(ub4), 0);
1767	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1768	bind->array.old_length		= bind->array.current_length;
1769	bind->array.max_length		= sizeof(ub4);
1770	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1771	memset(bind->array.element_lengths, 0, max_table_length * sizeof(ub2));
1772	bind->array.indicators		= NULL;
1773
1774	zend_hash_internal_pointer_reset(hash);
1775	for (i = 0; i < max_table_length; i++) {
1776		if (i < bind->array.current_length) {
1777			bind->array.element_lengths[i] = sizeof(ub4);
1778		}
1779		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1780			convert_to_long_ex(entry);
1781			((ub4 *)bind->array.elements)[i] = (ub4) Z_LVAL_P(entry);
1782			zend_hash_move_forward(hash);
1783		} else {
1784			((ub4 *)bind->array.elements)[i] = 0;
1785		}
1786	}
1787	zend_hash_internal_pointer_reset(hash);
1788
1789	return bind;
1790}
1791/* }}} */
1792
1793/* {{{ php_oci_bind_array_helper_double()
1794 Bind arrays to PL/SQL types */
1795php_oci_bind *php_oci_bind_array_helper_double(zval *var, zend_long max_table_length)
1796{
1797	php_oci_bind *bind;
1798	ub4 i;
1799	HashTable *hash;
1800	zval *entry;
1801
1802	hash = HASH_OF(var);
1803
1804	bind = emalloc(sizeof(php_oci_bind));
1805	bind->array.elements		= (double *)safe_emalloc(max_table_length, sizeof(double), 0);
1806	bind->array.current_length	= zend_hash_num_elements(Z_ARRVAL_P(var));
1807	bind->array.old_length		= bind->array.current_length;
1808	bind->array.max_length		= sizeof(double);
1809	bind->array.element_lengths	= safe_emalloc(max_table_length, sizeof(ub2), 0);
1810	memset(bind->array.element_lengths, 0, max_table_length * sizeof(ub2));
1811	bind->array.indicators		= NULL;
1812
1813	zend_hash_internal_pointer_reset(hash);
1814	for (i = 0; i < max_table_length; i++) {
1815		if (i < bind->array.current_length) {
1816			bind->array.element_lengths[i] = sizeof(double);
1817		}
1818		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1819			convert_to_double_ex(entry);
1820			((double *)bind->array.elements)[i] = (double) Z_DVAL_P(entry);
1821			zend_hash_move_forward(hash);
1822		} else {
1823			((double *)bind->array.elements)[i] = 0;
1824		}
1825	}
1826	zend_hash_internal_pointer_reset(hash);
1827
1828	return bind;
1829}
1830/* }}} */
1831
1832/* {{{ php_oci_bind_array_helper_date()
1833 Bind arrays to PL/SQL types */
1834php_oci_bind *php_oci_bind_array_helper_date(zval *var, zend_long max_table_length, php_oci_connection *connection)
1835{
1836	php_oci_bind *bind;
1837	ub4 i;
1838	HashTable *hash;
1839	zval *entry;
1840	sword errstatus;
1841
1842	hash = HASH_OF(var);
1843
1844	bind = emalloc(sizeof(php_oci_bind));
1845	bind->array.elements		= (OCIDate *)safe_emalloc(max_table_length, sizeof(OCIDate), 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(OCIDate);
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		OCIDate oci_date;
1856		if (i < bind->array.current_length) {
1857			bind->array.element_lengths[i] = sizeof(OCIDate);
1858		}
1859		if ((i < bind->array.current_length) && (entry = zend_hash_get_current_data(hash)) != NULL) {
1860
1861			convert_to_string_ex(entry);
1862			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));
1863
1864			if (errstatus != OCI_SUCCESS) {
1865				/* failed to convert string to date */
1866				efree(bind->array.element_lengths);
1867				efree(bind->array.elements);
1868				efree(bind);
1869				connection->errcode = php_oci_error(connection->err, errstatus);
1870				PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1871				return NULL;
1872			}
1873
1874			((OCIDate *)bind->array.elements)[i] = oci_date;
1875			zend_hash_move_forward(hash);
1876		} else {
1877			PHP_OCI_CALL_RETURN(errstatus, OCIDateFromText, (connection->err, (CONST text *)"01-JAN-00", sizeof("01-JAN-00")-1, NULL, 0, NULL, 0, &oci_date));
1878
1879			if (errstatus != OCI_SUCCESS) {
1880				/* failed to convert string to date */
1881				efree(bind->array.element_lengths);
1882				efree(bind->array.elements);
1883				efree(bind);
1884				connection->errcode = php_oci_error(connection->err, errstatus);
1885				PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1886				return NULL;
1887			}
1888
1889			((OCIDate *)bind->array.elements)[i] = oci_date;
1890		}
1891		connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1892	}
1893	zend_hash_internal_pointer_reset(hash);
1894
1895	return bind;
1896}
1897/* }}} */
1898
1899#endif /* HAVE_OCI8 */
1900
1901/*
1902 * Local variables:
1903 * tab-width: 4
1904 * c-basic-offset: 4
1905 * End:
1906 * vim600: noet sw=4 ts=4 fdm=marker
1907 * vim<600: noet sw=4 ts=4
1908 */
1909