]> granicus.if.org Git - apache/commitdiff
Add SQL Query capability to RewriteMap
authorNick Kew <niq@apache.org>
Tue, 10 Oct 2006 10:33:06 +0000 (10:33 +0000)
committerNick Kew <niq@apache.org>
Tue, 10 Oct 2006 10:33:06 +0000 (10:33 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@454683 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/mod/mod_rewrite.xml
modules/mappers/mod_rewrite.c

diff --git a/CHANGES b/CHANGES
index 8e7797f74fa187fb4a96d8dea21d736477d80a9a..6cc742f40455979046a1a88bafba6b021391d22a 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,8 @@
 Changes with Apache 2.3.0
   [Remove entries to the current 2.0 and 2.2 section below, when backported]
 
+  *) mod_rewrite: support rewritemap by SQL query [Nick Kew]
+
   *) Fix issue which could cause piped loggers to be orphaned and never
      terminate after a graceful restart.  PR 40651.  [Joe Orton, 
      Ruediger Pluem]
index f8970775a92b02fffb3d4e7c3b16db6b828ad094..a8abddad7b879f153384e540f7d22de4755cf846 100644 (file)
@@ -511,6 +511,26 @@ while (&lt;STDIN&gt;) {
             synchronization takes place.</li>
           </ol>
         </li>
+        <li>
+          <p><strong>SQL Query</strong><br />
+           MapType: <code>dbd</code> or <code>fastdbd</code>,
+           MapSource: An SQL SELECT statement that takes a single
+                      argument and returns a single value.</p>
+          <p>This uses <module>mod_dbd</module> to implement a rewritemap
+          by lookup in an SQL database.  There are two forms:
+          <code>fastdbd</code> caches database lookups internally,
+          <code>dbd</code> doesn't.  So <code>dbd</code> incurs a
+          performance penalty but responds immediately if the database
+          contents are updated, while <code>fastdbd</code> is more
+          efficient but won't re-read database contents until server
+          restart.</p>
+          <p>If a query returns more than one row, a random row from
+          the result set is used.</p>
+<example>
+<title>Example</title>
+RewriteMap myquery "fastdbd:SELECT destination FROM rewrite WHERE source = %s"
+</example>
+        </li>
       </ul>
       <p>The <directive>RewriteMap</directive> directive can occur more than
       once. For each mapping-function use one
index fceafd606cc200662ca4a254219fb7cfa0f02763..9b452a2d7ed1592d8abf5f590db0899cf481e489 100644 (file)
@@ -54,6 +54,8 @@
 #include "apr_signal.h"
 #include "apr_global_mutex.h"
 #include "apr_dbm.h"
+#include "apr_dbd.h"
+#include "mod_dbd.h"
 
 #if APR_HAS_THREADS
 #include "apr_thread_mutex.h"
 #include "unixd.h"
 #endif
 
+static ap_dbd_t *(*dbd_acquire)(request_rec*) = NULL;
+static void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
+
 /*
  * in order to improve performance on running production systems, you
  * may strip all rewritelog code entirely from mod_rewrite by using the
 #define MAPTYPE_PRG                 1<<2
 #define MAPTYPE_INT                 1<<3
 #define MAPTYPE_RND                 1<<4
+#define MAPTYPE_DBD                 1<<5
+#define MAPTYPE_DBD_CACHE           1<<6
 
 #define ENGINE_DISABLED             1<<0
 #define ENGINE_ENABLED              1<<1
@@ -225,6 +232,7 @@ typedef struct {
     char *(*func)(request_rec *,   /* function pointer for internal maps  */
                   char *);
     char **argv;                   /* argv of the external rewrite map    */
+    const char *dbdq;              /* SQL SELECT statement for rewritemap */
 } rewritemap_entry;
 
 /* special pattern types for RewriteCond */
@@ -1318,6 +1326,51 @@ static char *lookup_map_dbmfile(request_rec *r, const char *file,
 
     return value;
 }
+static char *lookup_map_dbd(request_rec *r, char *key, const char *label)
+{
+    apr_status_t rv;
+    apr_dbd_prepared_t *stmt;
+    apr_dbd_results_t *res = NULL;
+    apr_dbd_row_t *row = NULL;
+    const char *ret = NULL;
+    int n = 0;
+    ap_dbd_t *db = dbd_acquire(r);
+
+    stmt = apr_hash_get(db->prepared, label, APR_HASH_KEY_STRING);
+
+    rv = apr_dbd_pvselect(db->driver, r->pool, db->handle, &res,
+                          stmt, 0, key, NULL);
+    if (rv != 0) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "rewritemap: error querying for %s", key);
+    }
+    while (rv = apr_dbd_get_row(db->driver, r->pool, res, &row, -1), rv == 0) {
+        ++n;
+        if (ret == NULL) {
+            ret = apr_dbd_get_entry(db->driver, row, 0);
+        }
+        else {
+            /* randomise crudely amongst multiple results */
+            if ((double)rand() < (double)RAND_MAX/(double)n) {
+                ret = apr_dbd_get_entry(db->driver, row, 0);
+            }
+        }
+    }
+    if (rv != -1) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "rewritemap: error looking up %s", key);
+    }
+    switch (n) {
+    case 0:
+        return NULL;
+    case 1:
+        return apr_pstrdup(r->pool, ret);
+    default:
+        /* what's a fair rewritelog level for this? */
+        rewritelog((r, 3, NULL, "Multiple values found for %s", key));
+        return apr_pstrdup(r->pool, ret);
+    }
+}
 
 static char *lookup_map_program(request_rec *r, apr_file_t *fpin,
                                 apr_file_t *fpout, char *key)
@@ -1585,6 +1638,49 @@ static char *lookup_map(request_rec *r, char *name, char *key)
                     name, key, value));
         return *value ? value : NULL;
 
+    /*
+     * SQL map without cache
+     */
+    case MAPTYPE_DBD:
+        value = lookup_map_dbd(r, key, s->dbdq);
+        if (!value) {
+            rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
+                        name, key));
+            return NULL;
+        }
+
+        rewritelog((r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s",
+                   name, key, value));
+
+        return value;
+
+    /*
+     * SQL map with cache
+     */
+    case MAPTYPE_DBD_CACHE:
+        value = get_cache_value(s->cachename, st.mtime, key, r->pool);
+        if (!value) {
+            rewritelog((r, 6, NULL,
+                        "cache lookup FAILED, forcing new map lookup"));
+
+            value = lookup_map_dbd(r, key, s->dbdq);
+            if (!value) {
+                rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s",
+                            name, key));
+                return NULL;
+            }
+
+            rewritelog((r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s",
+                        name, key, value));
+
+            set_cache_value(s->cachename, st.mtime, key, value);
+            return value;
+        }
+
+        rewritelog((r, 5, NULL, "cache lookup OK: map=%s[SQL] key=%s, val=%s",
+                    name, key, value));
+        return *value ? value : NULL;
+        
     /*
      * Program file map
      */
@@ -2871,6 +2967,24 @@ static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, const char *a1,
                                newmap->dbmtype, " is invalid", NULL);
         }
     }
+    else if ((strncasecmp(a2, "dbd:", 4) == 0)
+             || (strncasecmp(a2, "fastdbd:", 8) == 0)) {
+        if (dbd_prepare == NULL) {
+            return "RewriteMap types dbd and fastdbd require mod_dbd!";
+        }
+        if ((a2[0] == 'd') || (a2[0] == 'D')) {
+            newmap->type = MAPTYPE_DBD;
+            fname = a2+4;
+        }
+        else {
+            newmap->type = MAPTYPE_DBD_CACHE;
+            fname = a2+8;
+            newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
+                                             (void *)cmd->server, a1);
+        }
+        newmap->dbdq = a1;
+        dbd_prepare(cmd->server, fname, newmap->dbdq);
+    }
     else if (strncasecmp(a2, "prg:", 4) == 0) {
         apr_tokenize_to_argv(a2 + 4, &newmap->argv, cmd->pool);
 
@@ -4796,6 +4910,11 @@ static void ap_register_rewrite_mapfunc(char *name, rewrite_mapfunc_t *func)
     apr_hash_set(mapfunc_hash, name, strlen(name), (const void *)func);
 }
 
+static void optional_fns(void) {
+    dbd_acquire = APR_RETRIEVE_OPTIONAL_FN(ap_dbd_acquire);
+    dbd_prepare = APR_RETRIEVE_OPTIONAL_FN(ap_dbd_prepare);
+}
+
 static void register_hooks(apr_pool_t *p)
 {
     /* fixup after mod_proxy, so that the proxied url will not
@@ -4813,6 +4932,7 @@ static void register_hooks(apr_pool_t *p)
     ap_hook_fixups(hook_fixup, aszPre, NULL, APR_HOOK_FIRST);
     ap_hook_fixups(hook_mimetype, NULL, NULL, APR_HOOK_LAST);
     ap_hook_translate_name(hook_uri2file, NULL, NULL, APR_HOOK_FIRST);
+    ap_hook_optional_fn_retrieve(optional_fns, NULL, NULL, APR_HOOK_MIDDLE);
 }
 
     /* the main config structure */