]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/apr-util/dbd/apr_dbd_freetds.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / apr-util / dbd / 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 #include "apu.h"
18 #include "apu_config.h"
19
20 /* COMPILE_STUBS: compile stubs for unimplemented functions.
21  *
22  * This is required to compile in /trunk/, but can be
23  * undefined to compile a driver for httpd-2.2 and other
24  * APR-1.2 applications
25  */
26 #define COMPILE_STUBS
27
28 #if APU_HAVE_FREETDS
29
30 #include <ctype.h>
31 #include <stdlib.h>
32
33 #include "apr_strings.h"
34 #include "apr_lib.h"
35
36 #include "apr_pools.h"
37 #include "apr_dbd_internal.h"
38
39 #ifdef HAVE_FREETDS_SYBDB_H
40 #include <freetds/sybdb.h>
41 #endif
42 #ifdef HAVE_SYBDB_H
43 #include <sybdb.h>
44 #endif
45
46 #include <stdio.h>
47 #include <sys/types.h>
48 #include <regex.h>
49
50 /* This probably needs to change for different applications */
51 #define MAX_COL_LEN 256
52
53 typedef struct freetds_cell_t {
54     int type;
55     DBINT len;
56     BYTE *data;
57 } freetds_cell_t;
58
59 struct apr_dbd_transaction_t {
60     int mode;
61     int errnum;
62     apr_dbd_t *handle;
63 };
64
65 struct apr_dbd_t {
66     DBPROCESS *proc;
67     apr_dbd_transaction_t *trans;
68     apr_pool_t *pool;
69     const char *params;
70     RETCODE err;
71 };
72
73 struct apr_dbd_results_t {
74     int random;
75     size_t ntuples;
76     size_t sz;
77     apr_pool_t *pool;
78     DBPROCESS *proc;
79 };
80
81 struct apr_dbd_row_t {
82     apr_dbd_results_t *res;
83     BYTE buf[MAX_COL_LEN];
84 };
85
86 struct apr_dbd_prepared_t {
87     int nargs;
88     regex_t **taint;
89     int *sz;
90     char *fmt;
91 };
92
93 #define dbd_freetds_is_success(x) (x == SUCCEED)
94
95 static int labelnum = 0; /* FIXME */
96 static regex_t dbd_freetds_find_arg;
97
98 /* execute a query that doesn't return a result set, mop up,
99  * and return and APR-flavoured status
100  */
101 static RETCODE freetds_exec(DBPROCESS *proc, const char *query,
102                             int want_results, int *nrows)
103 {
104     /* TBD */
105     RETCODE rv = dbcmd(proc, query);
106     if (rv != SUCCEED) {
107         return rv;
108     }
109     rv = dbsqlexec(proc);
110     if (rv != SUCCEED) {
111         return rv;
112     }
113     if (!want_results) {
114         while (dbresults(proc) != NO_MORE_RESULTS) {
115             ++*nrows;
116         }
117     }
118     return SUCCEED;
119 }
120 static apr_status_t clear_result(void *data)
121 {
122     /* clear cursor */
123     return (dbcanquery((DBPROCESS*)data) == SUCCEED)
124             ? APR_SUCCESS
125             : APR_EGENERAL;
126 }
127
128 static int dbd_freetds_select(apr_pool_t *pool, apr_dbd_t *sql,
129                               apr_dbd_results_t **results,
130                               const char *query, int seek)
131 {
132     apr_dbd_results_t *res;
133     if (sql->trans && (sql->trans->errnum != SUCCEED)) {
134         return 1;
135     }
136     /* the core of this is
137      * dbcmd(proc, query);
138      * dbsqlexec(proc);
139      * while (dbnextrow(dbproc) != NO_MORE_ROWS) {
140      *     do things
141      * }
142      *
143      * Ignore seek
144      */
145
146     sql->err = freetds_exec(sql->proc, query, 1, NULL);
147     if (!dbd_freetds_is_success(sql->err)) {
148         if (sql->trans) {
149             sql->trans->errnum = sql->err;
150         }
151         return 1;
152     }
153
154     sql->err = dbresults(sql->proc);
155     if (sql->err != SUCCEED) {
156         if (sql->trans) {
157             sql->trans->errnum = sql->err;
158         }
159         return 1;
160     }
161
162     if (!*results) {
163         *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
164     }
165     res = *results;
166     res->proc = sql->proc;
167     res->random = seek;
168     res->pool = pool;
169     res->ntuples = dblastrow(sql->proc);
170     res->sz = dbnumcols(sql->proc);
171     apr_pool_cleanup_register(pool, sql->proc, clear_result,
172                               apr_pool_cleanup_null);
173
174 #if 0
175     /* Now we have a result set.  We need to bind to its vars */
176     res->vars = apr_palloc(pool, res->sz * sizeof(freetds_cell_t*));
177     for (i=1; i <= res->sz; ++i) {
178         freetds_cell_t *cell = &res->vars[i-1];
179         cell->type = dbcoltype(sql->proc, i);
180         cell->len = dbcollen(sql->proc, i);
181         cell->data = apr_palloc(pool, cell->len);
182         sql->err = dbbind(sql->proc, i, /*cell->type */ STRINGBIND, cell->len, cell->data);
183         if (sql->err != SUCCEED) {
184             fprintf(stderr, "dbbind error: %d, %d, %d", i, cell->type, cell->len);
185         }
186         if ((sql->err != SUCCEED) && (sql->trans != NULL)) {
187             sql->trans->errnum = sql->err;
188         }
189     }
190 #endif
191     return (sql->err == SUCCEED) ? 0 : 1;
192 }
193 static const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val)
194 {
195     regmatch_t match[1];
196     if (rx == NULL) {
197         /* no untaint expression */
198         return val;
199     }
200     if (regexec(rx, val, 1, match, 0) == 0) {
201         return apr_pstrndup(pool, val+match[0].rm_so,
202                             match[0].rm_eo - match[0].rm_so);
203     }
204     return "";
205 }
206 static const char *dbd_statement(apr_pool_t *pool,
207                                  apr_dbd_prepared_t *stmt,
208                                  int nargs, const char **args)
209 {
210     int i;
211     int len;
212     const char *var;
213     char *ret;
214     const char *p_in;
215     char *p_out;
216     char *q;
217    
218     /* compute upper bound on length (since untaint shrinks) */
219     len  = strlen(stmt->fmt) +1;
220     for (i=0; i<nargs; ++i) {
221         len += strlen(args[i]) - 2;
222     }
223     i = 0;
224     p_in = stmt->fmt;
225     p_out = ret = apr_palloc(pool, len);
226     /* FIXME silly bug - this'll catch %%s */
227     while (q = strstr(p_in, "%s"), q != NULL) {
228         len = q-p_in;
229         strncpy(p_out, p_in, len);
230         p_in += len;
231         p_out += len;
232         var = dbd_untaint(pool, stmt->taint[i], args[i]);
233         len = strlen(var);
234         strncpy(p_out, var, len);
235         p_in += 2;
236         p_out += len;
237         ++i;
238     }
239     strcpy(p_out, p_in);
240     return ret;
241 }
242 static int dbd_freetds_pselect(apr_pool_t *pool, apr_dbd_t *sql,
243                                apr_dbd_results_t **results,
244                                apr_dbd_prepared_t *statement,
245                                int seek, const char **values)
246 {
247     const char *query = dbd_statement(pool, statement,
248                                       statement->nargs, values);
249     return dbd_freetds_select(pool, sql, results, query, seek);
250 }
251 static int dbd_freetds_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
252                                 apr_dbd_results_t **results,
253                                 apr_dbd_prepared_t *statement,
254                                 int seek, va_list args)
255 {
256     const char **values;
257     int i;
258
259     if (sql->trans && sql->trans->errnum) {
260         return sql->trans->errnum;
261     }
262
263     values = apr_palloc(pool, sizeof(*values) * statement->nargs);
264
265     for (i = 0; i < statement->nargs; i++) {
266         values[i] = va_arg(args, const char*);
267     }
268
269     return dbd_freetds_pselect(pool, sql, results, statement, seek, values);
270 }
271 static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query);
272 static int dbd_freetds_pquery(apr_pool_t *pool, apr_dbd_t *sql,
273                               int *nrows, apr_dbd_prepared_t *statement,
274                               const char **values)
275 {
276     const char *query = dbd_statement(pool, statement,
277                                       statement->nargs, values);
278     return dbd_freetds_query(sql, nrows, query);
279 }
280 static int dbd_freetds_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
281                                apr_dbd_prepared_t *statement, va_list args)
282 {
283     const char **values;
284     int i;
285
286     if (sql->trans && sql->trans->errnum) {
287         return sql->trans->errnum;
288     }
289
290     values = apr_palloc(pool, sizeof(*values) * statement->nargs);
291
292     for (i = 0; i < statement->nargs; i++) {
293         values[i] = va_arg(args, const char*);
294     }
295     return dbd_freetds_pquery(pool, sql, nrows, statement, values);
296 }
297
298 static int dbd_freetds_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
299                                apr_dbd_row_t **rowp, int rownum)
300 {
301     RETCODE rv = 0;
302     apr_dbd_row_t *row = *rowp;
303     int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
304
305     if (row == NULL) {
306         row = apr_palloc(pool, sizeof(apr_dbd_row_t));
307         *rowp = row;
308         row->res = res;
309     }
310     /*
311     else {
312         if ( sequential ) {
313             ++row->n;
314         }
315         else {
316             row->n = rownum;
317         }
318     }
319     */
320     if (sequential) {
321         rv = dbnextrow(res->proc);
322     }
323     else {
324         rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
325     }
326     switch (rv) {
327     case SUCCEED: return 0;
328     case REG_ROW: return 0;
329     case NO_MORE_ROWS:
330         apr_pool_cleanup_run(res->pool, res->proc, clear_result);
331         *rowp = NULL;
332         return -1;
333     case FAIL: return 1;
334     case BUF_FULL: return 2; /* FIXME */
335     default: return 3;
336     }
337
338     return 0;
339 }
340
341 static const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n)
342 {
343     /* FIXME: support different data types */
344     /* this fails - bind gets some vars but not others
345     return (const char*)row->res->vars[n].data;
346      */
347     DBPROCESS* proc = row->res->proc;
348     BYTE *ptr = dbdata(proc, n+1);
349     int t = dbcoltype(proc, n+1);
350     int l = dbcollen(proc, n+1);
351     if (dbwillconvert(t, SYBCHAR)) {
352       dbconvert(proc, t, ptr, l, SYBCHAR, (BYTE *)row->buf, -1);
353       return (const char*)row->buf;
354     }
355     return (char*)ptr;
356 }
357
358 static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
359 {
360     /* XXX this doesn't seem to exist in the API ??? */
361     return apr_psprintf(sql->pool, "Error %d", sql->err);
362 }
363
364 static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query)
365 {
366     if (sql->trans && sql->trans->errnum) {
367         return sql->trans->errnum;
368     }
369     *nrows = 0;
370     sql->err = freetds_exec(sql->proc, query, 0, nrows);
371
372     if (sql->err != SUCCEED) {
373         if (sql->trans) {
374             sql->trans->errnum = sql->err;
375         }
376         return 1;
377     }
378     return 0;
379 }
380
381 static const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg,
382                                       apr_dbd_t *sql)
383 {
384     return arg;
385 }
386
387 static apr_status_t freetds_regfree(void *rx)
388 {
389     regfree((regex_t*)rx);
390     return APR_SUCCESS;
391 }
392 static int recurse_args(apr_pool_t *pool, int n, const char *query,
393                         apr_dbd_prepared_t *stmt, int offs)
394 {
395
396     /* we only support %s arguments for now */
397     int ret;
398     char arg[256];
399     regmatch_t matches[3];
400     if (regexec(&dbd_freetds_find_arg, query, 3, matches, 0) != 0) {
401         /* No more args */
402         stmt->nargs = n;
403         stmt->taint = apr_palloc(pool, n*sizeof(regex_t*));
404         stmt->sz = apr_palloc(pool, n*sizeof(int));
405         ret = 0;
406     }
407     else {
408         int i;
409         int sz = 0;
410         int len = matches[1].rm_eo - matches[1].rm_so - 2;
411         if (len > 255) {
412             return 9999;
413         }
414
415         ret = recurse_args(pool, n+1, query+matches[0].rm_eo,
416                            stmt, offs+matches[0].rm_eo);
417
418         memmove(stmt->fmt + offs + matches[1].rm_so,
419                 stmt->fmt + offs + matches[0].rm_eo-1,
420                 strlen(stmt->fmt+offs+matches[0].rm_eo)+2);
421
422         /* compile untaint to a regex if found */
423         if (matches[1].rm_so == -1) {
424             stmt->taint[n] = NULL;
425         }
426         else {
427             strncpy(arg, query+matches[1].rm_so+1,
428                     matches[1].rm_eo - matches[1].rm_so - 2);
429             arg[matches[1].rm_eo - matches[1].rm_so - 2] = '\0';
430             stmt->taint[n] = apr_palloc(pool, sizeof(regex_t));
431             if (regcomp(stmt->taint[n], arg, REG_ICASE|REG_EXTENDED) != 0) {
432                 ++ret;
433             }
434             else {
435                 apr_pool_cleanup_register(pool, stmt->taint[n], freetds_regfree,
436                                           apr_pool_cleanup_null);
437             }
438         }
439
440         /* record length if specified */
441         for (i=matches[2].rm_so; i<matches[2].rm_eo; ++i) {
442             sz = 10*sz + (query[i]-'\0');
443         }
444     }
445     return ret;
446 }
447
448 static int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql,
449                              const char *query, const char *label,
450                              int nargs, int nvals, apr_dbd_type_e *types,
451                              apr_dbd_prepared_t **statement)
452 {
453     apr_dbd_prepared_t *stmt;
454
455     if (label == NULL) {
456         label = apr_psprintf(pool, "%d", labelnum++);
457     }
458
459     if (!*statement) {
460         *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
461     }
462     stmt = *statement;
463
464 #if 0
465     /* count args */
466     stmt->fmt = apr_pstrdup(pool, query);
467     stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt);
468
469     /* overestimate by a byte or two to simplify */
470     len = strlen("CREATE PROC apr.")
471             + strlen(label)
472             + stmt->nargs * strlen(" @arg1 varchar(len1),")
473             + strlen(" AS begin ")
474             + strlen(stmt->fmt)
475             + strlen(" end "); /* extra byte for terminator */
476
477     pquery = apr_pcalloc(pool, len);
478     sprintf(pquery, "CREATE PROC apr.%s", label);
479     for (i=0; i<stmt->nargs; ++i) {
480         sprintf(pquery+strlen(pquery), " @arg%d varchar(%d)", i, stmt->sz[i]);
481         if (i < stmt->nargs-1) {
482             pquery[strlen(pquery)] = ',';
483         }
484     }
485     strcat(pquery, " AS BEGIN ");
486     strcat(pquery, stmt->fmt);
487     strcat(pquery, " END");
488
489     return (freetds_exec(sql->proc, pquery, 0, &i) == SUCCEED) ? 0 : 1;
490 #else
491     stmt->fmt = apr_pstrdup(pool, query);
492     return recurse_args(pool, 0, query, stmt, 0);
493 #endif
494
495 }
496
497 static int dbd_freetds_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
498                                          apr_dbd_transaction_t **trans)
499 {
500     int dummy;
501
502     /* XXX handle recursive transactions here */
503
504     handle->err = freetds_exec(handle->proc, "BEGIN TRANSACTION", 0, &dummy);
505
506     if (dbd_freetds_is_success(handle->err)) {
507         if (!*trans) {
508             *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
509         }
510         (*trans)->handle = handle;
511         handle->trans = *trans;
512         return 0;
513     }
514
515     return 1;
516 }
517
518 static int dbd_freetds_end_transaction(apr_dbd_transaction_t *trans)
519 {
520     int dummy;
521     if (trans) {
522         /* rollback on error or explicit rollback request */
523         if (trans->errnum) {
524             trans->errnum = 0;
525             trans->handle->err = freetds_exec(trans->handle->proc,
526                                               "ROLLBACK", 0, &dummy);
527         }
528         else {
529             trans->handle->err = freetds_exec(trans->handle->proc,
530                                               "COMMIT", 0, &dummy);
531         }
532         trans->handle->trans = NULL;
533     }
534     return (trans->handle->err == SUCCEED) ? 0 : 1;
535 }
536
537 static DBPROCESS *freetds_open(apr_pool_t *pool, const char *params,
538                                const char **error)
539 {
540     char *server = NULL;
541     DBPROCESS *process;
542     LOGINREC *login;
543     static const char *delims = " \r\n\t;|,";
544     char *ptr;
545     char *key;
546     char *value;
547     int vlen;
548     int klen;
549     char *buf;
550     char *databaseName = NULL;
551
552     /* FIXME - this uses malloc */
553     /* FIXME - pass error message back to the caller in case of failure */
554     login = dblogin();
555     if (login == NULL) {
556         return NULL;
557     }
558     /* now set login properties */
559     for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
560         /* don't dereference memory that may not belong to us */
561         if (ptr == params) {
562             ++ptr;
563             continue;
564         }
565         for (key = ptr-1; apr_isspace(*key); --key);
566         klen = 0;
567         while (apr_isalpha(*key)) {
568             --key;
569             ++klen;
570         }
571         ++key;
572         for (value = ptr+1; apr_isspace(*value); ++value);
573
574         vlen = strcspn(value, delims);
575         buf = apr_pstrndup(pool, value, vlen);        /* NULL-terminated copy */
576
577         if (!strncasecmp(key, "username", klen)) {
578             DBSETLUSER(login, buf);
579         }
580         else if (!strncasecmp(key, "password", klen)) {
581             DBSETLPWD(login, buf);
582         }
583         else if (!strncasecmp(key, "appname", klen)) {
584             DBSETLAPP(login, buf);
585         }
586         else if (!strncasecmp(key, "dbname", klen)) {
587             databaseName = buf;
588         }
589         else if (!strncasecmp(key, "host", klen)) {
590             DBSETLHOST(login, buf);
591         }
592         else if (!strncasecmp(key, "charset", klen)) {
593             DBSETLCHARSET(login, buf);
594         }
595         else if (!strncasecmp(key, "lang", klen)) {
596             DBSETLNATLANG(login, buf);
597         }
598         else if (!strncasecmp(key, "server", klen)) {
599             server = buf;
600         }
601         else {
602             /* unknown param */
603         }
604         ptr = value+vlen;
605     }
606
607     process = dbopen(login, server);
608
609     if (process != NULL && databaseName != NULL)
610     {
611         dbuse(process, databaseName);
612     }
613  
614     dbloginfree(login);
615     if (process == NULL) {
616         return NULL;
617     }
618
619     return process;
620 }
621 static apr_dbd_t *dbd_freetds_open(apr_pool_t *pool, const char *params,
622                                    const char **error)
623 {
624     apr_dbd_t *sql;
625     /* FIXME - pass error message back to the caller in case of failure */
626     DBPROCESS *process = freetds_open(pool, params, error);
627     if (process == NULL) {
628         return NULL;
629     }
630     sql = apr_pcalloc(pool, sizeof (apr_dbd_t));
631     sql->pool = pool;
632     sql->proc = process;
633     sql->params = params;
634     return sql;
635 }
636
637 static apr_status_t dbd_freetds_close(apr_dbd_t *handle)
638 {
639     dbclose(handle->proc);
640     return APR_SUCCESS;
641 }
642
643 static apr_status_t dbd_freetds_check_conn(apr_pool_t *pool,
644                                            apr_dbd_t *handle)
645 {
646     if (dbdead(handle->proc)) {
647         /* try again */
648         dbclose(handle->proc);
649         handle->proc = freetds_open(handle->pool, handle->params, NULL);
650         if (!handle->proc || dbdead(handle->proc)) {
651             return APR_EGENERAL;
652         }
653     }
654     /* clear it, in case this is called in error handling */
655     dbcancel(handle->proc);
656     return APR_SUCCESS;
657 }
658
659 static int dbd_freetds_select_db(apr_pool_t *pool, apr_dbd_t *handle,
660                                const char *name)
661 {
662     /* ouch, it's declared int.  But we can use APR 0/nonzero */
663     return (dbuse(handle->proc, (char*)name) == SUCCEED) ? APR_SUCCESS : APR_EGENERAL;
664 }
665
666 static void *dbd_freetds_native(apr_dbd_t *handle)
667 {
668     return handle->proc;
669 }
670
671 static int dbd_freetds_num_cols(apr_dbd_results_t* res)
672 {
673     return res->sz;
674 }
675
676 static int dbd_freetds_num_tuples(apr_dbd_results_t* res)
677 {
678     if (res->random) {
679         return res->ntuples;
680     }
681     else {
682         return -1;
683     }
684 }
685
686 static apr_status_t freetds_term(void *dummy)
687 {
688     dbexit();
689     regfree(&dbd_freetds_find_arg);
690     return APR_SUCCESS;
691 }
692 static int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr,
693                                int oserr, char *dberrstr, char *oserrstr)
694 {
695     return INT_CANCEL; /* never exit */
696 }
697 static void dbd_freetds_init(apr_pool_t *pool)
698 {
699     int rv = regcomp(&dbd_freetds_find_arg,
700                      "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED);
701     if (rv != 0) {
702         char errmsg[256];
703         regerror(rv, &dbd_freetds_find_arg, errmsg, 256);
704         fprintf(stderr, "regcomp failed: %s\n", errmsg);
705     }
706     dbinit();
707     dberrhandle(freetds_err_handler);
708     apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
709 }
710
711 #ifdef COMPILE_STUBS
712 /* get_name is the only one of these that is implemented */
713 static const char *dbd_freetds_get_name(const apr_dbd_results_t *res, int n)
714 {
715     return (const char*) dbcolname(res->proc, n+1); /* numbering starts at 1 */
716 }
717
718 /* These are stubs: transaction modes not implemented here */
719 #define DBD_NOTIMPL APR_ENOTIMPL;
720 static int dbd_freetds_transaction_mode_get(apr_dbd_transaction_t *trans)
721 {
722     return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT;
723 }
724
725 static int dbd_freetds_transaction_mode_set(apr_dbd_transaction_t *trans,
726                                             int mode)
727 {
728     if (trans) {
729         trans->mode = mode & TXN_MODE_BITS;
730         return trans->mode;
731     }
732     return APR_DBD_TRANSACTION_COMMIT;
733 }
734 static int dbd_freetds_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
735                                 apr_dbd_prepared_t *statement, va_list args)
736 {
737     return DBD_NOTIMPL;
738 }
739 static int dbd_freetds_pbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
740                                apr_dbd_prepared_t * statement,
741                                const void **values)
742 {
743     return DBD_NOTIMPL;
744 }
745
746 static int dbd_freetds_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
747                                  apr_dbd_results_t **results,
748                                  apr_dbd_prepared_t *statement,
749                                  int seek, va_list args)
750 {
751     return DBD_NOTIMPL;
752 }
753 static int dbd_freetds_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
754                                 apr_dbd_results_t **results,
755                                 apr_dbd_prepared_t *statement,
756                                 int seek, const void **values)
757 {
758     return DBD_NOTIMPL;
759 }
760 static apr_status_t dbd_freetds_datum_get(const apr_dbd_row_t *row, int n,
761                                           apr_dbd_type_e type, void *data)
762 {
763     return APR_ENOTIMPL;
764 }
765 #endif
766
767 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = {
768     "freetds",
769     dbd_freetds_init,
770     dbd_freetds_native,
771     dbd_freetds_open,
772     dbd_freetds_check_conn,
773     dbd_freetds_close,
774     dbd_freetds_select_db,
775     dbd_freetds_start_transaction,
776     dbd_freetds_end_transaction,
777     dbd_freetds_query,
778     dbd_freetds_select,
779     dbd_freetds_num_cols,
780     dbd_freetds_num_tuples,
781     dbd_freetds_get_row,
782     dbd_freetds_get_entry,
783     dbd_freetds_error,
784     dbd_freetds_escape,
785     dbd_freetds_prepare,
786     dbd_freetds_pvquery,
787     dbd_freetds_pvselect,
788     dbd_freetds_pquery,
789     dbd_freetds_pselect,
790     /* this is only implemented to support httpd/2.2 standard usage,
791      * as in the original DBD implementation.  Everything else is NOTIMPL.
792      */
793 #ifdef COMPILE_STUBS
794     dbd_freetds_get_name,
795     dbd_freetds_transaction_mode_get,
796     dbd_freetds_transaction_mode_set,
797     "",
798     dbd_freetds_pvbquery,
799     dbd_freetds_pvbselect,
800     dbd_freetds_pbquery,
801     dbd_freetds_pbselect,
802     dbd_freetds_datum_get
803 #endif
804 };
805 #endif