Wietse Venema writes:

Unless I am mistaken, this implements the same functionality as the
pipemap table. It queries tables in sequence, not in parallel.

Attached is the new patch. Sorry about the confusion.
This one has some documentation changes as well.

Thanks,

Roel
Add support for joinmap tables

This patchs adds support for joinmap tables. A joinmap table is a table
that contains two or more other tables. A lookup in the joinmap tables is
done in all of the tables it contains. The result is concatenated and
returned.

The use case for this type of table is if you have two *different* tables
containing similar information (say an ldap table and a hash table with
virtial aliases) or if you have to do two lookups to get all the information
you want.

diff -ruN a/.indent.pro e/.indent.pro
--- a/.indent.pro       2014-07-18 01:09:15.000000000 +0200
+++ e/.indent.pro       2014-09-11 11:47:57.208236388 +0200
@@ -77,6 +77,7 @@
 -TDICT_DEBUG
 -TDICT_ENV
 -TDICT_FAIL
+-TDICT_JOIN
 -TDICT_HT
 -TDICT_LDAP
 -TDICT_LMDB
diff -ruN a/README_FILES/DATABASE_README e/README_FILES/DATABASE_README
--- a/README_FILES/DATABASE_README      2014-06-30 23:00:51.000000000 +0200
+++ e/README_FILES/DATABASE_README      2014-09-11 15:38:41.238330621 +0200
@@ -215,6 +215,15 @@
     iinntteerrnnaall
         A non-shared, in-memory hash table. Its content are lost when a process
         terminates.
+    jjooiinnmmaapp (read-only)
+        Join the result of lookups in several tables. Example: 
"jjooiinnmmaapp::
+        !type1:name1! ... !typen:namen". A "joinmap:" query is given to all
+        tables in the joinmap. Any results that are received are joined and
+        the end result is returned. The query is always given to all tables,
+        even if a result has already been found. Results are not deduplicated.
+        The first ASCII character after "joinmap:" will be used as the 
separator
+        between the lookup tables that follow (do not use space, ",", ":" or
+        non-ASCII).
     llmmddbb
         OpenLDAP LMDB database. This is available only on systems with support
         for LMDB databases. Public database files are created with the postmap
diff -ruN a/html/DATABASE_README.html e/html/DATABASE_README.html
--- a/html/DATABASE_README.html 2014-06-30 23:00:50.000000000 +0200
+++ e/html/DATABASE_README.html 2014-09-11 15:54:24.842153050 +0200
@@ -318,6 +318,19 @@
 <dd> A non-shared, in-memory hash table. Its content are lost when
 a process terminates. </dd>
 
+<dt> <b>joinmap</b> (read-only) </dt>
+
+<dd> Joins the result of lookups in several tables. Example:
+"<b><a 
href="DATABASE_README.html#types">joinmap</a>:</b><i>!type<sub>1</sub>:name<sub>1</sub>!
 ...
+!type<sub>n</sub>:name<sub>n</sub></i>".  A "<a 
href="DATABASE_README.html#types">joinmap</a>:" query is
+given to all tables in the joinmap.  Any results that are received
+are joined and the end result is returned.  The query is always
+given to all tables, even if a result has already been found.  Results
+are not deduplicated.
+The first ASCII character after "<a 
href="DATABASE_README.html#types">pipemap</a>:"
+will be used as the separator between the lookup tables that follow
+(do not use space, ",", ":" or non-ASCII).  </dd>
+
 <dt> <b>lmdb</b> </dt>
 
 <dd> OpenLDAP LMDB database.  This is available only on systems
diff -ruN a/man/man1/postconf.1 e/man/man1/postconf.1
--- a/man/man1/postconf.1       2014-06-30 19:49:18.000000000 +0200
+++ e/man/man1/postconf.1       2014-09-11 15:43:38.878332662 +0200
@@ -255,6 +255,16 @@
 .IP \fBinternal\fR
 A non-shared, in-memory hash table. Its content are lost
 when a process terminates.
+.IP "\fBjoinmap\fR (read-only)"
+Join the result of lookups in several tables. Example: 
+"\fBjoinmap:\fI!type_1:name_1!  ... !type_n:name_n\fR".
+A "joinmap:" query is given to all tables in the joinmap.
+Any results that are received are joined and the end
+result is returned. The query is always given to all tables,
+even if a result has already been found. Results are not
+deduplicated. The first ASCII character after "joinmap:"
+will be used as the separator between the lookup tables
+that follow (do not use space, ",", ":" or non-ASCII).
 .IP "\fBlmdb\fR"
 OpenLDAP LMDB database (a memory-mapped, persistent file).
 Available on systems with support for LMDB databases.  This
diff -ruN a/mantools/postlink e/mantools/postlink
--- a/mantools/postlink 2014-09-07 18:01:34.000000000 +0200
+++ e/mantools/postlink 2014-09-11 15:45:02.148333229 +0200
@@ -1118,6 +1118,7 @@
     s/\b(fail):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
     s/\b(hash):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
     s/\b(internal):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
+    s/\b(joinmap):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
     s/\b(ldap):/<a href="ldap_table.5.html">$1<\/a>:/g;
     s/\b(lmdb):/<a href="lmdb_table.5.html">$1<\/a>:/g;
     s/\b(memcache):/<a href="memcache_table.5.html">$1<\/a>:/g;
diff -ruN a/proto/DATABASE_README.html e/proto/DATABASE_README.html
--- a/proto/DATABASE_README.html        2014-06-30 22:56:16.000000000 +0200
+++ e/proto/DATABASE_README.html        2014-09-11 15:47:22.272194203 +0200
@@ -318,6 +318,18 @@
 <dd> A non-shared, in-memory hash table. Its content are lost when
 a process terminates. </dd>
 
+<dt> <b>joinmap</b> (read-only) </dt>
+
+<dd> Joins the result of lookups in several tables. Example:
+"<b>joinmap:</b><i>!type<sub>1</sub>:name<sub>1</sub>! ...
+!type<sub>n</sub>:name<sub>n</sub></i>".  A "joinmap:" query is
+given to all tables in the joinmap.  Any results that are received
+are joined and the end result is returned.  The query is always
+given to all tables, even if a result has already been found.
+Results are not deduplicated.  The first ASCII character after
+"joinmap:" will be used as the separator between the lookup tables
+that follow (do not use space, ",", ":" or non-ASCII).</dd>
+
 <dt> <b>lmdb</b> </dt>
 
 <dd> OpenLDAP LMDB database.  This is available only on systems
diff -ruN a/src/postconf/postconf.c e/src/postconf/postconf.c
--- a/src/postconf/postconf.c   2014-06-30 19:49:01.000000000 +0200
+++ e/src/postconf/postconf.c   2014-09-11 15:50:07.002151229 +0200
@@ -249,6 +249,16 @@
 /* .IP \fBinternal\fR
 /*     A non-shared, in-memory hash table. Its content are lost
 /*     when a process terminates.
+/* .IP "\fBjoinmap\fR (read-only)"
+/*     Joins the result of lookups in several tables. Example:
+/*     "\fBjoinmap:\fI!type_1:name_1!  ... !type_n:name_n\fR".
+/*     A "joinmap:" query is given to all tables in the joinmap.
+/*     Any results that are received are joined and the end result
+/*     is returned. The query is always given to all tables, even
+/*     if a result has already been found. Results are not
+/*     deduplicated. The first ASCII character after "joinmap:"
+/*     will be used as the separator between the lookup tables
+/*     that follow (do not use space, ",", ":" or non-ASCII).
 /* .IP "\fBlmdb\fR"
 /*     OpenLDAP LMDB database (a memory-mapped, persistent file).
 /*     Available on systems with support for LMDB databases.  This
diff -ruN a/src/util/Makefile.in e/src/util/Makefile.in
--- a/src/util/Makefile.in      2014-07-21 01:21:23.000000000 +0200
+++ e/src/util/Makefile.in      2014-09-11 11:54:25.868238221 +0200
@@ -38,7 +38,7 @@
        dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \
        dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c \
        poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \
-       valid_utf8_hostname.c midna.c
+       valid_utf8_hostname.c midna.c dict_join.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -78,7 +78,7 @@
        dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \
        dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o \
        poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \
-       valid_utf8_hostname.o midna.o
+       valid_utf8_hostname.o midna.o dict_join.o
 # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
 # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
 # otherwise it sets the PLUGIN_* macros.
@@ -107,7 +107,7 @@
        edit_file.h dict_cache.h dict_thash.h ip_match.h nbbio.h base32_code.h \
        dict_fail.h warn_stat.h dict_sockmap.h line_number.h timecmp.h \
        slmdb.h compat_va_copy.h dict_pipe.h dict_random.h \
-       valid_utf8_hostname.h midna.h
+       valid_utf8_hostname.h midna.h dict_join.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c
 DEFS   = -I. -D$(SYSTYPE)
@@ -1043,6 +1043,19 @@
 dict_ht.o: vbuf.h
 dict_ht.o: vstream.h
 dict_ht.o: vstring.h
+dict_join.o: argv.h
+dict_join.o: dict.h
+dict_join.o: dict_join.c
+dict_join.o: dict_join.h
+dict_join.o: htable.h
+dict_join.o: msg.h
+dict_join.o: myflock.h
+dict_join.o: mymalloc.h
+dict_join.o: stringops.h
+dict_join.o: sys_defs.h
+dict_join.o: vbuf.h
+dict_join.o: vstream.h
+dict_join.o: vstring.h
 dict_lmdb.o: argv.h
 dict_lmdb.o: dict.h
 dict_lmdb.o: dict_lmdb.c
@@ -1094,6 +1107,7 @@
 dict_open.o: dict_env.h
 dict_open.o: dict_fail.h
 dict_open.o: dict_ht.h
+dict_open.o: dict_join.h
 dict_open.o: dict_lmdb.h
 dict_open.o: dict_ni.h
 dict_open.o: dict_nis.h
diff -ruN a/src/util/dict_join.c e/src/util/dict_join.c
--- a/src/util/dict_join.c      1970-01-01 01:00:00.000000000 +0100
+++ e/src/util/dict_join.c      2014-09-11 14:09:08.548294332 +0200
@@ -0,0 +1,200 @@
+/*++
+/* NAME
+/*     dict_join 3
+/* SUMMARY
+/*     dictionary manager interface for pipelined tables
+/* SYNOPSIS
+/*     #include <dict_join.h>
+/*
+/*     DICT    *dict_join_open(name, open_flags, dict_flags)
+/*     const char *name;
+/*     int     open_flags;
+/*     int     dict_flags;
+/* DESCRIPTION
+/*     dict_join_open() opens a pipeline of one or more tables.
+/*     Example: "\fBpipemap:\fI!type_1:name_1! ... !type_n:name_n\fR".
+/*
+/*     Each "pipemap:" query is given to the first table.  Each
+/*     lookup result becomes the query for the next table in the
+/*     pipeline, and the last table produces the final result.
+/*     When any table lookup produces no result, the pipeline
+/*     produces no result.
+/*
+/*     The first ASCII character after "pipemap:" will be used as
+/*     the separator between the lookup tables that follow (do not
+/*     use space, ",", ":" or non-ASCII).
+/*
+/*     The open_flags and dict_flags arguments are passed on to
+/*     the underlying dictionaries.
+/* SEE ALSO
+/*     dict(3) generic dictionary manager
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include "mymalloc.h"
+#include "htable.h"
+#include "dict.h"
+#include "dict_join.h"
+#include "stringops.h"
+#include "vstring.h"
+
+/* Application-specific. */
+
+typedef struct {
+    DICT    dict;                      /* generic members */
+    ARGV   *map_join;                  /* pipelined tables */
+    VSTRING *re_buf;                   /* reply buffer */
+} DICT_JOIN;
+
+#define STR(x) vstring_str(x)
+
+/* dict_join_lookup - search pipelined tables */
+
+static const char *dict_join_lookup(DICT *dict, const char *query)
+{
+    const char myname[] = "dict_join_lookup";
+    DICT_JOIN *dict_join = (DICT_JOIN *) dict;
+    DICT   *map;
+    char  **cpp;
+    char   *dict_type_name;
+    const char *result = 0;
+
+    VSTRING_RESET(dict_join->re_buf);
+    for (cpp = dict_join->map_join->argv; (dict_type_name = *cpp) != 0; cpp++) 
{
+       if ((map = dict_handle(dict_type_name)) == 0)
+           msg_panic("%s: dictionary \"%s\" not found", myname, 
dict_type_name);
+       if ((result = dict_get(map, query)) == 0)
+           continue;
+       if (VSTRING_LEN(dict_join->re_buf) > 0)
+               VSTRING_ADDCH(dict_join->re_buf, ',');
+       vstring_strcat(dict_join->re_buf, result);
+    }
+    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE,
+                                       VSTRING_LEN(dict_join->re_buf) > 0
+                                       ? STR(dict_join->re_buf)
+                                       : 0);
+}
+
+/* dict_join_close - disassociate from pipelined tables */
+
+static void dict_join_close(DICT *dict)
+{
+    DICT_JOIN *dict_join = (DICT_JOIN *) dict;
+    char  **cpp;
+    char   *dict_type_name;
+
+    for (cpp = dict_join->map_join->argv; (dict_type_name = *cpp) != 0; cpp++)
+       dict_unregister(dict_type_name);
+    argv_free(dict_join->map_join);
+    vstring_free(dict_join->re_buf);
+    dict_free(dict);
+}
+
+/* dict_join_open - open pipelined tables */
+
+DICT   *dict_join_open(const char *name, int open_flags, int dict_flags)
+{
+    const char myname[] = "dict_join_open";
+    DICT_JOIN *dict_join;
+    char   *saved_name = 0;
+    char   *dict_type_name;
+    ARGV   *argv = 0;
+    char  **cpp;
+    DICT   *dict;
+    int     match_flags = 0;
+    struct DICT_OWNER aggr_owner;
+    char    delim[2];
+
+    /*
+     * Clarity first. Let the optimizer worry about redundant code.
+     */
+#define DICT_JOIN_RETURN(x) do { \
+           if (saved_name != 0) \
+               myfree(saved_name); \
+           if (argv != 0) \
+               argv_free(argv); \
+           return (x); \
+       } while (0)
+
+    /*
+     * Sanity checks.
+     */
+    if (open_flags != O_RDONLY)
+       DICT_JOIN_RETURN(dict_surrogate(DICT_TYPE_JOIN, name,
+                                       open_flags, dict_flags,
+                                 "%s:%s map requires O_RDONLY access mode",
+                                       DICT_TYPE_JOIN, name));
+    if (name[0] == ':')
+       DICT_JOIN_RETURN(dict_surrogate(DICT_TYPE_JOIN, name,
+                                       open_flags, dict_flags,
+                              "invalid list delimiter \"%c\" in \"%s:%s\"",
+                                       name[0], DICT_TYPE_JOIN, name));
+
+    /*
+     * Split the table name on the user-specified delimiter.
+     */
+    delim[0] = name[0];                                /* XXX ASCII delimiter 
*/
+    delim[1] = 0;
+    saved_name = mystrdup(name + 1);           /* XXX ASCII delimiter */
+    if (*saved_name == 0)
+       DICT_JOIN_RETURN(dict_surrogate(DICT_TYPE_JOIN, name,
+                                       open_flags, dict_flags,
+                                       "bad syntax: \"%s:%s\"; "
+                                       "need \"%s:%stype:name%s...\"",
+                                       DICT_TYPE_JOIN, name,
+                                       DICT_TYPE_JOIN, delim, delim));
+
+    /*
+     * The least-trusted table in the pipeline determines the over-all trust
+     * level. The first table determines the pattern-matching flags.
+     */
+    DICT_OWNER_AGGREGATE_INIT(aggr_owner);
+    argv = argv_split(saved_name, delim);
+    for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
+       if (msg_verbose)
+           msg_info("%s: %s", myname, dict_type_name);
+       if (strchr(dict_type_name, ':') == 0)
+           DICT_JOIN_RETURN(dict_surrogate(DICT_TYPE_JOIN, name,
+                                           open_flags, dict_flags,
+                                           "bad syntax: \"%s:%s\"; "
+                                           "need \"%s:%stype:name%s...\"",
+                                           DICT_TYPE_JOIN, name,
+                                           DICT_TYPE_JOIN, delim, delim));
+       if ((dict = dict_handle(dict_type_name)) == 0)
+           dict = dict_open(dict_type_name, open_flags, dict_flags);
+       dict_register(dict_type_name, dict);
+       DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
+       if (cpp == argv->argv)
+           match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
+    }
+
+    /*
+     * Bundle up the result.
+     */
+    dict_join =
+       (DICT_JOIN *) dict_alloc(DICT_TYPE_JOIN, name, sizeof(*dict_join));
+    dict_join->dict.lookup = dict_join_lookup;
+    dict_join->dict.close = dict_join_close;
+    dict_join->dict.flags = dict_flags | match_flags;
+    dict_join->dict.owner = aggr_owner;
+    dict_join->re_buf = vstring_alloc(100);
+    dict_join->map_join = argv;
+    argv = 0;
+    DICT_JOIN_RETURN(DICT_DEBUG (&dict_join->dict));
+}
diff -ruN a/src/util/dict_join.h e/src/util/dict_join.h
--- a/src/util/dict_join.h      1970-01-01 01:00:00.000000000 +0100
+++ e/src/util/dict_join.h      2014-09-11 12:03:06.138242563 +0200
@@ -0,0 +1,37 @@
+#ifndef _DICT_JOIN_H_INCLUDED_
+#define _DICT_JOIN_H_INCLUDED_
+
+/*++
+/* NAME
+/*     dict_join 3h
+/* SUMMARY
+/*     dictionary manager interface for joined tables
+/* SYNOPSIS
+/*     #include <dict_join.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <dict.h>
+
+ /*
+  * External interface.
+  */
+#define DICT_TYPE_JOIN "joinmap"
+
+extern DICT *dict_join_open(const char *, int, int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff -ruN a/src/util/dict_open.c e/src/util/dict_open.c
--- a/src/util/dict_open.c      2014-06-30 20:26:57.000000000 +0200
+++ e/src/util/dict_open.c      2014-09-11 12:03:46.898242860 +0200
@@ -300,6 +300,7 @@
 #include <dict_fail.h>
 #include <dict_pipe.h>
 #include <dict_random.h>
+#include <dict_join.h>
 #include <stringops.h>
 #include <split_at.h>
 #include <htable.h>
@@ -343,6 +344,7 @@
     DICT_TYPE_SOCKMAP, dict_sockmap_open,
     DICT_TYPE_FAIL, dict_fail_open,
     DICT_TYPE_PIPE, dict_pipe_open,
+    DICT_TYPE_JOIN, dict_join_open,
 #ifdef DICT_TYPE_PIPE_LEGACY
     DICT_TYPE_PIPE_LEGACY, dict_pipe_open,
 #endif

Reply via email to