bojan 1acc84
/* Licensed to the Apache Software Foundation (ASF) under one or more
bojan 1acc84
 * contributor license agreements.  See the NOTICE file distributed with
bojan 1acc84
 * this work for additional information regarding copyright ownership.
bojan 1acc84
 * The ASF licenses this file to You under the Apache License, Version 2.0
bojan 1acc84
 * (the "License"); you may not use this file except in compliance with
bojan 1acc84
 * the License.  You may obtain a copy of the License at
jorton 040b95
 *
bojan 1acc84
 *     http://www.apache.org/licenses/LICENSE-2.0
jorton 040b95
 *
bojan 1acc84
 * Unless required by applicable law or agreed to in writing, software
bojan 1acc84
 * distributed under the License is distributed on an "AS IS" BASIS,
bojan 1acc84
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
bojan 1acc84
 * See the License for the specific language governing permissions and
bojan 1acc84
 * limitations under the License.
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
};
jorton 040b95
jorton 040b95
struct apr_dbd_transaction_t {
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
};
jorton 040b95
struct apr_dbd_row_t {
jorton 040b95
    MYSQL_ROW row;
jorton 040b95
    apr_dbd_results_t *res;
jorton 040b95
};
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 (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 (sql->trans) {
jorton 040b95
        sql->trans->errnum = ret;
jorton 040b95
    }
jorton 040b95
    return ret;
jorton 040b95
}
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
    }
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
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 (sql->trans) {
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
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
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 (trans->errnum) {
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
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
};
jorton 040b95
jorton 040b95
#endif