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