1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251876Speter * contributor license agreements. See the NOTICE file distributed with 3251876Speter * this work for additional information regarding copyright ownership. 4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251876Speter * (the "License"); you may not use this file except in compliance with 6251876Speter * the License. You may obtain a copy of the License at 7251876Speter * 8251876Speter * http://www.apache.org/licenses/LICENSE-2.0 9251876Speter * 10251876Speter * Unless required by applicable law or agreed to in writing, software 11251876Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251876Speter * See the License for the specific language governing permissions and 14251876Speter * limitations under the License. 15251876Speter */ 16251876Speter 17251876Speter#include "apu.h" 18251876Speter 19251876Speter#if APU_HAVE_MYSQL 20251876Speter 21251876Speter#include "apu_version.h" 22251876Speter#include "apu_config.h" 23251876Speter 24251876Speter#include <ctype.h> 25251876Speter#include <stdlib.h> 26251876Speter 27251876Speter#if defined(HAVE_MYSQL_MYSQL_H) 28251876Speter#if defined(HAVE_MYSQL_MY_GLOBAL_H) 29251876Speter#include <mysql/my_global.h> 30251876Speter#if defined(HAVE_MYSQL_MY_SYS_H) 31251876Speter#include <mysql/my_sys.h> 32251876Speter#endif 33251876Speter#endif 34251876Speter#include <mysql/mysql.h> 35251876Speter#include <mysql/errmsg.h> 36251876Speter#else /* !defined(HAVE_MYSQL_MYSQL_H) */ 37251876Speter#if defined(HAVE_MY_GLOBAL_H) 38251876Speter#include <my_global.h> 39251876Speter#if defined(HAVE_MY_SYS_H) 40251876Speter#include <my_sys.h> 41251876Speter#endif 42251876Speter#endif 43251876Speter#include <mysql.h> 44251876Speter#include <errmsg.h> 45251876Speter#endif 46251876Speter 47251876Speter#include "apr_strings.h" 48251876Speter#include "apr_lib.h" 49251876Speter#include "apr_buckets.h" 50251876Speter 51251876Speter#include "apr_dbd_internal.h" 52251876Speter 53251876Speter/* default maximum field size 1 MB */ 54251876Speter#define FIELDSIZE 1048575 55251876Speter 56251876Speterstruct apr_dbd_prepared_t { 57251876Speter MYSQL_STMT* stmt; 58251876Speter int nargs; 59251876Speter int nvals; 60251876Speter apr_dbd_type_e *types; 61251876Speter}; 62251876Speter 63251876Speterstruct apr_dbd_transaction_t { 64251876Speter int mode; 65251876Speter int errnum; 66251876Speter apr_dbd_t *handle; 67251876Speter}; 68251876Speter 69251876Speterstruct apr_dbd_t { 70251876Speter MYSQL* conn ; 71251876Speter apr_dbd_transaction_t* trans ; 72251876Speter unsigned long fldsz; 73251876Speter}; 74251876Speter 75251876Speterstruct apr_dbd_results_t { 76251876Speter int random; 77251876Speter MYSQL_RES *res; 78251876Speter MYSQL_STMT *statement; 79251876Speter MYSQL_BIND *bind; 80251876Speter apr_pool_t *pool; 81251876Speter}; 82251876Speterstruct apr_dbd_row_t { 83251876Speter MYSQL_ROW row; 84251876Speter apr_dbd_results_t *res; 85251876Speter unsigned long *len; 86251876Speter}; 87251876Speter 88251876Speter/* MySQL specific bucket for BLOB types */ 89251876Spetertypedef struct apr_bucket_lob apr_bucket_lob; 90251876Speter/** 91251876Speter * A bucket referring to a MySQL BLOB 92251876Speter */ 93251876Speterstruct apr_bucket_lob { 94251876Speter /** Number of buckets using this memory */ 95251876Speter apr_bucket_refcount refcount; 96251876Speter /** The row this bucket refers to */ 97251876Speter const apr_dbd_row_t *row; 98251876Speter /** The column this bucket refers to */ 99251876Speter int col; 100251876Speter /** The pool into which any needed structures should 101251876Speter * be created while reading from this bucket */ 102251876Speter apr_pool_t *readpool; 103251876Speter}; 104251876Speter 105251876Speterstatic void lob_bucket_destroy(void *data); 106251876Speterstatic apr_status_t lob_bucket_read(apr_bucket *e, const char **str, 107251876Speter apr_size_t *len, apr_read_type_e block); 108251876Speterstatic apr_bucket *apr_bucket_lob_make(apr_bucket *b, 109251876Speter const apr_dbd_row_t *row, int col, 110251876Speter apr_off_t offset, apr_size_t len, 111251876Speter apr_pool_t *p); 112251876Speterstatic apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, 113251876Speter apr_off_t offset, 114251876Speter apr_size_t len, apr_pool_t *p, 115251876Speter apr_bucket_alloc_t *list); 116251876Speterstatic int dbd_mysql_num_cols(apr_dbd_results_t *res); 117251876Speter 118251876Speterstatic const apr_bucket_type_t apr_bucket_type_lob = { 119251876Speter "LOB", 5, APR_BUCKET_DATA, 120251876Speter lob_bucket_destroy, 121251876Speter lob_bucket_read, 122251876Speter apr_bucket_setaside_notimpl, 123251876Speter apr_bucket_shared_split, 124251876Speter apr_bucket_shared_copy 125251876Speter}; 126251876Speter 127251876Speterstatic void lob_bucket_destroy(void *data) 128251876Speter{ 129251876Speter apr_bucket_lob *f = data; 130251876Speter 131251876Speter if (apr_bucket_shared_destroy(f)) { 132251876Speter /* no need to destroy database objects here; it will get 133251876Speter * done automatically when the pool gets cleaned up */ 134251876Speter apr_bucket_free(f); 135251876Speter } 136251876Speter} 137251876Speter 138251876Speterstatic apr_status_t lob_bucket_read(apr_bucket *e, const char **str, 139251876Speter apr_size_t *len, apr_read_type_e block) 140251876Speter{ 141251876Speter apr_bucket_lob *a = e->data; 142251876Speter const apr_dbd_row_t *row = a->row; 143251876Speter apr_dbd_results_t *res = row->res; 144251876Speter int col = a->col; 145251876Speter apr_bucket *b = NULL; 146251876Speter int rv; 147251876Speter apr_size_t blength = e->length; /* bytes remaining in file past offset */ 148251876Speter apr_off_t boffset = e->start; 149251876Speter MYSQL_BIND *bind = &res->bind[col]; 150251876Speter 151251876Speter *str = NULL; /* in case we die prematurely */ 152251876Speter 153251876Speter /* fetch from offset if not at the beginning */ 154251876Speter if (boffset > 0) { 155251876Speter rv = mysql_stmt_fetch_column(res->statement, bind, col, 156251876Speter (unsigned long) boffset); 157251876Speter if (rv != 0) { 158251876Speter return APR_EGENERAL; 159251876Speter } 160251876Speter } 161251876Speter blength -= blength > bind->buffer_length ? bind->buffer_length : blength; 162251876Speter *len = e->length - blength; 163251876Speter *str = bind->buffer; 164251876Speter 165251876Speter /* allocate new buffer, since we used this one for the bucket */ 166251876Speter bind->buffer = apr_palloc(res->pool, bind->buffer_length); 167251876Speter 168251876Speter /* 169251876Speter * Change the current bucket to refer to what we read, 170251876Speter * even if we read nothing because we hit EOF. 171251876Speter */ 172251876Speter apr_bucket_pool_make(e, *str, *len, res->pool); 173251876Speter 174251876Speter /* If we have more to read from the field, then create another bucket */ 175251876Speter if (blength > 0) { 176251876Speter /* for efficiency, we can just build a new apr_bucket struct 177251876Speter * to wrap around the existing LOB bucket */ 178251876Speter b = apr_bucket_alloc(sizeof(*b), e->list); 179251876Speter b->start = boffset + *len; 180251876Speter b->length = blength; 181251876Speter b->data = a; 182251876Speter b->type = &apr_bucket_type_lob; 183251876Speter b->free = apr_bucket_free; 184251876Speter b->list = e->list; 185251876Speter APR_BUCKET_INSERT_AFTER(e, b); 186251876Speter } 187251876Speter else { 188251876Speter lob_bucket_destroy(a); 189251876Speter } 190251876Speter 191251876Speter return APR_SUCCESS; 192251876Speter} 193251876Speter 194251876Speterstatic apr_bucket *apr_bucket_lob_make(apr_bucket *b, 195251876Speter const apr_dbd_row_t *row, int col, 196251876Speter apr_off_t offset, apr_size_t len, 197251876Speter apr_pool_t *p) 198251876Speter{ 199251876Speter apr_bucket_lob *f; 200251876Speter 201251876Speter f = apr_bucket_alloc(sizeof(*f), b->list); 202251876Speter f->row = row; 203251876Speter f->col = col; 204251876Speter f->readpool = p; 205251876Speter 206251876Speter b = apr_bucket_shared_make(b, f, offset, len); 207251876Speter b->type = &apr_bucket_type_lob; 208251876Speter 209251876Speter return b; 210251876Speter} 211251876Speter 212251876Speterstatic apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, 213251876Speter apr_off_t offset, 214251876Speter apr_size_t len, apr_pool_t *p, 215251876Speter apr_bucket_alloc_t *list) 216251876Speter{ 217251876Speter apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); 218251876Speter 219251876Speter APR_BUCKET_INIT(b); 220251876Speter b->free = apr_bucket_free; 221251876Speter b->list = list; 222251876Speter return apr_bucket_lob_make(b, row, col, offset, len, p); 223251876Speter} 224251876Speter 225251876Speterstatic apr_status_t free_result(void *data) 226251876Speter{ 227251876Speter mysql_free_result(data); 228251876Speter return APR_SUCCESS; 229251876Speter} 230251876Speter 231251876Speterstatic int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql, 232251876Speter apr_dbd_results_t **results, 233251876Speter const char *query, int seek) 234251876Speter{ 235251876Speter int sz; 236251876Speter int ret; 237251876Speter if (sql->trans && sql->trans->errnum) { 238251876Speter return sql->trans->errnum; 239251876Speter } 240251876Speter ret = mysql_query(sql->conn, query); 241251876Speter if (!ret) { 242251876Speter if (sz = mysql_field_count(sql->conn), sz > 0) { 243251876Speter if (!*results) { 244251876Speter *results = apr_palloc(pool, sizeof(apr_dbd_results_t)); 245251876Speter } 246251876Speter (*results)->random = seek; 247251876Speter (*results)->statement = NULL; 248251876Speter (*results)->pool = pool; 249251876Speter if (seek) { 250251876Speter (*results)->res = mysql_store_result(sql->conn); 251251876Speter } 252251876Speter else { 253251876Speter (*results)->res = mysql_use_result(sql->conn); 254251876Speter } 255251876Speter apr_pool_cleanup_register(pool, (*results)->res, 256251876Speter free_result,apr_pool_cleanup_null); 257251876Speter } 258251876Speter } else { 259251876Speter ret = mysql_errno(sql->conn); 260251876Speter } 261251876Speter 262251876Speter if (TXN_NOTICE_ERRORS(sql->trans)) { 263251876Speter sql->trans->errnum = ret; 264251876Speter } 265251876Speter return ret; 266251876Speter} 267251876Speter 268251876Speterstatic const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n) 269251876Speter{ 270251876Speter if ((n < 0) || (n >= (int) mysql_num_fields(res->res))) { 271251876Speter return NULL; 272251876Speter } 273251876Speter 274251876Speter return mysql_fetch_fields(res->res)[n].name; 275251876Speter} 276251876Speter 277251876Speterstatic int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res, 278251876Speter apr_dbd_row_t **row, int rownum) 279251876Speter{ 280251876Speter MYSQL_ROW r = NULL; 281251876Speter int ret = 0; 282251876Speter 283251876Speter if (res->statement) { 284251876Speter if (res->random) { 285251876Speter if (rownum > 0) { 286251876Speter mysql_stmt_data_seek(res->statement, (my_ulonglong) --rownum); 287251876Speter } 288251876Speter else { 289251876Speter return -1; /* invalid row */ 290251876Speter } 291251876Speter } 292251876Speter ret = mysql_stmt_fetch(res->statement); 293251876Speter switch (ret) { 294251876Speter case 1: 295251876Speter ret = mysql_stmt_errno(res->statement); 296251876Speter break; 297251876Speter case MYSQL_NO_DATA: 298251876Speter ret = -1; 299251876Speter break; 300251876Speter default: 301251876Speter ret = 0; /* bad luck - get_entry will deal with this */ 302251876Speter break; 303251876Speter } 304251876Speter } 305251876Speter else { 306251876Speter if (res->random) { 307251876Speter if (rownum > 0) { 308251876Speter mysql_data_seek(res->res, (my_ulonglong) --rownum); 309251876Speter } 310251876Speter else { 311251876Speter return -1; /* invalid row */ 312251876Speter } 313251876Speter } 314251876Speter r = mysql_fetch_row(res->res); 315251876Speter if (r == NULL) { 316251876Speter ret = -1; 317251876Speter } 318251876Speter } 319251876Speter if (ret == 0) { 320251876Speter if (!*row) { 321251876Speter *row = apr_palloc(pool, sizeof(apr_dbd_row_t)); 322251876Speter } 323251876Speter (*row)->row = r; 324251876Speter (*row)->res = res; 325251876Speter (*row)->len = mysql_fetch_lengths(res->res); 326251876Speter } 327251876Speter else { 328253734Speter apr_pool_cleanup_run(res->pool, res->res, free_result); 329251876Speter } 330251876Speter return ret; 331251876Speter} 332251876Speter#if 0 333251876Speter/* An improved API that was proposed but not followed up */ 334251876Speterstatic int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n, 335251876Speter apr_dbd_datum_t *val) 336251876Speter{ 337251876Speter MYSQL_BIND *bind; 338251876Speter if (dbd_mysql_num_cols(row->res) <= n) { 339251876Speter return NULL; 340251876Speter } 341251876Speter if (row->res->statement) { 342251876Speter bind = &row->res->bind[n]; 343251876Speter if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 344251876Speter val->type = APR_DBD_VALUE_NULL; 345251876Speter return -1; 346251876Speter } 347251876Speter if (*bind->is_null) { 348251876Speter val->type = APR_DBD_VALUE_NULL; 349251876Speter return -1; 350251876Speter } 351251876Speter else { 352251876Speter val->type = APR_DBD_VALUE_STRING; 353251876Speter val->value.stringval = bind->buffer; 354251876Speter } 355251876Speter } 356251876Speter else { 357251876Speter val->type = APR_DBD_VALUE_STRING; 358251876Speter val->value.stringval = row->row[n]; 359251876Speter } 360251876Speter return 0; 361251876Speter} 362251876Speter#else 363251876Speter 364251876Speterstatic const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n) 365251876Speter{ 366251876Speter MYSQL_BIND *bind; 367251876Speter if (dbd_mysql_num_cols(row->res) <= n) { 368251876Speter return NULL; 369251876Speter } 370251876Speter if (row->res->statement) { 371251876Speter bind = &row->res->bind[n]; 372251876Speter if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 373251876Speter return NULL; 374251876Speter } 375251876Speter if (*bind->is_null) { 376251876Speter return NULL; 377251876Speter } 378251876Speter else { 379251876Speter return bind->buffer; 380251876Speter } 381251876Speter } 382251876Speter else { 383251876Speter return row->row[n]; 384251876Speter } 385251876Speter return NULL; 386251876Speter} 387251876Speter#endif 388251876Speter 389251876Speterstatic apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n, 390251876Speter apr_dbd_type_e type, void *data) 391251876Speter{ 392251876Speter if (row->res->statement) { 393251876Speter MYSQL_BIND *bind = &row->res->bind[n]; 394251876Speter unsigned long len = *bind->length; 395251876Speter 396251876Speter if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { 397251876Speter return APR_EGENERAL; 398251876Speter } 399251876Speter 400251876Speter if (*bind->is_null) { 401251876Speter return APR_ENOENT; 402251876Speter } 403251876Speter 404251876Speter switch (type) { 405251876Speter case APR_DBD_TYPE_TINY: 406251876Speter *(char*)data = atoi(bind->buffer); 407251876Speter break; 408251876Speter case APR_DBD_TYPE_UTINY: 409251876Speter *(unsigned char*)data = atoi(bind->buffer); 410251876Speter break; 411251876Speter case APR_DBD_TYPE_SHORT: 412251876Speter *(short*)data = atoi(bind->buffer); 413251876Speter break; 414251876Speter case APR_DBD_TYPE_USHORT: 415251876Speter *(unsigned short*)data = atoi(bind->buffer); 416251876Speter break; 417251876Speter case APR_DBD_TYPE_INT: 418251876Speter *(int*)data = atoi(bind->buffer); 419251876Speter break; 420251876Speter case APR_DBD_TYPE_UINT: 421251876Speter *(unsigned int*)data = atoi(bind->buffer); 422251876Speter break; 423251876Speter case APR_DBD_TYPE_LONG: 424251876Speter *(long*)data = atol(bind->buffer); 425251876Speter break; 426251876Speter case APR_DBD_TYPE_ULONG: 427251876Speter *(unsigned long*)data = atol(bind->buffer); 428251876Speter break; 429251876Speter case APR_DBD_TYPE_LONGLONG: 430251876Speter *(apr_int64_t*)data = apr_atoi64(bind->buffer); 431251876Speter break; 432251876Speter case APR_DBD_TYPE_ULONGLONG: 433251876Speter *(apr_uint64_t*)data = apr_atoi64(bind->buffer); 434251876Speter break; 435251876Speter case APR_DBD_TYPE_FLOAT: 436251876Speter *(float*)data = (float) atof(bind->buffer); 437251876Speter break; 438251876Speter case APR_DBD_TYPE_DOUBLE: 439251876Speter *(double*)data = atof(bind->buffer); 440251876Speter break; 441251876Speter case APR_DBD_TYPE_STRING: 442251876Speter case APR_DBD_TYPE_TEXT: 443251876Speter case APR_DBD_TYPE_TIME: 444251876Speter case APR_DBD_TYPE_DATE: 445251876Speter case APR_DBD_TYPE_DATETIME: 446251876Speter case APR_DBD_TYPE_TIMESTAMP: 447251876Speter case APR_DBD_TYPE_ZTIMESTAMP: 448251876Speter *((char*)bind->buffer+bind->buffer_length-1) = '\0'; 449251876Speter *(char**)data = bind->buffer; 450251876Speter break; 451251876Speter case APR_DBD_TYPE_BLOB: 452251876Speter case APR_DBD_TYPE_CLOB: 453251876Speter { 454251876Speter apr_bucket *e; 455251876Speter apr_bucket_brigade *b = (apr_bucket_brigade*)data; 456251876Speter 457251876Speter e = apr_bucket_lob_create(row, n, 0, len, 458251876Speter row->res->pool, b->bucket_alloc); 459251876Speter APR_BRIGADE_INSERT_TAIL(b, e); 460251876Speter } 461251876Speter break; 462251876Speter case APR_DBD_TYPE_NULL: 463251876Speter *(void**)data = NULL; 464251876Speter break; 465251876Speter default: 466251876Speter return APR_EGENERAL; 467251876Speter } 468251876Speter } 469251876Speter else { 470251876Speter if (row->row[n] == NULL) { 471251876Speter return APR_ENOENT; 472251876Speter } 473251876Speter 474251876Speter switch (type) { 475251876Speter case APR_DBD_TYPE_TINY: 476251876Speter *(char*)data = atoi(row->row[n]); 477251876Speter break; 478251876Speter case APR_DBD_TYPE_UTINY: 479251876Speter *(unsigned char*)data = atoi(row->row[n]); 480251876Speter break; 481251876Speter case APR_DBD_TYPE_SHORT: 482251876Speter *(short*)data = atoi(row->row[n]); 483251876Speter break; 484251876Speter case APR_DBD_TYPE_USHORT: 485251876Speter *(unsigned short*)data = atoi(row->row[n]); 486251876Speter break; 487251876Speter case APR_DBD_TYPE_INT: 488251876Speter *(int*)data = atoi(row->row[n]); 489251876Speter break; 490251876Speter case APR_DBD_TYPE_UINT: 491251876Speter *(unsigned int*)data = atoi(row->row[n]); 492251876Speter break; 493251876Speter case APR_DBD_TYPE_LONG: 494251876Speter *(long*)data = atol(row->row[n]); 495251876Speter break; 496251876Speter case APR_DBD_TYPE_ULONG: 497251876Speter *(unsigned long*)data = atol(row->row[n]); 498251876Speter break; 499251876Speter case APR_DBD_TYPE_LONGLONG: 500251876Speter *(apr_int64_t*)data = apr_atoi64(row->row[n]); 501251876Speter break; 502251876Speter case APR_DBD_TYPE_ULONGLONG: 503251876Speter *(apr_uint64_t*)data = apr_atoi64(row->row[n]); 504251876Speter break; 505251876Speter case APR_DBD_TYPE_FLOAT: 506251876Speter *(float*)data = (float) atof(row->row[n]); 507251876Speter break; 508251876Speter case APR_DBD_TYPE_DOUBLE: 509251876Speter *(double*)data = atof(row->row[n]); 510251876Speter break; 511251876Speter case APR_DBD_TYPE_STRING: 512251876Speter case APR_DBD_TYPE_TEXT: 513251876Speter case APR_DBD_TYPE_TIME: 514251876Speter case APR_DBD_TYPE_DATE: 515251876Speter case APR_DBD_TYPE_DATETIME: 516251876Speter case APR_DBD_TYPE_TIMESTAMP: 517251876Speter case APR_DBD_TYPE_ZTIMESTAMP: 518251876Speter *(char**)data = row->row[n]; 519251876Speter break; 520251876Speter case APR_DBD_TYPE_BLOB: 521251876Speter case APR_DBD_TYPE_CLOB: 522251876Speter { 523251876Speter apr_bucket *e; 524251876Speter apr_bucket_brigade *b = (apr_bucket_brigade*)data; 525251876Speter 526251876Speter e = apr_bucket_pool_create(row->row[n], row->len[n], 527251876Speter row->res->pool, b->bucket_alloc); 528251876Speter APR_BRIGADE_INSERT_TAIL(b, e); 529251876Speter } 530251876Speter break; 531251876Speter case APR_DBD_TYPE_NULL: 532251876Speter *(void**)data = NULL; 533251876Speter break; 534251876Speter default: 535251876Speter return APR_EGENERAL; 536251876Speter } 537251876Speter } 538251876Speter return 0; 539251876Speter} 540251876Speter 541251876Speterstatic const char *dbd_mysql_error(apr_dbd_t *sql, int n) 542251876Speter{ 543251876Speter return mysql_error(sql->conn); 544251876Speter} 545251876Speter 546251876Speterstatic int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query) 547251876Speter{ 548251876Speter int ret; 549251876Speter if (sql->trans && sql->trans->errnum) { 550251876Speter return sql->trans->errnum; 551251876Speter } 552251876Speter ret = mysql_query(sql->conn, query); 553251876Speter if (ret != 0) { 554251876Speter ret = mysql_errno(sql->conn); 555251876Speter } 556251876Speter *nrows = (int) mysql_affected_rows(sql->conn); 557251876Speter if (TXN_NOTICE_ERRORS(sql->trans)) { 558251876Speter sql->trans->errnum = ret; 559251876Speter } 560251876Speter return ret; 561251876Speter} 562251876Speter 563251876Speterstatic const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg, 564251876Speter apr_dbd_t *sql) 565251876Speter{ 566251876Speter unsigned long len = strlen(arg); 567251876Speter char *ret = apr_palloc(pool, 2*len + 1); 568251876Speter mysql_real_escape_string(sql->conn, ret, arg, len); 569251876Speter return ret; 570251876Speter} 571251876Speter 572251876Speterstatic apr_status_t stmt_close(void *data) 573251876Speter{ 574251876Speter mysql_stmt_close(data); 575251876Speter return APR_SUCCESS; 576251876Speter} 577251876Speter 578251876Speterstatic int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql, 579251876Speter const char *query, const char *label, 580251876Speter int nargs, int nvals, apr_dbd_type_e *types, 581251876Speter apr_dbd_prepared_t **statement) 582251876Speter{ 583251876Speter /* Translate from apr_dbd to native query format */ 584251876Speter int ret; 585251876Speter 586251876Speter if (!*statement) { 587251876Speter *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t)); 588251876Speter } 589251876Speter (*statement)->stmt = mysql_stmt_init(sql->conn); 590251876Speter 591251876Speter if ((*statement)->stmt) { 592251876Speter apr_pool_cleanup_register(pool, (*statement)->stmt, 593251876Speter stmt_close, apr_pool_cleanup_null); 594251876Speter ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query)); 595251876Speter 596251876Speter if (ret != 0) { 597251876Speter ret = mysql_stmt_errno((*statement)->stmt); 598251876Speter } 599251876Speter 600251876Speter (*statement)->nargs = nargs; 601251876Speter (*statement)->nvals = nvals; 602251876Speter (*statement)->types = types; 603251876Speter 604251876Speter return ret; 605251876Speter } 606251876Speter 607251876Speter return CR_OUT_OF_MEMORY; 608251876Speter} 609251876Speter 610251876Speterstatic void dbd_mysql_bind(apr_dbd_prepared_t *statement, 611251876Speter const char **values, MYSQL_BIND *bind) 612251876Speter{ 613251876Speter int i, j; 614251876Speter 615251876Speter for (i = 0, j = 0; i < statement->nargs; i++, j++) { 616251876Speter bind[i].length = &bind[i].buffer_length; 617251876Speter bind[i].is_unsigned = 0; 618251876Speter bind[i].is_null = NULL; 619251876Speter 620251876Speter if (values[j] == NULL) { 621251876Speter bind[i].buffer_type = MYSQL_TYPE_NULL; 622251876Speter } 623251876Speter else { 624251876Speter switch (statement->types[i]) { 625251876Speter case APR_DBD_TYPE_BLOB: 626251876Speter case APR_DBD_TYPE_CLOB: 627251876Speter bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 628251876Speter bind[i].buffer = (void*)values[j]; 629251876Speter bind[i].buffer_length = atol(values[++j]); 630251876Speter 631251876Speter /* skip table and column */ 632251876Speter j += 2; 633251876Speter break; 634251876Speter default: 635251876Speter bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 636251876Speter bind[i].buffer = (void*)values[j]; 637251876Speter bind[i].buffer_length = strlen(values[j]); 638251876Speter break; 639251876Speter } 640251876Speter } 641251876Speter } 642251876Speter 643251876Speter return; 644251876Speter} 645251876Speter 646251876Speterstatic int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql, 647251876Speter int *nrows, apr_dbd_prepared_t *statement, 648251876Speter MYSQL_BIND *bind) 649251876Speter{ 650251876Speter int ret; 651251876Speter 652251876Speter ret = mysql_stmt_bind_param(statement->stmt, bind); 653251876Speter if (ret != 0) { 654251876Speter *nrows = 0; 655251876Speter ret = mysql_stmt_errno(statement->stmt); 656251876Speter } 657251876Speter else { 658251876Speter ret = mysql_stmt_execute(statement->stmt); 659251876Speter if (ret != 0) { 660251876Speter ret = mysql_stmt_errno(statement->stmt); 661251876Speter } 662251876Speter *nrows = (int) mysql_stmt_affected_rows(statement->stmt); 663251876Speter } 664251876Speter 665251876Speter return ret; 666251876Speter} 667251876Speter 668251876Speterstatic int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql, 669251876Speter int *nrows, apr_dbd_prepared_t *statement, 670251876Speter const char **values) 671251876Speter{ 672251876Speter MYSQL_BIND *bind; 673251876Speter int ret; 674251876Speter 675251876Speter if (sql->trans && sql->trans->errnum) { 676251876Speter return sql->trans->errnum; 677251876Speter } 678251876Speter 679251876Speter bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 680251876Speter 681251876Speter dbd_mysql_bind(statement, values, bind); 682251876Speter 683251876Speter ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind); 684251876Speter 685251876Speter if (TXN_NOTICE_ERRORS(sql->trans)) { 686251876Speter sql->trans->errnum = ret; 687251876Speter } 688251876Speter return ret; 689251876Speter} 690251876Speter 691251876Speterstatic int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 692251876Speter apr_dbd_prepared_t *statement, va_list args) 693251876Speter{ 694251876Speter const char **values; 695251876Speter int i; 696251876Speter 697251876Speter if (sql->trans && sql->trans->errnum) { 698251876Speter return sql->trans->errnum; 699251876Speter } 700251876Speter 701251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nvals); 702251876Speter 703251876Speter for (i = 0; i < statement->nvals; i++) { 704251876Speter values[i] = va_arg(args, const char*); 705251876Speter } 706251876Speter 707251876Speter return dbd_mysql_pquery(pool, sql, nrows, statement, values); 708251876Speter} 709251876Speter 710251876Speterstatic int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql, 711251876Speter apr_dbd_results_t **res, 712251876Speter apr_dbd_prepared_t *statement, 713251876Speter int random, MYSQL_BIND *bind) 714251876Speter{ 715251876Speter int nfields, i; 716251876Speter my_bool *is_nullr; 717251876Speter#if MYSQL_VERSION_ID >= 50000 718251876Speter my_bool *error; 719251876Speter#endif 720251876Speter int ret; 721251876Speter unsigned long *length, maxlen; 722251876Speter 723251876Speter ret = mysql_stmt_bind_param(statement->stmt, bind); 724251876Speter if (ret == 0) { 725251876Speter ret = mysql_stmt_execute(statement->stmt); 726251876Speter if (!ret) { 727251876Speter if (!*res) { 728251876Speter *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 729251876Speter } 730251876Speter (*res)->random = random; 731251876Speter (*res)->statement = statement->stmt; 732251876Speter (*res)->res = mysql_stmt_result_metadata(statement->stmt); 733251876Speter (*res)->pool = pool; 734251876Speter apr_pool_cleanup_register(pool, (*res)->res, 735251876Speter free_result, apr_pool_cleanup_null); 736251876Speter nfields = mysql_num_fields((*res)->res); 737251876Speter if (!(*res)->bind) { 738251876Speter (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND)); 739251876Speter length = apr_pcalloc(pool, nfields*sizeof(unsigned long)); 740251876Speter#if MYSQL_VERSION_ID >= 50000 741251876Speter error = apr_palloc(pool, nfields*sizeof(my_bool)); 742251876Speter#endif 743251876Speter is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool)); 744251876Speter for ( i = 0; i < nfields; ++i ) { 745251876Speter maxlen = ((*res)->res->fields[i].length < sql->fldsz ? 746251876Speter (*res)->res->fields[i].length : sql->fldsz) + 1; 747251876Speter if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) { 748251876Speter (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 749251876Speter } 750251876Speter else { 751251876Speter (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 752251876Speter } 753251876Speter (*res)->bind[i].buffer_length = maxlen; 754251876Speter (*res)->bind[i].length = &length[i]; 755251876Speter (*res)->bind[i].buffer = apr_palloc(pool, maxlen); 756251876Speter (*res)->bind[i].is_null = is_nullr+i; 757251876Speter#if MYSQL_VERSION_ID >= 50000 758251876Speter (*res)->bind[i].error = error+i; 759251876Speter#endif 760251876Speter } 761251876Speter } 762251876Speter ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind); 763251876Speter if (!ret) { 764251876Speter ret = mysql_stmt_store_result(statement->stmt); 765251876Speter } 766251876Speter } 767251876Speter } 768251876Speter if (ret != 0) { 769251876Speter ret = mysql_stmt_errno(statement->stmt); 770251876Speter } 771251876Speter 772251876Speter return ret; 773251876Speter} 774251876Speter 775251876Speterstatic int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql, 776251876Speter apr_dbd_results_t **res, 777251876Speter apr_dbd_prepared_t *statement, int random, 778251876Speter const char **args) 779251876Speter{ 780251876Speter int ret; 781251876Speter MYSQL_BIND *bind; 782251876Speter 783251876Speter if (sql->trans && sql->trans->errnum) { 784251876Speter return sql->trans->errnum; 785251876Speter } 786251876Speter 787251876Speter bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 788251876Speter 789251876Speter dbd_mysql_bind(statement, args, bind); 790251876Speter 791251876Speter ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind); 792251876Speter 793251876Speter if (TXN_NOTICE_ERRORS(sql->trans)) { 794251876Speter sql->trans->errnum = ret; 795251876Speter } 796251876Speter return ret; 797251876Speter} 798251876Speter 799251876Speterstatic int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql, 800251876Speter apr_dbd_results_t **res, 801251876Speter apr_dbd_prepared_t *statement, int random, 802251876Speter va_list args) 803251876Speter{ 804251876Speter const char **values; 805251876Speter int i; 806251876Speter 807251876Speter if (sql->trans && sql->trans->errnum) { 808251876Speter return sql->trans->errnum; 809251876Speter } 810251876Speter 811251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nvals); 812251876Speter 813251876Speter for (i = 0; i < statement->nvals; i++) { 814251876Speter values[i] = va_arg(args, const char*); 815251876Speter } 816251876Speter 817251876Speter return dbd_mysql_pselect(pool, sql, res, statement, random, values); 818251876Speter} 819251876Speter 820251876Speterstatic void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement, 821251876Speter const void **values, MYSQL_BIND *bind) 822251876Speter{ 823251876Speter void *arg; 824251876Speter int i, j; 825251876Speter apr_dbd_type_e type; 826251876Speter 827251876Speter for (i = 0, j = 0; i < statement->nargs; i++, j++) { 828251876Speter arg = (void *)values[j]; 829251876Speter 830251876Speter bind[i].length = &bind[i].buffer_length; 831251876Speter bind[i].is_null = NULL; 832251876Speter 833251876Speter type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]); 834251876Speter switch (type) { 835251876Speter case APR_DBD_TYPE_TINY: 836251876Speter bind[i].buffer = arg; 837251876Speter bind[i].buffer_type = MYSQL_TYPE_TINY; 838251876Speter bind[i].is_unsigned = 0; 839251876Speter break; 840251876Speter case APR_DBD_TYPE_UTINY: 841251876Speter bind[i].buffer = arg; 842251876Speter bind[i].buffer_type = MYSQL_TYPE_TINY; 843251876Speter bind[i].is_unsigned = 1; 844251876Speter break; 845251876Speter case APR_DBD_TYPE_SHORT: 846251876Speter bind[i].buffer = arg; 847251876Speter bind[i].buffer_type = MYSQL_TYPE_SHORT; 848251876Speter bind[i].is_unsigned = 0; 849251876Speter break; 850251876Speter case APR_DBD_TYPE_USHORT: 851251876Speter bind[i].buffer = arg; 852251876Speter bind[i].buffer_type = MYSQL_TYPE_SHORT; 853251876Speter bind[i].is_unsigned = 1; 854251876Speter break; 855251876Speter case APR_DBD_TYPE_INT: 856251876Speter bind[i].buffer = arg; 857251876Speter bind[i].buffer_type = MYSQL_TYPE_LONG; 858251876Speter bind[i].is_unsigned = 0; 859251876Speter break; 860251876Speter case APR_DBD_TYPE_UINT: 861251876Speter bind[i].buffer = arg; 862251876Speter bind[i].buffer_type = MYSQL_TYPE_LONG; 863251876Speter bind[i].is_unsigned = 1; 864251876Speter break; 865251876Speter case APR_DBD_TYPE_LONG: 866251876Speter if (sizeof(int) == sizeof(long)) { 867251876Speter bind[i].buffer = arg; 868251876Speter } 869251876Speter else { 870251876Speter bind[i].buffer = apr_palloc(pool, sizeof(int)); 871251876Speter *(int*)bind[i].buffer = *(long*)arg; 872251876Speter } 873251876Speter bind[i].buffer_type = MYSQL_TYPE_LONG; 874251876Speter bind[i].is_unsigned = 0; 875251876Speter break; 876251876Speter case APR_DBD_TYPE_ULONG: 877251876Speter if (sizeof(unsigned int) == sizeof(unsigned long)) { 878251876Speter bind[i].buffer = arg; 879251876Speter } 880251876Speter else { 881251876Speter bind[i].buffer = apr_palloc(pool, sizeof(unsigned int)); 882251876Speter *(unsigned int*)bind[i].buffer = *(unsigned long*)arg; 883251876Speter } 884251876Speter bind[i].buffer_type = MYSQL_TYPE_LONG; 885251876Speter bind[i].is_unsigned = 1; 886251876Speter break; 887251876Speter case APR_DBD_TYPE_LONGLONG: 888251876Speter if (sizeof(my_ulonglong) == sizeof(apr_int64_t)) { 889251876Speter bind[i].buffer = arg; 890251876Speter bind[i].buffer_type = MYSQL_TYPE_LONGLONG; 891251876Speter } 892251876Speter else { /* have to downsize, long long is not portable */ 893251876Speter bind[i].buffer = apr_palloc(pool, sizeof(long)); 894251876Speter *(long*)bind[i].buffer = (long) *(apr_int64_t*)arg; 895251876Speter bind[i].buffer_type = MYSQL_TYPE_LONG; 896251876Speter } 897251876Speter bind[i].is_unsigned = 0; 898251876Speter break; 899251876Speter case APR_DBD_TYPE_ULONGLONG: 900251876Speter if (sizeof(my_ulonglong) == sizeof(apr_uint64_t)) { 901251876Speter bind[i].buffer = arg; 902251876Speter bind[i].buffer_type = MYSQL_TYPE_LONGLONG; 903251876Speter } 904251876Speter else { /* have to downsize, long long is not portable */ 905251876Speter bind[i].buffer = apr_palloc(pool, sizeof(long)); 906251876Speter *(unsigned long*)bind[i].buffer = 907251876Speter (unsigned long) *(apr_uint64_t*)arg; 908251876Speter bind[i].buffer_type = MYSQL_TYPE_LONG; 909251876Speter } 910251876Speter bind[i].is_unsigned = 1; 911251876Speter break; 912251876Speter case APR_DBD_TYPE_FLOAT: 913251876Speter bind[i].buffer = arg; 914251876Speter bind[i].buffer_type = MYSQL_TYPE_FLOAT; 915251876Speter bind[i].is_unsigned = 0; 916251876Speter break; 917251876Speter case APR_DBD_TYPE_DOUBLE: 918251876Speter bind[i].buffer = arg; 919251876Speter bind[i].buffer_type = MYSQL_TYPE_DOUBLE; 920251876Speter bind[i].is_unsigned = 0; 921251876Speter break; 922251876Speter case APR_DBD_TYPE_STRING: 923251876Speter case APR_DBD_TYPE_TEXT: 924251876Speter case APR_DBD_TYPE_TIME: 925251876Speter case APR_DBD_TYPE_DATE: 926251876Speter case APR_DBD_TYPE_DATETIME: 927251876Speter case APR_DBD_TYPE_TIMESTAMP: 928251876Speter case APR_DBD_TYPE_ZTIMESTAMP: 929251876Speter bind[i].buffer = arg; 930251876Speter bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; 931251876Speter bind[i].is_unsigned = 0; 932251876Speter bind[i].buffer_length = strlen((const char *)arg); 933251876Speter break; 934251876Speter case APR_DBD_TYPE_BLOB: 935251876Speter case APR_DBD_TYPE_CLOB: 936251876Speter bind[i].buffer = (void *)arg; 937251876Speter bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB; 938251876Speter bind[i].is_unsigned = 0; 939251876Speter bind[i].buffer_length = *(apr_size_t*)values[++j]; 940251876Speter 941251876Speter /* skip table and column */ 942251876Speter j += 2; 943251876Speter break; 944251876Speter case APR_DBD_TYPE_NULL: 945251876Speter default: 946251876Speter bind[i].buffer_type = MYSQL_TYPE_NULL; 947251876Speter break; 948251876Speter } 949251876Speter } 950251876Speter 951251876Speter return; 952251876Speter} 953251876Speter 954251876Speterstatic int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql, 955251876Speter int *nrows, apr_dbd_prepared_t *statement, 956251876Speter const void **values) 957251876Speter{ 958251876Speter MYSQL_BIND *bind; 959251876Speter int ret; 960251876Speter 961251876Speter if (sql->trans && sql->trans->errnum) { 962251876Speter return sql->trans->errnum; 963251876Speter } 964251876Speter 965251876Speter bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 966251876Speter 967251876Speter dbd_mysql_bbind(pool, statement, values, bind); 968251876Speter 969251876Speter ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind); 970251876Speter 971251876Speter if (TXN_NOTICE_ERRORS(sql->trans)) { 972251876Speter sql->trans->errnum = ret; 973251876Speter } 974251876Speter return ret; 975251876Speter} 976251876Speter 977251876Speterstatic int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, 978251876Speter apr_dbd_prepared_t *statement, va_list args) 979251876Speter{ 980251876Speter const void **values; 981251876Speter int i; 982251876Speter 983251876Speter if (sql->trans && sql->trans->errnum) { 984251876Speter return sql->trans->errnum; 985251876Speter } 986251876Speter 987251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nvals); 988251876Speter 989251876Speter for (i = 0; i < statement->nvals; i++) { 990251876Speter values[i] = va_arg(args, const void*); 991251876Speter } 992251876Speter 993251876Speter return dbd_mysql_pbquery(pool, sql, nrows, statement, values); 994251876Speter} 995251876Speter 996251876Speterstatic int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql, 997251876Speter apr_dbd_results_t **res, 998251876Speter apr_dbd_prepared_t *statement, int random, 999251876Speter const void **args) 1000251876Speter{ 1001251876Speter int ret; 1002251876Speter MYSQL_BIND *bind; 1003251876Speter 1004251876Speter if (sql->trans && sql->trans->errnum) { 1005251876Speter return sql->trans->errnum; 1006251876Speter } 1007251876Speter 1008251876Speter bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND)); 1009251876Speter 1010251876Speter dbd_mysql_bbind(pool, statement, args, bind); 1011251876Speter 1012251876Speter ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind); 1013251876Speter 1014251876Speter if (TXN_NOTICE_ERRORS(sql->trans)) { 1015251876Speter sql->trans->errnum = ret; 1016251876Speter } 1017251876Speter return ret; 1018251876Speter} 1019251876Speter 1020251876Speterstatic int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql, 1021251876Speter apr_dbd_results_t **res, 1022251876Speter apr_dbd_prepared_t *statement, int random, 1023251876Speter va_list args) 1024251876Speter{ 1025251876Speter const void **values; 1026251876Speter int i; 1027251876Speter 1028251876Speter if (sql->trans && sql->trans->errnum) { 1029251876Speter return sql->trans->errnum; 1030251876Speter } 1031251876Speter 1032251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1033251876Speter 1034251876Speter for (i = 0; i < statement->nvals; i++) { 1035251876Speter values[i] = va_arg(args, const void*); 1036251876Speter } 1037251876Speter 1038251876Speter return dbd_mysql_pbselect(pool, sql, res, statement, random, values); 1039251876Speter} 1040251876Speter 1041251876Speterstatic int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans) 1042251876Speter{ 1043251876Speter int ret = -1; 1044251876Speter if (trans) { 1045251876Speter /* rollback on error or explicit rollback request */ 1046251876Speter if (trans->errnum || TXN_DO_ROLLBACK(trans)) { 1047251876Speter trans->errnum = 0; 1048251876Speter ret = mysql_rollback(trans->handle->conn); 1049251876Speter } 1050251876Speter else { 1051251876Speter ret = mysql_commit(trans->handle->conn); 1052251876Speter } 1053272076Speter ret |= mysql_autocommit(trans->handle->conn, 1); 1054272076Speter trans->handle->trans = NULL; 1055251876Speter } 1056251876Speter return ret; 1057251876Speter} 1058251876Speter/* Whether or not transactions work depends on whether the 1059251876Speter * underlying DB supports them within MySQL. Unfortunately 1060251876Speter * it fails silently with the default InnoDB. 1061251876Speter */ 1062251876Speter 1063251876Speterstatic int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle, 1064251876Speter apr_dbd_transaction_t **trans) 1065251876Speter{ 1066251876Speter /* Don't try recursive transactions here */ 1067251876Speter if (handle->trans) { 1068251876Speter dbd_mysql_end_transaction(handle->trans) ; 1069251876Speter } 1070251876Speter if (!*trans) { 1071251876Speter *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t)); 1072251876Speter } 1073251876Speter (*trans)->errnum = mysql_autocommit(handle->conn, 0); 1074251876Speter (*trans)->handle = handle; 1075251876Speter handle->trans = *trans; 1076251876Speter return (*trans)->errnum; 1077251876Speter} 1078251876Speter 1079251876Speterstatic int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans) 1080251876Speter{ 1081251876Speter if (!trans) 1082251876Speter return APR_DBD_TRANSACTION_COMMIT; 1083251876Speter 1084251876Speter return trans->mode; 1085251876Speter} 1086251876Speter 1087251876Speterstatic int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans, 1088251876Speter int mode) 1089251876Speter{ 1090251876Speter if (!trans) 1091251876Speter return APR_DBD_TRANSACTION_COMMIT; 1092251876Speter 1093251876Speter return trans->mode = (mode & TXN_MODE_BITS); 1094251876Speter} 1095251876Speter 1096251876Speterstatic apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params, 1097251876Speter const char **error) 1098251876Speter{ 1099251876Speter static const char *const delims = " \r\n\t;|,"; 1100251876Speter const char *ptr; 1101251876Speter int i; 1102251876Speter const char *key; 1103251876Speter size_t klen; 1104251876Speter const char *value; 1105251876Speter size_t vlen; 1106251876Speter#if MYSQL_VERSION_ID >= 50013 1107251876Speter my_bool do_reconnect = 1; 1108251876Speter#endif 1109251876Speter MYSQL *real_conn; 1110251876Speter unsigned long flags = 0; 1111251876Speter 1112251876Speter struct { 1113251876Speter const char *field; 1114251876Speter const char *value; 1115251876Speter } fields[] = { 1116251876Speter {"host", NULL}, 1117251876Speter {"user", NULL}, 1118251876Speter {"pass", NULL}, 1119251876Speter {"dbname", NULL}, 1120251876Speter {"port", NULL}, 1121251876Speter {"sock", NULL}, 1122251876Speter {"flags", NULL}, 1123251876Speter {"fldsz", NULL}, 1124251876Speter {"group", NULL}, 1125251876Speter {"reconnect", NULL}, 1126251876Speter {NULL, NULL} 1127251876Speter }; 1128251876Speter unsigned int port = 0; 1129251876Speter apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t)); 1130251876Speter sql->fldsz = FIELDSIZE; 1131251876Speter sql->conn = mysql_init(sql->conn); 1132251876Speter if ( sql->conn == NULL ) { 1133251876Speter return NULL; 1134251876Speter } 1135251876Speter for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) { 1136251876Speter /* don't dereference memory that may not belong to us */ 1137251876Speter if (ptr == params) { 1138251876Speter ++ptr; 1139251876Speter continue; 1140251876Speter } 1141251876Speter for (key = ptr-1; apr_isspace(*key); --key); 1142251876Speter klen = 0; 1143251876Speter while (apr_isalpha(*key)) { 1144251876Speter /* don't parse backwards off the start of the string */ 1145251876Speter if (key == params) { 1146251876Speter --key; 1147251876Speter ++klen; 1148251876Speter break; 1149251876Speter } 1150251876Speter --key; 1151251876Speter ++klen; 1152251876Speter } 1153251876Speter ++key; 1154251876Speter for (value = ptr+1; apr_isspace(*value); ++value); 1155251876Speter vlen = strcspn(value, delims); 1156251876Speter for (i = 0; fields[i].field != NULL; i++) { 1157251876Speter if (!strncasecmp(fields[i].field, key, klen)) { 1158251876Speter fields[i].value = apr_pstrndup(pool, value, vlen); 1159251876Speter break; 1160251876Speter } 1161251876Speter } 1162251876Speter ptr = value+vlen; 1163251876Speter } 1164251876Speter if (fields[4].value != NULL) { 1165251876Speter port = atoi(fields[4].value); 1166251876Speter } 1167251876Speter if (fields[6].value != NULL && 1168251876Speter !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) { 1169251876Speter flags |= CLIENT_FOUND_ROWS; /* only option we know */ 1170251876Speter } 1171251876Speter if (fields[7].value != NULL) { 1172251876Speter sql->fldsz = atol(fields[7].value); 1173251876Speter } 1174251876Speter if (fields[8].value != NULL) { 1175251876Speter mysql_options(sql->conn, MYSQL_READ_DEFAULT_GROUP, fields[8].value); 1176251876Speter } 1177251876Speter#if MYSQL_VERSION_ID >= 50013 1178251876Speter if (fields[9].value != NULL) { 1179251876Speter do_reconnect = atoi(fields[9].value) ? 1 : 0; 1180251876Speter } 1181251876Speter#endif 1182251876Speter 1183251876Speter#if MYSQL_VERSION_ID >= 50013 1184251876Speter /* the MySQL manual says this should be BEFORE mysql_real_connect */ 1185251876Speter mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect); 1186251876Speter#endif 1187251876Speter 1188251876Speter real_conn = mysql_real_connect(sql->conn, fields[0].value, 1189251876Speter fields[1].value, fields[2].value, 1190251876Speter fields[3].value, port, 1191251876Speter fields[5].value, flags); 1192251876Speter 1193251876Speter if(real_conn == NULL) { 1194251876Speter if (error) { 1195251876Speter *error = apr_pstrdup(pool, mysql_error(sql->conn)); 1196251876Speter } 1197251876Speter mysql_close(sql->conn); 1198251876Speter return NULL; 1199251876Speter } 1200251876Speter 1201251876Speter#if MYSQL_VERSION_ID >= 50013 1202251876Speter /* Some say this should be AFTER mysql_real_connect */ 1203251876Speter mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect); 1204251876Speter#endif 1205251876Speter 1206251876Speter return sql; 1207251876Speter} 1208251876Speter 1209251876Speterstatic apr_status_t dbd_mysql_close(apr_dbd_t *handle) 1210251876Speter{ 1211251876Speter mysql_close(handle->conn); 1212251876Speter return APR_SUCCESS; 1213251876Speter} 1214251876Speter 1215251876Speterstatic apr_status_t dbd_mysql_check_conn(apr_pool_t *pool, 1216251876Speter apr_dbd_t *handle) 1217251876Speter{ 1218251876Speter return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS; 1219251876Speter} 1220251876Speter 1221251876Speterstatic int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle, 1222251876Speter const char* name) 1223251876Speter{ 1224251876Speter return mysql_select_db(handle->conn, name); 1225251876Speter} 1226251876Speter 1227251876Speterstatic void *dbd_mysql_native(apr_dbd_t *handle) 1228251876Speter{ 1229251876Speter return handle->conn; 1230251876Speter} 1231251876Speter 1232251876Speterstatic int dbd_mysql_num_cols(apr_dbd_results_t *res) 1233251876Speter{ 1234251876Speter if (res->statement) { 1235251876Speter return mysql_stmt_field_count(res->statement); 1236251876Speter } 1237251876Speter else { 1238251876Speter return mysql_num_fields(res->res); 1239251876Speter } 1240251876Speter} 1241251876Speter 1242251876Speterstatic int dbd_mysql_num_tuples(apr_dbd_results_t *res) 1243251876Speter{ 1244251876Speter if (res->random) { 1245251876Speter if (res->statement) { 1246251876Speter return (int) mysql_stmt_num_rows(res->statement); 1247251876Speter } 1248251876Speter else { 1249251876Speter return (int) mysql_num_rows(res->res); 1250251876Speter } 1251251876Speter } 1252251876Speter else { 1253251876Speter return -1; 1254251876Speter } 1255251876Speter} 1256251876Speter 1257251876Speterstatic apr_status_t thread_end(void *data) 1258251876Speter{ 1259251876Speter mysql_thread_end(); 1260251876Speter return APR_SUCCESS; 1261251876Speter} 1262251876Speter 1263251876Speterstatic void dbd_mysql_init(apr_pool_t *pool) 1264251876Speter{ 1265251876Speter my_init(); 1266251876Speter mysql_thread_init(); 1267251876Speter 1268251876Speter /* FIXME: this is a guess; find out what it really does */ 1269251876Speter apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null); 1270251876Speter} 1271251876SpeterAPU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = { 1272251876Speter "mysql", 1273251876Speter dbd_mysql_init, 1274251876Speter dbd_mysql_native, 1275251876Speter dbd_mysql_open, 1276251876Speter dbd_mysql_check_conn, 1277251876Speter dbd_mysql_close, 1278251876Speter dbd_mysql_select_db, 1279251876Speter dbd_mysql_transaction, 1280251876Speter dbd_mysql_end_transaction, 1281251876Speter dbd_mysql_query, 1282251876Speter dbd_mysql_select, 1283251876Speter dbd_mysql_num_cols, 1284251876Speter dbd_mysql_num_tuples, 1285251876Speter dbd_mysql_get_row, 1286251876Speter dbd_mysql_get_entry, 1287251876Speter dbd_mysql_error, 1288251876Speter dbd_mysql_escape, 1289251876Speter dbd_mysql_prepare, 1290251876Speter dbd_mysql_pvquery, 1291251876Speter dbd_mysql_pvselect, 1292251876Speter dbd_mysql_pquery, 1293251876Speter dbd_mysql_pselect, 1294251876Speter dbd_mysql_get_name, 1295251876Speter dbd_mysql_transaction_mode_get, 1296251876Speter dbd_mysql_transaction_mode_set, 1297251876Speter "?", 1298251876Speter dbd_mysql_pvbquery, 1299251876Speter dbd_mysql_pvbselect, 1300251876Speter dbd_mysql_pbquery, 1301251876Speter dbd_mysql_pbselect, 1302251876Speter dbd_mysql_datum_get 1303251876Speter}; 1304251876Speter 1305251876Speter#endif 1306