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