]> granicus.if.org Git - apache/commitdiff
Introduce configuration groups to allow inheritance by virtual hosts of
authorChris Darroch <chrisd@apache.org>
Tue, 6 Feb 2007 00:25:15 +0000 (00:25 +0000)
committerChris Darroch <chrisd@apache.org>
Tue, 6 Feb 2007 00:25:15 +0000 (00:25 +0000)
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.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@503931 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
modules/database/mod_dbd.c

diff --git a/CHANGES b/CHANGES
index af2a35a346635d5b407c4eeed6f566f955fa0f59..8c9799b3f3b1f1fae50c32fd9a84a5082615c166 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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]
 
index eba281c25b231f4c324603c3062d2e34e12d0b2e..a8d7521dc473125e09ba293165276cd27b8af9e0 100644 (file)
@@ -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 @@ extern module AP_MODULE_DECLARE_DATA dbd_module;
 #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 const char *const no_dbdriver = "[DBDriver unset]";
 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;
+    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
-    svr->nmin = DEFAULT_NMIN;
-    svr->nkeep = DEFAULT_NKEEP;
-    svr->nmax = DEFAULT_NMAX;
-    svr->exptime = DEFAULT_EXPTIME;
+    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;
+    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
-    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->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 @@ static const char *dbd_param(cmd_parms *cmd, void *dconf, const char *val)
     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 @@ static const char *dbd_param_flag(cmd_parms *cmd, void *dconf, int flag)
 
     switch ((long) cmd->info) {
     case cmd_persist:
-        svr->persist = flag;
+        svr->cfg->persist = flag;
         break;
     }
 
@@ -212,6 +225,11 @@ static const char *dbd_param_flag(cmd_parms *cmd, void *dconf, int flag)
 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 @@ static const command_rec dbd_cmds[] = {
                   "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 const command_rec dbd_cmds[] = {
 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);
+    svr_cfg *svr;
 
-    prepared->label = label;
-    prepared->query = query;
-    prepared->next = apr_hash_get(dbd_prepared_defns, key, APR_HASH_KEY_STRING);
+    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(dbd_prepared_defns, key, APR_HASH_KEY_STRING, prepared);
+    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;
 
-        svr->prepared = apr_hash_get(dbd_prepared_defns, key,
-                                     APR_HASH_KEY_STRING);
+                    apr_hash_set(group_cfg->queries, add_query->label,
+                                 APR_HASH_KEY_STRING, add_query->query);
+                }
+
+                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 @@ static apr_status_t dbd_close(void *data)
 #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_destruct(void *data, void *params, apr_pool_t *pool)
 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 @@ static apr_status_t dbd_construct(void **data_ptr,
      * 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 @@ static apr_status_t dbd_construct(void **data_ptr,
         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 @@ static apr_status_t dbd_construct(void **data_ptr,
      */
     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 @@ static apr_status_t dbd_construct(void **data_ptr,
 #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 @@ static apr_status_t dbd_setup(server_rec *s, svr_cfg *svr)
      * 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(server_rec *s, svr_cfg *svr)
 
 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 @@ DBD_DECLARE_NONSTD(void) ap_dbd_close(server_rec *s, ap_dbd_t *rec)
 {
     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 @@ static apr_status_t dbd_check(apr_pool_t *pool, server_rec *s, ap_dbd_t *rec)
 
     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 @@ DBD_DECLARE_NONSTD(ap_dbd_t*) ap_dbd_open(apr_pool_t *pool, server_rec *s)
     }
 
     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 @@ DBD_DECLARE_NONSTD(ap_dbd_t*) ap_dbd_open(apr_pool_t *pool, server_rec *s)
 
     /* 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_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_acquire(request_rec *r)
                                                 &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_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_cacquire(conn_rec *c)
                                                 &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);
             }