jorton 040b95
/*
jorton 040b95
         Copyright (c) 2003-7, WebThing Ltd and other contributors
jorton 040b95
 
jorton 040b95
This program is free software; you can redistribute it and/or modify
jorton 040b95
it under the terms of the GNU General Public License as published by
jorton 040b95
the Free Software Foundation; either version 2 of the License, or
jorton 040b95
(at your option) any later version.  Alternatively, where this program
jorton 040b95
is aggregated with the Apache APR-UTIL package version 1.2 or later,
jorton 040b95
you can distribute and/or modify it under the terms of the Apache
jorton 040b95
License 2.0 as published by the Apache Software Foundation.
jorton 040b95
 
jorton 040b95
This program is distributed in the hope that it will be useful,
jorton 040b95
but WITHOUT ANY WARRANTY; without even the implied warranty of
jorton 040b95
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
jorton 040b95
applicable License for more details.
jorton 040b95
 
jorton 040b95
You should have received a copy of the GNU General Public License
jorton 040b95
along with this program; if not, write to the Free Software
jorton 040b95
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
jorton 040b95
You may obtain a copy of the Apache License 2.0 at:
jorton 040b95
http://www.apache.org/licenses/LICENSE-2.0
jorton 040b95
 
jorton 040b95
*/
jorton 040b95
jorton 040b95
/* LICENSE NOTE
jorton 040b95
 *
jorton 040b95
 * The current GPL satisfies MySQL licensing terms without
jorton 040b95
 * invoking any exceptions.  ASF policy doesn't permit GPL
jorton 040b95
 * software to be distributed by apache.org, but this should
jorton 040b95
 * not be a problem for third-parties who wish to distribute
jorton 040b95
 * it alongside the APR and other Apache software.
jorton 040b95
 * 
jorton 040b95
 * It is updated to dual license after discussion with Debian
jorton 040b95
 * and taking into account MySQL's FOSS exception.
jorton 040b95
 * http://bahumbug.wordpress.com/2007/01/09/re-licensing-apr_dbd_mysql/
jorton 040b95
 * http://www.mysql.com/company/legal/licensing/foss-exception.html
jorton 040b95
 *
jorton 040b95
 * MAINTAINERS
jorton 040b95
 *
jorton 040b95
 * This code was originally written by Nick Kew for MySQL 4.1.
jorton 040b95
 * and subsequently updated by others to support MySQL 5.0.
jorton 040b95
 * The current lead maintainer is Bojan Smojver, with others
jorton 040b95
 * contributing via the developer list at apr.apache.org.
jorton 040b95
 *
jorton 040b95
 */
jorton 040b95
jorton 040b95
jorton 040b95
#include "apu.h"
jorton 040b95
#define HAVE_MYSQL_MYSQL_H
jorton 040b95
jorton 040b95
#if APU_HAVE_MYSQL
jorton 040b95
jorton 040b95
#include "apu_version.h"
jorton 040b95
#include "apu_config.h"
jorton 040b95
jorton 040b95
#include <ctype.h>
jorton 040b95
#include <stdlib.h>
jorton 040b95
jorton 040b95
#ifdef HAVE_MYSQL_H
jorton 040b95
#include <mysql.h>
jorton 040b95
#include <errmsg.h>
jorton 040b95
#elif defined(HAVE_MYSQL_MYSQL_H)
jorton 040b95
#include <mysql/mysql.h>
jorton 040b95
#include <mysql/errmsg.h>
jorton 040b95
#endif
jorton 040b95
jorton 040b95
#include "apr_strings.h"
jorton 040b95
#include "apr_buckets.h"
jorton 040b95
jorton 040b95
#include "apr_dbd_internal.h"
jorton 040b95
jorton 040b95
/* default maximum field size 1 MB */
jorton 040b95
#define FIELDSIZE 1048575
jorton 040b95
jorton 040b95
struct apr_dbd_prepared_t {
jorton 040b95
    MYSQL_STMT* stmt;
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
    int nargs;
jorton 040b95
    int nvals;
jorton 040b95
    apr_dbd_type_e *types;
jorton 040b95
#endif
jorton 040b95
};
jorton 040b95
jorton 040b95
struct apr_dbd_transaction_t {
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
    int mode;
jorton 040b95
#endif
jorton 040b95
    int errnum;
jorton 040b95
    apr_dbd_t *handle;
jorton 040b95
};
jorton 040b95
jorton 040b95
struct apr_dbd_t {
jorton 040b95
    MYSQL* conn ;
jorton 040b95
    apr_dbd_transaction_t* trans ;
jorton 040b95
    unsigned long fldsz;
jorton 040b95
};
jorton 040b95
jorton 040b95
struct apr_dbd_results_t {
jorton 040b95
    int random;
jorton 040b95
    MYSQL_RES *res;
jorton 040b95
    MYSQL_STMT *statement;
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
    apr_pool_t *pool;
jorton 040b95
#endif
jorton 040b95
};
jorton 040b95
struct apr_dbd_row_t {
jorton 040b95
    MYSQL_ROW row;
jorton 040b95
    apr_dbd_results_t *res;
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
    unsigned long *len;
jorton 040b95
#endif
jorton 040b95
};
jorton 040b95
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
/* MySQL specific bucket for BLOB types */
jorton 040b95
typedef struct apr_bucket_lob apr_bucket_lob;
jorton 040b95
/**
jorton 040b95
 * A bucket referring to a MySQL BLOB
jorton 040b95
 */
jorton 040b95
struct apr_bucket_lob {
jorton 040b95
    /** Number of buckets using this memory */
jorton 040b95
    apr_bucket_refcount  refcount;
jorton 040b95
    /** The row this bucket refers to */
jorton 040b95
    const apr_dbd_row_t *row;
jorton 040b95
    /** The column this bucket refers to */
jorton 040b95
    int col;
jorton 040b95
    /** The pool into which any needed structures should
jorton 040b95
     *  be created while reading from this bucket */
jorton 040b95
    apr_pool_t *readpool;
jorton 040b95
};
jorton 040b95
jorton 040b95
static void lob_bucket_destroy(void *data);
jorton 040b95
static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
jorton 040b95
                                    apr_size_t *len, apr_read_type_e block);
jorton 040b95
static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
jorton 040b95
                                       const apr_dbd_row_t *row, int col,
jorton 040b95
                                       apr_off_t offset, apr_size_t len,
jorton 040b95
                                       apr_pool_t *p);
jorton 040b95
static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
jorton 040b95
                                         apr_off_t offset,
jorton 040b95
                                         apr_size_t len, apr_pool_t *p,
jorton 040b95
                                         apr_bucket_alloc_t *list);
jorton 040b95
jorton 040b95
static const apr_bucket_type_t apr_bucket_type_lob = {
jorton 040b95
    "LOB", 5, APR_BUCKET_DATA,
jorton 040b95
    lob_bucket_destroy,
jorton 040b95
    lob_bucket_read,
jorton 040b95
    apr_bucket_setaside_notimpl,
jorton 040b95
    apr_bucket_shared_split,
jorton 040b95
    apr_bucket_shared_copy
jorton 040b95
};
jorton 040b95
jorton 040b95
static void lob_bucket_destroy(void *data)
jorton 040b95
{
jorton 040b95
    apr_bucket_lob *f = data;
jorton 040b95
jorton 040b95
    if (apr_bucket_shared_destroy(f)) {
jorton 040b95
        /* no need to destroy database objects here; it will get
jorton 040b95
         * done automatically when the pool gets cleaned up */
jorton 040b95
        apr_bucket_free(f);
jorton 040b95
    }
jorton 040b95
}
jorton 040b95
jorton 040b95
static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
jorton 040b95
                                    apr_size_t *len, apr_read_type_e block)
jorton 040b95
{
jorton 040b95
    apr_bucket_lob *a = e->data;
jorton 040b95
    const apr_dbd_row_t *row = a->row;
jorton 040b95
    apr_dbd_results_t *res = row->res;
jorton 040b95
    int col = a->col;
jorton 040b95
    apr_bucket *b = NULL;
jorton 040b95
    int rv;
jorton 040b95
    apr_size_t blength = e->length;  /* bytes remaining in file past offset */
jorton 040b95
    apr_off_t boffset = e->start;
jorton 040b95
    MYSQL_BIND *bind = &res->bind[col];
jorton 040b95
jorton 040b95
    *str = NULL;  /* in case we die prematurely */
jorton 040b95
jorton 040b95
    /* fetch from offset if not at the beginning */
jorton 040b95
    if (boffset > 0) {
jorton 040b95
        rv = mysql_stmt_fetch_column(res->statement, bind, col, boffset);
jorton 040b95
        if (rv != 0) {
jorton 040b95
            return APR_EGENERAL;
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    blength -= blength > bind->buffer_length ? bind->buffer_length : blength;
jorton 040b95
    *len = e->length - blength;
jorton 040b95
    *str = bind->buffer;
jorton 040b95
jorton 040b95
    /* allocate new buffer, since we used this one for the bucket */
jorton 040b95
    bind->buffer = apr_palloc(res->pool, bind->buffer_length);
jorton 040b95
jorton 040b95
    /*
jorton 040b95
     * Change the current bucket to refer to what we read,
jorton 040b95
     * even if we read nothing because we hit EOF.
jorton 040b95
     */
jorton 040b95
    apr_bucket_pool_make(e, *str, *len, res->pool);
jorton 040b95
jorton 040b95
    /* If we have more to read from the field, then create another bucket */
jorton 040b95
    if (blength > 0) {
jorton 040b95
        /* for efficiency, we can just build a new apr_bucket struct
jorton 040b95
         * to wrap around the existing LOB bucket */
jorton 040b95
        b = apr_bucket_alloc(sizeof(*b), e->list);
jorton 040b95
        b->start  = boffset + *len;
jorton 040b95
        b->length = blength;
jorton 040b95
        b->data   = a;
jorton 040b95
        b->type   = &apr_bucket_type_lob;
jorton 040b95
        b->free   = apr_bucket_free;
jorton 040b95
        b->list   = e->list;
jorton 040b95
        APR_BUCKET_INSERT_AFTER(e, b);
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        lob_bucket_destroy(a);
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return APR_SUCCESS;
jorton 040b95
}
jorton 040b95
jorton 040b95
static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
jorton 040b95
                                       const apr_dbd_row_t *row, int col,
jorton 040b95
                                       apr_off_t offset, apr_size_t len,
jorton 040b95
                                       apr_pool_t *p)
jorton 040b95
{
jorton 040b95
    apr_bucket_lob *f;
jorton 040b95
jorton 040b95
    f = apr_bucket_alloc(sizeof(*f), b->list);
jorton 040b95
    f->row = row;
jorton 040b95
    f->col = col;
jorton 040b95
    f->readpool = p;
jorton 040b95
jorton 040b95
    b = apr_bucket_shared_make(b, f, offset, len);
jorton 040b95
    b->type = &apr_bucket_type_lob;
jorton 040b95
jorton 040b95
    return b;
jorton 040b95
}
jorton 040b95
jorton 040b95
static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
jorton 040b95
                                         apr_off_t offset,
jorton 040b95
                                         apr_size_t len, apr_pool_t *p,
jorton 040b95
                                         apr_bucket_alloc_t *list)
jorton 040b95
{
jorton 040b95
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
jorton 040b95
jorton 040b95
    APR_BUCKET_INIT(b);
jorton 040b95
    b->free = apr_bucket_free;
jorton 040b95
    b->list = list;
jorton 040b95
    return apr_bucket_lob_make(b, row, col, offset, len, p);
jorton 040b95
}
jorton 040b95
jorton 040b95
#endif
jorton 040b95
jorton 040b95
static apr_status_t free_result(void *data)
jorton 040b95
{
jorton 040b95
    mysql_free_result(data);
jorton 040b95
    return APR_SUCCESS;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                            apr_dbd_results_t **results,
jorton 040b95
                            const char *query, int seek)
jorton 040b95
{
jorton 040b95
    int sz;
jorton 040b95
    int ret;
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
    ret = mysql_query(sql->conn, query);
jorton 040b95
    if (!ret) {
jorton 040b95
        if (sz = mysql_field_count(sql->conn), sz > 0) {
jorton 040b95
            if (!*results) {
jorton 040b95
                *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
jorton 040b95
            }
jorton 040b95
            (*results)->random = seek;
jorton 040b95
            (*results)->statement = NULL;
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
            (*results)->pool = pool;
jorton 040b95
#endif
jorton 040b95
            if (seek) {
jorton 040b95
                (*results)->res = mysql_store_result(sql->conn);
jorton 040b95
            }
jorton 040b95
            else {
jorton 040b95
                (*results)->res = mysql_use_result(sql->conn);
jorton 040b95
            }
jorton 040b95
            apr_pool_cleanup_register(pool, (*results)->res,
jorton 040b95
                                      free_result,apr_pool_cleanup_null);
jorton 040b95
        }
jorton 040b95
    } else {
jorton 040b95
        ret = mysql_errno(sql->conn);
jorton 040b95
    }
jorton 040b95
    
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
    if (TXN_NOTICE_ERRORS(sql->trans)) {
jorton 040b95
#else
jorton 040b95
    if (sql->trans) {
jorton 040b95
#endif
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n)
jorton 040b95
{
jorton 040b95
    if ((n < 0) || (n >= mysql_num_fields(res->res))) {
jorton 040b95
        return NULL;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return mysql_fetch_fields(res->res)[n].name;
jorton 040b95
}
jorton 040b95
#endif
jorton 040b95
jorton 040b95
static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
jorton 040b95
                             apr_dbd_row_t **row, int rownum)
jorton 040b95
{
jorton 040b95
    MYSQL_ROW r = NULL;
jorton 040b95
    int ret = 0;
jorton 040b95
jorton 040b95
    if (res->statement) {
jorton 040b95
        if (res->random) {
jorton 040b95
            if (rownum >= 0) {
jorton 040b95
                mysql_stmt_data_seek(res->statement, (my_ulonglong)rownum);
jorton 040b95
            }
jorton 040b95
        }
jorton 040b95
        ret = mysql_stmt_fetch(res->statement);
jorton 040b95
        switch (ret) {
jorton 040b95
        case 1:
jorton 040b95
            ret = mysql_stmt_errno(res->statement);
jorton 040b95
            break;
jorton 040b95
        case MYSQL_NO_DATA:
jorton 040b95
            ret = -1;
jorton 040b95
            break;
jorton 040b95
        default:
jorton 040b95
            ret = 0; /* bad luck - get_entry will deal with this */
jorton 040b95
            break;
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        if (res->random) {
jorton 040b95
            if (rownum >= 0) {
jorton 040b95
                mysql_data_seek(res->res, (my_ulonglong) rownum);
jorton 040b95
            }
jorton 040b95
        }
jorton 040b95
        r = mysql_fetch_row(res->res);
jorton 040b95
        if (r == NULL) {
jorton 040b95
            ret = -1;
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    if (ret == 0) {
jorton 040b95
        if (!*row) {
jorton 040b95
            *row = apr_palloc(pool, sizeof(apr_dbd_row_t));
jorton 040b95
        }
jorton 040b95
        (*row)->row = r;
jorton 040b95
        (*row)->res = res;
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
        (*row)->len = mysql_fetch_lengths(res->res);
jorton 040b95
#endif
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        apr_pool_cleanup_run(pool, res->res, free_result);
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
#if 0
jorton 040b95
/* An improved API that was proposed but not followed up */
jorton 040b95
static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n,
jorton 040b95
                               apr_dbd_datum_t *val)
jorton 040b95
{
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
    if (row->res->statement) {
jorton 040b95
        bind = &row->res->bind[n];
jorton 040b95
        if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
jorton 040b95
            val->type = APR_DBD_VALUE_NULL;
jorton 040b95
            return -1;
jorton 040b95
        }
jorton 040b95
        if (*bind->is_null) {
jorton 040b95
            val->type = APR_DBD_VALUE_NULL;
jorton 040b95
            return -1;
jorton 040b95
        }
jorton 040b95
        else {
jorton 040b95
            val->type = APR_DBD_VALUE_STRING;
jorton 040b95
            val->value.stringval = bind->buffer;
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        val->type = APR_DBD_VALUE_STRING;
jorton 040b95
        val->value.stringval = row->row[n];
jorton 040b95
    }
jorton 040b95
    return 0;
jorton 040b95
}
jorton 040b95
#else
jorton 040b95
jorton 040b95
static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
jorton 040b95
{
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
    if (row->res->statement) {
jorton 040b95
        bind = &row->res->bind[n];
jorton 040b95
        if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
jorton 040b95
            return NULL;
jorton 040b95
        }
jorton 040b95
        if (*bind->is_null) {
jorton 040b95
            return NULL;
jorton 040b95
        }
jorton 040b95
        else {
jorton 040b95
            return bind->buffer;
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        return row->row[n];
jorton 040b95
    }
jorton 040b95
    return NULL;
jorton 040b95
}
jorton 040b95
#endif
jorton 040b95
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n,
jorton 040b95
                                        apr_dbd_type_e type, void *data)
jorton 040b95
{
jorton 040b95
    if (row->res->statement) {
jorton 040b95
        MYSQL_BIND *bind = &row->res->bind[n];
jorton 040b95
        unsigned long len = *bind->length;
jorton 040b95
jorton 040b95
        if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
jorton 040b95
            return APR_EGENERAL;
jorton 040b95
        }
jorton 040b95
jorton 040b95
        if (*bind->is_null) {
jorton 040b95
            return APR_ENOENT;
jorton 040b95
        }
jorton 040b95
jorton 040b95
        switch (type) {
jorton 040b95
        case APR_DBD_TYPE_TINY:
jorton 040b95
            *(char*)data = atoi(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_UTINY:
jorton 040b95
            *(unsigned char*)data = atoi(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_SHORT:
jorton 040b95
            *(short*)data = atoi(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_USHORT:
jorton 040b95
            *(unsigned short*)data = atoi(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_INT:
jorton 040b95
            *(int*)data = atoi(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_UINT:
jorton 040b95
            *(unsigned int*)data = atoi(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_LONG:
jorton 040b95
            *(long*)data = atol(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_ULONG:
jorton 040b95
            *(unsigned long*)data = atol(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_LONGLONG:
jorton 040b95
            *(apr_int64_t*)data = apr_atoi64(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_ULONGLONG:
jorton 040b95
            *(apr_uint64_t*)data = apr_atoi64(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_FLOAT:
jorton 040b95
            *(float*)data = atof(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_DOUBLE:
jorton 040b95
            *(double*)data = atof(bind->buffer);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_STRING:
jorton 040b95
        case APR_DBD_TYPE_TEXT:
jorton 040b95
        case APR_DBD_TYPE_TIME:
jorton 040b95
        case APR_DBD_TYPE_DATE:
jorton 040b95
        case APR_DBD_TYPE_DATETIME:
jorton 040b95
        case APR_DBD_TYPE_TIMESTAMP:
jorton 040b95
        case APR_DBD_TYPE_ZTIMESTAMP:
jorton 040b95
            *((char*)bind->buffer+bind->buffer_length-1) = '\0';
jorton 040b95
            *(char**)data = bind->buffer;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_BLOB:
jorton 040b95
        case APR_DBD_TYPE_CLOB:
jorton 040b95
            {
jorton 040b95
            apr_bucket *e;
jorton 040b95
            apr_bucket_brigade *b = (apr_bucket_brigade*)data;
jorton 040b95
jorton 040b95
            e = apr_bucket_lob_create(row, n, 0, len,
jorton 040b95
                                      row->res->pool, b->bucket_alloc);
jorton 040b95
            APR_BRIGADE_INSERT_TAIL(b, e);
jorton 040b95
            }
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_NULL:
jorton 040b95
            *(void**)data = NULL;
jorton 040b95
            break;
jorton 040b95
        default:
jorton 040b95
            return APR_EGENERAL;
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        if (row->row[n] == NULL) {
jorton 040b95
            return APR_ENOENT;
jorton 040b95
        }
jorton 040b95
jorton 040b95
        switch (type) {
jorton 040b95
        case APR_DBD_TYPE_TINY:
jorton 040b95
            *(char*)data = atoi(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_UTINY:
jorton 040b95
            *(unsigned char*)data = atoi(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_SHORT:
jorton 040b95
            *(short*)data = atoi(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_USHORT:
jorton 040b95
            *(unsigned short*)data = atoi(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_INT:
jorton 040b95
            *(int*)data = atoi(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_UINT:
jorton 040b95
            *(unsigned int*)data = atoi(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_LONG:
jorton 040b95
            *(long*)data = atol(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_ULONG:
jorton 040b95
            *(unsigned long*)data = atol(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_LONGLONG:
jorton 040b95
            *(apr_int64_t*)data = apr_atoi64(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_ULONGLONG:
jorton 040b95
            *(apr_uint64_t*)data = apr_atoi64(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_FLOAT:
jorton 040b95
            *(float*)data = atof(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_DOUBLE:
jorton 040b95
            *(double*)data = atof(row->row[n]);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_STRING:
jorton 040b95
        case APR_DBD_TYPE_TEXT:
jorton 040b95
        case APR_DBD_TYPE_TIME:
jorton 040b95
        case APR_DBD_TYPE_DATE:
jorton 040b95
        case APR_DBD_TYPE_DATETIME:
jorton 040b95
        case APR_DBD_TYPE_TIMESTAMP:
jorton 040b95
        case APR_DBD_TYPE_ZTIMESTAMP:
jorton 040b95
            *(char**)data = row->row[n];
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_BLOB:
jorton 040b95
        case APR_DBD_TYPE_CLOB:
jorton 040b95
            {
jorton 040b95
            apr_bucket *e;
jorton 040b95
            apr_bucket_brigade *b = (apr_bucket_brigade*)data;
jorton 040b95
jorton 040b95
            e = apr_bucket_pool_create(row->row[n], row->len[n],
jorton 040b95
                                       row->res->pool, b->bucket_alloc);
jorton 040b95
            APR_BRIGADE_INSERT_TAIL(b, e);
jorton 040b95
            }
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_NULL:
jorton 040b95
            *(void**)data = NULL;
jorton 040b95
            break;
jorton 040b95
        default:
jorton 040b95
            return APR_EGENERAL;
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    return 0;
jorton 040b95
}
jorton 040b95
#endif
jorton 040b95
jorton 040b95
static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
jorton 040b95
{
jorton 040b95
    return mysql_error(sql->conn);
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
jorton 040b95
{
jorton 040b95
    int ret;
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
    ret = mysql_query(sql->conn, query);
jorton 040b95
    if (ret != 0) {
jorton 040b95
        ret = mysql_errno(sql->conn);
jorton 040b95
    }
jorton 040b95
    *nrows = mysql_affected_rows(sql->conn);
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
    if (TXN_NOTICE_ERRORS(sql->trans)) {
jorton 040b95
#else
jorton 040b95
    if (sql->trans) {
jorton 040b95
#endif
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
jorton 040b95
                                    apr_dbd_t *sql)
jorton 040b95
{
jorton 040b95
    unsigned long len = strlen(arg);
jorton 040b95
    char *ret = apr_palloc(pool, 2*len + 1);
jorton 040b95
    mysql_real_escape_string(sql->conn, ret, arg, len);
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static apr_status_t stmt_close(void *data)
jorton 040b95
{
jorton 040b95
    mysql_stmt_close(data);
jorton 040b95
    return APR_SUCCESS;
jorton 040b95
}
jorton 040b95
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                             const char *query, const char *label,
jorton 040b95
                             int nargs, int nvals, apr_dbd_type_e *types,
jorton 040b95
                             apr_dbd_prepared_t **statement)
jorton 040b95
{
jorton 040b95
    /* Translate from apr_dbd to native query format */
jorton 040b95
    int ret;
jorton 040b95
jorton 040b95
    if (!*statement) {
jorton 040b95
        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
jorton 040b95
    }
jorton 040b95
    (*statement)->stmt = mysql_stmt_init(sql->conn);
jorton 040b95
jorton 040b95
    if ((*statement)->stmt) {
jorton 040b95
        apr_pool_cleanup_register(pool, (*statement)->stmt,
jorton 040b95
                                  stmt_close, apr_pool_cleanup_null);
jorton 040b95
        ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query));
jorton 040b95
jorton 040b95
        if (ret != 0) {
jorton 040b95
            ret = mysql_stmt_errno((*statement)->stmt);
jorton 040b95
        }
jorton 040b95
jorton 040b95
        (*statement)->nargs = nargs;
jorton 040b95
        (*statement)->nvals = nvals;
jorton 040b95
        (*statement)->types = types;
jorton 040b95
jorton 040b95
        return ret;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return CR_OUT_OF_MEMORY;
jorton 040b95
}
jorton 040b95
jorton 040b95
static void dbd_mysql_bind(apr_dbd_prepared_t *statement,
jorton 040b95
                           const char **values, MYSQL_BIND *bind)
jorton 040b95
{
jorton 040b95
    int i, j;
jorton 040b95
jorton 040b95
    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
jorton 040b95
        bind[i].length = &bind[i].buffer_length;
jorton 040b95
        bind[i].is_unsigned = 0;
jorton 040b95
        bind[i].is_null = NULL;
jorton 040b95
jorton 040b95
        if (values[j] == NULL) {
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_NULL;
jorton 040b95
        }
jorton 040b95
        else {
jorton 040b95
            switch (statement->types[i]) {
jorton 040b95
            case APR_DBD_TYPE_BLOB:
jorton 040b95
            case APR_DBD_TYPE_CLOB:
jorton 040b95
                bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
jorton 040b95
                bind[i].buffer = (void*)values[j];
jorton 040b95
                bind[i].buffer_length = atol(values[++j]);
jorton 040b95
jorton 040b95
                /* skip table and column */
jorton 040b95
                j += 2;
jorton 040b95
                break;
jorton 040b95
            default:
jorton 040b95
                bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
                bind[i].buffer = (void*)values[j];
jorton 040b95
                bind[i].buffer_length = strlen(values[j]);
jorton 040b95
                break;
jorton 040b95
            }
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                                     int *nrows, apr_dbd_prepared_t *statement,
jorton 040b95
                                     MYSQL_BIND *bind)
jorton 040b95
{
jorton 040b95
    int ret;
jorton 040b95
jorton 040b95
    ret = mysql_stmt_bind_param(statement->stmt, bind);
jorton 040b95
    if (ret != 0) {
jorton 040b95
        *nrows = 0;
jorton 040b95
        ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        ret = mysql_stmt_execute(statement->stmt);
jorton 040b95
        if (ret != 0) {
jorton 040b95
            ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
        }
jorton 040b95
        *nrows = mysql_stmt_affected_rows(statement->stmt);
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                            int *nrows, apr_dbd_prepared_t *statement,
jorton 040b95
                            const char **values)
jorton 040b95
{
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
    int ret;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
jorton 040b95
jorton 040b95
    dbd_mysql_bind(statement, values, bind);
jorton 040b95
jorton 040b95
    ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
jorton 040b95
jorton 040b95
    if (TXN_NOTICE_ERRORS(sql->trans)) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
jorton 040b95
                             apr_dbd_prepared_t *statement, va_list args)
jorton 040b95
{
jorton 040b95
    const char **values;
jorton 040b95
    int i;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
jorton 040b95
jorton 040b95
    for (i = 0; i < statement->nvals; i++) {
jorton 040b95
        values[i] = va_arg(args, const char*);
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return dbd_mysql_pquery(pool, sql, nrows, statement, values);
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                                      apr_dbd_results_t **res,
jorton 040b95
                                      apr_dbd_prepared_t *statement,
jorton 040b95
                                      int random, MYSQL_BIND *bind)
jorton 040b95
{
jorton 040b95
    int nfields, i;
jorton 040b95
    my_bool *is_nullr;
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
    my_bool *error;
jorton 040b95
#endif
jorton 040b95
    int ret;
jorton 040b95
    unsigned long *length, maxlen;
jorton 040b95
jorton 040b95
    ret = mysql_stmt_bind_param(statement->stmt, bind);
jorton 040b95
    if (ret == 0) {
jorton 040b95
        ret = mysql_stmt_execute(statement->stmt);
jorton 040b95
        if (!ret) {
jorton 040b95
            if (!*res) {
jorton 040b95
                *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
jorton 040b95
            }
jorton 040b95
            (*res)->random = random;
jorton 040b95
            (*res)->statement = statement->stmt;
jorton 040b95
            (*res)->res = mysql_stmt_result_metadata(statement->stmt);
jorton 040b95
            (*res)->pool = pool;
jorton 040b95
            apr_pool_cleanup_register(pool, (*res)->res,
jorton 040b95
                                      free_result, apr_pool_cleanup_null);
jorton 040b95
            nfields = mysql_num_fields((*res)->res);
jorton 040b95
            if (!(*res)->bind) {
jorton 040b95
                (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
jorton 040b95
                length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
                error = apr_palloc(pool, nfields*sizeof(my_bool));
jorton 040b95
#endif
jorton 040b95
                is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
jorton 040b95
                for ( i = 0; i < nfields; ++i ) {
jorton 040b95
                    maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
jorton 040b95
                              (*res)->res->fields[i].length : sql->fldsz) + 1;
jorton 040b95
                    if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) {
jorton 040b95
                        (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
jorton 040b95
                    }
jorton 040b95
                    else {
jorton 040b95
                        (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
                    }
jorton 040b95
                    (*res)->bind[i].buffer_length = maxlen;
jorton 040b95
                    (*res)->bind[i].length = &length[i];
jorton 040b95
                    (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
jorton 040b95
                    (*res)->bind[i].is_null = is_nullr+i;
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
                    (*res)->bind[i].error = error+i;
jorton 040b95
#endif
jorton 040b95
                }
jorton 040b95
            }
jorton 040b95
            ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
jorton 040b95
            if (!ret) {
jorton 040b95
                ret = mysql_stmt_store_result(statement->stmt);
jorton 040b95
            }
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    if (ret != 0) {
jorton 040b95
        ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                             apr_dbd_results_t **res,
jorton 040b95
                             apr_dbd_prepared_t *statement, int random,
jorton 040b95
                             const char **args)
jorton 040b95
{
jorton 040b95
    int ret;
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
jorton 040b95
jorton 040b95
    dbd_mysql_bind(statement, args, bind);
jorton 040b95
jorton 040b95
    ret = dbd_mysql_pselect_internal(pool, sql,  res, statement, random, bind);
jorton 040b95
jorton 040b95
    if (TXN_NOTICE_ERRORS(sql->trans)) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                              apr_dbd_results_t **res,
jorton 040b95
                              apr_dbd_prepared_t *statement, int random,
jorton 040b95
                              va_list args)
jorton 040b95
{
jorton 040b95
    const char **values;
jorton 040b95
    int i;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
jorton 040b95
jorton 040b95
    for (i = 0; i < statement->nvals; i++) {
jorton 040b95
        values[i] = va_arg(args, const char*);
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return dbd_mysql_pselect(pool, sql, res, statement, random, values);
jorton 040b95
}
jorton 040b95
jorton 040b95
static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement,
jorton 040b95
                            const void **values, MYSQL_BIND *bind)
jorton 040b95
{
jorton 040b95
    void *arg;
jorton 040b95
    int i, j;
jorton 040b95
    apr_dbd_type_e type;
jorton 040b95
jorton 040b95
    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
jorton 040b95
        arg = (void *)values[j];
jorton 040b95
jorton 040b95
        bind[i].length = &bind[i].buffer_length;
jorton 040b95
        bind[i].is_null = NULL;
jorton 040b95
jorton 040b95
        type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
jorton 040b95
        switch (type) {
jorton 040b95
        case APR_DBD_TYPE_TINY:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_TINY;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_UTINY:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_TINY;
jorton 040b95
            bind[i].is_unsigned = 1;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_SHORT:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_SHORT;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_USHORT:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_SHORT;
jorton 040b95
            bind[i].is_unsigned = 1;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_INT:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_LONG;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_UINT:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_LONG;
jorton 040b95
            bind[i].is_unsigned = 1;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_LONG:
jorton 040b95
            if (sizeof(int) == sizeof(long)) {
jorton 040b95
                bind[i].buffer = arg;
jorton 040b95
            }
jorton 040b95
            else {
jorton 040b95
                bind[i].buffer = apr_palloc(pool, sizeof(int));
jorton 040b95
                *(int*)bind[i].buffer = *(long*)arg;
jorton 040b95
            }
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_LONG;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_ULONG:
jorton 040b95
            if (sizeof(unsigned int) == sizeof(unsigned long)) {
jorton 040b95
                bind[i].buffer = arg;
jorton 040b95
            }
jorton 040b95
            else {
jorton 040b95
                bind[i].buffer = apr_palloc(pool, sizeof(unsigned int));
jorton 040b95
                *(unsigned int*)bind[i].buffer = *(unsigned long*)arg;
jorton 040b95
            }
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_LONG;
jorton 040b95
            bind[i].is_unsigned = 1;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_LONGLONG:
jorton 040b95
            if (sizeof(long long) == sizeof(apr_int64_t)) {
jorton 040b95
                bind[i].buffer = arg;
jorton 040b95
            }
jorton 040b95
            else {
jorton 040b95
                bind[i].buffer = apr_palloc(pool, sizeof(long long));
jorton 040b95
                *(long long*)bind[i].buffer = *(apr_int64_t*)arg;
jorton 040b95
            }
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_ULONGLONG:
jorton 040b95
            if (sizeof(unsigned long long) == sizeof(apr_uint64_t)) {
jorton 040b95
                bind[i].buffer = arg;
jorton 040b95
            }
jorton 040b95
            else {
jorton 040b95
                bind[i].buffer = apr_palloc(pool, sizeof(unsigned long long));
jorton 040b95
                *(unsigned long long*)bind[i].buffer = *(apr_uint64_t*)arg;
jorton 040b95
            }
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
jorton 040b95
            bind[i].is_unsigned = 1;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_FLOAT:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_FLOAT;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_DOUBLE:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_STRING:
jorton 040b95
        case APR_DBD_TYPE_TEXT:
jorton 040b95
        case APR_DBD_TYPE_TIME:
jorton 040b95
        case APR_DBD_TYPE_DATE:
jorton 040b95
        case APR_DBD_TYPE_DATETIME:
jorton 040b95
        case APR_DBD_TYPE_TIMESTAMP:
jorton 040b95
        case APR_DBD_TYPE_ZTIMESTAMP:
jorton 040b95
            bind[i].buffer = arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            bind[i].buffer_length = strlen((const char *)arg);
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_BLOB:
jorton 040b95
        case APR_DBD_TYPE_CLOB:
jorton 040b95
            bind[i].buffer = (void *)arg;
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
jorton 040b95
            bind[i].is_unsigned = 0;
jorton 040b95
            bind[i].buffer_length = *(apr_size_t*)values[++j];
jorton 040b95
jorton 040b95
            /* skip table and column */
jorton 040b95
            j += 2;
jorton 040b95
            break;
jorton 040b95
        case APR_DBD_TYPE_NULL:
jorton 040b95
        default:
jorton 040b95
            bind[i].buffer_type = MYSQL_TYPE_NULL;
jorton 040b95
            break;
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                             int *nrows, apr_dbd_prepared_t *statement,
jorton 040b95
                             const void **values)
jorton 040b95
{
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
    int ret;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
jorton 040b95
jorton 040b95
    dbd_mysql_bbind(pool, statement, values, bind);
jorton 040b95
jorton 040b95
    ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
jorton 040b95
jorton 040b95
    if (TXN_NOTICE_ERRORS(sql->trans)) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
jorton 040b95
                              apr_dbd_prepared_t *statement, va_list args)
jorton 040b95
{
jorton 040b95
    const void **values;
jorton 040b95
    int i;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
jorton 040b95
jorton 040b95
    for (i = 0; i < statement->nvals; i++) {
jorton 040b95
        values[i] = va_arg(args, const void*);
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return dbd_mysql_pbquery(pool, sql, nrows, statement, values);
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                              apr_dbd_results_t **res,
jorton 040b95
                              apr_dbd_prepared_t *statement, int random,
jorton 040b95
                              const void **args)
jorton 040b95
{
jorton 040b95
    int ret;
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
jorton 040b95
jorton 040b95
    dbd_mysql_bbind(pool, statement, args, bind);
jorton 040b95
jorton 040b95
    ret = dbd_mysql_pselect_internal(pool, sql,  res, statement, random, bind);
jorton 040b95
jorton 040b95
    if (TXN_NOTICE_ERRORS(sql->trans)) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                               apr_dbd_results_t **res,
jorton 040b95
                               apr_dbd_prepared_t *statement, int random,
jorton 040b95
                               va_list args)
jorton 040b95
{
jorton 040b95
    const void **values;
jorton 040b95
    int i;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
jorton 040b95
jorton 040b95
    for (i = 0; i < statement->nvals; i++) {
jorton 040b95
        values[i] = va_arg(args, const void*);
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return dbd_mysql_pbselect(pool, sql, res, statement, random, values);
jorton 040b95
}
jorton 040b95
#else
jorton 040b95
static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                             const char *query, const char *label,
jorton 040b95
                             apr_dbd_prepared_t **statement)
jorton 040b95
{
jorton 040b95
    /* Translate from apr_dbd to native query format */
jorton 040b95
    char *myquery = apr_pstrdup(pool, query);
jorton 040b95
    char *p = myquery;
jorton 040b95
    const char *q;
jorton 040b95
    int ret;
jorton 040b95
    for (q = query; *q; ++q) {
jorton 040b95
        if (q[0] == '%') {
jorton 040b95
            if (isalpha(q[1])) {
jorton 040b95
                *p++ = '?';
jorton 040b95
                ++q;
jorton 040b95
            }
jorton 040b95
            else if (q[1] == '%') {
jorton 040b95
                /* reduce %% to % */
jorton 040b95
                *p++ = *q++;
jorton 040b95
            }
jorton 040b95
            else {
jorton 040b95
                *p++ = *q;
jorton 040b95
            }
jorton 040b95
        }
jorton 040b95
        else {
jorton 040b95
            *p++ = *q;
jorton 040b95
        }
jorton 040b95
    } 
jorton 040b95
    *p = 0;
jorton 040b95
    if (!*statement) {
jorton 040b95
        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
jorton 040b95
    }
jorton 040b95
    (*statement)->stmt = mysql_stmt_init(sql->conn);
jorton 040b95
jorton 040b95
    if ((*statement)->stmt) {
jorton 040b95
        apr_pool_cleanup_register(pool, (*statement)->stmt,
jorton 040b95
                                  stmt_close, apr_pool_cleanup_null);
jorton 040b95
        ret = mysql_stmt_prepare((*statement)->stmt, myquery, strlen(myquery));
jorton 040b95
jorton 040b95
        if (ret != 0) {
jorton 040b95
            ret = mysql_stmt_errno((*statement)->stmt);
jorton 040b95
        }
jorton 040b95
jorton 040b95
        return ret;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    return CR_OUT_OF_MEMORY;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                            int *nrows, apr_dbd_prepared_t *statement,
jorton 040b95
                            int nargs, const char **values)
jorton 040b95
{
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
    char *arg;
jorton 040b95
    int ret;
jorton 040b95
    int i;
jorton 040b95
    my_bool is_null = FALSE;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
    nargs = mysql_stmt_param_count(statement->stmt);
jorton 040b95
jorton 040b95
    bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
jorton 040b95
    for (i=0; i < nargs; ++i) {
jorton 040b95
        arg = (char*)values[i];
jorton 040b95
        bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
        bind[i].buffer = arg;
jorton 040b95
        bind[i].buffer_length = strlen(arg);
jorton 040b95
        bind[i].length = &bind[i].buffer_length;
jorton 040b95
        bind[i].is_null = &is_null;
jorton 040b95
        bind[i].is_unsigned = 0;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    ret = mysql_stmt_bind_param(statement->stmt, bind);
jorton 040b95
    if (ret != 0) {
jorton 040b95
        *nrows = 0;
jorton 040b95
        ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        ret = mysql_stmt_execute(statement->stmt);
jorton 040b95
        if (ret != 0) {
jorton 040b95
            ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
        }
jorton 040b95
        *nrows = mysql_stmt_affected_rows(statement->stmt);
jorton 040b95
    }
jorton 040b95
    if (sql->trans) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
jorton 040b95
                             apr_dbd_prepared_t *statement, va_list args)
jorton 040b95
{
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
    char *arg;
jorton 040b95
    int ret;
jorton 040b95
    int nargs = 0;
jorton 040b95
    int i;
jorton 040b95
    my_bool is_null = FALSE;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
    nargs = mysql_stmt_param_count(statement->stmt);
jorton 040b95
jorton 040b95
    bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
jorton 040b95
    for (i=0; i < nargs; ++i) {
jorton 040b95
        arg = va_arg(args, char*);
jorton 040b95
        bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
        bind[i].buffer = arg;
jorton 040b95
        bind[i].buffer_length = strlen(arg);
jorton 040b95
        bind[i].length = &bind[i].buffer_length;
jorton 040b95
        bind[i].is_null = &is_null;
jorton 040b95
        bind[i].is_unsigned = 0;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    ret = mysql_stmt_bind_param(statement->stmt, bind);
jorton 040b95
    if (ret != 0) {
jorton 040b95
        *nrows = 0;
jorton 040b95
        ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        ret = mysql_stmt_execute(statement->stmt);
jorton 040b95
        if (ret != 0) {
jorton 040b95
            ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
        }
jorton 040b95
        *nrows = mysql_stmt_affected_rows(statement->stmt);
jorton 040b95
    }
jorton 040b95
    if (sql->trans) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                             apr_dbd_results_t **res,
jorton 040b95
                             apr_dbd_prepared_t *statement, int random,
jorton 040b95
                             int nargs, const char **args)
jorton 040b95
{
jorton 040b95
    int i;
jorton 040b95
    int nfields;
jorton 040b95
    char *arg;
jorton 040b95
    my_bool is_null = FALSE;
jorton 040b95
    my_bool *is_nullr;
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
    my_bool *error;
jorton 040b95
#endif
jorton 040b95
    int ret;
jorton 040b95
    unsigned long *length, maxlen;
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    nargs = mysql_stmt_param_count(statement->stmt);
jorton 040b95
    bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
jorton 040b95
jorton 040b95
    for (i=0; i < nargs; ++i) {
jorton 040b95
        arg = (char*)args[i];
jorton 040b95
        bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
        bind[i].buffer = arg;
jorton 040b95
        bind[i].buffer_length = strlen(arg);
jorton 040b95
        bind[i].length = &bind[i].buffer_length;
jorton 040b95
        bind[i].is_null = &is_null;
jorton 040b95
        bind[i].is_unsigned = 0;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    ret = mysql_stmt_bind_param(statement->stmt, bind);
jorton 040b95
    if (ret == 0) {
jorton 040b95
        ret = mysql_stmt_execute(statement->stmt);
jorton 040b95
        if (!ret) {
jorton 040b95
            if (!*res) {
jorton 040b95
                *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
jorton 040b95
            }
jorton 040b95
            (*res)->random = random;
jorton 040b95
            (*res)->statement = statement->stmt;
jorton 040b95
            (*res)->res = mysql_stmt_result_metadata(statement->stmt);
jorton 040b95
            apr_pool_cleanup_register(pool, (*res)->res,
jorton 040b95
                                      free_result, apr_pool_cleanup_null);
jorton 040b95
            nfields = mysql_num_fields((*res)->res);
jorton 040b95
            if (!(*res)->bind) {
jorton 040b95
                (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
jorton 040b95
                length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
                error = apr_palloc(pool, nfields*sizeof(my_bool));
jorton 040b95
#endif
jorton 040b95
                is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
jorton 040b95
                for ( i = 0; i < nfields; ++i ) {
jorton 040b95
                    maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
jorton 040b95
                              (*res)->res->fields[i].length : sql->fldsz) + 1;
jorton 040b95
                    (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
                    (*res)->bind[i].buffer_length = maxlen;
jorton 040b95
                    (*res)->bind[i].length = &length[i];
jorton 040b95
                    (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
jorton 040b95
                    (*res)->bind[i].is_null = is_nullr+i;
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
                    (*res)->bind[i].error = error+i;
jorton 040b95
#endif
jorton 040b95
                }
jorton 040b95
            }
jorton 040b95
            ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
jorton 040b95
            if (!ret) {
jorton 040b95
                ret = mysql_stmt_store_result(statement->stmt);
jorton 040b95
            }
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    if (ret != 0) {
jorton 040b95
        ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
    }
jorton 040b95
    if (sql->trans) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
jorton 040b95
                              apr_dbd_results_t **res,
jorton 040b95
                              apr_dbd_prepared_t *statement, int random,
jorton 040b95
                              va_list args)
jorton 040b95
{
jorton 040b95
    int i;
jorton 040b95
    int nfields;
jorton 040b95
    char *arg;
jorton 040b95
    my_bool is_null = FALSE;
jorton 040b95
    my_bool *is_nullr;
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
    my_bool *error;
jorton 040b95
#endif
jorton 040b95
    int ret;
jorton 040b95
    unsigned long *length, maxlen;
jorton 040b95
    int nargs;
jorton 040b95
    MYSQL_BIND *bind;
jorton 040b95
jorton 040b95
    if (sql->trans && sql->trans->errnum) {
jorton 040b95
        return sql->trans->errnum;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    nargs = mysql_stmt_param_count(statement->stmt);
jorton 040b95
    bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
jorton 040b95
jorton 040b95
    for (i=0; i < nargs; ++i) {
jorton 040b95
        arg = va_arg(args, char*);
jorton 040b95
        bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
        bind[i].buffer = arg;
jorton 040b95
        bind[i].buffer_length = strlen(arg);
jorton 040b95
        bind[i].length = &bind[i].buffer_length;
jorton 040b95
        bind[i].is_null = &is_null;
jorton 040b95
        bind[i].is_unsigned = 0;
jorton 040b95
    }
jorton 040b95
jorton 040b95
    ret = mysql_stmt_bind_param(statement->stmt, bind);
jorton 040b95
    if (ret == 0) {
jorton 040b95
        ret = mysql_stmt_execute(statement->stmt);
jorton 040b95
        if (!ret) {
jorton 040b95
            if (!*res) {
jorton 040b95
                *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
jorton 040b95
            }
jorton 040b95
            (*res)->random = random;
jorton 040b95
            (*res)->statement = statement->stmt;
jorton 040b95
            (*res)->res = mysql_stmt_result_metadata(statement->stmt);
jorton 040b95
            apr_pool_cleanup_register(pool, (*res)->res,
jorton 040b95
                                      free_result, apr_pool_cleanup_null);
jorton 040b95
            nfields = mysql_num_fields((*res)->res);
jorton 040b95
            if (!(*res)->bind) {
jorton 040b95
                (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
jorton 040b95
                length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
                error = apr_palloc(pool, nfields*sizeof(my_bool));
jorton 040b95
#endif
jorton 040b95
                is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
jorton 040b95
                for ( i = 0; i < nfields; ++i ) {
jorton 040b95
                    maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
jorton 040b95
                              (*res)->res->fields[i].length : sql->fldsz) + 1;
jorton 040b95
                    (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
jorton 040b95
                    (*res)->bind[i].buffer_length = maxlen;
jorton 040b95
                    (*res)->bind[i].length = &length[i];
jorton 040b95
                    (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
jorton 040b95
                    (*res)->bind[i].is_null = is_nullr+i;
jorton 040b95
#if MYSQL_VERSION_ID >= 50000
jorton 040b95
                    (*res)->bind[i].error = error+i;
jorton 040b95
#endif
jorton 040b95
                }
jorton 040b95
            }
jorton 040b95
            ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
jorton 040b95
            if (!ret) {
jorton 040b95
                ret = mysql_stmt_store_result(statement->stmt);
jorton 040b95
            }
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    if (ret != 0) {
jorton 040b95
        ret = mysql_stmt_errno(statement->stmt);
jorton 040b95
    }
jorton 040b95
    if (sql->trans) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
#endif
jorton 040b95
jorton 040b95
static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
jorton 040b95
{
jorton 040b95
    int ret = -1;
jorton 040b95
    if (trans) {
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
        /* rollback on error or explicit rollback request */
jorton 040b95
        if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
jorton 040b95
#else
jorton 040b95
        if (trans->errnum) {
jorton 040b95
#endif
jorton 040b95
            trans->errnum = 0;
jorton 040b95
            ret = mysql_rollback(trans->handle->conn);
jorton 040b95
        }
jorton 040b95
        else {
jorton 040b95
            ret = mysql_commit(trans->handle->conn);
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    ret |= mysql_autocommit(trans->handle->conn, 1);
jorton 040b95
    trans->handle->trans = NULL;
jorton 040b95
    return ret;
jorton 040b95
}
jorton 040b95
/* Whether or not transactions work depends on whether the
jorton 040b95
 * underlying DB supports them within MySQL.  Unfortunately
jorton 040b95
 * it fails silently with the default InnoDB.
jorton 040b95
 */
jorton 040b95
jorton 040b95
static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
jorton 040b95
                                 apr_dbd_transaction_t **trans)
jorton 040b95
{
jorton 040b95
    /* Don't try recursive transactions here */
jorton 040b95
    if (handle->trans) {
jorton 040b95
        dbd_mysql_end_transaction(handle->trans) ;
jorton 040b95
    }
jorton 040b95
    if (!*trans) {
jorton 040b95
        *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
jorton 040b95
    }
jorton 040b95
    (*trans)->errnum = mysql_autocommit(handle->conn, 0);
jorton 040b95
    (*trans)->handle = handle;
jorton 040b95
    handle->trans = *trans;
jorton 040b95
    return (*trans)->errnum;
jorton 040b95
}
jorton 040b95
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
jorton 040b95
{
jorton 040b95
    if (!trans)
jorton 040b95
        return APR_DBD_TRANSACTION_COMMIT;
jorton 040b95
jorton 040b95
    return trans->mode;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans,
jorton 040b95
                                          int mode)
jorton 040b95
{
jorton 040b95
    if (!trans)
jorton 040b95
        return APR_DBD_TRANSACTION_COMMIT;
jorton 040b95
jorton 040b95
    return trans->mode = (mode & TXN_MODE_BITS);
jorton 040b95
}
jorton 040b95
#endif
jorton 040b95
jorton 040b95
static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params)
jorton 040b95
{
jorton 040b95
    static const char *const delims = " \r\n\t;|,";
jorton 040b95
    const char *ptr;
jorton 040b95
    int i;
jorton 040b95
    const char *key;
jorton 040b95
    size_t klen;
jorton 040b95
    const char *value;
jorton 040b95
    size_t vlen;
jorton 040b95
#if MYSQL_VERSION_ID >= 50013
jorton 040b95
    my_bool do_reconnect = 1;
jorton 040b95
#endif
jorton 040b95
    MYSQL *real_conn;
jorton 040b95
    unsigned long flags = 0;
jorton 040b95
    
jorton 040b95
    struct {
jorton 040b95
        const char *field;
jorton 040b95
        const char *value;
jorton 040b95
    } fields[] = {
jorton 040b95
        {"host", NULL},
jorton 040b95
        {"user", NULL},
jorton 040b95
        {"pass", NULL},
jorton 040b95
        {"dbname", NULL},
jorton 040b95
        {"port", NULL},
jorton 040b95
        {"sock", NULL},
jorton 040b95
        {"flags", NULL},
jorton 040b95
        {"fldsz", NULL},
jorton 040b95
        {NULL, NULL}
jorton 040b95
    };
jorton 040b95
    unsigned int port = 0;
jorton 040b95
    apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t));
jorton 040b95
    sql->fldsz = FIELDSIZE;
jorton 040b95
    sql->conn = mysql_init(sql->conn);
jorton 040b95
    if ( sql->conn == NULL ) {
jorton 040b95
        return NULL;
jorton 040b95
    }
jorton 040b95
    for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
jorton 040b95
        for (key = ptr-1; isspace(*key); --key);
jorton 040b95
        klen = 0;
jorton 040b95
        while (isalpha(*key)) {
jorton 040b95
            /* don't parse backwards off the start of the string */
jorton 040b95
            if (key == params) {
jorton 040b95
                --key;
jorton 040b95
                ++klen;
jorton 040b95
                break;
jorton 040b95
            }
jorton 040b95
            --key;
jorton 040b95
            ++klen;
jorton 040b95
        }
jorton 040b95
        ++key;
jorton 040b95
        for (value = ptr+1; isspace(*value); ++value);
jorton 040b95
        vlen = strcspn(value, delims);
jorton 040b95
        for (i = 0; fields[i].field != NULL; i++) {
jorton 040b95
            if (!strncasecmp(fields[i].field, key, klen)) {
jorton 040b95
                fields[i].value = apr_pstrndup(pool, value, vlen);
jorton 040b95
                break;
jorton 040b95
            }
jorton 040b95
        }
jorton 040b95
        ptr = value+vlen;
jorton 040b95
    }
jorton 040b95
    if (fields[4].value != NULL) {
jorton 040b95
        port = atoi(fields[4].value);
jorton 040b95
    }
jorton 040b95
    if (fields[6].value != NULL &&
jorton 040b95
        !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) {
jorton 040b95
        flags |= CLIENT_FOUND_ROWS; /* only option we know */
jorton 040b95
    }
jorton 040b95
    if (fields[7].value != NULL) {
jorton 040b95
        sql->fldsz = atol(fields[7].value);
jorton 040b95
    }
jorton 040b95
jorton 040b95
#if MYSQL_VERSION_ID >= 50013
jorton 040b95
    /* the MySQL manual says this should be BEFORE mysql_real_connect */
jorton 040b95
    mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
jorton 040b95
#endif
jorton 040b95
    
jorton 040b95
    real_conn = mysql_real_connect(sql->conn, fields[0].value,
jorton 040b95
                                   fields[1].value, fields[2].value,
jorton 040b95
                                   fields[3].value, port,
jorton 040b95
                                   fields[5].value, flags);
jorton 040b95
jorton 040b95
    if(real_conn == NULL) {
jorton 040b95
        mysql_close(sql->conn);
jorton 040b95
        return NULL;
jorton 040b95
    }
jorton 040b95
jorton 040b95
#if MYSQL_VERSION_ID >= 50013
jorton 040b95
    /* Some say this should be AFTER mysql_real_connect */
jorton 040b95
    mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
jorton 040b95
#endif
jorton 040b95
jorton 040b95
    return sql;
jorton 040b95
}
jorton 040b95
jorton 040b95
static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
jorton 040b95
{
jorton 040b95
    mysql_close(handle->conn);
jorton 040b95
    return APR_SUCCESS;
jorton 040b95
}
jorton 040b95
jorton 040b95
static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
jorton 040b95
                                         apr_dbd_t *handle)
jorton 040b95
{
jorton 040b95
    return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
jorton 040b95
                               const char* name)
jorton 040b95
{
jorton 040b95
    return mysql_select_db(handle->conn, name);
jorton 040b95
}
jorton 040b95
jorton 040b95
static void *dbd_mysql_native(apr_dbd_t *handle)
jorton 040b95
{
jorton 040b95
    return handle->conn;
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_num_cols(apr_dbd_results_t *res)
jorton 040b95
{
jorton 040b95
    if (res->statement) {
jorton 040b95
        return mysql_stmt_field_count(res->statement);
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        return mysql_num_fields(res->res);
jorton 040b95
    }
jorton 040b95
}
jorton 040b95
jorton 040b95
static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
jorton 040b95
{
jorton 040b95
    if (res->random) {
jorton 040b95
        if (res->statement) {
jorton 040b95
            return (int) mysql_stmt_num_rows(res->statement);
jorton 040b95
        }
jorton 040b95
        else {
jorton 040b95
            return (int) mysql_num_rows(res->res);
jorton 040b95
        }
jorton 040b95
    }
jorton 040b95
    else {
jorton 040b95
        return -1;
jorton 040b95
    }
jorton 040b95
}
jorton 040b95
jorton 040b95
static apr_status_t thread_end(void *data)
jorton 040b95
{
jorton 040b95
    mysql_thread_end();
jorton 040b95
    return APR_SUCCESS;
jorton 040b95
}
jorton 040b95
jorton 040b95
static void dbd_mysql_init(apr_pool_t *pool)
jorton 040b95
{
jorton 040b95
    my_init();
jorton 040b95
    /* FIXME: this is a guess; find out what it really does */ 
jorton 040b95
    apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null);
jorton 040b95
}
jorton 040b95
APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = {
jorton 040b95
    "mysql",
jorton 040b95
    dbd_mysql_init,
jorton 040b95
    dbd_mysql_native,
jorton 040b95
    dbd_mysql_open,
jorton 040b95
    dbd_mysql_check_conn,
jorton 040b95
    dbd_mysql_close,
jorton 040b95
    dbd_mysql_select_db,
jorton 040b95
    dbd_mysql_transaction,
jorton 040b95
    dbd_mysql_end_transaction,
jorton 040b95
    dbd_mysql_query,
jorton 040b95
    dbd_mysql_select,
jorton 040b95
    dbd_mysql_num_cols,
jorton 040b95
    dbd_mysql_num_tuples,
jorton 040b95
    dbd_mysql_get_row,
jorton 040b95
    dbd_mysql_get_entry,
jorton 040b95
    dbd_mysql_error,
jorton 040b95
    dbd_mysql_escape,
jorton 040b95
    dbd_mysql_prepare,
jorton 040b95
    dbd_mysql_pvquery,
jorton 040b95
    dbd_mysql_pvselect,
jorton 040b95
    dbd_mysql_pquery,
jorton 040b95
    dbd_mysql_pselect
jorton 040b95
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
jorton 040b95
    ,
jorton 040b95
    dbd_mysql_get_name,
jorton 040b95
    dbd_mysql_transaction_mode_get,
jorton 040b95
    dbd_mysql_transaction_mode_set,
jorton 040b95
    "?",
jorton 040b95
    dbd_mysql_pvbquery,
jorton 040b95
    dbd_mysql_pvbselect,
jorton 040b95
    dbd_mysql_pbquery,
jorton 040b95
    dbd_mysql_pbselect,
jorton 040b95
    dbd_mysql_datum_get
jorton 040b95
#endif
jorton 040b95
};
jorton 040b95
jorton 040b95
#endif