Before this change, a comma would be inserted if and only if the string to return to the caller was already non-empty. This caused an unfortunate asymmetry:
* Underlying tables that returned a found but empty result *before* any underlying tables that returned a found and non-empty result were treated as if the key did not exist in those tables. * Underlying tables that returned a found but empty result *after* an underlying table that returned a found and non-empty result would cause a comma to be appended to the unionmap result (followed by an empty string). For example, the behavior before this change was: $ postmap -q x 'unionmap:{static:{},static:a,static:{},static:{},static:b,static:{}}' a,,,b, With this change, a comma is always added if there was any previous result, regardless of whether that result was the empty string or not: $ postmap -q x 'unionmap:{static:{},static:a,static:{},static:{},static:b,static:{}}' ,a,,,b, Also, if none of the underlying tables return a non-empty string and at least one returns a "found, but it's an empty string" result, unionmap previously returned a "not found" result. Now it returns a found result (a string consisting entirely of zero or more commas). A reasonable alternative to this change is to only add a comma for a non-empty result: $ postmap -q x 'unionmap:{static:{},static:a,static:{},static:{},static:b,static:{}}' a,b This alternative was rejected because: * Users might need to distinguish "not found" from "found, but it's an empty string". This is particularly true if the number of results matters. * It is usually easy to filter out or ignore empty strings or superfluous commas. It is much more difficult to convert empty strings into special markers that indicate present but semantically empty, then process those markers as if they were empty strings. * This converts found but empty results into not found, which may break tables that are used as lists. To convert empty string results into not found results, users can do something like: pipemap:{$table_spec,pcre:{{/^(.+)$/sE $1}}} --- proto/DATABASE_README.html | 11 ++++++++--- src/postconf/postconf.c | 12 +++++++++--- src/util/dict_union.c | 8 ++++---- src/util/dict_union_test.in | 12 ++++++++++++ src/util/dict_union_test.ref | 16 ++++++++++++++++ 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/proto/DATABASE_README.html b/proto/DATABASE_README.html index 42a54ac7c..f4e054c81 100644 --- a/proto/DATABASE_README.html +++ b/proto/DATABASE_README.html @@ -465,9 +465,14 @@ the file name is taken literally; no suffix is appended. </dd> <dt> <b>unionmap</b> (read-only) </dt> -<dd> A table that sends each query to multiple lookup tables and -that concatenates all found results, separated by comma. The table -name syntax is the same as for pipemap tables. </dd> +<dd> A table that sends each lookup query to a list of underlying +tables and then concatenates all found results (including any empty +string results), separated by comma. The results are concatenated in +the same order as the underlying tables that produced them. The table +name syntax is the same as for pipemap tables. If all underlying +tables return a not found result, the unionmap returns a not found +result. If any underlying table returns an error, the unionmap +returns an error. </dd> <dt> <b>unix</b> (read-only) </dt> diff --git a/src/postconf/postconf.c b/src/postconf/postconf.c index 74f13b2cd..71eb75730 100644 --- a/src/postconf/postconf.c +++ b/src/postconf/postconf.c @@ -377,9 +377,15 @@ /* /* This feature is available with Postfix 2.8 and later. /* .IP "\fBunionmap\fR (read-only)" -/* A table that sends each query to multiple lookup tables and -/* that concatenates all found results, separated by comma. -/* The table name syntax is the same as for \fBpipemap\fR. +/* A table that sends each lookup query to a list of underlying +/* tables and then concatenates all found results (including any +/* empty string results), separated by comma. The results are +/* concatenated in the same order as the underlying tables that +/* produced them. The table name syntax is the same as for +/* \fBpipemap\fR tables. If all underlying tables return a not +/* found result, the \fBunionmap\fR returns a not found result. +/* If any underlying table returns an error, the \fBunionmap\fR +/* returns an error. /* /* This feature is available with Postfix 3.0 and later. /* .IP "\fBunix\fR (read-only)" diff --git a/src/util/dict_union.c b/src/util/dict_union.c index 80df03b61..5a38b0fe6 100644 --- a/src/util/dict_union.c +++ b/src/util/dict_union.c @@ -78,6 +78,7 @@ static const char *dict_union_lookup(DICT *dict, const char *query) char **cpp; char *dict_type_name; const char *result = 0; + const char *ret = 0; /* * After Roel van Meer, postfix-users mailing list, Sept 2014. @@ -87,16 +88,15 @@ static const char *dict_union_lookup(DICT *dict, const char *query) 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) { - if (VSTRING_LEN(dict_union->re_buf) > 0) + if (ret != 0) VSTRING_ADDCH(dict_union->re_buf, ','); vstring_strcat(dict_union->re_buf, result); + ret = STR(dict_union->re_buf); } else if (map->error != 0) { DICT_ERR_VAL_RETURN(dict, map->error, 0); } } - DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, - VSTRING_LEN(dict_union->re_buf) > 0 ? - STR(dict_union->re_buf) : 0); + DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, ret); } /* dict_union_close - disassociate from a bunch of tables */ diff --git a/src/util/dict_union_test.in b/src/util/dict_union_test.in index 9d111d43e..eae1c20db 100644 --- a/src/util/dict_union_test.in +++ b/src/util/dict_union_test.in @@ -5,3 +5,15 @@ EOF ${VALGRIND} ./dict_open 'unionmap:{static:one,fail:fail}' read <<EOF get foo EOF +${VALGRIND} ./dict_open 'unionmap:{static:{},static:a,static:{}}' read <<EOF +get foo +EOF +${VALGRIND} ./dict_open 'unionmap:{static:{}}' read <<EOF +get foo +EOF +${VALGRIND} ./dict_open 'unionmap:{static:{} static:{}}' read <<EOF +get foo +EOF +${VALGRIND} ./dict_open 'unionmap:{inline:{x=x}}' read <<EOF +get foo +EOF diff --git a/src/util/dict_union_test.ref b/src/util/dict_union_test.ref index b6094107c..cd7a93ad3 100644 --- a/src/util/dict_union_test.ref +++ b/src/util/dict_union_test.ref @@ -8,3 +8,19 @@ bar=one,two owner=trusted (uid=2147483647) > get foo foo: error ++ ./dict_open unionmap:{static:{},static:a,static:{}} read +owner=trusted (uid=2147483647) +> get foo +foo=,a, ++ ./dict_open unionmap:{static:{}} read +owner=trusted (uid=2147483647) +> get foo +foo= ++ ./dict_open unionmap:{static:{} static:{}} read +owner=trusted (uid=2147483647) +> get foo +foo=, ++ ./dict_open unionmap:{inline:{x=x}} read +owner=trusted (uid=2147483647) +> get foo +foo: not found -- 2.49.0 _______________________________________________ Postfix-devel mailing list -- postfix-devel@postfix.org To unsubscribe send an email to postfix-devel-le...@postfix.org