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