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 19#if APU_HAVE_MYSQL 20 21#include "apu_version.h" 22#include "apu_config.h" 23 24#include <ctype.h> 25#include <stdlib.h> 26 27#if defined(HAVE_MYSQL_MYSQL_H) 28#if defined(HAVE_MYSQL_MY_GLOBAL_H) 29#include <mysql/my_global.h> 30#if defined(HAVE_MYSQL_MY_SYS_H) 31#include <mysql/my_sys.h> 32#endif 33#endif 34#include <mysql/mysql.h> 35#include <mysql/errmsg.h> 36#else /* !defined(HAVE_MYSQL_MYSQL_H) */ 37#if defined(HAVE_MY_GLOBAL_H) 38#include <my_global.h> 39#if defined(HAVE_MY_SYS_H) 40#include <my_sys.h> 41#endif 42#endif 43#include <mysql.h> 44#include <errmsg.h> 45#endif 46 47#include "apr_strings.h" 48#include "apr_lib.h" 49#include "apr_buckets.h" 50 51#include "apr_dbd_internal.h" 52 53/* default maximum field size 1 MB */ 54#define FIELDSIZE 1048575 55 56struct apr_dbd_prepared_t { 57 MYSQL_STMT* stmt; 58 int nargs; 59 int nvals; 60 apr_dbd_type_e *types; 61}; 62 63struct apr_dbd_transaction_t { 64 int mode; 65 int errnum; 66 apr_dbd_t *handle; 67}; 68 69struct apr_dbd_t { 70 MYSQL* conn ; 71 apr_dbd_transaction_t* trans ; 72 unsigned long fldsz; 73}; 74 75struct apr_dbd_results_t { 76 int random; 77 MYSQL_RES *res; 78 MYSQL_STMT *statement; 79 MYSQL_BIND *bind; 80 apr_pool_t *pool; 81}; 82struct apr_dbd_row_t { 83 MYSQL_ROW row; 84 apr_dbd_results_t *res; 85 unsigned long *len; 86}; 87 88/* MySQL specific bucket for BLOB types */ 89typedef struct apr_bucket_lob apr_bucket_lob; 90/** 91 * A bucket referring to a MySQL BLOB 92 */ 93struct apr_bucket_lob { 94 /** Number of buckets using this memory */ 95 apr_bucket_refcount refcount; 96 /** The row this bucket refers to */ 97 const apr_dbd_row_t *row; 98 /** The column this bucket refers to */ 99 int col; 100 /** The pool into which any needed structures should 101 * be created while reading from this bucket */ 102 apr_pool_t *readpool; 103}; 104 105static void lob_bucket_destroy(void *data); 106static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, 107 apr_size_t *len, apr_read_type_e block); 108static apr_bucket *apr_bucket_lob_make(apr_bucket *b, 109 const apr_dbd_row_t *row, int col, 110 apr_off_t offset, apr_size_t len, 111 apr_pool_t *p); 112static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, 113 apr_off_t offset, 114 apr_size_t len, apr_pool_t *p, 115 apr_bucket_alloc_t *list); 116static int dbd_mysql_num_cols(apr_dbd_results_t *res); 117 118static const apr_bucket_type_t apr_bucket_type_lob = { 119 "LOB", 5, APR_BUCKET_DATA, 120 lob_bucket_destroy, 121 lob_bucket_read, 122 apr_bucket_setaside_notimpl, 123 apr_bucket_shared_split, 124 apr_bucket_shared_copy 125}; 126 127static void lob_bucket_destroy(void *data) 128{ 129 apr_bucket_lob *f = data; 130 131 if (apr_bucket_shared_destroy(f)) { 132 /* no need to destroy database objects here; it will get 133 * done automatically when the pool gets cleaned up */ 134 apr_bucket_free(f); 135 } 136} 137 138static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, 139 apr_size_t *len, apr_read_type_e block) 140{ 141 apr_bucket_lob *a = e->data; 142 const apr_dbd_row_t *row = a->row; 143 apr_dbd_results_t *res = row->res; 144 int col = a->col; 145 apr_bucket *b = NULL; 146 int rv; 147 apr_size_t blength = e->length; /* bytes remaining in file past offset */ 148 apr_off_t boffset = e->start; 149 MYSQL_BIND *bind = &res->bind[col]; 150 151 *str = NULL; /* in case we die prematurely */ 152 153 /* fetch from offset if not at the beginning */ 154 if (boffset > 0) { 155 rv = mysql_stmt_fetch_column(res->statement, bind, col, 156 (unsigned long) boffset); 157 if (rv != 0) { 158 return APR_EGENERAL; 159 } 160 } 161 blength -= blength > bind->buffer_length ? bind->buffer_length : blength; 162 *len = e->length - blength; 163 *str = bind->buffer; 164 165 /* allocate new buffer, since we used this one for the bucket */ 166 bind->buffer = apr_palloc(res->pool, bind->buffer_length); 167 168 /* 169 * Change the current bucket to refer to what we read, 170 * even if we read nothing because we hit EOF. 171 */ 172 apr_bucket_pool_make(e, *str, *len, res->pool); 173 174 /* If we have more to read from the field, then create another bucket */ 175 if (blength > 0) { 176 /* for efficiency, we can just build a new apr_bucket struct 177 * to wrap around the existing LOB bucket */ 178 b = apr_bucket_alloc(sizeof(*b), e->list); 179 b->start = boffset + *len; 180 b->length = blength; 181 b->data = a; 182 b->type = &apr_bucket_type_lob; 183 b->free = apr_bucket_free; 184 b->list = e->list; 185 APR_BUCKET_INSERT_AFTER(e, b); 186 } 187 else { 188 lob_bucket_destroy(a); 189 } 190 191 return APR_SUCCESS; 192} 193 194static apr_bucket *apr_bucket_lob_make(apr_bucket *b, 195 const apr_dbd_row_t *row, int col, 196 apr_off_t offset, apr_size_t len, 197 apr_pool_t *p) 198{ 199 apr_bucket_lob *f; 200 201 f = apr_bucket_alloc(sizeof(*f), b->list); 202 f->row = row; 203 f->col = col; 204 f->readpool = p; 205 206 b = apr_bucket_shared_make(b, f, offset, len); 207 b->type = &apr_bucket_type_lob; 208 209 return b; 210} 211 212static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, 213 apr_off_t offset, 214 apr_size_t len, apr_pool_t *p, 215 apr_bucket_alloc_t *list) 216{ 217 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); 218 219 APR_BUCKET_INIT(b); 220 b->free = apr_bucket_free; 221 b->list = list; 222 return apr_bucket_lob_make(b, row, col, offset, len, p); 223} 224 225static apr_status_t free_result(void *data) 226{ 227 mysql_free_result(data); 228 return APR_SUCCESS; 229} 230 231static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql, 232 apr_dbd_results_t **results, 233 const char *query, int seek) 234{ 235 int sz; 236 int ret; 237 if (sql->trans && sql->trans->errnum) { 238 return sql->trans->errnum; 239 } 240 ret = mysql_query(sql->conn, query); 241 if (!ret) { 242 if (sz = mysql_field_count(sql->conn), sz > 0) { 243 if (!*results) { 244 *results = apr_palloc(pool, sizeof(apr_dbd_results_t)); 245 } 246 (*results)->random = seek; 247 (*results)->statement = NULL; 248 (*results)->pool = pool; 249 if (seek) { 250 (*results)->res = mysql_store_result(sql->conn); 251 } 252 else { 253 (*results)->res = mysql_use_result(sql->conn); 254 } 255 apr_pool_cleanup_register(pool, (*results)->res, 256 free_result,apr_pool_cleanup_null); 257 } 258 } else { 259 ret = mysql_errno(sql->conn); 260 } 261 262 if (TXN_NOTICE_ERRORS(sql->trans)) { 263 sql->trans->errnum = ret; 264 } 265 return ret; 266} 267 268static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n) 269{ 270 if ((n < 0) || (n >= (int) mysql_num_fields(res->res))) { 271 return NULL; 272 } 273 274 return mysql_fetch_fields(res->res)[n].name; 275} 276 277static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res, 278 apr_dbd_row_t **row, int rownum) 279{ 280 MYSQL_ROW r = NULL; 281 int ret = 0; 282 283 if (res->statement) { 284 if (res->random) { 285 if (rownum > 0) { 286 mysql_stmt_data_seek(res->statement, (my_ulonglong) --rownum); 287 } 288 else { 289 return -1; /* invalid row */ 290 } 291 } 292 ret = mysql_stmt_fetch(res->statement); 293 switch (ret) { 294 case 1: 295 ret = mysql_stmt_errno(res->statement); 296 break; 297 case MYSQL_NO_DATA: 298 ret = -1; 299 break; 300 default: 301 ret = 0; /* bad luck - get_entry will deal with this */ 302 break; 303 } 304 } 305 else { 306 if (res->random) { 307 if (rownum > 0) { 308 mysql_data_seek(res->res, (my_ulonglong) --rownum); 309 } 310 else { 311 return -1; /* invalid row */ 312 } 313 } 314 r = mysql_fetch_row(res->res); 315 if (r == NULL) { 316 ret = -1; 317 } 318 } 319 if (ret == 0) { 320 if (!*row) { 321 *row = apr_palloc(pool, sizeof(apr_dbd_row_t)); 322 } 323 (*row)->row = r; 324 (*row)->res = res; 325 (*row)->len = mysql_fetch_lengths(res->res); 326 } 327 else { 328 apr_pool_cleanup_run(res->pool, res->res, free_result); 329 } 330 return ret; 331} 332#if 0 333/* An improved API that was proposed but not followed up */ 334static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n, 335 apr_dbd_datum_t *val) 336{ 337 MYSQL_BIND *bind; 338 if (dbd_mysql_num_cols(row->res) <= n) { 339 return NULL; 340 } 341 if (row->res->statement) { 342 bind = &row->res->bind[n]; 343 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 344 val->type = APR_DBD_VALUE_NULL; 345 return -1; 346 } 347 if (*bind->is_null) { 348 val->type = APR_DBD_VALUE_NULL; 349 return -1; 350 } 351 else { 352 val->type = APR_DBD_VALUE_STRING; 353 val->value.stringval = bind->buffer; 354 } 355 } 356 else { 357 val->type = APR_DBD_VALUE_STRING; 358 val->value.stringval = row->row[n]; 359 } 360 return 0; 361} 362#else 363 364static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n) 365{ 366 MYSQL_BIND *bind; 367 if (dbd_mysql_num_cols(row->res) <= n) { 368 return NULL; 369 } 370 if (row->res->statement) { 371 bind = &row->res->bind[n]; 372 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 373 return NULL; 374 } 375 if (*bind->is_null) { 376 return NULL; 377 } 378 else { 379 return bind->buffer; 380 } 381 } 382 else { 383 return row->row[n]; 384 } 385 return NULL; 386} 387#endif 388 389static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n, 390 apr_dbd_type_e type, void *data) 391{ 392 if (row->res->statement) { 393 MYSQL_BIND *bind = &row->res->bind[n]; 394 unsigned long len = *bind->length; 395 396 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 397 return APR_EGENERAL; 398 } 399 400 if (*bind->is_null) { 401 return APR_ENOENT; 402 } 403 404 switch (type) { 405 case APR_DBD_TYPE_TINY: 406 *(char*)data = atoi(bind->buffer); 407 break; 408 case APR_DBD_TYPE_UTINY: 409 *(unsigned char*)data = atoi(bind->buffer); 410 break; 411 case APR_DBD_TYPE_SHORT: 412 *(short*)data = atoi(bind->buffer); 413 break; 414 case APR_DBD_TYPE_USHORT: 415 *(unsigned short*)data = atoi(bind->buffer); 416 break; 417 case APR_DBD_TYPE_INT: 418 *(int*)data = atoi(bind->buffer); 419 break; 420 case APR_DBD_TYPE_UINT: 421 *(unsigned int*)data = atoi(bind->buffer); 422 break; 423 case APR_DBD_TYPE_LONG: 424 *(long*)data = atol(bind->buffer); 425 break; 426 case APR_DBD_TYPE_ULONG: 427 *(unsigned long*)data = atol(bind->buffer); 428 break; 429 case APR_DBD_TYPE_LONGLONG: 430 *(apr_int64_t*)data = apr_atoi64(bind->buffer); 431 break; 432 case APR_DBD_TYPE_ULONGLONG: 433 *(apr_uint64_t*)data = apr_atoi64(bind->buffer); 434 break; 435 case APR_DBD_TYPE_FLOAT: 436 *(float*)data = (float) atof(bind->buffer); 437 break; 438 case APR_DBD_TYPE_DOUBLE: 439 *(double*)data = atof(bind->buffer); 440 break; 441 case APR_DBD_TYPE_STRING: 442 case APR_DBD_TYPE_TEXT: 443 case APR_DBD_TYPE_TIME: 444 case APR_DBD_TYPE_DATE: 445 case APR_DBD_TYPE_DATETIME: 446 case APR_DBD_TYPE_TIMESTAMP: 447 case APR_DBD_TYPE_ZTIMESTAMP: 448 *((char*)bind->buffer+bind->buffer_length-1) = '\0'; 449 *(char**)data = bind->buffer; 450 break; 451 case APR_DBD_TYPE_BLOB: 452 case APR_DBD_TYPE_CLOB: 453 { 454 apr_bucket *e; 455 apr_bucket_brigade *b = (apr_bucket_brigade*)data; 456 457 e = apr_bucket_lob_create(row, n, 0, len, 458 row->res->pool, b->bucket_alloc); 459 APR_BRIGADE_INSERT_TAIL(b, e); 460 } 461 break; 462 case APR_DBD_TYPE_NULL: 463 *(void**)data = NULL; 464 break; 465 default: 466 return APR_EGENERAL; 467 } 468 } 469 else { 470 if (row->row[n] == NULL) { 471 return APR_ENOENT; 472 } 473 474 switch (type) { 475 case APR_DBD_TYPE_TINY: 476 *(char*)data = atoi(row->row[n]); 477 break; 478 case APR_DBD_TYPE_UTINY: 479 *(unsigned char*)data = atoi(row->row[n]); 480 break; 481 case APR_DBD_TYPE_SHORT: 482 *(short*)data = atoi(row->row[n]); 483 break; 484 case APR_DBD_TYPE_USHORT: 485 *(unsigned short*)data = atoi(row->row[n]); 486 break; 487 case APR_DBD_TYPE_INT: 488 *(int*)data = atoi(row->row[n]); 489 break; 490 case APR_DBD_TYPE_UINT: 491 *(unsigned int*)data = atoi(row->row[n]); 492 break; 493 case APR_DBD_TYPE_LONG: 494 *(long*)data = atol(row->row[n]); 495 break; 496 case APR_DBD_TYPE_ULONG: 497 *(unsigned long*)data = atol(row->row[n]); 498 break; 499 case APR_DBD_TYPE_LONGLONG: 500 *(apr_int64_t*)data = apr_atoi64(row->row[n]); 501 break; 502 case APR_DBD_TYPE_ULONGLONG: 503 *(apr_uint64_t*)data = apr_atoi64(row->row[n]); 504 break; 505 case APR_DBD_TYPE_FLOAT: 506 *(float*)data = (float) atof(row->row[n]); 507 break; 508 case APR_DBD_TYPE_DOUBLE: 509 *(double*)data = atof(row->row[n]); 510 break; 511 case APR_DBD_TYPE_STRING: 512 case APR_DBD_TYPE_TEXT: 513 case APR_DBD_TYPE_TIME: 514 case APR_DBD_TYPE_DATE: 515 case APR_DBD_TYPE_DATETIME: 516 case APR_DBD_TYPE_TIMESTAMP: 517 case APR_DBD_TYPE_ZTIMESTAMP: 518 *(char**)data = row->row[n]; 519 break; 520 case APR_DBD_TYPE_BLOB: 521 case APR_DBD_TYPE_CLOB: 522 { 523 apr_bucket *e; 524 apr_bucket_brigade *b = (apr_bucket_brigade*)data; 525 526 e = apr_bucket_pool_create(row->row[n], row->len[n], 527 row->res->pool, b->bucket_alloc); 528 APR_BRIGADE_INSERT_TAIL(b, e); 529 } 530 break; 531 case APR_DBD_TYPE_NULL: 532 *(void**)data = NULL; 533 break; 534 default: 535 return APR_EGENERAL; 536 } 537 } 538 return 0; 539} 540 541static const char *dbd_mysql_error(apr_dbd_t *sql, int n) 542{ 543 return mysql_error(sql->conn); 544} 545 546static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query) 547{ 548 int ret; 549 if (sql->trans && sql->trans->errnum) { 550 return sql->trans->errnum; 551 } 552 ret = mysql_query(sql->conn, query); 553 if (ret != 0) { 554 ret = mysql_errno(sql->conn); 555 } 556 *nrows = (int) mysql_affected_rows(sql->conn); 557 if (TXN_NOTICE_ERRORS(sql->trans)) { 558 sql->trans->errnum = ret; 559 } 560 return ret; 561} 562 563static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg, 564 apr_dbd_t *sql) 565{ 566 unsigned long len = strlen(arg); 567 char *ret = apr_palloc(pool, 2*len + 1); 568 mysql_real_escape_string(sql->conn, ret, arg, len); 569 return ret; 570} 571 572static apr_status_t stmt_close(void *data) 573{ 574 mysql_stmt_close(data); 575 return APR_SUCCESS; 576} 577 578static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql, 579 const char *query, const char *label, 580 int nargs, int nvals, apr_dbd_type_e *types, 581 apr_dbd_prepared_t **statement) 582{ 583 /* Translate from apr_dbd to native query format */ 584 int ret; 585 586 if (!*statement) { 587 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t)); 588 } 589 (*statement)->stmt = mysql_stmt_init(sql->conn); 590 591 if ((*statement)->stmt) { 592 apr_pool_cleanup_register(pool, (*statement)->stmt, 593 stmt_close, apr_pool_cleanup_null); 594 ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query)); 595 596 if (ret != 0) { 597 ret = mysql_stmt_errno((*statement)->stmt); 598 } 599 600 (*statement)->nargs = nargs; 601 (*statement)->nvals = nvals; 602 (*statement)->types = types; 603 604 return ret; 605 } 606 607 return CR_OUT_OF_MEMORY; 608} 609 610static void dbd_mysql_bind(apr_dbd_prepared_t *statement, 611 const char **values, MYSQL_BIND *bind) 612{ 613 int i, j; 614 615 for (i = 0, j = 0; i < statement->nargs; i++, j++) { 616 bind[i].length = &bind[i].buffer_length; 617 bind[i].is_unsigned = 0; 618 bind[i].is_null = NULL; 619 620 if (values[j] == NULL) { 621 bind[i].buffer_type = MYSQL_TYPE_NULL; 622 } 623 else { 624 switch (statement->types[i]) { 625 case APR_DBD_TYPE_BLOB: 626 case APR_DBD_TYPE_CLOB: 627 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 628 bind[i].buffer = (void*)values[j]; 629 bind[i].buffer_length = atol(values[++j]); 630 631 /* skip table and column */ 632 j += 2; 633 break; 634 default: 635 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 636 bind[i].buffer = (void*)values[j]; 637 bind[i].buffer_length = strlen(values[j]); 638 break; 639 } 640 } 641 } 642 643 return; 644} 645 646static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql, 647 int *nrows, apr_dbd_prepared_t *statement, 648 MYSQL_BIND *bind) 649{ 650 int ret; 651 652 ret = mysql_stmt_bind_param(statement->stmt, bind); 653 if (ret != 0) { 654 *nrows = 0; 655 ret = mysql_stmt_errno(statement->stmt); 656 } 657 else { 658 ret = mysql_stmt_execute(statement->stmt); 659 if (ret != 0) { 660 ret = mysql_stmt_errno(statement->stmt); 661 } 662 *nrows = (int) mysql_stmt_affected_rows(statement->stmt); 663 } 664 665 return ret; 666} 667 668static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql, 669 int *nrows, apr_dbd_prepared_t *statement, 670 const char **values) 671{ 672 MYSQL_BIND *bind; 673 int ret; 674 675 if (sql->trans && sql->trans->errnum) { 676 return sql->trans->errnum; 677 } 678 679 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 680 681 dbd_mysql_bind(statement, values, bind); 682 683 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind); 684 685 if (TXN_NOTICE_ERRORS(sql->trans)) { 686 sql->trans->errnum = ret; 687 } 688 return ret; 689} 690 691static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 692 apr_dbd_prepared_t *statement, va_list args) 693{ 694 const char **values; 695 int i; 696 697 if (sql->trans && sql->trans->errnum) { 698 return sql->trans->errnum; 699 } 700 701 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 702 703 for (i = 0; i < statement->nvals; i++) { 704 values[i] = va_arg(args, const char*); 705 } 706 707 return dbd_mysql_pquery(pool, sql, nrows, statement, values); 708} 709 710static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql, 711 apr_dbd_results_t **res, 712 apr_dbd_prepared_t *statement, 713 int random, MYSQL_BIND *bind) 714{ 715 int nfields, i; 716 my_bool *is_nullr; 717#if MYSQL_VERSION_ID >= 50000 718 my_bool *error; 719#endif 720 int ret; 721 unsigned long *length, maxlen; 722 723 ret = mysql_stmt_bind_param(statement->stmt, bind); 724 if (ret == 0) { 725 ret = mysql_stmt_execute(statement->stmt); 726 if (!ret) { 727 if (!*res) { 728 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 729 } 730 (*res)->random = random; 731 (*res)->statement = statement->stmt; 732 (*res)->res = mysql_stmt_result_metadata(statement->stmt); 733 (*res)->pool = pool; 734 apr_pool_cleanup_register(pool, (*res)->res, 735 free_result, apr_pool_cleanup_null); 736 nfields = mysql_num_fields((*res)->res); 737 if (!(*res)->bind) { 738 (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND)); 739 length = apr_pcalloc(pool, nfields*sizeof(unsigned long)); 740#if MYSQL_VERSION_ID >= 50000 741 error = apr_palloc(pool, nfields*sizeof(my_bool)); 742#endif 743 is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool)); 744 for ( i = 0; i < nfields; ++i ) { 745 maxlen = ((*res)->res->fields[i].length < sql->fldsz ? 746 (*res)->res->fields[i].length : sql->fldsz) + 1; 747 if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) { 748 (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 749 } 750 else { 751 (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 752 } 753 (*res)->bind[i].buffer_length = maxlen; 754 (*res)->bind[i].length = &length[i]; 755 (*res)->bind[i].buffer = apr_palloc(pool, maxlen); 756 (*res)->bind[i].is_null = is_nullr+i; 757#if MYSQL_VERSION_ID >= 50000 758 (*res)->bind[i].error = error+i; 759#endif 760 } 761 } 762 ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind); 763 if (!ret) { 764 ret = mysql_stmt_store_result(statement->stmt); 765 } 766 } 767 } 768 if (ret != 0) { 769 ret = mysql_stmt_errno(statement->stmt); 770 } 771 772 return ret; 773} 774 775static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql, 776 apr_dbd_results_t **res, 777 apr_dbd_prepared_t *statement, int random, 778 const char **args) 779{ 780 int ret; 781 MYSQL_BIND *bind; 782 783 if (sql->trans && sql->trans->errnum) { 784 return sql->trans->errnum; 785 } 786 787 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 788 789 dbd_mysql_bind(statement, args, bind); 790 791 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind); 792 793 if (TXN_NOTICE_ERRORS(sql->trans)) { 794 sql->trans->errnum = ret; 795 } 796 return ret; 797} 798 799static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql, 800 apr_dbd_results_t **res, 801 apr_dbd_prepared_t *statement, int random, 802 va_list args) 803{ 804 const char **values; 805 int i; 806 807 if (sql->trans && sql->trans->errnum) { 808 return sql->trans->errnum; 809 } 810 811 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 812 813 for (i = 0; i < statement->nvals; i++) { 814 values[i] = va_arg(args, const char*); 815 } 816 817 return dbd_mysql_pselect(pool, sql, res, statement, random, values); 818} 819 820static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement, 821 const void **values, MYSQL_BIND *bind) 822{ 823 void *arg; 824 int i, j; 825 apr_dbd_type_e type; 826 827 for (i = 0, j = 0; i < statement->nargs; i++, j++) { 828 arg = (void *)values[j]; 829 830 bind[i].length = &bind[i].buffer_length; 831 bind[i].is_null = NULL; 832 833 type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]); 834 switch (type) { 835 case APR_DBD_TYPE_TINY: 836 bind[i].buffer = arg; 837 bind[i].buffer_type = MYSQL_TYPE_TINY; 838 bind[i].is_unsigned = 0; 839 break; 840 case APR_DBD_TYPE_UTINY: 841 bind[i].buffer = arg; 842 bind[i].buffer_type = MYSQL_TYPE_TINY; 843 bind[i].is_unsigned = 1; 844 break; 845 case APR_DBD_TYPE_SHORT: 846 bind[i].buffer = arg; 847 bind[i].buffer_type = MYSQL_TYPE_SHORT; 848 bind[i].is_unsigned = 0; 849 break; 850 case APR_DBD_TYPE_USHORT: 851 bind[i].buffer = arg; 852 bind[i].buffer_type = MYSQL_TYPE_SHORT; 853 bind[i].is_unsigned = 1; 854 break; 855 case APR_DBD_TYPE_INT: 856 bind[i].buffer = arg; 857 bind[i].buffer_type = MYSQL_TYPE_LONG; 858 bind[i].is_unsigned = 0; 859 break; 860 case APR_DBD_TYPE_UINT: 861 bind[i].buffer = arg; 862 bind[i].buffer_type = MYSQL_TYPE_LONG; 863 bind[i].is_unsigned = 1; 864 break; 865 case APR_DBD_TYPE_LONG: 866 if (sizeof(int) == sizeof(long)) { 867 bind[i].buffer = arg; 868 } 869 else { 870 bind[i].buffer = apr_palloc(pool, sizeof(int)); 871 *(int*)bind[i].buffer = *(long*)arg; 872 } 873 bind[i].buffer_type = MYSQL_TYPE_LONG; 874 bind[i].is_unsigned = 0; 875 break; 876 case APR_DBD_TYPE_ULONG: 877 if (sizeof(unsigned int) == sizeof(unsigned long)) { 878 bind[i].buffer = arg; 879 } 880 else { 881 bind[i].buffer = apr_palloc(pool, sizeof(unsigned int)); 882 *(unsigned int*)bind[i].buffer = *(unsigned long*)arg; 883 } 884 bind[i].buffer_type = MYSQL_TYPE_LONG; 885 bind[i].is_unsigned = 1; 886 break; 887 case APR_DBD_TYPE_LONGLONG: 888 if (sizeof(my_ulonglong) == sizeof(apr_int64_t)) { 889 bind[i].buffer = arg; 890 bind[i].buffer_type = MYSQL_TYPE_LONGLONG; 891 } 892 else { /* have to downsize, long long is not portable */ 893 bind[i].buffer = apr_palloc(pool, sizeof(long)); 894 *(long*)bind[i].buffer = (long) *(apr_int64_t*)arg; 895 bind[i].buffer_type = MYSQL_TYPE_LONG; 896 } 897 bind[i].is_unsigned = 0; 898 break; 899 case APR_DBD_TYPE_ULONGLONG: 900 if (sizeof(my_ulonglong) == sizeof(apr_uint64_t)) { 901 bind[i].buffer = arg; 902 bind[i].buffer_type = MYSQL_TYPE_LONGLONG; 903 } 904 else { /* have to downsize, long long is not portable */ 905 bind[i].buffer = apr_palloc(pool, sizeof(long)); 906 *(unsigned long*)bind[i].buffer = 907 (unsigned long) *(apr_uint64_t*)arg; 908 bind[i].buffer_type = MYSQL_TYPE_LONG; 909 } 910 bind[i].is_unsigned = 1; 911 break; 912 case APR_DBD_TYPE_FLOAT: 913 bind[i].buffer = arg; 914 bind[i].buffer_type = MYSQL_TYPE_FLOAT; 915 bind[i].is_unsigned = 0; 916 break; 917 case APR_DBD_TYPE_DOUBLE: 918 bind[i].buffer = arg; 919 bind[i].buffer_type = MYSQL_TYPE_DOUBLE; 920 bind[i].is_unsigned = 0; 921 break; 922 case APR_DBD_TYPE_STRING: 923 case APR_DBD_TYPE_TEXT: 924 case APR_DBD_TYPE_TIME: 925 case APR_DBD_TYPE_DATE: 926 case APR_DBD_TYPE_DATETIME: 927 case APR_DBD_TYPE_TIMESTAMP: 928 case APR_DBD_TYPE_ZTIMESTAMP: 929 bind[i].buffer = arg; 930 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 931 bind[i].is_unsigned = 0; 932 bind[i].buffer_length = strlen((const char *)arg); 933 break; 934 case APR_DBD_TYPE_BLOB: 935 case APR_DBD_TYPE_CLOB: 936 bind[i].buffer = (void *)arg; 937 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 938 bind[i].is_unsigned = 0; 939 bind[i].buffer_length = *(apr_size_t*)values[++j]; 940 941 /* skip table and column */ 942 j += 2; 943 break; 944 case APR_DBD_TYPE_NULL: 945 default: 946 bind[i].buffer_type = MYSQL_TYPE_NULL; 947 break; 948 } 949 } 950 951 return; 952} 953 954static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql, 955 int *nrows, apr_dbd_prepared_t *statement, 956 const void **values) 957{ 958 MYSQL_BIND *bind; 959 int ret; 960 961 if (sql->trans && sql->trans->errnum) { 962 return sql->trans->errnum; 963 } 964 965 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 966 967 dbd_mysql_bbind(pool, statement, values, bind); 968 969 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind); 970 971 if (TXN_NOTICE_ERRORS(sql->trans)) { 972 sql->trans->errnum = ret; 973 } 974 return ret; 975} 976 977static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 978 apr_dbd_prepared_t *statement, va_list args) 979{ 980 const void **values; 981 int i; 982 983 if (sql->trans && sql->trans->errnum) { 984 return sql->trans->errnum; 985 } 986 987 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 988 989 for (i = 0; i < statement->nvals; i++) { 990 values[i] = va_arg(args, const void*); 991 } 992 993 return dbd_mysql_pbquery(pool, sql, nrows, statement, values); 994} 995 996static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql, 997 apr_dbd_results_t **res, 998 apr_dbd_prepared_t *statement, int random, 999 const void **args) 1000{ 1001 int ret; 1002 MYSQL_BIND *bind; 1003 1004 if (sql->trans && sql->trans->errnum) { 1005 return sql->trans->errnum; 1006 } 1007 1008 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 1009 1010 dbd_mysql_bbind(pool, statement, args, bind); 1011 1012 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind); 1013 1014 if (TXN_NOTICE_ERRORS(sql->trans)) { 1015 sql->trans->errnum = ret; 1016 } 1017 return ret; 1018} 1019 1020static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql, 1021 apr_dbd_results_t **res, 1022 apr_dbd_prepared_t *statement, int random, 1023 va_list args) 1024{ 1025 const void **values; 1026 int i; 1027 1028 if (sql->trans && sql->trans->errnum) { 1029 return sql->trans->errnum; 1030 } 1031 1032 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1033 1034 for (i = 0; i < statement->nvals; i++) { 1035 values[i] = va_arg(args, const void*); 1036 } 1037 1038 return dbd_mysql_pbselect(pool, sql, res, statement, random, values); 1039} 1040 1041static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans) 1042{ 1043 int ret = -1; 1044 if (trans) { 1045 /* rollback on error or explicit rollback request */ 1046 if (trans->errnum || TXN_DO_ROLLBACK(trans)) { 1047 trans->errnum = 0; 1048 ret = mysql_rollback(trans->handle->conn); 1049 } 1050 else { 1051 ret = mysql_commit(trans->handle->conn); 1052 }
| 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 19#if APU_HAVE_MYSQL 20 21#include "apu_version.h" 22#include "apu_config.h" 23 24#include <ctype.h> 25#include <stdlib.h> 26 27#if defined(HAVE_MYSQL_MYSQL_H) 28#if defined(HAVE_MYSQL_MY_GLOBAL_H) 29#include <mysql/my_global.h> 30#if defined(HAVE_MYSQL_MY_SYS_H) 31#include <mysql/my_sys.h> 32#endif 33#endif 34#include <mysql/mysql.h> 35#include <mysql/errmsg.h> 36#else /* !defined(HAVE_MYSQL_MYSQL_H) */ 37#if defined(HAVE_MY_GLOBAL_H) 38#include <my_global.h> 39#if defined(HAVE_MY_SYS_H) 40#include <my_sys.h> 41#endif 42#endif 43#include <mysql.h> 44#include <errmsg.h> 45#endif 46 47#include "apr_strings.h" 48#include "apr_lib.h" 49#include "apr_buckets.h" 50 51#include "apr_dbd_internal.h" 52 53/* default maximum field size 1 MB */ 54#define FIELDSIZE 1048575 55 56struct apr_dbd_prepared_t { 57 MYSQL_STMT* stmt; 58 int nargs; 59 int nvals; 60 apr_dbd_type_e *types; 61}; 62 63struct apr_dbd_transaction_t { 64 int mode; 65 int errnum; 66 apr_dbd_t *handle; 67}; 68 69struct apr_dbd_t { 70 MYSQL* conn ; 71 apr_dbd_transaction_t* trans ; 72 unsigned long fldsz; 73}; 74 75struct apr_dbd_results_t { 76 int random; 77 MYSQL_RES *res; 78 MYSQL_STMT *statement; 79 MYSQL_BIND *bind; 80 apr_pool_t *pool; 81}; 82struct apr_dbd_row_t { 83 MYSQL_ROW row; 84 apr_dbd_results_t *res; 85 unsigned long *len; 86}; 87 88/* MySQL specific bucket for BLOB types */ 89typedef struct apr_bucket_lob apr_bucket_lob; 90/** 91 * A bucket referring to a MySQL BLOB 92 */ 93struct apr_bucket_lob { 94 /** Number of buckets using this memory */ 95 apr_bucket_refcount refcount; 96 /** The row this bucket refers to */ 97 const apr_dbd_row_t *row; 98 /** The column this bucket refers to */ 99 int col; 100 /** The pool into which any needed structures should 101 * be created while reading from this bucket */ 102 apr_pool_t *readpool; 103}; 104 105static void lob_bucket_destroy(void *data); 106static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, 107 apr_size_t *len, apr_read_type_e block); 108static apr_bucket *apr_bucket_lob_make(apr_bucket *b, 109 const apr_dbd_row_t *row, int col, 110 apr_off_t offset, apr_size_t len, 111 apr_pool_t *p); 112static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, 113 apr_off_t offset, 114 apr_size_t len, apr_pool_t *p, 115 apr_bucket_alloc_t *list); 116static int dbd_mysql_num_cols(apr_dbd_results_t *res); 117 118static const apr_bucket_type_t apr_bucket_type_lob = { 119 "LOB", 5, APR_BUCKET_DATA, 120 lob_bucket_destroy, 121 lob_bucket_read, 122 apr_bucket_setaside_notimpl, 123 apr_bucket_shared_split, 124 apr_bucket_shared_copy 125}; 126 127static void lob_bucket_destroy(void *data) 128{ 129 apr_bucket_lob *f = data; 130 131 if (apr_bucket_shared_destroy(f)) { 132 /* no need to destroy database objects here; it will get 133 * done automatically when the pool gets cleaned up */ 134 apr_bucket_free(f); 135 } 136} 137 138static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, 139 apr_size_t *len, apr_read_type_e block) 140{ 141 apr_bucket_lob *a = e->data; 142 const apr_dbd_row_t *row = a->row; 143 apr_dbd_results_t *res = row->res; 144 int col = a->col; 145 apr_bucket *b = NULL; 146 int rv; 147 apr_size_t blength = e->length; /* bytes remaining in file past offset */ 148 apr_off_t boffset = e->start; 149 MYSQL_BIND *bind = &res->bind[col]; 150 151 *str = NULL; /* in case we die prematurely */ 152 153 /* fetch from offset if not at the beginning */ 154 if (boffset > 0) { 155 rv = mysql_stmt_fetch_column(res->statement, bind, col, 156 (unsigned long) boffset); 157 if (rv != 0) { 158 return APR_EGENERAL; 159 } 160 } 161 blength -= blength > bind->buffer_length ? bind->buffer_length : blength; 162 *len = e->length - blength; 163 *str = bind->buffer; 164 165 /* allocate new buffer, since we used this one for the bucket */ 166 bind->buffer = apr_palloc(res->pool, bind->buffer_length); 167 168 /* 169 * Change the current bucket to refer to what we read, 170 * even if we read nothing because we hit EOF. 171 */ 172 apr_bucket_pool_make(e, *str, *len, res->pool); 173 174 /* If we have more to read from the field, then create another bucket */ 175 if (blength > 0) { 176 /* for efficiency, we can just build a new apr_bucket struct 177 * to wrap around the existing LOB bucket */ 178 b = apr_bucket_alloc(sizeof(*b), e->list); 179 b->start = boffset + *len; 180 b->length = blength; 181 b->data = a; 182 b->type = &apr_bucket_type_lob; 183 b->free = apr_bucket_free; 184 b->list = e->list; 185 APR_BUCKET_INSERT_AFTER(e, b); 186 } 187 else { 188 lob_bucket_destroy(a); 189 } 190 191 return APR_SUCCESS; 192} 193 194static apr_bucket *apr_bucket_lob_make(apr_bucket *b, 195 const apr_dbd_row_t *row, int col, 196 apr_off_t offset, apr_size_t len, 197 apr_pool_t *p) 198{ 199 apr_bucket_lob *f; 200 201 f = apr_bucket_alloc(sizeof(*f), b->list); 202 f->row = row; 203 f->col = col; 204 f->readpool = p; 205 206 b = apr_bucket_shared_make(b, f, offset, len); 207 b->type = &apr_bucket_type_lob; 208 209 return b; 210} 211 212static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, 213 apr_off_t offset, 214 apr_size_t len, apr_pool_t *p, 215 apr_bucket_alloc_t *list) 216{ 217 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); 218 219 APR_BUCKET_INIT(b); 220 b->free = apr_bucket_free; 221 b->list = list; 222 return apr_bucket_lob_make(b, row, col, offset, len, p); 223} 224 225static apr_status_t free_result(void *data) 226{ 227 mysql_free_result(data); 228 return APR_SUCCESS; 229} 230 231static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql, 232 apr_dbd_results_t **results, 233 const char *query, int seek) 234{ 235 int sz; 236 int ret; 237 if (sql->trans && sql->trans->errnum) { 238 return sql->trans->errnum; 239 } 240 ret = mysql_query(sql->conn, query); 241 if (!ret) { 242 if (sz = mysql_field_count(sql->conn), sz > 0) { 243 if (!*results) { 244 *results = apr_palloc(pool, sizeof(apr_dbd_results_t)); 245 } 246 (*results)->random = seek; 247 (*results)->statement = NULL; 248 (*results)->pool = pool; 249 if (seek) { 250 (*results)->res = mysql_store_result(sql->conn); 251 } 252 else { 253 (*results)->res = mysql_use_result(sql->conn); 254 } 255 apr_pool_cleanup_register(pool, (*results)->res, 256 free_result,apr_pool_cleanup_null); 257 } 258 } else { 259 ret = mysql_errno(sql->conn); 260 } 261 262 if (TXN_NOTICE_ERRORS(sql->trans)) { 263 sql->trans->errnum = ret; 264 } 265 return ret; 266} 267 268static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n) 269{ 270 if ((n < 0) || (n >= (int) mysql_num_fields(res->res))) { 271 return NULL; 272 } 273 274 return mysql_fetch_fields(res->res)[n].name; 275} 276 277static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res, 278 apr_dbd_row_t **row, int rownum) 279{ 280 MYSQL_ROW r = NULL; 281 int ret = 0; 282 283 if (res->statement) { 284 if (res->random) { 285 if (rownum > 0) { 286 mysql_stmt_data_seek(res->statement, (my_ulonglong) --rownum); 287 } 288 else { 289 return -1; /* invalid row */ 290 } 291 } 292 ret = mysql_stmt_fetch(res->statement); 293 switch (ret) { 294 case 1: 295 ret = mysql_stmt_errno(res->statement); 296 break; 297 case MYSQL_NO_DATA: 298 ret = -1; 299 break; 300 default: 301 ret = 0; /* bad luck - get_entry will deal with this */ 302 break; 303 } 304 } 305 else { 306 if (res->random) { 307 if (rownum > 0) { 308 mysql_data_seek(res->res, (my_ulonglong) --rownum); 309 } 310 else { 311 return -1; /* invalid row */ 312 } 313 } 314 r = mysql_fetch_row(res->res); 315 if (r == NULL) { 316 ret = -1; 317 } 318 } 319 if (ret == 0) { 320 if (!*row) { 321 *row = apr_palloc(pool, sizeof(apr_dbd_row_t)); 322 } 323 (*row)->row = r; 324 (*row)->res = res; 325 (*row)->len = mysql_fetch_lengths(res->res); 326 } 327 else { 328 apr_pool_cleanup_run(res->pool, res->res, free_result); 329 } 330 return ret; 331} 332#if 0 333/* An improved API that was proposed but not followed up */ 334static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n, 335 apr_dbd_datum_t *val) 336{ 337 MYSQL_BIND *bind; 338 if (dbd_mysql_num_cols(row->res) <= n) { 339 return NULL; 340 } 341 if (row->res->statement) { 342 bind = &row->res->bind[n]; 343 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 344 val->type = APR_DBD_VALUE_NULL; 345 return -1; 346 } 347 if (*bind->is_null) { 348 val->type = APR_DBD_VALUE_NULL; 349 return -1; 350 } 351 else { 352 val->type = APR_DBD_VALUE_STRING; 353 val->value.stringval = bind->buffer; 354 } 355 } 356 else { 357 val->type = APR_DBD_VALUE_STRING; 358 val->value.stringval = row->row[n]; 359 } 360 return 0; 361} 362#else 363 364static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n) 365{ 366 MYSQL_BIND *bind; 367 if (dbd_mysql_num_cols(row->res) <= n) { 368 return NULL; 369 } 370 if (row->res->statement) { 371 bind = &row->res->bind[n]; 372 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 373 return NULL; 374 } 375 if (*bind->is_null) { 376 return NULL; 377 } 378 else { 379 return bind->buffer; 380 } 381 } 382 else { 383 return row->row[n]; 384 } 385 return NULL; 386} 387#endif 388 389static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n, 390 apr_dbd_type_e type, void *data) 391{ 392 if (row->res->statement) { 393 MYSQL_BIND *bind = &row->res->bind[n]; 394 unsigned long len = *bind->length; 395 396 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 397 return APR_EGENERAL; 398 } 399 400 if (*bind->is_null) { 401 return APR_ENOENT; 402 } 403 404 switch (type) { 405 case APR_DBD_TYPE_TINY: 406 *(char*)data = atoi(bind->buffer); 407 break; 408 case APR_DBD_TYPE_UTINY: 409 *(unsigned char*)data = atoi(bind->buffer); 410 break; 411 case APR_DBD_TYPE_SHORT: 412 *(short*)data = atoi(bind->buffer); 413 break; 414 case APR_DBD_TYPE_USHORT: 415 *(unsigned short*)data = atoi(bind->buffer); 416 break; 417 case APR_DBD_TYPE_INT: 418 *(int*)data = atoi(bind->buffer); 419 break; 420 case APR_DBD_TYPE_UINT: 421 *(unsigned int*)data = atoi(bind->buffer); 422 break; 423 case APR_DBD_TYPE_LONG: 424 *(long*)data = atol(bind->buffer); 425 break; 426 case APR_DBD_TYPE_ULONG: 427 *(unsigned long*)data = atol(bind->buffer); 428 break; 429 case APR_DBD_TYPE_LONGLONG: 430 *(apr_int64_t*)data = apr_atoi64(bind->buffer); 431 break; 432 case APR_DBD_TYPE_ULONGLONG: 433 *(apr_uint64_t*)data = apr_atoi64(bind->buffer); 434 break; 435 case APR_DBD_TYPE_FLOAT: 436 *(float*)data = (float) atof(bind->buffer); 437 break; 438 case APR_DBD_TYPE_DOUBLE: 439 *(double*)data = atof(bind->buffer); 440 break; 441 case APR_DBD_TYPE_STRING: 442 case APR_DBD_TYPE_TEXT: 443 case APR_DBD_TYPE_TIME: 444 case APR_DBD_TYPE_DATE: 445 case APR_DBD_TYPE_DATETIME: 446 case APR_DBD_TYPE_TIMESTAMP: 447 case APR_DBD_TYPE_ZTIMESTAMP: 448 *((char*)bind->buffer+bind->buffer_length-1) = '\0'; 449 *(char**)data = bind->buffer; 450 break; 451 case APR_DBD_TYPE_BLOB: 452 case APR_DBD_TYPE_CLOB: 453 { 454 apr_bucket *e; 455 apr_bucket_brigade *b = (apr_bucket_brigade*)data; 456 457 e = apr_bucket_lob_create(row, n, 0, len, 458 row->res->pool, b->bucket_alloc); 459 APR_BRIGADE_INSERT_TAIL(b, e); 460 } 461 break; 462 case APR_DBD_TYPE_NULL: 463 *(void**)data = NULL; 464 break; 465 default: 466 return APR_EGENERAL; 467 } 468 } 469 else { 470 if (row->row[n] == NULL) { 471 return APR_ENOENT; 472 } 473 474 switch (type) { 475 case APR_DBD_TYPE_TINY: 476 *(char*)data = atoi(row->row[n]); 477 break; 478 case APR_DBD_TYPE_UTINY: 479 *(unsigned char*)data = atoi(row->row[n]); 480 break; 481 case APR_DBD_TYPE_SHORT: 482 *(short*)data = atoi(row->row[n]); 483 break; 484 case APR_DBD_TYPE_USHORT: 485 *(unsigned short*)data = atoi(row->row[n]); 486 break; 487 case APR_DBD_TYPE_INT: 488 *(int*)data = atoi(row->row[n]); 489 break; 490 case APR_DBD_TYPE_UINT: 491 *(unsigned int*)data = atoi(row->row[n]); 492 break; 493 case APR_DBD_TYPE_LONG: 494 *(long*)data = atol(row->row[n]); 495 break; 496 case APR_DBD_TYPE_ULONG: 497 *(unsigned long*)data = atol(row->row[n]); 498 break; 499 case APR_DBD_TYPE_LONGLONG: 500 *(apr_int64_t*)data = apr_atoi64(row->row[n]); 501 break; 502 case APR_DBD_TYPE_ULONGLONG: 503 *(apr_uint64_t*)data = apr_atoi64(row->row[n]); 504 break; 505 case APR_DBD_TYPE_FLOAT: 506 *(float*)data = (float) atof(row->row[n]); 507 break; 508 case APR_DBD_TYPE_DOUBLE: 509 *(double*)data = atof(row->row[n]); 510 break; 511 case APR_DBD_TYPE_STRING: 512 case APR_DBD_TYPE_TEXT: 513 case APR_DBD_TYPE_TIME: 514 case APR_DBD_TYPE_DATE: 515 case APR_DBD_TYPE_DATETIME: 516 case APR_DBD_TYPE_TIMESTAMP: 517 case APR_DBD_TYPE_ZTIMESTAMP: 518 *(char**)data = row->row[n]; 519 break; 520 case APR_DBD_TYPE_BLOB: 521 case APR_DBD_TYPE_CLOB: 522 { 523 apr_bucket *e; 524 apr_bucket_brigade *b = (apr_bucket_brigade*)data; 525 526 e = apr_bucket_pool_create(row->row[n], row->len[n], 527 row->res->pool, b->bucket_alloc); 528 APR_BRIGADE_INSERT_TAIL(b, e); 529 } 530 break; 531 case APR_DBD_TYPE_NULL: 532 *(void**)data = NULL; 533 break; 534 default: 535 return APR_EGENERAL; 536 } 537 } 538 return 0; 539} 540 541static const char *dbd_mysql_error(apr_dbd_t *sql, int n) 542{ 543 return mysql_error(sql->conn); 544} 545 546static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query) 547{ 548 int ret; 549 if (sql->trans && sql->trans->errnum) { 550 return sql->trans->errnum; 551 } 552 ret = mysql_query(sql->conn, query); 553 if (ret != 0) { 554 ret = mysql_errno(sql->conn); 555 } 556 *nrows = (int) mysql_affected_rows(sql->conn); 557 if (TXN_NOTICE_ERRORS(sql->trans)) { 558 sql->trans->errnum = ret; 559 } 560 return ret; 561} 562 563static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg, 564 apr_dbd_t *sql) 565{ 566 unsigned long len = strlen(arg); 567 char *ret = apr_palloc(pool, 2*len + 1); 568 mysql_real_escape_string(sql->conn, ret, arg, len); 569 return ret; 570} 571 572static apr_status_t stmt_close(void *data) 573{ 574 mysql_stmt_close(data); 575 return APR_SUCCESS; 576} 577 578static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql, 579 const char *query, const char *label, 580 int nargs, int nvals, apr_dbd_type_e *types, 581 apr_dbd_prepared_t **statement) 582{ 583 /* Translate from apr_dbd to native query format */ 584 int ret; 585 586 if (!*statement) { 587 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t)); 588 } 589 (*statement)->stmt = mysql_stmt_init(sql->conn); 590 591 if ((*statement)->stmt) { 592 apr_pool_cleanup_register(pool, (*statement)->stmt, 593 stmt_close, apr_pool_cleanup_null); 594 ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query)); 595 596 if (ret != 0) { 597 ret = mysql_stmt_errno((*statement)->stmt); 598 } 599 600 (*statement)->nargs = nargs; 601 (*statement)->nvals = nvals; 602 (*statement)->types = types; 603 604 return ret; 605 } 606 607 return CR_OUT_OF_MEMORY; 608} 609 610static void dbd_mysql_bind(apr_dbd_prepared_t *statement, 611 const char **values, MYSQL_BIND *bind) 612{ 613 int i, j; 614 615 for (i = 0, j = 0; i < statement->nargs; i++, j++) { 616 bind[i].length = &bind[i].buffer_length; 617 bind[i].is_unsigned = 0; 618 bind[i].is_null = NULL; 619 620 if (values[j] == NULL) { 621 bind[i].buffer_type = MYSQL_TYPE_NULL; 622 } 623 else { 624 switch (statement->types[i]) { 625 case APR_DBD_TYPE_BLOB: 626 case APR_DBD_TYPE_CLOB: 627 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 628 bind[i].buffer = (void*)values[j]; 629 bind[i].buffer_length = atol(values[++j]); 630 631 /* skip table and column */ 632 j += 2; 633 break; 634 default: 635 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 636 bind[i].buffer = (void*)values[j]; 637 bind[i].buffer_length = strlen(values[j]); 638 break; 639 } 640 } 641 } 642 643 return; 644} 645 646static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql, 647 int *nrows, apr_dbd_prepared_t *statement, 648 MYSQL_BIND *bind) 649{ 650 int ret; 651 652 ret = mysql_stmt_bind_param(statement->stmt, bind); 653 if (ret != 0) { 654 *nrows = 0; 655 ret = mysql_stmt_errno(statement->stmt); 656 } 657 else { 658 ret = mysql_stmt_execute(statement->stmt); 659 if (ret != 0) { 660 ret = mysql_stmt_errno(statement->stmt); 661 } 662 *nrows = (int) mysql_stmt_affected_rows(statement->stmt); 663 } 664 665 return ret; 666} 667 668static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql, 669 int *nrows, apr_dbd_prepared_t *statement, 670 const char **values) 671{ 672 MYSQL_BIND *bind; 673 int ret; 674 675 if (sql->trans && sql->trans->errnum) { 676 return sql->trans->errnum; 677 } 678 679 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 680 681 dbd_mysql_bind(statement, values, bind); 682 683 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind); 684 685 if (TXN_NOTICE_ERRORS(sql->trans)) { 686 sql->trans->errnum = ret; 687 } 688 return ret; 689} 690 691static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 692 apr_dbd_prepared_t *statement, va_list args) 693{ 694 const char **values; 695 int i; 696 697 if (sql->trans && sql->trans->errnum) { 698 return sql->trans->errnum; 699 } 700 701 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 702 703 for (i = 0; i < statement->nvals; i++) { 704 values[i] = va_arg(args, const char*); 705 } 706 707 return dbd_mysql_pquery(pool, sql, nrows, statement, values); 708} 709 710static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql, 711 apr_dbd_results_t **res, 712 apr_dbd_prepared_t *statement, 713 int random, MYSQL_BIND *bind) 714{ 715 int nfields, i; 716 my_bool *is_nullr; 717#if MYSQL_VERSION_ID >= 50000 718 my_bool *error; 719#endif 720 int ret; 721 unsigned long *length, maxlen; 722 723 ret = mysql_stmt_bind_param(statement->stmt, bind); 724 if (ret == 0) { 725 ret = mysql_stmt_execute(statement->stmt); 726 if (!ret) { 727 if (!*res) { 728 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 729 } 730 (*res)->random = random; 731 (*res)->statement = statement->stmt; 732 (*res)->res = mysql_stmt_result_metadata(statement->stmt); 733 (*res)->pool = pool; 734 apr_pool_cleanup_register(pool, (*res)->res, 735 free_result, apr_pool_cleanup_null); 736 nfields = mysql_num_fields((*res)->res); 737 if (!(*res)->bind) { 738 (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND)); 739 length = apr_pcalloc(pool, nfields*sizeof(unsigned long)); 740#if MYSQL_VERSION_ID >= 50000 741 error = apr_palloc(pool, nfields*sizeof(my_bool)); 742#endif 743 is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool)); 744 for ( i = 0; i < nfields; ++i ) { 745 maxlen = ((*res)->res->fields[i].length < sql->fldsz ? 746 (*res)->res->fields[i].length : sql->fldsz) + 1; 747 if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) { 748 (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 749 } 750 else { 751 (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 752 } 753 (*res)->bind[i].buffer_length = maxlen; 754 (*res)->bind[i].length = &length[i]; 755 (*res)->bind[i].buffer = apr_palloc(pool, maxlen); 756 (*res)->bind[i].is_null = is_nullr+i; 757#if MYSQL_VERSION_ID >= 50000 758 (*res)->bind[i].error = error+i; 759#endif 760 } 761 } 762 ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind); 763 if (!ret) { 764 ret = mysql_stmt_store_result(statement->stmt); 765 } 766 } 767 } 768 if (ret != 0) { 769 ret = mysql_stmt_errno(statement->stmt); 770 } 771 772 return ret; 773} 774 775static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql, 776 apr_dbd_results_t **res, 777 apr_dbd_prepared_t *statement, int random, 778 const char **args) 779{ 780 int ret; 781 MYSQL_BIND *bind; 782 783 if (sql->trans && sql->trans->errnum) { 784 return sql->trans->errnum; 785 } 786 787 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 788 789 dbd_mysql_bind(statement, args, bind); 790 791 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind); 792 793 if (TXN_NOTICE_ERRORS(sql->trans)) { 794 sql->trans->errnum = ret; 795 } 796 return ret; 797} 798 799static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql, 800 apr_dbd_results_t **res, 801 apr_dbd_prepared_t *statement, int random, 802 va_list args) 803{ 804 const char **values; 805 int i; 806 807 if (sql->trans && sql->trans->errnum) { 808 return sql->trans->errnum; 809 } 810 811 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 812 813 for (i = 0; i < statement->nvals; i++) { 814 values[i] = va_arg(args, const char*); 815 } 816 817 return dbd_mysql_pselect(pool, sql, res, statement, random, values); 818} 819 820static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement, 821 const void **values, MYSQL_BIND *bind) 822{ 823 void *arg; 824 int i, j; 825 apr_dbd_type_e type; 826 827 for (i = 0, j = 0; i < statement->nargs; i++, j++) { 828 arg = (void *)values[j]; 829 830 bind[i].length = &bind[i].buffer_length; 831 bind[i].is_null = NULL; 832 833 type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]); 834 switch (type) { 835 case APR_DBD_TYPE_TINY: 836 bind[i].buffer = arg; 837 bind[i].buffer_type = MYSQL_TYPE_TINY; 838 bind[i].is_unsigned = 0; 839 break; 840 case APR_DBD_TYPE_UTINY: 841 bind[i].buffer = arg; 842 bind[i].buffer_type = MYSQL_TYPE_TINY; 843 bind[i].is_unsigned = 1; 844 break; 845 case APR_DBD_TYPE_SHORT: 846 bind[i].buffer = arg; 847 bind[i].buffer_type = MYSQL_TYPE_SHORT; 848 bind[i].is_unsigned = 0; 849 break; 850 case APR_DBD_TYPE_USHORT: 851 bind[i].buffer = arg; 852 bind[i].buffer_type = MYSQL_TYPE_SHORT; 853 bind[i].is_unsigned = 1; 854 break; 855 case APR_DBD_TYPE_INT: 856 bind[i].buffer = arg; 857 bind[i].buffer_type = MYSQL_TYPE_LONG; 858 bind[i].is_unsigned = 0; 859 break; 860 case APR_DBD_TYPE_UINT: 861 bind[i].buffer = arg; 862 bind[i].buffer_type = MYSQL_TYPE_LONG; 863 bind[i].is_unsigned = 1; 864 break; 865 case APR_DBD_TYPE_LONG: 866 if (sizeof(int) == sizeof(long)) { 867 bind[i].buffer = arg; 868 } 869 else { 870 bind[i].buffer = apr_palloc(pool, sizeof(int)); 871 *(int*)bind[i].buffer = *(long*)arg; 872 } 873 bind[i].buffer_type = MYSQL_TYPE_LONG; 874 bind[i].is_unsigned = 0; 875 break; 876 case APR_DBD_TYPE_ULONG: 877 if (sizeof(unsigned int) == sizeof(unsigned long)) { 878 bind[i].buffer = arg; 879 } 880 else { 881 bind[i].buffer = apr_palloc(pool, sizeof(unsigned int)); 882 *(unsigned int*)bind[i].buffer = *(unsigned long*)arg; 883 } 884 bind[i].buffer_type = MYSQL_TYPE_LONG; 885 bind[i].is_unsigned = 1; 886 break; 887 case APR_DBD_TYPE_LONGLONG: 888 if (sizeof(my_ulonglong) == sizeof(apr_int64_t)) { 889 bind[i].buffer = arg; 890 bind[i].buffer_type = MYSQL_TYPE_LONGLONG; 891 } 892 else { /* have to downsize, long long is not portable */ 893 bind[i].buffer = apr_palloc(pool, sizeof(long)); 894 *(long*)bind[i].buffer = (long) *(apr_int64_t*)arg; 895 bind[i].buffer_type = MYSQL_TYPE_LONG; 896 } 897 bind[i].is_unsigned = 0; 898 break; 899 case APR_DBD_TYPE_ULONGLONG: 900 if (sizeof(my_ulonglong) == sizeof(apr_uint64_t)) { 901 bind[i].buffer = arg; 902 bind[i].buffer_type = MYSQL_TYPE_LONGLONG; 903 } 904 else { /* have to downsize, long long is not portable */ 905 bind[i].buffer = apr_palloc(pool, sizeof(long)); 906 *(unsigned long*)bind[i].buffer = 907 (unsigned long) *(apr_uint64_t*)arg; 908 bind[i].buffer_type = MYSQL_TYPE_LONG; 909 } 910 bind[i].is_unsigned = 1; 911 break; 912 case APR_DBD_TYPE_FLOAT: 913 bind[i].buffer = arg; 914 bind[i].buffer_type = MYSQL_TYPE_FLOAT; 915 bind[i].is_unsigned = 0; 916 break; 917 case APR_DBD_TYPE_DOUBLE: 918 bind[i].buffer = arg; 919 bind[i].buffer_type = MYSQL_TYPE_DOUBLE; 920 bind[i].is_unsigned = 0; 921 break; 922 case APR_DBD_TYPE_STRING: 923 case APR_DBD_TYPE_TEXT: 924 case APR_DBD_TYPE_TIME: 925 case APR_DBD_TYPE_DATE: 926 case APR_DBD_TYPE_DATETIME: 927 case APR_DBD_TYPE_TIMESTAMP: 928 case APR_DBD_TYPE_ZTIMESTAMP: 929 bind[i].buffer = arg; 930 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 931 bind[i].is_unsigned = 0; 932 bind[i].buffer_length = strlen((const char *)arg); 933 break; 934 case APR_DBD_TYPE_BLOB: 935 case APR_DBD_TYPE_CLOB: 936 bind[i].buffer = (void *)arg; 937 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 938 bind[i].is_unsigned = 0; 939 bind[i].buffer_length = *(apr_size_t*)values[++j]; 940 941 /* skip table and column */ 942 j += 2; 943 break; 944 case APR_DBD_TYPE_NULL: 945 default: 946 bind[i].buffer_type = MYSQL_TYPE_NULL; 947 break; 948 } 949 } 950 951 return; 952} 953 954static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql, 955 int *nrows, apr_dbd_prepared_t *statement, 956 const void **values) 957{ 958 MYSQL_BIND *bind; 959 int ret; 960 961 if (sql->trans && sql->trans->errnum) { 962 return sql->trans->errnum; 963 } 964 965 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 966 967 dbd_mysql_bbind(pool, statement, values, bind); 968 969 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind); 970 971 if (TXN_NOTICE_ERRORS(sql->trans)) { 972 sql->trans->errnum = ret; 973 } 974 return ret; 975} 976 977static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 978 apr_dbd_prepared_t *statement, va_list args) 979{ 980 const void **values; 981 int i; 982 983 if (sql->trans && sql->trans->errnum) { 984 return sql->trans->errnum; 985 } 986 987 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 988 989 for (i = 0; i < statement->nvals; i++) { 990 values[i] = va_arg(args, const void*); 991 } 992 993 return dbd_mysql_pbquery(pool, sql, nrows, statement, values); 994} 995 996static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql, 997 apr_dbd_results_t **res, 998 apr_dbd_prepared_t *statement, int random, 999 const void **args) 1000{ 1001 int ret; 1002 MYSQL_BIND *bind; 1003 1004 if (sql->trans && sql->trans->errnum) { 1005 return sql->trans->errnum; 1006 } 1007 1008 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 1009 1010 dbd_mysql_bbind(pool, statement, args, bind); 1011 1012 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind); 1013 1014 if (TXN_NOTICE_ERRORS(sql->trans)) { 1015 sql->trans->errnum = ret; 1016 } 1017 return ret; 1018} 1019 1020static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql, 1021 apr_dbd_results_t **res, 1022 apr_dbd_prepared_t *statement, int random, 1023 va_list args) 1024{ 1025 const void **values; 1026 int i; 1027 1028 if (sql->trans && sql->trans->errnum) { 1029 return sql->trans->errnum; 1030 } 1031 1032 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1033 1034 for (i = 0; i < statement->nvals; i++) { 1035 values[i] = va_arg(args, const void*); 1036 } 1037 1038 return dbd_mysql_pbselect(pool, sql, res, statement, random, values); 1039} 1040 1041static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans) 1042{ 1043 int ret = -1; 1044 if (trans) { 1045 /* rollback on error or explicit rollback request */ 1046 if (trans->errnum || TXN_DO_ROLLBACK(trans)) { 1047 trans->errnum = 0; 1048 ret = mysql_rollback(trans->handle->conn); 1049 } 1050 else { 1051 ret = mysql_commit(trans->handle->conn); 1052 }
|
1056 return ret; 1057} 1058/* Whether or not transactions work depends on whether the 1059 * underlying DB supports them within MySQL. Unfortunately 1060 * it fails silently with the default InnoDB. 1061 */ 1062 1063static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle, 1064 apr_dbd_transaction_t **trans) 1065{ 1066 /* Don't try recursive transactions here */ 1067 if (handle->trans) { 1068 dbd_mysql_end_transaction(handle->trans) ; 1069 } 1070 if (!*trans) { 1071 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t)); 1072 } 1073 (*trans)->errnum = mysql_autocommit(handle->conn, 0); 1074 (*trans)->handle = handle; 1075 handle->trans = *trans; 1076 return (*trans)->errnum; 1077} 1078 1079static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans) 1080{ 1081 if (!trans) 1082 return APR_DBD_TRANSACTION_COMMIT; 1083 1084 return trans->mode; 1085} 1086 1087static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans, 1088 int mode) 1089{ 1090 if (!trans) 1091 return APR_DBD_TRANSACTION_COMMIT; 1092 1093 return trans->mode = (mode & TXN_MODE_BITS); 1094} 1095 1096static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params, 1097 const char **error) 1098{ 1099 static const char *const delims = " \r\n\t;|,"; 1100 const char *ptr; 1101 int i; 1102 const char *key; 1103 size_t klen; 1104 const char *value; 1105 size_t vlen; 1106#if MYSQL_VERSION_ID >= 50013 1107 my_bool do_reconnect = 1; 1108#endif 1109 MYSQL *real_conn; 1110 unsigned long flags = 0; 1111 1112 struct { 1113 const char *field; 1114 const char *value; 1115 } fields[] = { 1116 {"host", NULL}, 1117 {"user", NULL}, 1118 {"pass", NULL}, 1119 {"dbname", NULL}, 1120 {"port", NULL}, 1121 {"sock", NULL}, 1122 {"flags", NULL}, 1123 {"fldsz", NULL}, 1124 {"group", NULL}, 1125 {"reconnect", NULL}, 1126 {NULL, NULL} 1127 }; 1128 unsigned int port = 0; 1129 apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t)); 1130 sql->fldsz = FIELDSIZE; 1131 sql->conn = mysql_init(sql->conn); 1132 if ( sql->conn == NULL ) { 1133 return NULL; 1134 } 1135 for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) { 1136 /* don't dereference memory that may not belong to us */ 1137 if (ptr == params) { 1138 ++ptr; 1139 continue; 1140 } 1141 for (key = ptr-1; apr_isspace(*key); --key); 1142 klen = 0; 1143 while (apr_isalpha(*key)) { 1144 /* don't parse backwards off the start of the string */ 1145 if (key == params) { 1146 --key; 1147 ++klen; 1148 break; 1149 } 1150 --key; 1151 ++klen; 1152 } 1153 ++key; 1154 for (value = ptr+1; apr_isspace(*value); ++value); 1155 vlen = strcspn(value, delims); 1156 for (i = 0; fields[i].field != NULL; i++) { 1157 if (!strncasecmp(fields[i].field, key, klen)) { 1158 fields[i].value = apr_pstrndup(pool, value, vlen); 1159 break; 1160 } 1161 } 1162 ptr = value+vlen; 1163 } 1164 if (fields[4].value != NULL) { 1165 port = atoi(fields[4].value); 1166 } 1167 if (fields[6].value != NULL && 1168 !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) { 1169 flags |= CLIENT_FOUND_ROWS; /* only option we know */ 1170 } 1171 if (fields[7].value != NULL) { 1172 sql->fldsz = atol(fields[7].value); 1173 } 1174 if (fields[8].value != NULL) { 1175 mysql_options(sql->conn, MYSQL_READ_DEFAULT_GROUP, fields[8].value); 1176 } 1177#if MYSQL_VERSION_ID >= 50013 1178 if (fields[9].value != NULL) { 1179 do_reconnect = atoi(fields[9].value) ? 1 : 0; 1180 } 1181#endif 1182 1183#if MYSQL_VERSION_ID >= 50013 1184 /* the MySQL manual says this should be BEFORE mysql_real_connect */ 1185 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect); 1186#endif 1187 1188 real_conn = mysql_real_connect(sql->conn, fields[0].value, 1189 fields[1].value, fields[2].value, 1190 fields[3].value, port, 1191 fields[5].value, flags); 1192 1193 if(real_conn == NULL) { 1194 if (error) { 1195 *error = apr_pstrdup(pool, mysql_error(sql->conn)); 1196 } 1197 mysql_close(sql->conn); 1198 return NULL; 1199 } 1200 1201#if MYSQL_VERSION_ID >= 50013 1202 /* Some say this should be AFTER mysql_real_connect */ 1203 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect); 1204#endif 1205 1206 return sql; 1207} 1208 1209static apr_status_t dbd_mysql_close(apr_dbd_t *handle) 1210{ 1211 mysql_close(handle->conn); 1212 return APR_SUCCESS; 1213} 1214 1215static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool, 1216 apr_dbd_t *handle) 1217{ 1218 return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS; 1219} 1220 1221static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle, 1222 const char* name) 1223{ 1224 return mysql_select_db(handle->conn, name); 1225} 1226 1227static void *dbd_mysql_native(apr_dbd_t *handle) 1228{ 1229 return handle->conn; 1230} 1231 1232static int dbd_mysql_num_cols(apr_dbd_results_t *res) 1233{ 1234 if (res->statement) { 1235 return mysql_stmt_field_count(res->statement); 1236 } 1237 else { 1238 return mysql_num_fields(res->res); 1239 } 1240} 1241 1242static int dbd_mysql_num_tuples(apr_dbd_results_t *res) 1243{ 1244 if (res->random) { 1245 if (res->statement) { 1246 return (int) mysql_stmt_num_rows(res->statement); 1247 } 1248 else { 1249 return (int) mysql_num_rows(res->res); 1250 } 1251 } 1252 else { 1253 return -1; 1254 } 1255} 1256 1257static apr_status_t thread_end(void *data) 1258{ 1259 mysql_thread_end(); 1260 return APR_SUCCESS; 1261} 1262 1263static void dbd_mysql_init(apr_pool_t *pool) 1264{ 1265 my_init(); 1266 mysql_thread_init(); 1267 1268 /* FIXME: this is a guess; find out what it really does */ 1269 apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null); 1270} 1271APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = { 1272 "mysql", 1273 dbd_mysql_init, 1274 dbd_mysql_native, 1275 dbd_mysql_open, 1276 dbd_mysql_check_conn, 1277 dbd_mysql_close, 1278 dbd_mysql_select_db, 1279 dbd_mysql_transaction, 1280 dbd_mysql_end_transaction, 1281 dbd_mysql_query, 1282 dbd_mysql_select, 1283 dbd_mysql_num_cols, 1284 dbd_mysql_num_tuples, 1285 dbd_mysql_get_row, 1286 dbd_mysql_get_entry, 1287 dbd_mysql_error, 1288 dbd_mysql_escape, 1289 dbd_mysql_prepare, 1290 dbd_mysql_pvquery, 1291 dbd_mysql_pvselect, 1292 dbd_mysql_pquery, 1293 dbd_mysql_pselect, 1294 dbd_mysql_get_name, 1295 dbd_mysql_transaction_mode_get, 1296 dbd_mysql_transaction_mode_set, 1297 "?", 1298 dbd_mysql_pvbquery, 1299 dbd_mysql_pvbselect, 1300 dbd_mysql_pbquery, 1301 dbd_mysql_pbselect, 1302 dbd_mysql_datum_get 1303}; 1304 1305#endif
| 1056 return ret; 1057} 1058/* Whether or not transactions work depends on whether the 1059 * underlying DB supports them within MySQL. Unfortunately 1060 * it fails silently with the default InnoDB. 1061 */ 1062 1063static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle, 1064 apr_dbd_transaction_t **trans) 1065{ 1066 /* Don't try recursive transactions here */ 1067 if (handle->trans) { 1068 dbd_mysql_end_transaction(handle->trans) ; 1069 } 1070 if (!*trans) { 1071 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t)); 1072 } 1073 (*trans)->errnum = mysql_autocommit(handle->conn, 0); 1074 (*trans)->handle = handle; 1075 handle->trans = *trans; 1076 return (*trans)->errnum; 1077} 1078 1079static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans) 1080{ 1081 if (!trans) 1082 return APR_DBD_TRANSACTION_COMMIT; 1083 1084 return trans->mode; 1085} 1086 1087static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans, 1088 int mode) 1089{ 1090 if (!trans) 1091 return APR_DBD_TRANSACTION_COMMIT; 1092 1093 return trans->mode = (mode & TXN_MODE_BITS); 1094} 1095 1096static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params, 1097 const char **error) 1098{ 1099 static const char *const delims = " \r\n\t;|,"; 1100 const char *ptr; 1101 int i; 1102 const char *key; 1103 size_t klen; 1104 const char *value; 1105 size_t vlen; 1106#if MYSQL_VERSION_ID >= 50013 1107 my_bool do_reconnect = 1; 1108#endif 1109 MYSQL *real_conn; 1110 unsigned long flags = 0; 1111 1112 struct { 1113 const char *field; 1114 const char *value; 1115 } fields[] = { 1116 {"host", NULL}, 1117 {"user", NULL}, 1118 {"pass", NULL}, 1119 {"dbname", NULL}, 1120 {"port", NULL}, 1121 {"sock", NULL}, 1122 {"flags", NULL}, 1123 {"fldsz", NULL}, 1124 {"group", NULL}, 1125 {"reconnect", NULL}, 1126 {NULL, NULL} 1127 }; 1128 unsigned int port = 0; 1129 apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t)); 1130 sql->fldsz = FIELDSIZE; 1131 sql->conn = mysql_init(sql->conn); 1132 if ( sql->conn == NULL ) { 1133 return NULL; 1134 } 1135 for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) { 1136 /* don't dereference memory that may not belong to us */ 1137 if (ptr == params) { 1138 ++ptr; 1139 continue; 1140 } 1141 for (key = ptr-1; apr_isspace(*key); --key); 1142 klen = 0; 1143 while (apr_isalpha(*key)) { 1144 /* don't parse backwards off the start of the string */ 1145 if (key == params) { 1146 --key; 1147 ++klen; 1148 break; 1149 } 1150 --key; 1151 ++klen; 1152 } 1153 ++key; 1154 for (value = ptr+1; apr_isspace(*value); ++value); 1155 vlen = strcspn(value, delims); 1156 for (i = 0; fields[i].field != NULL; i++) { 1157 if (!strncasecmp(fields[i].field, key, klen)) { 1158 fields[i].value = apr_pstrndup(pool, value, vlen); 1159 break; 1160 } 1161 } 1162 ptr = value+vlen; 1163 } 1164 if (fields[4].value != NULL) { 1165 port = atoi(fields[4].value); 1166 } 1167 if (fields[6].value != NULL && 1168 !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) { 1169 flags |= CLIENT_FOUND_ROWS; /* only option we know */ 1170 } 1171 if (fields[7].value != NULL) { 1172 sql->fldsz = atol(fields[7].value); 1173 } 1174 if (fields[8].value != NULL) { 1175 mysql_options(sql->conn, MYSQL_READ_DEFAULT_GROUP, fields[8].value); 1176 } 1177#if MYSQL_VERSION_ID >= 50013 1178 if (fields[9].value != NULL) { 1179 do_reconnect = atoi(fields[9].value) ? 1 : 0; 1180 } 1181#endif 1182 1183#if MYSQL_VERSION_ID >= 50013 1184 /* the MySQL manual says this should be BEFORE mysql_real_connect */ 1185 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect); 1186#endif 1187 1188 real_conn = mysql_real_connect(sql->conn, fields[0].value, 1189 fields[1].value, fields[2].value, 1190 fields[3].value, port, 1191 fields[5].value, flags); 1192 1193 if(real_conn == NULL) { 1194 if (error) { 1195 *error = apr_pstrdup(pool, mysql_error(sql->conn)); 1196 } 1197 mysql_close(sql->conn); 1198 return NULL; 1199 } 1200 1201#if MYSQL_VERSION_ID >= 50013 1202 /* Some say this should be AFTER mysql_real_connect */ 1203 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect); 1204#endif 1205 1206 return sql; 1207} 1208 1209static apr_status_t dbd_mysql_close(apr_dbd_t *handle) 1210{ 1211 mysql_close(handle->conn); 1212 return APR_SUCCESS; 1213} 1214 1215static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool, 1216 apr_dbd_t *handle) 1217{ 1218 return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS; 1219} 1220 1221static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle, 1222 const char* name) 1223{ 1224 return mysql_select_db(handle->conn, name); 1225} 1226 1227static void *dbd_mysql_native(apr_dbd_t *handle) 1228{ 1229 return handle->conn; 1230} 1231 1232static int dbd_mysql_num_cols(apr_dbd_results_t *res) 1233{ 1234 if (res->statement) { 1235 return mysql_stmt_field_count(res->statement); 1236 } 1237 else { 1238 return mysql_num_fields(res->res); 1239 } 1240} 1241 1242static int dbd_mysql_num_tuples(apr_dbd_results_t *res) 1243{ 1244 if (res->random) { 1245 if (res->statement) { 1246 return (int) mysql_stmt_num_rows(res->statement); 1247 } 1248 else { 1249 return (int) mysql_num_rows(res->res); 1250 } 1251 } 1252 else { 1253 return -1; 1254 } 1255} 1256 1257static apr_status_t thread_end(void *data) 1258{ 1259 mysql_thread_end(); 1260 return APR_SUCCESS; 1261} 1262 1263static void dbd_mysql_init(apr_pool_t *pool) 1264{ 1265 my_init(); 1266 mysql_thread_init(); 1267 1268 /* FIXME: this is a guess; find out what it really does */ 1269 apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null); 1270} 1271APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = { 1272 "mysql", 1273 dbd_mysql_init, 1274 dbd_mysql_native, 1275 dbd_mysql_open, 1276 dbd_mysql_check_conn, 1277 dbd_mysql_close, 1278 dbd_mysql_select_db, 1279 dbd_mysql_transaction, 1280 dbd_mysql_end_transaction, 1281 dbd_mysql_query, 1282 dbd_mysql_select, 1283 dbd_mysql_num_cols, 1284 dbd_mysql_num_tuples, 1285 dbd_mysql_get_row, 1286 dbd_mysql_get_entry, 1287 dbd_mysql_error, 1288 dbd_mysql_escape, 1289 dbd_mysql_prepare, 1290 dbd_mysql_pvquery, 1291 dbd_mysql_pvselect, 1292 dbd_mysql_pquery, 1293 dbd_mysql_pselect, 1294 dbd_mysql_get_name, 1295 dbd_mysql_transaction_mode_get, 1296 dbd_mysql_transaction_mode_set, 1297 "?", 1298 dbd_mysql_pvbquery, 1299 dbd_mysql_pvbselect, 1300 dbd_mysql_pbquery, 1301 dbd_mysql_pbselect, 1302 dbd_mysql_datum_get 1303}; 1304 1305#endif
|