1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2013 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__ TSRMLS_CC); \
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__ TSRMLS_CC); \
49        if (S->last_err) {                                              \
50            return 0;                                                   \
51        }                                                               \
52    } while(0)
53
54static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob TSRMLS_DC);
55
56static int oci_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
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                        /* do nothing */
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 TSRMLS_DC) /* {{{ */
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    TSRMLS_FETCH();
192
193    if (!param || !param->parameter) {
194        php_error_docref(NULL TSRMLS_CC, 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 (P->thing) {
201        *bufpp = P->thing;
202        *alenp = sizeof(void*);
203    } else if (ZVAL_IS_NULL(param->parameter)) {
204        /* insert a NULL value into the column */
205        P->indicator = -1; /* NULL */
206        *bufpp = 0;
207        *alenp = -1;
208    } else if (!P->thing) {
209        /* regular string bind */
210        convert_to_string(param->parameter);
211        *bufpp = Z_STRVAL_P(param->parameter);
212        *alenp = Z_STRLEN_P(param->parameter);
213    }
214
215    *piecep = OCI_ONE_PIECE;
216    return OCI_CONTINUE;
217} /* }}} */
218
219static sb4 oci_bind_output_cb(dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) /* {{{ */
220{
221    struct pdo_bound_param_data *param = (struct pdo_bound_param_data*)ctx;
222    pdo_oci_bound_param *P = (pdo_oci_bound_param*)param->driver_data;
223    TSRMLS_FETCH();
224
225    if (!param || !param->parameter) {
226        php_error_docref(NULL TSRMLS_CC, E_WARNING, "param is NULL in oci_bind_output_cb; this should not happen");
227        return OCI_ERROR;
228    }
229
230    if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
231        P->actual_len = sizeof(OCILobLocator*);
232        *bufpp = P->thing;
233        *alenpp = &P->actual_len;
234        *piecep = OCI_ONE_PIECE;
235        *rcodepp = &P->retcode;
236        *indpp = &P->indicator;
237        return OCI_CONTINUE;
238    }
239
240    if (Z_TYPE_P(param->parameter) == IS_OBJECT || Z_TYPE_P(param->parameter) == IS_RESOURCE) {
241        return OCI_CONTINUE;
242    }
243
244    convert_to_string(param->parameter);
245    zval_dtor(param->parameter);
246
247    Z_STRLEN_P(param->parameter) = param->max_value_len;
248    Z_STRVAL_P(param->parameter) = ecalloc(1, Z_STRLEN_P(param->parameter)+1);
249    P->used_for_output = 1;
250
251    P->actual_len = Z_STRLEN_P(param->parameter);
252    *alenpp = &P->actual_len;
253    *bufpp = Z_STRVAL_P(param->parameter);
254    *piecep = OCI_ONE_PIECE;
255    *rcodepp = &P->retcode;
256    *indpp = &P->indicator;
257
258    return OCI_CONTINUE;
259} /* }}} */
260
261static int oci_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type TSRMLS_DC) /* {{{ */
262{
263    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
264
265    /* we're only interested in parameters for prepared SQL right now */
266    if (param->is_param) {
267        pdo_oci_bound_param *P;
268        sb4 value_sz = -1;
269
270        P = (pdo_oci_bound_param*)param->driver_data;
271
272        switch (event_type) {
273            case PDO_PARAM_EVT_FREE:
274                P = param->driver_data;
275                if (P) {
276                    efree(P);
277                }
278                break;
279
280            case PDO_PARAM_EVT_ALLOC:
281                P = (pdo_oci_bound_param*)ecalloc(1, sizeof(pdo_oci_bound_param));
282                param->driver_data = P;
283
284                /* figure out what we're doing */
285                switch (PDO_PARAM_TYPE(param->param_type)) {
286                    case PDO_PARAM_STMT:
287                        return 0;
288
289                    case PDO_PARAM_LOB:
290                        /* P->thing is now an OCILobLocator * */
291                        P->oci_type = SQLT_BLOB;
292                        value_sz = sizeof(OCILobLocator*);
293                        break;
294
295                    case PDO_PARAM_STR:
296                    default:
297                        P->oci_type = SQLT_CHR;
298                        value_sz = param->max_value_len;
299                        if (param->max_value_len == 0) {
300                            value_sz = 1332; /* maximum size before value is interpreted as a LONG value */
301                        }
302
303                }
304
305                if (param->name) {
306                    STMT_CALL(OCIBindByName, (S->stmt,
307                            &P->bind, S->err, (text*)param->name,
308                            param->namelen, 0, value_sz, P->oci_type,
309                            &P->indicator, 0, &P->retcode, 0, 0,
310                            OCI_DATA_AT_EXEC));
311                } else {
312                    STMT_CALL(OCIBindByPos, (S->stmt,
313                            &P->bind, S->err, param->paramno+1,
314                            0, value_sz, P->oci_type,
315                            &P->indicator, 0, &P->retcode, 0, 0,
316                            OCI_DATA_AT_EXEC));
317                }
318
319                STMT_CALL(OCIBindDynamic, (P->bind,
320                            S->err,
321                            param, oci_bind_input_cb,
322                            param, oci_bind_output_cb));
323
324                return 1;
325
326            case PDO_PARAM_EVT_EXEC_PRE:
327                P->indicator = 0;
328                P->used_for_output = 0;
329                if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
330                    ub4 empty = 0;
331                    STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL));
332                    STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err));
333                    S->have_blobs = 1;
334                }
335                return 1;
336
337            case PDO_PARAM_EVT_EXEC_POST:
338                /* fixup stuff set in motion in oci_bind_output_cb */
339                if (P->used_for_output) {
340                    if (P->indicator == -1) {
341                        /* set up a NULL value */
342                        if (Z_TYPE_P(param->parameter) == IS_STRING
343#if ZEND_EXTENSION_API_NO < 220040718
344                                && Z_STRVAL_P(param->parameter) != empty_string
345#endif
346                           ) {
347                            /* OCI likes to stick non-terminated strings in things */
348                            *Z_STRVAL_P(param->parameter) = '\0';
349                        }
350                        zval_dtor(param->parameter);
351                        ZVAL_NULL(param->parameter);
352                    } else if (Z_TYPE_P(param->parameter) == IS_STRING
353#if ZEND_EXTENSION_API_NO < 220040718
354                            && Z_STRVAL_P(param->parameter) != empty_string
355#endif
356                            ) {
357                        Z_STRLEN_P(param->parameter) = P->actual_len;
358                        Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), P->actual_len+1);
359                        Z_STRVAL_P(param->parameter)[P->actual_len] = '\0';
360                    }
361                } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) {
362                    php_stream *stm;
363
364                    if (Z_TYPE_P(param->parameter) == IS_NULL) {
365                        /* if the param is NULL, then we assume that they
366                         * wanted to bind a lob locator into it from the query
367                         * */
368
369                        stm = oci_create_lob_stream(stmt, (OCILobLocator*)P->thing TSRMLS_CC);
370                        if (stm) {
371                            OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
372                            php_stream_to_zval(stm, param->parameter);
373                            P->thing = NULL;
374                        }
375                    } else {
376                        /* we're a LOB being used for insert; transfer the data now */
377                        size_t n;
378                        ub4 amt, offset = 1;
379                        char *consume;
380
381                        php_stream_from_zval_no_verify(stm, &param->parameter);
382                        if (stm) {
383                            OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
384                            do {
385                                char buf[8192];
386                                n = php_stream_read(stm, buf, sizeof(buf));
387                                if ((int)n <= 0) {
388                                    break;
389                                }
390                                consume = buf;
391                                do {
392                                    amt = n;
393                                    OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
394                                            &amt, offset, consume, n,
395                                            OCI_ONE_PIECE,
396                                            NULL, NULL, 0, SQLCS_IMPLICIT);
397                                    offset += amt;
398                                    n -= amt;
399                                    consume += amt;
400                                } while (n);
401                            } while (1);
402                            OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
403                            OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator*)P->thing, 0);
404                        } else if (Z_TYPE_P(param->parameter) == IS_STRING) {
405                            /* stick the string into the LOB */
406                            consume = Z_STRVAL_P(param->parameter);
407                            n = Z_STRLEN_P(param->parameter);
408                            if (n) {
409                                OCILobOpen(S->H->svc, S->err, (OCILobLocator*)P->thing, OCI_LOB_READWRITE);
410                                while (n) {
411                                    amt = n;
412                                    OCILobWrite(S->H->svc, S->err, (OCILobLocator*)P->thing,
413                                            &amt, offset, consume, n,
414                                            OCI_ONE_PIECE,
415                                            NULL, NULL, 0, SQLCS_IMPLICIT);
416                                    consume += amt;
417                                    n -= amt;
418                                }
419                                OCILobClose(S->H->svc, S->err, (OCILobLocator*)P->thing);
420                            }
421                        }
422                        OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);
423                        P->thing = NULL;
424                    }
425                }
426
427                return 1;
428        }
429    }
430
431    return 1;
432} /* }}} */
433
434static int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, long offset TSRMLS_DC) /* {{{ */
435{
436#if HAVE_OCISTMTFETCH2
437    ub4 ociori;
438#endif
439    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
440
441#if HAVE_OCISTMTFETCH2
442    switch (ori) {
443        case PDO_FETCH_ORI_NEXT:    ociori = OCI_FETCH_NEXT; break;
444        case PDO_FETCH_ORI_PRIOR:   ociori = OCI_FETCH_PRIOR; break;
445        case PDO_FETCH_ORI_FIRST:   ociori = OCI_FETCH_FIRST; break;
446        case PDO_FETCH_ORI_LAST:    ociori = OCI_FETCH_LAST; break;
447        case PDO_FETCH_ORI_ABS:     ociori = OCI_FETCH_ABSOLUTE; break;
448        case PDO_FETCH_ORI_REL:     ociori = OCI_FETCH_RELATIVE; break;
449    }
450    S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, offset, OCI_DEFAULT);
451#else
452    S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
453#endif
454
455    if (S->last_err == OCI_NO_DATA) {
456        /* no (more) data */
457        return 0;
458    }
459
460    if (S->last_err == OCI_NEED_DATA) {
461        oci_stmt_error("OCI_NEED_DATA");
462        return 0;
463    }
464
465    if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) {
466        return 1;
467    }
468
469    oci_stmt_error("OCIStmtFetch");
470
471    return 0;
472} /* }}} */
473
474static sb4 oci_define_callback(dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp,
475        ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp)
476{
477    pdo_oci_column *col = (pdo_oci_column*)octxp;
478    TSRMLS_FETCH();
479
480    switch (col->dtype) {
481        case SQLT_BLOB:
482        case SQLT_CLOB:
483            *piecep = OCI_ONE_PIECE;
484            *bufpp = col->data;
485            *alenpp = &col->datalen;
486            *indpp = (dvoid *)&col->indicator;
487            break;
488
489        default:
490            php_error_docref(NULL TSRMLS_CC, E_WARNING,
491                "unhandled datatype in oci_define_callback; this should not happen");
492            return OCI_ERROR;
493    }
494
495    return OCI_CONTINUE;
496}
497
498static int oci_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) /* {{{ */
499{
500    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
501    OCIParam *param = NULL;
502    text *colname;
503    ub2 dtype, data_size, scale, precis;
504    ub4 namelen;
505    struct pdo_column_data *col = &stmt->columns[colno];
506    zend_bool dyn = FALSE;
507
508    /* describe the column */
509    STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid*)&param, colno+1));
510
511    /* what type ? */
512    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_TYPE",
513            (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));
514
515    /* how big ? */
516    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_DATA_SIZE",
517            (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));
518
519    /* scale ? */
520    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_SCALE",
521            (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));
522
523    /* precision ? */
524    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_PRECISION",
525            (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));
526
527    /* name ? */
528    STMT_CALL_MSG(OCIAttrGet, "OCI_ATTR_NAME",
529            (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));
530
531    col->precision = scale;
532    col->maxlen = data_size;
533    col->namelen = namelen;
534    col->name = estrndup((char *)colname, namelen);
535
536    S->cols[colno].dtype = dtype;
537
538    /* how much room do we need to store the field */
539    switch (dtype) {
540        case SQLT_LBI:
541        case SQLT_LNG:
542            if (dtype == SQLT_LBI) {
543                dtype = SQLT_BIN;
544            } else {
545                dtype = SQLT_CHR;
546            }
547            S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */
548            S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
549            col->param_type = PDO_PARAM_STR;
550            break;
551
552        case SQLT_BLOB:
553        case SQLT_CLOB:
554            col->param_type = PDO_PARAM_LOB;
555            STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid**)&S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL));
556            S->cols[colno].datalen = sizeof(OCILobLocator*);
557            dyn = TRUE;
558            break;
559
560        case SQLT_BIN:
561        default:
562            if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD
563#ifdef SQLT_TIMESTAMP
564                    || dtype == SQLT_TIMESTAMP
565#endif
566#ifdef SQLT_TIMESTAMP_TZ
567                    || dtype == SQLT_TIMESTAMP_TZ
568#endif
569                    ) {
570                /* should be big enough for most date formats and numbers */
571                S->cols[colno].datalen = 512;
572#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)
573            } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) {
574                S->cols[colno].datalen = 1024;
575#endif
576            } else {
577                S->cols[colno].datalen = col->maxlen;
578            }
579            if (dtype == SQLT_BIN) {
580                S->cols[colno].datalen *= 3;
581            }
582            S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);
583            dtype = SQLT_CHR;
584
585            /* returning data as a string */
586            col->param_type = PDO_PARAM_STR;
587    }
588
589    STMT_CALL(OCIDefineByPos, (S->stmt, &S->cols[colno].def, S->err, colno+1,
590                S->cols[colno].data, S->cols[colno].datalen, dtype, &S->cols[colno].indicator,
591                &S->cols[colno].fetched_len, &S->cols[colno].retcode, dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT));
592
593    if (dyn) {
594        STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno],
595                oci_define_callback));
596    }
597
598    return 1;
599} /* }}} */
600
601struct oci_lob_self {
602    pdo_stmt_t *stmt;
603    pdo_oci_stmt *S;
604    OCILobLocator *lob;
605    ub4 offset;
606};
607
608static size_t oci_blob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
609{
610    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
611    ub4 amt;
612    sword r;
613
614    amt = count;
615    r = OCILobWrite(self->S->H->svc, self->S->err, self->lob,
616        &amt, self->offset, (char*)buf, count,
617        OCI_ONE_PIECE,
618        NULL, NULL, 0, SQLCS_IMPLICIT);
619
620    if (r != OCI_SUCCESS) {
621        return (size_t)-1;
622    }
623
624    self->offset += amt;
625    return amt;
626}
627
628static size_t oci_blob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
629{
630    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
631    ub4 amt;
632    sword r;
633
634    amt = count;
635    r = OCILobRead(self->S->H->svc, self->S->err, self->lob,
636        &amt, self->offset, buf, count,
637        NULL, NULL, 0, SQLCS_IMPLICIT);
638
639    if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {
640        return (size_t)-1;
641    }
642
643    self->offset += amt;
644    if (amt < count) {
645        stream->eof = 1;
646    }
647    return amt;
648}
649
650static int oci_blob_close(php_stream *stream, int close_handle TSRMLS_DC)
651{
652    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
653    pdo_stmt_t *stmt = self->stmt;
654
655    if (close_handle) {
656        OCILobClose(self->S->H->svc, self->S->err, self->lob);
657        OCIDescriptorFree(self->lob, OCI_DTYPE_LOB);
658        efree(self);
659    }
660
661    php_pdo_stmt_delref(stmt TSRMLS_CC);
662    return 0;
663}
664
665static int oci_blob_flush(php_stream *stream TSRMLS_DC)
666{
667    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
668    OCILobFlushBuffer(self->S->H->svc, self->S->err, self->lob, 0);
669    return 0;
670}
671
672static int oci_blob_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC)
673{
674    struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract;
675
676    if (offset >= PDO_OCI_LOBMAXSIZE) {
677        return -1;
678    } else {
679        self->offset = offset + 1;  /* Oracle LOBS are 1-based, but PHP is 0-based */
680        return 0;
681    }
682}
683
684static php_stream_ops oci_blob_stream_ops = {
685    oci_blob_write,
686    oci_blob_read,
687    oci_blob_close,
688    oci_blob_flush,
689    "pdo_oci blob stream",
690    oci_blob_seek,
691    NULL,
692    NULL,
693    NULL
694};
695
696static php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob TSRMLS_DC)
697{
698    php_stream *stm;
699    struct oci_lob_self *self = ecalloc(1, sizeof(*self));
700    self->lob = lob;
701    self->offset = 1; /* 1-based */
702    self->stmt = stmt;
703    self->S = (pdo_oci_stmt*)stmt->driver_data;
704
705    stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, "r+b");
706
707    if (stm) {
708        php_pdo_stmt_addref(stmt TSRMLS_CC);
709        return stm;
710    }
711
712    efree(self);
713    return NULL;
714}
715
716static int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) /* {{{ */
717{
718    pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data;
719    pdo_oci_column *C = &S->cols[colno];
720
721    /* check the indicator to ensure that the data is intact */
722    if (C->indicator == -1) {
723        /* A NULL value */
724        *ptr = NULL;
725        *len = 0;
726        return 1;
727    } else if (C->indicator == 0) {
728        /* it was stored perfectly */
729
730        if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) {
731            if (C->data) {
732                *ptr = (char*)oci_create_lob_stream(stmt, (OCILobLocator*)C->data TSRMLS_CC);
733                OCILobOpen(S->H->svc, S->err, (OCILobLocator*)C->data, OCI_LOB_READONLY);
734            }
735            *len = 0;
736            return *ptr ? 1 : 0;
737        }
738
739        *ptr = C->data;
740        *len = C->fetched_len;
741        return 1;
742    } else {
743        /* it was truncated */
744        php_error_docref(NULL TSRMLS_CC, E_WARNING, "column %d data was too large for buffer and was truncated to fit it", colno);
745
746        *ptr = C->data;
747        *len = C->fetched_len;
748        return 1;
749    }
750} /* }}} */
751
752struct pdo_stmt_methods oci_stmt_methods = {
753    oci_stmt_dtor,
754    oci_stmt_execute,
755    oci_stmt_fetch,
756    oci_stmt_describe,
757    oci_stmt_get_col,
758    oci_stmt_param_hook
759};
760
761/*
762 * Local variables:
763 * tab-width: 4
764 * c-basic-offset: 4
765 * End:
766 * vim600: noet sw=4 ts=4 fdm=marker
767 * vim<600: noet sw=4 ts=4
768 */
769