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