Apache HTTPD
apr_dbd_freetds.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifdef I_CAN_DEAL_WITH_THIS_PARTIAL_DRIVER_AND_UNMAINTAINED_CODE_FOR_FREETDS
18
19#include "apu.h"
20#include "apu_config.h"
21
22/* COMPILE_STUBS: compile stubs for unimplemented functions.
23 *
24 * This is required to compile in /trunk/, but can be
25 * undefined to compile a driver for httpd-2.2 and other
26 * APR-1.2 applications
27 */
28#define COMPILE_STUBS
29
30#if APU_HAVE_FREETDS
31
32#include <ctype.h>
33#include <stdlib.h>
34
35#include "apr_strings.h"
36#include "apr_lib.h"
37
38#include "apr_pools.h"
39#include "apr_dbd_internal.h"
40
41#ifdef HAVE_FREETDS_SYBDB_H
42#include <freetds/sybdb.h>
43#endif
44#ifdef HAVE_SYBDB_H
45#include <sybdb.h>
46#endif
47
48#include <stdio.h>
49#include <sys/types.h>
50#include <regex.h>
51
52/* This probably needs to change for different applications */
53#define MAX_COL_LEN 256
54
55typedef struct freetds_cell_t {
56 int type;
57 DBINT len;
58 BYTE *data;
60
62 int mode;
63 int errnum;
65};
66
67struct apr_dbd_t {
71 const char *params;
73};
74
75struct apr_dbd_results_t {
76 int random;
77 size_t ntuples;
78 size_t sz;
81};
82
83struct apr_dbd_row_t {
86};
87
88struct apr_dbd_prepared_t {
89 int nargs;
90 regex_t **taint;
91 int *sz;
92 char *fmt;
93};
94
95#define dbd_freetds_is_success(x) (x == SUCCEED)
96
97static int labelnum = 0; /* FIXME */
99
100/* execute a query that doesn't return a result set, mop up,
101 * and return and APR-flavoured status
102 */
103static RETCODE freetds_exec(DBPROCESS *proc, const char *query,
104 int want_results, int *nrows)
105{
106 /* TBD */
107 RETCODE rv = dbcmd(proc, query);
108 if (rv != SUCCEED) {
109 return rv;
110 }
111 rv = dbsqlexec(proc);
112 if (rv != SUCCEED) {
113 return rv;
114 }
115 if (!want_results) {
116 while (dbresults(proc) != NO_MORE_RESULTS) {
117 ++*nrows;
118 }
119 }
120 return SUCCEED;
121}
122static apr_status_t clear_result(void *data)
123{
124 /* clear cursor */
125 return (dbcanquery((DBPROCESS*)data) == SUCCEED)
127 : APR_EGENERAL;
128}
129
131 apr_dbd_results_t **results,
132 const char *query, int seek)
133{
135 if (sql->trans && (sql->trans->errnum != SUCCEED)) {
136 return 1;
137 }
138 /* the core of this is
139 * dbcmd(proc, query);
140 * dbsqlexec(proc);
141 * while (dbnextrow(dbproc) != NO_MORE_ROWS) {
142 * do things
143 * }
144 *
145 * Ignore seek
146 */
147
148 sql->err = freetds_exec(sql->proc, query, 1, NULL);
149 if (!dbd_freetds_is_success(sql->err)) {
150 if (sql->trans) {
151 sql->trans->errnum = sql->err;
152 }
153 return 1;
154 }
155
156 sql->err = dbresults(sql->proc);
157 if (sql->err != SUCCEED) {
158 if (sql->trans) {
159 sql->trans->errnum = sql->err;
160 }
161 return 1;
162 }
163
164 if (!*results) {
165 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
166 }
167 res = *results;
168 res->proc = sql->proc;
169 res->random = seek;
170 res->pool = pool;
171 res->ntuples = dblastrow(sql->proc);
172 res->sz = dbnumcols(sql->proc);
175
176#if 0
177 /* Now we have a result set. We need to bind to its vars */
178 res->vars = apr_palloc(pool, res->sz * sizeof(freetds_cell_t*));
179 for (i=1; i <= res->sz; ++i) {
180 freetds_cell_t *cell = &res->vars[i-1];
181 cell->type = dbcoltype(sql->proc, i);
182 cell->len = dbcollen(sql->proc, i);
183 cell->data = apr_palloc(pool, cell->len);
184 sql->err = dbbind(sql->proc, i, /*cell->type */ STRINGBIND, cell->len, cell->data);
185 if (sql->err != SUCCEED) {
186 fprintf(stderr, "dbbind error: %d, %d, %d", i, cell->type, cell->len);
187 }
188 if ((sql->err != SUCCEED) && (sql->trans != NULL)) {
189 sql->trans->errnum = sql->err;
190 }
191 }
192#endif
193 return (sql->err == SUCCEED) ? 0 : 1;
194}
195static const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val)
196{
197 regmatch_t match[1];
198 if (rx == NULL) {
199 /* no untaint expression */
200 return val;
201 }
202 if (regexec(rx, val, 1, match, 0) == 0) {
203 return apr_pstrndup(pool, val+match[0].rm_so,
204 match[0].rm_eo - match[0].rm_so);
205 }
206 return "";
207}
208static const char *dbd_statement(apr_pool_t *pool,
210 int nargs, const char **args)
211{
212 int i;
213 int len;
214 const char *var;
215 char *ret;
216 const char *p_in;
217 char *p_out;
218 char *q;
219
220 /* compute upper bound on length (since untaint shrinks) */
221 len = strlen(stmt->fmt) +1;
222 for (i=0; i<nargs; ++i) {
223 len += strlen(args[i]) - 2;
224 }
225 i = 0;
226 p_in = stmt->fmt;
228 /* FIXME silly bug - this'll catch %%s */
229 while (q = strstr(p_in, "%s"), q != NULL) {
230 len = q-p_in;
232 p_in += len;
233 p_out += len;
234 var = dbd_untaint(pool, stmt->taint[i], args[i]);
235 len = strlen(var);
236 strncpy(p_out, var, len);
237 p_in += 2;
238 p_out += len;
239 ++i;
240 }
241 strcpy(p_out, p_in);
242 return ret;
243}
245 apr_dbd_results_t **results,
247 int seek, const char **values)
248{
249 const char *query = dbd_statement(pool, statement,
250 statement->nargs, values);
251 return dbd_freetds_select(pool, sql, results, query, seek);
252}
254 apr_dbd_results_t **results,
256 int seek, va_list args)
257{
258 const char **values;
259 int i;
260
261 if (sql->trans && sql->trans->errnum) {
262 return sql->trans->errnum;
263 }
264
265 values = apr_palloc(pool, sizeof(*values) * statement->nargs);
266
267 for (i = 0; i < statement->nargs; i++) {
268 values[i] = va_arg(args, const char*);
269 }
270
271 return dbd_freetds_pselect(pool, sql, results, statement, seek, values);
272}
273static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query);
276 const char **values)
277{
278 const char *query = dbd_statement(pool, statement,
279 statement->nargs, values);
281}
284{
285 const char **values;
286 int i;
287
288 if (sql->trans && sql->trans->errnum) {
289 return sql->trans->errnum;
290 }
291
292 values = apr_palloc(pool, sizeof(*values) * statement->nargs);
293
294 for (i = 0; i < statement->nargs; i++) {
295 values[i] = va_arg(args, const char*);
296 }
298}
299
302{
303 RETCODE rv = 0;
305 int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
306
307 if (row == NULL) {
308 row = apr_palloc(pool, sizeof(apr_dbd_row_t));
309 *rowp = row;
310 row->res = res;
311 }
312 /*
313 else {
314 if ( sequential ) {
315 ++row->n;
316 }
317 else {
318 row->n = rownum;
319 }
320 }
321 */
322 if (sequential) {
323 rv = dbnextrow(res->proc);
324 }
325 else {
326 rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
327 }
328 switch (rv) {
329 case SUCCEED: return 0;
330 case REG_ROW: return 0;
331 case NO_MORE_ROWS:
333 *rowp = NULL;
334 return -1;
335 case FAIL: return 1;
336 case BUF_FULL: return 2; /* FIXME */
337 default: return 3;
338 }
339
340 return 0;
341}
342
343static const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n)
344{
345 /* FIXME: support different data types */
346 /* this fails - bind gets some vars but not others
347 return (const char*)row->res->vars[n].data;
348 */
349 DBPROCESS* proc = row->res->proc;
350 BYTE *ptr = dbdata(proc, n+1);
351 int t = dbcoltype(proc, n+1);
352 int l = dbcollen(proc, n+1);
353 if (dbwillconvert(t, SYBCHAR)) {
354 dbconvert(proc, t, ptr, l, SYBCHAR, (BYTE *)row->buf, -1);
355 return (const char*)row->buf;
356 }
357 return (char*)ptr;
358}
359
360static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
361{
362 /* XXX this doesn't seem to exist in the API ??? */
363 return apr_psprintf(sql->pool, "Error %d", sql->err);
364}
365
366static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query)
367{
368 if (sql->trans && sql->trans->errnum) {
369 return sql->trans->errnum;
370 }
371 *nrows = 0;
372 sql->err = freetds_exec(sql->proc, query, 0, nrows);
373
374 if (sql->err != SUCCEED) {
375 if (sql->trans) {
376 sql->trans->errnum = sql->err;
377 }
378 return 1;
379 }
380 return 0;
381}
382
383static const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg,
384 apr_dbd_t *sql)
385{
386 return arg;
387}
388
389static apr_status_t freetds_regfree(void *rx)
390{
391 regfree((regex_t*)rx);
392 return APR_SUCCESS;
393}
394static int recurse_args(apr_pool_t *pool, int n, const char *query,
396{
397
398 /* we only support %s arguments for now */
399 int ret;
400 char arg[256];
402 if (regexec(&dbd_freetds_find_arg, query, 3, matches, 0) != 0) {
403 /* No more args */
404 stmt->nargs = n;
405 stmt->taint = apr_palloc(pool, n*sizeof(regex_t*));
406 stmt->sz = apr_palloc(pool, n*sizeof(int));
407 ret = 0;
408 }
409 else {
410 int i;
411 int sz = 0;
412 int len = matches[1].rm_eo - matches[1].rm_so - 2;
413 if (len > 255) {
414 return 9999;
415 }
416
417 ret = recurse_args(pool, n+1, query+matches[0].rm_eo,
418 stmt, offs+matches[0].rm_eo);
419
420 memmove(stmt->fmt + offs + matches[1].rm_so,
421 stmt->fmt + offs + matches[0].rm_eo-1,
422 strlen(stmt->fmt+offs+matches[0].rm_eo)+2);
423
424 /* compile untaint to a regex if found */
425 if (matches[1].rm_so == -1) {
426 stmt->taint[n] = NULL;
427 }
428 else {
429 strncpy(arg, query+matches[1].rm_so+1,
430 matches[1].rm_eo - matches[1].rm_so - 2);
431 arg[matches[1].rm_eo - matches[1].rm_so - 2] = '\0';
432 stmt->taint[n] = apr_palloc(pool, sizeof(regex_t));
433 if (regcomp(stmt->taint[n], arg, REG_ICASE|REG_EXTENDED) != 0) {
434 ++ret;
435 }
436 else {
439 }
440 }
441
442 /* record length if specified */
443 for (i=matches[2].rm_so; i<matches[2].rm_eo; ++i) {
444 sz = 10*sz + (query[i]-'\0');
445 }
446 }
447 return ret;
448}
449
451 const char *query, const char *label,
452 int nargs, int nvals, apr_dbd_type_e *types,
454{
456
457 if (label == NULL) {
458 label = apr_psprintf(pool, "%d", labelnum++);
459 }
460
461 if (!*statement) {
463 }
464 stmt = *statement;
465
466#if 0
467 /* count args */
468 stmt->fmt = apr_pstrdup(pool, query);
469 stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt);
470
471 /* overestimate by a byte or two to simplify */
472 len = strlen("CREATE PROC apr.")
473 + strlen(label)
474 + stmt->nargs * strlen(" @arg1 varchar(len1),")
475 + strlen(" AS begin ")
476 + strlen(stmt->fmt)
477 + strlen(" end "); /* extra byte for terminator */
478
479 pquery = apr_pcalloc(pool, len);
480 sprintf(pquery, "CREATE PROC apr.%s", label);
481 for (i=0; i<stmt->nargs; ++i) {
482 sprintf(pquery+strlen(pquery), " @arg%d varchar(%d)", i, stmt->sz[i]);
483 if (i < stmt->nargs-1) {
484 pquery[strlen(pquery)] = ',';
485 }
486 }
487 strcat(pquery, " AS BEGIN ");
488 strcat(pquery, stmt->fmt);
489 strcat(pquery, " END");
490
491 return (freetds_exec(sql->proc, pquery, 0, &i) == SUCCEED) ? 0 : 1;
492#else
493 stmt->fmt = apr_pstrdup(pool, query);
494 return recurse_args(pool, 0, query, stmt, 0);
495#endif
496
497}
498
501{
502 int dummy;
503
504 /* XXX handle recursive transactions here */
505
506 handle->err = freetds_exec(handle->proc, "BEGIN TRANSACTION", 0, &dummy);
507
508 if (dbd_freetds_is_success(handle->err)) {
509 if (!*trans) {
511 }
512 (*trans)->handle = handle;
513 handle->trans = *trans;
514 return 0;
515 }
516
517 return 1;
518}
519
521{
522 int dummy;
523 if (trans) {
524 /* rollback on error or explicit rollback request */
525 if (trans->errnum) {
526 trans->errnum = 0;
527 trans->handle->err = freetds_exec(trans->handle->proc,
528 "ROLLBACK", 0, &dummy);
529 }
530 else {
531 trans->handle->err = freetds_exec(trans->handle->proc,
532 "COMMIT", 0, &dummy);
533 }
534 trans->handle->trans = NULL;
535 }
536 return (trans->handle->err == SUCCEED) ? 0 : 1;
537}
538
539static DBPROCESS *freetds_open(apr_pool_t *pool, const char *params,
540 const char **error)
541{
542 char *server = NULL;
543 DBPROCESS *process;
545 static const char *delims = " \r\n\t;|,";
546 char *ptr;
547 char *key;
548 char *value;
549 int vlen;
550 int klen;
551 char *buf;
552 char *databaseName = NULL;
553
554 /* FIXME - this uses malloc */
555 /* FIXME - pass error message back to the caller in case of failure */
556 login = dblogin();
557 if (login == NULL) {
558 return NULL;
559 }
560 /* now set login properties */
561 for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
562 /* don't dereference memory that may not belong to us */
563 if (ptr == params) {
564 ++ptr;
565 continue;
566 }
567 for (key = ptr-1; apr_isspace(*key); --key);
568 klen = 0;
569 while (apr_isalpha(*key)) {
570 --key;
571 ++klen;
572 }
573 ++key;
574 for (value = ptr+1; apr_isspace(*value); ++value);
575
577 buf = apr_pstrndup(pool, value, vlen); /* NULL-terminated copy */
578
579 if (!strncasecmp(key, "username", klen)) {
581 }
582 else if (!strncasecmp(key, "password", klen)) {
584 }
585 else if (!strncasecmp(key, "appname", klen)) {
587 }
588 else if (!strncasecmp(key, "dbname", klen)) {
590 }
591 else if (!strncasecmp(key, "host", klen)) {
593 }
594 else if (!strncasecmp(key, "charset", klen)) {
596 }
597 else if (!strncasecmp(key, "lang", klen)) {
599 }
600 else if (!strncasecmp(key, "server", klen)) {
601 server = buf;
602 }
603 else {
604 /* unknown param */
605 }
606 ptr = value+vlen;
607 }
608
609 process = dbopen(login, server);
610
611 if (process != NULL && databaseName != NULL)
612 {
613 dbuse(process, databaseName);
614 }
615
617 if (process == NULL) {
618 return NULL;
619 }
620
621 return process;
622}
623static apr_dbd_t *dbd_freetds_open(apr_pool_t *pool, const char *params,
624 const char **error)
625{
626 apr_dbd_t *sql;
627 /* FIXME - pass error message back to the caller in case of failure */
628 DBPROCESS *process = freetds_open(pool, params, error);
629 if (process == NULL) {
630 return NULL;
631 }
632 sql = apr_pcalloc(pool, sizeof (apr_dbd_t));
633 sql->pool = pool;
634 sql->proc = process;
635 sql->params = params;
636 return sql;
637}
638
640{
641 dbclose(handle->proc);
642 return APR_SUCCESS;
643}
644
647{
648 if (dbdead(handle->proc)) {
649 /* try again */
650 dbclose(handle->proc);
651 handle->proc = freetds_open(handle->pool, handle->params, NULL);
652 if (!handle->proc || dbdead(handle->proc)) {
653 return APR_EGENERAL;
654 }
655 }
656 /* clear it, in case this is called in error handling */
657 dbcancel(handle->proc);
658 return APR_SUCCESS;
659}
660
662 const char *name)
663{
664 /* ouch, it's declared int. But we can use APR 0/nonzero */
665 return (dbuse(handle->proc, (char*)name) == SUCCEED) ? APR_SUCCESS : APR_EGENERAL;
666}
667
669{
670 return handle->proc;
671}
672
674{
675 return res->sz;
676}
677
679{
680 if (res->random) {
681 return res->ntuples;
682 }
683 else {
684 return -1;
685 }
686}
687
688static apr_status_t freetds_term(void *dummy)
689{
690 dbexit();
692 return APR_SUCCESS;
693}
695 int oserr, char *dberrstr, char *oserrstr)
696{
697 return INT_CANCEL; /* never exit */
698}
699static void dbd_freetds_init(apr_pool_t *pool)
700{
702 "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED);
703 if (rv != 0) {
704 char errmsg[256];
705 regerror(rv, &dbd_freetds_find_arg, errmsg, 256);
706 fprintf(stderr, "regcomp failed: %s\n", errmsg);
707 }
708 dbinit();
711}
712
713#ifdef COMPILE_STUBS
714/* get_name is the only one of these that is implemented */
715static const char *dbd_freetds_get_name(const apr_dbd_results_t *res, int n)
716{
717 return (const char*) dbcolname(res->proc, n+1); /* numbering starts at 1 */
718}
719
720/* These are stubs: transaction modes not implemented here */
721#define DBD_NOTIMPL APR_ENOTIMPL;
723{
724 return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT;
725}
726
728 int mode)
729{
730 if (trans) {
731 trans->mode = mode & TXN_MODE_BITS;
732 return trans->mode;
733 }
735}
738{
739 return DBD_NOTIMPL;
740}
743 const void **values)
744{
745 return DBD_NOTIMPL;
746}
747
749 apr_dbd_results_t **results,
751 int seek, va_list args)
752{
753 return DBD_NOTIMPL;
754}
756 apr_dbd_results_t **results,
758 int seek, const void **values)
759{
760 return DBD_NOTIMPL;
761}
763 apr_dbd_type_e type, void *data)
764{
765 return APR_ENOTIMPL;
766}
767#endif
768
770 "freetds",
792 /* this is only implemented to support httpd/2.2 standard usage,
793 * as in the original DBD implementation. Everything else is NOTIMPL.
794 */
795#ifdef COMPILE_STUBS
799 "",
805#endif
806};
807#endif
808
809#endif
int n
Definition ap_regex.h:278
int int const char ** match
Definition ap_regex.h:279
ap_rxplus_t * rx
Definition ap_regex.h:256
const char apr_size_t len
Definition ap_regex.h:187
char * strstr(char *s1, char *s2)
#define TXN_MODE_BITS
APR general purpose library routines.
APR memory allocation.
APR Strings library.
const unsigned char * buf
Definition util_md5.h:50
void * dummy
Definition http_vhost.h:62
void const char * arg
Definition http_vhost.h:63
#define APR_EGENERAL
Definition apr_errno.h:313
#define APR_ENOTIMPL
Definition apr_errno.h:476
apr_pool_t apr_dbd_t const char * query
Definition apr_dbd.h:396
apr_dbd_type_e
Definition apr_dbd.h:55
apr_pool_t const char apr_dbd_t const char ** error
Definition apr_dbd.h:143
struct apr_dbd_prepared_t apr_dbd_prepared_t
Definition apr_dbd.h:87
apr_pool_t apr_dbd_t const char const char * label
Definition apr_dbd.h:397
apr_pool_t apr_dbd_t apr_dbd_results_t ** res
Definition apr_dbd.h:287
struct apr_dbd_t apr_dbd_t
Definition apr_dbd.h:83
apr_dbd_t int const char * statement
Definition apr_dbd.h:272
apr_dbd_transaction_t int mode
Definition apr_dbd.h:261
struct apr_dbd_results_t apr_dbd_results_t
Definition apr_dbd.h:85
apr_pool_t const char apr_dbd_t ** handle
Definition apr_dbd.h:142
apr_dbd_t int errnum
Definition apr_dbd.h:353
apr_pool_t apr_dbd_results_t apr_dbd_row_t int rownum
Definition apr_dbd.h:321
apr_pool_t apr_dbd_t int apr_dbd_prepared_t int nargs
Definition apr_dbd.h:414
struct apr_dbd_transaction_t apr_dbd_transaction_t
Definition apr_dbd.h:84
apr_pool_t apr_dbd_results_t apr_dbd_row_t ** row
Definition apr_dbd.h:320
apr_pool_t const char * params
Definition apr_dbd.h:141
apr_dbd_t int * nrows
Definition apr_dbd.h:272
struct apr_dbd_row_t apr_dbd_row_t
Definition apr_dbd.h:86
#define APR_DBD_TRANSACTION_COMMIT
Definition apr_dbd.h:239
const char apr_hash_t ** values
apr_memcache_server_t * server
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define apr_isspace(c)
Definition apr_lib.h:225
#define apr_isalpha(c)
Definition apr_lib.h:205
const char * value
Definition apr_env.h:51
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
void * data
int type
int strncasecmp(const char *a, const char *b, size_t n)
#define memmove(a, b, c)
apr_ssize_t * klen
Definition apr_hash.h:71
apr_vformatter_buff_t const char * fmt
Definition apr_lib.h:175
apr_interval_time_t t
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
void * random
Definition apr_random.h:99
apr_proc_t * proc
apr_int32_t apr_int32_t apr_int32_t err
const char const char *const * args
apr_time_t apr_int32_t offs
Definition apr_time.h:142
static const char *const types[]
#define BYTE
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
static const char *const trans[040]
Definition sed1.c:28
char * name
#define var