Roel van Meer writes:

What I am actually trying to do is a lookup with a single key in two maps. Maybe stackmap or concatmap?

Now, if you specify two maps somewhere, and the first map returns a result, there is no lookup done in the second map. With concatmap, both lookups would happen, and the combined result would be returned.

Largely based on the code of pipemap, I now have something that does this.
It goes by the name of joinmap.

/etc/postfix/a:
a       res-a
c       res-ca
d       res-d

/etc/postfix/b:
b       res-b
c       res-cb
d       res-d

# postmap -q a 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
res-a

# postmap -q b 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
res-b

# postmap -q c 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
res-ca,res-cb

# postmap -q d 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'
res-d,res-d

# postmap -q e 'joinmap:!hash:/etc/postfix/a!hash:/etc/postfix/b'


So far it does what I need: I can now use the output of two ldap lookups in virtual_alias_maps.

The patch is just code though. If you're interested, I can write some documentation to make it more complete. And although it seems to work on my test server, it hasn't been tested very thoroughly yet..

Thanks again (it's always a pleasure to work with Postfix),

Roel
diff -ruN a/.indent.pro b/.indent.pro
--- a/.indent.pro       2014-07-18 01:09:15.000000000 +0200
+++ b/.indent.pro       2014-09-11 12:29:49.298253498 +0200
@@ -77,6 +77,7 @@
 -TDICT_DEBUG
 -TDICT_ENV
 -TDICT_FAIL
+-TDICT_JOIN
 -TDICT_HT
 -TDICT_LDAP
 -TDICT_LMDB
diff -ruN b/src/util/Makefile.in c/src/util/Makefile.in
--- b/src/util/Makefile.in      2014-07-21 01:21:23.000000000 +0200
+++ c/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 b/src/util/dict_join.c c/src/util/dict_join.c
--- b/src/util/dict_join.c      1970-01-01 01:00:00.000000000 +0100
+++ c/src/util/dict_join.c      2014-09-11 12:02:19.538242235 +0200
@@ -0,0 +1,195 @@
+/*++
+/* 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 *qr_buf;                   /* query/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_strcpy(dict_join->qr_buf, query);
+    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, STR(dict_join->qr_buf))) == 0)
+           DICT_ERR_VAL_RETURN(dict, map->error, result);
+       vstring_strcpy(dict_join->qr_buf, result);
+    }
+    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, STR(dict_join->qr_buf));
+}
+
+/* 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->qr_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->qr_buf = vstring_alloc(100);
+    dict_join->map_join = argv;
+    argv = 0;
+    DICT_JOIN_RETURN(DICT_DEBUG (&dict_join->dict));
+}
diff -ruN b/src/util/dict_join.h c/src/util/dict_join.h
--- b/src/util/dict_join.h      1970-01-01 01:00:00.000000000 +0100
+++ c/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 b/src/util/dict_open.c c/src/util/dict_open.c
--- b/src/util/dict_open.c      2014-06-30 20:26:57.000000000 +0200
+++ c/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
diff -ruN c/src/util/dict_join.c d/src/util/dict_join.c
--- c/src/util/dict_join.c      2014-09-11 12:02:19.538242235 +0200
+++ d/src/util/dict_join.c      2014-09-11 14:09:08.548294332 +0200
@@ -59,7 +59,7 @@
 typedef struct {
     DICT    dict;                      /* generic members */
     ARGV   *map_join;                  /* pipelined tables */
-    VSTRING *qr_buf;                   /* query/reply buffer */
+    VSTRING *re_buf;                   /* reply buffer */
 } DICT_JOIN;
 
 #define STR(x) vstring_str(x)
@@ -75,15 +75,20 @@
     char   *dict_type_name;
     const char *result = 0;
 
-    vstring_strcpy(dict_join->qr_buf, query);
+    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, STR(dict_join->qr_buf))) == 0)
-           DICT_ERR_VAL_RETURN(dict, map->error, result);
-       vstring_strcpy(dict_join->qr_buf, result);
+       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, STR(dict_join->qr_buf));
+    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 */
@@ -97,7 +102,7 @@
     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->qr_buf);
+    vstring_free(dict_join->re_buf);
     dict_free(dict);
 }
 
@@ -188,7 +193,7 @@
     dict_join->dict.close = dict_join_close;
     dict_join->dict.flags = dict_flags | match_flags;
     dict_join->dict.owner = aggr_owner;
-    dict_join->qr_buf = vstring_alloc(100);
+    dict_join->re_buf = vstring_alloc(100);
     dict_join->map_join = argv;
     argv = 0;
     DICT_JOIN_RETURN(DICT_DEBUG (&dict_join->dict));

Reply via email to