You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ch...@apache.org on 2007/02/06 01:25:16 UTC

svn commit: r503931 - in /httpd/httpd/trunk: CHANGES modules/database/mod_dbd.c

Author: chrisd
Date: Mon Feb  5 16:25:15 2007
New Revision: 503931

URL: http://svn.apache.org/viewvc?view=rev&rev=503931
Log:
Introduce configuration groups to allow inheritance by virtual hosts of
database configurations from the main server.  The post_config hook function
determines the minimal set of distinct configurations necessary so that
database connection pools are shared between virtual hosts whenever possible.

The SQL statements which should be prepared for each database connection
are now stored in a hash for each virtual host during the configuration
phase, and these hashes are merged in the normal manner using
apr_hash_overlay() with that of the main server.  This allows for statements
to be de-registered by DBDPrepareSQL, if desired.  The post_config hook
function then compares the statements registered for each virtual host
when determining if a separate configuration group is required.  The
changes in r424798, r432560, r432562, and r466641, which still have problems
with configuration inheritance, are therefore no longer necessary.

Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/database/mod_dbd.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?view=diff&rev=503931&r1=503930&r2=503931
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Mon Feb  5 16:25:15 2007
@@ -2,6 +2,12 @@
 Changes with Apache 2.3.0
   [Remove entries to the current 2.0 and 2.2 section below, when backported]
 
+  *) mod_dbd: Introduce configuration groups to allow inheritance by virtual
+     hosts of database configurations from the main server.  Determine the
+     minimal set of distinct configurations and share connection pools
+     whenever possible.  Allow virtual hosts to override inherited SQL
+     statements.  [Chris Darroch]
+
   *) core: Fix broken chunk filtering that causes all non blocking reads to be
      converted into blocking reads.  PR 41056. [Jean-Frederic Clere, Jim Jagielski]
 

Modified: httpd/httpd/trunk/modules/database/mod_dbd.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/database/mod_dbd.c?view=diff&rev=503931&r1=503930&r2=503931
==============================================================================
--- httpd/httpd/trunk/modules/database/mod_dbd.c (original)
+++ httpd/httpd/trunk/modules/database/mod_dbd.c Mon Feb  5 16:25:15 2007
@@ -23,6 +23,7 @@
 #include "apr_reslist.h"
 #include "apr_strings.h"
 #include "apr_hash.h"
+#include "apr_tables.h"
 #include "apr_lib.h"
 #include "apr_dbd.h"
 
@@ -45,40 +46,47 @@
 #define NMAX_SET     0x4
 #define EXPTIME_SET  0x8
 
-typedef struct dbd_prepared dbd_prepared;
-
-struct dbd_prepared {
-    const char *label;
-    const char *query;
-    dbd_prepared *next;
-};
-
 typedef struct {
     server_rec *server;
     const char *name;
     const char *params;
     int persist;
-    dbd_prepared *prepared;
-    apr_pool_t *pool;
 #if APR_HAS_THREADS
-    apr_thread_mutex_t *mutex;
-    apr_reslist_t *reslist;
-    int destroyed;
     int nmin;
     int nkeep;
     int nmax;
     int exptime;
     int set;
+#endif
+    apr_hash_t *queries;
+} dbd_cfg_t;
+
+typedef struct dbd_group_t dbd_group_t;
+
+struct dbd_group_t {
+    dbd_cfg_t *cfg;
+    dbd_group_t *next;
+    apr_pool_t *pool;
+#if APR_HAS_THREADS
+    apr_thread_mutex_t *mutex;
+    apr_reslist_t *reslist;
+    int destroyed;
 #else
     ap_dbd_t *rec;
 #endif
+};
+
+typedef struct {
+    dbd_cfg_t *cfg;
+    dbd_group_t *group;
 } svr_cfg;
 
 typedef enum { cmd_name, cmd_params, cmd_persist,
                cmd_min, cmd_keep, cmd_max, cmd_exp
 } cmd_parts;
 
-static apr_hash_t *dbd_prepared_defns;
+static apr_pool_t *config_pool;
+static dbd_group_t *group_list;
 
 /* a default DBDriver value that'll generate meaningful error messages */
 static const char *const no_dbdriver = "[DBDriver unset]";
@@ -94,37 +102,41 @@
 static void *create_dbd_config(apr_pool_t *pool, server_rec *s)
 {
     svr_cfg *svr = apr_pcalloc(pool, sizeof(svr_cfg));
+    dbd_cfg_t *cfg = svr->cfg = apr_pcalloc(pool, sizeof(dbd_cfg_t));
 
-    svr->server = s;
-    svr->name = no_dbdriver; /* to generate meaningful error messages */
-    svr->params = ""; /* don't risk segfault on misconfiguration */
-    svr->persist = -1;
-#if APR_HAS_THREADS
-    svr->nmin = DEFAULT_NMIN;
-    svr->nkeep = DEFAULT_NKEEP;
-    svr->nmax = DEFAULT_NMAX;
-    svr->exptime = DEFAULT_EXPTIME;
+    cfg->server = s;
+    cfg->name = no_dbdriver; /* to generate meaningful error messages */
+    cfg->params = ""; /* don't risk segfault on misconfiguration */
+    cfg->persist = -1;
+#if APR_HAS_THREADS
+    cfg->nmin = DEFAULT_NMIN;
+    cfg->nkeep = DEFAULT_NKEEP;
+    cfg->nmax = DEFAULT_NMAX;
+    cfg->exptime = DEFAULT_EXPTIME;
 #endif
+    cfg->queries = apr_hash_make(pool);
 
     return svr;
 }
 
 static void *merge_dbd_config(apr_pool_t *pool, void *basev, void *addv)
 {
-    svr_cfg *base = (svr_cfg*) basev;
-    svr_cfg *add = (svr_cfg*) addv;
+    dbd_cfg_t *base = ((svr_cfg*) basev)->cfg;
+    dbd_cfg_t *add = ((svr_cfg*) addv)->cfg;
     svr_cfg *svr = apr_pcalloc(pool, sizeof(svr_cfg));
+    dbd_cfg_t *new = svr->cfg = apr_pcalloc(pool, sizeof(dbd_cfg_t));
 
-    svr->server = add->server;
-    svr->name = (add->name != no_dbdriver) ? add->name : base->name;
-    svr->params = strcmp(add->params, "") ? add->params : base->params;
-    svr->persist = (add->persist != -1) ? add->persist : base->persist;
-#if APR_HAS_THREADS
-    svr->nmin = (add->set&NMIN_SET) ? add->nmin : base->nmin;
-    svr->nkeep = (add->set&NKEEP_SET) ? add->nkeep : base->nkeep;
-    svr->nmax = (add->set&NMAX_SET) ? add->nmax : base->nmax;
-    svr->exptime = (add->set&EXPTIME_SET) ? add->exptime : base->exptime;
+    new->server = add->server;
+    new->name = (add->name != no_dbdriver) ? add->name : base->name;
+    new->params = strcmp(add->params, "") ? add->params : base->params;
+    new->persist = (add->persist != -1) ? add->persist : base->persist;
+#if APR_HAS_THREADS
+    new->nmin = (add->set&NMIN_SET) ? add->nmin : base->nmin;
+    new->nkeep = (add->set&NKEEP_SET) ? add->nkeep : base->nkeep;
+    new->nmax = (add->set&NMAX_SET) ? add->nmax : base->nmax;
+    new->exptime = (add->set&EXPTIME_SET) ? add->exptime : base->exptime;
 #endif
+    new->queries = apr_hash_overlay(pool, add->queries, base->queries);
 
     return svr;
 }
@@ -144,50 +156,51 @@
     const apr_dbd_driver_t *driver = NULL;
     svr_cfg *svr = ap_get_module_config(cmd->server->module_config,
                                         &dbd_module);
+    dbd_cfg_t *cfg = svr->cfg;
 
     switch ((long) cmd->info) {
     case cmd_name:
-        svr->name = val;
+        cfg->name = val;
         /* loading the driver involves once-only dlloading that is
          * best done at server startup.  This also guarantees that
          * we won't return an error later.
          */
-        switch (apr_dbd_get_driver(cmd->pool, svr->name, &driver)) {
+        switch (apr_dbd_get_driver(cmd->pool, cfg->name, &driver)) {
         case APR_ENOTIMPL:
-            return apr_psprintf(cmd->pool, "DBD: No driver for %s", svr->name);
+            return apr_psprintf(cmd->pool, "DBD: No driver for %s", cfg->name);
         case APR_EDSOOPEN:
             return apr_psprintf(cmd->pool,
                                 "DBD: Can't load driver file apr_dbd_%s.so",
-                                svr->name);
+                                cfg->name);
         case APR_ESYMNOTFOUND:
             return apr_psprintf(cmd->pool,
                                 "DBD: Failed to load driver apr_dbd_%s_driver",
-                                svr->name);
+                                cfg->name);
         }
         break;
     case cmd_params:
-        svr->params = val;
+        cfg->params = val;
         break;
 #if APR_HAS_THREADS
     case cmd_min:
         ISINT(val);
-        svr->nmin = atoi(val);
-        svr->set |= NMIN_SET;
+        cfg->nmin = atoi(val);
+        cfg->set |= NMIN_SET;
         break;
     case cmd_keep:
         ISINT(val);
-        svr->nkeep = atoi(val);
-        svr->set |= NKEEP_SET;
+        cfg->nkeep = atoi(val);
+        cfg->set |= NKEEP_SET;
         break;
     case cmd_max:
         ISINT(val);
-        svr->nmax = atoi(val);
-        svr->set |= NMAX_SET;
+        cfg->nmax = atoi(val);
+        cfg->set |= NMAX_SET;
         break;
     case cmd_exp:
         ISINT(val);
-        svr->exptime = atoi(val);
-        svr->set |= EXPTIME_SET;
+        cfg->exptime = atoi(val);
+        cfg->set |= EXPTIME_SET;
         break;
 #endif
     }
@@ -202,7 +215,7 @@
 
     switch ((long) cmd->info) {
     case cmd_persist:
-        svr->persist = flag;
+        svr->cfg->persist = flag;
         break;
     }
 
@@ -212,6 +225,11 @@
 static const char *dbd_prepare(cmd_parms *cmd, void *dconf, const char *query,
                                const char *label)
 {
+    if (!label) {
+        label = query;
+        query = "";
+    }
+
     ap_dbd_prepare(cmd->server, query, label);
 
     return NULL;
@@ -224,8 +242,9 @@
                   "SQL Driver Params"),
     AP_INIT_FLAG("DBDPersist", dbd_param_flag, (void*)cmd_persist, RSRC_CONF,
                  "Use persistent connection/pool"),
-    AP_INIT_TAKE2("DBDPrepareSQL", dbd_prepare, NULL, RSRC_CONF,
-                  "SQL statement to prepare and label"),
+    AP_INIT_TAKE12("DBDPrepareSQL", dbd_prepare, NULL, RSRC_CONF,
+                   "SQL statement to prepare (or nothing, to override "
+                   "statement inherited from main server) and label"),
 #if APR_HAS_THREADS
     AP_INIT_TAKE1("DBDMin", dbd_param, (void*)cmd_min, RSRC_CONF,
                   "Minimum number of connections"),
@@ -244,57 +263,158 @@
 static int dbd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
                           apr_pool_t *ptemp)
 {
-   dbd_prepared_defns = apr_hash_make(ptemp);
+   config_pool = pconf;
+   group_list = NULL;
    return OK;
 }
 
 DBD_DECLARE_NONSTD(void) ap_dbd_prepare(server_rec *s, const char *query,
                                         const char *label)
 {
-    dbd_prepared *prepared = apr_pcalloc(s->process->pool,
-                                         sizeof(dbd_prepared));
-    const char *key = apr_psprintf(s->process->pool, "%pp", s);
-
-    prepared->label = label;
-    prepared->query = query;
-    prepared->next = apr_hash_get(dbd_prepared_defns, key, APR_HASH_KEY_STRING);
+    svr_cfg *svr;
 
-    apr_hash_set(dbd_prepared_defns, key, APR_HASH_KEY_STRING, prepared);
+    svr = ap_get_module_config(s->module_config, &dbd_module);
+    if (!svr) {
+         /* some modules may call from within config directive handlers, and
+          * if these are called in a server context that contains no mod_dbd
+          * config directives, then we have to create our own server config
+          */
+         svr = create_dbd_config(config_pool, s);
+         ap_set_module_config(s->module_config, &dbd_module, svr);
+    }
+
+    if (apr_hash_get(svr->cfg->queries, label, APR_HASH_KEY_STRING)
+        && strcmp(query, "")) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
+                     "conflicting SQL statements with label %s", label);
+    }
+
+    apr_hash_set(svr->cfg->queries, label, APR_HASH_KEY_STRING, query);
 }
 
+typedef struct {
+    const char *label, *query;
+} dbd_query_t;
+
 static int dbd_post_config(apr_pool_t *pconf, apr_pool_t *plog,
                            apr_pool_t *ptemp, server_rec *s)
 {
     server_rec *sp;
+    apr_array_header_t *add_queries = apr_array_make(ptemp, 10,
+                                                     sizeof(dbd_query_t));
 
     for (sp = s; sp; sp = sp->next) {
         svr_cfg *svr = ap_get_module_config(sp->module_config, &dbd_module);
-        const char *key = apr_psprintf(ptemp, "%pp", s);
+        dbd_cfg_t *cfg = svr->cfg;
+        apr_hash_index_t *hi_first = apr_hash_first(ptemp, cfg->queries);
+        dbd_group_t *group;
+
+        /* dbd_setup in 2.2.3 and under was causing spurious error messages
+         * when dbd isn't configured.  We can stop that with a quick check here
+         * together with a similar check in ap_dbd_open (where being
+         * unconfigured is a genuine error that must be reported).
+         */
+        if (cfg->name == no_dbdriver || !cfg->persist) {
+            continue;
+        }
+
+        for (group = group_list; group; group = group->next) {
+            dbd_cfg_t *group_cfg = group->cfg;
+            apr_hash_index_t *hi;
+            int group_ok = 1;
+
+            if (strcmp(cfg->name, group_cfg->name)
+                || strcmp(cfg->params, group_cfg->params)) {
+                continue;
+            }
+
+#if APR_HAS_THREADS
+            if (cfg->nmin != group_cfg->nmin
+                || cfg->nkeep != group_cfg->nkeep
+                || cfg->nmax != group_cfg->nmax
+                || cfg->exptime != group_cfg->exptime) {
+                continue;
+            }
+#endif
+
+            add_queries->nelts = 0;
+
+            for (hi = hi_first; hi; hi = apr_hash_next(hi)) {
+                const char *label, *query;
+                const char *group_query;
+
+                apr_hash_this(hi, (void*) &label, NULL, (void*) &query);
+
+                group_query = apr_hash_get(group_cfg->queries, label,
+                                           APR_HASH_KEY_STRING);
+
+                if (!group_query) {
+                    dbd_query_t *add_query = apr_array_push(add_queries);
+
+                    add_query->label = label;
+                    add_query->query = query;
+                }
+                else if (strcmp(query, group_query)) {
+                    group_ok = 0;
+                    break;
+                }
+            }
+
+            if (group_ok) {
+                int i;
+
+                for (i = 0; i < add_queries->nelts; ++i) {
+                    dbd_query_t *add_query = ((dbd_query_t*) add_queries->elts)
+                                             + i;
+
+                    apr_hash_set(group_cfg->queries, add_query->label,
+                                 APR_HASH_KEY_STRING, add_query->query);
+                }
 
-        svr->prepared = apr_hash_get(dbd_prepared_defns, key,
-                                     APR_HASH_KEY_STRING);
+                svr->group = group;
+                break;
+            }
+        }
+
+        if (!svr->group) {
+            svr->group = group = apr_pcalloc(pconf, sizeof(dbd_group_t));
+
+            group->cfg = cfg;
+
+            group->next = group_list;
+            group_list = group;
+        }
     }
 
     return OK;
 }
 
-static apr_status_t dbd_prepared_init(apr_pool_t *pool, svr_cfg *svr,
+static apr_status_t dbd_prepared_init(apr_pool_t *pool, dbd_cfg_t *cfg,
                                       ap_dbd_t *rec)
 {
-    dbd_prepared *p;
+    apr_hash_index_t *hi;
     apr_status_t rv = APR_SUCCESS;
-    apr_dbd_prepared_t *stmt;
 
     rec->prepared = apr_hash_make(pool);
 
-    for (p = svr->prepared; p; p = p->next) {
+    for (hi = apr_hash_first(pool, cfg->queries); hi;
+         hi = apr_hash_next(hi)) {
+        const char *label, *query;
+        apr_dbd_prepared_t *stmt;
+
+        apr_hash_this(hi, (void*) &label, NULL, (void*) &query);
+
+        if (!strcmp(query, "")) {
+            continue;
+        }
+
         stmt = NULL;
-        if (apr_dbd_prepare(rec->driver, pool, rec->handle, p->query,
-                            p->label, &stmt) == 0) {
-            apr_hash_set(rec->prepared, p->label, APR_HASH_KEY_STRING, stmt);
+        if (apr_dbd_prepare(rec->driver, pool, rec->handle, query,
+                            label, &stmt)) {
+            rv = APR_EGENERAL;
         }
         else {
-            rv = APR_EGENERAL;
+            apr_hash_set(rec->prepared, label, APR_HASH_KEY_STRING, stmt);
         }
     }
 
@@ -311,9 +431,9 @@
 #if APR_HAS_THREADS
 static apr_status_t dbd_destruct(void *data, void *params, apr_pool_t *pool)
 {
-    svr_cfg *svr = params;
+    dbd_group_t *group = params;
 
-    if (!svr->destroyed) {
+    if (!group->destroyed) {
         ap_dbd_t *rec = data;
 
         apr_pool_destroy(rec->pool);
@@ -330,14 +450,15 @@
 static apr_status_t dbd_construct(void **data_ptr,
                                   void *params, apr_pool_t *pool)
 {
-    svr_cfg *svr = params;
+    dbd_group_t *group = params;
+    dbd_cfg_t *cfg = group->cfg;
     apr_pool_t *rec_pool, *prepared_pool;
     ap_dbd_t *rec;
     apr_status_t rv;
 
     rv = apr_pool_create(&rec_pool, pool);
     if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, svr->server,
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, cfg->server,
                      "DBD: Failed to create memory pool");
         return rv;
     }
@@ -351,24 +472,24 @@
      * our pool, which is probably not what we want.  Error checking isn't
      * necessary now, but in case that changes in the future ...
      */
-    rv = apr_dbd_get_driver(rec->pool, svr->name, &rec->driver);
+    rv = apr_dbd_get_driver(rec->pool, cfg->name, &rec->driver);
     if (rv != APR_SUCCESS) {
         switch (rv) {
         case APR_ENOTIMPL:
-            ap_log_error(APLOG_MARK, APLOG_ERR, rv, svr->server,
-                         "DBD: driver for %s not available", svr->name);
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
+                         "DBD: driver for %s not available", cfg->name);
             break;
         case APR_EDSOOPEN:
-            ap_log_error(APLOG_MARK, APLOG_ERR, rv, svr->server,
-                         "DBD: can't find driver for %s", svr->name);
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
+                         "DBD: can't find driver for %s", cfg->name);
             break;
         case APR_ESYMNOTFOUND:
-            ap_log_error(APLOG_MARK, APLOG_ERR, rv, svr->server,
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
                          "DBD: driver for %s is invalid or corrupted",
-                         svr->name);
+                         cfg->name);
             break;
         default:
-            ap_log_error(APLOG_MARK, APLOG_ERR, rv, svr->server,
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
                          "DBD: mod_dbd not compatible with APR in get_driver");
             break;
         }
@@ -377,15 +498,15 @@
         return rv;
     }
 
-    rv = apr_dbd_open(rec->driver, rec->pool, svr->params, &rec->handle);
+    rv = apr_dbd_open(rec->driver, rec->pool, cfg->params, &rec->handle);
     if (rv != APR_SUCCESS) {
         switch (rv) {
         case APR_EGENERAL:
-            ap_log_error(APLOG_MARK, APLOG_ERR, rv, svr->server,
-                         "DBD: Can't connect to %s", svr->name);
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
+                         "DBD: Can't connect to %s", cfg->name);
             break;
         default:
-            ap_log_error(APLOG_MARK, APLOG_ERR, rv, svr->server,
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
                          "DBD: mod_dbd not compatible with APR in open");
             break;
         }
@@ -402,17 +523,17 @@
      */
     rv = apr_pool_create(&prepared_pool, rec->pool);
     if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, svr->server,
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, cfg->server,
                      "DBD: Failed to create memory pool");
 
         apr_pool_destroy(rec->pool);
         return rv;
     }
 
-    rv = dbd_prepared_init(prepared_pool, svr, rec);
+    rv = dbd_prepared_init(prepared_pool, cfg, rec);
     if (rv != APR_SUCCESS) {
         const char *errmsg = apr_dbd_error(rec->driver, rec->handle, rv);
-        ap_log_error(APLOG_MARK, APLOG_ERR, rv, svr->server,
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server,
                      "DBD: failed to prepare SQL statements: %s",
                      (errmsg ? errmsg : "[???]"));
 
@@ -428,15 +549,16 @@
 #if APR_HAS_THREADS
 static apr_status_t dbd_destroy(void *data)
 {
-    svr_cfg *svr = data;
+    dbd_group_t *group = data;
 
-    svr->destroyed = 1;
+    group->destroyed = 1;
 
     return APR_SUCCESS;
 }
 
-static apr_status_t dbd_setup(server_rec *s, svr_cfg *svr)
+static apr_status_t dbd_setup(server_rec *s, dbd_group_t *group)
 {
+    dbd_cfg_t *cfg = group->cfg;
     apr_status_t rv;
 
     /* We create the reslist using a sub-pool of the pool passed to our
@@ -457,18 +579,18 @@
      * on each resource, which would then attempt to destroy the sub-pools
      * a second time.
      */
-    rv = apr_reslist_create(&svr->reslist,
-                            svr->nmin, svr->nkeep, svr->nmax,
-                            apr_time_from_sec(svr->exptime),
-                            dbd_construct, dbd_destruct, svr,
-                            svr->pool);
+    rv = apr_reslist_create(&group->reslist,
+                            cfg->nmin, cfg->nkeep, cfg->nmax,
+                            apr_time_from_sec(cfg->exptime),
+                            dbd_construct, dbd_destruct, group,
+                            group->pool);
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
                      "DBD: failed to initialise");
         return rv;
     }
 
-    apr_pool_cleanup_register(svr->pool, svr, dbd_destroy,
+    apr_pool_cleanup_register(group->pool, group, dbd_destroy,
                               apr_pool_cleanup_null);
 
     return APR_SUCCESS;
@@ -477,74 +599,69 @@
 
 static apr_status_t dbd_setup_init(apr_pool_t *pool, server_rec *s)
 {
-    svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module);
-    apr_status_t rv;
-
-    /* dbd_setup in 2.2.3 and under was causing spurious error messages
-     * when dbd isn't configured.  We can stop that with a quick check here
-     * together with a similar check in ap_dbd_open (where being
-     * unconfigured is a genuine error that must be reported).
-     */
-    if (svr->name == no_dbdriver) {
-        return APR_SUCCESS;
-    }
+    dbd_group_t *group;
+    apr_status_t rv = APR_SUCCESS;
 
-    if (!svr->persist) {
-        return APR_SUCCESS;
-    }
+    for (group = group_list; group; group = group->next) {
+        apr_status_t rv2;
 
-    rv = apr_pool_create(&svr->pool, pool);
-    if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
-                     "DBD: Failed to create reslist cleanup memory pool");
-        return rv;
-    }
+        rv2 = apr_pool_create(&group->pool, pool);
+        if (rv2 != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_CRIT, rv2, s,
+                         "DBD: Failed to create reslist cleanup memory pool");
+            return rv2;
+        }
 
 #if APR_HAS_THREADS
-    rv = dbd_setup(s, svr);
-    if (rv == APR_SUCCESS) {
-        return rv;
-    }
+        rv2 = dbd_setup(s, group);
+        if (rv2 == APR_SUCCESS) {
+            continue;
+        }
+        else if (rv == APR_SUCCESS) {
+            rv = rv2;
+        }
 
-    /* we failed, so create a mutex so that subsequent competing callers
-     * to ap_dbd_open can serialize themselves while they retry
-     */
-    rv = apr_thread_mutex_create(&svr->mutex,
-                                 APR_THREAD_MUTEX_DEFAULT, pool);
-    if (rv != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
-                     "DBD: Failed to create thread mutex");
-    }
+        /* we failed, so create a mutex so that subsequent competing callers
+         * to ap_dbd_open can serialize themselves while they retry
+         */
+        rv2 = apr_thread_mutex_create(&group->mutex,
+                                      APR_THREAD_MUTEX_DEFAULT, pool);
+        if (rv2 != APR_SUCCESS) {
+             ap_log_error(APLOG_MARK, APLOG_CRIT, rv2, s,
+                          "DBD: Failed to create thread mutex");
+             return rv2;
+        }
 #endif
+    }
 
     return rv;
 }
 
 #if APR_HAS_THREADS
-static apr_status_t dbd_setup_lock(server_rec *s, svr_cfg *svr)
+static apr_status_t dbd_setup_lock(server_rec *s, dbd_group_t *group)
 {
     apr_status_t rv = APR_SUCCESS, rv2;
 
     /* several threads could be here at the same time, all trying to
      * initialize the reslist because dbd_setup_init failed to do so
      */
-    if (!svr->mutex) {
+    if (!group->mutex) {
         /* we already logged an error when the mutex couldn't be created */
         return APR_EGENERAL;
     }
 
-    rv2 = apr_thread_mutex_lock(svr->mutex);
+    rv2 = apr_thread_mutex_lock(group->mutex);
     if (rv2 != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv2, s,
                      "DBD: Failed to acquire thread mutex");
         return rv2;
     }
 
-    if (!svr->reslist) {
-        rv = dbd_setup(s, svr);
+    if (!group->reslist) {
+        rv = dbd_setup(s, group);
     }
 
-    rv2 = apr_thread_mutex_unlock(svr->mutex);
+    rv2 = apr_thread_mutex_unlock(group->mutex);
     if (rv2 != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv2, s,
                      "DBD: Failed to release thread mutex");
@@ -565,12 +682,12 @@
 {
     svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module);
 
-    if (!svr->persist) {
+    if (!svr->cfg->persist) {
         apr_pool_destroy(rec->pool);
     }
 #if APR_HAS_THREADS
     else {
-        apr_reslist_release(svr->reslist, rec);
+        apr_reslist_release(svr->group->reslist, rec);
     }
 #endif
 }
@@ -592,38 +709,44 @@
 
     svr = ap_get_module_config(s->module_config, &dbd_module);
     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
-                 "DBD [%s] Error: %s", svr->name, errmsg);
+                 "DBD [%s] Error: %s", svr->cfg->name, errmsg);
     return rv;
 }
 
 DBD_DECLARE_NONSTD(ap_dbd_t*) ap_dbd_open(apr_pool_t *pool, server_rec *s)
 {
     svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module);
+    dbd_group_t *group = svr->group;
+    dbd_cfg_t *cfg = svr->cfg;
     ap_dbd_t *rec = NULL;
 #if APR_HAS_THREADS
     apr_status_t rv;
 #endif
 
     /* If nothing is configured, we shouldn't be here */
-    if (svr->name == no_dbdriver) {
+    if (cfg->name == no_dbdriver) {
         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "DBD: not configured");
         return NULL;
     }
 
-    if (!svr->persist) {
+    if (!cfg->persist) {
         /* Return a once-only connection */
-        dbd_construct((void*) &rec, svr, pool);
+        group = apr_pcalloc(pool, sizeof(dbd_group_t));
+
+        group->cfg = cfg;
+
+        dbd_construct((void*) &rec, group, pool);
         return rec;
     }
 
 #if APR_HAS_THREADS
-    if (!svr->reslist) {
-        if (dbd_setup_lock(s, svr) != APR_SUCCESS) {
+    if (!group->reslist) {
+        if (dbd_setup_lock(s, group) != APR_SUCCESS) {
             return NULL;
         }
     }
 
-    rv = apr_reslist_acquire(svr->reslist, (void*) &rec);
+    rv = apr_reslist_acquire(group->reslist, (void*) &rec);
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
                      "Failed to acquire DBD connection from pool!");
@@ -631,15 +754,15 @@
     }
 
     if (dbd_check(pool, s, rec) != APR_SUCCESS) {
-        apr_reslist_invalidate(svr->reslist, rec);
+        apr_reslist_invalidate(group->reslist, rec);
         return NULL;
     }
 #else
     /* If we have a persistent connection and it's good, we'll use it;
      * since this is non-threaded, we can update without a mutex
      */
-    rec = svr->rec;
-    if (rec) { 
+    rec = group->rec;
+    if (rec) {
         if (dbd_check(pool, s, rec) != APR_SUCCESS) {
             apr_pool_destroy(rec->pool);
             rec = NULL;
@@ -648,8 +771,8 @@
 
     /* We don't have a connection right now, so we'll open one */
     if (!rec) {
-        dbd_construct((void*) &rec, svr, svr->pool);
-        svr->rec = rec;
+        dbd_construct((void*) &rec, group, group->pool);
+        group->rec = rec;
     }
 #endif
 
@@ -691,8 +814,8 @@
                                                 &dbd_module);
 
             ap_set_module_config(r->request_config, &dbd_module, acq);
-            if (svr->persist) {
-                acq->reslist = svr->reslist;
+            if (svr->cfg->persist) {
+                acq->reslist = svr->group->reslist;
                 apr_pool_cleanup_register(r->pool, acq, dbd_release,
                                           apr_pool_cleanup_null);
             }
@@ -714,8 +837,8 @@
                                                 &dbd_module);
 
             ap_set_module_config(c->conn_config, &dbd_module, acq);
-            if (svr->persist) {
-                acq->reslist = svr->reslist;
+            if (svr->cfg->persist) {
+                acq->reslist = svr->group->reslist;
                 apr_pool_cleanup_register(c->pool, acq, dbd_release,
                                           apr_pool_cleanup_null);
             }