apr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From n..@apache.org
Subject svn commit: r572642 - in /apr/apr-util/trunk: CHANGES INSTALL.MySQL build/dbd.m4 configure.in dbd/apr_dbd_freetds.c dbd/apr_dbd_mysql.c
Date Tue, 04 Sep 2007 12:05:36 GMT
Author: niq
Date: Tue Sep  4 05:05:35 2007
New Revision: 572642

URL: http://svn.apache.org/viewvc?rev=572642&view=rev
Log:
Commit DBD MySQL driver (relicensed) and FreeTDS driver (new)

Added:
    apr/apr-util/trunk/dbd/apr_dbd_freetds.c
    apr/apr-util/trunk/dbd/apr_dbd_mysql.c
Removed:
    apr/apr-util/trunk/INSTALL.MySQL
Modified:
    apr/apr-util/trunk/CHANGES
    apr/apr-util/trunk/build/dbd.m4
    apr/apr-util/trunk/configure.in

Modified: apr/apr-util/trunk/CHANGES
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/CHANGES?rev=572642&r1=572641&r2=572642&view=diff
==============================================================================
--- apr/apr-util/trunk/CHANGES [utf-8] (original)
+++ apr/apr-util/trunk/CHANGES [utf-8] Tue Sep  4 05:05:35 2007
@@ -1,6 +1,10 @@
                                                      -*- coding: utf-8 -*-
 Changes with APR-util 1.3.0
 
+  *) Add limited apr_dbd_freetds driver (MSSQL and Sybase) [Nick Kew]
+
+  *) Commit relicensed apr_dbd_mysql driver to /trunk/  [Nick Kew]
+
   *) Support BerkeleyDB 4.6.  [Arfrever Frehtes Taifersar Arahesis]
 
   *) Support Tivoli ITDS LDAP client library.  [Paul Reder]

Modified: apr/apr-util/trunk/build/dbd.m4
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/build/dbd.m4?rev=572642&r1=572641&r2=572642&view=diff
==============================================================================
--- apr/apr-util/trunk/build/dbd.m4 (original)
+++ apr/apr-util/trunk/build/dbd.m4 Tue Sep  4 05:05:35 2007
@@ -365,6 +365,57 @@
   AC_SUBST(LDADD_dbd_oracle)
 ])
 
+dnl
+AC_DEFUN([APU_CHECK_DBD_FREETDS], [
+  apu_have_freetds=0
+
+  AC_ARG_WITH([freetds], [
+  --with-freetds=DIR         
+  ], [
+    apu_have_freetds=0
+    if test "$withval" = "yes"; then
+      AC_CHECK_HEADERS(sybdb.h, AC_CHECK_LIB(sybdb, tdsdbopen, [apu_have_freetds=1]))
+    elif test "$withval" = "no"; then
+      apu_have_freetds=0
+    else
+      old_cppflags="$CPPFLAGS"
+      old_ldflags="$LDFLAGS"
+
+      sybdb_CPPFLAGS="-I$withval/include"
+      sybdb_LDFLAGS="-L$withval/lib "
+
+      APR_ADDTO(CPPFLAGS, [$sybdb_CPPFLAGS])
+      APR_ADDTO(LDFLAGS, [$sybdb_LDFLAGS])
+
+      AC_MSG_NOTICE(checking for freetds in $withval)
+      AC_CHECK_HEADERS(sybdb.h, AC_CHECK_LIB(sybdb, tdsdbopen, [apu_have_freetds=1]))
+      if test "$apu_have_freetds" != "0"; then
+        APR_ADDTO(APRUTIL_LDFLAGS, [-L$withval/lib])
+        APR_ADDTO(APRUTIL_INCLUDES, [-I$withval/include])
+      fi
+
+      CPPFLAGS="$old_cppflags"
+      LDFLAGS="$old_ldflags"
+    fi
+  ], [
+    apu_have_freetds=0
+    AC_CHECK_HEADERS(sybdb.h, AC_CHECK_LIB(sybdb, tdsdbopen, [apu_have_freetds=1]))
+  ])
+
+  AC_SUBST(apu_have_freetds)
+
+  dnl Since we have already done the AC_CHECK_LIB tests, if we have it, 
+  dnl we know the library is there.
+  if test "$apu_have_freetds" = "1"; then
+    LDADD_dbd_freetds="$LDADD_dbd_freetds -lsybdb"
+    dnl Erm, I needed pcreposix, but I think that dependency has gone
+    dnl from the current code
+    dnl LDADD_dbd_freetds="$LDADD_dbd_freetds -lsybdb -lpcreposix"
+  fi
+  AC_SUBST(LDADD_dbd_freetds)
+])
+dnl
+
 AC_DEFUN([APU_CHECK_DBD_DSO], [
 
   AC_ARG_ENABLE([dbd-dso], 
@@ -379,6 +430,7 @@
      test $apu_have_mysql = 1 && dsos="$dsos dbd/apr_dbd_mysql.la"
      test $apu_have_sqlite2 = 1 && dsos="$dsos dbd/apr_dbd_sqlite2.la"
      test $apu_have_sqlite3 = 1 && dsos="$dsos dbd/apr_dbd_sqlite3.la"
+     test $apu_have_freetds = 1 && dsos="$dsos dbd/apr_dbd_freetds.la"
 
      APU_MODULES="$APU_MODULES $dsos"
   else
@@ -390,9 +442,10 @@
      test $apu_have_mysql = 1 && objs="$objs dbd/apr_dbd_mysql.lo"
      test $apu_have_sqlite2 = 1 && objs="$objs dbd/apr_dbd_sqlite2.lo"
      test $apu_have_sqlite3 = 1 && objs="$objs dbd/apr_dbd_sqlite3.lo"
+     test $apu_have_freetds = 1 && objs="$objs dbd/apr_dbd_freetds.lo"
      EXTRA_OBJECTS="$EXTRA_OBJECTS $objs"
 
-     APRUTIL_LIBS="$APRUTIL_LIBS $LDADD_dbd_pgsql $LDADD_dbd_sqlite2 $LDADD_dbd_sqlite3 $LDADD_dbd_oracle $LDADD_dbd_mysql"
-     APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS $LDADD_dbd_pgsql $LDADD_dbd_sqlite2 $LDADD_dbd_sqlite3 $LDADD_dbd_oracle $LDADD_dbd_mysql"
+     APRUTIL_LIBS="$APRUTIL_LIBS $LDADD_dbd_pgsql $LDADD_dbd_sqlite2 $LDADD_dbd_sqlite3 $LDADD_dbd_oracle $LDADD_dbd_mysql $LDADD_dbd_freetds"
+     APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS $LDADD_dbd_pgsql $LDADD_dbd_sqlite2 $LDADD_dbd_sqlite3 $LDADD_dbd_oracle $LDADD_dbd_mysql $LDADD_dbd_freetds"
   fi
 ])

Modified: apr/apr-util/trunk/configure.in
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/configure.in?rev=572642&r1=572641&r2=572642&view=diff
==============================================================================
--- apr/apr-util/trunk/configure.in (original)
+++ apr/apr-util/trunk/configure.in Tue Sep  4 05:05:35 2007
@@ -151,6 +151,7 @@
 APU_CHECK_DBD_SQLITE3
 APU_CHECK_DBD_SQLITE2
 APU_CHECK_DBD_ORACLE
+APU_CHECK_DBD_FREETDS
 dnl Enable DSO build; must be last:
 APU_CHECK_DBD_DSO
 APU_FIND_EXPAT

Added: apr/apr-util/trunk/dbd/apr_dbd_freetds.c
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/dbd/apr_dbd_freetds.c?rev=572642&view=auto
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd_freetds.c (added)
+++ apr/apr-util/trunk/dbd/apr_dbd_freetds.c Tue Sep  4 05:05:35 2007
@@ -0,0 +1,786 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apu.h"
+#include "apu_config.h"
+
+/* COMPILE_STUBS: compile stubs for unimplemented functions.
+ *
+ * This is required to compile in /trunk/, but can be
+ * undefined to compile a driver for httpd-2.2 and other
+ * APR-1.2 applications
+ */
+#define COMPILE_STUBS
+
+#if APU_HAVE_FREETDS
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "apr_strings.h"
+#include "apr_lib.h"
+
+#include "apr_pools.h"
+#include "apr_dbd_internal.h"
+
+#include <sybdb.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <regex.h>
+
+/* This probably needs to change for different applications */
+#define MAX_COL_LEN 256
+
+typedef struct freetds_cell_t {
+    int type;
+    DBINT len;
+    BYTE *data;
+} freetds_cell_t;
+
+struct apr_dbd_transaction_t {
+    int mode;
+    int errnum;
+    apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+    DBPROCESS *proc;
+    apr_dbd_transaction_t *trans;
+    apr_pool_t *pool;
+    const char *params;
+    RETCODE err;
+};
+
+struct apr_dbd_results_t {
+    int random;
+    size_t ntuples;
+    size_t sz;
+    apr_pool_t *pool;
+    DBPROCESS *proc;
+};
+
+struct apr_dbd_row_t {
+    apr_dbd_results_t *res;
+    BYTE buf[MAX_COL_LEN];
+};
+
+struct apr_dbd_prepared_t {
+    int nargs;
+    regex_t **taint;
+    int *sz;
+    char *fmt;
+};
+
+#define dbd_freetds_is_success(x) (x == SUCCEED)
+
+static int labelnum = 0; /* FIXME */
+static regex_t dbd_freetds_find_arg;
+
+/* execute a query that doesn't return a result set, mop up,
+ * and return and APR-flavoured status
+ */
+static RETCODE freetds_exec(DBPROCESS *proc, const char *query,
+                            int want_results, int *nrows)
+{
+    /* TBD */
+    RETCODE rv = dbcmd(proc, query);
+    if (rv != SUCCEED) {
+        return rv;
+    }
+    rv = dbsqlexec(proc);
+    if (rv != SUCCEED) {
+        return rv;
+    }
+    if (!want_results) {
+        while (dbresults(proc) != NO_MORE_RESULTS) {
+            ++*nrows;
+        }
+    }
+    return SUCCEED;
+}
+static apr_status_t clear_result(void *data)
+{
+    /* clear cursor */
+    return (dbcanquery((DBPROCESS*)data) == SUCCEED)
+            ? APR_SUCCESS
+            : APR_EGENERAL;
+}
+
+static int dbd_freetds_select(apr_pool_t *pool, apr_dbd_t *sql,
+                              apr_dbd_results_t **results,
+                              const char *query, int seek)
+{
+    apr_dbd_results_t *res;
+    int i;
+    if (sql->trans && (sql->trans->errnum != SUCCEED)) {
+        return 1;
+    }
+    /* the core of this is
+     * dbcmd(proc, query);
+     * dbsqlexec(proc);
+     * while (dbnextrow(dbproc) != NO_MORE_ROWS) {
+     *     do things
+     * }
+     *
+     * Ignore seek
+     */
+
+    sql->err = freetds_exec(sql->proc, query, 1, NULL);
+    if (!dbd_freetds_is_success(sql->err)) {
+        if (sql->trans) {
+            sql->trans->errnum = sql->err;
+        }
+        return 1;
+    }
+
+    sql->err = dbresults(sql->proc);
+    if (sql->err != SUCCEED) {
+        if (sql->trans) {
+            sql->trans->errnum = sql->err;
+        }
+        return 1;
+    }
+
+    if (!*results) {
+        *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+    }
+    res = *results;
+    res->proc = sql->proc;
+    res->random = seek;
+    res->pool = pool;
+    res->ntuples = dblastrow(sql->proc);
+    res->sz = dbnumcols(sql->proc);
+    apr_pool_cleanup_register(pool, sql->proc, clear_result,
+                              apr_pool_cleanup_null);
+
+#if 0
+    /* Now we have a result set.  We need to bind to its vars */
+    res->vars = apr_palloc(pool, res->sz * sizeof(freetds_cell_t*));
+    for (i=1; i <= res->sz; ++i) {
+        freetds_cell_t *cell = &res->vars[i-1];
+        cell->type = dbcoltype(sql->proc, i);
+        cell->len = dbcollen(sql->proc, i);
+        cell->data = apr_palloc(pool, cell->len);
+        sql->err = dbbind(sql->proc, i, /*cell->type */ STRINGBIND, cell->len, cell->data);
+        if (sql->err != SUCCEED) {
+            fprintf(stderr, "dbbind error: %d, %d, %d", i, cell->type, cell->len);
+        }
+        if ((sql->err != SUCCEED) && (sql->trans != NULL)) {
+            sql->trans->errnum = sql->err;
+        }
+    }
+#endif
+    return (sql->err == SUCCEED) ? 0 : 1;
+}
+static const char *dbd_untaint(apr_pool_t *pool, regex_t *rx, const char *val)
+{
+    regmatch_t match[1];
+    if (rx == NULL) {
+        /* no untaint expression */
+        return val;
+    }
+    if (regexec(rx, val, 1, match, 0) == 0) {
+        return apr_pstrndup(pool, val+match[0].rm_so,
+                            match[0].rm_eo - match[0].rm_so);
+    }
+    return "";
+}
+static const char *dbd_statement(apr_pool_t *pool,
+                                 apr_dbd_prepared_t *stmt,
+                                 int nargs, const char **args)
+{
+    int i;
+    int len;
+    const char *var;
+    char *ret;
+    const char *p_in;
+    char *p_out;
+    char *q;
+   
+    /* compute upper bound on length (since untaint shrinks) */
+    len  = strlen(stmt->fmt) +1;
+    for (i=0; i<nargs; ++i) {
+        len += strlen(args[i]) - 2;
+    }
+    i = 0;
+    p_in = stmt->fmt;
+    p_out = ret = apr_palloc(pool, len);
+    /* FIXME silly bug - this'll catch %%s */
+    while (q = strstr(p_in, "%s"), q != NULL) {
+        len = q-p_in;
+        strncpy(p_out, p_in, len);
+        p_in += len;
+        p_out += len;
+        var = dbd_untaint(pool, stmt->taint[i], args[i]);
+        len = strlen(var);
+        strncpy(p_out, var, len);
+        p_in += 2;
+        p_out += len;
+        ++i;
+    }
+    strcpy(p_out, p_in);
+    return ret;
+}
+static int dbd_freetds_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+                               apr_dbd_results_t **results,
+                               apr_dbd_prepared_t *statement,
+                               int seek, int nargs, const char **values)
+{
+    const char *query = dbd_statement(pool, statement, nargs, values);
+    return dbd_freetds_select(pool, sql, results, query, seek);
+}
+static int dbd_freetds_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+                                apr_dbd_results_t **results,
+                                apr_dbd_prepared_t *statement,
+                                int seek, va_list args)
+{
+    const char **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+    for (i = 0; i < statement->nargs; i++) {
+        values[i] = va_arg(args, const char*);
+    }
+
+    return dbd_freetds_pselect(pool, sql, results, statement, seek,
+                               statement->nargs, values);
+}
+static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query);
+static int dbd_freetds_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+                              int *nrows, apr_dbd_prepared_t *statement,
+                              int nargs, const char **values)
+{
+    const char *query = dbd_statement(pool, statement, nargs, values);
+    return dbd_freetds_query(sql, nrows, query);
+}
+static int dbd_freetds_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+                               apr_dbd_prepared_t *statement, va_list args)
+{
+    const char **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+    for (i = 0; i < statement->nargs; i++) {
+        values[i] = va_arg(args, const char*);
+    }
+    return dbd_freetds_pquery(pool, sql, nrows, statement,
+                              statement->nargs, values);
+}
+
+static int dbd_freetds_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+                               apr_dbd_row_t **rowp, int rownum)
+{
+    RETCODE rv = 0;
+    apr_dbd_row_t *row = *rowp;
+    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
+
+    if (row == NULL) {
+        row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+        *rowp = row;
+        row->res = res;
+    }
+    /*
+    else {
+        if ( sequential ) {
+            ++row->n;
+        }
+        else {
+            row->n = rownum;
+        }
+    }
+    */
+    if (sequential) {
+        rv = dbnextrow(res->proc);
+    }
+    else {
+        rv = (rownum >= 0) ? dbgetrow(res->proc, rownum) : NO_MORE_ROWS;
+    }
+    switch (rv) {
+    case SUCCEED: return 0;
+    case REG_ROW: return 0;
+    case NO_MORE_ROWS:
+        apr_pool_cleanup_run(pool, res->proc, clear_result);
+        *rowp = NULL;
+        return -1;
+    case FAIL: return 1;
+    case BUF_FULL: return 2; /* FIXME */
+    default: return 3;
+    }
+
+    return 0;
+}
+
+static const char *dbd_freetds_get_entry(const apr_dbd_row_t *row, int n)
+{
+    /* FIXME: support different data types */
+    /* this fails - bind gets some vars but not others
+    return (const char*)row->res->vars[n].data;
+     */
+    DBPROCESS* proc = row->res->proc;
+    BYTE *ptr = dbdata(proc, n+1);
+    int t = dbcoltype(proc, n+1);
+    int l = dbcollen(proc, n+1);
+    if (dbwillconvert(t, SYBCHAR)) {
+      dbconvert(proc, t, ptr, l, SYBCHAR, row->buf, -1);
+      return (const char*)row->buf;
+    }
+    return ptr;
+}
+
+static const char *dbd_freetds_error(apr_dbd_t *sql, int n)
+{
+    /* XXX this doesn't seem to exist in the API ??? */
+    return apr_psprintf(sql->pool, "Error %d", sql->err);
+}
+
+static int dbd_freetds_query(apr_dbd_t *sql, int *nrows, const char *query)
+{
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+    *nrows = 0;
+    sql->err = freetds_exec(sql->proc, query, 0, nrows);
+
+    if (sql->err != SUCCEED) {
+        if (sql->trans) {
+            sql->trans->errnum = sql->err;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+static const char *dbd_freetds_escape(apr_pool_t *pool, const char *arg,
+                                      apr_dbd_t *sql)
+{
+    return arg;
+}
+
+static apr_status_t freetds_regfree(void *rx)
+{
+    regfree((regex_t*)rx);
+    return APR_SUCCESS;
+}
+static int recurse_args(apr_pool_t *pool, int n, const char *query,
+                        apr_dbd_prepared_t *stmt, int offs)
+{
+
+    /* we only support %s arguments for now */
+    int ret;
+    char arg[256];
+    regmatch_t matches[3];
+    if (regexec(&dbd_freetds_find_arg, query, 3, matches, 0) != 0) {
+        /* No more args */
+        stmt->nargs = n;
+        stmt->taint = apr_palloc(pool, n*sizeof(regex_t*));
+        stmt->sz = apr_palloc(pool, n*sizeof(int));
+        ret = 0;
+    }
+    else {
+        int i;
+        int sz = 0;
+        int len = matches[1].rm_eo - matches[1].rm_so - 2;
+        if (len > 255) {
+            return 9999;
+        }
+
+        ret = recurse_args(pool, n+1, query+matches[0].rm_eo,
+                           stmt, offs+matches[0].rm_eo);
+
+        memmove(stmt->fmt + offs + matches[1].rm_so,
+                stmt->fmt + offs + matches[0].rm_eo-1,
+                strlen(stmt->fmt+offs+matches[0].rm_eo)+2);
+
+        /* compile untaint to a regex if found */
+        if (matches[1].rm_so == -1) {
+            stmt->taint[n] = NULL;
+        }
+        else {
+            strncpy(arg, query+matches[1].rm_so+1,
+                    matches[1].rm_eo - matches[1].rm_so - 2);
+            arg[matches[1].rm_eo - matches[1].rm_so - 2] = '\0';
+            stmt->taint[n] = apr_palloc(pool, sizeof(regex_t));
+            if (regcomp(stmt->taint[n], arg, REG_ICASE|REG_EXTENDED) != 0) {
+                ++ret;
+            }
+            else {
+                apr_pool_cleanup_register(pool, stmt->taint[n], freetds_regfree,
+                                          apr_pool_cleanup_null);
+            }
+        }
+
+        /* record length if specified */
+        for (i=matches[2].rm_so; i<matches[2].rm_eo; ++i) {
+            sz = 10*sz + (query[i]-'\0');
+        }
+    }
+    return ret;
+}
+
+static int dbd_freetds_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+                             const char *query, const char *label,
+                             apr_dbd_prepared_t **statement)
+{
+    apr_dbd_prepared_t *stmt;
+
+    if (label == NULL) {
+        label = apr_psprintf(pool, "%d", labelnum++);
+    }
+
+    if (!*statement) {
+        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
+    }
+    stmt = *statement;
+
+#if 0
+    /* count args */
+    stmt->fmt = apr_pstrdup(pool, query);
+    stmt->fmt = recurse_args(pool, 0, query, stmt, stmt->fmt);
+
+    /* overestimate by a byte or two to simplify */
+    len = strlen("CREATE PROC apr.")
+            + strlen(label)
+            + stmt->nargs * strlen(" @arg1 varchar(len1),")
+            + strlen(" AS begin ")
+            + strlen(stmt->fmt)
+            + strlen(" end "); /* extra byte for terminator */
+
+    pquery = apr_pcalloc(pool, len);
+    sprintf(pquery, "CREATE PROC apr.%s", label);
+    for (i=0; i<stmt->nargs; ++i) {
+        sprintf(pquery+strlen(pquery), " @arg%d varchar(%d)", i, stmt->sz[i]);
+        if (i < stmt->nargs-1) {
+            pquery[strlen(pquery)] = ',';
+        }
+    }
+    strcat(pquery, " AS BEGIN ");
+    strcat(pquery, stmt->fmt);
+    strcat(pquery, " END");
+
+    return (freetds_exec(sql->proc, pquery, 0, &i) == SUCCEED) ? 0 : 1;
+#else
+    stmt->fmt = apr_pstrdup(pool, query);
+    return recurse_args(pool, 0, query, stmt, 0);
+#endif
+
+}
+
+static int dbd_freetds_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
+                                         apr_dbd_transaction_t **trans)
+{
+    int dummy;
+
+    /* XXX handle recursive transactions here */
+
+    handle->err = freetds_exec(handle->proc, "BEGIN TRANSACTION", 0, &dummy);
+
+    if (dbd_freetds_is_success(handle->err)) {
+        if (!*trans) {
+            *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+        }
+        (*trans)->handle = handle;
+        handle->trans = *trans;
+        return 0;
+    }
+
+    return 1;
+}
+
+static int dbd_freetds_end_transaction(apr_dbd_transaction_t *trans)
+{
+    int dummy;
+    if (trans) {
+        /* rollback on error or explicit rollback request */
+        if (trans->errnum) {
+            trans->errnum = 0;
+            trans->handle->err = freetds_exec(trans->handle->proc,
+                                              "ROLLBACK", 0, &dummy);
+        }
+        else {
+            trans->handle->err = freetds_exec(trans->handle->proc,
+                                              "COMMIT", 0, &dummy);
+        }
+        trans->handle->trans = NULL;
+    }
+    return (trans->handle->err == SUCCEED) ? 0 : 1;
+}
+
+static DBPROCESS *freetds_open(apr_pool_t *pool, const char *params)
+{
+    char *server = NULL;
+    DBPROCESS *process;
+    LOGINREC *login;
+    static const char *delims = " \r\n\t;|,";
+    char *ptr;
+    char *key;
+    char *value;
+    int vlen;
+    int klen;
+    char *buf;
+    char *databaseName = NULL;
+
+    /* FIXME - this uses malloc */
+    login = dblogin();
+    if (login == NULL) {
+        return NULL;
+    }
+    /* now set login properties */
+    for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
+        for (key = ptr-1; isspace(*key); --key);
+        klen = 0;
+        while (isalpha(*key)) {
+            --key;
+            ++klen;
+        }
+        ++key;
+        for (value = ptr+1; isspace(*value); ++value);
+
+        vlen = strcspn(value, delims);
+        buf = apr_pstrndup(pool, value, vlen);        /* NULL-terminated copy */
+
+        if (!strncasecmp(key, "username", klen)) {
+            DBSETLUSER(login, buf);
+        }
+        else if (!strncasecmp(key, "password", klen)) {
+            DBSETLPWD(login, buf);
+        }
+        else if (!strncasecmp(key, "appname", klen)) {
+            DBSETLAPP(login, buf);
+        }
+        else if (!strncasecmp(key, "dbname", klen)) {
+            databaseName = buf;
+        }
+        else if (!strncasecmp(key, "host", klen)) {
+            DBSETLHOST(login, buf);
+        }
+        else if (!strncasecmp(key, "charset", klen)) {
+            DBSETLCHARSET(login, buf);
+        }
+        else if (!strncasecmp(key, "lang", klen)) {
+            DBSETLNATLANG(login, buf);
+        }
+        else if (!strncasecmp(key, "server", klen)) {
+            server = buf;
+        }
+        else {
+            /* unknown param */
+        }
+        ptr = value+vlen;
+    }
+
+    process = dbopen(login, server);
+
+    fprintf(stderr, "databaseName [%s]\n", databaseName);
+
+    if (databaseName != NULL)
+    {
+        dbuse(process, databaseName);
+    }
+ 
+    dbloginfree(login);
+    if (process == NULL) {
+        return NULL;
+    }
+
+    return process;
+}
+static apr_dbd_t *dbd_freetds_open(apr_pool_t *pool, const char *params)
+{
+    apr_dbd_t *sql;
+    DBPROCESS *process = freetds_open(pool, params);
+    if (process == NULL) {
+        return NULL;
+    }
+    sql = apr_palloc (pool, sizeof (apr_dbd_t));
+    sql->pool = pool;
+    sql->proc = process;
+    sql->params = params;
+    return sql;
+}
+
+static apr_status_t dbd_freetds_close(apr_dbd_t *handle)
+{
+    dbclose(handle->proc);
+    return APR_SUCCESS;
+}
+
+static apr_status_t dbd_freetds_check_conn(apr_pool_t *pool,
+                                           apr_dbd_t *handle)
+{
+    if (dbdead(handle->proc)) {
+        /* try again */
+        dbclose(handle->proc);
+        handle->proc = freetds_open(handle->pool, handle->params);
+        if (!handle->proc || dbdead(handle->proc)) {
+            return APR_EGENERAL;
+        }
+    }
+    /* clear it, in case this is called in error handling */
+    dbcancel(handle->proc);
+    return APR_SUCCESS;
+}
+
+static int dbd_freetds_select_db(apr_pool_t *pool, apr_dbd_t *handle,
+                               const char *name)
+{
+    /* ouch, it's declared int.  But we can use APR 0/nonzero */
+    return (dbuse(handle->proc, (char*)name) == SUCCEED) ? APR_SUCCESS : APR_EGENERAL;
+}
+
+static void *dbd_freetds_native(apr_dbd_t *handle)
+{
+    return handle->proc;
+}
+
+static int dbd_freetds_num_cols(apr_dbd_results_t* res)
+{
+    return res->sz;
+}
+
+static int dbd_freetds_num_tuples(apr_dbd_results_t* res)
+{
+    if (res->random) {
+        return res->ntuples;
+    }
+    else {
+        return -1;
+    }
+}
+
+static apr_status_t freetds_term(void *dummy)
+{
+    dbexit();
+    regfree(&dbd_freetds_find_arg);
+    return APR_SUCCESS;
+}
+static void dbd_freetds_init(apr_pool_t *pool)
+{
+    int rv = regcomp(&dbd_freetds_find_arg,
+                     "%(\\{[^}]*\\})?([0-9]*)[A-Za-z]", REG_EXTENDED);
+    if (rv != 0) {
+        char errmsg[256];
+        regerror(rv, &dbd_freetds_find_arg, errmsg, 256);
+        fprintf(stderr, "regcomp failed: %s\n", errmsg);
+    }
+    dbinit();
+    apr_pool_cleanup_register(pool, NULL, freetds_term, apr_pool_cleanup_null);
+}
+
+#ifdef COMPILE_STUBS
+/* get_name is the only one of these that is implemented */
+static const char *dbd_freetds_get_name(const apr_dbd_results_t *res, int n)
+{
+    return (const char*) dbcolname(res->proc, n+1); /* numbering starts at 1 */
+}
+
+/* These are stubs: transaction modes not implemented here */
+#define DBD_NOTIMPL APR_ENOTIMPL;
+static int dbd_freetds_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+    return trans ? trans->mode : APR_DBD_TRANSACTION_COMMIT;
+}
+
+static int dbd_freetds_transaction_mode_set(apr_dbd_transaction_t *trans,
+                                            int mode)
+{
+    if (trans) {
+        trans->mode = mode & TXN_MODE_BITS;
+        return trans->mode;
+    }
+    return APR_DBD_TRANSACTION_COMMIT;
+}
+static int dbd_freetds_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+                                apr_dbd_prepared_t *statement, va_list args)
+{
+    return DBD_NOTIMPL;
+}
+static int dbd_freetds_pbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+                               apr_dbd_prepared_t * statement,
+                               const void **values)
+{
+    return DBD_NOTIMPL;
+}
+
+static int dbd_freetds_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
+                                 apr_dbd_results_t **results,
+                                 apr_dbd_prepared_t *statement,
+                                 int seek, va_list args)
+{
+    return DBD_NOTIMPL;
+}
+static int dbd_freetds_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
+                                apr_dbd_results_t **results,
+                                apr_dbd_prepared_t *statement,
+                                int seek, const void **values)
+{
+    return DBD_NOTIMPL;
+}
+static apr_status_t dbd_freetds_datum_get(const apr_dbd_row_t *row, int n,
+                                          apr_dbd_type_e type, void *data)
+{
+    return APR_ENOTIMPL;
+}
+#endif
+
+APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_freetds_driver = {
+    "freetds",
+    dbd_freetds_init,
+    dbd_freetds_native,
+    dbd_freetds_open,
+    dbd_freetds_check_conn,
+    dbd_freetds_close,
+    dbd_freetds_select_db,
+    dbd_freetds_start_transaction,
+    dbd_freetds_end_transaction,
+    dbd_freetds_query,
+    dbd_freetds_select,
+    dbd_freetds_num_cols,
+    dbd_freetds_num_tuples,
+    dbd_freetds_get_row,
+    dbd_freetds_get_entry,
+    dbd_freetds_error,
+    dbd_freetds_escape,
+    dbd_freetds_prepare,
+    dbd_freetds_pvquery,
+    dbd_freetds_pvselect,
+    dbd_freetds_pquery,
+    dbd_freetds_pselect,
+    /* this is only implemented to support httpd/2.2 standard usage,
+     * as in the original DBD implementation.  Everything else is NOTIMPL.
+     */
+#ifdef COMPILE_STUBS
+    dbd_freetds_get_name,
+    dbd_freetds_transaction_mode_get,
+    dbd_freetds_transaction_mode_set,
+    "",
+    dbd_freetds_pvbquery,
+    dbd_freetds_pvbselect,
+    dbd_freetds_pbquery,
+    dbd_freetds_pbselect,
+    dbd_freetds_datum_get
+#endif
+};
+#endif

Added: apr/apr-util/trunk/dbd/apr_dbd_mysql.c
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/dbd/apr_dbd_mysql.c?rev=572642&view=auto
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd_mysql.c (added)
+++ apr/apr-util/trunk/dbd/apr_dbd_mysql.c Tue Sep  4 05:05:35 2007
@@ -0,0 +1,1594 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apu.h"
+#define HAVE_MYSQL_MYSQL_H
+
+#if APU_HAVE_MYSQL
+
+#include "apu_version.h"
+#include "apu_config.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#ifdef HAVE_MYSQL_H
+#include <mysql.h>
+#include <errmsg.h>
+#elif defined(HAVE_MYSQL_MYSQL_H)
+#include <mysql/mysql.h>
+#include <mysql/errmsg.h>
+#endif
+
+#include "apr_strings.h"
+#include "apr_buckets.h"
+
+#include "apr_dbd_internal.h"
+
+/* default maximum field size 1 MB */
+#define FIELDSIZE 1048575
+
+struct apr_dbd_prepared_t {
+    MYSQL_STMT* stmt;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+    int nargs;
+    int nvals;
+    apr_dbd_type_e *types;
+#endif
+};
+
+struct apr_dbd_transaction_t {
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+    int mode;
+#endif
+    int errnum;
+    apr_dbd_t *handle;
+};
+
+struct apr_dbd_t {
+    MYSQL* conn ;
+    apr_dbd_transaction_t* trans ;
+    unsigned long fldsz;
+};
+
+struct apr_dbd_results_t {
+    int random;
+    MYSQL_RES *res;
+    MYSQL_STMT *statement;
+    MYSQL_BIND *bind;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+    apr_pool_t *pool;
+#endif
+};
+struct apr_dbd_row_t {
+    MYSQL_ROW row;
+    apr_dbd_results_t *res;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+    unsigned long *len;
+#endif
+};
+
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+/* MySQL specific bucket for BLOB types */
+typedef struct apr_bucket_lob apr_bucket_lob;
+/**
+ * A bucket referring to a MySQL BLOB
+ */
+struct apr_bucket_lob {
+    /** Number of buckets using this memory */
+    apr_bucket_refcount  refcount;
+    /** The row this bucket refers to */
+    const apr_dbd_row_t *row;
+    /** The column this bucket refers to */
+    int col;
+    /** The pool into which any needed structures should
+     *  be created while reading from this bucket */
+    apr_pool_t *readpool;
+};
+
+static void lob_bucket_destroy(void *data);
+static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
+                                    apr_size_t *len, apr_read_type_e block);
+static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
+                                       const apr_dbd_row_t *row, int col,
+                                       apr_off_t offset, apr_size_t len,
+                                       apr_pool_t *p);
+static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
+                                         apr_off_t offset,
+                                         apr_size_t len, apr_pool_t *p,
+                                         apr_bucket_alloc_t *list);
+
+static const apr_bucket_type_t apr_bucket_type_lob = {
+    "LOB", 5, APR_BUCKET_DATA,
+    lob_bucket_destroy,
+    lob_bucket_read,
+    apr_bucket_setaside_notimpl,
+    apr_bucket_shared_split,
+    apr_bucket_shared_copy
+};
+
+static void lob_bucket_destroy(void *data)
+{
+    apr_bucket_lob *f = data;
+
+    if (apr_bucket_shared_destroy(f)) {
+        /* no need to destroy database objects here; it will get
+         * done automatically when the pool gets cleaned up */
+        apr_bucket_free(f);
+    }
+}
+
+static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
+                                    apr_size_t *len, apr_read_type_e block)
+{
+    apr_bucket_lob *a = e->data;
+    const apr_dbd_row_t *row = a->row;
+    apr_dbd_results_t *res = row->res;
+    int col = a->col;
+    apr_bucket *b = NULL;
+    int rv;
+    apr_size_t blength = e->length;  /* bytes remaining in file past offset */
+    apr_off_t boffset = e->start;
+    MYSQL_BIND *bind = &res->bind[col];
+
+    *str = NULL;  /* in case we die prematurely */
+
+    /* fetch from offset if not at the beginning */
+    if (boffset > 0) {
+        rv = mysql_stmt_fetch_column(res->statement, bind, col, boffset);
+        if (rv != 0) {
+            return APR_EGENERAL;
+        }
+    }
+    blength -= blength > bind->buffer_length ? bind->buffer_length : blength;
+    *len = e->length - blength;
+    *str = bind->buffer;
+
+    /* allocate new buffer, since we used this one for the bucket */
+    bind->buffer = apr_palloc(res->pool, bind->buffer_length);
+
+    /*
+     * Change the current bucket to refer to what we read,
+     * even if we read nothing because we hit EOF.
+     */
+    apr_bucket_pool_make(e, *str, *len, res->pool);
+
+    /* If we have more to read from the field, then create another bucket */
+    if (blength > 0) {
+        /* for efficiency, we can just build a new apr_bucket struct
+         * to wrap around the existing LOB bucket */
+        b = apr_bucket_alloc(sizeof(*b), e->list);
+        b->start  = boffset + *len;
+        b->length = blength;
+        b->data   = a;
+        b->type   = &apr_bucket_type_lob;
+        b->free   = apr_bucket_free;
+        b->list   = e->list;
+        APR_BUCKET_INSERT_AFTER(e, b);
+    }
+    else {
+        lob_bucket_destroy(a);
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
+                                       const apr_dbd_row_t *row, int col,
+                                       apr_off_t offset, apr_size_t len,
+                                       apr_pool_t *p)
+{
+    apr_bucket_lob *f;
+
+    f = apr_bucket_alloc(sizeof(*f), b->list);
+    f->row = row;
+    f->col = col;
+    f->readpool = p;
+
+    b = apr_bucket_shared_make(b, f, offset, len);
+    b->type = &apr_bucket_type_lob;
+
+    return b;
+}
+
+static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
+                                         apr_off_t offset,
+                                         apr_size_t len, apr_pool_t *p,
+                                         apr_bucket_alloc_t *list)
+{
+    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+    APR_BUCKET_INIT(b);
+    b->free = apr_bucket_free;
+    b->list = list;
+    return apr_bucket_lob_make(b, row, col, offset, len, p);
+}
+
+#endif
+
+static apr_status_t free_result(void *data)
+{
+    mysql_free_result(data);
+    return APR_SUCCESS;
+}
+
+static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql,
+                            apr_dbd_results_t **results,
+                            const char *query, int seek)
+{
+    int sz;
+    int ret;
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+    ret = mysql_query(sql->conn, query);
+    if (!ret) {
+        if (sz = mysql_field_count(sql->conn), sz > 0) {
+            if (!*results) {
+                *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
+            }
+            (*results)->random = seek;
+            (*results)->statement = NULL;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+            (*results)->pool = pool;
+#endif
+            if (seek) {
+                (*results)->res = mysql_store_result(sql->conn);
+            }
+            else {
+                (*results)->res = mysql_use_result(sql->conn);
+            }
+            apr_pool_cleanup_register(pool, (*results)->res,
+                                      free_result,apr_pool_cleanup_null);
+        }
+    } else {
+        ret = mysql_errno(sql->conn);
+    }
+    
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+    if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
+    if (sql->trans) {
+#endif
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n)
+{
+    if ((n < 0) || (n >= mysql_num_fields(res->res))) {
+        return NULL;
+    }
+
+    return mysql_fetch_fields(res->res)[n].name;
+}
+#endif
+
+static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+                             apr_dbd_row_t **row, int rownum)
+{
+    MYSQL_ROW r = NULL;
+    int ret = 0;
+
+    if (res->statement) {
+        if (res->random) {
+            if (rownum >= 0) {
+                mysql_stmt_data_seek(res->statement, (my_ulonglong)rownum);
+            }
+        }
+        ret = mysql_stmt_fetch(res->statement);
+        switch (ret) {
+        case 1:
+            ret = mysql_stmt_errno(res->statement);
+            break;
+        case MYSQL_NO_DATA:
+            ret = -1;
+            break;
+        default:
+            ret = 0; /* bad luck - get_entry will deal with this */
+            break;
+        }
+    }
+    else {
+        if (res->random) {
+            if (rownum >= 0) {
+                mysql_data_seek(res->res, (my_ulonglong) rownum);
+            }
+        }
+        r = mysql_fetch_row(res->res);
+        if (r == NULL) {
+            ret = -1;
+        }
+    }
+    if (ret == 0) {
+        if (!*row) {
+            *row = apr_palloc(pool, sizeof(apr_dbd_row_t));
+        }
+        (*row)->row = r;
+        (*row)->res = res;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+        (*row)->len = mysql_fetch_lengths(res->res);
+#endif
+    }
+    else {
+        apr_pool_cleanup_run(pool, res->res, free_result);
+    }
+    return ret;
+}
+#if 0
+/* An improved API that was proposed but not followed up */
+static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n,
+                               apr_dbd_datum_t *val)
+{
+    MYSQL_BIND *bind;
+    if (row->res->statement) {
+        bind = &row->res->bind[n];
+        if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
+            val->type = APR_DBD_VALUE_NULL;
+            return -1;
+        }
+        if (*bind->is_null) {
+            val->type = APR_DBD_VALUE_NULL;
+            return -1;
+        }
+        else {
+            val->type = APR_DBD_VALUE_STRING;
+            val->value.stringval = bind->buffer;
+        }
+    }
+    else {
+        val->type = APR_DBD_VALUE_STRING;
+        val->value.stringval = row->row[n];
+    }
+    return 0;
+}
+#else
+
+static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
+{
+    MYSQL_BIND *bind;
+    if (row->res->statement) {
+        bind = &row->res->bind[n];
+        if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
+            return NULL;
+        }
+        if (*bind->is_null) {
+            return NULL;
+        }
+        else {
+            return bind->buffer;
+        }
+    }
+    else {
+        return row->row[n];
+    }
+    return NULL;
+}
+#endif
+
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n,
+                                        apr_dbd_type_e type, void *data)
+{
+    if (row->res->statement) {
+        MYSQL_BIND *bind = &row->res->bind[n];
+        unsigned long len = *bind->length;
+
+        if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
+            return APR_EGENERAL;
+        }
+
+        if (*bind->is_null) {
+            return APR_ENOENT;
+        }
+
+        switch (type) {
+        case APR_DBD_TYPE_TINY:
+            *(char*)data = atoi(bind->buffer);
+            break;
+        case APR_DBD_TYPE_UTINY:
+            *(unsigned char*)data = atoi(bind->buffer);
+            break;
+        case APR_DBD_TYPE_SHORT:
+            *(short*)data = atoi(bind->buffer);
+            break;
+        case APR_DBD_TYPE_USHORT:
+            *(unsigned short*)data = atoi(bind->buffer);
+            break;
+        case APR_DBD_TYPE_INT:
+            *(int*)data = atoi(bind->buffer);
+            break;
+        case APR_DBD_TYPE_UINT:
+            *(unsigned int*)data = atoi(bind->buffer);
+            break;
+        case APR_DBD_TYPE_LONG:
+            *(long*)data = atol(bind->buffer);
+            break;
+        case APR_DBD_TYPE_ULONG:
+            *(unsigned long*)data = atol(bind->buffer);
+            break;
+        case APR_DBD_TYPE_LONGLONG:
+            *(apr_int64_t*)data = apr_atoi64(bind->buffer);
+            break;
+        case APR_DBD_TYPE_ULONGLONG:
+            *(apr_uint64_t*)data = apr_atoi64(bind->buffer);
+            break;
+        case APR_DBD_TYPE_FLOAT:
+            *(float*)data = atof(bind->buffer);
+            break;
+        case APR_DBD_TYPE_DOUBLE:
+            *(double*)data = atof(bind->buffer);
+            break;
+        case APR_DBD_TYPE_STRING:
+        case APR_DBD_TYPE_TEXT:
+        case APR_DBD_TYPE_TIME:
+        case APR_DBD_TYPE_DATE:
+        case APR_DBD_TYPE_DATETIME:
+        case APR_DBD_TYPE_TIMESTAMP:
+        case APR_DBD_TYPE_ZTIMESTAMP:
+            *((char*)bind->buffer+bind->buffer_length-1) = '\0';
+            *(char**)data = bind->buffer;
+            break;
+        case APR_DBD_TYPE_BLOB:
+        case APR_DBD_TYPE_CLOB:
+            {
+            apr_bucket *e;
+            apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+            e = apr_bucket_lob_create(row, n, 0, len,
+                                      row->res->pool, b->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(b, e);
+            }
+            break;
+        case APR_DBD_TYPE_NULL:
+            *(void**)data = NULL;
+            break;
+        default:
+            return APR_EGENERAL;
+        }
+    }
+    else {
+        if (row->row[n] == NULL) {
+            return APR_ENOENT;
+        }
+
+        switch (type) {
+        case APR_DBD_TYPE_TINY:
+            *(char*)data = atoi(row->row[n]);
+            break;
+        case APR_DBD_TYPE_UTINY:
+            *(unsigned char*)data = atoi(row->row[n]);
+            break;
+        case APR_DBD_TYPE_SHORT:
+            *(short*)data = atoi(row->row[n]);
+            break;
+        case APR_DBD_TYPE_USHORT:
+            *(unsigned short*)data = atoi(row->row[n]);
+            break;
+        case APR_DBD_TYPE_INT:
+            *(int*)data = atoi(row->row[n]);
+            break;
+        case APR_DBD_TYPE_UINT:
+            *(unsigned int*)data = atoi(row->row[n]);
+            break;
+        case APR_DBD_TYPE_LONG:
+            *(long*)data = atol(row->row[n]);
+            break;
+        case APR_DBD_TYPE_ULONG:
+            *(unsigned long*)data = atol(row->row[n]);
+            break;
+        case APR_DBD_TYPE_LONGLONG:
+            *(apr_int64_t*)data = apr_atoi64(row->row[n]);
+            break;
+        case APR_DBD_TYPE_ULONGLONG:
+            *(apr_uint64_t*)data = apr_atoi64(row->row[n]);
+            break;
+        case APR_DBD_TYPE_FLOAT:
+            *(float*)data = atof(row->row[n]);
+            break;
+        case APR_DBD_TYPE_DOUBLE:
+            *(double*)data = atof(row->row[n]);
+            break;
+        case APR_DBD_TYPE_STRING:
+        case APR_DBD_TYPE_TEXT:
+        case APR_DBD_TYPE_TIME:
+        case APR_DBD_TYPE_DATE:
+        case APR_DBD_TYPE_DATETIME:
+        case APR_DBD_TYPE_TIMESTAMP:
+        case APR_DBD_TYPE_ZTIMESTAMP:
+            *(char**)data = row->row[n];
+            break;
+        case APR_DBD_TYPE_BLOB:
+        case APR_DBD_TYPE_CLOB:
+            {
+            apr_bucket *e;
+            apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+            e = apr_bucket_pool_create(row->row[n], row->len[n],
+                                       row->res->pool, b->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(b, e);
+            }
+            break;
+        case APR_DBD_TYPE_NULL:
+            *(void**)data = NULL;
+            break;
+        default:
+            return APR_EGENERAL;
+        }
+    }
+    return 0;
+}
+#endif
+
+static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
+{
+    return mysql_error(sql->conn);
+}
+
+static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
+{
+    int ret;
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+    ret = mysql_query(sql->conn, query);
+    if (ret != 0) {
+        ret = mysql_errno(sql->conn);
+    }
+    *nrows = mysql_affected_rows(sql->conn);
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+    if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
+    if (sql->trans) {
+#endif
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
+                                    apr_dbd_t *sql)
+{
+    unsigned long len = strlen(arg);
+    char *ret = apr_palloc(pool, 2*len + 1);
+    mysql_real_escape_string(sql->conn, ret, arg, len);
+    return ret;
+}
+
+static apr_status_t stmt_close(void *data)
+{
+    mysql_stmt_close(data);
+    return APR_SUCCESS;
+}
+
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+                             const char *query, const char *label,
+                             int nargs, int nvals, apr_dbd_type_e *types,
+                             apr_dbd_prepared_t **statement)
+{
+    /* Translate from apr_dbd to native query format */
+    int ret;
+
+    if (!*statement) {
+        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
+    }
+    (*statement)->stmt = mysql_stmt_init(sql->conn);
+
+    if ((*statement)->stmt) {
+        apr_pool_cleanup_register(pool, (*statement)->stmt,
+                                  stmt_close, apr_pool_cleanup_null);
+        ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query));
+
+        if (ret != 0) {
+            ret = mysql_stmt_errno((*statement)->stmt);
+        }
+
+        (*statement)->nargs = nargs;
+        (*statement)->nvals = nvals;
+        (*statement)->types = types;
+
+        return ret;
+    }
+
+    return CR_OUT_OF_MEMORY;
+}
+
+static void dbd_mysql_bind(apr_dbd_prepared_t *statement,
+                           const char **values, MYSQL_BIND *bind)
+{
+    int i, j;
+
+    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+        bind[i].length = &bind[i].buffer_length;
+        bind[i].is_unsigned = 0;
+        bind[i].is_null = NULL;
+
+        if (values[j] == NULL) {
+            bind[i].buffer_type = MYSQL_TYPE_NULL;
+        }
+        else {
+            switch (statement->types[i]) {
+            case APR_DBD_TYPE_BLOB:
+            case APR_DBD_TYPE_CLOB:
+                bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
+                bind[i].buffer = (void*)values[j];
+                bind[i].buffer_length = atol(values[++j]);
+
+                /* skip table and column */
+                j += 2;
+                break;
+            default:
+                bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+                bind[i].buffer = (void*)values[j];
+                bind[i].buffer_length = strlen(values[j]);
+                break;
+            }
+        }
+    }
+
+    return;
+}
+
+static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
+                                     int *nrows, apr_dbd_prepared_t *statement,
+                                     MYSQL_BIND *bind)
+{
+    int ret;
+
+    ret = mysql_stmt_bind_param(statement->stmt, bind);
+    if (ret != 0) {
+        *nrows = 0;
+        ret = mysql_stmt_errno(statement->stmt);
+    }
+    else {
+        ret = mysql_stmt_execute(statement->stmt);
+        if (ret != 0) {
+            ret = mysql_stmt_errno(statement->stmt);
+        }
+        *nrows = mysql_stmt_affected_rows(statement->stmt);
+    }
+
+    return ret;
+}
+
+static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+                            int *nrows, apr_dbd_prepared_t *statement,
+                            const char **values)
+{
+    MYSQL_BIND *bind;
+    int ret;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
+
+    dbd_mysql_bind(statement, values, bind);
+
+    ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
+
+    if (TXN_NOTICE_ERRORS(sql->trans)) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+                             apr_dbd_prepared_t *statement, va_list args)
+{
+    const char **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+    for (i = 0; i < statement->nvals; i++) {
+        values[i] = va_arg(args, const char*);
+    }
+
+    return dbd_mysql_pquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
+                                      apr_dbd_results_t **res,
+                                      apr_dbd_prepared_t *statement,
+                                      int random, MYSQL_BIND *bind)
+{
+    int nfields, i;
+    my_bool *is_nullr;
+#if MYSQL_VERSION_ID >= 50000
+    my_bool *error;
+#endif
+    int ret;
+    unsigned long *length, maxlen;
+
+    ret = mysql_stmt_bind_param(statement->stmt, bind);
+    if (ret == 0) {
+        ret = mysql_stmt_execute(statement->stmt);
+        if (!ret) {
+            if (!*res) {
+                *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+            }
+            (*res)->random = random;
+            (*res)->statement = statement->stmt;
+            (*res)->res = mysql_stmt_result_metadata(statement->stmt);
+            (*res)->pool = pool;
+            apr_pool_cleanup_register(pool, (*res)->res,
+                                      free_result, apr_pool_cleanup_null);
+            nfields = mysql_num_fields((*res)->res);
+            if (!(*res)->bind) {
+                (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
+                length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
+#if MYSQL_VERSION_ID >= 50000
+                error = apr_palloc(pool, nfields*sizeof(my_bool));
+#endif
+                is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
+                for ( i = 0; i < nfields; ++i ) {
+                    maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
+                              (*res)->res->fields[i].length : sql->fldsz) + 1;
+                    if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) {
+                        (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
+                    }
+                    else {
+                        (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+                    }
+                    (*res)->bind[i].buffer_length = maxlen;
+                    (*res)->bind[i].length = &length[i];
+                    (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
+                    (*res)->bind[i].is_null = is_nullr+i;
+#if MYSQL_VERSION_ID >= 50000
+                    (*res)->bind[i].error = error+i;
+#endif
+                }
+            }
+            ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
+            if (!ret) {
+                ret = mysql_stmt_store_result(statement->stmt);
+            }
+        }
+    }
+    if (ret != 0) {
+        ret = mysql_stmt_errno(statement->stmt);
+    }
+
+    return ret;
+}
+
+static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+                             apr_dbd_results_t **res,
+                             apr_dbd_prepared_t *statement, int random,
+                             const char **args)
+{
+    int ret;
+    MYSQL_BIND *bind;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
+
+    dbd_mysql_bind(statement, args, bind);
+
+    ret = dbd_mysql_pselect_internal(pool, sql,  res, statement, random, bind);
+
+    if (TXN_NOTICE_ERRORS(sql->trans)) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+                              apr_dbd_results_t **res,
+                              apr_dbd_prepared_t *statement, int random,
+                              va_list args)
+{
+    const char **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+    for (i = 0; i < statement->nvals; i++) {
+        values[i] = va_arg(args, const char*);
+    }
+
+    return dbd_mysql_pselect(pool, sql, res, statement, random, values);
+}
+
+static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement,
+                            const void **values, MYSQL_BIND *bind)
+{
+    void *arg;
+    int i, j;
+    apr_dbd_type_e type;
+
+    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
+        arg = (void *)values[j];
+
+        bind[i].length = &bind[i].buffer_length;
+        bind[i].is_null = NULL;
+
+        type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
+        switch (type) {
+        case APR_DBD_TYPE_TINY:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_TINY;
+            bind[i].is_unsigned = 0;
+            break;
+        case APR_DBD_TYPE_UTINY:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_TINY;
+            bind[i].is_unsigned = 1;
+            break;
+        case APR_DBD_TYPE_SHORT:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_SHORT;
+            bind[i].is_unsigned = 0;
+            break;
+        case APR_DBD_TYPE_USHORT:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_SHORT;
+            bind[i].is_unsigned = 1;
+            break;
+        case APR_DBD_TYPE_INT:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_LONG;
+            bind[i].is_unsigned = 0;
+            break;
+        case APR_DBD_TYPE_UINT:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_LONG;
+            bind[i].is_unsigned = 1;
+            break;
+        case APR_DBD_TYPE_LONG:
+            if (sizeof(int) == sizeof(long)) {
+                bind[i].buffer = arg;
+            }
+            else {
+                bind[i].buffer = apr_palloc(pool, sizeof(int));
+                *(int*)bind[i].buffer = *(long*)arg;
+            }
+            bind[i].buffer_type = MYSQL_TYPE_LONG;
+            bind[i].is_unsigned = 0;
+            break;
+        case APR_DBD_TYPE_ULONG:
+            if (sizeof(unsigned int) == sizeof(unsigned long)) {
+                bind[i].buffer = arg;
+            }
+            else {
+                bind[i].buffer = apr_palloc(pool, sizeof(unsigned int));
+                *(unsigned int*)bind[i].buffer = *(unsigned long*)arg;
+            }
+            bind[i].buffer_type = MYSQL_TYPE_LONG;
+            bind[i].is_unsigned = 1;
+            break;
+        case APR_DBD_TYPE_LONGLONG:
+            if (sizeof(long long) == sizeof(apr_int64_t)) {
+                bind[i].buffer = arg;
+            }
+            else {
+                bind[i].buffer = apr_palloc(pool, sizeof(long long));
+                *(long long*)bind[i].buffer = *(apr_int64_t*)arg;
+            }
+            bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
+            bind[i].is_unsigned = 0;
+            break;
+        case APR_DBD_TYPE_ULONGLONG:
+            if (sizeof(unsigned long long) == sizeof(apr_uint64_t)) {
+                bind[i].buffer = arg;
+            }
+            else {
+                bind[i].buffer = apr_palloc(pool, sizeof(unsigned long long));
+                *(unsigned long long*)bind[i].buffer = *(apr_uint64_t*)arg;
+            }
+            bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
+            bind[i].is_unsigned = 1;
+            break;
+        case APR_DBD_TYPE_FLOAT:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_FLOAT;
+            bind[i].is_unsigned = 0;
+            break;
+        case APR_DBD_TYPE_DOUBLE:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
+            bind[i].is_unsigned = 0;
+            break;
+        case APR_DBD_TYPE_STRING:
+        case APR_DBD_TYPE_TEXT:
+        case APR_DBD_TYPE_TIME:
+        case APR_DBD_TYPE_DATE:
+        case APR_DBD_TYPE_DATETIME:
+        case APR_DBD_TYPE_TIMESTAMP:
+        case APR_DBD_TYPE_ZTIMESTAMP:
+            bind[i].buffer = arg;
+            bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+            bind[i].is_unsigned = 0;
+            bind[i].buffer_length = strlen((const char *)arg);
+            break;
+        case APR_DBD_TYPE_BLOB:
+        case APR_DBD_TYPE_CLOB:
+            bind[i].buffer = (void *)arg;
+            bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
+            bind[i].is_unsigned = 0;
+            bind[i].buffer_length = *(apr_size_t*)values[++j];
+
+            /* skip table and column */
+            j += 2;
+            break;
+        case APR_DBD_TYPE_NULL:
+        default:
+            bind[i].buffer_type = MYSQL_TYPE_NULL;
+            break;
+        }
+    }
+
+    return;
+}
+
+static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql,
+                             int *nrows, apr_dbd_prepared_t *statement,
+                             const void **values)
+{
+    MYSQL_BIND *bind;
+    int ret;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
+
+    dbd_mysql_bbind(pool, statement, values, bind);
+
+    ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
+
+    if (TXN_NOTICE_ERRORS(sql->trans)) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+                              apr_dbd_prepared_t *statement, va_list args)
+{
+    const void **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+    for (i = 0; i < statement->nvals; i++) {
+        values[i] = va_arg(args, const void*);
+    }
+
+    return dbd_mysql_pbquery(pool, sql, nrows, statement, values);
+}
+
+static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
+                              apr_dbd_results_t **res,
+                              apr_dbd_prepared_t *statement, int random,
+                              const void **args)
+{
+    int ret;
+    MYSQL_BIND *bind;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
+
+    dbd_mysql_bbind(pool, statement, args, bind);
+
+    ret = dbd_mysql_pselect_internal(pool, sql,  res, statement, random, bind);
+
+    if (TXN_NOTICE_ERRORS(sql->trans)) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
+                               apr_dbd_results_t **res,
+                               apr_dbd_prepared_t *statement, int random,
+                               va_list args)
+{
+    const void **values;
+    int i;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
+
+    for (i = 0; i < statement->nvals; i++) {
+        values[i] = va_arg(args, const void*);
+    }
+
+    return dbd_mysql_pbselect(pool, sql, res, statement, random, values);
+}
+#else
+static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+                             const char *query, const char *label,
+                             apr_dbd_prepared_t **statement)
+{
+    /* Translate from apr_dbd to native query format */
+    char *myquery = apr_pstrdup(pool, query);
+    char *p = myquery;
+    const char *q;
+    int ret;
+    for (q = query; *q; ++q) {
+        if (q[0] == '%') {
+            if (isalpha(q[1])) {
+                *p++ = '?';
+                ++q;
+            }
+            else if (q[1] == '%') {
+                /* reduce %% to % */
+                *p++ = *q++;
+            }
+            else {
+                *p++ = *q;
+            }
+        }
+        else {
+            *p++ = *q;
+        }
+    } 
+    *p = 0;
+    if (!*statement) {
+        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
+    }
+    (*statement)->stmt = mysql_stmt_init(sql->conn);
+
+    if ((*statement)->stmt) {
+        apr_pool_cleanup_register(pool, (*statement)->stmt,
+                                  stmt_close, apr_pool_cleanup_null);
+        ret = mysql_stmt_prepare((*statement)->stmt, myquery, strlen(myquery));
+
+        if (ret != 0) {
+            ret = mysql_stmt_errno((*statement)->stmt);
+        }
+
+        return ret;
+    }
+
+    return CR_OUT_OF_MEMORY;
+}
+
+static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+                            int *nrows, apr_dbd_prepared_t *statement,
+                            int nargs, const char **values)
+{
+    MYSQL_BIND *bind;
+    char *arg;
+    int ret;
+    int i;
+    my_bool is_null = FALSE;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+    nargs = mysql_stmt_param_count(statement->stmt);
+
+    bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+    for (i=0; i < nargs; ++i) {
+        arg = (char*)values[i];
+        bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+        bind[i].buffer = arg;
+        bind[i].buffer_length = strlen(arg);
+        bind[i].length = &bind[i].buffer_length;
+        bind[i].is_null = &is_null;
+        bind[i].is_unsigned = 0;
+    }
+
+    ret = mysql_stmt_bind_param(statement->stmt, bind);
+    if (ret != 0) {
+        *nrows = 0;
+        ret = mysql_stmt_errno(statement->stmt);
+    }
+    else {
+        ret = mysql_stmt_execute(statement->stmt);
+        if (ret != 0) {
+            ret = mysql_stmt_errno(statement->stmt);
+        }
+        *nrows = mysql_stmt_affected_rows(statement->stmt);
+    }
+    if (sql->trans) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+                             apr_dbd_prepared_t *statement, va_list args)
+{
+    MYSQL_BIND *bind;
+    char *arg;
+    int ret;
+    int nargs = 0;
+    int i;
+    my_bool is_null = FALSE;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+    nargs = mysql_stmt_param_count(statement->stmt);
+
+    bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+    for (i=0; i < nargs; ++i) {
+        arg = va_arg(args, char*);
+        bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+        bind[i].buffer = arg;
+        bind[i].buffer_length = strlen(arg);
+        bind[i].length = &bind[i].buffer_length;
+        bind[i].is_null = &is_null;
+        bind[i].is_unsigned = 0;
+    }
+
+    ret = mysql_stmt_bind_param(statement->stmt, bind);
+    if (ret != 0) {
+        *nrows = 0;
+        ret = mysql_stmt_errno(statement->stmt);
+    }
+    else {
+        ret = mysql_stmt_execute(statement->stmt);
+        if (ret != 0) {
+            ret = mysql_stmt_errno(statement->stmt);
+        }
+        *nrows = mysql_stmt_affected_rows(statement->stmt);
+    }
+    if (sql->trans) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+                             apr_dbd_results_t **res,
+                             apr_dbd_prepared_t *statement, int random,
+                             int nargs, const char **args)
+{
+    int i;
+    int nfields;
+    char *arg;
+    my_bool is_null = FALSE;
+    my_bool *is_nullr;
+#if MYSQL_VERSION_ID >= 50000
+    my_bool *error;
+#endif
+    int ret;
+    unsigned long *length, maxlen;
+    MYSQL_BIND *bind;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    nargs = mysql_stmt_param_count(statement->stmt);
+    bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+
+    for (i=0; i < nargs; ++i) {
+        arg = (char*)args[i];
+        bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+        bind[i].buffer = arg;
+        bind[i].buffer_length = strlen(arg);
+        bind[i].length = &bind[i].buffer_length;
+        bind[i].is_null = &is_null;
+        bind[i].is_unsigned = 0;
+    }
+
+    ret = mysql_stmt_bind_param(statement->stmt, bind);
+    if (ret == 0) {
+        ret = mysql_stmt_execute(statement->stmt);
+        if (!ret) {
+            if (!*res) {
+                *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+            }
+            (*res)->random = random;
+            (*res)->statement = statement->stmt;
+            (*res)->res = mysql_stmt_result_metadata(statement->stmt);
+            apr_pool_cleanup_register(pool, (*res)->res,
+                                      free_result, apr_pool_cleanup_null);
+            nfields = mysql_num_fields((*res)->res);
+            if (!(*res)->bind) {
+                (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
+                length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
+#if MYSQL_VERSION_ID >= 50000
+                error = apr_palloc(pool, nfields*sizeof(my_bool));
+#endif
+                is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
+                for ( i = 0; i < nfields; ++i ) {
+                    maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
+                              (*res)->res->fields[i].length : sql->fldsz) + 1;
+                    (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+                    (*res)->bind[i].buffer_length = maxlen;
+                    (*res)->bind[i].length = &length[i];
+                    (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
+                    (*res)->bind[i].is_null = is_nullr+i;
+#if MYSQL_VERSION_ID >= 50000
+                    (*res)->bind[i].error = error+i;
+#endif
+                }
+            }
+            ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
+            if (!ret) {
+                ret = mysql_stmt_store_result(statement->stmt);
+            }
+        }
+    }
+    if (ret != 0) {
+        ret = mysql_stmt_errno(statement->stmt);
+    }
+    if (sql->trans) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+
+static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+                              apr_dbd_results_t **res,
+                              apr_dbd_prepared_t *statement, int random,
+                              va_list args)
+{
+    int i;
+    int nfields;
+    char *arg;
+    my_bool is_null = FALSE;
+    my_bool *is_nullr;
+#if MYSQL_VERSION_ID >= 50000
+    my_bool *error;
+#endif
+    int ret;
+    unsigned long *length, maxlen;
+    int nargs;
+    MYSQL_BIND *bind;
+
+    if (sql->trans && sql->trans->errnum) {
+        return sql->trans->errnum;
+    }
+
+    nargs = mysql_stmt_param_count(statement->stmt);
+    bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+
+    for (i=0; i < nargs; ++i) {
+        arg = va_arg(args, char*);
+        bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+        bind[i].buffer = arg;
+        bind[i].buffer_length = strlen(arg);
+        bind[i].length = &bind[i].buffer_length;
+        bind[i].is_null = &is_null;
+        bind[i].is_unsigned = 0;
+    }
+
+    ret = mysql_stmt_bind_param(statement->stmt, bind);
+    if (ret == 0) {
+        ret = mysql_stmt_execute(statement->stmt);
+        if (!ret) {
+            if (!*res) {
+                *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+            }
+            (*res)->random = random;
+            (*res)->statement = statement->stmt;
+            (*res)->res = mysql_stmt_result_metadata(statement->stmt);
+            apr_pool_cleanup_register(pool, (*res)->res,
+                                      free_result, apr_pool_cleanup_null);
+            nfields = mysql_num_fields((*res)->res);
+            if (!(*res)->bind) {
+                (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
+                length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
+#if MYSQL_VERSION_ID >= 50000
+                error = apr_palloc(pool, nfields*sizeof(my_bool));
+#endif
+                is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
+                for ( i = 0; i < nfields; ++i ) {
+                    maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
+                              (*res)->res->fields[i].length : sql->fldsz) + 1;
+                    (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+                    (*res)->bind[i].buffer_length = maxlen;
+                    (*res)->bind[i].length = &length[i];
+                    (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
+                    (*res)->bind[i].is_null = is_nullr+i;
+#if MYSQL_VERSION_ID >= 50000
+                    (*res)->bind[i].error = error+i;
+#endif
+                }
+            }
+            ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
+            if (!ret) {
+                ret = mysql_stmt_store_result(statement->stmt);
+            }
+        }
+    }
+    if (ret != 0) {
+        ret = mysql_stmt_errno(statement->stmt);
+    }
+    if (sql->trans) {
+        sql->trans->errnum = ret;
+    }
+    return ret;
+}
+#endif
+
+static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
+{
+    int ret = -1;
+    if (trans) {
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+        /* rollback on error or explicit rollback request */
+        if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
+#else
+        if (trans->errnum) {
+#endif
+            trans->errnum = 0;
+            ret = mysql_rollback(trans->handle->conn);
+        }
+        else {
+            ret = mysql_commit(trans->handle->conn);
+        }
+    }
+    ret |= mysql_autocommit(trans->handle->conn, 1);
+    trans->handle->trans = NULL;
+    return ret;
+}
+/* Whether or not transactions work depends on whether the
+ * underlying DB supports them within MySQL.  Unfortunately
+ * it fails silently with the default InnoDB.
+ */
+
+static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
+                                 apr_dbd_transaction_t **trans)
+{
+    /* Don't try recursive transactions here */
+    if (handle->trans) {
+        dbd_mysql_end_transaction(handle->trans) ;
+    }
+    if (!*trans) {
+        *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
+    }
+    (*trans)->errnum = mysql_autocommit(handle->conn, 0);
+    (*trans)->handle = handle;
+    handle->trans = *trans;
+    return (*trans)->errnum;
+}
+
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+    if (!trans)
+        return APR_DBD_TRANSACTION_COMMIT;
+
+    return trans->mode;
+}
+
+static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans,
+                                          int mode)
+{
+    if (!trans)
+        return APR_DBD_TRANSACTION_COMMIT;
+
+    return trans->mode = (mode & TXN_MODE_BITS);
+}
+#endif
+
+static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params)
+{
+    static const char *const delims = " \r\n\t;|,";
+    const char *ptr;
+    int i;
+    const char *key;
+    size_t klen;
+    const char *value;
+    size_t vlen;
+#if MYSQL_VERSION_ID >= 50013
+    my_bool do_reconnect = 1;
+#endif
+    MYSQL *real_conn;
+    unsigned long flags = 0;
+    
+    struct {
+        const char *field;
+        const char *value;
+    } fields[] = {
+        {"host", NULL},
+        {"user", NULL},
+        {"pass", NULL},
+        {"dbname", NULL},
+        {"port", NULL},
+        {"sock", NULL},
+        {"flags", NULL},
+        {"fldsz", NULL},
+        {NULL, NULL}
+    };
+    unsigned int port = 0;
+    apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t));
+    sql->fldsz = FIELDSIZE;
+    sql->conn = mysql_init(sql->conn);
+    if ( sql->conn == NULL ) {
+        return NULL;
+    }
+    for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
+        for (key = ptr-1; isspace(*key); --key);
+        klen = 0;
+        while (isalpha(*key)) {
+            /* don't parse backwards off the start of the string */
+            if (key == params) {
+                --key;
+                ++klen;
+                break;
+            }
+            --key;
+            ++klen;
+        }
+        ++key;
+        for (value = ptr+1; isspace(*value); ++value);
+        vlen = strcspn(value, delims);
+        for (i = 0; fields[i].field != NULL; i++) {
+            if (!strncasecmp(fields[i].field, key, klen)) {
+                fields[i].value = apr_pstrndup(pool, value, vlen);
+                break;
+            }
+        }
+        ptr = value+vlen;
+    }
+    if (fields[4].value != NULL) {
+        port = atoi(fields[4].value);
+    }
+    if (fields[6].value != NULL &&
+        !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) {
+        flags |= CLIENT_FOUND_ROWS; /* only option we know */
+    }
+    if (fields[7].value != NULL) {
+        sql->fldsz = atol(fields[7].value);
+    }
+
+#if MYSQL_VERSION_ID >= 50013
+    /* the MySQL manual says this should be BEFORE mysql_real_connect */
+    mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
+#endif
+    
+    real_conn = mysql_real_connect(sql->conn, fields[0].value,
+                                   fields[1].value, fields[2].value,
+                                   fields[3].value, port,
+                                   fields[5].value, flags);
+
+    if(real_conn == NULL) {
+        mysql_close(sql->conn);
+        return NULL;
+    }
+
+#if MYSQL_VERSION_ID >= 50013
+    /* Some say this should be AFTER mysql_real_connect */
+    mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
+#endif
+
+    return sql;
+}
+
+static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
+{
+    mysql_close(handle->conn);
+    return APR_SUCCESS;
+}
+
+static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
+                                         apr_dbd_t *handle)
+{
+    return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
+}
+
+static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
+                               const char* name)
+{
+    return mysql_select_db(handle->conn, name);
+}
+
+static void *dbd_mysql_native(apr_dbd_t *handle)
+{
+    return handle->conn;
+}
+
+static int dbd_mysql_num_cols(apr_dbd_results_t *res)
+{
+    if (res->statement) {
+        return mysql_stmt_field_count(res->statement);
+    }
+    else {
+        return mysql_num_fields(res->res);
+    }
+}
+
+static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
+{
+    if (res->random) {
+        if (res->statement) {
+            return (int) mysql_stmt_num_rows(res->statement);
+        }
+        else {
+            return (int) mysql_num_rows(res->res);
+        }
+    }
+    else {
+        return -1;
+    }
+}
+
+static apr_status_t thread_end(void *data)
+{
+    mysql_thread_end();
+    return APR_SUCCESS;
+}
+
+static void dbd_mysql_init(apr_pool_t *pool)
+{
+    my_init();
+    /* FIXME: this is a guess; find out what it really does */ 
+    apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null);
+}
+APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = {
+    "mysql",
+    dbd_mysql_init,
+    dbd_mysql_native,
+    dbd_mysql_open,
+    dbd_mysql_check_conn,
+    dbd_mysql_close,
+    dbd_mysql_select_db,
+    dbd_mysql_transaction,
+    dbd_mysql_end_transaction,
+    dbd_mysql_query,
+    dbd_mysql_select,
+    dbd_mysql_num_cols,
+    dbd_mysql_num_tuples,
+    dbd_mysql_get_row,
+    dbd_mysql_get_entry,
+    dbd_mysql_error,
+    dbd_mysql_escape,
+    dbd_mysql_prepare,
+    dbd_mysql_pvquery,
+    dbd_mysql_pvselect,
+    dbd_mysql_pquery,
+    dbd_mysql_pselect
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+    ,
+    dbd_mysql_get_name,
+    dbd_mysql_transaction_mode_get,
+    dbd_mysql_transaction_mode_set,
+    "?",
+    dbd_mysql_pvbquery,
+    dbd_mysql_pvbselect,
+    dbd_mysql_pbquery,
+    dbd_mysql_pbselect,
+    dbd_mysql_datum_get
+#endif
+};
+
+#endif



Mime
View raw message