Make sure macro int_errorcode is last declaration
[apr-util.git] / dbd / apr_dbd_mysql.c
blob58d2aad3f8aa6649c114e80dc1bbc8ac9740b19d
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_buckets.h"
39 #include "apr_dbd_internal.h"
41 /* default maximum field size 1 MB */
42 #define FIELDSIZE 1048575
44 struct apr_dbd_prepared_t {
45 MYSQL_STMT* stmt;
46 int nargs;
47 int nvals;
48 apr_dbd_type_e *types;
51 struct apr_dbd_transaction_t {
52 int mode;
53 int errnum;
54 apr_dbd_t *handle;
57 struct apr_dbd_t {
58 MYSQL* conn ;
59 apr_dbd_transaction_t* trans ;
60 unsigned long fldsz;
63 struct apr_dbd_results_t {
64 int random;
65 MYSQL_RES *res;
66 MYSQL_STMT *statement;
67 MYSQL_BIND *bind;
68 apr_pool_t *pool;
70 struct apr_dbd_row_t {
71 MYSQL_ROW row;
72 apr_dbd_results_t *res;
73 unsigned long *len;
76 /* MySQL specific bucket for BLOB types */
77 typedef struct apr_bucket_lob apr_bucket_lob;
78 /**
79 * A bucket referring to a MySQL BLOB
81 struct apr_bucket_lob {
82 /** Number of buckets using this memory */
83 apr_bucket_refcount refcount;
84 /** The row this bucket refers to */
85 const apr_dbd_row_t *row;
86 /** The column this bucket refers to */
87 int col;
88 /** The pool into which any needed structures should
89 * be created while reading from this bucket */
90 apr_pool_t *readpool;
93 static void lob_bucket_destroy(void *data);
94 static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
95 apr_size_t *len, apr_read_type_e block);
96 static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
97 const apr_dbd_row_t *row, int col,
98 apr_off_t offset, apr_size_t len,
99 apr_pool_t *p);
100 static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
101 apr_off_t offset,
102 apr_size_t len, apr_pool_t *p,
103 apr_bucket_alloc_t *list);
105 static const apr_bucket_type_t apr_bucket_type_lob = {
106 "LOB", 5, APR_BUCKET_DATA,
107 lob_bucket_destroy,
108 lob_bucket_read,
109 apr_bucket_setaside_notimpl,
110 apr_bucket_shared_split,
111 apr_bucket_shared_copy
114 static void lob_bucket_destroy(void *data)
116 apr_bucket_lob *f = data;
118 if (apr_bucket_shared_destroy(f)) {
119 /* no need to destroy database objects here; it will get
120 * done automatically when the pool gets cleaned up */
121 apr_bucket_free(f);
125 static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
126 apr_size_t *len, apr_read_type_e block)
128 apr_bucket_lob *a = e->data;
129 const apr_dbd_row_t *row = a->row;
130 apr_dbd_results_t *res = row->res;
131 int col = a->col;
132 apr_bucket *b = NULL;
133 int rv;
134 apr_size_t blength = e->length; /* bytes remaining in file past offset */
135 apr_off_t boffset = e->start;
136 MYSQL_BIND *bind = &res->bind[col];
138 *str = NULL; /* in case we die prematurely */
140 /* fetch from offset if not at the beginning */
141 if (boffset > 0) {
142 rv = mysql_stmt_fetch_column(res->statement, bind, col, boffset);
143 if (rv != 0) {
144 return APR_EGENERAL;
147 blength -= blength > bind->buffer_length ? bind->buffer_length : blength;
148 *len = e->length - blength;
149 *str = bind->buffer;
151 /* allocate new buffer, since we used this one for the bucket */
152 bind->buffer = apr_palloc(res->pool, bind->buffer_length);
155 * Change the current bucket to refer to what we read,
156 * even if we read nothing because we hit EOF.
158 apr_bucket_pool_make(e, *str, *len, res->pool);
160 /* If we have more to read from the field, then create another bucket */
161 if (blength > 0) {
162 /* for efficiency, we can just build a new apr_bucket struct
163 * to wrap around the existing LOB bucket */
164 b = apr_bucket_alloc(sizeof(*b), e->list);
165 b->start = boffset + *len;
166 b->length = blength;
167 b->data = a;
168 b->type = &apr_bucket_type_lob;
169 b->free = apr_bucket_free;
170 b->list = e->list;
171 APR_BUCKET_INSERT_AFTER(e, b);
173 else {
174 lob_bucket_destroy(a);
177 return APR_SUCCESS;
180 static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
181 const apr_dbd_row_t *row, int col,
182 apr_off_t offset, apr_size_t len,
183 apr_pool_t *p)
185 apr_bucket_lob *f;
187 f = apr_bucket_alloc(sizeof(*f), b->list);
188 f->row = row;
189 f->col = col;
190 f->readpool = p;
192 b = apr_bucket_shared_make(b, f, offset, len);
193 b->type = &apr_bucket_type_lob;
195 return b;
198 static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
199 apr_off_t offset,
200 apr_size_t len, apr_pool_t *p,
201 apr_bucket_alloc_t *list)
203 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
205 APR_BUCKET_INIT(b);
206 b->free = apr_bucket_free;
207 b->list = list;
208 return apr_bucket_lob_make(b, row, col, offset, len, p);
211 static apr_status_t free_result(void *data)
213 mysql_free_result(data);
214 return APR_SUCCESS;
217 static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql,
218 apr_dbd_results_t **results,
219 const char *query, int seek)
221 int sz;
222 int ret;
223 if (sql->trans && sql->trans->errnum) {
224 return sql->trans->errnum;
226 ret = mysql_query(sql->conn, query);
227 if (!ret) {
228 if (sz = mysql_field_count(sql->conn), sz > 0) {
229 if (!*results) {
230 *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
232 (*results)->random = seek;
233 (*results)->statement = NULL;
234 (*results)->pool = pool;
235 if (seek) {
236 (*results)->res = mysql_store_result(sql->conn);
238 else {
239 (*results)->res = mysql_use_result(sql->conn);
241 apr_pool_cleanup_register(pool, (*results)->res,
242 free_result,apr_pool_cleanup_null);
244 } else {
245 ret = mysql_errno(sql->conn);
248 if (TXN_NOTICE_ERRORS(sql->trans)) {
249 sql->trans->errnum = ret;
251 return ret;
254 static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n)
256 if ((n < 0) || (n >= mysql_num_fields(res->res))) {
257 return NULL;
260 return mysql_fetch_fields(res->res)[n].name;
263 static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
264 apr_dbd_row_t **row, int rownum)
266 MYSQL_ROW r = NULL;
267 int ret = 0;
269 if (res->statement) {
270 if (res->random) {
271 if (rownum >= 0) {
272 mysql_stmt_data_seek(res->statement, (my_ulonglong)rownum);
275 ret = mysql_stmt_fetch(res->statement);
276 switch (ret) {
277 case 1:
278 ret = mysql_stmt_errno(res->statement);
279 break;
280 case MYSQL_NO_DATA:
281 ret = -1;
282 break;
283 default:
284 ret = 0; /* bad luck - get_entry will deal with this */
285 break;
288 else {
289 if (res->random) {
290 if (rownum >= 0) {
291 mysql_data_seek(res->res, (my_ulonglong) rownum);
294 r = mysql_fetch_row(res->res);
295 if (r == NULL) {
296 ret = -1;
299 if (ret == 0) {
300 if (!*row) {
301 *row = apr_palloc(pool, sizeof(apr_dbd_row_t));
303 (*row)->row = r;
304 (*row)->res = res;
305 (*row)->len = mysql_fetch_lengths(res->res);
307 else {
308 apr_pool_cleanup_run(pool, res->res, free_result);
310 return ret;
312 #if 0
313 /* An improved API that was proposed but not followed up */
314 static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n,
315 apr_dbd_datum_t *val)
317 MYSQL_BIND *bind;
318 if (row->res->statement) {
319 bind = &row->res->bind[n];
320 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
321 val->type = APR_DBD_VALUE_NULL;
322 return -1;
324 if (*bind->is_null) {
325 val->type = APR_DBD_VALUE_NULL;
326 return -1;
328 else {
329 val->type = APR_DBD_VALUE_STRING;
330 val->value.stringval = bind->buffer;
333 else {
334 val->type = APR_DBD_VALUE_STRING;
335 val->value.stringval = row->row[n];
337 return 0;
339 #else
341 static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
343 MYSQL_BIND *bind;
344 if (row->res->statement) {
345 bind = &row->res->bind[n];
346 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
347 return NULL;
349 if (*bind->is_null) {
350 return NULL;
352 else {
353 return bind->buffer;
356 else {
357 return row->row[n];
359 return NULL;
361 #endif
363 static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n,
364 apr_dbd_type_e type, void *data)
366 if (row->res->statement) {
367 MYSQL_BIND *bind = &row->res->bind[n];
368 unsigned long len = *bind->length;
370 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
371 return APR_EGENERAL;
374 if (*bind->is_null) {
375 return APR_ENOENT;
378 switch (type) {
379 case APR_DBD_TYPE_TINY:
380 *(char*)data = atoi(bind->buffer);
381 break;
382 case APR_DBD_TYPE_UTINY:
383 *(unsigned char*)data = atoi(bind->buffer);
384 break;
385 case APR_DBD_TYPE_SHORT:
386 *(short*)data = atoi(bind->buffer);
387 break;
388 case APR_DBD_TYPE_USHORT:
389 *(unsigned short*)data = atoi(bind->buffer);
390 break;
391 case APR_DBD_TYPE_INT:
392 *(int*)data = atoi(bind->buffer);
393 break;
394 case APR_DBD_TYPE_UINT:
395 *(unsigned int*)data = atoi(bind->buffer);
396 break;
397 case APR_DBD_TYPE_LONG:
398 *(long*)data = atol(bind->buffer);
399 break;
400 case APR_DBD_TYPE_ULONG:
401 *(unsigned long*)data = atol(bind->buffer);
402 break;
403 case APR_DBD_TYPE_LONGLONG:
404 *(apr_int64_t*)data = apr_atoi64(bind->buffer);
405 break;
406 case APR_DBD_TYPE_ULONGLONG:
407 *(apr_uint64_t*)data = apr_atoi64(bind->buffer);
408 break;
409 case APR_DBD_TYPE_FLOAT:
410 *(float*)data = atof(bind->buffer);
411 break;
412 case APR_DBD_TYPE_DOUBLE:
413 *(double*)data = atof(bind->buffer);
414 break;
415 case APR_DBD_TYPE_STRING:
416 case APR_DBD_TYPE_TEXT:
417 case APR_DBD_TYPE_TIME:
418 case APR_DBD_TYPE_DATE:
419 case APR_DBD_TYPE_DATETIME:
420 case APR_DBD_TYPE_TIMESTAMP:
421 case APR_DBD_TYPE_ZTIMESTAMP:
422 *((char*)bind->buffer+bind->buffer_length-1) = '\0';
423 *(char**)data = bind->buffer;
424 break;
425 case APR_DBD_TYPE_BLOB:
426 case APR_DBD_TYPE_CLOB:
428 apr_bucket *e;
429 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
431 e = apr_bucket_lob_create(row, n, 0, len,
432 row->res->pool, b->bucket_alloc);
433 APR_BRIGADE_INSERT_TAIL(b, e);
435 break;
436 case APR_DBD_TYPE_NULL:
437 *(void**)data = NULL;
438 break;
439 default:
440 return APR_EGENERAL;
443 else {
444 if (row->row[n] == NULL) {
445 return APR_ENOENT;
448 switch (type) {
449 case APR_DBD_TYPE_TINY:
450 *(char*)data = atoi(row->row[n]);
451 break;
452 case APR_DBD_TYPE_UTINY:
453 *(unsigned char*)data = atoi(row->row[n]);
454 break;
455 case APR_DBD_TYPE_SHORT:
456 *(short*)data = atoi(row->row[n]);
457 break;
458 case APR_DBD_TYPE_USHORT:
459 *(unsigned short*)data = atoi(row->row[n]);
460 break;
461 case APR_DBD_TYPE_INT:
462 *(int*)data = atoi(row->row[n]);
463 break;
464 case APR_DBD_TYPE_UINT:
465 *(unsigned int*)data = atoi(row->row[n]);
466 break;
467 case APR_DBD_TYPE_LONG:
468 *(long*)data = atol(row->row[n]);
469 break;
470 case APR_DBD_TYPE_ULONG:
471 *(unsigned long*)data = atol(row->row[n]);
472 break;
473 case APR_DBD_TYPE_LONGLONG:
474 *(apr_int64_t*)data = apr_atoi64(row->row[n]);
475 break;
476 case APR_DBD_TYPE_ULONGLONG:
477 *(apr_uint64_t*)data = apr_atoi64(row->row[n]);
478 break;
479 case APR_DBD_TYPE_FLOAT:
480 *(float*)data = atof(row->row[n]);
481 break;
482 case APR_DBD_TYPE_DOUBLE:
483 *(double*)data = atof(row->row[n]);
484 break;
485 case APR_DBD_TYPE_STRING:
486 case APR_DBD_TYPE_TEXT:
487 case APR_DBD_TYPE_TIME:
488 case APR_DBD_TYPE_DATE:
489 case APR_DBD_TYPE_DATETIME:
490 case APR_DBD_TYPE_TIMESTAMP:
491 case APR_DBD_TYPE_ZTIMESTAMP:
492 *(char**)data = row->row[n];
493 break;
494 case APR_DBD_TYPE_BLOB:
495 case APR_DBD_TYPE_CLOB:
497 apr_bucket *e;
498 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
500 e = apr_bucket_pool_create(row->row[n], row->len[n],
501 row->res->pool, b->bucket_alloc);
502 APR_BRIGADE_INSERT_TAIL(b, e);
504 break;
505 case APR_DBD_TYPE_NULL:
506 *(void**)data = NULL;
507 break;
508 default:
509 return APR_EGENERAL;
512 return 0;
515 static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
517 return mysql_error(sql->conn);
520 static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
522 int ret;
523 if (sql->trans && sql->trans->errnum) {
524 return sql->trans->errnum;
526 ret = mysql_query(sql->conn, query);
527 if (ret != 0) {
528 ret = mysql_errno(sql->conn);
530 *nrows = mysql_affected_rows(sql->conn);
531 if (TXN_NOTICE_ERRORS(sql->trans)) {
532 sql->trans->errnum = ret;
534 return ret;
537 static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
538 apr_dbd_t *sql)
540 unsigned long len = strlen(arg);
541 char *ret = apr_palloc(pool, 2*len + 1);
542 mysql_real_escape_string(sql->conn, ret, arg, len);
543 return ret;
546 static apr_status_t stmt_close(void *data)
548 mysql_stmt_close(data);
549 return APR_SUCCESS;
552 static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
553 const char *query, const char *label,
554 int nargs, int nvals, apr_dbd_type_e *types,
555 apr_dbd_prepared_t **statement)
557 /* Translate from apr_dbd to native query format */
558 int ret;
560 if (!*statement) {
561 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
563 (*statement)->stmt = mysql_stmt_init(sql->conn);
565 if ((*statement)->stmt) {
566 apr_pool_cleanup_register(pool, (*statement)->stmt,
567 stmt_close, apr_pool_cleanup_null);
568 ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query));
570 if (ret != 0) {
571 ret = mysql_stmt_errno((*statement)->stmt);
574 (*statement)->nargs = nargs;
575 (*statement)->nvals = nvals;
576 (*statement)->types = types;
578 return ret;
581 return CR_OUT_OF_MEMORY;
584 static void dbd_mysql_bind(apr_dbd_prepared_t *statement,
585 const char **values, MYSQL_BIND *bind)
587 int i, j;
589 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
590 bind[i].length = &bind[i].buffer_length;
591 bind[i].is_unsigned = 0;
592 bind[i].is_null = NULL;
594 if (values[j] == NULL) {
595 bind[i].buffer_type = MYSQL_TYPE_NULL;
597 else {
598 switch (statement->types[i]) {
599 case APR_DBD_TYPE_BLOB:
600 case APR_DBD_TYPE_CLOB:
601 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
602 bind[i].buffer = (void*)values[j];
603 bind[i].buffer_length = atol(values[++j]);
605 /* skip table and column */
606 j += 2;
607 break;
608 default:
609 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
610 bind[i].buffer = (void*)values[j];
611 bind[i].buffer_length = strlen(values[j]);
612 break;
617 return;
620 static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
621 int *nrows, apr_dbd_prepared_t *statement,
622 MYSQL_BIND *bind)
624 int ret;
626 ret = mysql_stmt_bind_param(statement->stmt, bind);
627 if (ret != 0) {
628 *nrows = 0;
629 ret = mysql_stmt_errno(statement->stmt);
631 else {
632 ret = mysql_stmt_execute(statement->stmt);
633 if (ret != 0) {
634 ret = mysql_stmt_errno(statement->stmt);
636 *nrows = mysql_stmt_affected_rows(statement->stmt);
639 return ret;
642 static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
643 int *nrows, apr_dbd_prepared_t *statement,
644 const char **values)
646 MYSQL_BIND *bind;
647 int ret;
649 if (sql->trans && sql->trans->errnum) {
650 return sql->trans->errnum;
653 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
655 dbd_mysql_bind(statement, values, bind);
657 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
659 if (TXN_NOTICE_ERRORS(sql->trans)) {
660 sql->trans->errnum = ret;
662 return ret;
665 static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
666 apr_dbd_prepared_t *statement, va_list args)
668 const char **values;
669 int i;
671 if (sql->trans && sql->trans->errnum) {
672 return sql->trans->errnum;
675 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
677 for (i = 0; i < statement->nvals; i++) {
678 values[i] = va_arg(args, const char*);
681 return dbd_mysql_pquery(pool, sql, nrows, statement, values);
684 static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
685 apr_dbd_results_t **res,
686 apr_dbd_prepared_t *statement,
687 int random, MYSQL_BIND *bind)
689 int nfields, i;
690 my_bool *is_nullr;
691 #if MYSQL_VERSION_ID >= 50000
692 my_bool *error;
693 #endif
694 int ret;
695 unsigned long *length, maxlen;
697 ret = mysql_stmt_bind_param(statement->stmt, bind);
698 if (ret == 0) {
699 ret = mysql_stmt_execute(statement->stmt);
700 if (!ret) {
701 if (!*res) {
702 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
704 (*res)->random = random;
705 (*res)->statement = statement->stmt;
706 (*res)->res = mysql_stmt_result_metadata(statement->stmt);
707 (*res)->pool = pool;
708 apr_pool_cleanup_register(pool, (*res)->res,
709 free_result, apr_pool_cleanup_null);
710 nfields = mysql_num_fields((*res)->res);
711 if (!(*res)->bind) {
712 (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
713 length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
714 #if MYSQL_VERSION_ID >= 50000
715 error = apr_palloc(pool, nfields*sizeof(my_bool));
716 #endif
717 is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
718 for ( i = 0; i < nfields; ++i ) {
719 maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
720 (*res)->res->fields[i].length : sql->fldsz) + 1;
721 if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) {
722 (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
724 else {
725 (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
727 (*res)->bind[i].buffer_length = maxlen;
728 (*res)->bind[i].length = &length[i];
729 (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
730 (*res)->bind[i].is_null = is_nullr+i;
731 #if MYSQL_VERSION_ID >= 50000
732 (*res)->bind[i].error = error+i;
733 #endif
736 ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
737 if (!ret) {
738 ret = mysql_stmt_store_result(statement->stmt);
742 if (ret != 0) {
743 ret = mysql_stmt_errno(statement->stmt);
746 return ret;
749 static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
750 apr_dbd_results_t **res,
751 apr_dbd_prepared_t *statement, int random,
752 const char **args)
754 int ret;
755 MYSQL_BIND *bind;
757 if (sql->trans && sql->trans->errnum) {
758 return sql->trans->errnum;
761 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
763 dbd_mysql_bind(statement, args, bind);
765 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
767 if (TXN_NOTICE_ERRORS(sql->trans)) {
768 sql->trans->errnum = ret;
770 return ret;
773 static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
774 apr_dbd_results_t **res,
775 apr_dbd_prepared_t *statement, int random,
776 va_list args)
778 const char **values;
779 int i;
781 if (sql->trans && sql->trans->errnum) {
782 return sql->trans->errnum;
785 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
787 for (i = 0; i < statement->nvals; i++) {
788 values[i] = va_arg(args, const char*);
791 return dbd_mysql_pselect(pool, sql, res, statement, random, values);
794 static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement,
795 const void **values, MYSQL_BIND *bind)
797 void *arg;
798 int i, j;
799 apr_dbd_type_e type;
801 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
802 arg = (void *)values[j];
804 bind[i].length = &bind[i].buffer_length;
805 bind[i].is_null = NULL;
807 type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
808 switch (type) {
809 case APR_DBD_TYPE_TINY:
810 bind[i].buffer = arg;
811 bind[i].buffer_type = MYSQL_TYPE_TINY;
812 bind[i].is_unsigned = 0;
813 break;
814 case APR_DBD_TYPE_UTINY:
815 bind[i].buffer = arg;
816 bind[i].buffer_type = MYSQL_TYPE_TINY;
817 bind[i].is_unsigned = 1;
818 break;
819 case APR_DBD_TYPE_SHORT:
820 bind[i].buffer = arg;
821 bind[i].buffer_type = MYSQL_TYPE_SHORT;
822 bind[i].is_unsigned = 0;
823 break;
824 case APR_DBD_TYPE_USHORT:
825 bind[i].buffer = arg;
826 bind[i].buffer_type = MYSQL_TYPE_SHORT;
827 bind[i].is_unsigned = 1;
828 break;
829 case APR_DBD_TYPE_INT:
830 bind[i].buffer = arg;
831 bind[i].buffer_type = MYSQL_TYPE_LONG;
832 bind[i].is_unsigned = 0;
833 break;
834 case APR_DBD_TYPE_UINT:
835 bind[i].buffer = arg;
836 bind[i].buffer_type = MYSQL_TYPE_LONG;
837 bind[i].is_unsigned = 1;
838 break;
839 case APR_DBD_TYPE_LONG:
840 if (sizeof(int) == sizeof(long)) {
841 bind[i].buffer = arg;
843 else {
844 bind[i].buffer = apr_palloc(pool, sizeof(int));
845 *(int*)bind[i].buffer = *(long*)arg;
847 bind[i].buffer_type = MYSQL_TYPE_LONG;
848 bind[i].is_unsigned = 0;
849 break;
850 case APR_DBD_TYPE_ULONG:
851 if (sizeof(unsigned int) == sizeof(unsigned long)) {
852 bind[i].buffer = arg;
854 else {
855 bind[i].buffer = apr_palloc(pool, sizeof(unsigned int));
856 *(unsigned int*)bind[i].buffer = *(unsigned long*)arg;
858 bind[i].buffer_type = MYSQL_TYPE_LONG;
859 bind[i].is_unsigned = 1;
860 break;
861 case APR_DBD_TYPE_LONGLONG:
862 if (sizeof(long long) == sizeof(apr_int64_t)) {
863 bind[i].buffer = arg;
865 else {
866 bind[i].buffer = apr_palloc(pool, sizeof(long long));
867 *(long long*)bind[i].buffer = *(apr_int64_t*)arg;
869 bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
870 bind[i].is_unsigned = 0;
871 break;
872 case APR_DBD_TYPE_ULONGLONG:
873 if (sizeof(unsigned long long) == sizeof(apr_uint64_t)) {
874 bind[i].buffer = arg;
876 else {
877 bind[i].buffer = apr_palloc(pool, sizeof(unsigned long long));
878 *(unsigned long long*)bind[i].buffer = *(apr_uint64_t*)arg;
880 bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
881 bind[i].is_unsigned = 1;
882 break;
883 case APR_DBD_TYPE_FLOAT:
884 bind[i].buffer = arg;
885 bind[i].buffer_type = MYSQL_TYPE_FLOAT;
886 bind[i].is_unsigned = 0;
887 break;
888 case APR_DBD_TYPE_DOUBLE:
889 bind[i].buffer = arg;
890 bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
891 bind[i].is_unsigned = 0;
892 break;
893 case APR_DBD_TYPE_STRING:
894 case APR_DBD_TYPE_TEXT:
895 case APR_DBD_TYPE_TIME:
896 case APR_DBD_TYPE_DATE:
897 case APR_DBD_TYPE_DATETIME:
898 case APR_DBD_TYPE_TIMESTAMP:
899 case APR_DBD_TYPE_ZTIMESTAMP:
900 bind[i].buffer = arg;
901 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
902 bind[i].is_unsigned = 0;
903 bind[i].buffer_length = strlen((const char *)arg);
904 break;
905 case APR_DBD_TYPE_BLOB:
906 case APR_DBD_TYPE_CLOB:
907 bind[i].buffer = (void *)arg;
908 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
909 bind[i].is_unsigned = 0;
910 bind[i].buffer_length = *(apr_size_t*)values[++j];
912 /* skip table and column */
913 j += 2;
914 break;
915 case APR_DBD_TYPE_NULL:
916 default:
917 bind[i].buffer_type = MYSQL_TYPE_NULL;
918 break;
922 return;
925 static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql,
926 int *nrows, apr_dbd_prepared_t *statement,
927 const void **values)
929 MYSQL_BIND *bind;
930 int ret;
932 if (sql->trans && sql->trans->errnum) {
933 return sql->trans->errnum;
936 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
938 dbd_mysql_bbind(pool, statement, values, bind);
940 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
942 if (TXN_NOTICE_ERRORS(sql->trans)) {
943 sql->trans->errnum = ret;
945 return ret;
948 static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
949 apr_dbd_prepared_t *statement, va_list args)
951 const void **values;
952 int i;
954 if (sql->trans && sql->trans->errnum) {
955 return sql->trans->errnum;
958 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
960 for (i = 0; i < statement->nvals; i++) {
961 values[i] = va_arg(args, const void*);
964 return dbd_mysql_pbquery(pool, sql, nrows, statement, values);
967 static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
968 apr_dbd_results_t **res,
969 apr_dbd_prepared_t *statement, int random,
970 const void **args)
972 int ret;
973 MYSQL_BIND *bind;
975 if (sql->trans && sql->trans->errnum) {
976 return sql->trans->errnum;
979 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
981 dbd_mysql_bbind(pool, statement, args, bind);
983 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
985 if (TXN_NOTICE_ERRORS(sql->trans)) {
986 sql->trans->errnum = ret;
988 return ret;
991 static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
992 apr_dbd_results_t **res,
993 apr_dbd_prepared_t *statement, int random,
994 va_list args)
996 const void **values;
997 int i;
999 if (sql->trans && sql->trans->errnum) {
1000 return sql->trans->errnum;
1003 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1005 for (i = 0; i < statement->nvals; i++) {
1006 values[i] = va_arg(args, const void*);
1009 return dbd_mysql_pbselect(pool, sql, res, statement, random, values);
1012 static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
1014 int ret = -1;
1015 if (trans) {
1016 /* rollback on error or explicit rollback request */
1017 if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1018 trans->errnum = 0;
1019 ret = mysql_rollback(trans->handle->conn);
1021 else {
1022 ret = mysql_commit(trans->handle->conn);
1025 ret |= mysql_autocommit(trans->handle->conn, 1);
1026 trans->handle->trans = NULL;
1027 return ret;
1029 /* Whether or not transactions work depends on whether the
1030 * underlying DB supports them within MySQL. Unfortunately
1031 * it fails silently with the default InnoDB.
1034 static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1035 apr_dbd_transaction_t **trans)
1037 /* Don't try recursive transactions here */
1038 if (handle->trans) {
1039 dbd_mysql_end_transaction(handle->trans) ;
1041 if (!*trans) {
1042 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1044 (*trans)->errnum = mysql_autocommit(handle->conn, 0);
1045 (*trans)->handle = handle;
1046 handle->trans = *trans;
1047 return (*trans)->errnum;
1050 static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
1052 if (!trans)
1053 return APR_DBD_TRANSACTION_COMMIT;
1055 return trans->mode;
1058 static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans,
1059 int mode)
1061 if (!trans)
1062 return APR_DBD_TRANSACTION_COMMIT;
1064 return trans->mode = (mode & TXN_MODE_BITS);
1067 static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params,
1068 const char **error)
1070 static const char *const delims = " \r\n\t;|,";
1071 const char *ptr;
1072 int i;
1073 const char *key;
1074 size_t klen;
1075 const char *value;
1076 size_t vlen;
1077 #if MYSQL_VERSION_ID >= 50013
1078 my_bool do_reconnect = 1;
1079 #endif
1080 MYSQL *real_conn;
1081 unsigned long flags = 0;
1083 struct {
1084 const char *field;
1085 const char *value;
1086 } fields[] = {
1087 {"host", NULL},
1088 {"user", NULL},
1089 {"pass", NULL},
1090 {"dbname", NULL},
1091 {"port", NULL},
1092 {"sock", NULL},
1093 {"flags", NULL},
1094 {"fldsz", NULL},
1095 {"group", NULL},
1096 {NULL, NULL}
1098 unsigned int port = 0;
1099 apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t));
1100 sql->fldsz = FIELDSIZE;
1101 sql->conn = mysql_init(sql->conn);
1102 if ( sql->conn == NULL ) {
1103 return NULL;
1105 for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
1106 /* don't dereference memory that may not belong to us */
1107 if (ptr == params) {
1108 ++ptr;
1109 continue;
1111 for (key = ptr-1; isspace(*key); --key);
1112 klen = 0;
1113 while (isalpha(*key)) {
1114 /* don't parse backwards off the start of the string */
1115 if (key == params) {
1116 --key;
1117 ++klen;
1118 break;
1120 --key;
1121 ++klen;
1123 ++key;
1124 for (value = ptr+1; isspace(*value); ++value);
1125 vlen = strcspn(value, delims);
1126 for (i = 0; fields[i].field != NULL; i++) {
1127 if (!strncasecmp(fields[i].field, key, klen)) {
1128 fields[i].value = apr_pstrndup(pool, value, vlen);
1129 break;
1132 ptr = value+vlen;
1134 if (fields[4].value != NULL) {
1135 port = atoi(fields[4].value);
1137 if (fields[6].value != NULL &&
1138 !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) {
1139 flags |= CLIENT_FOUND_ROWS; /* only option we know */
1141 if (fields[7].value != NULL) {
1142 sql->fldsz = atol(fields[7].value);
1144 if (fields[8].value != NULL) {
1145 mysql_options(sql->conn, MYSQL_READ_DEFAULT_GROUP, fields[8].value);
1148 #if MYSQL_VERSION_ID >= 50013
1149 /* the MySQL manual says this should be BEFORE mysql_real_connect */
1150 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
1151 #endif
1153 real_conn = mysql_real_connect(sql->conn, fields[0].value,
1154 fields[1].value, fields[2].value,
1155 fields[3].value, port,
1156 fields[5].value, flags);
1158 if(real_conn == NULL) {
1159 if (error) {
1160 *error = apr_pstrdup(pool, mysql_error(sql->conn));
1162 mysql_close(sql->conn);
1163 return NULL;
1166 #if MYSQL_VERSION_ID >= 50013
1167 /* Some say this should be AFTER mysql_real_connect */
1168 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
1169 #endif
1171 return sql;
1174 static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
1176 mysql_close(handle->conn);
1177 return APR_SUCCESS;
1180 static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
1181 apr_dbd_t *handle)
1183 return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
1186 static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
1187 const char* name)
1189 return mysql_select_db(handle->conn, name);
1192 static void *dbd_mysql_native(apr_dbd_t *handle)
1194 return handle->conn;
1197 static int dbd_mysql_num_cols(apr_dbd_results_t *res)
1199 if (res->statement) {
1200 return mysql_stmt_field_count(res->statement);
1202 else {
1203 return mysql_num_fields(res->res);
1207 static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
1209 if (res->random) {
1210 if (res->statement) {
1211 return (int) mysql_stmt_num_rows(res->statement);
1213 else {
1214 return (int) mysql_num_rows(res->res);
1217 else {
1218 return -1;
1222 static apr_status_t thread_end(void *data)
1224 mysql_thread_end();
1225 return APR_SUCCESS;
1228 static void dbd_mysql_init(apr_pool_t *pool)
1230 my_init();
1231 mysql_thread_init();
1233 /* FIXME: this is a guess; find out what it really does */
1234 apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null);
1236 APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = {
1237 "mysql",
1238 dbd_mysql_init,
1239 dbd_mysql_native,
1240 dbd_mysql_open,
1241 dbd_mysql_check_conn,
1242 dbd_mysql_close,
1243 dbd_mysql_select_db,
1244 dbd_mysql_transaction,
1245 dbd_mysql_end_transaction,
1246 dbd_mysql_query,
1247 dbd_mysql_select,
1248 dbd_mysql_num_cols,
1249 dbd_mysql_num_tuples,
1250 dbd_mysql_get_row,
1251 dbd_mysql_get_entry,
1252 dbd_mysql_error,
1253 dbd_mysql_escape,
1254 dbd_mysql_prepare,
1255 dbd_mysql_pvquery,
1256 dbd_mysql_pvselect,
1257 dbd_mysql_pquery,
1258 dbd_mysql_pselect,
1259 dbd_mysql_get_name,
1260 dbd_mysql_transaction_mode_get,
1261 dbd_mysql_transaction_mode_set,
1262 "?",
1263 dbd_mysql_pvbquery,
1264 dbd_mysql_pvbselect,
1265 dbd_mysql_pbquery,
1266 dbd_mysql_pbselect,
1267 dbd_mysql_datum_get
1270 #endif