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
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #ifdef I_CAN_DEAL_WITH_THIS_PARTIAL_DRIVER_AND_UNMAINTAINED_CODE_FOR_FREETDS
20 #include "apu_config.h"
22 /* COMPILE_STUBS: compile stubs for unimplemented functions.
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
35 #include "apr_strings.h"
38 #include "apr_pools.h"
39 #include "apr_dbd_internal.h"
41 #ifdef HAVE_FREETDS_SYBDB_H
42 #include <freetds/sybdb.h>
49 #include <sys/types.h>
52 /* This probably needs to change for different applications */
53 #define MAX_COL_LEN 256
55 typedef struct freetds_cell_t {
61 struct apr_dbd_transaction_t {
69 apr_dbd_transaction_t *trans;
75 struct apr_dbd_results_t {
83 struct apr_dbd_row_t {
84 apr_dbd_results_t *res;
85 BYTE buf[MAX_COL_LEN];
88 struct apr_dbd_prepared_t {
95 #define dbd_freetds_is_success(x) (x == SUCCEED)
97 static int labelnum = 0; /* FIXME */
98 static regex_t dbd_freetds_find_arg;
100 /* execute a query that doesn't return a result set, mop up,
101 * and return and APR-flavoured status
103 static RETCODE freetds_exec(DBPROCESS *proc, const char *query,
104 int want_results, int *nrows)
107 RETCODE rv = dbcmd(proc, query);
111 rv = dbsqlexec(proc);
116 while (dbresults(proc) != NO_MORE_RESULTS) {
122 static apr_status_t clear_result(void *data)
125 return (dbcanquery((DBPROCESS*)data) == SUCCEED)
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)
134 apr_dbd_results_t *res;
135 if (sql->trans && (sql->trans->errnum != SUCCEED)) {
138 /* the core of this is
139 * dbcmd(proc, query);
141 * while (dbnextrow(dbproc) != NO_MORE_ROWS) {
148 sql->err = freetds_exec(sql->proc, query, 1, NULL);
149 if (!dbd_freetds_is_success(sql->err)) {
151 sql->trans->errnum = sql->err;
156 sql->err = dbresults(sql->proc);
157 if (sql->err != SUCCEED) {
159 sql->trans->errnum = sql->err;
165 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
168 res->proc = sql->proc;
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);
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);
188 if ((sql->err != SUCCEED) && (sql->trans != NULL)) {
189 sql->trans->errnum = sql->err;
193 return (sql->err == SUCCEED) ? 0 : 1;
195 static const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val)
199 /* no untaint expression */
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);
208 static const char *dbd_statement(apr_pool_t *pool,
209 apr_dbd_prepared_t *stmt,
210 int nargs, const char **args)
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;
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) {
231 strncpy(p_out, p_in, len);
234 var = dbd_untaint(pool, stmt->taint[i], args[i]);
236 strncpy(p_out, var, len);
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)
249 const char *query = dbd_statement(pool, statement,
250 statement->nargs, values);
251 return dbd_freetds_select(pool, sql, results, query, seek);
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)
261 if (sql->trans && sql->trans->errnum) {
262 return sql->trans->errnum;
265 values = apr_palloc(pool, sizeof(*values) * statement->nargs);
267 for (i = 0; i < statement->nargs; i++) {
268 values[i] = va_arg(args, const char*);
271 return dbd_freetds_pselect(pool, sql, results, statement, seek, values);
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,
278 const char *query = dbd_statement(pool, statement,
279 statement->nargs, values);
280 return dbd_freetds_query(sql, nrows, query);
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)
288 if (sql->trans && sql->trans->errnum) {
289 return sql->trans->errnum;
292 values = apr_palloc(pool, sizeof(*values) * statement->nargs);
294 for (i = 0; i < statement->nargs; i++) {
295 values[i] = va_arg(args, const char*);
297 return dbd_freetds_pquery(pool, sql, nrows, statement, values);
300 static int dbd_freetds_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
301 apr_dbd_row_t **rowp, int rownum)
304 apr_dbd_row_t *row = *rowp;
305 int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
308 row = apr_palloc(pool, sizeof(apr_dbd_row_t));
323 rv = dbnextrow(res->proc);
326 rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
329 case SUCCEED: return 0;
330 case REG_ROW: return 0;
332 apr_pool_cleanup_run(res->pool, res->proc, clear_result);
336 case BUF_FULL: return 2; /* FIXME */
343 static const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n)
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;
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;
360 static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
362 /* XXX this doesn't seem to exist in the API ??? */
363 return apr_psprintf(sql->pool, "Error %d", sql->err);
366 static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query)
368 if (sql->trans && sql->trans->errnum) {
369 return sql->trans->errnum;
372 sql->err = freetds_exec(sql->proc, query, 0, nrows);
374 if (sql->err != SUCCEED) {
376 sql->trans->errnum = sql->err;
383 static const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg,
389 static apr_status_t freetds_regfree(void *rx)
391 regfree((regex_t*)rx);
394 static int recurse_args(apr_pool_t *pool, int n, const char *query,
395 apr_dbd_prepared_t *stmt, int offs)
398 /* we only support %s arguments for now */
401 regmatch_t matches[3];
402 if (regexec(&dbd_freetds_find_arg, query, 3, matches, 0) != 0) {
405 stmt->taint = apr_palloc(pool, n*sizeof(regex_t*));
406 stmt->sz = apr_palloc(pool, n*sizeof(int));
412 int len = matches[1].rm_eo - matches[1].rm_so - 2;
417 ret = recurse_args(pool, n+1, query+matches[0].rm_eo,
418 stmt, offs+matches[0].rm_eo);
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);
424 /* compile untaint to a regex if found */
425 if (matches[1].rm_so == -1) {
426 stmt->taint[n] = NULL;
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) {
437 apr_pool_cleanup_register(pool, stmt->taint[n], freetds_regfree,
438 apr_pool_cleanup_null);
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');
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)
455 apr_dbd_prepared_t *stmt;
458 label = apr_psprintf(pool, "%d", labelnum++);
462 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
468 stmt->fmt = apr_pstrdup(pool, query);
469 stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt);
471 /* overestimate by a byte or two to simplify */
472 len = strlen("CREATE PROC apr.")
474 + stmt->nargs * strlen(" @arg1 varchar(len1),")
475 + strlen(" AS begin ")
477 + strlen(" end "); /* extra byte for terminator */
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)] = ',';
487 strcat(pquery, " AS BEGIN ");
488 strcat(pquery, stmt->fmt);
489 strcat(pquery, " END");
491 return (freetds_exec(sql->proc, pquery, 0, &i) == SUCCEED) ? 0 : 1;
493 stmt->fmt = apr_pstrdup(pool, query);
494 return recurse_args(pool, 0, query, stmt, 0);
499 static int dbd_freetds_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
500 apr_dbd_transaction_t **trans)
504 /* XXX handle recursive transactions here */
506 handle->err = freetds_exec(handle->proc, "BEGIN TRANSACTION", 0, &dummy);
508 if (dbd_freetds_is_success(handle->err)) {
510 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
512 (*trans)->handle = handle;
513 handle->trans = *trans;
520 static int dbd_freetds_end_transaction(apr_dbd_transaction_t *trans)
524 /* rollback on error or explicit rollback request */
527 trans->handle->err = freetds_exec(trans->handle->proc,
528 "ROLLBACK", 0, &dummy);
531 trans->handle->err = freetds_exec(trans->handle->proc,
532 "COMMIT", 0, &dummy);
534 trans->handle->trans = NULL;
536 return (trans->handle->err == SUCCEED) ? 0 : 1;
539 static DBPROCESS *freetds_open(apr_pool_t *pool, const char *params,
545 static const char *delims = " \r\n\t;|,";
552 char *databaseName = NULL;
554 /* FIXME - this uses malloc */
555 /* FIXME - pass error message back to the caller in case of failure */
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 */
567 for (key = ptr-1; apr_isspace(*key); --key);
569 while (apr_isalpha(*key)) {
574 for (value = ptr+1; apr_isspace(*value); ++value);
576 vlen = strcspn(value, delims);
577 buf = apr_pstrndup(pool, value, vlen); /* NULL-terminated copy */
579 if (!strncasecmp(key, "username", klen)) {
580 DBSETLUSER(login, buf);
582 else if (!strncasecmp(key, "password", klen)) {
583 DBSETLPWD(login, buf);
585 else if (!strncasecmp(key, "appname", klen)) {
586 DBSETLAPP(login, buf);
588 else if (!strncasecmp(key, "dbname", klen)) {
591 else if (!strncasecmp(key, "host", klen)) {
592 DBSETLHOST(login, buf);
594 else if (!strncasecmp(key, "charset", klen)) {
595 DBSETLCHARSET(login, buf);
597 else if (!strncasecmp(key, "lang", klen)) {
598 DBSETLNATLANG(login, buf);
600 else if (!strncasecmp(key, "server", klen)) {
609 process = dbopen(login, server);
611 if (process != NULL && databaseName != NULL)
613 dbuse(process, databaseName);
617 if (process == NULL) {
623 static apr_dbd_t *dbd_freetds_open(apr_pool_t *pool, const char *params,
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) {
632 sql = apr_pcalloc(pool, sizeof (apr_dbd_t));
635 sql->params = params;
639 static apr_status_t dbd_freetds_close(apr_dbd_t *handle)
641 dbclose(handle->proc);
645 static apr_status_t dbd_freetds_check_conn(apr_pool_t *pool,
648 if (dbdead(handle->proc)) {
650 dbclose(handle->proc);
651 handle->proc = freetds_open(handle->pool, handle->params, NULL);
652 if (!handle->proc || dbdead(handle->proc)) {
656 /* clear it, in case this is called in error handling */
657 dbcancel(handle->proc);
661 static int dbd_freetds_select_db(apr_pool_t *pool, apr_dbd_t *handle,
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;
668 static void *dbd_freetds_native(apr_dbd_t *handle)
673 static int dbd_freetds_num_cols(apr_dbd_results_t* res)
678 static int dbd_freetds_num_tuples(apr_dbd_results_t* res)
688 static apr_status_t freetds_term(void *dummy)
691 regfree(&dbd_freetds_find_arg);
694 static int freetds_err_handler(DBPROCESS *dbproc, int severity, int dberr,
695 int oserr, char *dberrstr, char *oserrstr)
697 return INT_CANCEL; /* never exit */
699 static void dbd_freetds_init(apr_pool_t *pool)
701 int rv = regcomp(&dbd_freetds_find_arg,
702 "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED);
705 regerror(rv, &dbd_freetds_find_arg, errmsg, 256);
706 fprintf(stderr, "regcomp failed: %s\n", errmsg);
709 dberrhandle(freetds_err_handler);
710 apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
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)
717 return (const char*) dbcolname(res->proc, n+1); /* numbering starts at 1 */
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)
724 return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT;
727 static int dbd_freetds_transaction_mode_set(apr_dbd_transaction_t *trans,
731 trans->mode = mode & TXN_MODE_BITS;
734 return APR_DBD_TRANSACTION_COMMIT;
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)
741 static int dbd_freetds_pbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
742 apr_dbd_prepared_t * statement,
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)
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)
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)
769 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = {
774 dbd_freetds_check_conn,
776 dbd_freetds_select_db,
777 dbd_freetds_start_transaction,
778 dbd_freetds_end_transaction,
781 dbd_freetds_num_cols,
782 dbd_freetds_num_tuples,
784 dbd_freetds_get_entry,
789 dbd_freetds_pvselect,
792 /* this is only implemented to support httpd/2.2 standard usage,
793 * as in the original DBD implementation. Everything else is NOTIMPL.
796 dbd_freetds_get_name,
797 dbd_freetds_transaction_mode_get,
798 dbd_freetds_transaction_mode_set,
800 dbd_freetds_pvbquery,
801 dbd_freetds_pvbselect,
803 dbd_freetds_pbselect,
804 dbd_freetds_datum_get