| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* Developed initially by Nick Kew and Chris Darroch. |
| * Contributed to the APR project by kind permission of |
| * Pearson Education Core Technology Group (CTG), |
| * formerly Central Media Group (CMG). |
| */ |
| |
| /* apr_dbd_oracle - a painful attempt |
| * |
| * Based first on the documentation at |
| * http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/toc.htm |
| * |
| * Those docs have a lot of internal inconsistencies, contradictions, etc |
| * So I've snarfed the demo programs (from Oracle 8, not included in |
| * the current downloadable oracle), and used code from them. |
| * |
| * Why do cdemo81.c and cdemo82.c do the same thing in very different ways? |
| * e.g. cdemo82 releases all its handle on shutdown; cdemo81 doesn't |
| * |
| * All the ORA* functions return a "sword". Some of them are documented; |
| * others aren't. So I've adopted a policy of using switch statements |
| * everywhere, even when we're not doing anything with the return values. |
| * |
| * This makes no attempt at performance tuning, such as setting |
| * prefetch cache size. We need some actual performance data |
| * to make that meaningful. Input from someone with experience |
| * as a sysop using oracle would be a good start. |
| */ |
| |
| /* shut compiler up */ |
| #ifdef DEBUG |
| #define int_errorcode int errorcode |
| #else |
| #define int_errorcode |
| #endif |
| |
| #include "apu.h" |
| |
| #if APU_HAVE_ORACLE |
| |
| #include <ctype.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #include <oci.h> |
| |
| #include "apr_strings.h" |
| #include "apr_lib.h" |
| #include "apr_time.h" |
| #include "apr_hash.h" |
| #include "apr_buckets.h" |
| |
| #define TRANS_TIMEOUT 30 |
| #define MAX_ARG_LEN 256 /* in line with other apr_dbd drivers. We alloc this |
| * lots of times, so a large value gets hungry. |
| * Should really make it configurable |
| */ |
| #define DEFAULT_LONG_SIZE 4096 |
| #define DBD_ORACLE_MAX_COLUMNS 256 |
| #define NUMERIC_FIELD_SIZE 32 |
| |
| #define CHECK_CONN_QUERY "SELECT 1 FROM dual" |
| |
| #define ERR_BUF_SIZE 200 |
| |
| #ifdef DEBUG |
| #include <stdio.h> |
| #endif |
| |
| #include "apr_dbd_internal.h" |
| |
| /* declarations */ |
| static const char *dbd_oracle_error(apr_dbd_t *sql, int n); |
| static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql, |
| const char *query, const char *label, |
| int nargs, int nvals, apr_dbd_type_e *types, |
| apr_dbd_prepared_t **statement); |
| static int outputParams(apr_dbd_t*, apr_dbd_prepared_t*); |
| static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql, |
| apr_dbd_results_t **results, |
| apr_dbd_prepared_t *statement, |
| int seek, const char **values); |
| static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql, |
| int *nrows, apr_dbd_prepared_t *statement, |
| const char **values); |
| static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql, |
| apr_dbd_transaction_t **trans); |
| static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans); |
| |
| struct apr_dbd_transaction_t { |
| int mode; |
| enum { TRANS_NONE, TRANS_ERROR, TRANS_1, TRANS_2 } status; |
| apr_dbd_t *handle; |
| OCITrans *trans; |
| OCISnapshot *snapshot1; |
| OCISnapshot *snapshot2; |
| }; |
| |
| struct apr_dbd_results_t { |
| apr_pool_t *pool; |
| apr_dbd_t* handle; |
| unsigned int rownum; |
| int seek; |
| int nrows; |
| apr_dbd_prepared_t *statement; |
| }; |
| |
| struct apr_dbd_t { |
| sword status; |
| OCIError *err; |
| OCIServer *svr; |
| OCISvcCtx *svc; |
| OCISession *auth; |
| apr_dbd_transaction_t* trans; |
| apr_pool_t *pool; |
| char buf[ERR_BUF_SIZE]; /* for error messages */ |
| apr_size_t long_size; |
| apr_dbd_prepared_t *check_conn_stmt; |
| }; |
| |
| struct apr_dbd_row_t { |
| int n; |
| apr_dbd_results_t *res; |
| apr_pool_t *pool; |
| }; |
| |
| typedef struct { |
| apr_dbd_type_e type; |
| sb2 ind; |
| sb4 len; |
| OCIBind *bind; |
| union { |
| void *raw; |
| char *sval; |
| int ival; |
| unsigned int uval; |
| double fval; |
| OCILobLocator *lobval; |
| } value; |
| } bind_arg; |
| |
| typedef struct { |
| int type; |
| sb2 ind; |
| ub2 len; /* length of actual output */ |
| OCIDefine *defn; |
| apr_size_t sz; /* length of buf for output */ |
| union { |
| void *raw; |
| char *sval; |
| OCILobLocator *lobval; |
| } buf; |
| const char *name; |
| } define_arg; |
| |
| struct apr_dbd_prepared_t { |
| OCIStmt *stmt; |
| int nargs; |
| int nvals; |
| bind_arg *args; |
| int nout; |
| define_arg *out; |
| apr_dbd_t *handle; |
| apr_pool_t *pool; |
| int type; |
| }; |
| |
| /* AFAICT from the docs, the OCIEnv thingey can be used async |
| * across threads, so lets have a global one. |
| * |
| * We'll need shorter-lived envs to deal with requests and connections |
| * |
| * Hmmm, that doesn't work: we don't have a usermem framework. |
| * OK, forget about using APR pools here, until we figure out |
| * the right way to do it (if such a thing exists). |
| */ |
| static OCIEnv *dbd_oracle_env = NULL; |
| |
| /* Oracle specific bucket for BLOB/CLOB types */ |
| typedef struct apr_bucket_lob apr_bucket_lob; |
| /** |
| * A bucket referring to a Oracle BLOB/CLOB |
| */ |
| struct apr_bucket_lob { |
| /** Number of buckets using this memory */ |
| apr_bucket_refcount refcount; |
| /** The row this bucket refers to */ |
| const apr_dbd_row_t *row; |
| /** The column this bucket refers to */ |
| int col; |
| /** The pool into which any needed structures should |
| * be created while reading from this bucket */ |
| apr_pool_t *readpool; |
| }; |
| |
| static void lob_bucket_destroy(void *data); |
| static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, |
| apr_size_t *len, apr_read_type_e block); |
| static apr_bucket *apr_bucket_lob_make(apr_bucket *b, |
| const apr_dbd_row_t *row, int col, |
| apr_off_t offset, apr_size_t len, |
| apr_pool_t *p); |
| static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, |
| apr_off_t offset, |
| apr_size_t len, apr_pool_t *p, |
| apr_bucket_alloc_t *list); |
| |
| static const apr_bucket_type_t apr_bucket_type_lob = { |
| "LOB", 5, APR_BUCKET_DATA, |
| lob_bucket_destroy, |
| lob_bucket_read, |
| apr_bucket_setaside_notimpl, |
| apr_bucket_shared_split, |
| apr_bucket_shared_copy |
| }; |
| |
| static void lob_bucket_destroy(void *data) |
| { |
| apr_bucket_lob *f = data; |
| |
| if (apr_bucket_shared_destroy(f)) { |
| /* no need to destroy database objects here; it will get |
| * done automatically when the pool gets cleaned up */ |
| apr_bucket_free(f); |
| } |
| } |
| |
| static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, |
| apr_size_t *len, apr_read_type_e block) |
| { |
| apr_bucket_lob *a = e->data; |
| const apr_dbd_row_t *row = a->row; |
| apr_dbd_results_t *res = row->res; |
| int col = a->col; |
| apr_bucket *b = NULL; |
| apr_size_t blength = e->length; /* bytes remaining in file past offset */ |
| apr_off_t boffset = e->start; |
| define_arg *val = &res->statement->out[col]; |
| apr_dbd_t *sql = res->handle; |
| /* Only with 10g, unfortunately |
| oraub8 length = APR_BUCKET_BUFF_SIZE; |
| */ |
| ub4 length = APR_BUCKET_BUFF_SIZE; |
| char *buf = NULL; |
| |
| *str = NULL; /* in case we die prematurely */ |
| |
| /* fetch from offset if not at the beginning */ |
| buf = apr_palloc(row->pool, APR_BUCKET_BUFF_SIZE); |
| sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval, |
| &length, 1 + (size_t)boffset, |
| (dvoid*) buf, APR_BUCKET_BUFF_SIZE, |
| NULL, NULL, 0, SQLCS_IMPLICIT); |
| /* Only with 10g, unfortunately |
| sql->status = OCILobRead2(sql->svc, sql->err, val->buf.lobval, |
| &length, NULL, 1 + boffset, |
| (dvoid*) buf, APR_BUCKET_BUFF_SIZE, |
| OCI_ONE_PIECE, NULL, NULL, 0, SQLCS_IMPLICIT); |
| */ |
| if (sql->status != OCI_SUCCESS) { |
| return APR_EGENERAL; |
| } |
| blength -= length; |
| *len = length; |
| *str = buf; |
| |
| /* |
| * Change the current bucket to refer to what we read, |
| * even if we read nothing because we hit EOF. |
| */ |
| apr_bucket_pool_make(e, *str, *len, res->pool); |
| |
| /* If we have more to read from the field, then create another bucket */ |
| if (blength > 0) { |
| /* for efficiency, we can just build a new apr_bucket struct |
| * to wrap around the existing LOB bucket */ |
| b = apr_bucket_alloc(sizeof(*b), e->list); |
| b->start = boffset + *len; |
| b->length = blength; |
| b->data = a; |
| b->type = &apr_bucket_type_lob; |
| b->free = apr_bucket_free; |
| b->list = e->list; |
| APR_BUCKET_INSERT_AFTER(e, b); |
| } |
| else { |
| lob_bucket_destroy(a); |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| static apr_bucket *apr_bucket_lob_make(apr_bucket *b, |
| const apr_dbd_row_t *row, int col, |
| apr_off_t offset, apr_size_t len, |
| apr_pool_t *p) |
| { |
| apr_bucket_lob *f; |
| |
| f = apr_bucket_alloc(sizeof(*f), b->list); |
| f->row = row; |
| f->col = col; |
| f->readpool = p; |
| |
| b = apr_bucket_shared_make(b, f, offset, len); |
| b->type = &apr_bucket_type_lob; |
| |
| return b; |
| } |
| |
| static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, |
| apr_off_t offset, |
| apr_size_t len, apr_pool_t *p, |
| apr_bucket_alloc_t *list) |
| { |
| apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); |
| |
| APR_BUCKET_INIT(b); |
| b->free = apr_bucket_free; |
| b->list = list; |
| return apr_bucket_lob_make(b, row, col, offset, len, p); |
| } |
| |
| static apr_status_t dbd_free_lobdesc(void *lob) |
| { |
| switch (OCIDescriptorFree(lob, OCI_DTYPE_LOB)) { |
| case OCI_SUCCESS: |
| return APR_SUCCESS; |
| default: |
| return APR_EGENERAL; |
| } |
| } |
| |
| static apr_status_t dbd_free_snapshot(void *snap) |
| { |
| switch (OCIDescriptorFree(snap, OCI_DTYPE_SNAP)) { |
| case OCI_SUCCESS: |
| return APR_SUCCESS; |
| default: |
| return APR_EGENERAL; |
| } |
| } |
| |
| static void dbd_oracle_init(apr_pool_t *pool) |
| { |
| if (dbd_oracle_env == NULL) { |
| /* Sadly, OCI_SHARED seems to be impossible to use, due to |
| * various Oracle bugs. See, for example, Oracle MetaLink bug 2972890 |
| * and PHP bug http://bugs.php.net/bug.php?id=23733 |
| */ |
| #ifdef OCI_NEW_LENGTH_SEMANTICS |
| OCIEnvCreate(&dbd_oracle_env, OCI_THREADED|OCI_NEW_LENGTH_SEMANTICS, |
| NULL, NULL, NULL, NULL, 0, NULL); |
| #else |
| OCIEnvCreate(&dbd_oracle_env, OCI_THREADED, |
| NULL, NULL, NULL, NULL, 0, NULL); |
| #endif |
| } |
| } |
| |
| static apr_dbd_t *dbd_oracle_open(apr_pool_t *pool, const char *params, |
| const char **error) |
| { |
| apr_dbd_t *ret = apr_pcalloc(pool, sizeof(apr_dbd_t)); |
| int errorcode; |
| |
| char *BLANK = ""; |
| struct { |
| const char *field; |
| char *value; |
| } fields[] = { |
| {"user", BLANK}, |
| {"pass", BLANK}, |
| {"dbname", BLANK}, |
| {"server", BLANK}, |
| {NULL, NULL} |
| }; |
| int i; |
| const char *ptr; |
| const char *key; |
| size_t klen; |
| const char *value; |
| size_t vlen; |
| static const char *const delims = " \r\n\t;|,"; |
| |
| ret->pool = pool; |
| ret->long_size = DEFAULT_LONG_SIZE; |
| |
| /* snitch parsing from the MySQL driver */ |
| for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) { |
| /* don't dereference memory that may not belong to us */ |
| if (ptr == params) { |
| ++ptr; |
| continue; |
| } |
| for (key = ptr-1; apr_isspace(*key); --key); |
| klen = 0; |
| while (apr_isalpha(*key)) { |
| if (key == params) { |
| /* Don't parse off the front of the params */ |
| --key; |
| ++klen; |
| break; |
| } |
| --key; |
| ++klen; |
| } |
| ++key; |
| for (value = ptr+1; apr_isspace(*value); ++value); |
| vlen = strcspn(value, delims); |
| for (i=0; fields[i].field != NULL; ++i) { |
| if (!strncasecmp(fields[i].field, key, klen)) { |
| fields[i].value = apr_pstrndup(pool, value, vlen); |
| break; |
| } |
| } |
| ptr = value+vlen; |
| } |
| |
| ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->err, |
| OCI_HTYPE_ERROR, 0, NULL); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| printf("ret->status is %d\n", ret->status); |
| break; |
| #else |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| |
| ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svr, |
| OCI_HTYPE_SERVER, 0, NULL); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (alloc svr): %s\n", ret->status, ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| |
| ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svc, |
| OCI_HTYPE_SVCCTX, 0, NULL); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (alloc svc): %s\n", ret->status, ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| |
| /* All the examples use the #else */ |
| #if CAN_DO_LOGIN |
| ret->status = OCILogon(dbd_oracle_env, ret->err, &ret->svc, fields[0].value, |
| strlen(fields[0].value), fields[1].value, |
| strlen(fields[1].value), fields[2].value, |
| strlen(fields[2].value)); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR: %s\n", ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| #else |
| ret->status = OCIServerAttach(ret->svr, ret->err, (text*) fields[3].value, |
| strlen(fields[3].value), OCI_DEFAULT); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (server attach): %s\n", ret->status, ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->svr, 0, |
| OCI_ATTR_SERVER, ret->err); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (attr set): %s\n", ret->status, ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->auth, |
| OCI_HTYPE_SESSION, 0, NULL); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (alloc auth): %s\n", ret->status, ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[0].value, |
| strlen(fields[0].value), OCI_ATTR_USERNAME, ret->err); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (attr username): %s\n", ret->status, ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[1].value, |
| strlen(fields[1].value), OCI_ATTR_PASSWORD, ret->err); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (attr password): %s\n", ret->status, ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| ret->status = OCISessionBegin(ret->svc, ret->err, ret->auth, |
| OCI_CRED_RDBMS, OCI_DEFAULT); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (session begin): %s\n", ret->status, ret->buf); |
| break; |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| case OCI_SUCCESS: |
| break; |
| } |
| ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->auth, 0, |
| OCI_ATTR_SESSION, ret->err); |
| switch (ret->status) { |
| default: |
| #ifdef DEBUG |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf, |
| sizeof(ret->buf), OCI_HTYPE_ERROR); |
| printf("OPEN ERROR %d (attr session): %s\n", ret->status, ret->buf); |
| #else |
| if (error) { |
| *error = apr_pcalloc(pool, ERR_BUF_SIZE); |
| OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error), |
| ERR_BUF_SIZE, OCI_HTYPE_ERROR); |
| } |
| return NULL; |
| #endif |
| break; |
| case OCI_SUCCESS: |
| break; |
| } |
| #endif |
| |
| if(dbd_oracle_prepare(pool, ret, CHECK_CONN_QUERY, NULL, 0, 0, NULL, |
| &ret->check_conn_stmt) != 0) { |
| return NULL; |
| } |
| |
| return ret; |
| } |
| |
| #ifdef EXPORT_NATIVE_FUNCS |
| static apr_size_t dbd_oracle_long_size_set(apr_dbd_t *sql, |
| apr_size_t long_size) |
| { |
| apr_size_t old_size = sql->long_size; |
| sql->long_size = long_size; |
| return old_size; |
| } |
| #endif |
| |
| static const char *dbd_oracle_get_name(const apr_dbd_results_t *res, int n) |
| { |
| define_arg *val = &res->statement->out[n]; |
| |
| if ((n < 0) || (n >= res->statement->nout)) { |
| return NULL; |
| } |
| return val->name; |
| } |
| |
| static int dbd_oracle_get_row(apr_pool_t *pool, apr_dbd_results_t *res, |
| apr_dbd_row_t **rowp, int rownum) |
| { |
| apr_dbd_row_t *row = *rowp; |
| apr_dbd_t *sql = res->handle; |
| int_errorcode; |
| |
| if (row == NULL) { |
| row = apr_palloc(pool, sizeof(apr_dbd_row_t)); |
| *rowp = row; |
| row->res = res; |
| /* Oracle starts counting at 1 according to the docs */ |
| row->n = res->seek ? rownum : 1; |
| row->pool = pool; |
| } |
| else { |
| if (res->seek) { |
| row->n = rownum; |
| } |
| else { |
| ++row->n; |
| } |
| } |
| |
| if (res->seek) { |
| sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1, |
| OCI_FETCH_ABSOLUTE, row->n, OCI_DEFAULT); |
| } |
| else { |
| sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1, |
| OCI_FETCH_NEXT, 0, OCI_DEFAULT); |
| } |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| (*rowp)->res = res; |
| return 0; |
| case OCI_NO_DATA: |
| return -1; |
| case OCI_ERROR: |
| #ifdef DEBUG |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Execute error %d: %s\n", sql->status, sql->buf); |
| #endif |
| /* fallthrough */ |
| default: |
| return 1; |
| } |
| return 0; |
| } |
| |
| static const char *dbd_oracle_error(apr_dbd_t *sql, int n) |
| { |
| /* This is ugly. Needs us to pass in a buffer of unknown size. |
| * Either we put it on the handle, or we have to keep allocing/copying |
| */ |
| sb4 errorcode; |
| |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| return "OCI_SUCCESS"; |
| case OCI_SUCCESS_WITH_INFO: |
| return "OCI_SUCCESS_WITH_INFO"; |
| case OCI_NEED_DATA: |
| return "OCI_NEED_DATA"; |
| case OCI_NO_DATA: |
| return "OCI_NO_DATA"; |
| case OCI_INVALID_HANDLE: |
| return "OCI_INVALID_HANDLE"; |
| case OCI_STILL_EXECUTING: |
| return "OCI_STILL_EXECUTING"; |
| case OCI_CONTINUE: |
| return "OCI_CONTINUE"; |
| } |
| |
| switch (OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| (text*) sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR)) { |
| case OCI_SUCCESS: |
| return sql->buf; |
| default: |
| return "internal error: OCIErrorGet failed"; |
| } |
| } |
| |
| static apr_status_t freeStatement(void *statement) |
| { |
| int rv = APR_SUCCESS; |
| OCIStmt *stmt = ((apr_dbd_prepared_t*)statement)->stmt; |
| |
| #ifdef PREPARE2 |
| OCIError *err; |
| |
| if (OCIHandleAlloc(dbd_oracle_env, (dvoid**)&err, OCI_HTYPE_ERROR, |
| 0, NULL) != OCI_SUCCESS) { |
| return APR_EGENERAL; |
| } |
| if (OCIStmtRelease(stmt, err, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS) { |
| rv = APR_EGENERAL; |
| } |
| if (OCIHandleFree(err, OCI_HTYPE_ERROR) != OCI_SUCCESS) { |
| rv = APR_EGENERAL; |
| } |
| #else |
| if (OCIHandleFree(stmt, OCI_HTYPE_STMT) != OCI_SUCCESS) { |
| rv = APR_EGENERAL; |
| } |
| #endif |
| |
| return rv; |
| } |
| |
| static int dbd_oracle_select(apr_pool_t *pool, apr_dbd_t *sql, |
| apr_dbd_results_t **results, |
| const char *query, int seek) |
| { |
| int ret = 0; |
| apr_dbd_prepared_t *statement = NULL; |
| |
| ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = dbd_oracle_pselect(pool, sql, results, statement, seek, NULL); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| static int dbd_oracle_query(apr_dbd_t *sql, int *nrows, const char *query) |
| { |
| int ret = 0; |
| apr_pool_t *pool; |
| apr_dbd_prepared_t *statement = NULL; |
| |
| if (sql->trans && sql->trans->status == TRANS_ERROR) { |
| return 1; |
| } |
| |
| /* make our own pool so that APR allocations don't linger and so that |
| * both Stmt and LOB handles are cleaned up (LOB handles may be |
| * allocated when preparing APR_DBD_TYPE_CLOB/BLOBs) |
| */ |
| apr_pool_create(&pool, sql->pool); |
| |
| ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement); |
| if (ret == 0) { |
| ret = dbd_oracle_pquery(pool, sql, nrows, statement, NULL); |
| if (ret == 0) { |
| sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, |
| nrows, 0, OCI_ATTR_ROW_COUNT, |
| sql->err); |
| } |
| } |
| |
| apr_pool_destroy(pool); |
| |
| return ret; |
| } |
| |
| static const char *dbd_oracle_escape(apr_pool_t *pool, const char *arg, |
| apr_dbd_t *sql) |
| { |
| return arg; /* OCI has no concept of string escape */ |
| } |
| |
| static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql, |
| const char *query, const char *label, |
| int nargs, int nvals, apr_dbd_type_e *types, |
| apr_dbd_prepared_t **statement) |
| { |
| int ret = 0; |
| int i; |
| apr_dbd_prepared_t *stmt ; |
| |
| if (*statement == NULL) { |
| *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t)); |
| } |
| stmt = *statement; |
| stmt->handle = sql; |
| stmt->pool = pool; |
| stmt->nargs = nargs; |
| stmt->nvals = nvals; |
| |
| /* populate our own args, if any */ |
| if (nargs > 0) { |
| stmt->args = apr_pcalloc(pool, nargs*sizeof(bind_arg)); |
| for (i = 0; i < nargs; i++) { |
| stmt->args[i].type = types[i]; |
| } |
| } |
| |
| sql->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**) &stmt->stmt, |
| OCI_HTYPE_STMT, 0, NULL); |
| if (sql->status != OCI_SUCCESS) { |
| return 1; |
| } |
| |
| sql->status = OCIStmtPrepare(stmt->stmt, sql->err, (text*) query, |
| strlen(query), OCI_NTV_SYNTAX, OCI_DEFAULT); |
| if (sql->status != OCI_SUCCESS) { |
| OCIHandleFree(stmt->stmt, OCI_HTYPE_STMT); |
| return 1; |
| } |
| |
| apr_pool_cleanup_register(pool, stmt, freeStatement, |
| apr_pool_cleanup_null); |
| |
| /* Perl gets statement type here */ |
| sql->status = OCIAttrGet(stmt->stmt, OCI_HTYPE_STMT, &stmt->type, 0, |
| OCI_ATTR_STMT_TYPE, sql->err); |
| if (sql->status != OCI_SUCCESS) { |
| return 1; |
| } |
| |
| /* Perl sets PREFETCH_MEMORY here, but the docs say there's a working default */ |
| #if 0 |
| sql->status = OCIAttrSet(stmt->stmt, OCI_HTYPE_STMT, &prefetch_size, |
| sizeof(prefetch_size), OCI_ATTR_PREFETCH_MEMORY, |
| sql->err); |
| if (sql->status != OCI_SUCCESS) { |
| return 1; |
| } |
| #endif |
| |
| if (stmt->type == OCI_STMT_SELECT) { |
| ret = outputParams(sql, stmt); |
| } |
| return ret; |
| } |
| |
| static void dbd_oracle_bind(apr_dbd_prepared_t *statement, const char **values) |
| { |
| OCIStmt *stmt = statement->stmt; |
| apr_dbd_t *sql = statement->handle; |
| int i, j; |
| sb2 null_ind = -1; |
| |
| for (i = 0, j = 0; i < statement->nargs; i++, j++) { |
| if (values[j] == NULL) { |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| NULL, 0, SQLT_STR, |
| &null_ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| } |
| else { |
| switch (statement->args[i].type) { |
| case APR_DBD_TYPE_BLOB: |
| { |
| char *data = (char *)values[j]; |
| int size = atoi((char*)values[++j]); |
| |
| /* skip table and column for now */ |
| j += 2; |
| |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| data, size, SQLT_LBI, |
| &statement->args[i].ind, |
| NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| } |
| break; |
| case APR_DBD_TYPE_CLOB: |
| { |
| char *data = (char *)values[j]; |
| int size = atoi((char*)values[++j]); |
| |
| /* skip table and column for now */ |
| j += 2; |
| |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| data, size, SQLT_LNG, |
| &statement->args[i].ind, |
| NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| } |
| break; |
| default: |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| (dvoid*) values[j], |
| strlen(values[j]) + 1, |
| SQLT_STR, |
| &statement->args[i].ind, |
| NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| } |
| } |
| |
| if (sql->status != OCI_SUCCESS) { |
| return; |
| } |
| } |
| |
| return; |
| } |
| |
| static int outputParams(apr_dbd_t *sql, apr_dbd_prepared_t *stmt) |
| { |
| OCIParam *parms; |
| int i; |
| ub2 paramtype[DBD_ORACLE_MAX_COLUMNS]; |
| ub2 paramsize[DBD_ORACLE_MAX_COLUMNS]; |
| char *paramname[DBD_ORACLE_MAX_COLUMNS]; |
| ub4 paramnamelen[DBD_ORACLE_MAX_COLUMNS]; |
| int_errorcode; |
| |
| /* Perl uses 0 where we used 1 */ |
| sql->status = OCIStmtExecute(sql->svc, stmt->stmt, sql->err, 0, 0, |
| NULL, NULL, OCI_DESCRIBE_ONLY); |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| case OCI_SUCCESS_WITH_INFO: |
| break; |
| case OCI_ERROR: |
| #ifdef DEBUG |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Describing prepared statement: %s\n", sql->buf); |
| #endif |
| default: |
| return 1; |
| } |
| while (sql->status == OCI_SUCCESS) { |
| sql->status = OCIParamGet(stmt->stmt, OCI_HTYPE_STMT, |
| sql->err, (dvoid**)&parms, stmt->nout+1); |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM, |
| ¶mtype[stmt->nout], |
| 0, OCI_ATTR_DATA_TYPE, sql->err); |
| sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM, |
| ¶msize[stmt->nout], |
| 0, OCI_ATTR_DATA_SIZE, sql->err); |
| sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM, |
| ¶mname[stmt->nout], |
| ¶mnamelen[stmt->nout], |
| OCI_ATTR_NAME, sql->err); |
| ++stmt->nout; |
| } |
| } |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| break; |
| case OCI_ERROR: |
| break; /* this is what we expect at end-of-loop */ |
| default: |
| return 1; |
| } |
| |
| /* OK, the above works. We have the params; now OCIDefine them */ |
| stmt->out = apr_palloc(stmt->pool, stmt->nout*sizeof(define_arg)); |
| for (i=0; i<stmt->nout; ++i) { |
| stmt->out[i].type = paramtype[i]; |
| stmt->out[i].len = stmt->out[i].sz = paramsize[i]; |
| stmt->out[i].name = apr_pstrmemdup(stmt->pool, |
| paramname[i], paramnamelen[i]); |
| switch (stmt->out[i].type) { |
| default: |
| switch (stmt->out[i].type) { |
| case SQLT_NUM: /* 2: numeric, Perl worst case=130+38+3 */ |
| stmt->out[i].sz = 171; |
| break; |
| case SQLT_CHR: /* 1: char */ |
| case SQLT_AFC: /* 96: ANSI fixed char */ |
| stmt->out[i].sz *= 4; /* ugh, wasteful UCS-4 handling */ |
| break; |
| case SQLT_DAT: /* 12: date, depends on NLS date format */ |
| stmt->out[i].sz = 75; |
| break; |
| case SQLT_BIN: /* 23: raw binary, perhaps UTF-16? */ |
| stmt->out[i].sz *= 2; |
| break; |
| case SQLT_RID: /* 11: rowid */ |
| case SQLT_RDD: /* 104: rowid descriptor */ |
| stmt->out[i].sz = 20; |
| break; |
| case SQLT_TIMESTAMP: /* 187: timestamp */ |
| case SQLT_TIMESTAMP_TZ: /* 188: timestamp with time zone */ |
| case SQLT_INTERVAL_YM: /* 189: interval year-to-month */ |
| case SQLT_INTERVAL_DS: /* 190: interval day-to-second */ |
| case SQLT_TIMESTAMP_LTZ: /* 232: timestamp with local time zone */ |
| stmt->out[i].sz = 75; |
| break; |
| default: |
| #ifdef DEBUG |
| printf("Unsupported data type: %d\n", stmt->out[i].type); |
| #endif |
| break; |
| } |
| ++stmt->out[i].sz; |
| stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz); |
| sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn, |
| sql->err, i+1, |
| stmt->out[i].buf.sval, |
| stmt->out[i].sz, SQLT_STR, |
| &stmt->out[i].ind, &stmt->out[i].len, |
| 0, OCI_DEFAULT); |
| break; |
| case SQLT_LNG: /* 8: long */ |
| stmt->out[i].sz = sql->long_size * 4 + 4; /* ugh, UCS-4 handling */ |
| stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz); |
| sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn, |
| sql->err, i+1, |
| stmt->out[i].buf.raw, |
| stmt->out[i].sz, SQLT_LVC, |
| &stmt->out[i].ind, NULL, |
| 0, OCI_DEFAULT); |
| break; |
| case SQLT_LBI: /* 24: long binary, perhaps UTF-16? */ |
| stmt->out[i].sz = sql->long_size * 2 + 4; /* room for int prefix */ |
| stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz); |
| sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn, |
| sql->err, i+1, |
| stmt->out[i].buf.raw, |
| stmt->out[i].sz, SQLT_LVB, |
| &stmt->out[i].ind, NULL, |
| 0, OCI_DEFAULT); |
| break; |
| case SQLT_BLOB: /* 113 */ |
| case SQLT_CLOB: /* 112 */ |
| /*http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/oci05bnd.htm#434937*/ |
| sql->status = OCIDescriptorAlloc(dbd_oracle_env, |
| (dvoid**)&stmt->out[i].buf.lobval, |
| OCI_DTYPE_LOB, 0, NULL); |
| apr_pool_cleanup_register(stmt->pool, stmt->out[i].buf.lobval, |
| dbd_free_lobdesc, |
| apr_pool_cleanup_null); |
| sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn, |
| sql->err, i+1, |
| (dvoid*) &stmt->out[i].buf.lobval, |
| -1, stmt->out[i].type, |
| &stmt->out[i].ind, &stmt->out[i].len, |
| 0, OCI_DEFAULT); |
| break; |
| } |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| break; |
| default: |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql, |
| int *nrows, apr_dbd_prepared_t *statement, |
| const char **values) |
| { |
| OCISnapshot *oldsnapshot = NULL; |
| OCISnapshot *newsnapshot = NULL; |
| apr_dbd_transaction_t* trans = sql->trans; |
| int exec_mode; |
| int_errorcode; |
| |
| if (trans) { |
| switch (trans->status) { |
| case TRANS_ERROR: |
| return -1; |
| case TRANS_NONE: |
| trans = NULL; |
| break; |
| case TRANS_1: |
| oldsnapshot = trans->snapshot1; |
| newsnapshot = trans->snapshot2; |
| trans->status = TRANS_2; |
| break; |
| case TRANS_2: |
| oldsnapshot = trans->snapshot2; |
| newsnapshot = trans->snapshot1; |
| trans->status = TRANS_1; |
| break; |
| } |
| exec_mode = OCI_DEFAULT; |
| } |
| else { |
| exec_mode = OCI_COMMIT_ON_SUCCESS; |
| } |
| |
| dbd_oracle_bind(statement, values); |
| |
| sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0, |
| oldsnapshot, newsnapshot, exec_mode); |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| break; |
| case OCI_ERROR: |
| #ifdef DEBUG |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Execute error %d: %s\n", sql->status, sql->buf); |
| #endif |
| /* fallthrough */ |
| default: |
| if (TXN_NOTICE_ERRORS(trans)) { |
| trans->status = TRANS_ERROR; |
| } |
| return 1; |
| } |
| |
| sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0, |
| OCI_ATTR_ROW_COUNT, sql->err); |
| return 0; |
| } |
| |
| static int dbd_oracle_pvquery(apr_pool_t *pool, apr_dbd_t *sql, |
| int *nrows, apr_dbd_prepared_t *statement, |
| va_list args) |
| { |
| const char **values; |
| int i; |
| |
| if (sql->trans && sql->trans->status == TRANS_ERROR) { |
| return -1; |
| } |
| |
| values = apr_palloc(pool, sizeof(*values) * statement->nvals); |
| |
| for (i = 0; i < statement->nvals; i++) { |
| values[i] = va_arg(args, const char*); |
| } |
| |
| return dbd_oracle_pquery(pool, sql, nrows, statement, values); |
| } |
| |
| static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql, |
| apr_dbd_results_t **results, |
| apr_dbd_prepared_t *statement, |
| int seek, const char **values) |
| { |
| int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT; |
| OCISnapshot *oldsnapshot = NULL; |
| OCISnapshot *newsnapshot = NULL; |
| apr_dbd_transaction_t* trans = sql->trans; |
| int_errorcode; |
| |
| if (trans) { |
| switch (trans->status) { |
| case TRANS_ERROR: |
| return 1; |
| case TRANS_NONE: |
| trans = NULL; |
| break; |
| case TRANS_1: |
| oldsnapshot = trans->snapshot1; |
| newsnapshot = trans->snapshot2; |
| trans->status = TRANS_2; |
| break; |
| case TRANS_2: |
| oldsnapshot = trans->snapshot2; |
| newsnapshot = trans->snapshot1; |
| trans->status = TRANS_1; |
| break; |
| } |
| } |
| |
| dbd_oracle_bind(statement, values); |
| |
| sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0, |
| oldsnapshot, newsnapshot, exec_mode); |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| break; |
| case OCI_ERROR: |
| #ifdef DEBUG |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Executing prepared statement: %s\n", sql->buf); |
| #endif |
| /* fallthrough */ |
| default: |
| if (TXN_NOTICE_ERRORS(trans)) { |
| trans->status = TRANS_ERROR; |
| } |
| return 1; |
| } |
| |
| if (!*results) { |
| *results = apr_palloc(pool, sizeof(apr_dbd_results_t)); |
| } |
| (*results)->handle = sql; |
| (*results)->statement = statement; |
| (*results)->seek = seek; |
| (*results)->rownum = seek ? 0 : -1; |
| (*results)->pool = pool; |
| |
| return 0; |
| } |
| |
| static int dbd_oracle_pvselect(apr_pool_t *pool, apr_dbd_t *sql, |
| apr_dbd_results_t **results, |
| apr_dbd_prepared_t *statement, |
| int seek, va_list args) |
| { |
| const char **values; |
| int i; |
| |
| if (sql->trans && sql->trans->status == TRANS_ERROR) { |
| return -1; |
| } |
| |
| values = apr_palloc(pool, sizeof(*values) * statement->nvals); |
| |
| for (i = 0; i < statement->nvals; i++) { |
| values[i] = va_arg(args, const char*); |
| } |
| |
| return dbd_oracle_pselect(pool, sql, results, statement, seek, values); |
| } |
| |
| static void dbd_oracle_bbind(apr_dbd_prepared_t * statement, |
| const void **values) |
| { |
| OCIStmt *stmt = statement->stmt; |
| apr_dbd_t *sql = statement->handle; |
| int i, j; |
| sb2 null_ind = -1; |
| apr_dbd_type_e type; |
| |
| for (i = 0, j = 0; i < statement->nargs; i++, j++) { |
| type = (values[j] == NULL ? APR_DBD_TYPE_NULL |
| : statement->args[i].type); |
| |
| switch (type) { |
| case APR_DBD_TYPE_TINY: |
| statement->args[i].value.ival = *(char*)values[j]; |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| &statement->args[i].value.ival, |
| sizeof(statement->args[i].value.ival), |
| SQLT_INT, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_UTINY: |
| statement->args[i].value.uval = *(unsigned char*)values[j]; |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| &statement->args[i].value.uval, |
| sizeof(statement->args[i].value.uval), |
| SQLT_UIN, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_SHORT: |
| statement->args[i].value.ival = *(short*)values[j]; |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| &statement->args[i].value.ival, |
| sizeof(statement->args[i].value.ival), |
| SQLT_INT, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_USHORT: |
| statement->args[i].value.uval = *(unsigned short*)values[j]; |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| &statement->args[i].value.uval, |
| sizeof(statement->args[i].value.uval), |
| SQLT_UIN, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_INT: |
| statement->args[i].value.ival = *(int*)values[j]; |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| &statement->args[i].value.ival, |
| sizeof(statement->args[i].value.ival), |
| SQLT_INT, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_UINT: |
| statement->args[i].value.uval = *(unsigned int*)values[j]; |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| &statement->args[i].value.uval, |
| sizeof(statement->args[i].value.uval), |
| SQLT_UIN, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_LONG: |
| statement->args[i].value.sval = |
| apr_psprintf(statement->pool, "%ld", *(long*)values[j]); |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| statement->args[i].value.sval, |
| strlen(statement->args[i].value.sval)+1, |
| SQLT_STR, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_ULONG: |
| statement->args[i].value.sval = |
| apr_psprintf(statement->pool, "%lu", |
| *(unsigned long*)values[j]); |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| statement->args[i].value.sval, |
| strlen(statement->args[i].value.sval)+1, |
| SQLT_STR, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_LONGLONG: |
| statement->args[i].value.sval = |
| apr_psprintf(statement->pool, "%" APR_INT64_T_FMT, |
| *(apr_int64_t*)values[j]); |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| statement->args[i].value.sval, |
| strlen(statement->args[i].value.sval)+1, |
| SQLT_STR, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_ULONGLONG: |
| statement->args[i].value.sval = |
| apr_psprintf(statement->pool, "%" APR_UINT64_T_FMT, |
| *(apr_uint64_t*)values[j]); |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| statement->args[i].value.sval, |
| strlen(statement->args[i].value.sval)+1, |
| SQLT_UIN, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_FLOAT: |
| statement->args[i].value.fval = *(float*)values[j]; |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| &statement->args[i].value.fval, |
| sizeof(statement->args[i].value.fval), |
| SQLT_FLT, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_DOUBLE: |
| statement->args[i].value.fval = *(double*)values[j]; |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| &statement->args[i].value.fval, |
| sizeof(statement->args[i].value.fval), |
| SQLT_FLT, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_STRING: |
| case APR_DBD_TYPE_TEXT: |
| case APR_DBD_TYPE_TIME: |
| case APR_DBD_TYPE_DATE: |
| case APR_DBD_TYPE_DATETIME: |
| case APR_DBD_TYPE_TIMESTAMP: |
| case APR_DBD_TYPE_ZTIMESTAMP: |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| (dvoid*) values[j], |
| strlen(values[j]) + 1, |
| SQLT_STR, |
| &statement->args[i].ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| case APR_DBD_TYPE_BLOB: |
| { |
| char *data = (char *)values[j]; |
| apr_size_t size = *(apr_size_t*)values[++j]; |
| |
| /* skip table and column for now */ |
| j += 2; |
| |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| data, size, SQLT_LBI, |
| &statement->args[i].ind, |
| NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| } |
| break; |
| case APR_DBD_TYPE_CLOB: |
| { |
| char *data = (char *)values[j]; |
| apr_size_t size = *(apr_size_t*)values[++j]; |
| |
| /* skip table and column for now */ |
| j += 2; |
| |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| data, size, SQLT_LNG, |
| &statement->args[i].ind, |
| NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| } |
| break; |
| case APR_DBD_TYPE_NULL: |
| default: |
| sql->status = OCIBindByPos(stmt, &statement->args[i].bind, |
| sql->err, i + 1, |
| NULL, 0, SQLT_STR, |
| &null_ind, NULL, |
| (ub2) 0, (ub4) 0, |
| (ub4 *) 0, OCI_DEFAULT); |
| break; |
| } |
| |
| if (sql->status != OCI_SUCCESS) { |
| return; |
| } |
| } |
| |
| return; |
| } |
| |
| static int dbd_oracle_pbquery(apr_pool_t * pool, apr_dbd_t * sql, |
| int *nrows, apr_dbd_prepared_t * statement, |
| const void **values) |
| { |
| OCISnapshot *oldsnapshot = NULL; |
| OCISnapshot *newsnapshot = NULL; |
| apr_dbd_transaction_t* trans = sql->trans; |
| int exec_mode; |
| int_errorcode; |
| |
| if (trans) { |
| switch (trans->status) { |
| case TRANS_ERROR: |
| return -1; |
| case TRANS_NONE: |
| trans = NULL; |
| break; |
| case TRANS_1: |
| oldsnapshot = trans->snapshot1; |
| newsnapshot = trans->snapshot2; |
| trans->status = TRANS_2; |
| break; |
| case TRANS_2: |
| oldsnapshot = trans->snapshot2; |
| newsnapshot = trans->snapshot1; |
| trans->status = TRANS_1; |
| break; |
| } |
| exec_mode = OCI_DEFAULT; |
| } |
| else { |
| exec_mode = OCI_COMMIT_ON_SUCCESS; |
| } |
| |
| dbd_oracle_bbind(statement, values); |
| |
| sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0, |
| oldsnapshot, newsnapshot, exec_mode); |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| break; |
| case OCI_ERROR: |
| #ifdef DEBUG |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Execute error %d: %s\n", sql->status, sql->buf); |
| #endif |
| /* fallthrough */ |
| default: |
| if (TXN_NOTICE_ERRORS(trans)) { |
| trans->status = TRANS_ERROR; |
| } |
| return 1; |
| } |
| |
| sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0, |
| OCI_ATTR_ROW_COUNT, sql->err); |
| return 0; |
| } |
| |
| static int dbd_oracle_pvbquery(apr_pool_t * pool, apr_dbd_t * sql, |
| int *nrows, apr_dbd_prepared_t * statement, |
| va_list args) |
| { |
| const void **values; |
| int i; |
| |
| if (sql->trans && sql->trans->status == TRANS_ERROR) { |
| return -1; |
| } |
| |
| values = apr_palloc(pool, sizeof(*values) * statement->nvals); |
| |
| for (i = 0; i < statement->nvals; i++) { |
| values[i] = va_arg(args, const void*); |
| } |
| |
| return dbd_oracle_pbquery(pool, sql, nrows, statement, values); |
| } |
| |
| static int dbd_oracle_pbselect(apr_pool_t * pool, apr_dbd_t * sql, |
| apr_dbd_results_t ** results, |
| apr_dbd_prepared_t * statement, |
| int seek, const void **values) |
| { |
| int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT; |
| OCISnapshot *oldsnapshot = NULL; |
| OCISnapshot *newsnapshot = NULL; |
| apr_dbd_transaction_t* trans = sql->trans; |
| int_errorcode; |
| |
| if (trans) { |
| switch (trans->status) { |
| case TRANS_ERROR: |
| return 1; |
| case TRANS_NONE: |
| trans = NULL; |
| break; |
| case TRANS_1: |
| oldsnapshot = trans->snapshot1; |
| newsnapshot = trans->snapshot2; |
| trans->status = TRANS_2; |
| break; |
| case TRANS_2: |
| oldsnapshot = trans->snapshot2; |
| newsnapshot = trans->snapshot1; |
| trans->status = TRANS_1; |
| break; |
| } |
| } |
| |
| dbd_oracle_bbind(statement, values); |
| |
| sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0, |
| oldsnapshot, newsnapshot, exec_mode); |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| break; |
| case OCI_ERROR: |
| #ifdef DEBUG |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Executing prepared statement: %s\n", sql->buf); |
| #endif |
| /* fallthrough */ |
| default: |
| if (TXN_NOTICE_ERRORS(trans)) { |
| trans->status = TRANS_ERROR; |
| } |
| return 1; |
| } |
| |
| if (!*results) { |
| *results = apr_palloc(pool, sizeof(apr_dbd_results_t)); |
| } |
| (*results)->handle = sql; |
| (*results)->statement = statement; |
| (*results)->seek = seek; |
| (*results)->rownum = seek ? 0 : -1; |
| (*results)->pool = pool; |
| |
| return 0; |
| } |
| |
| static int dbd_oracle_pvbselect(apr_pool_t * pool, apr_dbd_t * sql, |
| apr_dbd_results_t ** results, |
| apr_dbd_prepared_t * statement, int seek, |
| va_list args) |
| { |
| const void **values; |
| int i; |
| |
| if (sql->trans && sql->trans->status == TRANS_ERROR) { |
| return -1; |
| } |
| |
| values = apr_palloc(pool, sizeof(*values) * statement->nvals); |
| |
| for (i = 0; i < statement->nvals; i++) { |
| values[i] = va_arg(args, const void*); |
| } |
| |
| return dbd_oracle_pbselect(pool, sql, results, statement, seek, values); |
| } |
| |
| static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql, |
| apr_dbd_transaction_t **trans) |
| { |
| int ret = 0; |
| int_errorcode; |
| if (*trans) { |
| dbd_oracle_end_transaction(*trans); |
| } |
| else { |
| *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t)); |
| OCIHandleAlloc(dbd_oracle_env, (dvoid**)&(*trans)->trans, |
| OCI_HTYPE_TRANS, 0, 0); |
| OCIAttrSet(sql->svc, OCI_HTYPE_SVCCTX, (*trans)->trans, 0, |
| OCI_ATTR_TRANS, sql->err); |
| } |
| |
| |
| sql->status = OCITransStart(sql->svc, sql->err, TRANS_TIMEOUT, |
| OCI_TRANS_NEW); |
| switch (sql->status) { |
| case OCI_ERROR: |
| #ifdef DEBUG |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, sql->buf, |
| sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Transaction: %s\n", sql->buf); |
| #endif |
| ret = 1; |
| break; |
| case OCI_SUCCESS: |
| (*trans)->handle = sql; |
| (*trans)->status = TRANS_1; |
| sql->trans = *trans; |
| switch (OCIDescriptorAlloc(dbd_oracle_env, |
| (dvoid**)&(*trans)->snapshot1, |
| OCI_DTYPE_SNAP, 0, NULL)) { |
| case OCI_SUCCESS: |
| apr_pool_cleanup_register(pool, (*trans)->snapshot1, |
| dbd_free_snapshot, apr_pool_cleanup_null); |
| break; |
| case OCI_INVALID_HANDLE: |
| ret = 1; |
| break; |
| } |
| switch (OCIDescriptorAlloc(dbd_oracle_env, |
| (dvoid**)&(*trans)->snapshot2, |
| OCI_DTYPE_SNAP, 0, NULL)) { |
| case OCI_SUCCESS: |
| apr_pool_cleanup_register(pool, (*trans)->snapshot2, |
| dbd_free_snapshot, apr_pool_cleanup_null); |
| break; |
| case OCI_INVALID_HANDLE: |
| ret = 1; |
| break; |
| } |
| break; |
| default: |
| ret = 1; |
| break; |
| } |
| return ret; |
| } |
| |
| static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans) |
| { |
| int ret = 1; /* no transaction is an error cond */ |
| sword status; |
| apr_dbd_t *handle = trans->handle; |
| if (trans) { |
| switch (trans->status) { |
| case TRANS_NONE: /* No trans is an error here */ |
| status = OCI_ERROR; |
| break; |
| case TRANS_ERROR: |
| status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT); |
| break; |
| default: |
| /* rollback on explicit rollback request */ |
| if (TXN_DO_ROLLBACK(trans)) { |
| status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT); |
| } else { |
| status = OCITransCommit(handle->svc, handle->err, OCI_DEFAULT); |
| } |
| break; |
| } |
| |
| handle->trans = NULL; |
| |
| switch (status) { |
| case OCI_SUCCESS: |
| ret = 0; |
| break; |
| default: |
| ret = 3; |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| static int dbd_oracle_transaction_mode_get(apr_dbd_transaction_t *trans) |
| { |
| if (!trans) |
| return APR_DBD_TRANSACTION_COMMIT; |
| |
| return trans->mode; |
| } |
| |
| static int dbd_oracle_transaction_mode_set(apr_dbd_transaction_t *trans, |
| int mode) |
| { |
| if (!trans) |
| return APR_DBD_TRANSACTION_COMMIT; |
| |
| return trans->mode = (mode & TXN_MODE_BITS); |
| } |
| |
| /* This doesn't work for BLOB because of NULLs, but it can fake it |
| * if the BLOB is really a string |
| */ |
| static const char *dbd_oracle_get_entry(const apr_dbd_row_t *row, int n) |
| { |
| ub4 len = 0; |
| ub1 csform = 0; |
| ub2 csid = 0; |
| apr_size_t buflen = 0; |
| char *buf = NULL; |
| define_arg *val = &row->res->statement->out[n]; |
| apr_dbd_t *sql = row->res->handle; |
| int_errorcode; |
| |
| if ((n < 0) || (n >= row->res->statement->nout) || (val->ind == -1)) { |
| return NULL; |
| } |
| |
| switch (val->type) { |
| case SQLT_BLOB: |
| case SQLT_CLOB: |
| sql->status = OCILobGetLength(sql->svc, sql->err, val->buf.lobval, |
| &len); |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| case OCI_SUCCESS_WITH_INFO: |
| if (len == 0) { |
| buf = ""; |
| } |
| break; |
| case OCI_ERROR: |
| #ifdef DEBUG |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Finding LOB length: %s\n", sql->buf); |
| break; |
| #endif |
| default: |
| break; |
| } |
| |
| if (len == 0) { |
| break; |
| } |
| |
| if (val->type == APR_DBD_TYPE_CLOB) { |
| #if 1 |
| /* Is this necessary, or can it be defaulted? */ |
| sql->status = OCILobCharSetForm(dbd_oracle_env, sql->err, |
| val->buf.lobval, &csform); |
| if (sql->status == OCI_SUCCESS) { |
| sql->status = OCILobCharSetId(dbd_oracle_env, sql->err, |
| val->buf.lobval, &csid); |
| } |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| case OCI_SUCCESS_WITH_INFO: |
| buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */ |
| /* zeroise all - where the string ends depends on charset */ |
| buf = apr_pcalloc(row->pool, buflen); |
| break; |
| #ifdef DEBUG |
| case OCI_ERROR: |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Reading LOB character set: %s\n", sql->buf); |
| break; /*** XXX?? ***/ |
| #endif |
| default: |
| break; /*** XXX?? ***/ |
| } |
| #else /* ignore charset */ |
| buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */ |
| /* zeroise all - where the string ends depends on charset */ |
| buf = apr_pcalloc(row->pool, buflen); |
| #endif |
| } else { |
| /* BUG: this'll only work if the BLOB looks like a string */ |
| buflen = len; |
| buf = apr_palloc(row->pool, buflen+1); |
| buf[buflen] = 0; |
| } |
| |
| if (!buf) { |
| break; |
| } |
| |
| sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval, |
| &len, 1, (dvoid*) buf, buflen, |
| NULL, NULL, csid, csform); |
| switch (sql->status) { |
| case OCI_SUCCESS: |
| case OCI_SUCCESS_WITH_INFO: |
| break; |
| #ifdef DEBUG |
| case OCI_ERROR: |
| OCIErrorGet(sql->err, 1, NULL, &errorcode, |
| sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); |
| printf("Reading LOB: %s\n", sql->buf); |
| buf = NULL; /*** XXX?? ***/ |
| break; |
| #endif |
| default: |
| buf = NULL; /*** XXX?? ***/ |
| break; |
| } |
| |
| break; |
| case SQLT_LNG: |
| case SQLT_LBI: |
| /* raw is struct { ub4 len; char *buf; } */ |
| len = *(ub4*) val->buf.raw; |
| buf = apr_pstrndup(row->pool, val->buf.sval + sizeof(ub4), len); |
| break; |
| default: |
| buf = apr_pstrndup(row->pool, val->buf.sval, val->len); |
| break; |
| } |
| return (const char*) buf; |
| } |
| |
| /* XXX Should this use Oracle proper API instead of calling get_entry()? */ |
| static apr_status_t dbd_oracle_datum_get(const apr_dbd_row_t *row, int n, |
| apr_dbd_type_e type, void *data) |
| { |
| define_arg *val = &row->res->statement->out[n]; |
| const char *entry; |
| |
| if ((n < 0) || (n >= row->res->statement->nout)) { |
| return APR_EGENERAL; |
| } |
| |
| if(val->ind == -1) { |
| return APR_ENOENT; |
| } |
| |
| switch (type) { |
| case APR_DBD_TYPE_TINY: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(char*)data = atoi(entry); |
| break; |
| case APR_DBD_TYPE_UTINY: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(unsigned char*)data = atoi(entry); |
| break; |
| case APR_DBD_TYPE_SHORT: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(short*)data = atoi(entry); |
| break; |
| case APR_DBD_TYPE_USHORT: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(unsigned short*)data = atoi(entry); |
| break; |
| case APR_DBD_TYPE_INT: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(int*)data = atoi(entry); |
| break; |
| case APR_DBD_TYPE_UINT: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(unsigned int*)data = atoi(entry); |
| break; |
| case APR_DBD_TYPE_LONG: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(long*)data = atol(entry); |
| break; |
| case APR_DBD_TYPE_ULONG: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(unsigned long*)data = atol(entry); |
| break; |
| case APR_DBD_TYPE_LONGLONG: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(apr_int64_t*)data = apr_atoi64(entry); |
| break; |
| case APR_DBD_TYPE_ULONGLONG: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(apr_uint64_t*)data = apr_atoi64(entry); |
| break; |
| case APR_DBD_TYPE_FLOAT: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(float*)data = (float)atof(entry); |
| break; |
| case APR_DBD_TYPE_DOUBLE: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(double*)data = atof(entry); |
| break; |
| case APR_DBD_TYPE_STRING: |
| case APR_DBD_TYPE_TEXT: |
| case APR_DBD_TYPE_TIME: |
| case APR_DBD_TYPE_DATE: |
| case APR_DBD_TYPE_DATETIME: |
| case APR_DBD_TYPE_TIMESTAMP: |
| case APR_DBD_TYPE_ZTIMESTAMP: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| *(char**)data = (char*)entry; |
| break; |
| case APR_DBD_TYPE_BLOB: |
| case APR_DBD_TYPE_CLOB: |
| { |
| apr_bucket *e; |
| apr_bucket_brigade *b = (apr_bucket_brigade*)data; |
| apr_dbd_t *sql = row->res->handle; |
| ub4 len = 0; |
| |
| switch (val->type) { |
| case SQLT_BLOB: |
| case SQLT_CLOB: |
| sql->status = OCILobGetLength(sql->svc, sql->err, |
| val->buf.lobval, &len); |
| switch(sql->status) { |
| case OCI_SUCCESS: |
| case OCI_SUCCESS_WITH_INFO: |
| if (len == 0) { |
| e = apr_bucket_eos_create(b->bucket_alloc); |
| } |
| else { |
| e = apr_bucket_lob_create(row, n, 0, len, |
| row->pool, b->bucket_alloc); |
| } |
| break; |
| default: |
| return APR_ENOENT; |
| } |
| break; |
| default: |
| entry = dbd_oracle_get_entry(row, n); |
| if (entry == NULL) { |
| return APR_ENOENT; |
| } |
| e = apr_bucket_pool_create(entry, strlen(entry), |
| row->pool, b->bucket_alloc); |
| break; |
| } |
| APR_BRIGADE_INSERT_TAIL(b, e); |
| } |
| break; |
| case APR_DBD_TYPE_NULL: |
| *(void**)data = NULL; |
| break; |
| default: |
| return APR_EGENERAL; |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t dbd_oracle_close(apr_dbd_t *handle) |
| { |
| /* FIXME: none of the oracle docs/examples say anything about |
| * closing/releasing handles. Which seems unlikely ... |
| */ |
| |
| /* OK, let's grab from cdemo again. |
| * cdemo81 does nothing; cdemo82 does OCIHandleFree on the handles |
| */ |
| switch (OCISessionEnd(handle->svc, handle->err, handle->auth, |
| (ub4)OCI_DEFAULT)) { |
| default: |
| break; |
| } |
| switch (OCIServerDetach(handle->svr, handle->err, (ub4) OCI_DEFAULT )) { |
| default: |
| break; |
| } |
| /* does OCISessionEnd imply this? */ |
| switch (OCIHandleFree((dvoid *) handle->auth, (ub4) OCI_HTYPE_SESSION)) { |
| default: |
| break; |
| } |
| switch (OCIHandleFree((dvoid *) handle->svr, (ub4) OCI_HTYPE_SERVER)) { |
| default: |
| break; |
| } |
| switch (OCIHandleFree((dvoid *) handle->svc, (ub4) OCI_HTYPE_SVCCTX)) { |
| default: |
| break; |
| } |
| switch (OCIHandleFree((dvoid *) handle->err, (ub4) OCI_HTYPE_ERROR)) { |
| default: |
| break; |
| } |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t dbd_oracle_check_conn(apr_pool_t *pool, apr_dbd_t *sql) |
| { |
| apr_dbd_results_t *res = NULL; |
| apr_dbd_row_t *row = NULL; |
| |
| if(dbd_oracle_pselect(pool, sql, &res, sql->check_conn_stmt, |
| 0, NULL) != 0) { |
| return APR_EGENERAL; |
| } |
| |
| if(dbd_oracle_get_row(pool, res, &row, -1) != 0) { |
| return APR_EGENERAL; |
| } |
| |
| if(dbd_oracle_get_row(pool, res, &row, -1) != -1) { |
| return APR_EGENERAL; |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| static int dbd_oracle_select_db(apr_pool_t *pool, apr_dbd_t *handle, |
| const char *name) |
| { |
| /* FIXME: need to find this in the docs */ |
| return APR_ENOTIMPL; |
| } |
| |
| static void *dbd_oracle_native(apr_dbd_t *handle) |
| { |
| /* FIXME: can we do anything better? Oracle doesn't seem to have |
| * a concept of a handle in the sense we use it. |
| */ |
| return dbd_oracle_env; |
| } |
| |
| static int dbd_oracle_num_cols(apr_dbd_results_t* res) |
| { |
| return res->statement->nout; |
| } |
| |
| static int dbd_oracle_num_tuples(apr_dbd_results_t* res) |
| { |
| if (!res->seek) { |
| return -1; |
| } |
| if (res->nrows >= 0) { |
| return res->nrows; |
| } |
| res->handle->status = OCIAttrGet(res->statement->stmt, OCI_HTYPE_STMT, |
| &res->nrows, 0, OCI_ATTR_ROW_COUNT, |
| res->handle->err); |
| return res->nrows; |
| } |
| |
| APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_oracle_driver = { |
| "oracle", |
| dbd_oracle_init, |
| dbd_oracle_native, |
| dbd_oracle_open, |
| dbd_oracle_check_conn, |
| dbd_oracle_close, |
| dbd_oracle_select_db, |
| dbd_oracle_start_transaction, |
| dbd_oracle_end_transaction, |
| dbd_oracle_query, |
| dbd_oracle_select, |
| dbd_oracle_num_cols, |
| dbd_oracle_num_tuples, |
| dbd_oracle_get_row, |
| dbd_oracle_get_entry, |
| dbd_oracle_error, |
| dbd_oracle_escape, |
| dbd_oracle_prepare, |
| dbd_oracle_pvquery, |
| dbd_oracle_pvselect, |
| dbd_oracle_pquery, |
| dbd_oracle_pselect, |
| dbd_oracle_get_name, |
| dbd_oracle_transaction_mode_get, |
| dbd_oracle_transaction_mode_set, |
| ":apr%d", |
| dbd_oracle_pvbquery, |
| dbd_oracle_pvbselect, |
| dbd_oracle_pbquery, |
| dbd_oracle_pbselect, |
| dbd_oracle_datum_get |
| }; |
| #endif |