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 */
freetds_exec(DBPROCESS * proc,const char * query,int want_results,int * nrows)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 }
clear_result(void * data)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
dbd_freetds_select(apr_pool_t * pool,apr_dbd_t * sql,apr_dbd_results_t ** results,const char * query,int seek)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 }
dbd_untaint(apr_pool_t * pool,regex_t * rx,const char * val)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 }
dbd_statement(apr_pool_t * pool,apr_dbd_prepared_t * stmt,int nargs,const char ** args)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 }
dbd_freetds_pselect(apr_pool_t * pool,apr_dbd_t * sql,apr_dbd_results_t ** results,apr_dbd_prepared_t * statement,int seek,const char ** values)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 }
dbd_freetds_pvselect(apr_pool_t * pool,apr_dbd_t * sql,apr_dbd_results_t ** results,apr_dbd_prepared_t * statement,int seek,va_list args)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);
dbd_freetds_pquery(apr_pool_t * pool,apr_dbd_t * sql,int * nrows,apr_dbd_prepared_t * statement,const char ** values)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 }
dbd_freetds_pvquery(apr_pool_t * pool,apr_dbd_t * sql,int * nrows,apr_dbd_prepared_t * statement,va_list args)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
dbd_freetds_get_row(apr_pool_t * pool,apr_dbd_results_t * res,apr_dbd_row_t ** rowp,int rownum)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
dbd_freetds_get_entry(const apr_dbd_row_t * row,int n)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
dbd_freetds_error(apr_dbd_t * sql,int n)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
dbd_freetds_query(apr_dbd_t * sql,int * nrows,const char * query)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
dbd_freetds_escape(apr_pool_t * pool,const char * arg,apr_dbd_t * sql)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
freetds_regfree(void * rx)387 static apr_status_t freetds_regfree(void *rx)
388 {
389 regfree((regex_t*)rx);
390 return APR_SUCCESS;
391 }
recurse_args(apr_pool_t * pool,int n,const char * query,apr_dbd_prepared_t * stmt,int offs)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
dbd_freetds_prepare(apr_pool_t * pool,apr_dbd_t * sql,const char * query,const char * label,int nargs,int nvals,apr_dbd_type_e * types,apr_dbd_prepared_t ** statement)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
dbd_freetds_start_transaction(apr_pool_t * pool,apr_dbd_t * handle,apr_dbd_transaction_t ** trans)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
dbd_freetds_end_transaction(apr_dbd_transaction_t * trans)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
freetds_open(apr_pool_t * pool,const char * params,const char ** error)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 }
dbd_freetds_open(apr_pool_t * pool,const char * params,const char ** error)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
dbd_freetds_close(apr_dbd_t * handle)637 static apr_status_t dbd_freetds_close(apr_dbd_t *handle)
638 {
639 dbclose(handle->proc);
640 return APR_SUCCESS;
641 }
642
dbd_freetds_check_conn(apr_pool_t * pool,apr_dbd_t * handle)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
dbd_freetds_select_db(apr_pool_t * pool,apr_dbd_t * handle,const char * name)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
dbd_freetds_native(apr_dbd_t * handle)666 static void *dbd_freetds_native(apr_dbd_t *handle)
667 {
668 return handle->proc;
669 }
670
dbd_freetds_num_cols(apr_dbd_results_t * res)671 static int dbd_freetds_num_cols(apr_dbd_results_t* res)
672 {
673 return res->sz;
674 }
675
dbd_freetds_num_tuples(apr_dbd_results_t * res)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
freetds_term(void * dummy)686 static apr_status_t freetds_term(void *dummy)
687 {
688 dbexit();
689 regfree(&dbd_freetds_find_arg);
690 return APR_SUCCESS;
691 }
freetds_err_handler(DBPROCESS * dbproc,int severity,int dberr,int oserr,char * dberrstr,char * oserrstr)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 }
dbd_freetds_init(apr_pool_t * pool)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 */
dbd_freetds_get_name(const apr_dbd_results_t * res,int n)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;
dbd_freetds_transaction_mode_get(apr_dbd_transaction_t * trans)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
dbd_freetds_transaction_mode_set(apr_dbd_transaction_t * trans,int mode)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 }
dbd_freetds_pvbquery(apr_pool_t * pool,apr_dbd_t * sql,int * nrows,apr_dbd_prepared_t * statement,va_list args)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 }
dbd_freetds_pbquery(apr_pool_t * pool,apr_dbd_t * sql,int * nrows,apr_dbd_prepared_t * statement,const void ** values)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
dbd_freetds_pvbselect(apr_pool_t * pool,apr_dbd_t * sql,apr_dbd_results_t ** results,apr_dbd_prepared_t * statement,int seek,va_list args)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 }
dbd_freetds_pbselect(apr_pool_t * pool,apr_dbd_t * sql,apr_dbd_results_t ** results,apr_dbd_prepared_t * statement,int seek,const void ** values)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 }
dbd_freetds_datum_get(const apr_dbd_row_t * row,int n,apr_dbd_type_e type,void * data)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
806