1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* Developed initially by Nick Kew and Chris Darroch.
18 * Contributed to the APR project by kind permission of
19 * Pearson Education Core Technology Group (CTG),
20 * formerly Central Media Group (CMG).
21 */
22
23/* apr_dbd_oracle - a painful attempt
24 *
25 * Based first on the documentation at
26 * http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/toc.htm
27 *
28 * Those docs have a lot of internal inconsistencies, contradictions, etc
29 * So I've snarfed the demo programs (from Oracle 8, not included in
30 * the current downloadable oracle), and used code from them.
31 *
32 * Why do cdemo81.c and cdemo82.c do the same thing in very different ways?
33 * e.g. cdemo82 releases all its handle on shutdown; cdemo81 doesn't
34 *
35 * All the ORA* functions return a "sword".  Some of them are documented;
36 * others aren't.  So I've adopted a policy of using switch statements
37 * everywhere, even when we're not doing anything with the return values.
38 *
39 * This makes no attempt at performance tuning, such as setting
40 * prefetch cache size.  We need some actual performance data
41 * to make that meaningful.  Input from someone with experience
42 * as a sysop using oracle would be a good start.
43 */
44
45/* shut compiler up */
46#ifdef DEBUG
47#define int_errorcode int errorcode
48#else
49#define int_errorcode
50#endif
51
52#include "apu.h"
53
54#if APU_HAVE_ORACLE
55
56#include <ctype.h>
57#include <stdlib.h>
58#include <stdio.h>
59
60#include <oci.h>
61
62#include "apr_strings.h"
63#include "apr_lib.h"
64#include "apr_time.h"
65#include "apr_hash.h"
66#include "apr_buckets.h"
67
68#define TRANS_TIMEOUT 30
69#define MAX_ARG_LEN 256 /* in line with other apr_dbd drivers.  We alloc this
70                         * lots of times, so a large value gets hungry.
71                         * Should really make it configurable
72                         */
73#define DEFAULT_LONG_SIZE 4096
74#define DBD_ORACLE_MAX_COLUMNS 256
75#define NUMERIC_FIELD_SIZE 32
76
77#define CHECK_CONN_QUERY "SELECT 1 FROM dual"
78
79#define ERR_BUF_SIZE 200
80
81#ifdef DEBUG
82#include <stdio.h>
83#endif
84
85#include "apr_dbd_internal.h"
86
87/* declarations */
88static const char *dbd_oracle_error(apr_dbd_t *sql, int n);
89static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql,
90                              const char *query, const char *label,
91                              int nargs, int nvals, apr_dbd_type_e *types,
92                              apr_dbd_prepared_t **statement);
93static int outputParams(apr_dbd_t*, apr_dbd_prepared_t*);
94static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql,
95                              apr_dbd_results_t **results,
96                              apr_dbd_prepared_t *statement,
97                              int seek, const char **values);
98static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
99                             int *nrows, apr_dbd_prepared_t *statement,
100                             const char **values);
101static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
102                                        apr_dbd_transaction_t **trans);
103static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans);
104
105struct apr_dbd_transaction_t {
106    int mode;
107    enum { TRANS_NONE, TRANS_ERROR, TRANS_1, TRANS_2 } status;
108    apr_dbd_t *handle;
109    OCITrans *trans;
110    OCISnapshot *snapshot1;
111    OCISnapshot *snapshot2;
112};
113
114struct apr_dbd_results_t {
115    apr_pool_t *pool;
116    apr_dbd_t* handle;
117    unsigned int rownum;
118    int seek;
119    int nrows;
120    apr_dbd_prepared_t *statement;
121};
122
123struct apr_dbd_t {
124    sword status;
125    OCIError *err;
126    OCIServer *svr;
127    OCISvcCtx *svc;
128    OCISession *auth;
129    apr_dbd_transaction_t* trans;
130    apr_pool_t *pool;
131    char buf[ERR_BUF_SIZE]; /* for error messages */
132    apr_size_t long_size;
133    apr_dbd_prepared_t *check_conn_stmt;
134};
135
136struct apr_dbd_row_t {
137    int n;
138    apr_dbd_results_t *res;
139    apr_pool_t *pool;
140};
141
142typedef struct {
143    apr_dbd_type_e type;
144    sb2 ind;
145    sb4 len;
146    OCIBind *bind;
147    union {
148        void *raw;
149        char *sval;
150        int ival;
151        unsigned int uval;
152        double fval;
153        OCILobLocator *lobval;
154    } value;
155} bind_arg;
156
157typedef struct {
158    int type;
159    sb2 ind;
160    ub2 len;         /* length of actual output */
161    OCIDefine *defn;
162    apr_size_t sz;   /* length of buf for output */
163    union {
164        void *raw;
165        char *sval;
166        OCILobLocator *lobval;
167    } buf;
168    const char *name;
169} define_arg;
170
171struct apr_dbd_prepared_t {
172    OCIStmt *stmt;
173    int nargs;
174    int nvals;
175    bind_arg *args;
176    int nout;
177    define_arg *out;
178    apr_dbd_t *handle;
179    apr_pool_t *pool;
180    ub2 type;
181};
182
183/* AFAICT from the docs, the OCIEnv thingey can be used async
184 * across threads, so lets have a global one.
185 *
186 * We'll need shorter-lived envs to deal with requests and connections
187 *
188 * Hmmm, that doesn't work: we don't have a usermem framework.
189 * OK, forget about using APR pools here, until we figure out
190 * the right way to do it (if such a thing exists).
191 */
192static OCIEnv *dbd_oracle_env = NULL;
193
194/* Oracle specific bucket for BLOB/CLOB types */
195typedef struct apr_bucket_lob apr_bucket_lob;
196/**
197 * A bucket referring to a Oracle BLOB/CLOB
198 */
199struct apr_bucket_lob {
200    /** Number of buckets using this memory */
201    apr_bucket_refcount  refcount;
202    /** The row this bucket refers to */
203    const apr_dbd_row_t *row;
204    /** The column this bucket refers to */
205    int col;
206    /** The pool into which any needed structures should
207     *  be created while reading from this bucket */
208    apr_pool_t *readpool;
209};
210
211static void lob_bucket_destroy(void *data);
212static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
213                                    apr_size_t *len, apr_read_type_e block);
214static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
215                                       const apr_dbd_row_t *row, int col,
216                                       apr_off_t offset, apr_size_t len,
217                                       apr_pool_t *p);
218static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
219                                         apr_off_t offset,
220                                         apr_size_t len, apr_pool_t *p,
221                                         apr_bucket_alloc_t *list);
222
223static const apr_bucket_type_t apr_bucket_type_lob = {
224    "LOB", 5, APR_BUCKET_DATA,
225    lob_bucket_destroy,
226    lob_bucket_read,
227    apr_bucket_setaside_notimpl,
228    apr_bucket_shared_split,
229    apr_bucket_shared_copy
230};
231
232static void lob_bucket_destroy(void *data)
233{
234    apr_bucket_lob *f = data;
235
236    if (apr_bucket_shared_destroy(f)) {
237        /* no need to destroy database objects here; it will get
238         * done automatically when the pool gets cleaned up */
239        apr_bucket_free(f);
240    }
241}
242
243static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
244                                    apr_size_t *len, apr_read_type_e block)
245{
246    apr_bucket_lob *a = e->data;
247    const apr_dbd_row_t *row = a->row;
248    apr_dbd_results_t *res = row->res;
249    int col = a->col;
250    apr_bucket *b = NULL;
251    apr_size_t blength = e->length;  /* bytes remaining in file past offset */
252    apr_off_t boffset = e->start;
253    define_arg *val = &res->statement->out[col];
254    apr_dbd_t *sql = res->handle;
255/* Only with 10g, unfortunately
256    oraub8 length = APR_BUCKET_BUFF_SIZE;
257*/
258    ub4 length = APR_BUCKET_BUFF_SIZE;
259    char *buf = NULL;
260
261    *str = NULL;  /* in case we die prematurely */
262
263    /* fetch from offset if not at the beginning */
264    buf = apr_palloc(row->pool, APR_BUCKET_BUFF_SIZE);
265    sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval,
266                             &length, 1 + (size_t)boffset,
267                             (dvoid*) buf, APR_BUCKET_BUFF_SIZE,
268                             NULL, NULL, 0, SQLCS_IMPLICIT);
269/* Only with 10g, unfortunately
270    sql->status = OCILobRead2(sql->svc, sql->err, val->buf.lobval,
271                              &length, NULL, 1 + boffset,
272                              (dvoid*) buf, APR_BUCKET_BUFF_SIZE,
273                              OCI_ONE_PIECE, NULL, NULL, 0, SQLCS_IMPLICIT);
274*/
275    if (sql->status != OCI_SUCCESS) {
276        return APR_EGENERAL;
277    }
278    blength -= length;
279    *len = length;
280    *str = buf;
281
282    /*
283     * Change the current bucket to refer to what we read,
284     * even if we read nothing because we hit EOF.
285     */
286    apr_bucket_pool_make(e, *str, *len, res->pool);
287
288    /* If we have more to read from the field, then create another bucket */
289    if (blength > 0) {
290        /* for efficiency, we can just build a new apr_bucket struct
291         * to wrap around the existing LOB bucket */
292        b = apr_bucket_alloc(sizeof(*b), e->list);
293        b->start  = boffset + *len;
294        b->length = blength;
295        b->data   = a;
296        b->type   = &apr_bucket_type_lob;
297        b->free   = apr_bucket_free;
298        b->list   = e->list;
299        APR_BUCKET_INSERT_AFTER(e, b);
300    }
301    else {
302        lob_bucket_destroy(a);
303    }
304
305    return APR_SUCCESS;
306}
307
308static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
309                                       const apr_dbd_row_t *row, int col,
310                                       apr_off_t offset, apr_size_t len,
311                                       apr_pool_t *p)
312{
313    apr_bucket_lob *f;
314
315    f = apr_bucket_alloc(sizeof(*f), b->list);
316    f->row = row;
317    f->col = col;
318    f->readpool = p;
319
320    b = apr_bucket_shared_make(b, f, offset, len);
321    b->type = &apr_bucket_type_lob;
322
323    return b;
324}
325
326static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
327                                         apr_off_t offset,
328                                         apr_size_t len, apr_pool_t *p,
329                                         apr_bucket_alloc_t *list)
330{
331    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
332
333    APR_BUCKET_INIT(b);
334    b->free = apr_bucket_free;
335    b->list = list;
336    return apr_bucket_lob_make(b, row, col, offset, len, p);
337}
338
339static apr_status_t dbd_free_lobdesc(void *lob)
340{
341    switch (OCIDescriptorFree(lob, OCI_DTYPE_LOB)) {
342    case OCI_SUCCESS:
343        return APR_SUCCESS;
344    default:
345        return APR_EGENERAL;
346    }
347}
348
349static apr_status_t dbd_free_snapshot(void *snap)
350{
351    switch (OCIDescriptorFree(snap, OCI_DTYPE_SNAP)) {
352    case OCI_SUCCESS:
353        return APR_SUCCESS;
354    default:
355        return APR_EGENERAL;
356    }
357}
358
359static void dbd_oracle_init(apr_pool_t *pool)
360{
361    if (dbd_oracle_env == NULL) {
362        /* Sadly, OCI_SHARED seems to be impossible to use, due to
363         * various Oracle bugs.  See, for example, Oracle MetaLink bug 2972890
364         * and PHP bug http://bugs.php.net/bug.php?id=23733
365         */
366#ifdef OCI_NEW_LENGTH_SEMANTICS
367        OCIEnvCreate(&dbd_oracle_env, OCI_THREADED|OCI_NEW_LENGTH_SEMANTICS,
368                     NULL, NULL, NULL, NULL, 0, NULL);
369#else
370        OCIEnvCreate(&dbd_oracle_env, OCI_THREADED,
371                     NULL, NULL, NULL, NULL, 0, NULL);
372#endif
373    }
374}
375
376static apr_dbd_t *dbd_oracle_open(apr_pool_t *pool, const char *params,
377                                  const char **error)
378{
379    apr_dbd_t *ret = apr_pcalloc(pool, sizeof(apr_dbd_t));
380    int errorcode;
381
382    char *BLANK = "";
383    struct {
384        const char *field;
385        char *value;
386    } fields[] = {
387        {"user", BLANK},
388        {"pass", BLANK},
389        {"dbname", BLANK},
390        {"server", BLANK},
391        {NULL, NULL}
392    };
393    int i;
394    const char *ptr;
395    const char *key;
396    size_t klen;
397    const char *value;
398    size_t vlen;
399    static const char *const delims = " \r\n\t;|,";
400
401    ret->pool = pool;
402    ret->long_size = DEFAULT_LONG_SIZE;
403
404    /* snitch parsing from the MySQL driver */
405    for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
406        /* don't dereference memory that may not belong to us */
407        if (ptr == params) {
408            ++ptr;
409            continue;
410        }
411        for (key = ptr-1; apr_isspace(*key); --key);
412        klen = 0;
413        while (apr_isalpha(*key)) {
414            if (key == params) {
415                /* Don't parse off the front of the params */
416                --key;
417                ++klen;
418                break;
419            }
420            --key;
421            ++klen;
422        }
423        ++key;
424        for (value = ptr+1; apr_isspace(*value); ++value);
425        vlen = strcspn(value, delims);
426        for (i=0; fields[i].field != NULL; ++i) {
427            if (!strncasecmp(fields[i].field, key, klen)) {
428                fields[i].value = apr_pstrndup(pool, value, vlen);
429                break;
430            }
431        }
432        ptr = value+vlen;
433    }
434
435    ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->err,
436                                 OCI_HTYPE_ERROR, 0, NULL);
437    switch (ret->status) {
438    default:
439#ifdef DEBUG
440        printf("ret->status is %d\n", ret->status);
441        break;
442#else
443        return NULL;
444#endif
445    case OCI_SUCCESS:
446        break;
447    }
448
449    ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svr,
450                                 OCI_HTYPE_SERVER, 0, NULL);
451    switch (ret->status) {
452    default:
453#ifdef DEBUG
454        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
455                    sizeof(ret->buf), OCI_HTYPE_ERROR);
456        printf("OPEN ERROR %d (alloc svr): %s\n", ret->status, ret->buf);
457        break;
458#else
459        if (error) {
460            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
461            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
462                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
463        }
464        return NULL;
465#endif
466    case OCI_SUCCESS:
467        break;
468    }
469
470    ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svc,
471                                 OCI_HTYPE_SVCCTX, 0, NULL);
472    switch (ret->status) {
473    default:
474#ifdef DEBUG
475        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
476                    sizeof(ret->buf), OCI_HTYPE_ERROR);
477        printf("OPEN ERROR %d (alloc svc): %s\n", ret->status, ret->buf);
478        break;
479#else
480        if (error) {
481            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
482            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
483                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
484        }
485        return NULL;
486#endif
487    case OCI_SUCCESS:
488        break;
489    }
490
491/* All the examples use the #else */
492#if CAN_DO_LOGIN
493    ret->status = OCILogon(dbd_oracle_env, ret->err, &ret->svc, fields[0].value,
494                     strlen(fields[0].value), fields[1].value,
495                     strlen(fields[1].value), fields[2].value,
496                     strlen(fields[2].value));
497    switch (ret->status) {
498    default:
499#ifdef DEBUG
500        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
501                    sizeof(ret->buf), OCI_HTYPE_ERROR);
502        printf("OPEN ERROR: %s\n", ret->buf);
503        break;
504#else
505        if (error) {
506            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
507            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
508                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
509        }
510        return NULL;
511#endif
512    case OCI_SUCCESS:
513        break;
514    }
515#else
516    ret->status = OCIServerAttach(ret->svr, ret->err, (text*) fields[3].value,
517                                  strlen(fields[3].value), OCI_DEFAULT);
518    switch (ret->status) {
519    default:
520#ifdef DEBUG
521        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
522                    sizeof(ret->buf), OCI_HTYPE_ERROR);
523        printf("OPEN ERROR %d (server attach): %s\n", ret->status, ret->buf);
524        break;
525#else
526        if (error) {
527            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
528            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
529                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
530        }
531        return NULL;
532#endif
533    case OCI_SUCCESS:
534        break;
535    }
536    ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->svr, 0,
537                        OCI_ATTR_SERVER, ret->err);
538    switch (ret->status) {
539    default:
540#ifdef DEBUG
541        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
542                    sizeof(ret->buf), OCI_HTYPE_ERROR);
543        printf("OPEN ERROR %d (attr set): %s\n", ret->status, ret->buf);
544        break;
545#else
546        if (error) {
547            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
548            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
549                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
550        }
551        return NULL;
552#endif
553    case OCI_SUCCESS:
554        break;
555    }
556    ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->auth,
557                            OCI_HTYPE_SESSION, 0, NULL);
558    switch (ret->status) {
559    default:
560#ifdef DEBUG
561        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
562                    sizeof(ret->buf), OCI_HTYPE_ERROR);
563        printf("OPEN ERROR %d (alloc auth): %s\n", ret->status, ret->buf);
564        break;
565#else
566        if (error) {
567            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
568            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
569                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
570        }
571        return NULL;
572#endif
573    case OCI_SUCCESS:
574        break;
575    }
576    ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[0].value,
577                        strlen(fields[0].value), OCI_ATTR_USERNAME, ret->err);
578    switch (ret->status) {
579    default:
580#ifdef DEBUG
581        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
582                    sizeof(ret->buf), OCI_HTYPE_ERROR);
583        printf("OPEN ERROR %d (attr username): %s\n", ret->status, ret->buf);
584        break;
585#else
586        if (error) {
587            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
588            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
589                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
590        }
591        return NULL;
592#endif
593    case OCI_SUCCESS:
594        break;
595    }
596    ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[1].value,
597                        strlen(fields[1].value), OCI_ATTR_PASSWORD, ret->err);
598    switch (ret->status) {
599    default:
600#ifdef DEBUG
601        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
602                    sizeof(ret->buf), OCI_HTYPE_ERROR);
603        printf("OPEN ERROR %d (attr password): %s\n", ret->status, ret->buf);
604        break;
605#else
606        if (error) {
607            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
608            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
609                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
610        }
611        return NULL;
612#endif
613    case OCI_SUCCESS:
614        break;
615    }
616    ret->status = OCISessionBegin(ret->svc, ret->err, ret->auth,
617                             OCI_CRED_RDBMS, OCI_DEFAULT);
618    switch (ret->status) {
619    default:
620#ifdef DEBUG
621        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
622                    sizeof(ret->buf), OCI_HTYPE_ERROR);
623        printf("OPEN ERROR %d (session begin): %s\n", ret->status, ret->buf);
624        break;
625#else
626        if (error) {
627            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
628            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
629                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
630        }
631        return NULL;
632#endif
633    case OCI_SUCCESS:
634        break;
635    }
636    ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->auth, 0,
637                        OCI_ATTR_SESSION, ret->err);
638    switch (ret->status) {
639    default:
640#ifdef DEBUG
641        OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
642                    sizeof(ret->buf), OCI_HTYPE_ERROR);
643        printf("OPEN ERROR %d (attr session): %s\n", ret->status, ret->buf);
644#else
645        if (error) {
646            *error = apr_pcalloc(pool, ERR_BUF_SIZE);
647            OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
648                        ERR_BUF_SIZE, OCI_HTYPE_ERROR);
649        }
650        return NULL;
651#endif
652        break;
653    case OCI_SUCCESS:
654        break;
655    }
656#endif
657
658    if(dbd_oracle_prepare(pool, ret, CHECK_CONN_QUERY, NULL, 0, 0, NULL,
659                          &ret->check_conn_stmt) != 0) {
660        return NULL;
661    }
662
663    return ret;
664}
665
666#ifdef EXPORT_NATIVE_FUNCS
667static apr_size_t dbd_oracle_long_size_set(apr_dbd_t *sql,
668                                           apr_size_t long_size)
669{
670    apr_size_t old_size = sql->long_size;
671    sql->long_size = long_size;
672    return old_size;
673}
674#endif
675
676static const char *dbd_oracle_get_name(const apr_dbd_results_t *res, int n)
677{
678    define_arg *val = &res->statement->out[n];
679
680    if ((n < 0) || (n >= res->statement->nout)) {
681        return NULL;
682    }
683    return val->name;
684}
685
686static int dbd_oracle_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
687                              apr_dbd_row_t **rowp, int rownum)
688{
689    apr_dbd_row_t *row = *rowp;
690    apr_dbd_t *sql = res->handle;
691    int_errorcode;
692
693    if (row == NULL) {
694        row = apr_palloc(pool, sizeof(apr_dbd_row_t));
695        *rowp = row;
696        row->res = res;
697        /* Oracle starts counting at 1 according to the docs */
698        row->n = res->seek ? rownum : 1;
699        row->pool = pool;
700    }
701    else {
702        if (res->seek) {
703            row->n = rownum;
704        }
705        else {
706            ++row->n;
707        }
708    }
709
710    if (res->seek) {
711        sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1,
712                                    OCI_FETCH_ABSOLUTE, row->n, OCI_DEFAULT);
713    }
714    else {
715        sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1,
716                                    OCI_FETCH_NEXT, 0, OCI_DEFAULT);
717    }
718    switch (sql->status) {
719    case OCI_SUCCESS:
720        (*rowp)->res = res;
721        return 0;
722    case OCI_NO_DATA:
723        return -1;
724    case OCI_ERROR:
725#ifdef DEBUG
726        OCIErrorGet(sql->err, 1, NULL, &errorcode,
727                    sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
728        printf("Execute error %d: %s\n", sql->status, sql->buf);
729#endif
730        /* fallthrough */
731    default:
732        return 1;
733    }
734    return 0;
735}
736
737static const char *dbd_oracle_error(apr_dbd_t *sql, int n)
738{
739    /* This is ugly.  Needs us to pass in a buffer of unknown size.
740     * Either we put it on the handle, or we have to keep allocing/copying
741     */
742    sb4 errorcode;
743
744    switch (sql->status) {
745    case OCI_SUCCESS:
746        return "OCI_SUCCESS";
747    case OCI_SUCCESS_WITH_INFO:
748        return "OCI_SUCCESS_WITH_INFO";
749    case OCI_NEED_DATA:
750        return "OCI_NEED_DATA";
751    case OCI_NO_DATA:
752        return "OCI_NO_DATA";
753    case OCI_INVALID_HANDLE:
754        return "OCI_INVALID_HANDLE";
755    case OCI_STILL_EXECUTING:
756        return "OCI_STILL_EXECUTING";
757    case OCI_CONTINUE:
758        return "OCI_CONTINUE";
759    }
760
761    switch (OCIErrorGet(sql->err, 1, NULL, &errorcode,
762                        (text*) sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR)) {
763    case OCI_SUCCESS:
764        return sql->buf;
765    default:
766        return "internal error: OCIErrorGet failed";
767    }
768}
769
770static apr_status_t freeStatement(void *statement)
771{
772    int rv = APR_SUCCESS;
773    OCIStmt *stmt = ((apr_dbd_prepared_t*)statement)->stmt;
774
775#ifdef PREPARE2
776    OCIError *err;
777
778    if (OCIHandleAlloc(dbd_oracle_env, (dvoid**)&err, OCI_HTYPE_ERROR,
779                       0, NULL) != OCI_SUCCESS) {
780        return APR_EGENERAL;
781    }
782    if (OCIStmtRelease(stmt, err, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS) {
783        rv = APR_EGENERAL;
784    }
785    if (OCIHandleFree(err, OCI_HTYPE_ERROR) != OCI_SUCCESS) {
786        rv = APR_EGENERAL;
787    }
788#else
789    if (OCIHandleFree(stmt, OCI_HTYPE_STMT) != OCI_SUCCESS) {
790        rv = APR_EGENERAL;
791    }
792#endif
793
794    return rv;
795}
796
797static int dbd_oracle_select(apr_pool_t *pool, apr_dbd_t *sql,
798                             apr_dbd_results_t **results,
799                             const char *query, int seek)
800{
801    int ret = 0;
802    apr_dbd_prepared_t *statement = NULL;
803
804    ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement);
805    if (ret != 0) {
806        return ret;
807    }
808
809    ret = dbd_oracle_pselect(pool, sql, results, statement, seek, NULL);
810    if (ret != 0) {
811        return ret;
812    }
813
814    return ret;
815}
816
817static int dbd_oracle_query(apr_dbd_t *sql, int *nrows, const char *query)
818{
819    int ret = 0;
820    apr_pool_t *pool;
821    apr_dbd_prepared_t *statement = NULL;
822
823    if (sql->trans && sql->trans->status == TRANS_ERROR) {
824        return 1;
825    }
826
827    /* make our own pool so that APR allocations don't linger and so that
828     * both Stmt and LOB handles are cleaned up (LOB handles may be
829     * allocated when preparing APR_DBD_TYPE_CLOB/BLOBs)
830     */
831    apr_pool_create(&pool, sql->pool);
832
833    ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement);
834    if (ret == 0) {
835        ret = dbd_oracle_pquery(pool, sql, nrows, statement, NULL);
836        if (ret == 0) {
837            sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT,
838                                     nrows, 0, OCI_ATTR_ROW_COUNT,
839                                     sql->err);
840        }
841    }
842
843    apr_pool_destroy(pool);
844
845    return ret;
846}
847
848static const char *dbd_oracle_escape(apr_pool_t *pool, const char *arg,
849                                     apr_dbd_t *sql)
850{
851    return arg;        /* OCI has no concept of string escape */
852}
853
854static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql,
855                              const char *query, const char *label,
856                              int nargs, int nvals, apr_dbd_type_e *types,
857                              apr_dbd_prepared_t **statement)
858{
859    int ret = 0;
860    int i;
861    apr_dbd_prepared_t *stmt ;
862
863    if (*statement == NULL) {
864        *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
865    }
866    stmt = *statement;
867    stmt->handle = sql;
868    stmt->pool = pool;
869    stmt->nargs = nargs;
870    stmt->nvals = nvals;
871
872    /* populate our own args, if any */
873    if (nargs > 0) {
874        stmt->args = apr_pcalloc(pool, nargs*sizeof(bind_arg));
875        for (i = 0; i < nargs; i++) {
876            stmt->args[i].type = types[i];
877        }
878    }
879
880    sql->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**) &stmt->stmt,
881                                 OCI_HTYPE_STMT, 0, NULL);
882    if (sql->status != OCI_SUCCESS) {
883        return 1;
884    }
885
886    sql->status = OCIStmtPrepare(stmt->stmt, sql->err, (text*) query,
887                                 strlen(query), OCI_NTV_SYNTAX, OCI_DEFAULT);
888    if (sql->status != OCI_SUCCESS) {
889        OCIHandleFree(stmt->stmt, OCI_HTYPE_STMT);
890        return 1;
891    }
892
893    apr_pool_cleanup_register(pool, stmt, freeStatement,
894                              apr_pool_cleanup_null);
895
896    /* Perl gets statement type here */
897    sql->status = OCIAttrGet(stmt->stmt, OCI_HTYPE_STMT, &stmt->type, 0,
898                             OCI_ATTR_STMT_TYPE, sql->err);
899    if (sql->status != OCI_SUCCESS) {
900        return 1;
901    }
902
903/* Perl sets PREFETCH_MEMORY here, but the docs say there's a working default */
904#if 0
905    sql->status = OCIAttrSet(stmt->stmt, OCI_HTYPE_STMT, &prefetch_size,
906                             sizeof(prefetch_size), OCI_ATTR_PREFETCH_MEMORY,
907                             sql->err);
908    if (sql->status != OCI_SUCCESS) {
909        return 1;
910    }
911#endif
912
913    if (stmt->type == OCI_STMT_SELECT) {
914        ret = outputParams(sql, stmt);
915    }
916    return ret;
917}
918
919static void dbd_oracle_bind(apr_dbd_prepared_t *statement, const char **values)
920{
921    OCIStmt *stmt = statement->stmt;
922    apr_dbd_t *sql = statement->handle;
923    int i, j;
924    sb2 null_ind = -1;
925
926    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
927        if (values[j] == NULL) {
928            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
929                                       sql->err, i + 1,
930                                       NULL, 0, SQLT_STR,
931                                       &null_ind, NULL,
932                                       (ub2) 0, (ub4) 0,
933                                       (ub4 *) 0, OCI_DEFAULT);
934        }
935        else {
936            switch (statement->args[i].type) {
937            case APR_DBD_TYPE_BLOB:
938                {
939                char *data = (char *)values[j];
940                int size = atoi((char*)values[++j]);
941
942                /* skip table and column for now */
943                j += 2;
944
945                sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
946                                           sql->err, i + 1,
947                                           data, size, SQLT_LBI,
948                                           &statement->args[i].ind,
949                                           NULL,
950                                           (ub2) 0, (ub4) 0,
951                                           (ub4 *) 0, OCI_DEFAULT);
952                }
953                break;
954            case APR_DBD_TYPE_CLOB:
955                {
956                char *data = (char *)values[j];
957                int size = atoi((char*)values[++j]);
958
959                /* skip table and column for now */
960                j += 2;
961
962                sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
963                                           sql->err, i + 1,
964                                           data, size, SQLT_LNG,
965                                           &statement->args[i].ind,
966                                           NULL,
967                                           (ub2) 0, (ub4) 0,
968                                           (ub4 *) 0, OCI_DEFAULT);
969                }
970                break;
971            default:
972                sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
973                                           sql->err, i + 1,
974                                           (dvoid*) values[j],
975                                           strlen(values[j]) + 1,
976                                           SQLT_STR,
977                                           &statement->args[i].ind,
978                                           NULL,
979                                           (ub2) 0, (ub4) 0,
980                                           (ub4 *) 0, OCI_DEFAULT);
981                break;
982            }
983        }
984
985        if (sql->status != OCI_SUCCESS) {
986            return;
987        }
988    }
989
990    return;
991}
992
993static int outputParams(apr_dbd_t *sql, apr_dbd_prepared_t *stmt)
994{
995    OCIParam *parms;
996    int i;
997    ub2 paramtype[DBD_ORACLE_MAX_COLUMNS];
998    ub2 paramsize[DBD_ORACLE_MAX_COLUMNS];
999    char *paramname[DBD_ORACLE_MAX_COLUMNS];
1000    ub4 paramnamelen[DBD_ORACLE_MAX_COLUMNS];
1001    int_errorcode;
1002
1003    /* Perl uses 0 where we used 1 */
1004    sql->status = OCIStmtExecute(sql->svc, stmt->stmt, sql->err, 0, 0,
1005                                 NULL, NULL, OCI_DESCRIBE_ONLY);
1006    switch (sql->status) {
1007    case OCI_SUCCESS:
1008    case OCI_SUCCESS_WITH_INFO:
1009        break;
1010    case OCI_ERROR:
1011#ifdef DEBUG
1012        OCIErrorGet(sql->err, 1, NULL, &errorcode,
1013                    sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1014        printf("Describing prepared statement: %s\n", sql->buf);
1015#endif
1016    default:
1017        return 1;
1018    }
1019    while (sql->status == OCI_SUCCESS) {
1020        sql->status = OCIParamGet(stmt->stmt, OCI_HTYPE_STMT,
1021                                  sql->err, (dvoid**)&parms, stmt->nout+1);
1022        switch (sql->status) {
1023        case OCI_SUCCESS:
1024            sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
1025                                     &paramtype[stmt->nout],
1026                                     0, OCI_ATTR_DATA_TYPE, sql->err);
1027            sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
1028                                     &paramsize[stmt->nout],
1029                                     0, OCI_ATTR_DATA_SIZE, sql->err);
1030            sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
1031                                     &paramname[stmt->nout],
1032                                     &paramnamelen[stmt->nout],
1033                                     OCI_ATTR_NAME, sql->err);
1034            ++stmt->nout;
1035        }
1036    }
1037    switch (sql->status) {
1038    case OCI_SUCCESS:
1039        break;
1040    case OCI_ERROR:
1041        break;        /* this is what we expect at end-of-loop */
1042    default:
1043        return 1;
1044    }
1045
1046    /* OK, the above works.  We have the params; now OCIDefine them */
1047    stmt->out = apr_palloc(stmt->pool, stmt->nout*sizeof(define_arg));
1048    for (i=0; i<stmt->nout; ++i) {
1049        stmt->out[i].type = paramtype[i];
1050        stmt->out[i].len = stmt->out[i].sz = paramsize[i];
1051        stmt->out[i].name = apr_pstrmemdup(stmt->pool,
1052                                           paramname[i], paramnamelen[i]);
1053        switch (stmt->out[i].type) {
1054        default:
1055            switch (stmt->out[i].type) {
1056            case SQLT_NUM:           /* 2: numeric, Perl worst case=130+38+3 */
1057                stmt->out[i].sz = 171;
1058                break;
1059            case SQLT_CHR:           /* 1: char */
1060            case SQLT_AFC:           /* 96: ANSI fixed char */
1061                stmt->out[i].sz *= 4; /* ugh, wasteful UCS-4 handling */
1062                break;
1063            case SQLT_DAT:           /* 12: date, depends on NLS date format */
1064                stmt->out[i].sz = 75;
1065                break;
1066            case SQLT_BIN:           /* 23: raw binary, perhaps UTF-16? */
1067                stmt->out[i].sz *= 2;
1068                break;
1069            case SQLT_RID:           /* 11: rowid */
1070            case SQLT_RDD:           /* 104: rowid descriptor */
1071                stmt->out[i].sz = 20;
1072                break;
1073            case SQLT_TIMESTAMP:     /* 187: timestamp */
1074            case SQLT_TIMESTAMP_TZ:  /* 188: timestamp with time zone */
1075            case SQLT_INTERVAL_YM:   /* 189: interval year-to-month */
1076            case SQLT_INTERVAL_DS:   /* 190: interval day-to-second */
1077            case SQLT_TIMESTAMP_LTZ: /* 232: timestamp with local time zone */
1078                stmt->out[i].sz = 75;
1079                break;
1080            default:
1081#ifdef DEBUG
1082                printf("Unsupported data type: %d\n", stmt->out[i].type);
1083#endif
1084                break;
1085            }
1086            ++stmt->out[i].sz;
1087            stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
1088            sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
1089                                         sql->err, i+1,
1090                                         stmt->out[i].buf.sval,
1091                                         stmt->out[i].sz, SQLT_STR,
1092                                         &stmt->out[i].ind, &stmt->out[i].len,
1093                                         0, OCI_DEFAULT);
1094            break;
1095        case SQLT_LNG: /* 8: long */
1096            stmt->out[i].sz = sql->long_size * 4 + 4; /* ugh, UCS-4 handling */
1097            stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
1098            sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
1099                                         sql->err, i+1,
1100                                         stmt->out[i].buf.raw,
1101                                         stmt->out[i].sz, SQLT_LVC,
1102                                         &stmt->out[i].ind, NULL,
1103                                         0, OCI_DEFAULT);
1104            break;
1105        case SQLT_LBI: /* 24: long binary, perhaps UTF-16? */
1106            stmt->out[i].sz = sql->long_size * 2 + 4; /* room for int prefix */
1107            stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
1108            sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
1109                                         sql->err, i+1,
1110                                         stmt->out[i].buf.raw,
1111                                         stmt->out[i].sz, SQLT_LVB,
1112                                         &stmt->out[i].ind, NULL,
1113                                         0, OCI_DEFAULT);
1114            break;
1115        case SQLT_BLOB: /* 113 */
1116        case SQLT_CLOB: /* 112 */
1117/*http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/oci05bnd.htm#434937*/
1118            sql->status = OCIDescriptorAlloc(dbd_oracle_env,
1119                                             (dvoid**)&stmt->out[i].buf.lobval,
1120                                             OCI_DTYPE_LOB, 0, NULL);
1121            apr_pool_cleanup_register(stmt->pool, stmt->out[i].buf.lobval,
1122                                      dbd_free_lobdesc,
1123                                      apr_pool_cleanup_null);
1124            sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
1125                                         sql->err, i+1,
1126                                         (dvoid*) &stmt->out[i].buf.lobval,
1127                                         -1, stmt->out[i].type,
1128                                         &stmt->out[i].ind, &stmt->out[i].len,
1129                                         0, OCI_DEFAULT);
1130            break;
1131        }
1132        switch (sql->status) {
1133        case OCI_SUCCESS:
1134            break;
1135        default:
1136            return 1;
1137        }
1138    }
1139    return 0;
1140}
1141
1142static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
1143                             int *nrows, apr_dbd_prepared_t *statement,
1144                             const char **values)
1145{
1146    OCISnapshot *oldsnapshot = NULL;
1147    OCISnapshot *newsnapshot = NULL;
1148    apr_dbd_transaction_t* trans = sql->trans;
1149    int exec_mode;
1150    int_errorcode;
1151
1152    if (trans) {
1153        switch (trans->status) {
1154        case TRANS_ERROR:
1155            return -1;
1156        case TRANS_NONE:
1157            trans = NULL;
1158            break;
1159        case TRANS_1:
1160            oldsnapshot = trans->snapshot1;
1161            newsnapshot = trans->snapshot2;
1162            trans->status = TRANS_2;
1163            break;
1164        case TRANS_2:
1165            oldsnapshot = trans->snapshot2;
1166            newsnapshot = trans->snapshot1;
1167            trans->status = TRANS_1;
1168            break;
1169        }
1170        exec_mode = OCI_DEFAULT;
1171    }
1172    else {
1173        exec_mode = OCI_COMMIT_ON_SUCCESS;
1174    }
1175
1176    dbd_oracle_bind(statement, values);
1177
1178    sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0,
1179                                 oldsnapshot, newsnapshot, exec_mode);
1180    switch (sql->status) {
1181    case OCI_SUCCESS:
1182        break;
1183    case OCI_ERROR:
1184#ifdef DEBUG
1185        OCIErrorGet(sql->err, 1, NULL, &errorcode,
1186                    sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1187        printf("Execute error %d: %s\n", sql->status, sql->buf);
1188#endif
1189        /* fallthrough */
1190    default:
1191        if (TXN_NOTICE_ERRORS(trans)) {
1192            trans->status = TRANS_ERROR;
1193        }
1194        return 1;
1195    }
1196
1197    sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0,
1198                             OCI_ATTR_ROW_COUNT, sql->err);
1199    return 0;
1200}
1201
1202static int dbd_oracle_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
1203                              int *nrows, apr_dbd_prepared_t *statement,
1204                              va_list args)
1205{
1206    const char **values;
1207    int i;
1208
1209    if (sql->trans && sql->trans->status == TRANS_ERROR) {
1210        return -1;
1211    }
1212
1213    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1214
1215    for (i = 0; i < statement->nvals; i++) {
1216        values[i] = va_arg(args, const char*);
1217    }
1218
1219    return dbd_oracle_pquery(pool, sql, nrows, statement, values);
1220}
1221
1222static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql,
1223                              apr_dbd_results_t **results,
1224                              apr_dbd_prepared_t *statement,
1225                              int seek, const char **values)
1226{
1227    int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
1228    OCISnapshot *oldsnapshot = NULL;
1229    OCISnapshot *newsnapshot = NULL;
1230    apr_dbd_transaction_t* trans = sql->trans;
1231    int_errorcode;
1232
1233    if (trans) {
1234        switch (trans->status) {
1235        case TRANS_ERROR:
1236            return 1;
1237        case TRANS_NONE:
1238            trans = NULL;
1239            break;
1240        case TRANS_1:
1241            oldsnapshot = trans->snapshot1;
1242            newsnapshot = trans->snapshot2;
1243            trans->status = TRANS_2;
1244            break;
1245        case TRANS_2:
1246            oldsnapshot = trans->snapshot2;
1247            newsnapshot = trans->snapshot1;
1248            trans->status = TRANS_1;
1249            break;
1250        }
1251    }
1252
1253    dbd_oracle_bind(statement, values);
1254
1255    sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
1256                                 oldsnapshot, newsnapshot, exec_mode);
1257    switch (sql->status) {
1258    case OCI_SUCCESS:
1259        break;
1260    case OCI_ERROR:
1261#ifdef DEBUG
1262        OCIErrorGet(sql->err, 1, NULL, &errorcode,
1263                    sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1264        printf("Executing prepared statement: %s\n", sql->buf);
1265#endif
1266        /* fallthrough */
1267    default:
1268        if (TXN_NOTICE_ERRORS(trans)) {
1269            trans->status = TRANS_ERROR;
1270        }
1271        return 1;
1272    }
1273
1274    if (!*results) {
1275        *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
1276    }
1277    (*results)->handle = sql;
1278    (*results)->statement = statement;
1279    (*results)->seek = seek;
1280    (*results)->rownum = seek ? 0 : -1;
1281    (*results)->pool = pool;
1282
1283    return 0;
1284}
1285
1286static int dbd_oracle_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
1287                               apr_dbd_results_t **results,
1288                               apr_dbd_prepared_t *statement,
1289                               int seek, va_list args)
1290{
1291    const char **values;
1292    int i;
1293
1294    if (sql->trans && sql->trans->status == TRANS_ERROR) {
1295        return -1;
1296    }
1297
1298    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1299
1300    for (i = 0; i < statement->nvals; i++) {
1301        values[i] = va_arg(args, const char*);
1302    }
1303
1304    return dbd_oracle_pselect(pool, sql, results, statement, seek, values);
1305}
1306
1307static void dbd_oracle_bbind(apr_dbd_prepared_t * statement,
1308                             const void **values)
1309{
1310    OCIStmt *stmt = statement->stmt;
1311    apr_dbd_t *sql = statement->handle;
1312    int i, j;
1313    sb2 null_ind = -1;
1314    apr_dbd_type_e type;
1315
1316    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
1317        type = (values[j] == NULL ? APR_DBD_TYPE_NULL
1318                                  : statement->args[i].type);
1319
1320        switch (type) {
1321        case APR_DBD_TYPE_TINY:
1322            statement->args[i].value.ival = *(char*)values[j];
1323            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1324                                       sql->err, i + 1,
1325                                       &statement->args[i].value.ival,
1326                                       sizeof(statement->args[i].value.ival),
1327                                       SQLT_INT,
1328                                       &statement->args[i].ind, NULL,
1329                                       (ub2) 0, (ub4) 0,
1330                                       (ub4 *) 0, OCI_DEFAULT);
1331            break;
1332        case APR_DBD_TYPE_UTINY:
1333            statement->args[i].value.uval = *(unsigned char*)values[j];
1334            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1335                                       sql->err, i + 1,
1336                                       &statement->args[i].value.uval,
1337                                       sizeof(statement->args[i].value.uval),
1338                                       SQLT_UIN,
1339                                       &statement->args[i].ind, NULL,
1340                                       (ub2) 0, (ub4) 0,
1341                                       (ub4 *) 0, OCI_DEFAULT);
1342            break;
1343        case APR_DBD_TYPE_SHORT:
1344            statement->args[i].value.ival = *(short*)values[j];
1345            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1346                                       sql->err, i + 1,
1347                                       &statement->args[i].value.ival,
1348                                       sizeof(statement->args[i].value.ival),
1349                                       SQLT_INT,
1350                                       &statement->args[i].ind, NULL,
1351                                       (ub2) 0, (ub4) 0,
1352                                       (ub4 *) 0, OCI_DEFAULT);
1353            break;
1354        case APR_DBD_TYPE_USHORT:
1355            statement->args[i].value.uval = *(unsigned short*)values[j];
1356            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1357                                       sql->err, i + 1,
1358                                       &statement->args[i].value.uval,
1359                                       sizeof(statement->args[i].value.uval),
1360                                       SQLT_UIN,
1361                                       &statement->args[i].ind, NULL,
1362                                       (ub2) 0, (ub4) 0,
1363                                       (ub4 *) 0, OCI_DEFAULT);
1364            break;
1365        case APR_DBD_TYPE_INT:
1366            statement->args[i].value.ival = *(int*)values[j];
1367            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1368                                       sql->err, i + 1,
1369                                       &statement->args[i].value.ival,
1370                                       sizeof(statement->args[i].value.ival),
1371                                       SQLT_INT,
1372                                       &statement->args[i].ind, NULL,
1373                                       (ub2) 0, (ub4) 0,
1374                                       (ub4 *) 0, OCI_DEFAULT);
1375            break;
1376        case APR_DBD_TYPE_UINT:
1377            statement->args[i].value.uval = *(unsigned int*)values[j];
1378            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1379                                       sql->err, i + 1,
1380                                       &statement->args[i].value.uval,
1381                                       sizeof(statement->args[i].value.uval),
1382                                       SQLT_UIN,
1383                                       &statement->args[i].ind, NULL,
1384                                       (ub2) 0, (ub4) 0,
1385                                       (ub4 *) 0, OCI_DEFAULT);
1386            break;
1387        case APR_DBD_TYPE_LONG:
1388            statement->args[i].value.sval =
1389                apr_psprintf(statement->pool, "%ld", *(long*)values[j]);
1390            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1391                                       sql->err, i + 1,
1392                                       statement->args[i].value.sval,
1393                                       strlen(statement->args[i].value.sval)+1,
1394                                       SQLT_STR,
1395                                       &statement->args[i].ind, NULL,
1396                                       (ub2) 0, (ub4) 0,
1397                                       (ub4 *) 0, OCI_DEFAULT);
1398            break;
1399        case APR_DBD_TYPE_ULONG:
1400            statement->args[i].value.sval =
1401                apr_psprintf(statement->pool, "%lu",
1402                                              *(unsigned long*)values[j]);
1403            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1404                                       sql->err, i + 1,
1405                                       statement->args[i].value.sval,
1406                                       strlen(statement->args[i].value.sval)+1,
1407                                       SQLT_STR,
1408                                       &statement->args[i].ind, NULL,
1409                                       (ub2) 0, (ub4) 0,
1410                                       (ub4 *) 0, OCI_DEFAULT);
1411            break;
1412        case APR_DBD_TYPE_LONGLONG:
1413            statement->args[i].value.sval =
1414                apr_psprintf(statement->pool, "%" APR_INT64_T_FMT,
1415                                              *(apr_int64_t*)values[j]);
1416            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1417                                       sql->err, i + 1,
1418                                       statement->args[i].value.sval,
1419                                       strlen(statement->args[i].value.sval)+1,
1420                                       SQLT_STR,
1421                                       &statement->args[i].ind, NULL,
1422                                       (ub2) 0, (ub4) 0,
1423                                       (ub4 *) 0, OCI_DEFAULT);
1424            break;
1425        case APR_DBD_TYPE_ULONGLONG:
1426            statement->args[i].value.sval =
1427                apr_psprintf(statement->pool, "%" APR_UINT64_T_FMT,
1428                                              *(apr_uint64_t*)values[j]);
1429            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1430                                       sql->err, i + 1,
1431                                       statement->args[i].value.sval,
1432                                       strlen(statement->args[i].value.sval)+1,
1433                                       SQLT_UIN,
1434                                       &statement->args[i].ind, NULL,
1435                                       (ub2) 0, (ub4) 0,
1436                                       (ub4 *) 0, OCI_DEFAULT);
1437            break;
1438        case APR_DBD_TYPE_FLOAT:
1439            statement->args[i].value.fval = *(float*)values[j];
1440            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1441                                       sql->err, i + 1,
1442                                       &statement->args[i].value.fval,
1443                                       sizeof(statement->args[i].value.fval),
1444                                       SQLT_FLT,
1445                                       &statement->args[i].ind, NULL,
1446                                       (ub2) 0, (ub4) 0,
1447                                       (ub4 *) 0, OCI_DEFAULT);
1448            break;
1449        case APR_DBD_TYPE_DOUBLE:
1450            statement->args[i].value.fval = *(double*)values[j];
1451            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1452                                       sql->err, i + 1,
1453                                       &statement->args[i].value.fval,
1454                                       sizeof(statement->args[i].value.fval),
1455                                       SQLT_FLT,
1456                                       &statement->args[i].ind, NULL,
1457                                       (ub2) 0, (ub4) 0,
1458                                       (ub4 *) 0, OCI_DEFAULT);
1459            break;
1460        case APR_DBD_TYPE_STRING:
1461        case APR_DBD_TYPE_TEXT:
1462        case APR_DBD_TYPE_TIME:
1463        case APR_DBD_TYPE_DATE:
1464        case APR_DBD_TYPE_DATETIME:
1465        case APR_DBD_TYPE_TIMESTAMP:
1466        case APR_DBD_TYPE_ZTIMESTAMP:
1467            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1468                                       sql->err, i + 1,
1469                                       (dvoid*) values[j],
1470                                       strlen(values[j]) + 1,
1471                                       SQLT_STR,
1472                                       &statement->args[i].ind, NULL,
1473                                       (ub2) 0, (ub4) 0,
1474                                       (ub4 *) 0, OCI_DEFAULT);
1475            break;
1476        case APR_DBD_TYPE_BLOB:
1477            {
1478            char *data = (char *)values[j];
1479            apr_size_t size = *(apr_size_t*)values[++j];
1480
1481            /* skip table and column for now */
1482            j += 2;
1483
1484            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1485                                       sql->err, i + 1,
1486                                       data, size, SQLT_LBI,
1487                                       &statement->args[i].ind,
1488                                       NULL,
1489                                       (ub2) 0, (ub4) 0,
1490                                       (ub4 *) 0, OCI_DEFAULT);
1491            }
1492            break;
1493        case APR_DBD_TYPE_CLOB:
1494            {
1495            char *data = (char *)values[j];
1496            apr_size_t size = *(apr_size_t*)values[++j];
1497
1498            /* skip table and column for now */
1499            j += 2;
1500
1501            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1502                                       sql->err, i + 1,
1503                                       data, size, SQLT_LNG,
1504                                       &statement->args[i].ind,
1505                                       NULL,
1506                                       (ub2) 0, (ub4) 0,
1507                                       (ub4 *) 0, OCI_DEFAULT);
1508            }
1509            break;
1510        case APR_DBD_TYPE_NULL:
1511        default:
1512            sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
1513                                       sql->err, i + 1,
1514                                       NULL, 0, SQLT_STR,
1515                                       &null_ind, NULL,
1516                                       (ub2) 0, (ub4) 0,
1517                                       (ub4 *) 0, OCI_DEFAULT);
1518            break;
1519        }
1520
1521        if (sql->status != OCI_SUCCESS) {
1522            return;
1523        }
1524    }
1525
1526    return;
1527}
1528
1529static int dbd_oracle_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
1530                              int *nrows, apr_dbd_prepared_t * statement,
1531                              const void **values)
1532{
1533    OCISnapshot *oldsnapshot = NULL;
1534    OCISnapshot *newsnapshot = NULL;
1535    apr_dbd_transaction_t* trans = sql->trans;
1536    int exec_mode;
1537    int_errorcode;
1538
1539    if (trans) {
1540        switch (trans->status) {
1541        case TRANS_ERROR:
1542            return -1;
1543        case TRANS_NONE:
1544            trans = NULL;
1545            break;
1546        case TRANS_1:
1547            oldsnapshot = trans->snapshot1;
1548            newsnapshot = trans->snapshot2;
1549            trans->status = TRANS_2;
1550            break;
1551        case TRANS_2:
1552            oldsnapshot = trans->snapshot2;
1553            newsnapshot = trans->snapshot1;
1554            trans->status = TRANS_1;
1555            break;
1556        }
1557        exec_mode = OCI_DEFAULT;
1558    }
1559    else {
1560        exec_mode = OCI_COMMIT_ON_SUCCESS;
1561    }
1562
1563    dbd_oracle_bbind(statement, values);
1564
1565    sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0,
1566                                 oldsnapshot, newsnapshot, exec_mode);
1567    switch (sql->status) {
1568    case OCI_SUCCESS:
1569        break;
1570    case OCI_ERROR:
1571#ifdef DEBUG
1572        OCIErrorGet(sql->err, 1, NULL, &errorcode,
1573                    sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1574        printf("Execute error %d: %s\n", sql->status, sql->buf);
1575#endif
1576        /* fallthrough */
1577    default:
1578        if (TXN_NOTICE_ERRORS(trans)) {
1579            trans->status = TRANS_ERROR;
1580        }
1581        return 1;
1582    }
1583
1584    sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0,
1585                             OCI_ATTR_ROW_COUNT, sql->err);
1586    return 0;
1587}
1588
1589static int dbd_oracle_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
1590                               int *nrows, apr_dbd_prepared_t * statement,
1591                               va_list args)
1592{
1593    const void **values;
1594    int i;
1595
1596    if (sql->trans && sql->trans->status == TRANS_ERROR) {
1597        return -1;
1598    }
1599
1600    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1601
1602    for (i = 0; i < statement->nvals; i++) {
1603        values[i] = va_arg(args, const void*);
1604    }
1605
1606    return dbd_oracle_pbquery(pool, sql, nrows, statement, values);
1607}
1608
1609static int dbd_oracle_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
1610                               apr_dbd_results_t ** results,
1611                               apr_dbd_prepared_t * statement,
1612                               int seek, const void **values)
1613{
1614    int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
1615    OCISnapshot *oldsnapshot = NULL;
1616    OCISnapshot *newsnapshot = NULL;
1617    apr_dbd_transaction_t* trans = sql->trans;
1618    int_errorcode;
1619
1620    if (trans) {
1621        switch (trans->status) {
1622        case TRANS_ERROR:
1623            return 1;
1624        case TRANS_NONE:
1625            trans = NULL;
1626            break;
1627        case TRANS_1:
1628            oldsnapshot = trans->snapshot1;
1629            newsnapshot = trans->snapshot2;
1630            trans->status = TRANS_2;
1631            break;
1632        case TRANS_2:
1633            oldsnapshot = trans->snapshot2;
1634            newsnapshot = trans->snapshot1;
1635            trans->status = TRANS_1;
1636            break;
1637        }
1638    }
1639
1640    dbd_oracle_bbind(statement, values);
1641
1642    sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
1643                                 oldsnapshot, newsnapshot, exec_mode);
1644    switch (sql->status) {
1645    case OCI_SUCCESS:
1646        break;
1647    case OCI_ERROR:
1648#ifdef DEBUG
1649        OCIErrorGet(sql->err, 1, NULL, &errorcode,
1650                    sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1651        printf("Executing prepared statement: %s\n", sql->buf);
1652#endif
1653        /* fallthrough */
1654    default:
1655        if (TXN_NOTICE_ERRORS(trans)) {
1656            trans->status = TRANS_ERROR;
1657        }
1658        return 1;
1659    }
1660
1661    if (!*results) {
1662        *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
1663    }
1664    (*results)->handle = sql;
1665    (*results)->statement = statement;
1666    (*results)->seek = seek;
1667    (*results)->rownum = seek ? 0 : -1;
1668    (*results)->pool = pool;
1669
1670    return 0;
1671}
1672
1673static int dbd_oracle_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
1674                                apr_dbd_results_t ** results,
1675                                apr_dbd_prepared_t * statement, int seek,
1676                                va_list args)
1677{
1678    const void **values;
1679    int i;
1680
1681    if (sql->trans && sql->trans->status == TRANS_ERROR) {
1682        return -1;
1683    }
1684
1685    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1686
1687    for (i = 0; i < statement->nvals; i++) {
1688        values[i] = va_arg(args, const void*);
1689    }
1690
1691    return dbd_oracle_pbselect(pool, sql, results, statement, seek, values);
1692}
1693
1694static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
1695                                        apr_dbd_transaction_t **trans)
1696{
1697    int ret = 0;
1698    int_errorcode;
1699    if (*trans) {
1700        dbd_oracle_end_transaction(*trans);
1701    }
1702    else {
1703        *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1704        OCIHandleAlloc(dbd_oracle_env, (dvoid**)&(*trans)->trans,
1705                       OCI_HTYPE_TRANS, 0, 0);
1706        OCIAttrSet(sql->svc, OCI_HTYPE_SVCCTX, (*trans)->trans, 0,
1707                   OCI_ATTR_TRANS, sql->err);
1708    }
1709
1710
1711    sql->status = OCITransStart(sql->svc, sql->err, TRANS_TIMEOUT,
1712                                OCI_TRANS_NEW);
1713    switch (sql->status) {
1714    case OCI_ERROR:
1715#ifdef DEBUG
1716        OCIErrorGet(sql->err, 1, NULL, &errorcode, sql->buf,
1717                    sizeof(sql->buf), OCI_HTYPE_ERROR);
1718        printf("Transaction: %s\n", sql->buf);
1719#endif
1720        ret = 1;
1721        break;
1722    case OCI_SUCCESS:
1723        (*trans)->handle = sql;
1724        (*trans)->status = TRANS_1;
1725        sql->trans = *trans;
1726        switch (OCIDescriptorAlloc(dbd_oracle_env,
1727                                   (dvoid**)&(*trans)->snapshot1,
1728                                   OCI_DTYPE_SNAP, 0, NULL)) {
1729        case OCI_SUCCESS:
1730            apr_pool_cleanup_register(pool, (*trans)->snapshot1,
1731                                      dbd_free_snapshot, apr_pool_cleanup_null);
1732            break;
1733        case OCI_INVALID_HANDLE:
1734            ret = 1;
1735            break;
1736        }
1737        switch (OCIDescriptorAlloc(dbd_oracle_env,
1738                                   (dvoid**)&(*trans)->snapshot2,
1739                                   OCI_DTYPE_SNAP, 0, NULL)) {
1740        case OCI_SUCCESS:
1741            apr_pool_cleanup_register(pool, (*trans)->snapshot2,
1742                                      dbd_free_snapshot, apr_pool_cleanup_null);
1743            break;
1744        case OCI_INVALID_HANDLE:
1745            ret = 1;
1746            break;
1747        }
1748        break;
1749    default:
1750        ret = 1;
1751        break;
1752    }
1753    return ret;
1754}
1755
1756static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans)
1757{
1758    int ret = 1;             /* no transaction is an error cond */
1759    sword status;
1760    apr_dbd_t *handle = trans->handle;
1761    if (trans) {
1762        switch (trans->status) {
1763        case TRANS_NONE:     /* No trans is an error here */
1764            status = OCI_ERROR;
1765            break;
1766        case TRANS_ERROR:
1767            status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
1768            break;
1769        default:
1770            /* rollback on explicit rollback request */
1771            if (TXN_DO_ROLLBACK(trans)) {
1772                status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
1773            } else {
1774                status = OCITransCommit(handle->svc, handle->err, OCI_DEFAULT);
1775            }
1776            break;
1777        }
1778
1779        handle->trans = NULL;
1780
1781        switch (status) {
1782        case OCI_SUCCESS:
1783            ret = 0;
1784            break;
1785        default:
1786            ret = 3;
1787            break;
1788        }
1789    }
1790    return ret;
1791}
1792
1793static int dbd_oracle_transaction_mode_get(apr_dbd_transaction_t *trans)
1794{
1795    if (!trans)
1796        return APR_DBD_TRANSACTION_COMMIT;
1797
1798    return trans->mode;
1799}
1800
1801static int dbd_oracle_transaction_mode_set(apr_dbd_transaction_t *trans,
1802                                           int mode)
1803{
1804    if (!trans)
1805        return APR_DBD_TRANSACTION_COMMIT;
1806
1807    return trans->mode = (mode & TXN_MODE_BITS);
1808}
1809
1810/* This doesn't work for BLOB because of NULLs, but it can fake it
1811 * if the BLOB is really a string
1812 */
1813static const char *dbd_oracle_get_entry(const apr_dbd_row_t *row, int n)
1814{
1815    ub4 len = 0;
1816    ub1 csform = 0;
1817    ub2 csid = 0;
1818    apr_size_t buflen = 0;
1819    char *buf = NULL;
1820    define_arg *val = &row->res->statement->out[n];
1821    apr_dbd_t *sql = row->res->handle;
1822    int_errorcode;
1823
1824    if ((n < 0) || (n >= row->res->statement->nout) || (val->ind == -1)) {
1825        return NULL;
1826    }
1827
1828    switch (val->type) {
1829    case SQLT_BLOB:
1830    case SQLT_CLOB:
1831        sql->status = OCILobGetLength(sql->svc, sql->err, val->buf.lobval,
1832                                      &len);
1833        switch (sql->status) {
1834        case OCI_SUCCESS:
1835        case OCI_SUCCESS_WITH_INFO:
1836            if (len == 0) {
1837                buf = "";
1838            }
1839            break;
1840        case OCI_ERROR:
1841#ifdef DEBUG
1842            OCIErrorGet(sql->err, 1, NULL, &errorcode,
1843                        sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1844            printf("Finding LOB length: %s\n", sql->buf);
1845            break;
1846#endif
1847        default:
1848            break;
1849        }
1850
1851        if (len == 0) {
1852            break;
1853        }
1854
1855        if (val->type == APR_DBD_TYPE_CLOB) {
1856#if 1
1857            /* Is this necessary, or can it be defaulted? */
1858            sql->status = OCILobCharSetForm(dbd_oracle_env, sql->err,
1859                                            val->buf.lobval, &csform);
1860            if (sql->status == OCI_SUCCESS) {
1861                sql->status = OCILobCharSetId(dbd_oracle_env, sql->err,
1862                                              val->buf.lobval, &csid);
1863            }
1864            switch (sql->status) {
1865            case OCI_SUCCESS:
1866            case OCI_SUCCESS_WITH_INFO:
1867                buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */
1868                /* zeroise all - where the string ends depends on charset */
1869                buf = apr_pcalloc(row->pool, buflen);
1870                break;
1871#ifdef DEBUG
1872            case OCI_ERROR:
1873                OCIErrorGet(sql->err, 1, NULL, &errorcode,
1874                            sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1875                printf("Reading LOB character set: %s\n", sql->buf);
1876                break; /*** XXX?? ***/
1877#endif
1878            default:
1879                break; /*** XXX?? ***/
1880            }
1881#else   /* ignore charset */
1882            buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */
1883            /* zeroise all - where the string ends depends on charset */
1884            buf = apr_pcalloc(row->pool, buflen);
1885#endif
1886        } else {
1887            /* BUG: this'll only work if the BLOB looks like a string */
1888            buflen = len;
1889            buf = apr_palloc(row->pool, buflen+1);
1890            buf[buflen] = 0;
1891        }
1892
1893        if (!buf) {
1894            break;
1895        }
1896
1897        sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval,
1898                                 &len, 1, (dvoid*) buf, buflen,
1899                                 NULL, NULL, csid, csform);
1900        switch (sql->status) {
1901        case OCI_SUCCESS:
1902        case OCI_SUCCESS_WITH_INFO:
1903            break;
1904#ifdef DEBUG
1905        case OCI_ERROR:
1906            OCIErrorGet(sql->err, 1, NULL, &errorcode,
1907                        sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1908            printf("Reading LOB: %s\n", sql->buf);
1909            buf = NULL; /*** XXX?? ***/
1910            break;
1911#endif
1912        default:
1913            buf = NULL; /*** XXX?? ***/
1914            break;
1915        }
1916
1917        break;
1918    case SQLT_LNG:
1919    case SQLT_LBI:
1920        /* raw is struct { ub4 len; char *buf; } */
1921        len = *(ub4*) val->buf.raw;
1922        buf = apr_pstrndup(row->pool, val->buf.sval + sizeof(ub4), len);
1923        break;
1924    default:
1925        buf = apr_pstrndup(row->pool, val->buf.sval, val->len);
1926        break;
1927    }
1928    return (const char*) buf;
1929}
1930
1931/* XXX Should this use Oracle proper API instead of calling get_entry()? */
1932static apr_status_t dbd_oracle_datum_get(const apr_dbd_row_t *row, int n,
1933                                         apr_dbd_type_e type, void *data)
1934{
1935    define_arg *val = &row->res->statement->out[n];
1936    const char *entry;
1937
1938    if ((n < 0) || (n >= row->res->statement->nout)) {
1939        return APR_EGENERAL;
1940    }
1941
1942    if(val->ind == -1) {
1943        return APR_ENOENT;
1944    }
1945
1946    switch (type) {
1947    case APR_DBD_TYPE_TINY:
1948        entry = dbd_oracle_get_entry(row, n);
1949        if (entry == NULL) {
1950            return APR_ENOENT;
1951        }
1952        *(char*)data = atoi(entry);
1953        break;
1954    case APR_DBD_TYPE_UTINY:
1955        entry = dbd_oracle_get_entry(row, n);
1956        if (entry == NULL) {
1957            return APR_ENOENT;
1958        }
1959        *(unsigned char*)data = atoi(entry);
1960        break;
1961    case APR_DBD_TYPE_SHORT:
1962        entry = dbd_oracle_get_entry(row, n);
1963        if (entry == NULL) {
1964            return APR_ENOENT;
1965        }
1966        *(short*)data = atoi(entry);
1967        break;
1968    case APR_DBD_TYPE_USHORT:
1969        entry = dbd_oracle_get_entry(row, n);
1970        if (entry == NULL) {
1971            return APR_ENOENT;
1972        }
1973        *(unsigned short*)data = atoi(entry);
1974        break;
1975    case APR_DBD_TYPE_INT:
1976        entry = dbd_oracle_get_entry(row, n);
1977        if (entry == NULL) {
1978            return APR_ENOENT;
1979        }
1980        *(int*)data = atoi(entry);
1981        break;
1982    case APR_DBD_TYPE_UINT:
1983        entry = dbd_oracle_get_entry(row, n);
1984        if (entry == NULL) {
1985            return APR_ENOENT;
1986        }
1987        *(unsigned int*)data = atoi(entry);
1988        break;
1989    case APR_DBD_TYPE_LONG:
1990        entry = dbd_oracle_get_entry(row, n);
1991        if (entry == NULL) {
1992            return APR_ENOENT;
1993        }
1994        *(long*)data = atol(entry);
1995        break;
1996    case APR_DBD_TYPE_ULONG:
1997        entry = dbd_oracle_get_entry(row, n);
1998        if (entry == NULL) {
1999            return APR_ENOENT;
2000        }
2001        *(unsigned long*)data = atol(entry);
2002        break;
2003    case APR_DBD_TYPE_LONGLONG:
2004        entry = dbd_oracle_get_entry(row, n);
2005        if (entry == NULL) {
2006            return APR_ENOENT;
2007        }
2008        *(apr_int64_t*)data = apr_atoi64(entry);
2009        break;
2010    case APR_DBD_TYPE_ULONGLONG:
2011        entry = dbd_oracle_get_entry(row, n);
2012        if (entry == NULL) {
2013            return APR_ENOENT;
2014        }
2015        *(apr_uint64_t*)data = apr_atoi64(entry);
2016        break;
2017    case APR_DBD_TYPE_FLOAT:
2018        entry = dbd_oracle_get_entry(row, n);
2019        if (entry == NULL) {
2020            return APR_ENOENT;
2021        }
2022        *(float*)data = (float)atof(entry);
2023        break;
2024    case APR_DBD_TYPE_DOUBLE:
2025        entry = dbd_oracle_get_entry(row, n);
2026        if (entry == NULL) {
2027            return APR_ENOENT;
2028        }
2029        *(double*)data = atof(entry);
2030        break;
2031    case APR_DBD_TYPE_STRING:
2032    case APR_DBD_TYPE_TEXT:
2033    case APR_DBD_TYPE_TIME:
2034    case APR_DBD_TYPE_DATE:
2035    case APR_DBD_TYPE_DATETIME:
2036    case APR_DBD_TYPE_TIMESTAMP:
2037    case APR_DBD_TYPE_ZTIMESTAMP:
2038        entry = dbd_oracle_get_entry(row, n);
2039        if (entry == NULL) {
2040            return APR_ENOENT;
2041        }
2042        *(char**)data = (char*)entry;
2043        break;
2044    case APR_DBD_TYPE_BLOB:
2045    case APR_DBD_TYPE_CLOB:
2046        {
2047        apr_bucket *e;
2048        apr_bucket_brigade *b = (apr_bucket_brigade*)data;
2049        apr_dbd_t *sql = row->res->handle;
2050        ub4 len = 0;
2051
2052        switch (val->type) {
2053        case SQLT_BLOB:
2054        case SQLT_CLOB:
2055            sql->status = OCILobGetLength(sql->svc, sql->err,
2056                                          val->buf.lobval, &len);
2057            switch(sql->status) {
2058            case OCI_SUCCESS:
2059            case OCI_SUCCESS_WITH_INFO:
2060                if (len == 0) {
2061                    e = apr_bucket_eos_create(b->bucket_alloc);
2062                }
2063                else {
2064                    e = apr_bucket_lob_create(row, n, 0, len,
2065                                              row->pool, b->bucket_alloc);
2066                }
2067                break;
2068            default:
2069                return APR_ENOENT;
2070            }
2071            break;
2072        default:
2073            entry = dbd_oracle_get_entry(row, n);
2074            if (entry == NULL) {
2075                return APR_ENOENT;
2076            }
2077            e = apr_bucket_pool_create(entry, strlen(entry),
2078                                       row->pool, b->bucket_alloc);
2079            break;
2080        }
2081        APR_BRIGADE_INSERT_TAIL(b, e);
2082        }
2083        break;
2084    case APR_DBD_TYPE_NULL:
2085        *(void**)data = NULL;
2086        break;
2087    default:
2088        return APR_EGENERAL;
2089    }
2090
2091    return APR_SUCCESS;
2092}
2093
2094static apr_status_t dbd_oracle_close(apr_dbd_t *handle)
2095{
2096    /* FIXME: none of the oracle docs/examples say anything about
2097     * closing/releasing handles.  Which seems unlikely ...
2098     */
2099
2100    /* OK, let's grab from cdemo again.
2101     * cdemo81 does nothing; cdemo82 does OCIHandleFree on the handles
2102     */
2103    switch (OCISessionEnd(handle->svc, handle->err, handle->auth,
2104            (ub4)OCI_DEFAULT)) {
2105    default:
2106        break;
2107    }
2108    switch (OCIServerDetach(handle->svr, handle->err, (ub4) OCI_DEFAULT )) {
2109    default:
2110        break;
2111    }
2112    /* does OCISessionEnd imply this? */
2113    switch (OCIHandleFree((dvoid *) handle->auth, (ub4) OCI_HTYPE_SESSION)) {
2114    default:
2115        break;
2116    }
2117    switch (OCIHandleFree((dvoid *) handle->svr, (ub4) OCI_HTYPE_SERVER)) {
2118    default:
2119        break;
2120    }
2121    switch (OCIHandleFree((dvoid *) handle->svc, (ub4) OCI_HTYPE_SVCCTX)) {
2122    default:
2123        break;
2124    }
2125    switch (OCIHandleFree((dvoid *) handle->err, (ub4) OCI_HTYPE_ERROR)) {
2126    default:
2127        break;
2128    }
2129    return APR_SUCCESS;
2130}
2131
2132static apr_status_t dbd_oracle_check_conn(apr_pool_t *pool, apr_dbd_t *sql)
2133{
2134    apr_dbd_results_t *res = NULL;
2135    apr_dbd_row_t *row = NULL;
2136
2137    if(dbd_oracle_pselect(pool, sql, &res, sql->check_conn_stmt,
2138                          0, NULL) != 0) {
2139        return APR_EGENERAL;
2140    }
2141
2142    if(dbd_oracle_get_row(pool, res, &row, -1) != 0) {
2143        return APR_EGENERAL;
2144    }
2145
2146    if(dbd_oracle_get_row(pool, res, &row, -1) != -1) {
2147        return APR_EGENERAL;
2148    }
2149
2150    return APR_SUCCESS;
2151}
2152
2153static int dbd_oracle_select_db(apr_pool_t *pool, apr_dbd_t *handle,
2154                                const char *name)
2155{
2156    /* FIXME: need to find this in the docs */
2157    return APR_ENOTIMPL;
2158}
2159
2160static void *dbd_oracle_native(apr_dbd_t *handle)
2161{
2162    /* FIXME: can we do anything better?  Oracle doesn't seem to have
2163     * a concept of a handle in the sense we use it.
2164     */
2165    return dbd_oracle_env;
2166}
2167
2168static int dbd_oracle_num_cols(apr_dbd_results_t* res)
2169{
2170    return res->statement->nout;
2171}
2172
2173static int dbd_oracle_num_tuples(apr_dbd_results_t* res)
2174{
2175    if (!res->seek) {
2176        return -1;
2177    }
2178    if (res->nrows >= 0) {
2179        return res->nrows;
2180    }
2181    res->handle->status = OCIAttrGet(res->statement->stmt, OCI_HTYPE_STMT,
2182                                     &res->nrows, 0, OCI_ATTR_ROW_COUNT,
2183                                     res->handle->err);
2184    return res->nrows;
2185}
2186
2187APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_oracle_driver = {
2188    "oracle",
2189    dbd_oracle_init,
2190    dbd_oracle_native,
2191    dbd_oracle_open,
2192    dbd_oracle_check_conn,
2193    dbd_oracle_close,
2194    dbd_oracle_select_db,
2195    dbd_oracle_start_transaction,
2196    dbd_oracle_end_transaction,
2197    dbd_oracle_query,
2198    dbd_oracle_select,
2199    dbd_oracle_num_cols,
2200    dbd_oracle_num_tuples,
2201    dbd_oracle_get_row,
2202    dbd_oracle_get_entry,
2203    dbd_oracle_error,
2204    dbd_oracle_escape,
2205    dbd_oracle_prepare,
2206    dbd_oracle_pvquery,
2207    dbd_oracle_pvselect,
2208    dbd_oracle_pquery,
2209    dbd_oracle_pselect,
2210    dbd_oracle_get_name,
2211    dbd_oracle_transaction_mode_get,
2212    dbd_oracle_transaction_mode_set,
2213    ":apr%d",
2214    dbd_oracle_pvbquery,
2215    dbd_oracle_pvbselect,
2216    dbd_oracle_pbquery,
2217    dbd_oracle_pbselect,
2218    dbd_oracle_datum_get
2219};
2220#endif
2221