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