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  | Author: Wez Furlong <wez@php.net>                                    |
16  +----------------------------------------------------------------------+
17*/
18
19/* $Id$ */
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include "php.h"
26#include "php_ini.h"
27#include "ext/standard/info.h"
28#include "pdo/php_pdo.h"
29#include "pdo/php_pdo_driver.h"
30#include "php_pdo_oci.h"
31#include "php_pdo_oci_int.h"
32#include "Zend/zend_extensions.h"
33
34#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */
35
36#define STMT_CALL(name, params)                                         \
37    do {                                                                \
38        S->last_err = name params;                                      \
39        S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__); \
40        if (S->last_err) {                                              \
41            return 0;                                                   \
42        }                                                               \
43    } while(0)
44
45#define STMT_CALL_MSG(name, msg, params)                                \
46    do {                                                                \
47        S->last_err = name params;                                      \
48        S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, FALSE, __FILE__, __LINE__); \
49        if (S->last_err) {                                              \
50            return 0;                                                   \
51        }                                                               \
52    } while(0)
53
54static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob);
55
56#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob)             \
57    do                                                          \
58    {                                                           \
59        boolean isTempLOB;                                      \
60        OCILobIsTemporary(envhp, errhp, lob, &isTempLOB);       \
61        if (isTempLOB)                                          \
62            OCILobFreeTemporary(svchp, errhp, lob);             \
63    } while(0)
64
65static int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
66{
67    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
68    HashTable *BC = stmt->bound_columns;
69    HashTable *BP = stmt->bound_params;
70
71    int i;
72
73    if (S->stmt) {
74        /* cancel server side resources for the statement if we didn't
75         * fetch it all */
76        OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
77
78        /* free the handle */
79        OCIHandleFree(S->stmt, OCI_HTYPE_STMT);
80        S->stmt = NULL;
81    }
82    if (S->err) {
83        OCIHandleFree(S->err, OCI_HTYPE_ERROR);
84        S->err = NULL;
85    }
86
87    /* need to ensure these go away now */
88    if (BC) {
89        zend_hash_destroy(BC);
90        FREE_HASHTABLE(stmt->bound_columns);
91        stmt->bound_columns = NULL;
92    }
93
94    if (BP) {
95        zend_hash_destroy(BP);
96        FREE_HASHTABLE(stmt->bound_params);
97        stmt->bound_params = NULL;
98    }
99
100    if (S->einfo.errmsg) {
101        pefree(S->einfo.errmsg, stmt->dbh->is_persistent);
102        S->einfo.errmsg = NULL;
103    }
104
105    if (S->cols) {
106        for (i = 0; i < stmt->column_count; i++) {
107            if (S->cols[i].data) {
108                switch (S->cols[i].dtype) {
109                    case SQLT_BLOB:
110                    case SQLT_CLOB:
111                        OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err,
112                            (OCILobLocator *) S->cols[i].data);
113                        OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB);
114                        break;
115                    default:
116                        efree(S->cols[i].data);
117                }
118            }
119        }
120        efree(S->cols);
121        S->cols = NULL;
122    }
123    efree(S);
124
125    stmt->driver_data = NULL;
126
127    return 1;
128} /* }}} */
129
130static int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
131{
132    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
133    ub4 rowcount;
134    b4 mode;
135
136    if (!S->stmt_type) {
137        STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_STMT_TYPE",
138                (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err));
139    }
140
141    if (stmt->executed) {
142        /* ensure that we cancel the cursor from a previous fetch */
143        OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
144    }
145
146#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */
147    if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) {
148        mode = OCI_STMT_SCROLLABLE_READONLY;
149    } else
150#endif
151    if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) {
152        mode = OCI_COMMIT_ON_SUCCESS;
153    } else {
154        mode = OCI_DEFAULT;
155    }
156
157    STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err,
158                (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL,
159                mode));
160
161    if (!stmt->executed) {
162        ub4 colcount;
163        /* do first-time-only definition of bind/mapping stuff */
164
165        /* how many columns do we have ? */
166        STMT_CALL_MSG(OCIAttrGet, "ATTR_PARAM_COUNT",
167                (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err));
168
169        stmt->column_count = (int)colcount;
170
171        if (S->cols) {
172            int i;
173            for (i = 0; i < stmt->column_count; i++) {
174                if (S->cols[i].data) {
175                    switch (S->cols[i].dtype) {
176                        case SQLT_BLOB:
177                        case SQLT_CLOB:
178                            /* do nothing */
179                            break;
180                        default:
181                            efree(S->cols[i].data);
182                    }
183                }
184            }
185            efree(S->cols);
186        }
187
188        S->cols = ecalloc(colcount, sizeof(pdo_oci_column));
189    }
190
191    STMT_CALL_MSG(OCIAttrGet, "ATTR_ROW_COUNT",
192            (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err));
193    stmt->row_count = (long)rowcount;
194
195    return 1;
196} /* }}} */
197
198static sb4 oci_bind_input_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */
199{
200    struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
201    pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
202    zval *parameter;
203
204    if (!param) {
205        php_error_docref(NULL, E_WARNING, "param is NULL in oci_bind_input_cb; this should not happen");
206        return OCI_ERROR;
207    }
208
209    *indpp = &P->indicator;
210
211    if (Z_ISREF(param->parameter))
212        parameter = Z_REFVAL(param->parameter);
213    else
214        parameter = &param->parameter;
215
216    if (P->thing) {
217        *bufpp = P->thing;
218        *alenp = sizeof(void*);
219    } else if (ZVAL_IS_NULL(parameter)) {
220        /* insert a NULL value into the column */
221        P->indicator = -1; /* NULL */
222        *bufpp = 0;
223        *alenp = -1;
224    } else if (!P->thing) {
225        /* regular string bind */
226        convert_to_string(parameter);
227        *bufpp = Z_STRVAL_P(parameter);
228        *alenp = (ub4) Z_STRLEN_P(parameter);
229    }
230
231    *piecep = OCI_ONE_PIECE;
232    return OCI_CONTINUE;
233} /* }}} */
234
235static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) /* {{{ */
236{
237    struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
238    pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
239    zval *parameter;
240
241    if (!param) {
242        php_error_docref(NULL, E_WARNING, "param is NULL in oci_bind_output_cb; this should not happen");
243        return OCI_ERROR;
244    }
245
246    if (Z_ISREF(param->parameter))
247        parameter = Z_REFVAL(param->parameter);
248    else
249        parameter = &param->parameter;
250
251    if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
252        P->actual_len = sizeof(OCILobLocator*);
253        *bufpp = P->thing;
254        *alenpp = &P->actual_len;
255        *piecep = OCI_ONE_PIECE;
256        *rcodepp = &P->retcode;
257        *indpp = &P->indicator;
258        return OCI_CONTINUE;
259    }
260
261    if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) {
262        return OCI_CONTINUE;
263    }
264
265    convert_to_string(parameter);
266    zval_dtor(parameter);
267
268    Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1);
269    P->used_for_output = 1;
270
271    P->actual_len = (ub4) Z_STRLEN_P(parameter);
272    *alenpp = &P->actual_len;
273    *bufpp = (Z_STR_P(parameter))->val;
274    *piecep = OCI_ONE_PIECE;
275    *rcodepp = &P->retcode;
276    *indpp = &P->indicator;
277
278    return OCI_CONTINUE;
279} /* }}} */
280
281static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) /* {{{ */
282{
283    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
284
285    /* we're only interested in parameters for prepared SQL right now */
286    if (param->is_param) {
287        pdo_oci_bound_param *P;
288        sb4 value_sz = -1;
289        zval *parameter;
290
291        if (Z_ISREF(param->parameter))
292            parameter = Z_REFVAL(param->parameter);
293        else
294            parameter = &param->parameter;
295
296        P = (pdo_oci_bound_param*)param->driver_data;
297
298        switch (event_type) {
299            case PDO_PARAM_EVT_FETCH_PRE:
300            case PDO_PARAM_EVT_FETCH_POST:
301            case PDO_PARAM_EVT_NORMALIZE:
302                /* Do nothing */
303                break;
304
305            case PDO_PARAM_EVT_FREE:
306                P = param->driver_data;
307                if (P && P->thing) {
308                    OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
309                    OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
310                    P->thing = NULL;
311                    efree(P);
312                }
313                else if (P) {
314                    efree(P);
315                }
316                break;
317
318            case PDO_PARAM_EVT_ALLOC:
319                P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param));
320                param->driver_data = P;
321
322                /* figure out what we're doing */
323                switch (PDO_PARAM_TYPE(param->param_type)) {
324                    case PDO_PARAM_STMT:
325                        return 0;
326
327                    case PDO_PARAM_LOB:
328                        /* P->thing is now an OCILobLocator * */
329                        P->oci_type = SQLT_BLOB;
330                        value_sz = (sb4) sizeof(OCILobLocator*);
331                        break;
332
333                    case PDO_PARAM_STR:
334                    default:
335                        P->oci_type = SQLT_CHR;
336                        value_sz = (sb4) param->max_value_len;
337                        if (param->max_value_len == 0) {
338                            value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */
339                        }
340
341                }
342
343                if (param->name) {
344                    STMT_CALL(OCIBindByName, (S->stmt,
345                            &P->bind, S->err, (text*)param->name->val,
346                            (sb4) param->name->len, 0, value_sz, P->oci_type,
347                            &P->indicator, 0, &P->retcode, 0, 0,
348                            OCI_DATA_AT_EXEC));
349                } else {
350                    STMT_CALL(OCIBindByPos, (S->stmt,
351                            &P->bind, S->err, ((ub4)param->paramno)+1,
352                            0, value_sz, P->oci_type,
353                            &P->indicator, 0, &P->retcode, 0, 0,
354                            OCI_DATA_AT_EXEC));
355                }
356
357                STMT_CALL(OCIBindDynamic, (P->bind,
358                            S->err,
359                            param, oci_bind_input_cb,
360                            param, oci_bind_output_cb));
361
362                return 1;
363
364            case PDO_PARAM_EVT_EXEC_PRE:
365                P->indicator = 0;
366                P->used_for_output = 0;
367                if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
368                    ub4 empty = 0;
369                    STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL));
370                    STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err));
371                    S->have_blobs = 1;
372                }
373                return 1;
374
375            case PDO_PARAM_EVT_EXEC_POST:
376                /* fixup stuff set in motion in oci_bind_output_cb */
377                if (P->used_for_output) {
378                    if (P->indicator == -1) {
379                        /* set up a NULL value */
380                        if (Z_TYPE_P(parameter) == IS_STRING) {
381                            /* OCI likes to stick non-terminated strings in things */
382                            *Z_STRVAL_P(parameter) = '\0';
383                        }
384                        zval_dtor(parameter);
385                        ZVAL_UNDEF(parameter);
386                    } else if (Z_TYPE_P(parameter) == IS_STRING) {
387                        Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1);
388                    }
389                } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) {
390                    php_stream *stm;
391
392                    if (Z_TYPE_P(parameter) == IS_NULL) {
393                        /* if the param is NULL, then we assume that they
394                         * wanted to bind a lob locator into it from the query
395                         * */
396
397                        stm = oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator*)P->thing);
398                        if (stm) {
399                            OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
400                            php_stream_to_zval(stm, parameter);
401                        }
402                    } else {
403                        /* we're a LOB being used for insert; transfer the data now */
404                        size_t n;
405                        ub4 amt, offset = 1;
406                        char *consume;
407
408                        php_stream_from_zval_no_verify(stm, parameter);
409                        if (stm) {
410                            OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
411                            do {
412                                char buf[8192];
413                                n = php_stream_read(stm, buf, sizeof(buf));
414                                if ((int)n <= 0) {
415                                    break;
416                                }
417                                consume = buf;
418                                do {
419                                    amt = (ub4) n;
420                                    OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
421                                            &amt, offset, consume, (ub4) n,
422                                            OCI_ONE_PIECE,
423                                            NULL, NULL, 0, SQLCS_IMPLICIT);
424                                    offset += amt;
425                                    n -= amt;
426                                    consume += amt;
427                                } while (n);
428                            } while (1);
429                            OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
430                            OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0);
431                        } else if (Z_TYPE_P(parameter) == IS_STRING) {
432                            /* stick the string into the LOB */
433                            consume = Z_STRVAL_P(parameter);
434                            n = Z_STRLEN_P(parameter);
435                            if (n) {
436                                OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
437                                while (n) {
438                                    amt = (ub4) n;
439                                    OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
440                                            &amt, offset, consume, (ub4) n,
441                                            OCI_ONE_PIECE,
442                                            NULL, NULL, 0, SQLCS_IMPLICIT);
443                                    consume += amt;
444                                    n -= amt;
445                                }
446                                OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
447                            }
448                        }
449                        OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);
450                        OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
451                        P->thing = NULL;
452                    }
453                }
454
455                return 1;
456        }
457    }
458
459    return 1;
460} /* }}} */
461
462static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */
463{
464#if HAVE_OCISTMTFETCH2
465    ub4 ociori;
466#endif
467    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
468
469#if HAVE_OCISTMTFETCH2
470    switch (ori) {
471        case PDO_FETCH_ORI_NEXT:    ociori = OCI_FETCH_NEXT; break;
472        case PDO_FETCH_ORI_PRIOR:   ociori = OCI_FETCH_PRIOR; break;
473        case PDO_FETCH_ORI_FIRST:   ociori = OCI_FETCH_FIRST; break;
474        case PDO_FETCH_ORI_LAST:    ociori = OCI_FETCH_LAST; break;
475        case PDO_FETCH_ORI_ABS:     ociori = OCI_FETCH_ABSOLUTE; break;
476        case PDO_FETCH_ORI_REL:     ociori = OCI_FETCH_RELATIVE; break;
477    }
478    S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT);
479#else
480    S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
481#endif
482
483    if (S->last_err == OCI_NO_DATA) {
484        /* no (more) data */
485        return 0;
486    }
487
488    if (S->last_err == OCI_NEED_DATA) {
489        oci_stmt_error("OCI_NEED_DATA");
490        return 0;
491    }
492
493    if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) {
494        return 1;
495    }
496
497    oci_stmt_error("OCIStmtFetch");
498
499    return 0;
500} /* }}} */
501
502static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp,
503        ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp)
504{
505    pdo_oci_column *col = (pdo_oci_column*)octxp;
506
507    switch (col->dtype) {
508        case SQLT_BLOB:
509        case SQLT_CLOB:
510            *piecep = OCI_ONE_PIECE;
511            *bufpp = col->data;
512            *alenpp = &col->datalen;
513            *indpp = (dvoid *)&col->indicator;
514            break;
515
516        default:
517            php_error_docref(NULL, E_WARNING,
518                "unhandled datatype in oci_define_callback; this should not happen");
519            return OCI_ERROR;
520    }
521
522    return OCI_CONTINUE;
523}
524
525static int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */
526{
527    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
528    OCIParam *param = NULL;
529    text *colname;
530    ub2 dtype, data_size, scale, precis;
531    ub4 namelen;
532    struct pdo_column_data *col = &stmt->columns[colno];
533    zend_bool dyn = FALSE;
534
535    /* describe the column */
536    STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)&param, colno+1));
537
538    /* what type ? */
539    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
540            (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
541
542    /* how big ? */
543    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE",
544            (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));
545
546    /* scale ? */
547    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
548            (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
549
550    /* precision ? */
551    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
552            (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
553
554    /* name ? */
555    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME",
556            (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));
557
558    col->precision = scale;
559    col->maxlen = data_size;
560    col->name = zend_string_init((char *)colname, namelen, 0);
561
562    S->cols[colno].dtype = dtype;
563
564    /* how much room do we need to store the field */
565    switch (dtype) {
566        case SQLT_LBI:
567        case SQLT_LNG:
568            if (dtype == SQLT_LBI) {
569                dtype = SQLT_BIN;
570            } else {
571                dtype = SQLT_CHR;
572            }
573            S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */
574            S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
575            col->param_type = PDO_PARAM_STR;
576            break;
577
578        case SQLT_BLOB:
579        case SQLT_CLOB:
580            col->param_type = PDO_PARAM_LOB;
581            STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL));
582            S->cols[colno].datalen = sizeof(OCILobLocator*);
583            dyn = TRUE;
584            break;
585
586        case SQLT_BIN:
587        default:
588            if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD
589#ifdef SQLT_TIMESTAMP
590                    || dtype == SQLT_TIMESTAMP
591#endif
592#ifdef SQLT_TIMESTAMP_TZ
593                    || dtype == SQLT_TIMESTAMP_TZ
594#endif
595                    ) {
596                /* should be big enough for most date formats and numbers */
597                S->cols[colno].datalen = 512;
598#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)
599            } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) {
600                S->cols[colno].datalen = 1024;
601#endif
602            } else {
603                S->cols[colno].datalen = (ub4) col->maxlen;
604            }
605            if (dtype == SQLT_BIN) {
606                S->cols[colno].datalen *= 3;
607            }
608            S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
609            dtype = SQLT_CHR;
610
611            /* returning data as a string */
612            col->param_type = PDO_PARAM_STR;
613    }
614
615    STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1,
616                S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator,
617                &S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT));
618
619    if (dyn) {
620        STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno],
621                oci_define_callback));
622    }
623
624    return 1;
625} /* }}} */
626
627struct _oci_lob_env {
628    OCISvcCtx *svc;
629    OCIError  *err;
630};
631typedef struct _oci_lob_env oci_lob_env;
632
633struct oci_lob_self {
634    zval dbh;
635    pdo_stmt_t *stmt;
636    pdo_oci_stmt *S;
637    OCILobLocator *lob;
638    oci_lob_env   *E;
639    ub4 offset;
640};
641
642static size_t oci_blob_write(php_stream *stream, const char *buf, size_t count)
643{
644    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
645    ub4 amt;
646    sword r;
647
648    amt = (ub4) count;
649    r = OCILobWrite(self->E->svc, self->E->err, self->lob,
650        &amt, self->offset, (char*)buf, (ub4) count,
651        OCI_ONE_PIECE,
652        NULL, NULL, 0, SQLCS_IMPLICIT);
653
654    if (r != OCI_SUCCESS) {
655        return (size_t)-1;
656    }
657
658    self->offset += amt;
659    return amt;
660}
661
662static size_t oci_blob_read(php_stream *stream, char *buf, size_t count)
663{
664    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
665    ub4 amt;
666    sword r;
667
668    amt = (ub4) count;
669    r = OCILobRead(self->E->svc, self->E->err, self->lob,
670        &amt, self->offset, buf, (ub4) count,
671        NULL, NULL, 0, SQLCS_IMPLICIT);
672
673    if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
674        return (size_t)-1;
675    }
676
677    self->offset += amt;
678    if (amt < count) {
679        stream->eof = 1;
680    }
681    return amt;
682}
683
684static int oci_blob_close(php_stream *stream, int close_handle)
685{
686    struct oci_lob_self *self = (struct oci_lob_self *)stream->abstract;
687    pdo_stmt_t *stmt = self->stmt;
688
689    if (close_handle) {
690        zend_object *obj = &stmt->std;
691
692        OCILobClose(self->E->svc, self->E->err, self->lob);
693        zval_ptr_dtor(&self->dbh);
694        GC_REFCOUNT(obj)--;
695        efree(self->E);
696        efree(self);
697    }
698
699    /* php_pdo_free_statement(stmt); */
700    return 0;
701}
702
703static int oci_blob_flush(php_stream *stream)
704{
705    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
706    OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0);
707    return 0;
708}
709
710static int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
711{
712    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
713
714    if (offset >= PDO_OCI_LOBMAXSIZE) {
715        return -1;
716    } else {
717        self->offset = (ub4) offset + 1;  /* Oracle LOBS are 1-based, but PHP is 0-based */
718        return 0;
719    }
720}
721
722static php_stream_ops oci_blob_stream_ops = {
723    oci_blob_write,
724    oci_blob_read,
725    oci_blob_close,
726    oci_blob_flush,
727    "pdo_oci blob stream",
728    oci_blob_seek,
729    NULL,
730    NULL,
731    NULL
732};
733
734static php_stream *oci_create_lob_stream(zval *dbh, pdo_stmt_t *stmt, OCILobLocator *lob)
735{
736    php_stream *stm;
737    struct oci_lob_self *self = ecalloc(1, sizeof(*self));
738
739    ZVAL_COPY_VALUE(&self->dbh, dbh);
740    self->lob = lob;
741    self->offset = 1; /* 1-based */
742    self->stmt = stmt;
743    self->S = (pdo_oci_stmt*)stmt->driver_data;
744    self->E = ecalloc(1, sizeof(oci_lob_env));
745    self->E->svc = self->S->H->svc;
746    self->E->err = self->S->err;
747
748    stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
749
750    if (stm) {
751        zend_object *obj;
752        obj = &stmt->std;
753        Z_ADDREF(self->dbh);
754        GC_REFCOUNT(obj)++;
755        return stm;
756    }
757
758    efree(self);
759    return NULL;
760}
761
762static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) /* {{{ */
763{
764    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
765    pdo_oci_column *C = &S->cols[colno];
766
767    /* check the indicator to ensure that the data is intact */
768    if (C->indicator == -1) {
769        /* A NULL value */
770        *ptr = NULL;
771        *len = 0;
772        return 1;
773    } else if (C->indicator == 0) {
774        /* it was stored perfectly */
775
776        if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) {
777            if (C->data) {
778                *ptr = (char*)oci_create_lob_stream(&stmt->database_object_handle, stmt, (OCILobLocator*)C->data);
779                OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY);
780            }
781            *len = (size_t) 0;
782            return *ptr ? 1 : 0;
783        }
784
785        *ptr = C->data;
786        *len = (size_t) C->fetched_len;
787        return 1;
788    } else {
789        /* it was truncated */
790        php_error_docref(NULL, E_WARNING, "column %d data was too large for buffer and was truncated to fit it", colno);
791
792        *ptr = C->data;
793        *len = (size_t) C->fetched_len;
794        return 1;
795    }
796} /* }}} */
797
798struct pdo_stmt_methods oci_stmt_methods = {
799    oci_stmt_dtor,
800    oci_stmt_execute,
801    oci_stmt_fetch,
802    oci_stmt_describe,
803    oci_stmt_get_col,
804    oci_stmt_param_hook
805};
806
807/*
808 * Local variables:
809 * tab-width: 4
810 * c-basic-offset: 4
811 * End:
812 * vim600: noet sw=4 ts=4 fdm=marker
813 * vim<600: noet sw=4 ts=4
814 */
815