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.
18 #include "apu_config.h"
20 /* COMPILE_STUBS: compile stubs for unimplemented functions.
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
33 #include "apr_strings.h"
36 #include "apr_pools.h"
37 #include "apr_dbd_internal.h"
41 #include <sys/types.h>
44 /* This probably needs to change for different applications */
45 #define MAX_COL_LEN 256
47 typedef struct freetds_cell_t
{
53 struct apr_dbd_transaction_t
{
61 apr_dbd_transaction_t
*trans
;
67 struct apr_dbd_results_t
{
75 struct apr_dbd_row_t
{
76 apr_dbd_results_t
*res
;
77 BYTE buf
[MAX_COL_LEN
];
80 struct apr_dbd_prepared_t
{
87 #define dbd_freetds_is_success(x) (x == SUCCEED)
89 static int labelnum
= 0; /* FIXME */
90 static regex_t dbd_freetds_find_arg
;
92 /* execute a query that doesn't return a result set, mop up,
93 * and return and APR-flavoured status
95 static RETCODE
freetds_exec(DBPROCESS
*proc
, const char *query
,
96 int want_results
, int *nrows
)
99 RETCODE rv
= dbcmd(proc
, query
);
103 rv
= dbsqlexec(proc
);
108 while (dbresults(proc
) != NO_MORE_RESULTS
) {
114 static apr_status_t
clear_result(void *data
)
117 return (dbcanquery((DBPROCESS
*)data
) == SUCCEED
)
122 static int dbd_freetds_select(apr_pool_t
*pool
, apr_dbd_t
*sql
,
123 apr_dbd_results_t
**results
,
124 const char *query
, int seek
)
126 apr_dbd_results_t
*res
;
127 if (sql
->trans
&& (sql
->trans
->errnum
!= SUCCEED
)) {
130 /* the core of this is
131 * dbcmd(proc, query);
133 * while (dbnextrow(dbproc) != NO_MORE_ROWS) {
140 sql
->err
= freetds_exec(sql
->proc
, query
, 1, NULL
);
141 if (!dbd_freetds_is_success(sql
->err
)) {
143 sql
->trans
->errnum
= sql
->err
;
148 sql
->err
= dbresults(sql
->proc
);
149 if (sql
->err
!= SUCCEED
) {
151 sql
->trans
->errnum
= sql
->err
;
157 *results
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
160 res
->proc
= sql
->proc
;
163 res
->ntuples
= dblastrow(sql
->proc
);
164 res
->sz
= dbnumcols(sql
->proc
);
165 apr_pool_cleanup_register(pool
, sql
->proc
, clear_result
,
166 apr_pool_cleanup_null
);
169 /* Now we have a result set. We need to bind to its vars */
170 res
->vars
= apr_palloc(pool
, res
->sz
* sizeof(freetds_cell_t
*));
171 for (i
=1; i
<= res
->sz
; ++i
) {
172 freetds_cell_t
*cell
= &res
->vars
[i
-1];
173 cell
->type
= dbcoltype(sql
->proc
, i
);
174 cell
->len
= dbcollen(sql
->proc
, i
);
175 cell
->data
= apr_palloc(pool
, cell
->len
);
176 sql
->err
= dbbind(sql
->proc
, i
, /*cell->type */ STRINGBIND
, cell
->len
, cell
->data
);
177 if (sql
->err
!= SUCCEED
) {
178 fprintf(stderr
, "dbbind error: %d, %d, %d", i
, cell
->type
, cell
->len
);
180 if ((sql
->err
!= SUCCEED
) && (sql
->trans
!= NULL
)) {
181 sql
->trans
->errnum
= sql
->err
;
185 return (sql
->err
== SUCCEED
) ? 0 : 1;
187 static const char *dbd_untaint(apr_pool_t
*pool
, regex_t
*rx
, const char *val
)
191 /* no untaint expression */
194 if (regexec(rx
, val
, 1, match
, 0) == 0) {
195 return apr_pstrndup(pool
, val
+match
[0].rm_so
,
196 match
[0].rm_eo
- match
[0].rm_so
);
200 static const char *dbd_statement(apr_pool_t
*pool
,
201 apr_dbd_prepared_t
*stmt
,
202 int nargs
, const char **args
)
212 /* compute upper bound on length (since untaint shrinks) */
213 len
= strlen(stmt
->fmt
) +1;
214 for (i
=0; i
<nargs
; ++i
) {
215 len
+= strlen(args
[i
]) - 2;
219 p_out
= ret
= apr_palloc(pool
, len
);
220 /* FIXME silly bug - this'll catch %%s */
221 while (q
= strstr(p_in
, "%s"), q
!= NULL
) {
223 strncpy(p_out
, p_in
, len
);
226 var
= dbd_untaint(pool
, stmt
->taint
[i
], args
[i
]);
228 strncpy(p_out
, var
, len
);
236 static int dbd_freetds_pselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
237 apr_dbd_results_t
**results
,
238 apr_dbd_prepared_t
*statement
,
239 int seek
, const char **values
)
241 const char *query
= dbd_statement(pool
, statement
,
242 statement
->nargs
, values
);
243 return dbd_freetds_select(pool
, sql
, results
, query
, seek
);
245 static int dbd_freetds_pvselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
246 apr_dbd_results_t
**results
,
247 apr_dbd_prepared_t
*statement
,
248 int seek
, va_list args
)
253 if (sql
->trans
&& sql
->trans
->errnum
) {
254 return sql
->trans
->errnum
;
257 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nargs
);
259 for (i
= 0; i
< statement
->nargs
; i
++) {
260 values
[i
] = va_arg(args
, const char*);
263 return dbd_freetds_pselect(pool
, sql
, results
, statement
, seek
, values
);
265 static int dbd_freetds_query(apr_dbd_t
*sql
, int *nrows
, const char *query
);
266 static int dbd_freetds_pquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
267 int *nrows
, apr_dbd_prepared_t
*statement
,
270 const char *query
= dbd_statement(pool
, statement
,
271 statement
->nargs
, values
);
272 return dbd_freetds_query(sql
, nrows
, query
);
274 static int dbd_freetds_pvquery(apr_pool_t
*pool
, apr_dbd_t
*sql
, int *nrows
,
275 apr_dbd_prepared_t
*statement
, va_list args
)
280 if (sql
->trans
&& sql
->trans
->errnum
) {
281 return sql
->trans
->errnum
;
284 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nargs
);
286 for (i
= 0; i
< statement
->nargs
; i
++) {
287 values
[i
] = va_arg(args
, const char*);
289 return dbd_freetds_pquery(pool
, sql
, nrows
, statement
, values
);
292 static int dbd_freetds_get_row(apr_pool_t
*pool
, apr_dbd_results_t
*res
,
293 apr_dbd_row_t
**rowp
, int rownum
)
296 apr_dbd_row_t
*row
= *rowp
;
297 int sequential
= ((rownum
>= 0) && res
->random
) ? 0 : 1;
300 row
= apr_palloc(pool
, sizeof(apr_dbd_row_t
));
315 rv
= dbnextrow(res
->proc
);
318 rv
= (rownum
>= 0) ? dbgetrow(res
->proc
, rownum
) : NO_MORE_ROWS
;
321 case SUCCEED
: return 0;
322 case REG_ROW
: return 0;
324 apr_pool_cleanup_run(pool
, res
->proc
, clear_result
);
328 case BUF_FULL
: return 2; /* FIXME */
335 static const char *dbd_freetds_get_entry(const apr_dbd_row_t
*row
, int n
)
337 /* FIXME: support different data types */
338 /* this fails - bind gets some vars but not others
339 return (const char*)row->res->vars[n].data;
341 DBPROCESS
* proc
= row
->res
->proc
;
342 BYTE
*ptr
= dbdata(proc
, n
+1);
343 int t
= dbcoltype(proc
, n
+1);
344 int l
= dbcollen(proc
, n
+1);
345 if (dbwillconvert(t
, SYBCHAR
)) {
346 dbconvert(proc
, t
, ptr
, l
, SYBCHAR
, (BYTE
*)row
->buf
, -1);
347 return (const char*)row
->buf
;
352 static const char *dbd_freetds_error(apr_dbd_t
*sql
, int n
)
354 /* XXX this doesn't seem to exist in the API ??? */
355 return apr_psprintf(sql
->pool
, "Error %d", sql
->err
);
358 static int dbd_freetds_query(apr_dbd_t
*sql
, int *nrows
, const char *query
)
360 if (sql
->trans
&& sql
->trans
->errnum
) {
361 return sql
->trans
->errnum
;
364 sql
->err
= freetds_exec(sql
->proc
, query
, 0, nrows
);
366 if (sql
->err
!= SUCCEED
) {
368 sql
->trans
->errnum
= sql
->err
;
375 static const char *dbd_freetds_escape(apr_pool_t
*pool
, const char *arg
,
381 static apr_status_t
freetds_regfree(void *rx
)
383 regfree((regex_t
*)rx
);
386 static int recurse_args(apr_pool_t
*pool
, int n
, const char *query
,
387 apr_dbd_prepared_t
*stmt
, int offs
)
390 /* we only support %s arguments for now */
393 regmatch_t matches
[3];
394 if (regexec(&dbd_freetds_find_arg
, query
, 3, matches
, 0) != 0) {
397 stmt
->taint
= apr_palloc(pool
, n
*sizeof(regex_t
*));
398 stmt
->sz
= apr_palloc(pool
, n
*sizeof(int));
404 int len
= matches
[1].rm_eo
- matches
[1].rm_so
- 2;
409 ret
= recurse_args(pool
, n
+1, query
+matches
[0].rm_eo
,
410 stmt
, offs
+matches
[0].rm_eo
);
412 memmove(stmt
->fmt
+ offs
+ matches
[1].rm_so
,
413 stmt
->fmt
+ offs
+ matches
[0].rm_eo
-1,
414 strlen(stmt
->fmt
+offs
+matches
[0].rm_eo
)+2);
416 /* compile untaint to a regex if found */
417 if (matches
[1].rm_so
== -1) {
418 stmt
->taint
[n
] = NULL
;
421 strncpy(arg
, query
+matches
[1].rm_so
+1,
422 matches
[1].rm_eo
- matches
[1].rm_so
- 2);
423 arg
[matches
[1].rm_eo
- matches
[1].rm_so
- 2] = '\0';
424 stmt
->taint
[n
] = apr_palloc(pool
, sizeof(regex_t
));
425 if (regcomp(stmt
->taint
[n
], arg
, REG_ICASE
|REG_EXTENDED
) != 0) {
429 apr_pool_cleanup_register(pool
, stmt
->taint
[n
], freetds_regfree
,
430 apr_pool_cleanup_null
);
434 /* record length if specified */
435 for (i
=matches
[2].rm_so
; i
<matches
[2].rm_eo
; ++i
) {
436 sz
= 10*sz
+ (query
[i
]-'\0');
442 static int dbd_freetds_prepare(apr_pool_t
*pool
, apr_dbd_t
*sql
,
443 const char *query
, const char *label
,
444 int nargs
, int nvals
, apr_dbd_type_e
*types
,
445 apr_dbd_prepared_t
**statement
)
447 apr_dbd_prepared_t
*stmt
;
450 label
= apr_psprintf(pool
, "%d", labelnum
++);
454 *statement
= apr_palloc(pool
, sizeof(apr_dbd_prepared_t
));
460 stmt
->fmt
= apr_pstrdup(pool
, query
);
461 stmt
->fmt
= recurse_args(pool
, 0, query
, stmt
, stmt
->fmt
);
463 /* overestimate by a byte or two to simplify */
464 len
= strlen("CREATE PROC apr.")
466 + stmt
->nargs
* strlen(" @arg1 varchar(len1),")
467 + strlen(" AS begin ")
469 + strlen(" end "); /* extra byte for terminator */
471 pquery
= apr_pcalloc(pool
, len
);
472 sprintf(pquery
, "CREATE PROC apr.%s", label
);
473 for (i
=0; i
<stmt
->nargs
; ++i
) {
474 sprintf(pquery
+strlen(pquery
), " @arg%d varchar(%d)", i
, stmt
->sz
[i
]);
475 if (i
< stmt
->nargs
-1) {
476 pquery
[strlen(pquery
)] = ',';
479 strcat(pquery
, " AS BEGIN ");
480 strcat(pquery
, stmt
->fmt
);
481 strcat(pquery
, " END");
483 return (freetds_exec(sql
->proc
, pquery
, 0, &i
) == SUCCEED
) ? 0 : 1;
485 stmt
->fmt
= apr_pstrdup(pool
, query
);
486 return recurse_args(pool
, 0, query
, stmt
, 0);
491 static int dbd_freetds_start_transaction(apr_pool_t
*pool
, apr_dbd_t
*handle
,
492 apr_dbd_transaction_t
**trans
)
496 /* XXX handle recursive transactions here */
498 handle
->err
= freetds_exec(handle
->proc
, "BEGIN TRANSACTION", 0, &dummy
);
500 if (dbd_freetds_is_success(handle
->err
)) {
502 *trans
= apr_pcalloc(pool
, sizeof(apr_dbd_transaction_t
));
504 (*trans
)->handle
= handle
;
505 handle
->trans
= *trans
;
512 static int dbd_freetds_end_transaction(apr_dbd_transaction_t
*trans
)
516 /* rollback on error or explicit rollback request */
519 trans
->handle
->err
= freetds_exec(trans
->handle
->proc
,
520 "ROLLBACK", 0, &dummy
);
523 trans
->handle
->err
= freetds_exec(trans
->handle
->proc
,
524 "COMMIT", 0, &dummy
);
526 trans
->handle
->trans
= NULL
;
528 return (trans
->handle
->err
== SUCCEED
) ? 0 : 1;
531 static DBPROCESS
*freetds_open(apr_pool_t
*pool
, const char *params
,
537 static const char *delims
= " \r\n\t;|,";
544 char *databaseName
= NULL
;
546 /* FIXME - this uses malloc */
547 /* FIXME - pass error message back to the caller in case of failure */
552 /* now set login properties */
553 for (ptr
= strchr(params
, '='); ptr
; ptr
= strchr(ptr
, '=')) {
554 /* don't dereference memory that may not belong to us */
559 for (key
= ptr
-1; apr_isspace(*key
); --key
);
561 while (apr_isalpha(*key
)) {
566 for (value
= ptr
+1; apr_isspace(*value
); ++value
);
568 vlen
= strcspn(value
, delims
);
569 buf
= apr_pstrndup(pool
, value
, vlen
); /* NULL-terminated copy */
571 if (!strncasecmp(key
, "username", klen
)) {
572 DBSETLUSER(login
, buf
);
574 else if (!strncasecmp(key
, "password", klen
)) {
575 DBSETLPWD(login
, buf
);
577 else if (!strncasecmp(key
, "appname", klen
)) {
578 DBSETLAPP(login
, buf
);
580 else if (!strncasecmp(key
, "dbname", klen
)) {
583 else if (!strncasecmp(key
, "host", klen
)) {
584 DBSETLHOST(login
, buf
);
586 else if (!strncasecmp(key
, "charset", klen
)) {
587 DBSETLCHARSET(login
, buf
);
589 else if (!strncasecmp(key
, "lang", klen
)) {
590 DBSETLNATLANG(login
, buf
);
592 else if (!strncasecmp(key
, "server", klen
)) {
601 process
= dbopen(login
, server
);
603 fprintf(stderr
, "databaseName [%s]\n", databaseName
);
605 if (databaseName
!= NULL
)
607 dbuse(process
, databaseName
);
611 if (process
== NULL
) {
617 static apr_dbd_t
*dbd_freetds_open(apr_pool_t
*pool
, const char *params
,
621 /* FIXME - pass error message back to the caller in case of failure */
622 DBPROCESS
*process
= freetds_open(pool
, params
, error
);
623 if (process
== NULL
) {
626 sql
= apr_palloc (pool
, sizeof (apr_dbd_t
));
629 sql
->params
= params
;
633 static apr_status_t
dbd_freetds_close(apr_dbd_t
*handle
)
635 dbclose(handle
->proc
);
639 static apr_status_t
dbd_freetds_check_conn(apr_pool_t
*pool
,
642 if (dbdead(handle
->proc
)) {
644 dbclose(handle
->proc
);
645 handle
->proc
= freetds_open(handle
->pool
, handle
->params
, NULL
);
646 if (!handle
->proc
|| dbdead(handle
->proc
)) {
650 /* clear it, in case this is called in error handling */
651 dbcancel(handle
->proc
);
655 static int dbd_freetds_select_db(apr_pool_t
*pool
, apr_dbd_t
*handle
,
658 /* ouch, it's declared int. But we can use APR 0/nonzero */
659 return (dbuse(handle
->proc
, (char*)name
) == SUCCEED
) ? APR_SUCCESS
: APR_EGENERAL
;
662 static void *dbd_freetds_native(apr_dbd_t
*handle
)
667 static int dbd_freetds_num_cols(apr_dbd_results_t
* res
)
672 static int dbd_freetds_num_tuples(apr_dbd_results_t
* res
)
682 static apr_status_t
freetds_term(void *dummy
)
685 regfree(&dbd_freetds_find_arg
);
688 static void dbd_freetds_init(apr_pool_t
*pool
)
690 int rv
= regcomp(&dbd_freetds_find_arg
,
691 "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED
);
694 regerror(rv
, &dbd_freetds_find_arg
, errmsg
, 256);
695 fprintf(stderr
, "regcomp failed: %s\n", errmsg
);
698 apr_pool_cleanup_register(pool
, NULL
, freetds_term
, apr_pool_cleanup_null
);
702 /* get_name is the only one of these that is implemented */
703 static const char *dbd_freetds_get_name(const apr_dbd_results_t
*res
, int n
)
705 return (const char*) dbcolname(res
->proc
, n
+1); /* numbering starts at 1 */
708 /* These are stubs: transaction modes not implemented here */
709 #define DBD_NOTIMPL APR_ENOTIMPL;
710 static int dbd_freetds_transaction_mode_get(apr_dbd_transaction_t
*trans
)
712 return trans
? trans
->mode
: APR_DBD_TRANSACTION_COMMIT
;
715 static int dbd_freetds_transaction_mode_set(apr_dbd_transaction_t
*trans
,
719 trans
->mode
= mode
& TXN_MODE_BITS
;
722 return APR_DBD_TRANSACTION_COMMIT
;
724 static int dbd_freetds_pvbquery(apr_pool_t
*pool
, apr_dbd_t
*sql
, int *nrows
,
725 apr_dbd_prepared_t
*statement
, va_list args
)
729 static int dbd_freetds_pbquery(apr_pool_t
*pool
, apr_dbd_t
*sql
, int *nrows
,
730 apr_dbd_prepared_t
* statement
,
736 static int dbd_freetds_pvbselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
737 apr_dbd_results_t
**results
,
738 apr_dbd_prepared_t
*statement
,
739 int seek
, va_list args
)
743 static int dbd_freetds_pbselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
744 apr_dbd_results_t
**results
,
745 apr_dbd_prepared_t
*statement
,
746 int seek
, const void **values
)
750 static apr_status_t
dbd_freetds_datum_get(const apr_dbd_row_t
*row
, int n
,
751 apr_dbd_type_e type
, void *data
)
757 APU_MODULE_DECLARE_DATA
const apr_dbd_driver_t apr_dbd_freetds_driver
= {
762 dbd_freetds_check_conn
,
764 dbd_freetds_select_db
,
765 dbd_freetds_start_transaction
,
766 dbd_freetds_end_transaction
,
769 dbd_freetds_num_cols
,
770 dbd_freetds_num_tuples
,
772 dbd_freetds_get_entry
,
777 dbd_freetds_pvselect
,
780 /* this is only implemented to support httpd/2.2 standard usage,
781 * as in the original DBD implementation. Everything else is NOTIMPL.
784 dbd_freetds_get_name
,
785 dbd_freetds_transaction_mode_get
,
786 dbd_freetds_transaction_mode_set
,
788 dbd_freetds_pvbquery
,
789 dbd_freetds_pvbselect
,
791 dbd_freetds_pbselect
,
792 dbd_freetds_datum_get