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