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