Set *nrows in dbd_pgsql_pquery
[apr-util.git] / dbd / apr_dbd_oracle.c
blobc9f63d94cd9eae3371d85216a0cedda7974752a5
1 /* Copyright 2005 The Apache Software Foundation or its licensors, as
2 * applicable.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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).
23 /* apr_dbd_oracle - a painful attempt
25 * Based first on the documentation at
26 * http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/toc.htm
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.
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
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.
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.
45 /*******************************************************************/
47 /* GLOBAL_PREPARED_STATEMENTS
49 * This probably needs bindings taken away from prepare and into
50 * execute, or else we'll get race conditions on the parameters
51 * Might be able to do it with some pool refactoring.
53 * In fact, though the documentation says statements can run with
54 * different interps and threads (IIRC), it doesn't say anything
55 * about race conditions between bind and execute. So we'd better
56 * not even try until and unless someone who knows oracle from the
57 * inside fixes it.
58 #define GLOBAL_PREPARED_STATEMENTS
61 /* shut compiler up */
62 #ifdef DEBUG
63 #define int_errorcode int errorcode
64 #else
65 #define int_errorcode
66 #endif
68 #include "apu.h"
70 #if APU_HAVE_ORACLE
72 #include <ctype.h>
73 #include <stdlib.h>
74 #include <stdio.h>
76 #include <oci.h>
78 #include "apr_strings.h"
79 #include "apr_time.h"
80 #include "apr_hash.h"
82 #define TRANS_TIMEOUT 30
83 #define MAX_ARG_LEN 256 /* in line with other apr_dbd drivers. We alloc this
84 * lots of times, so a large value gets hungry.
85 * Should really make it configurable
87 #define DEFAULT_LONG_SIZE 4096
88 #define DBD_ORACLE_MAX_COLUMNS 256
89 #define NUMERIC_FIELD_SIZE 32
91 typedef enum {
92 APR_DBD_ORACLE_NULL,
93 APR_DBD_ORACLE_STRING,
94 APR_DBD_ORACLE_INT,
95 APR_DBD_ORACLE_FLOAT,
96 APR_DBD_ORACLE_LOB,
97 /* don't work */
98 APR_DBD_ORACLE_BLOB,
99 APR_DBD_ORACLE_CLOB,
100 } dbd_field_type;
102 #ifdef DEBUG
103 #include <stdio.h>
104 #endif
106 #include "apr_dbd_internal.h"
108 /* declarations */
109 static const char *dbd_oracle_error(apr_dbd_t *sql, int n);
110 static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql,
111 const char *query, const char *label,
112 apr_dbd_prepared_t **statement);
113 static int outputParams(apr_dbd_t*, apr_dbd_prepared_t*);
114 static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql,
115 apr_dbd_results_t **results,
116 apr_dbd_prepared_t *statement,
117 int seek, int nargs, const char **values);
118 static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
119 int *nrows, apr_dbd_prepared_t *statement,
120 int nargs, const char **values);
121 static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
122 apr_dbd_transaction_t **trans);
123 static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans);
125 struct apr_dbd_transaction_t {
126 enum { TRANS_NONE, TRANS_ERROR, TRANS_1, TRANS_2 } status;
127 apr_dbd_t *handle;
128 OCITrans *trans;
129 OCISnapshot *snapshot1;
130 OCISnapshot *snapshot2;
133 struct apr_dbd_results_t {
134 apr_dbd_t* handle;
135 unsigned int rownum;
136 int seek;
137 int nrows;
138 apr_dbd_prepared_t *statement;
141 struct apr_dbd_t {
142 sword status;
143 OCIError *err;
144 OCIServer *svr;
145 OCISvcCtx *svc;
146 OCISession *auth;
147 apr_dbd_transaction_t* trans;
148 apr_pool_t *pool;
149 char buf[200]; /* for error messages */
150 apr_size_t long_size;
153 struct apr_dbd_row_t {
154 int n;
155 apr_dbd_results_t *res;
156 apr_pool_t *pool;
159 typedef struct {
160 dbd_field_type type;
161 sb2 ind;
162 sb4 len;
163 OCIBind *bind;
164 union {
165 void *raw;
166 char *stringval;
167 int *ival;
168 double *floatval;
169 OCILobLocator *lobval;
170 } value;
171 } bind_arg;
173 typedef struct {
174 int type;
175 sb2 ind;
176 ub2 len; /* length of actual output */
177 OCIDefine *defn;
178 apr_size_t sz; /* length of buf for output */
179 union {
180 void *raw;
181 char *stringval;
182 OCILobLocator *lobval;
183 } buf;
184 const char *name;
185 } define_arg;
187 struct apr_dbd_prepared_t {
188 OCIStmt *stmt;
189 int nargs;
190 bind_arg *args;
191 int nout;
192 define_arg *out;
193 apr_dbd_t *handle;
194 apr_pool_t *pool;
195 int type;
198 /* AFAICT from the docs, the OCIEnv thingey can be used async
199 * across threads, so lets have a global one.
201 * We'll need shorter-lived envs to deal with requests and connections
203 * Hmmm, that doesn't work: we don't have a usermem framework.
204 * OK, forget about using APR pools here, until we figure out
205 * the right way to do it (if such a thing exists).
207 static ub4 null = 0;
208 static OCIEnv *dbd_oracle_env = NULL;
209 #ifdef GLOBAL_PREPARED_STATEMENTS
210 static apr_hash_t *oracle_statements = NULL;
211 #endif
213 static apr_status_t dbd_free_lobdesc(void *lob)
215 switch (OCIDescriptorFree(lob, OCI_DTYPE_LOB)) {
216 case OCI_SUCCESS:
217 return APR_SUCCESS;
218 default:
219 return APR_EGENERAL;
223 static apr_status_t dbd_free_snapshot(void *snap)
225 switch (OCIDescriptorFree(snap, OCI_DTYPE_SNAP)) {
226 case OCI_SUCCESS:
227 return APR_SUCCESS;
228 default:
229 return APR_EGENERAL;
233 #ifdef GLOBAL_PREPARED_STATEMENTS
234 static apr_status_t freeStatements(void *ptr)
236 apr_status_t rv = APR_SUCCESS;
237 OCIStmt *stmt;
238 OCIError *err;
239 apr_hash_index_t *index;
240 apr_pool_t *cachepool = apr_hash_pool_get(oracle_statements);
242 if (OCIHandleAlloc(dbd_oracle_env, (dvoid**)&err, OCI_HTYPE_ERROR, 0, NULL)
243 != OCI_SUCCESS) {
244 return APR_EGENERAL;
247 for (index = apr_hash_first(cachepool, oracle_statements);
248 index != NULL;
249 index = apr_hash_next(index)) {
250 apr_hash_this(index, NULL, NULL, (void**)&stmt);
251 if (OCIStmtRelease(stmt, err, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS) {
252 rv = APR_EGENERAL;
254 if (OCIHandleFree(stmt, OCI_HTYPE_STMT) != OCI_SUCCESS) {
255 rv = APR_EGENERAL;
258 if (OCIHandleFree(err, OCI_HTYPE_ERROR) != OCI_SUCCESS) {
259 rv = APR_EGENERAL;
261 return rv;
263 #endif
265 static void dbd_oracle_init(apr_pool_t *pool)
267 if (dbd_oracle_env == NULL) {
268 /* Sadly, OCI_SHARED seems to be impossible to use, due to
269 * various Oracle bugs. See, for example, Oracle MetaLink bug 2972890
270 * and PHP bug http://bugs.php.net/bug.php?id=23733
272 OCIEnvCreate(&dbd_oracle_env, OCI_THREADED, NULL,
273 NULL, NULL, NULL, 0, NULL);
275 #ifdef GLOBAL_PREPARED_STATEMENTS
276 if (oracle_statements == NULL) {
278 oracle_statements = apr_hash_make(pool);
279 apr_pool_cleanup_register(pool, oracle_statements,
280 freeStatements, apr_pool_cleanup_null);
282 #endif
285 static apr_dbd_t *dbd_oracle_open(apr_pool_t *pool, const char *params)
287 apr_dbd_t *ret = apr_pcalloc(pool, sizeof(apr_dbd_t));
288 int_errorcode;
290 char *BLANK = "";
291 struct {
292 const char *field;
293 char *value;
294 } fields[] = {
295 {"user", BLANK},
296 {"pass", BLANK},
297 {"dbname", BLANK},
298 {"server", BLANK},
299 {NULL, NULL}
301 int i;
302 const char *ptr;
303 const char *key;
304 size_t klen;
305 const char *value;
306 size_t vlen;
307 static const char *const delims = " \r\n\t;|,";
309 ret->long_size = DEFAULT_LONG_SIZE;
311 /* Use our own pool, to avoid possible race conditions
312 * on pool ops
314 if (apr_pool_create(&ret->pool, pool) != APR_SUCCESS) {
315 return NULL;
318 /* snitch parsing from the MySQL driver */
319 for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
320 for (key = ptr-1; isspace(*key); --key);
321 klen = 0;
322 while (isalpha(*key)) {
323 if (key == params) {
324 /* Don't parse off the front of the params */
325 --key;
326 ++klen;
327 break;
329 --key;
330 ++klen;
332 ++key;
333 for (value = ptr+1; isspace(*value); ++value);
334 vlen = strcspn(value, delims);
335 for (i=0; fields[i].field != NULL; ++i) {
336 if (!strncasecmp(fields[i].field, key, klen)) {
337 fields[i].value = apr_pstrndup(pool, value, vlen);
338 break;
341 ptr = value+vlen;
344 ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->err,
345 OCI_HTYPE_ERROR, 0, NULL);
346 switch (ret->status) {
347 default:
348 #ifdef DEBUG
349 printf("ret->status is %d\n", ret->status);
350 break;
351 #else
352 return NULL;
353 #endif
354 case OCI_SUCCESS:
355 break;
358 ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svr,
359 OCI_HTYPE_SERVER, 0, NULL);
360 switch (ret->status) {
361 default:
362 #ifdef DEBUG
363 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
364 sizeof(ret->buf), OCI_HTYPE_ERROR);
365 printf("OPEN ERROR %d (alloc svr): %s\n", ret->status, ret->buf);
366 break;
367 #else
368 return NULL;
369 #endif
370 case OCI_SUCCESS:
371 break;
374 ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svc,
375 OCI_HTYPE_SVCCTX, 0, NULL);
376 switch (ret->status) {
377 default:
378 #ifdef DEBUG
379 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
380 sizeof(ret->buf), OCI_HTYPE_ERROR);
381 printf("OPEN ERROR %d (alloc svc): %s\n", ret->status, ret->buf);
382 break;
383 #else
384 return NULL;
385 #endif
386 case OCI_SUCCESS:
387 break;
390 /* All the examples use the #else */
391 #if CAN_DO_LOGIN
392 ret->status = OCILogon(dbd_oracle_env, ret->err, &ret->svc, fields[0].value,
393 strlen(fields[0].value), fields[1].value,
394 strlen(fields[1].value), fields[2].value,
395 strlen(fields[2].value));
396 switch (ret->status) {
397 default:
398 #ifdef DEBUG
399 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
400 sizeof(ret->buf), OCI_HTYPE_ERROR);
401 printf("OPEN ERROR: %s\n", ret->buf);
402 break;
403 #else
404 return NULL;
405 #endif
406 case OCI_SUCCESS:
407 break;
409 #else
410 ret->status = OCIServerAttach(ret->svr, ret->err, fields[3].value,
411 strlen(fields[3].value), OCI_DEFAULT);
412 switch (ret->status) {
413 default:
414 #ifdef DEBUG
415 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
416 sizeof(ret->buf), OCI_HTYPE_ERROR);
417 printf("OPEN ERROR %d (server attach): %s\n", ret->status, ret->buf);
418 break;
419 #else
420 return NULL;
421 #endif
422 case OCI_SUCCESS:
423 break;
425 ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->svr, 0,
426 OCI_ATTR_SERVER, ret->err);
427 switch (ret->status) {
428 default:
429 #ifdef DEBUG
430 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
431 sizeof(ret->buf), OCI_HTYPE_ERROR);
432 printf("OPEN ERROR %d (attr set): %s\n", ret->status, ret->buf);
433 break;
434 #else
435 return NULL;
436 #endif
437 case OCI_SUCCESS:
438 break;
440 ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->auth,
441 OCI_HTYPE_SESSION, 0, NULL);
442 switch (ret->status) {
443 default:
444 #ifdef DEBUG
445 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
446 sizeof(ret->buf), OCI_HTYPE_ERROR);
447 printf("OPEN ERROR %d (alloc auth): %s\n", ret->status, ret->buf);
448 break;
449 #else
450 return NULL;
451 #endif
452 case OCI_SUCCESS:
453 break;
455 ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[0].value,
456 strlen(fields[0].value), OCI_ATTR_USERNAME, ret->err);
457 switch (ret->status) {
458 default:
459 #ifdef DEBUG
460 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
461 sizeof(ret->buf), OCI_HTYPE_ERROR);
462 printf("OPEN ERROR %d (attr username): %s\n", ret->status, ret->buf);
463 break;
464 #else
465 return NULL;
466 #endif
467 case OCI_SUCCESS:
468 break;
470 ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[1].value,
471 strlen(fields[1].value), OCI_ATTR_PASSWORD, ret->err);
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 (attr password): %s\n", ret->status, ret->buf);
478 break;
479 #else
480 return NULL;
481 #endif
482 case OCI_SUCCESS:
483 break;
485 ret->status = OCISessionBegin(ret->svc, ret->err, ret->auth,
486 OCI_CRED_RDBMS, OCI_DEFAULT);
487 switch (ret->status) {
488 default:
489 #ifdef DEBUG
490 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
491 sizeof(ret->buf), OCI_HTYPE_ERROR);
492 printf("OPEN ERROR %d (session begin): %s\n", ret->status, ret->buf);
493 break;
494 #else
495 return NULL;
496 #endif
497 case OCI_SUCCESS:
498 break;
500 ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->auth, 0,
501 OCI_ATTR_SESSION, ret->err);
502 switch (ret->status) {
503 default:
504 #ifdef DEBUG
505 OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
506 sizeof(ret->buf), OCI_HTYPE_ERROR);
507 printf("OPEN ERROR %d (attr session): %s\n", ret->status, ret->buf);
508 #else
509 return NULL;
510 #endif
511 break;
512 case OCI_SUCCESS:
513 break;
515 #endif
516 return ret;
519 #ifdef EXPORT_NATIVE_FUNCS
520 static apr_size_t dbd_oracle_long_size_set(apr_dbd_t *sql,
521 apr_size_t long_size)
523 apr_size_t old_size = sql->long_size;
524 sql->long_size = long_size;
525 return old_size;
527 #endif
529 static const char *dbd_oracle_get_name(const apr_dbd_results_t *res, int n)
531 define_arg *val = &res->statement->out[n];
533 if ((n < 0) || (n >= res->statement->nout)) {
534 return NULL;
536 return val->name;
539 static int dbd_oracle_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
540 apr_dbd_row_t **rowp, int rownum)
542 apr_dbd_row_t *row = *rowp;
543 apr_dbd_t *sql = res->handle;
544 int_errorcode;
546 if (row == NULL) {
547 row = apr_palloc(pool, sizeof(apr_dbd_row_t));
548 *rowp = row;
549 row->res = res;
550 /* Oracle starts counting at 1 according to the docs */
551 row->n = res->seek ? rownum : 1;
552 row->pool = pool;
554 else {
555 if (res->seek) {
556 row->n = rownum;
558 else {
559 ++row->n;
563 if (res->seek) {
564 sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1,
565 OCI_FETCH_ABSOLUTE, row->n, OCI_DEFAULT);
567 else {
568 sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1,
569 OCI_FETCH_NEXT, 0, OCI_DEFAULT);
571 switch (sql->status) {
572 case OCI_SUCCESS:
573 (*rowp)->res = res;
574 return 0;
575 case OCI_NO_DATA:
576 return -1;
577 case OCI_ERROR:
578 #ifdef DEBUG
579 OCIErrorGet(sql->err, 1, NULL, &errorcode,
580 sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
581 printf("Execute error %d: %s\n", sql->status, sql->buf);
582 #endif
583 /* fallthrough */
584 default:
585 return 1;
587 return 0;
590 static const char *dbd_oracle_error(apr_dbd_t *sql, int n)
592 /* This is ugly. Needs us to pass in a buffer of unknown size.
593 * Either we put it on the handle, or we have to keep allocing/copying
595 sb4 errorcode;
597 switch (sql->status) {
598 case OCI_SUCCESS:
599 return "OCI_SUCCESS";
600 case OCI_SUCCESS_WITH_INFO:
601 return "OCI_SUCCESS_WITH_INFO";
602 case OCI_NEED_DATA:
603 return "OCI_NEED_DATA";
604 case OCI_NO_DATA:
605 return "OCI_NO_DATA";
606 case OCI_INVALID_HANDLE:
607 return "OCI_INVALID_HANDLE";
608 case OCI_STILL_EXECUTING:
609 return "OCI_STILL_EXECUTING";
610 case OCI_CONTINUE:
611 return "OCI_CONTINUE";
614 switch (OCIErrorGet(sql->err, 1, NULL, &errorcode,
615 sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR)) {
616 case OCI_SUCCESS:
617 return sql->buf;
618 default:
619 return "internal error: OCIErrorGet failed";
623 static int freeStatement(void *statement)
625 int ret = 0;
627 apr_dbd_t *sql = ((apr_dbd_prepared_t*)statement)->handle;
628 OCIStmt *stmt = ((apr_dbd_prepared_t*)statement)->stmt;
629 OCIError *err = sql->err;
631 sql->status = OCIStmtRelease(stmt, err, NULL, 0, OCI_DEFAULT);
632 switch (sql->status) {
633 case OCI_SUCCESS:
634 break;
635 default:
636 ++ret;
637 break;
640 sql->status = OCIHandleFree(stmt, OCI_HTYPE_STMT);
641 switch (sql->status) {
642 case OCI_SUCCESS:
643 break;
644 default:
645 ++ret;
646 break;
649 return ret;
652 static int dbd_oracle_select(apr_pool_t *pool, apr_dbd_t *sql,
653 apr_dbd_results_t **results,
654 const char *query, int seek)
656 int ret = 0;
657 apr_dbd_prepared_t *statement = NULL;
659 ret = dbd_oracle_prepare(pool, sql, query, NULL, &statement);
660 if (ret != 0) {
661 return ret;
664 ret = dbd_oracle_pselect(pool, sql, results, statement, seek, 0, NULL);
665 if (ret != 0) {
666 return ret;
669 return ret;
672 static int dbd_oracle_query(apr_dbd_t *sql, int *nrows, const char *query)
674 int ret = 0;
675 apr_pool_t *pool;
676 apr_dbd_prepared_t *statement = NULL;
678 if (sql->trans && sql->trans->status == TRANS_ERROR) {
679 return 1;
682 /* make our own pool so that APR allocations don't linger and so that
683 * both Stmt and LOB handles are cleaned up (LOB handles may be
684 * allocated when preparing APR_DBD_ORACLE_CLOB/BLOBs)
686 apr_pool_create(&pool, sql->pool);
688 ret = dbd_oracle_prepare(pool, sql, query, NULL, &statement);
689 if (ret == 0) {
690 ret = dbd_oracle_pquery(pool, sql, nrows, statement, 0, NULL);
691 if (ret == 0) {
692 sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT,
693 nrows, 0, OCI_ATTR_ROW_COUNT,
694 sql->err);
698 apr_pool_destroy(pool);
700 return ret;
703 static const char *dbd_oracle_escape(apr_pool_t *pool, const char *arg,
704 apr_dbd_t *sql)
706 return arg; /* OCI has no concept of string escape */
709 static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql,
710 const char *query, const char *label,
711 apr_dbd_prepared_t **statement)
713 int ret = 0;
714 size_t length;
715 size_t bindlen = 0;
716 int i;
717 char *sqlptr;
718 char *orastr;
719 char *oraptr;
720 apr_dbd_prepared_t *stmt ;
722 /* prepared statements in a global lookup table would be nice,
723 * but we can't do that here because our pool may die leaving
724 * the cached statement orphaned.
725 * OTOH we can do that with Oracle statements, which aren't on
726 * the pool, so long as we don't register a cleanup on our pool!
728 * FIXME:
729 * There's a race condition between cache-lookup and cache-set
730 * But the worst outcome is a statement prepared more than once
731 * and leaked. Is that worth mutexing for?
732 * Hmmm, yes it probably is ... OK, done
735 if (*statement == NULL) {
736 *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
738 stmt = *statement;
739 stmt->handle = sql;
740 stmt->pool = pool;
742 /* If we have a label, we're going to cache it globally.
743 * Check first if we already have it. If not, prepare the
744 * statement under mutex, so we don't end up leaking
745 * concurrent statements
747 * FIXME: Oracle docs say a statement can be used even across
748 * multiple servers, so I assume this is safe .....
750 #ifdef GLOBAL_PREPARED_STATEMENTS
751 if (label != NULL) {
752 stmt->stmt = apr_hash_get(oracle_statements, label, APR_HASH_KEY_STRING);
753 if (stmt->stmt != NULL) {
754 return ret;
756 apr_dbd_mutex_lock();
757 stmt->stmt = apr_hash_get(oracle_statements, label, APR_HASH_KEY_STRING);
758 if (stmt->stmt != NULL) {
759 apr_dbd_mutex_unlock();
760 return ret;
763 #endif
764 /* translate from apr_dbd to native query format */
765 for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
766 if ((sqlptr[0] == '%') && isalnum(sqlptr[1])) {
767 ++stmt->nargs;
769 else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
770 /* ignore %% */
771 ++sqlptr;
774 length = strlen(query) + 1;
775 for (i = stmt->nargs; i > 0; i /= 10) {
776 ++bindlen;
778 length += (2 + bindlen) * stmt->nargs; /* replace "%x" with ":aprN" */
780 oraptr = orastr = apr_palloc(pool, length);
782 if (stmt->nargs > 0) {
783 stmt->args = apr_pcalloc(pool, stmt->nargs*sizeof(bind_arg));
784 for (i=0; i<stmt->nargs; ++i) {
785 stmt->args[i].type = APR_DBD_ORACLE_STRING;
789 i = 0;
790 for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
791 if ((sqlptr[0] == '%') && isalnum(sqlptr[1])) {
792 while (isdigit(*++sqlptr)) {
793 stmt->args[i].len *= 10;
794 stmt->args[i].len += (*sqlptr - '0');
796 oraptr += apr_snprintf(oraptr, length - (oraptr - orastr),
797 ":apr%d", i + 1);
798 switch (*sqlptr) {
799 case 'd':
800 stmt->args[i].type = APR_DBD_ORACLE_INT;
801 break;
802 case 'f':
803 stmt->args[i].type = APR_DBD_ORACLE_FLOAT;
804 break;
805 case 'L':
806 stmt->args[i].type = APR_DBD_ORACLE_LOB;
807 break;
808 /* BLOB and CLOB won't work - use LOB instead */
809 case 'C':
810 stmt->args[i].type = APR_DBD_ORACLE_CLOB;
811 break;
812 case 'B':
813 stmt->args[i].type = APR_DBD_ORACLE_BLOB;
814 break;
815 /* default is STRING, but we already set that */
816 default:
817 stmt->args[i].type = APR_DBD_ORACLE_STRING;
818 break;
820 ++i;
822 else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
823 /* reduce %% to % */
824 *oraptr++ = *sqlptr++;
826 else {
827 *oraptr++ = *sqlptr;
830 *oraptr = '\0';
832 sql->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**) &stmt->stmt,
833 OCI_HTYPE_STMT, 0, NULL);
834 switch (sql->status) {
835 case OCI_SUCCESS:
836 break;
837 default:
838 apr_dbd_mutex_unlock();
839 return 1;
842 sql->status = OCIStmtPrepare(stmt->stmt, sql->err, orastr,
843 strlen(orastr), OCI_NTV_SYNTAX, OCI_DEFAULT);
844 switch (sql->status) {
845 case OCI_SUCCESS:
846 break;
847 default:
848 OCIHandleFree(stmt->stmt, OCI_HTYPE_STMT);
849 apr_dbd_mutex_unlock();
850 return 1;
853 apr_pool_cleanup_register(pool, stmt, freeStatement,
854 apr_pool_cleanup_null);
856 /* Perl gets statement type here */
857 sql->status = OCIAttrGet(stmt->stmt, OCI_HTYPE_STMT, &stmt->type, 0,
858 OCI_ATTR_STMT_TYPE, sql->err);
859 switch (sql->status) {
860 case OCI_SUCCESS:
861 break;
862 default:
863 apr_dbd_mutex_unlock();
864 return 1;
867 /* Perl sets PREFETCH_MEMORY here, but the docs say there's a working default */
868 #if 0
869 sql->status = OCIAttrSet(stmt->stmt, OCI_HTYPE_STMT, &prefetch_size,
870 sizeof(prefetch_size), OCI_ATTR_PREFETCH_MEMORY,
871 sql->err);
872 switch (sql->status) {
873 case OCI_SUCCESS:
874 break;
875 default:
876 apr_dbd_mutex_unlock();
877 return 1;
879 #endif
881 sql->status = OCI_SUCCESS;
882 for (i = 0; i < stmt->nargs; ++i) {
883 switch (stmt->args[i].type) {
884 default:
885 case APR_DBD_ORACLE_STRING:
886 if (stmt->args[i].len == 0) {
887 stmt->args[i].len = MAX_ARG_LEN;
889 stmt->args[i].value.stringval = apr_palloc(pool, stmt->args[i].len);
890 sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
891 sql->err, i+1,
892 stmt->args[i].value.stringval,
893 stmt->args[i].len,
894 SQLT_STR,
895 &stmt->args[i].ind,
896 NULL,
897 (ub2) 0, (ub4) 0,
898 (ub4 *) 0, OCI_DEFAULT);
899 break;
900 case APR_DBD_ORACLE_FLOAT:
901 stmt->args[i].value.raw = apr_palloc(pool, NUMERIC_FIELD_SIZE);
902 sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
903 sql->err, i+1,
904 (void*)&stmt->args[i].value.floatval,
905 sizeof(stmt->args[i].value.floatval),
906 SQLT_FLT,
907 &stmt->args[i].ind,
908 NULL,
909 (ub2) 0, (ub4) 0,
910 (ub4 *) 0, OCI_DEFAULT);
911 break;
912 case APR_DBD_ORACLE_INT:
913 stmt->args[i].value.raw = apr_palloc(pool, NUMERIC_FIELD_SIZE);
914 sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
915 sql->err, i+1,
916 (void*)stmt->args[i].value.ival,
917 sizeof(*stmt->args[i].value.ival),
918 SQLT_INT,
919 &stmt->args[i].ind,
920 NULL,
921 (ub2) 0, (ub4) 0,
922 (ub4 *) 0, OCI_DEFAULT);
923 break;
924 /* lots of examples in the docs use this for LOB
925 * but it relies on knowing the size in advance and
926 * holding it in memory
928 case APR_DBD_ORACLE_LOB:
929 break; /* bind LOBs at write-time */
930 /* This is also cited in the docs for LOB, if we don't
931 * want the whole thing in memory
933 case APR_DBD_ORACLE_BLOB:
934 sql->status = OCIDescriptorAlloc(dbd_oracle_env,
935 (dvoid**)&stmt->args[i].value.lobval,
936 OCI_DTYPE_LOB, 0, NULL);
937 apr_pool_cleanup_register(pool, stmt->args[i].value.lobval,
938 dbd_free_lobdesc,
939 apr_pool_cleanup_null);
940 sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
941 sql->err, i+1,
942 (void*) &stmt->args[i].value.lobval,
944 SQLT_BLOB,
945 &stmt->args[i].ind,
946 NULL,
947 (ub2) 0, (ub4) 0,
948 (ub4 *) 0, OCI_DEFAULT);
949 break;
950 /* This is also cited in the docs for LOB, if we don't
951 * want the whole thing in memory
953 case APR_DBD_ORACLE_CLOB:
954 sql->status = OCIDescriptorAlloc(dbd_oracle_env,
955 (dvoid**)&stmt->args[i].value.lobval,
956 OCI_DTYPE_LOB, 0, NULL);
957 apr_pool_cleanup_register(pool, stmt->args[i].value.lobval,
958 dbd_free_lobdesc,
959 apr_pool_cleanup_null);
960 sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
961 sql->err, i+1,
962 (dvoid*) &stmt->args[i].value.lobval,
964 SQLT_CLOB,
965 &stmt->args[i].ind,
966 NULL,
967 (ub2) 0, (ub4) 0,
968 (ub4 *) 0, OCI_DEFAULT);
969 break;
971 switch (sql->status) {
972 case OCI_SUCCESS:
973 break;
974 default:
975 apr_dbd_mutex_unlock();
976 return 1;
979 switch (stmt->type) {
980 case OCI_STMT_SELECT:
981 ret = outputParams(sql, stmt);
982 break;
983 default:
984 break;
986 #ifdef GLOBAL_PREPARED_STATEMENTS
987 if (label != NULL) {
988 apr_hash_set(oracle_statements, label, APR_HASH_KEY_STRING, stmt->stmt);
989 apr_dbd_mutex_unlock();
991 #endif
992 return ret;
995 static int outputParams(apr_dbd_t *sql, apr_dbd_prepared_t *stmt)
997 OCIParam *parms;
998 int i;
999 int_errorcode;
1000 ub2 paramtype[DBD_ORACLE_MAX_COLUMNS];
1001 ub2 paramsize[DBD_ORACLE_MAX_COLUMNS];
1002 const char *paramname[DBD_ORACLE_MAX_COLUMNS];
1003 ub4 paramnamelen[DBD_ORACLE_MAX_COLUMNS];
1004 /* Perl uses 0 where we used 1 */
1005 sql->status = OCIStmtExecute(sql->svc, stmt->stmt, sql->err, 0, 0,
1006 NULL, NULL, OCI_DESCRIBE_ONLY);
1007 switch (sql->status) {
1008 case OCI_SUCCESS:
1009 case OCI_SUCCESS_WITH_INFO:
1010 break;
1011 case OCI_ERROR:
1012 #ifdef DEBUG
1013 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1014 sql->buf, sizeof(sql->buf),
1015 OCI_HTYPE_ERROR);
1016 printf("Describing prepared statement: %s\n", sql->buf);
1017 #endif
1018 default:
1019 return 1;
1021 while (sql->status == OCI_SUCCESS) {
1022 sql->status = OCIParamGet(stmt->stmt, OCI_HTYPE_STMT,
1023 sql->err, (dvoid**)&parms, stmt->nout+1);
1024 switch (sql->status) {
1025 case OCI_SUCCESS:
1026 sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
1027 &paramtype[stmt->nout],
1028 0, OCI_ATTR_DATA_TYPE, sql->err);
1029 sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
1030 &paramsize[stmt->nout],
1031 0, OCI_ATTR_DATA_SIZE, sql->err);
1032 sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
1033 &paramname[stmt->nout],
1034 &paramnamelen[stmt->nout],
1035 OCI_ATTR_NAME, sql->err);
1036 ++stmt->nout;
1039 switch (sql->status) {
1040 case OCI_SUCCESS:
1041 break;
1042 case OCI_ERROR:
1043 break; /* this is what we expect at end-of-loop */
1044 default:
1045 return 1;
1048 /* OK, the above works. We have the params; now OCIDefine them */
1049 stmt->out = apr_palloc(stmt->pool, stmt->nout*sizeof(define_arg));
1050 for (i=0; i<stmt->nout; ++i) {
1051 stmt->out[i].type = paramtype[i];
1052 stmt->out[i].len = stmt->out[i].sz = paramsize[i];
1053 stmt->out[i].name = apr_pstrmemdup(stmt->pool,
1054 paramname[i], paramnamelen[i]);
1055 switch (stmt->out[i].type) {
1056 default:
1057 switch (stmt->out[i].type) {
1058 case SQLT_NUM: /* 2: numeric, Perl worst case=130+38+3 */
1059 stmt->out[i].sz = 171;
1060 break;
1061 case SQLT_CHR: /* 1: char */
1062 case SQLT_AFC: /* 96: ANSI fixed char */
1063 stmt->out[i].sz *= 4; /* ugh, wasteful UCS-4 handling */
1064 break;
1065 case SQLT_DAT: /* 12: date, depends on NLS date format */
1066 stmt->out[i].sz = 75;
1067 break;
1068 case SQLT_BIN: /* 23: raw binary, perhaps UTF-16? */
1069 stmt->out[i].sz *= 2;
1070 break;
1071 case SQLT_RID: /* 11: rowid */
1072 case SQLT_RDD: /* 104: rowid descriptor */
1073 stmt->out[i].sz = 20;
1074 break;
1075 case SQLT_TIMESTAMP: /* 187: timestamp */
1076 case SQLT_TIMESTAMP_TZ: /* 188: timestamp with time zone */
1077 case SQLT_INTERVAL_YM: /* 189: interval year-to-month */
1078 case SQLT_INTERVAL_DS: /* 190: interval day-to-second */
1079 case SQLT_TIMESTAMP_LTZ: /* 232: timestamp with local time zone */
1080 stmt->out[i].sz = 75;
1081 break;
1082 default:
1083 #ifdef DEBUG
1084 printf("Unsupported data type: %d\n", stmt->out[i].type);
1085 #endif
1086 break;
1088 ++stmt->out[i].sz;
1089 stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
1090 sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
1091 sql->err, i+1,
1092 stmt->out[i].buf.stringval,
1093 stmt->out[i].sz, SQLT_STR,
1094 &stmt->out[i].ind, &stmt->out[i].len,
1095 0, OCI_DEFAULT);
1096 break;
1097 case SQLT_LNG: /* 8: long */
1098 stmt->out[i].sz = sql->long_size * 4 + 4; /* ugh, UCS-4 handling */
1099 stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
1100 sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
1101 sql->err, i+1,
1102 stmt->out[i].buf.raw,
1103 stmt->out[i].sz, SQLT_LVC,
1104 &stmt->out[i].ind, NULL,
1105 0, OCI_DEFAULT);
1106 break;
1107 case SQLT_LBI: /* 24: long binary, perhaps UTF-16? */
1108 stmt->out[i].sz = sql->long_size * 2 + 4; /* room for int prefix */
1109 stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
1110 sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
1111 sql->err, i+1,
1112 stmt->out[i].buf.raw,
1113 stmt->out[i].sz, SQLT_LVB,
1114 &stmt->out[i].ind, NULL,
1115 0, OCI_DEFAULT);
1116 break;
1117 case SQLT_BLOB: /* 113 */
1118 case SQLT_CLOB: /* 112 */
1119 /*http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/oci05bnd.htm#434937*/
1120 sql->status = OCIDescriptorAlloc(dbd_oracle_env,
1121 (dvoid**)&stmt->out[i].buf.lobval,
1122 OCI_DTYPE_LOB, 0, NULL);
1123 apr_pool_cleanup_register(stmt->pool, stmt->out[i].buf.lobval,
1124 dbd_free_lobdesc,
1125 apr_pool_cleanup_null);
1126 sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
1127 sql->err, i+1,
1128 (dvoid*) &stmt->out[i].buf.lobval,
1129 -1, stmt->out[i].type,
1130 &stmt->out[i].ind, &stmt->out[i].len,
1131 0, OCI_DEFAULT);
1132 break;
1134 switch (sql->status) {
1135 case OCI_SUCCESS:
1136 break;
1137 default:
1138 return 1;
1141 return 0;
1144 static int dbd_oracle_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
1145 int *nrows, apr_dbd_prepared_t *statement,
1146 va_list args)
1148 const char *arg = NULL;
1149 OCISnapshot *oldsnapshot = NULL;
1150 OCISnapshot *newsnapshot = NULL;
1151 apr_dbd_transaction_t* trans = sql->trans;
1152 int i;
1153 int n;
1154 int exec_mode;
1155 int_errorcode;
1157 if (trans) {
1158 switch (trans->status) {
1159 case TRANS_ERROR:
1160 return -1;
1161 case TRANS_NONE:
1162 trans = NULL;
1163 break;
1164 case TRANS_1:
1165 oldsnapshot = trans->snapshot1;
1166 newsnapshot = trans->snapshot2;
1167 trans->status = TRANS_2;
1168 break;
1169 case TRANS_2:
1170 oldsnapshot = trans->snapshot2;
1171 newsnapshot = trans->snapshot1;
1172 trans->status = TRANS_1;
1173 break;
1175 exec_mode = OCI_DEFAULT;
1177 else {
1178 exec_mode = OCI_COMMIT_ON_SUCCESS;
1181 /* we've bound these vars, so now we just copy data in to them */
1182 for (i=0; i<statement->nargs; ++i) {
1183 switch (statement->args[i].type) {
1184 case APR_DBD_ORACLE_INT:
1185 n = va_arg(args, int);
1186 sprintf(statement->args[i].value.stringval, "%d", n);
1187 break;
1188 case APR_DBD_ORACLE_FLOAT:
1189 *statement->args[i].value.floatval = va_arg(args, double);
1190 break;
1191 case APR_DBD_ORACLE_BLOB:
1192 case APR_DBD_ORACLE_CLOB:
1193 /* Nothing works */
1194 break;
1195 case APR_DBD_ORACLE_LOB:
1196 /* requires strlen() over large data, which may fail for binary */
1197 statement->args[i].value.raw = va_arg(args, char*);
1198 statement->args[i].len =
1199 strlen(statement->args[i].value.stringval);
1200 sql->status = OCIBindByPos(statement->stmt,
1201 &statement->args[i].bind,
1202 sql->err, i+1,
1203 (void*)statement->args[i].value.raw,
1204 statement->args[i].len, SQLT_LNG,
1205 &statement->args[i].ind,
1206 NULL,
1207 (ub2) 0, (ub4) 0,
1208 (ub4 *) 0, OCI_DEFAULT);
1209 break;
1210 case APR_DBD_ORACLE_STRING:
1211 default:
1212 arg = va_arg(args, char*);
1213 if (strlen(arg) >= statement->args[i].len) {
1214 strncpy(statement->args[i].value.stringval, arg,
1215 statement->args[i].len-1);
1217 else {
1218 strcpy(statement->args[i].value.stringval, arg);
1220 break;
1224 sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0,
1225 oldsnapshot, newsnapshot, exec_mode);
1226 switch (sql->status) {
1227 case OCI_SUCCESS:
1228 break;
1229 case OCI_ERROR:
1230 #ifdef DEBUG
1231 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1232 sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1233 printf("Execute error %d: %s\n", sql->status, sql->buf);
1234 #endif
1235 /* fallthrough */
1236 default:
1237 if (trans) {
1238 trans->status = TRANS_ERROR;
1240 return 1;
1243 for (i=0; i<statement->nargs; ++i) {
1244 /* what should these really be? */
1245 ub1 csfrm = SQLCS_IMPLICIT;
1246 ub2 csid = 0;
1247 ub4 len = statement->args[i].len;
1248 switch (statement->args[i].type) {
1249 case APR_DBD_ORACLE_BLOB:
1250 case APR_DBD_ORACLE_CLOB:
1251 /* doesn't work - use APR_DBD_ORACLE_LOB instead */
1252 sql->status = OCILobEnableBuffering(sql->svc, sql->err,statement->args[i].value.lobval);
1253 #ifdef DEBUG
1254 if (sql->status == OCI_ERROR) {
1255 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1256 sql->buf, sizeof(sql->buf),
1257 OCI_HTYPE_ERROR);
1258 printf("LOB error %d: %s\n", sql->status, sql->buf);
1260 #endif
1261 sql->status = OCILobWrite(sql->svc, sql->err,
1262 statement->args[i].value.lobval,
1263 &len, 0, (dvoid*) arg, strlen(arg),
1264 OCI_ONE_PIECE, NULL, NULL, csid, csfrm);
1265 break;
1266 default:
1267 break;
1269 switch (sql->status) {
1270 case OCI_SUCCESS:
1271 break;
1272 case OCI_ERROR:
1273 #ifdef DEBUG
1274 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1275 sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1276 printf("LOB error %d: %s\n", sql->status, sql->buf);
1277 #endif
1278 /* fallthrough */
1279 default:
1280 if (trans) {
1281 trans->status = TRANS_ERROR;
1283 return 1;
1287 sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0,
1288 OCI_ATTR_ROW_COUNT, sql->err);
1289 return 0;
1292 static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
1293 int *nrows, apr_dbd_prepared_t *statement,
1294 int nargs, const char **values)
1296 int_errorcode;
1297 OCISnapshot *oldsnapshot = NULL;
1298 OCISnapshot *newsnapshot = NULL;
1299 apr_dbd_transaction_t* trans = sql->trans;
1300 int i;
1301 int exec_mode;
1303 if (trans) {
1304 switch (trans->status) {
1305 case TRANS_ERROR:
1306 return -1;
1307 case TRANS_NONE:
1308 trans = NULL;
1309 break;
1310 case TRANS_1:
1311 oldsnapshot = trans->snapshot1;
1312 newsnapshot = trans->snapshot2;
1313 trans->status = TRANS_2;
1314 break;
1315 case TRANS_2:
1316 oldsnapshot = trans->snapshot2;
1317 newsnapshot = trans->snapshot1;
1318 trans->status = TRANS_1;
1319 break;
1321 exec_mode = OCI_DEFAULT;
1323 else {
1324 exec_mode = OCI_COMMIT_ON_SUCCESS;
1327 /* we've bound these vars, so now we just copy data in to them */
1328 if (nargs > statement->nargs) {
1329 nargs = statement->nargs;
1331 for (i=0; i<nargs; ++i) {
1332 switch (statement->args[i].type) {
1333 case APR_DBD_ORACLE_INT:
1334 sscanf(values[i], "%d", statement->args[i].value.ival);
1335 break;
1336 case APR_DBD_ORACLE_FLOAT:
1337 sscanf(values[i], "%lf", statement->args[i].value.floatval);
1338 break;
1339 case APR_DBD_ORACLE_BLOB:
1340 case APR_DBD_ORACLE_CLOB:
1341 sql->status = OCIAttrSet(statement->args[i].value.lobval,
1342 OCI_DTYPE_LOB, &null, 0,
1343 OCI_ATTR_LOBEMPTY, sql->err);
1344 break;
1345 case APR_DBD_ORACLE_LOB:
1346 /* requires strlen() over large data, which may fail for binary */
1347 statement->args[i].value.raw = (char*)values[i];
1348 statement->args[i].len =
1349 strlen(statement->args[i].value.stringval);
1350 sql->status = OCIBindByPos(statement->stmt,
1351 &statement->args[i].bind,
1352 sql->err, i+1,
1353 (void*)statement->args[i].value.raw,
1354 statement->args[i].len, SQLT_LNG,
1355 &statement->args[i].ind,
1356 NULL,
1357 (ub2) 0, (ub4) 0,
1358 (ub4 *) 0, OCI_DEFAULT);
1359 break;
1360 case APR_DBD_ORACLE_STRING:
1361 default:
1362 if (strlen(values[i]) >= statement->args[i].len) {
1363 strncpy(statement->args[i].value.stringval, values[i],
1364 statement->args[i].len-1);
1366 else {
1367 strcpy(statement->args[i].value.stringval, values[i]);
1369 break;
1373 sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0,
1374 oldsnapshot, newsnapshot, exec_mode);
1375 switch (sql->status) {
1376 case OCI_SUCCESS:
1377 break;
1378 case OCI_ERROR:
1379 #ifdef DEBUG
1380 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1381 sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
1382 printf("Execute error %d: %s\n", sql->status, sql->buf);
1383 #endif
1384 /* fallthrough */
1385 default:
1386 if (trans) {
1387 trans->status = TRANS_ERROR;
1389 return 1;
1392 sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0,
1393 OCI_ATTR_ROW_COUNT, sql->err);
1394 return 0;
1397 static int dbd_oracle_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
1398 apr_dbd_results_t **results,
1399 apr_dbd_prepared_t *statement,
1400 int seek, va_list args)
1402 int i;
1403 char *arg;
1404 int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
1405 OCISnapshot *oldsnapshot = NULL;
1406 OCISnapshot *newsnapshot = NULL;
1407 apr_dbd_transaction_t* trans = sql->trans;
1408 int_errorcode;
1410 if (trans) {
1411 switch (trans->status) {
1412 case TRANS_ERROR:
1413 return 1;
1414 case TRANS_NONE:
1415 trans = NULL;
1416 break;
1417 case TRANS_1:
1418 oldsnapshot = trans->snapshot1;
1419 newsnapshot = trans->snapshot2;
1420 trans->status = TRANS_2;
1421 break;
1422 case TRANS_2:
1423 oldsnapshot = trans->snapshot2;
1424 newsnapshot = trans->snapshot1;
1425 trans->status = TRANS_1;
1426 break;
1430 /* we've bound these vars, so now we just copy data in to them */
1431 for (i=0; i<statement->nargs; ++i) {
1432 int len;
1433 switch (statement->args[i].type) {
1434 case APR_DBD_ORACLE_INT:
1435 *statement->args[i].value.ival = va_arg(args, int);
1436 break;
1437 case APR_DBD_ORACLE_FLOAT:
1438 *statement->args[i].value.floatval = va_arg(args, double);
1439 break;
1440 case APR_DBD_ORACLE_BLOB:
1441 case APR_DBD_ORACLE_CLOB:
1442 sql->status = OCIAttrSet(statement->args[i].value.lobval,
1443 OCI_DTYPE_LOB, &null, 0,
1444 OCI_ATTR_LOBEMPTY, sql->err);
1445 break;
1446 case APR_DBD_ORACLE_STRING:
1447 default:
1448 arg = va_arg(args, char*);
1449 len = strlen(arg);
1450 if (len >= statement->args[i].len) {
1451 len = statement->args[i].len - 1;
1452 strncpy(statement->args[i].value.stringval, arg, len);
1453 statement->args[i].value.stringval[len] = '\0';
1455 else {
1456 strcpy(statement->args[i].value.stringval, arg);
1458 ++len;
1459 sql->status = OCIAttrSet(statement->args[i].bind,
1460 OCI_DTYPE_PARAM, &len, 0,
1461 OCI_ATTR_DATA_SIZE, sql->err);
1462 break;
1466 sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
1467 oldsnapshot, newsnapshot, exec_mode);
1468 switch (sql->status) {
1469 case OCI_SUCCESS:
1470 break;
1471 case OCI_ERROR:
1472 #ifdef DEBUG
1473 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1474 sql->buf, sizeof(sql->buf),
1475 OCI_HTYPE_ERROR);
1476 printf("Executing prepared statement: %s\n", sql->buf);
1477 #endif
1478 default:
1479 if (trans) {
1480 trans->status = TRANS_ERROR;
1482 return 1;
1485 if (!*results) {
1486 *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
1488 (*results)->handle = sql;
1489 (*results)->statement = statement;
1490 (*results)->seek = seek;
1491 (*results)->rownum = seek ? 0 : -1;
1493 return 0;
1496 static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql,
1497 apr_dbd_results_t **results,
1498 apr_dbd_prepared_t *statement,
1499 int seek, int nargs, const char **values)
1501 int i;
1502 int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
1503 OCISnapshot *oldsnapshot = NULL;
1504 OCISnapshot *newsnapshot = NULL;
1505 apr_dbd_transaction_t* trans = sql->trans;
1507 if (trans) {
1508 switch (trans->status) {
1509 case TRANS_ERROR:
1510 return 1;
1511 case TRANS_NONE:
1512 trans = NULL;
1513 break;
1514 case TRANS_1:
1515 oldsnapshot = trans->snapshot1;
1516 newsnapshot = trans->snapshot2;
1517 trans->status = TRANS_2;
1518 break;
1519 case TRANS_2:
1520 oldsnapshot = trans->snapshot2;
1521 newsnapshot = trans->snapshot1;
1522 trans->status = TRANS_1;
1523 break;
1527 /* we've bound these vars, so now we just copy data in to them */
1528 if (nargs > statement->nargs) {
1529 nargs = statement->nargs;
1531 for (i=0; i<nargs; ++i) {
1532 int foo;
1533 switch (statement->args[i].type) {
1534 case APR_DBD_ORACLE_INT:
1535 sscanf(values[i], "%d", &foo);
1536 *statement->args[i].value.ival = foo;
1537 break;
1538 case APR_DBD_ORACLE_FLOAT:
1539 sscanf(values[i], "%lf", statement->args[i].value.floatval);
1540 break;
1541 case APR_DBD_ORACLE_BLOB:
1542 case APR_DBD_ORACLE_CLOB:
1543 sql->status = OCIAttrSet(statement->args[i].value.lobval,
1544 OCI_DTYPE_LOB, &null, 0,
1545 OCI_ATTR_LOBEMPTY, sql->err);
1546 break;
1547 case APR_DBD_ORACLE_STRING:
1548 default:
1549 if (strlen(values[i]) >= MAX_ARG_LEN) {
1550 strncpy(statement->args[i].value.stringval, values[i],
1551 MAX_ARG_LEN-1);
1553 else {
1554 strcpy(statement->args[i].value.stringval, values[i]);
1556 break;
1560 sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
1561 oldsnapshot, newsnapshot, exec_mode);
1562 switch (sql->status) {
1563 int_errorcode;
1564 case OCI_SUCCESS:
1565 break;
1566 case OCI_ERROR:
1567 #ifdef DEBUG
1568 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1569 sql->buf, sizeof(sql->buf),
1570 OCI_HTYPE_ERROR);
1571 printf("Executing prepared statement: %s\n", sql->buf);
1572 #endif
1573 default:
1574 if (trans) {
1575 trans->status = TRANS_ERROR;
1577 return 1;
1580 if (!*results) {
1581 *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
1583 (*results)->handle = sql;
1584 (*results)->statement = statement;
1585 (*results)->seek = seek;
1586 (*results)->rownum = seek ? 0 : -1;
1588 return 0;
1591 static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
1592 apr_dbd_transaction_t **trans)
1594 int ret = 0;
1595 int_errorcode;
1596 if (*trans) {
1597 dbd_oracle_end_transaction(*trans);
1599 else {
1600 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1601 OCIHandleAlloc(dbd_oracle_env, (dvoid**)&(*trans)->trans,
1602 OCI_HTYPE_TRANS, 0, 0);
1603 OCIAttrSet(sql->svc, OCI_HTYPE_SVCCTX, (*trans)->trans, 0,
1604 OCI_ATTR_TRANS, sql->err);
1608 sql->status = OCITransStart(sql->svc, sql->err, TRANS_TIMEOUT,
1609 OCI_TRANS_NEW);
1610 switch (sql->status) {
1611 case OCI_ERROR:
1612 #ifdef DEBUG
1613 OCIErrorGet(sql->err, 1, NULL, &errorcode, sql->buf,
1614 sizeof(sql->buf), OCI_HTYPE_ERROR);
1615 printf("Transaction: %s\n", sql->buf);
1616 #endif
1617 ret = 1;
1618 break;
1619 case OCI_SUCCESS:
1620 (*trans)->handle = sql;
1621 (*trans)->status = TRANS_1;
1622 sql->trans = *trans;
1623 switch (OCIDescriptorAlloc(dbd_oracle_env,
1624 (dvoid**)&(*trans)->snapshot1,
1625 OCI_DTYPE_SNAP, 0, NULL)) {
1626 case OCI_SUCCESS:
1627 apr_pool_cleanup_register(pool, (*trans)->snapshot1,
1628 dbd_free_snapshot, apr_pool_cleanup_null);
1629 break;
1630 case OCI_INVALID_HANDLE:
1631 ret = 1;
1632 break;
1634 switch (OCIDescriptorAlloc(dbd_oracle_env,
1635 (dvoid**)&(*trans)->snapshot2,
1636 OCI_DTYPE_SNAP, 0, NULL)) {
1637 case OCI_SUCCESS:
1638 apr_pool_cleanup_register(pool, (*trans)->snapshot2,
1639 dbd_free_snapshot, apr_pool_cleanup_null);
1640 break;
1641 case OCI_INVALID_HANDLE:
1642 ret = 1;
1643 break;
1645 break;
1646 default:
1647 ret = 1;
1648 break;
1650 return ret;
1653 static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans)
1655 int ret = 1; /* no transaction is an error cond */
1656 sword status;
1657 apr_dbd_t *handle = trans->handle;
1658 if (trans) {
1659 switch (trans->status) {
1660 case TRANS_NONE: /* No trans is an error here */
1661 status = OCI_ERROR;
1662 break;
1663 case TRANS_ERROR:
1664 status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
1665 break;
1666 default:
1667 status = OCITransCommit(handle->svc, handle->err, OCI_DEFAULT);
1668 break;
1671 handle->trans = NULL;
1673 switch (status) {
1674 case OCI_SUCCESS:
1675 ret = 0;
1676 break;
1677 default:
1678 ret = 3;
1679 break;
1682 return ret;
1685 /* This doesn't work for BLOB because of NULLs, but it can fake it
1686 * if the BLOB is really a string
1688 static const char *dbd_oracle_get_entry(const apr_dbd_row_t *row, int n)
1690 int_errorcode;
1691 ub4 len = 0;
1692 ub1 csform = 0;
1693 ub2 csid = 0;
1694 apr_size_t buflen = 0;
1695 char *buf = NULL;
1696 define_arg *val = &row->res->statement->out[n];
1697 apr_dbd_t *sql = row->res->handle;
1699 if ((n < 0) || (n >= row->res->statement->nout) || (val->ind == -1)) {
1700 return NULL;
1703 switch (val->type) {
1704 case SQLT_BLOB:
1705 case SQLT_CLOB:
1706 sql->status = OCILobGetLength(sql->svc, sql->err, val->buf.lobval,
1707 &len);
1708 switch (sql->status) {
1709 case OCI_SUCCESS:
1710 case OCI_SUCCESS_WITH_INFO:
1711 if (len == 0) {
1712 buf = "";
1714 break;
1715 case OCI_ERROR:
1716 #ifdef DEBUG
1717 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1718 sql->buf, sizeof(sql->buf),
1719 OCI_HTYPE_ERROR);
1720 printf("Finding LOB length: %s\n", sql->buf);
1721 break;
1722 #endif
1723 default:
1724 break;
1727 if (len == 0) {
1728 break;
1731 if (val->type == APR_DBD_ORACLE_CLOB) {
1733 #if 1
1734 /* Is this necessary, or can it be defaulted? */
1735 sql->status = OCILobCharSetForm(dbd_oracle_env, sql->err,
1736 val->buf.lobval, &csform);
1737 if (sql->status == OCI_SUCCESS) {
1738 sql->status = OCILobCharSetId(dbd_oracle_env, sql->err,
1739 val->buf.lobval, &csid);
1741 switch (sql->status) {
1742 case OCI_SUCCESS:
1743 case OCI_SUCCESS_WITH_INFO:
1744 buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */
1745 /* zeroise all - where the string ends depends on charset */
1746 buf = apr_pcalloc(row->pool, buflen);
1747 break;
1748 #ifdef DEBUG
1749 case OCI_ERROR:
1750 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1751 sql->buf, sizeof(sql->buf),
1752 OCI_HTYPE_ERROR);
1753 printf("Reading LOB character set: %s\n", sql->buf);
1754 break; /*** XXX?? ***/
1755 #endif
1756 default:
1757 break; /*** XXX?? ***/
1759 #else /* ignore charset */
1760 buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */
1761 /* zeroise all - where the string ends depends on charset */
1762 buf = apr_pcalloc(row->pool, buflen);
1763 #endif
1764 } else {
1765 /* BUG: this'll only work if the BLOB looks like a string */
1766 buflen = len;
1767 buf = apr_palloc(row->pool, buflen+1);
1768 buf[buflen] = 0;
1771 if (!buf) {
1772 break;
1775 sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval,
1776 &len, 1, (dvoid*) buf, buflen,
1777 NULL, NULL, csid, csform);
1778 switch (sql->status) {
1779 case OCI_SUCCESS:
1780 case OCI_SUCCESS_WITH_INFO:
1781 break;
1782 #ifdef DEBUG
1783 case OCI_ERROR:
1784 sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
1785 sql->buf, sizeof(sql->buf),
1786 OCI_HTYPE_ERROR);
1787 printf("Reading LOB: %s\n", sql->buf);
1788 buf = NULL; /*** XXX?? ***/
1789 break;
1790 #endif
1791 default:
1792 buf = NULL; /*** XXX?? ***/
1793 break;
1796 break;
1797 case SQLT_LNG:
1798 case SQLT_LBI:
1799 /* raw is struct { ub4 len; char *buf; } */
1800 len = *(ub4*) val->buf.raw;
1801 buf = apr_pstrndup(row->pool, val->buf.stringval + sizeof(ub4), len);
1802 break;
1803 default:
1804 buf = apr_pstrndup(row->pool, val->buf.stringval, val->len);
1805 break;
1807 return (const char*) buf;
1810 static apr_status_t dbd_oracle_close(apr_dbd_t *handle)
1812 /* FIXME: none of the oracle docs/examples say anything about
1813 * closing/releasing handles. Which seems unlikely ...
1816 /* OK, let's grab from cdemo again.
1817 * cdemo81 does nothing; cdemo82 does OCIHandleFree on the handles
1819 switch (OCISessionEnd(handle->svc, handle->err, handle->auth,
1820 (ub4)OCI_DEFAULT)) {
1821 default:
1822 break;
1824 switch (OCIServerDetach(handle->svr, handle->err, (ub4) OCI_DEFAULT )) {
1825 default:
1826 break;
1828 /* does OCISessionEnd imply this? */
1829 switch (OCIHandleFree((dvoid *) handle->auth, (ub4) OCI_HTYPE_SESSION)) {
1830 default:
1831 break;
1833 switch (OCIHandleFree((dvoid *) handle->svr, (ub4) OCI_HTYPE_SERVER)) {
1834 default:
1835 break;
1837 switch (OCIHandleFree((dvoid *) handle->svc, (ub4) OCI_HTYPE_SVCCTX)) {
1838 default:
1839 break;
1841 switch (OCIHandleFree((dvoid *) handle->err, (ub4) OCI_HTYPE_ERROR)) {
1842 default:
1843 break;
1845 apr_pool_destroy(handle->pool);
1846 return APR_SUCCESS;
1849 static apr_status_t dbd_oracle_check_conn(apr_pool_t *pool,
1850 apr_dbd_t *handle)
1852 /* FIXME: need to find this in the docs */
1853 return APR_ENOTIMPL;
1856 static int dbd_oracle_select_db(apr_pool_t *pool, apr_dbd_t *handle,
1857 const char *name)
1859 /* FIXME: need to find this in the docs */
1860 return APR_ENOTIMPL;
1863 static void *dbd_oracle_native(apr_dbd_t *handle)
1865 /* FIXME: can we do anything better? Oracle doesn't seem to have
1866 * a concept of a handle in the sense we use it.
1868 return dbd_oracle_env;
1871 static int dbd_oracle_num_cols(apr_dbd_results_t* res)
1873 return res->statement->nout;
1876 static int dbd_oracle_num_tuples(apr_dbd_results_t* res)
1878 if (!res->seek) {
1879 return -1;
1881 if (res->nrows >= 0) {
1882 return res->nrows;
1884 res->handle->status = OCIAttrGet(res->statement->stmt, OCI_HTYPE_STMT,
1885 &res->nrows, 0, OCI_ATTR_ROW_COUNT,
1886 res->handle->err);
1887 return res->nrows;
1890 APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_oracle_driver = {
1891 "oracle",
1892 dbd_oracle_init,
1893 dbd_oracle_native,
1894 dbd_oracle_open,
1895 dbd_oracle_check_conn,
1896 dbd_oracle_close,
1897 dbd_oracle_select_db,
1898 dbd_oracle_start_transaction,
1899 dbd_oracle_end_transaction,
1900 dbd_oracle_query,
1901 dbd_oracle_select,
1902 dbd_oracle_num_cols,
1903 dbd_oracle_num_tuples,
1904 dbd_oracle_get_row,
1905 dbd_oracle_get_entry,
1906 dbd_oracle_error,
1907 dbd_oracle_escape,
1908 dbd_oracle_prepare,
1909 dbd_oracle_pvquery,
1910 dbd_oracle_pvselect,
1911 dbd_oracle_pquery,
1912 dbd_oracle_pselect,
1913 dbd_oracle_get_name
1915 #endif