Use of pre_cleanups is not the default for reslists.
[apr-util.git] / dbd / apr_dbd_mysql.c
blobec1d4e2332ff2344aeb474a00fe6e0f5a46116bc
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "apu.h"
18 #define HAVE_MYSQL_MYSQL_H
20 #if APU_HAVE_MYSQL
22 #include "apu_version.h"
23 #include "apu_config.h"
25 #include <ctype.h>
26 #include <stdlib.h>
28 #ifdef HAVE_MYSQL_H
29 #include <mysql.h>
30 #include <errmsg.h>
31 #elif defined(HAVE_MYSQL_MYSQL_H)
32 #include <mysql/mysql.h>
33 #include <mysql/errmsg.h>
34 #endif
36 #include "apr_strings.h"
37 #include "apr_lib.h"
38 #include "apr_buckets.h"
40 #include "apr_dbd_internal.h"
42 /* default maximum field size 1 MB */
43 #define FIELDSIZE 1048575
45 struct apr_dbd_prepared_t {
46 MYSQL_STMT* stmt;
47 int nargs;
48 int nvals;
49 apr_dbd_type_e *types;
52 struct apr_dbd_transaction_t {
53 int mode;
54 int errnum;
55 apr_dbd_t *handle;
58 struct apr_dbd_t {
59 MYSQL* conn ;
60 apr_dbd_transaction_t* trans ;
61 unsigned long fldsz;
64 struct apr_dbd_results_t {
65 int random;
66 MYSQL_RES *res;
67 MYSQL_STMT *statement;
68 MYSQL_BIND *bind;
69 apr_pool_t *pool;
71 struct apr_dbd_row_t {
72 MYSQL_ROW row;
73 apr_dbd_results_t *res;
74 unsigned long *len;
77 /* MySQL specific bucket for BLOB types */
78 typedef struct apr_bucket_lob apr_bucket_lob;
79 /**
80 * A bucket referring to a MySQL BLOB
82 struct apr_bucket_lob {
83 /** Number of buckets using this memory */
84 apr_bucket_refcount refcount;
85 /** The row this bucket refers to */
86 const apr_dbd_row_t *row;
87 /** The column this bucket refers to */
88 int col;
89 /** The pool into which any needed structures should
90 * be created while reading from this bucket */
91 apr_pool_t *readpool;
94 static void lob_bucket_destroy(void *data);
95 static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
96 apr_size_t *len, apr_read_type_e block);
97 static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
98 const apr_dbd_row_t *row, int col,
99 apr_off_t offset, apr_size_t len,
100 apr_pool_t *p);
101 static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
102 apr_off_t offset,
103 apr_size_t len, apr_pool_t *p,
104 apr_bucket_alloc_t *list);
106 static const apr_bucket_type_t apr_bucket_type_lob = {
107 "LOB", 5, APR_BUCKET_DATA,
108 lob_bucket_destroy,
109 lob_bucket_read,
110 apr_bucket_setaside_notimpl,
111 apr_bucket_shared_split,
112 apr_bucket_shared_copy
115 static void lob_bucket_destroy(void *data)
117 apr_bucket_lob *f = data;
119 if (apr_bucket_shared_destroy(f)) {
120 /* no need to destroy database objects here; it will get
121 * done automatically when the pool gets cleaned up */
122 apr_bucket_free(f);
126 static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
127 apr_size_t *len, apr_read_type_e block)
129 apr_bucket_lob *a = e->data;
130 const apr_dbd_row_t *row = a->row;
131 apr_dbd_results_t *res = row->res;
132 int col = a->col;
133 apr_bucket *b = NULL;
134 int rv;
135 apr_size_t blength = e->length; /* bytes remaining in file past offset */
136 apr_off_t boffset = e->start;
137 MYSQL_BIND *bind = &res->bind[col];
139 *str = NULL; /* in case we die prematurely */
141 /* fetch from offset if not at the beginning */
142 if (boffset > 0) {
143 rv = mysql_stmt_fetch_column(res->statement, bind, col,
144 (unsigned long) boffset);
145 if (rv != 0) {
146 return APR_EGENERAL;
149 blength -= blength > bind->buffer_length ? bind->buffer_length : blength;
150 *len = e->length - blength;
151 *str = bind->buffer;
153 /* allocate new buffer, since we used this one for the bucket */
154 bind->buffer = apr_palloc(res->pool, bind->buffer_length);
157 * Change the current bucket to refer to what we read,
158 * even if we read nothing because we hit EOF.
160 apr_bucket_pool_make(e, *str, *len, res->pool);
162 /* If we have more to read from the field, then create another bucket */
163 if (blength > 0) {
164 /* for efficiency, we can just build a new apr_bucket struct
165 * to wrap around the existing LOB bucket */
166 b = apr_bucket_alloc(sizeof(*b), e->list);
167 b->start = boffset + *len;
168 b->length = blength;
169 b->data = a;
170 b->type = &apr_bucket_type_lob;
171 b->free = apr_bucket_free;
172 b->list = e->list;
173 APR_BUCKET_INSERT_AFTER(e, b);
175 else {
176 lob_bucket_destroy(a);
179 return APR_SUCCESS;
182 static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
183 const apr_dbd_row_t *row, int col,
184 apr_off_t offset, apr_size_t len,
185 apr_pool_t *p)
187 apr_bucket_lob *f;
189 f = apr_bucket_alloc(sizeof(*f), b->list);
190 f->row = row;
191 f->col = col;
192 f->readpool = p;
194 b = apr_bucket_shared_make(b, f, offset, len);
195 b->type = &apr_bucket_type_lob;
197 return b;
200 static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
201 apr_off_t offset,
202 apr_size_t len, apr_pool_t *p,
203 apr_bucket_alloc_t *list)
205 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
207 APR_BUCKET_INIT(b);
208 b->free = apr_bucket_free;
209 b->list = list;
210 return apr_bucket_lob_make(b, row, col, offset, len, p);
213 static apr_status_t free_result(void *data)
215 mysql_free_result(data);
216 return APR_SUCCESS;
219 static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql,
220 apr_dbd_results_t **results,
221 const char *query, int seek)
223 int sz;
224 int ret;
225 if (sql->trans && sql->trans->errnum) {
226 return sql->trans->errnum;
228 ret = mysql_query(sql->conn, query);
229 if (!ret) {
230 if (sz = mysql_field_count(sql->conn), sz > 0) {
231 if (!*results) {
232 *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
234 (*results)->random = seek;
235 (*results)->statement = NULL;
236 (*results)->pool = pool;
237 if (seek) {
238 (*results)->res = mysql_store_result(sql->conn);
240 else {
241 (*results)->res = mysql_use_result(sql->conn);
243 apr_pool_cleanup_register(pool, (*results)->res,
244 free_result,apr_pool_cleanup_null);
246 } else {
247 ret = mysql_errno(sql->conn);
250 if (TXN_NOTICE_ERRORS(sql->trans)) {
251 sql->trans->errnum = ret;
253 return ret;
256 static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n)
258 if ((n < 0) || (n >= (int) mysql_num_fields(res->res))) {
259 return NULL;
262 return mysql_fetch_fields(res->res)[n].name;
265 static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
266 apr_dbd_row_t **row, int rownum)
268 MYSQL_ROW r = NULL;
269 int ret = 0;
271 if (res->statement) {
272 if (res->random) {
273 if (rownum > 0) {
274 mysql_stmt_data_seek(res->statement, (my_ulonglong) --rownum);
276 else {
277 return -1; /* invalid row */
280 ret = mysql_stmt_fetch(res->statement);
281 switch (ret) {
282 case 1:
283 ret = mysql_stmt_errno(res->statement);
284 break;
285 case MYSQL_NO_DATA:
286 ret = -1;
287 break;
288 default:
289 ret = 0; /* bad luck - get_entry will deal with this */
290 break;
293 else {
294 if (res->random) {
295 if (rownum > 0) {
296 mysql_data_seek(res->res, (my_ulonglong) --rownum);
298 else {
299 return -1; /* invalid row */
302 r = mysql_fetch_row(res->res);
303 if (r == NULL) {
304 ret = -1;
307 if (ret == 0) {
308 if (!*row) {
309 *row = apr_palloc(pool, sizeof(apr_dbd_row_t));
311 (*row)->row = r;
312 (*row)->res = res;
313 (*row)->len = mysql_fetch_lengths(res->res);
315 else {
316 apr_pool_cleanup_run(pool, res->res, free_result);
318 return ret;
320 #if 0
321 /* An improved API that was proposed but not followed up */
322 static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n,
323 apr_dbd_datum_t *val)
325 MYSQL_BIND *bind;
326 if (row->res->statement) {
327 bind = &row->res->bind[n];
328 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
329 val->type = APR_DBD_VALUE_NULL;
330 return -1;
332 if (*bind->is_null) {
333 val->type = APR_DBD_VALUE_NULL;
334 return -1;
336 else {
337 val->type = APR_DBD_VALUE_STRING;
338 val->value.stringval = bind->buffer;
341 else {
342 val->type = APR_DBD_VALUE_STRING;
343 val->value.stringval = row->row[n];
345 return 0;
347 #else
349 static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
351 MYSQL_BIND *bind;
352 if (row->res->statement) {
353 bind = &row->res->bind[n];
354 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
355 return NULL;
357 if (*bind->is_null) {
358 return NULL;
360 else {
361 return bind->buffer;
364 else {
365 return row->row[n];
367 return NULL;
369 #endif
371 static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n,
372 apr_dbd_type_e type, void *data)
374 if (row->res->statement) {
375 MYSQL_BIND *bind = &row->res->bind[n];
376 unsigned long len = *bind->length;
378 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
379 return APR_EGENERAL;
382 if (*bind->is_null) {
383 return APR_ENOENT;
386 switch (type) {
387 case APR_DBD_TYPE_TINY:
388 *(char*)data = atoi(bind->buffer);
389 break;
390 case APR_DBD_TYPE_UTINY:
391 *(unsigned char*)data = atoi(bind->buffer);
392 break;
393 case APR_DBD_TYPE_SHORT:
394 *(short*)data = atoi(bind->buffer);
395 break;
396 case APR_DBD_TYPE_USHORT:
397 *(unsigned short*)data = atoi(bind->buffer);
398 break;
399 case APR_DBD_TYPE_INT:
400 *(int*)data = atoi(bind->buffer);
401 break;
402 case APR_DBD_TYPE_UINT:
403 *(unsigned int*)data = atoi(bind->buffer);
404 break;
405 case APR_DBD_TYPE_LONG:
406 *(long*)data = atol(bind->buffer);
407 break;
408 case APR_DBD_TYPE_ULONG:
409 *(unsigned long*)data = atol(bind->buffer);
410 break;
411 case APR_DBD_TYPE_LONGLONG:
412 *(apr_int64_t*)data = apr_atoi64(bind->buffer);
413 break;
414 case APR_DBD_TYPE_ULONGLONG:
415 *(apr_uint64_t*)data = apr_atoi64(bind->buffer);
416 break;
417 case APR_DBD_TYPE_FLOAT:
418 *(float*)data = (float) atof(bind->buffer);
419 break;
420 case APR_DBD_TYPE_DOUBLE:
421 *(double*)data = atof(bind->buffer);
422 break;
423 case APR_DBD_TYPE_STRING:
424 case APR_DBD_TYPE_TEXT:
425 case APR_DBD_TYPE_TIME:
426 case APR_DBD_TYPE_DATE:
427 case APR_DBD_TYPE_DATETIME:
428 case APR_DBD_TYPE_TIMESTAMP:
429 case APR_DBD_TYPE_ZTIMESTAMP:
430 *((char*)bind->buffer+bind->buffer_length-1) = '\0';
431 *(char**)data = bind->buffer;
432 break;
433 case APR_DBD_TYPE_BLOB:
434 case APR_DBD_TYPE_CLOB:
436 apr_bucket *e;
437 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
439 e = apr_bucket_lob_create(row, n, 0, len,
440 row->res->pool, b->bucket_alloc);
441 APR_BRIGADE_INSERT_TAIL(b, e);
443 break;
444 case APR_DBD_TYPE_NULL:
445 *(void**)data = NULL;
446 break;
447 default:
448 return APR_EGENERAL;
451 else {
452 if (row->row[n] == NULL) {
453 return APR_ENOENT;
456 switch (type) {
457 case APR_DBD_TYPE_TINY:
458 *(char*)data = atoi(row->row[n]);
459 break;
460 case APR_DBD_TYPE_UTINY:
461 *(unsigned char*)data = atoi(row->row[n]);
462 break;
463 case APR_DBD_TYPE_SHORT:
464 *(short*)data = atoi(row->row[n]);
465 break;
466 case APR_DBD_TYPE_USHORT:
467 *(unsigned short*)data = atoi(row->row[n]);
468 break;
469 case APR_DBD_TYPE_INT:
470 *(int*)data = atoi(row->row[n]);
471 break;
472 case APR_DBD_TYPE_UINT:
473 *(unsigned int*)data = atoi(row->row[n]);
474 break;
475 case APR_DBD_TYPE_LONG:
476 *(long*)data = atol(row->row[n]);
477 break;
478 case APR_DBD_TYPE_ULONG:
479 *(unsigned long*)data = atol(row->row[n]);
480 break;
481 case APR_DBD_TYPE_LONGLONG:
482 *(apr_int64_t*)data = apr_atoi64(row->row[n]);
483 break;
484 case APR_DBD_TYPE_ULONGLONG:
485 *(apr_uint64_t*)data = apr_atoi64(row->row[n]);
486 break;
487 case APR_DBD_TYPE_FLOAT:
488 *(float*)data = (float) atof(row->row[n]);
489 break;
490 case APR_DBD_TYPE_DOUBLE:
491 *(double*)data = atof(row->row[n]);
492 break;
493 case APR_DBD_TYPE_STRING:
494 case APR_DBD_TYPE_TEXT:
495 case APR_DBD_TYPE_TIME:
496 case APR_DBD_TYPE_DATE:
497 case APR_DBD_TYPE_DATETIME:
498 case APR_DBD_TYPE_TIMESTAMP:
499 case APR_DBD_TYPE_ZTIMESTAMP:
500 *(char**)data = row->row[n];
501 break;
502 case APR_DBD_TYPE_BLOB:
503 case APR_DBD_TYPE_CLOB:
505 apr_bucket *e;
506 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
508 e = apr_bucket_pool_create(row->row[n], row->len[n],
509 row->res->pool, b->bucket_alloc);
510 APR_BRIGADE_INSERT_TAIL(b, e);
512 break;
513 case APR_DBD_TYPE_NULL:
514 *(void**)data = NULL;
515 break;
516 default:
517 return APR_EGENERAL;
520 return 0;
523 static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
525 return mysql_error(sql->conn);
528 static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
530 int ret;
531 if (sql->trans && sql->trans->errnum) {
532 return sql->trans->errnum;
534 ret = mysql_query(sql->conn, query);
535 if (ret != 0) {
536 ret = mysql_errno(sql->conn);
538 *nrows = (int) mysql_affected_rows(sql->conn);
539 if (TXN_NOTICE_ERRORS(sql->trans)) {
540 sql->trans->errnum = ret;
542 return ret;
545 static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
546 apr_dbd_t *sql)
548 unsigned long len = strlen(arg);
549 char *ret = apr_palloc(pool, 2*len + 1);
550 mysql_real_escape_string(sql->conn, ret, arg, len);
551 return ret;
554 static apr_status_t stmt_close(void *data)
556 mysql_stmt_close(data);
557 return APR_SUCCESS;
560 static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
561 const char *query, const char *label,
562 int nargs, int nvals, apr_dbd_type_e *types,
563 apr_dbd_prepared_t **statement)
565 /* Translate from apr_dbd to native query format */
566 int ret;
568 if (!*statement) {
569 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
571 (*statement)->stmt = mysql_stmt_init(sql->conn);
573 if ((*statement)->stmt) {
574 apr_pool_cleanup_register(pool, (*statement)->stmt,
575 stmt_close, apr_pool_cleanup_null);
576 ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query));
578 if (ret != 0) {
579 ret = mysql_stmt_errno((*statement)->stmt);
582 (*statement)->nargs = nargs;
583 (*statement)->nvals = nvals;
584 (*statement)->types = types;
586 return ret;
589 return CR_OUT_OF_MEMORY;
592 static void dbd_mysql_bind(apr_dbd_prepared_t *statement,
593 const char **values, MYSQL_BIND *bind)
595 int i, j;
597 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
598 bind[i].length = &bind[i].buffer_length;
599 bind[i].is_unsigned = 0;
600 bind[i].is_null = NULL;
602 if (values[j] == NULL) {
603 bind[i].buffer_type = MYSQL_TYPE_NULL;
605 else {
606 switch (statement->types[i]) {
607 case APR_DBD_TYPE_BLOB:
608 case APR_DBD_TYPE_CLOB:
609 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
610 bind[i].buffer = (void*)values[j];
611 bind[i].buffer_length = atol(values[++j]);
613 /* skip table and column */
614 j += 2;
615 break;
616 default:
617 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
618 bind[i].buffer = (void*)values[j];
619 bind[i].buffer_length = strlen(values[j]);
620 break;
625 return;
628 static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
629 int *nrows, apr_dbd_prepared_t *statement,
630 MYSQL_BIND *bind)
632 int ret;
634 ret = mysql_stmt_bind_param(statement->stmt, bind);
635 if (ret != 0) {
636 *nrows = 0;
637 ret = mysql_stmt_errno(statement->stmt);
639 else {
640 ret = mysql_stmt_execute(statement->stmt);
641 if (ret != 0) {
642 ret = mysql_stmt_errno(statement->stmt);
644 *nrows = (int) mysql_stmt_affected_rows(statement->stmt);
647 return ret;
650 static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
651 int *nrows, apr_dbd_prepared_t *statement,
652 const char **values)
654 MYSQL_BIND *bind;
655 int ret;
657 if (sql->trans && sql->trans->errnum) {
658 return sql->trans->errnum;
661 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
663 dbd_mysql_bind(statement, values, bind);
665 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
667 if (TXN_NOTICE_ERRORS(sql->trans)) {
668 sql->trans->errnum = ret;
670 return ret;
673 static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
674 apr_dbd_prepared_t *statement, va_list args)
676 const char **values;
677 int i;
679 if (sql->trans && sql->trans->errnum) {
680 return sql->trans->errnum;
683 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
685 for (i = 0; i < statement->nvals; i++) {
686 values[i] = va_arg(args, const char*);
689 return dbd_mysql_pquery(pool, sql, nrows, statement, values);
692 static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
693 apr_dbd_results_t **res,
694 apr_dbd_prepared_t *statement,
695 int random, MYSQL_BIND *bind)
697 int nfields, i;
698 my_bool *is_nullr;
699 #if MYSQL_VERSION_ID >= 50000
700 my_bool *error;
701 #endif
702 int ret;
703 unsigned long *length, maxlen;
705 ret = mysql_stmt_bind_param(statement->stmt, bind);
706 if (ret == 0) {
707 ret = mysql_stmt_execute(statement->stmt);
708 if (!ret) {
709 if (!*res) {
710 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
712 (*res)->random = random;
713 (*res)->statement = statement->stmt;
714 (*res)->res = mysql_stmt_result_metadata(statement->stmt);
715 (*res)->pool = pool;
716 apr_pool_cleanup_register(pool, (*res)->res,
717 free_result, apr_pool_cleanup_null);
718 nfields = mysql_num_fields((*res)->res);
719 if (!(*res)->bind) {
720 (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
721 length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
722 #if MYSQL_VERSION_ID >= 50000
723 error = apr_palloc(pool, nfields*sizeof(my_bool));
724 #endif
725 is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
726 for ( i = 0; i < nfields; ++i ) {
727 maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
728 (*res)->res->fields[i].length : sql->fldsz) + 1;
729 if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) {
730 (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
732 else {
733 (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
735 (*res)->bind[i].buffer_length = maxlen;
736 (*res)->bind[i].length = &length[i];
737 (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
738 (*res)->bind[i].is_null = is_nullr+i;
739 #if MYSQL_VERSION_ID >= 50000
740 (*res)->bind[i].error = error+i;
741 #endif
744 ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
745 if (!ret) {
746 ret = mysql_stmt_store_result(statement->stmt);
750 if (ret != 0) {
751 ret = mysql_stmt_errno(statement->stmt);
754 return ret;
757 static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
758 apr_dbd_results_t **res,
759 apr_dbd_prepared_t *statement, int random,
760 const char **args)
762 int ret;
763 MYSQL_BIND *bind;
765 if (sql->trans && sql->trans->errnum) {
766 return sql->trans->errnum;
769 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
771 dbd_mysql_bind(statement, args, bind);
773 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
775 if (TXN_NOTICE_ERRORS(sql->trans)) {
776 sql->trans->errnum = ret;
778 return ret;
781 static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
782 apr_dbd_results_t **res,
783 apr_dbd_prepared_t *statement, int random,
784 va_list args)
786 const char **values;
787 int i;
789 if (sql->trans && sql->trans->errnum) {
790 return sql->trans->errnum;
793 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
795 for (i = 0; i < statement->nvals; i++) {
796 values[i] = va_arg(args, const char*);
799 return dbd_mysql_pselect(pool, sql, res, statement, random, values);
802 static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement,
803 const void **values, MYSQL_BIND *bind)
805 void *arg;
806 int i, j;
807 apr_dbd_type_e type;
809 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
810 arg = (void *)values[j];
812 bind[i].length = &bind[i].buffer_length;
813 bind[i].is_null = NULL;
815 type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
816 switch (type) {
817 case APR_DBD_TYPE_TINY:
818 bind[i].buffer = arg;
819 bind[i].buffer_type = MYSQL_TYPE_TINY;
820 bind[i].is_unsigned = 0;
821 break;
822 case APR_DBD_TYPE_UTINY:
823 bind[i].buffer = arg;
824 bind[i].buffer_type = MYSQL_TYPE_TINY;
825 bind[i].is_unsigned = 1;
826 break;
827 case APR_DBD_TYPE_SHORT:
828 bind[i].buffer = arg;
829 bind[i].buffer_type = MYSQL_TYPE_SHORT;
830 bind[i].is_unsigned = 0;
831 break;
832 case APR_DBD_TYPE_USHORT:
833 bind[i].buffer = arg;
834 bind[i].buffer_type = MYSQL_TYPE_SHORT;
835 bind[i].is_unsigned = 1;
836 break;
837 case APR_DBD_TYPE_INT:
838 bind[i].buffer = arg;
839 bind[i].buffer_type = MYSQL_TYPE_LONG;
840 bind[i].is_unsigned = 0;
841 break;
842 case APR_DBD_TYPE_UINT:
843 bind[i].buffer = arg;
844 bind[i].buffer_type = MYSQL_TYPE_LONG;
845 bind[i].is_unsigned = 1;
846 break;
847 case APR_DBD_TYPE_LONG:
848 if (sizeof(int) == sizeof(long)) {
849 bind[i].buffer = arg;
851 else {
852 bind[i].buffer = apr_palloc(pool, sizeof(int));
853 *(int*)bind[i].buffer = *(long*)arg;
855 bind[i].buffer_type = MYSQL_TYPE_LONG;
856 bind[i].is_unsigned = 0;
857 break;
858 case APR_DBD_TYPE_ULONG:
859 if (sizeof(unsigned int) == sizeof(unsigned long)) {
860 bind[i].buffer = arg;
862 else {
863 bind[i].buffer = apr_palloc(pool, sizeof(unsigned int));
864 *(unsigned int*)bind[i].buffer = *(unsigned long*)arg;
866 bind[i].buffer_type = MYSQL_TYPE_LONG;
867 bind[i].is_unsigned = 1;
868 break;
869 case APR_DBD_TYPE_LONGLONG:
870 if (sizeof(my_ulonglong) == sizeof(apr_int64_t)) {
871 bind[i].buffer = arg;
872 bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
874 else { /* have to downsize, long long is not portable */
875 bind[i].buffer = apr_palloc(pool, sizeof(long));
876 *(long*)bind[i].buffer = (long) *(apr_int64_t*)arg;
877 bind[i].buffer_type = MYSQL_TYPE_LONG;
879 bind[i].is_unsigned = 0;
880 break;
881 case APR_DBD_TYPE_ULONGLONG:
882 if (sizeof(my_ulonglong) == sizeof(apr_uint64_t)) {
883 bind[i].buffer = arg;
884 bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
886 else { /* have to downsize, long long is not portable */
887 bind[i].buffer = apr_palloc(pool, sizeof(long));
888 *(unsigned long*)bind[i].buffer =
889 (unsigned long) *(apr_uint64_t*)arg;
890 bind[i].buffer_type = MYSQL_TYPE_LONG;
892 bind[i].is_unsigned = 1;
893 break;
894 case APR_DBD_TYPE_FLOAT:
895 bind[i].buffer = arg;
896 bind[i].buffer_type = MYSQL_TYPE_FLOAT;
897 bind[i].is_unsigned = 0;
898 break;
899 case APR_DBD_TYPE_DOUBLE:
900 bind[i].buffer = arg;
901 bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
902 bind[i].is_unsigned = 0;
903 break;
904 case APR_DBD_TYPE_STRING:
905 case APR_DBD_TYPE_TEXT:
906 case APR_DBD_TYPE_TIME:
907 case APR_DBD_TYPE_DATE:
908 case APR_DBD_TYPE_DATETIME:
909 case APR_DBD_TYPE_TIMESTAMP:
910 case APR_DBD_TYPE_ZTIMESTAMP:
911 bind[i].buffer = arg;
912 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
913 bind[i].is_unsigned = 0;
914 bind[i].buffer_length = strlen((const char *)arg);
915 break;
916 case APR_DBD_TYPE_BLOB:
917 case APR_DBD_TYPE_CLOB:
918 bind[i].buffer = (void *)arg;
919 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
920 bind[i].is_unsigned = 0;
921 bind[i].buffer_length = *(apr_size_t*)values[++j];
923 /* skip table and column */
924 j += 2;
925 break;
926 case APR_DBD_TYPE_NULL:
927 default:
928 bind[i].buffer_type = MYSQL_TYPE_NULL;
929 break;
933 return;
936 static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql,
937 int *nrows, apr_dbd_prepared_t *statement,
938 const void **values)
940 MYSQL_BIND *bind;
941 int ret;
943 if (sql->trans && sql->trans->errnum) {
944 return sql->trans->errnum;
947 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
949 dbd_mysql_bbind(pool, statement, values, bind);
951 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
953 if (TXN_NOTICE_ERRORS(sql->trans)) {
954 sql->trans->errnum = ret;
956 return ret;
959 static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
960 apr_dbd_prepared_t *statement, va_list args)
962 const void **values;
963 int i;
965 if (sql->trans && sql->trans->errnum) {
966 return sql->trans->errnum;
969 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
971 for (i = 0; i < statement->nvals; i++) {
972 values[i] = va_arg(args, const void*);
975 return dbd_mysql_pbquery(pool, sql, nrows, statement, values);
978 static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
979 apr_dbd_results_t **res,
980 apr_dbd_prepared_t *statement, int random,
981 const void **args)
983 int ret;
984 MYSQL_BIND *bind;
986 if (sql->trans && sql->trans->errnum) {
987 return sql->trans->errnum;
990 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
992 dbd_mysql_bbind(pool, statement, args, bind);
994 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
996 if (TXN_NOTICE_ERRORS(sql->trans)) {
997 sql->trans->errnum = ret;
999 return ret;
1002 static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
1003 apr_dbd_results_t **res,
1004 apr_dbd_prepared_t *statement, int random,
1005 va_list args)
1007 const void **values;
1008 int i;
1010 if (sql->trans && sql->trans->errnum) {
1011 return sql->trans->errnum;
1014 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1016 for (i = 0; i < statement->nvals; i++) {
1017 values[i] = va_arg(args, const void*);
1020 return dbd_mysql_pbselect(pool, sql, res, statement, random, values);
1023 static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
1025 int ret = -1;
1026 if (trans) {
1027 /* rollback on error or explicit rollback request */
1028 if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1029 trans->errnum = 0;
1030 ret = mysql_rollback(trans->handle->conn);
1032 else {
1033 ret = mysql_commit(trans->handle->conn);
1036 ret |= mysql_autocommit(trans->handle->conn, 1);
1037 trans->handle->trans = NULL;
1038 return ret;
1040 /* Whether or not transactions work depends on whether the
1041 * underlying DB supports them within MySQL. Unfortunately
1042 * it fails silently with the default InnoDB.
1045 static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1046 apr_dbd_transaction_t **trans)
1048 /* Don't try recursive transactions here */
1049 if (handle->trans) {
1050 dbd_mysql_end_transaction(handle->trans) ;
1052 if (!*trans) {
1053 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1055 (*trans)->errnum = mysql_autocommit(handle->conn, 0);
1056 (*trans)->handle = handle;
1057 handle->trans = *trans;
1058 return (*trans)->errnum;
1061 static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
1063 if (!trans)
1064 return APR_DBD_TRANSACTION_COMMIT;
1066 return trans->mode;
1069 static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans,
1070 int mode)
1072 if (!trans)
1073 return APR_DBD_TRANSACTION_COMMIT;
1075 return trans->mode = (mode & TXN_MODE_BITS);
1078 static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params,
1079 const char **error)
1081 static const char *const delims = " \r\n\t;|,";
1082 const char *ptr;
1083 int i;
1084 const char *key;
1085 size_t klen;
1086 const char *value;
1087 size_t vlen;
1088 #if MYSQL_VERSION_ID >= 50013
1089 my_bool do_reconnect = 1;
1090 #endif
1091 MYSQL *real_conn;
1092 unsigned long flags = 0;
1094 struct {
1095 const char *field;
1096 const char *value;
1097 } fields[] = {
1098 {"host", NULL},
1099 {"user", NULL},
1100 {"pass", NULL},
1101 {"dbname", NULL},
1102 {"port", NULL},
1103 {"sock", NULL},
1104 {"flags", NULL},
1105 {"fldsz", NULL},
1106 {"group", NULL},
1107 {"reconnect", NULL},
1108 {NULL, NULL}
1110 unsigned int port = 0;
1111 apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t));
1112 sql->fldsz = FIELDSIZE;
1113 sql->conn = mysql_init(sql->conn);
1114 if ( sql->conn == NULL ) {
1115 return NULL;
1117 for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
1118 /* don't dereference memory that may not belong to us */
1119 if (ptr == params) {
1120 ++ptr;
1121 continue;
1123 for (key = ptr-1; apr_isspace(*key); --key);
1124 klen = 0;
1125 while (apr_isalpha(*key)) {
1126 /* don't parse backwards off the start of the string */
1127 if (key == params) {
1128 --key;
1129 ++klen;
1130 break;
1132 --key;
1133 ++klen;
1135 ++key;
1136 for (value = ptr+1; apr_isspace(*value); ++value);
1137 vlen = strcspn(value, delims);
1138 for (i = 0; fields[i].field != NULL; i++) {
1139 if (!strncasecmp(fields[i].field, key, klen)) {
1140 fields[i].value = apr_pstrndup(pool, value, vlen);
1141 break;
1144 ptr = value+vlen;
1146 if (fields[4].value != NULL) {
1147 port = atoi(fields[4].value);
1149 if (fields[6].value != NULL &&
1150 !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) {
1151 flags |= CLIENT_FOUND_ROWS; /* only option we know */
1153 if (fields[7].value != NULL) {
1154 sql->fldsz = atol(fields[7].value);
1156 if (fields[8].value != NULL) {
1157 mysql_options(sql->conn, MYSQL_READ_DEFAULT_GROUP, fields[8].value);
1159 #if MYSQL_VERSION_ID >= 50013
1160 if (fields[9].value != NULL) {
1161 do_reconnect = atoi(fields[9].value) ? 1 : 0;
1163 #endif
1165 #if MYSQL_VERSION_ID >= 50013
1166 /* the MySQL manual says this should be BEFORE mysql_real_connect */
1167 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
1168 #endif
1170 real_conn = mysql_real_connect(sql->conn, fields[0].value,
1171 fields[1].value, fields[2].value,
1172 fields[3].value, port,
1173 fields[5].value, flags);
1175 if(real_conn == NULL) {
1176 if (error) {
1177 *error = apr_pstrdup(pool, mysql_error(sql->conn));
1179 mysql_close(sql->conn);
1180 return NULL;
1183 #if MYSQL_VERSION_ID >= 50013
1184 /* Some say this should be AFTER mysql_real_connect */
1185 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
1186 #endif
1188 return sql;
1191 static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
1193 mysql_close(handle->conn);
1194 return APR_SUCCESS;
1197 static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
1198 apr_dbd_t *handle)
1200 return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
1203 static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
1204 const char* name)
1206 return mysql_select_db(handle->conn, name);
1209 static void *dbd_mysql_native(apr_dbd_t *handle)
1211 return handle->conn;
1214 static int dbd_mysql_num_cols(apr_dbd_results_t *res)
1216 if (res->statement) {
1217 return mysql_stmt_field_count(res->statement);
1219 else {
1220 return mysql_num_fields(res->res);
1224 static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
1226 if (res->random) {
1227 if (res->statement) {
1228 return (int) mysql_stmt_num_rows(res->statement);
1230 else {
1231 return (int) mysql_num_rows(res->res);
1234 else {
1235 return -1;
1239 static apr_status_t thread_end(void *data)
1241 mysql_thread_end();
1242 return APR_SUCCESS;
1245 static void dbd_mysql_init(apr_pool_t *pool)
1247 my_init();
1248 mysql_thread_init();
1250 /* FIXME: this is a guess; find out what it really does */
1251 apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null);
1253 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = {
1254 "mysql",
1255 dbd_mysql_init,
1256 dbd_mysql_native,
1257 dbd_mysql_open,
1258 dbd_mysql_check_conn,
1259 dbd_mysql_close,
1260 dbd_mysql_select_db,
1261 dbd_mysql_transaction,
1262 dbd_mysql_end_transaction,
1263 dbd_mysql_query,
1264 dbd_mysql_select,
1265 dbd_mysql_num_cols,
1266 dbd_mysql_num_tuples,
1267 dbd_mysql_get_row,
1268 dbd_mysql_get_entry,
1269 dbd_mysql_error,
1270 dbd_mysql_escape,
1271 dbd_mysql_prepare,
1272 dbd_mysql_pvquery,
1273 dbd_mysql_pvselect,
1274 dbd_mysql_pquery,
1275 dbd_mysql_pselect,
1276 dbd_mysql_get_name,
1277 dbd_mysql_transaction_mode_get,
1278 dbd_mysql_transaction_mode_set,
1279 "?",
1280 dbd_mysql_pvbquery,
1281 dbd_mysql_pvbselect,
1282 dbd_mysql_pbquery,
1283 dbd_mysql_pbselect,
1284 dbd_mysql_datum_get
1287 #endif