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