]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/apr-util/dbd/unsupported/apr_dbd_freetds.c
Update Subversion and dependencies to 1.14.0 LTS.
[FreeBSD/FreeBSD.git] / contrib / apr-util / dbd / unsupported / apr_dbd_freetds.c
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
55 typedef struct freetds_cell_t {
56     int type;
57     DBINT len;
58     BYTE *data;
59 } freetds_cell_t;
60
61 struct apr_dbd_transaction_t {
62     int mode;
63     int errnum;
64     apr_dbd_t *handle;
65 };
66
67 struct apr_dbd_t {
68     DBPROCESS *proc;
69     apr_dbd_transaction_t *trans;
70     apr_pool_t *pool;
71     const char *params;
72     RETCODE err;
73 };
74
75 struct apr_dbd_results_t {
76     int random;
77     size_t ntuples;
78     size_t sz;
79     apr_pool_t *pool;
80     DBPROCESS *proc;
81 };
82
83 struct apr_dbd_row_t {
84     apr_dbd_results_t *res;
85     BYTE buf[MAX_COL_LEN];
86 };
87
88 struct 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
97 static int labelnum = 0; /* FIXME */
98 static regex_t dbd_freetds_find_arg;
99
100 /* execute a query that doesn't return a result set, mop up,
101  * and return and APR-flavoured status
102  */
103 static 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 }
122 static apr_status_t clear_result(void *data)
123 {
124     /* clear cursor */
125     return (dbcanquery((DBPROCESS*)data) == SUCCEED)
126             ? APR_SUCCESS
127             : APR_EGENERAL;
128 }
129
130 static int dbd_freetds_select(apr_pool_t *pool, apr_dbd_t *sql,
131                               apr_dbd_results_t **results,
132                               const char *query, int seek)
133 {
134     apr_dbd_results_t *res;
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);
173     apr_pool_cleanup_register(pool, sql->proc, clear_result,
174                               apr_pool_cleanup_null);
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 }
195 static 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 }
208 static const char *dbd_statement(apr_pool_t *pool,
209                                  apr_dbd_prepared_t *stmt,
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;
227     p_out = ret = apr_palloc(pool, len);
228     /* FIXME silly bug - this'll catch %%s */
229     while (q = strstr(p_in, "%s"), q != NULL) {
230         len = q-p_in;
231         strncpy(p_out, p_in, len);
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 }
244 static int dbd_freetds_pselect(apr_pool_t *pool, apr_dbd_t *sql,
245                                apr_dbd_results_t **results,
246                                apr_dbd_prepared_t *statement,
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 }
253 static int dbd_freetds_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
254                                 apr_dbd_results_t **results,
255                                 apr_dbd_prepared_t *statement,
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 }
273 static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query);
274 static int dbd_freetds_pquery(apr_pool_t *pool, apr_dbd_t *sql,
275                               int *nrows, apr_dbd_prepared_t *statement,
276                               const char **values)
277 {
278     const char *query = dbd_statement(pool, statement,
279                                       statement->nargs, values);
280     return dbd_freetds_query(sql, nrows, query);
281 }
282 static int dbd_freetds_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
283                                apr_dbd_prepared_t *statement, va_list args)
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     }
297     return dbd_freetds_pquery(pool, sql, nrows, statement, values);
298 }
299
300 static int dbd_freetds_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
301                                apr_dbd_row_t **rowp, int rownum)
302 {
303     RETCODE rv = 0;
304     apr_dbd_row_t *row = *rowp;
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:
332         apr_pool_cleanup_run(res->pool, res->proc, clear_result);
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
343 static 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
360 static 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
366 static 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
383 static const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg,
384                                       apr_dbd_t *sql)
385 {
386     return arg;
387 }
388
389 static apr_status_t freetds_regfree(void *rx)
390 {
391     regfree((regex_t*)rx);
392     return APR_SUCCESS;
393 }
394 static int recurse_args(apr_pool_t *pool, int n, const char *query,
395                         apr_dbd_prepared_t *stmt, int offs)
396 {
397
398     /* we only support %s arguments for now */
399     int ret;
400     char arg[256];
401     regmatch_t matches[3];
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 {
437                 apr_pool_cleanup_register(pool, stmt->taint[n], freetds_regfree,
438                                           apr_pool_cleanup_null);
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
450 static int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql,
451                              const char *query, const char *label,
452                              int nargs, int nvals, apr_dbd_type_e *types,
453                              apr_dbd_prepared_t **statement)
454 {
455     apr_dbd_prepared_t *stmt;
456
457     if (label == NULL) {
458         label = apr_psprintf(pool, "%d", labelnum++);
459     }
460
461     if (!*statement) {
462         *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
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
499 static int dbd_freetds_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
500                                          apr_dbd_transaction_t **trans)
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) {
510             *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
511         }
512         (*trans)->handle = handle;
513         handle->trans = *trans;
514         return 0;
515     }
516
517     return 1;
518 }
519
520 static int dbd_freetds_end_transaction(apr_dbd_transaction_t *trans)
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
539 static DBPROCESS *freetds_open(apr_pool_t *pool, const char *params,
540                                const char **error)
541 {
542     char *server = NULL;
543     DBPROCESS *process;
544     LOGINREC *login;
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
576         vlen = strcspn(value, delims);
577         buf = apr_pstrndup(pool, value, vlen);        /* NULL-terminated copy */
578
579         if (!strncasecmp(key, "username", klen)) {
580             DBSETLUSER(login, buf);
581         }
582         else if (!strncasecmp(key, "password", klen)) {
583             DBSETLPWD(login, buf);
584         }
585         else if (!strncasecmp(key, "appname", klen)) {
586             DBSETLAPP(login, buf);
587         }
588         else if (!strncasecmp(key, "dbname", klen)) {
589             databaseName = buf;
590         }
591         else if (!strncasecmp(key, "host", klen)) {
592             DBSETLHOST(login, buf);
593         }
594         else if (!strncasecmp(key, "charset", klen)) {
595             DBSETLCHARSET(login, buf);
596         }
597         else if (!strncasecmp(key, "lang", klen)) {
598             DBSETLNATLANG(login, buf);
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  
616     dbloginfree(login);
617     if (process == NULL) {
618         return NULL;
619     }
620
621     return process;
622 }
623 static 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
639 static apr_status_t dbd_freetds_close(apr_dbd_t *handle)
640 {
641     dbclose(handle->proc);
642     return APR_SUCCESS;
643 }
644
645 static apr_status_t dbd_freetds_check_conn(apr_pool_t *pool,
646                                            apr_dbd_t *handle)
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
661 static int dbd_freetds_select_db(apr_pool_t *pool, apr_dbd_t *handle,
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
668 static void *dbd_freetds_native(apr_dbd_t *handle)
669 {
670     return handle->proc;
671 }
672
673 static int dbd_freetds_num_cols(apr_dbd_results_t* res)
674 {
675     return res->sz;
676 }
677
678 static int dbd_freetds_num_tuples(apr_dbd_results_t* res)
679 {
680     if (res->random) {
681         return res->ntuples;
682     }
683     else {
684         return -1;
685     }
686 }
687
688 static apr_status_t freetds_term(void *dummy)
689 {
690     dbexit();
691     regfree(&dbd_freetds_find_arg);
692     return APR_SUCCESS;
693 }
694 static int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr,
695                                int oserr, char *dberrstr, char *oserrstr)
696 {
697     return INT_CANCEL; /* never exit */
698 }
699 static void dbd_freetds_init(apr_pool_t *pool)
700 {
701     int rv = regcomp(&dbd_freetds_find_arg,
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();
709     dberrhandle(freetds_err_handler);
710     apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
711 }
712
713 #ifdef COMPILE_STUBS
714 /* get_name is the only one of these that is implemented */
715 static 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;
722 static int dbd_freetds_transaction_mode_get(apr_dbd_transaction_t *trans)
723 {
724     return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT;
725 }
726
727 static int dbd_freetds_transaction_mode_set(apr_dbd_transaction_t *trans,
728                                             int mode)
729 {
730     if (trans) {
731         trans->mode = mode & TXN_MODE_BITS;
732         return trans->mode;
733     }
734     return APR_DBD_TRANSACTION_COMMIT;
735 }
736 static int dbd_freetds_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
737                                 apr_dbd_prepared_t *statement, va_list args)
738 {
739     return DBD_NOTIMPL;
740 }
741 static int dbd_freetds_pbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
742                                apr_dbd_prepared_t * statement,
743                                const void **values)
744 {
745     return DBD_NOTIMPL;
746 }
747
748 static int dbd_freetds_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
749                                  apr_dbd_results_t **results,
750                                  apr_dbd_prepared_t *statement,
751                                  int seek, va_list args)
752 {
753     return DBD_NOTIMPL;
754 }
755 static int dbd_freetds_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
756                                 apr_dbd_results_t **results,
757                                 apr_dbd_prepared_t *statement,
758                                 int seek, const void **values)
759 {
760     return DBD_NOTIMPL;
761 }
762 static apr_status_t dbd_freetds_datum_get(const apr_dbd_row_t *row, int n,
763                                           apr_dbd_type_e type, void *data)
764 {
765     return APR_ENOTIMPL;
766 }
767 #endif
768
769 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = {
770     "freetds",
771     dbd_freetds_init,
772     dbd_freetds_native,
773     dbd_freetds_open,
774     dbd_freetds_check_conn,
775     dbd_freetds_close,
776     dbd_freetds_select_db,
777     dbd_freetds_start_transaction,
778     dbd_freetds_end_transaction,
779     dbd_freetds_query,
780     dbd_freetds_select,
781     dbd_freetds_num_cols,
782     dbd_freetds_num_tuples,
783     dbd_freetds_get_row,
784     dbd_freetds_get_entry,
785     dbd_freetds_error,
786     dbd_freetds_escape,
787     dbd_freetds_prepare,
788     dbd_freetds_pvquery,
789     dbd_freetds_pvselect,
790     dbd_freetds_pquery,
791     dbd_freetds_pselect,
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
796     dbd_freetds_get_name,
797     dbd_freetds_transaction_mode_get,
798     dbd_freetds_transaction_mode_set,
799     "",
800     dbd_freetds_pvbquery,
801     dbd_freetds_pvbselect,
802     dbd_freetds_pbquery,
803     dbd_freetds_pbselect,
804     dbd_freetds_datum_get
805 #endif
806 };
807 #endif
808
809 #endif