apr_dbd_freetds.c revision 259065
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 53typedef struct freetds_cell_t { 54 int type; 55 DBINT len; 56 BYTE *data; 57} freetds_cell_t; 58 59struct apr_dbd_transaction_t { 60 int mode; 61 int errnum; 62 apr_dbd_t *handle; 63}; 64 65struct 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 73struct 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 81struct apr_dbd_row_t { 82 apr_dbd_results_t *res; 83 BYTE buf[MAX_COL_LEN]; 84}; 85 86struct 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 95static int labelnum = 0; /* FIXME */ 96static 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 */ 101static 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} 120static 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 128static 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} 193static 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} 206static 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} 242static 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} 251static 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} 271static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query); 272static 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} 280static 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 298static 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 341static 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 358static 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 364static 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 381static const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg, 382 apr_dbd_t *sql) 383{ 384 return arg; 385} 386 387static apr_status_t freetds_regfree(void *rx) 388{ 389 regfree((regex_t*)rx); 390 return APR_SUCCESS; 391} 392static 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 448static 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 497static 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 518static 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 537static 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} 621static 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 637static apr_status_t dbd_freetds_close(apr_dbd_t *handle) 638{ 639 dbclose(handle->proc); 640 return APR_SUCCESS; 641} 642 643static 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 659static 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 666static void *dbd_freetds_native(apr_dbd_t *handle) 667{ 668 return handle->proc; 669} 670 671static int dbd_freetds_num_cols(apr_dbd_results_t* res) 672{ 673 return res->sz; 674} 675 676static 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 686static apr_status_t freetds_term(void *dummy) 687{ 688 dbexit(); 689 regfree(&dbd_freetds_find_arg); 690 return APR_SUCCESS; 691} 692static 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} 697static 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 */ 713static 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; 720static int dbd_freetds_transaction_mode_get(apr_dbd_transaction_t *trans) 721{ 722 return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT; 723} 724 725static 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} 734static 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} 739static 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 746static 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} 753static 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} 760static 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 767APU_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