From 5b0b13c24eb000dde855d0969439fcb34fce494c Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Mon, 26 Dec 2016 00:00:00 -0500 Subject: [PATCH] postfix-3.2-20161226-nonprod --- postfix/.indent.pro | 1 + postfix/HISTORY | 8 ++ postfix/html/mysql_table.5.html | 159 ++++++++++++++------------- postfix/man/man5/mysql_table.5 | 48 ++++---- postfix/proto/mysql_table | 48 ++++---- postfix/src/global/dict_mysql.c | 155 ++++++++++++++------------ postfix/src/global/mail_version.h | 2 +- postfix/src/postconf/postconf_dbms.c | 4 +- 8 files changed, 233 insertions(+), 192 deletions(-) diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 06435a2a6..0b5723cf5 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -223,6 +223,7 @@ -TMVECT -TMYSQL -TMYSQL_NAME +-TMYSQL_RES -TNAMADR_LIST -TNAME_ASSIGNMENT -TNAME_CODE diff --git a/postfix/HISTORY b/postfix/HISTORY index 0e536cc79..d4c6b7d65 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -22702,3 +22702,11 @@ Apologies for any names omitted. Cleanup: simplified code structure in the MySQL client support for stored procedures. File: global/dict_mysql.c. + +20161226 + + Cleanup: more MySQL client code simplification, better error + messages, new per-database "require_result_set" parameter + (default: yes) which can be set to "no" to avoid the need + for dummy SELECT statements in stored procedures. Files: + global/dict_mysql.c, proto/mysql_table, postconf/postconf_dbms.c. diff --git a/postfix/html/mysql_table.5.html b/postfix/html/mysql_table.5.html index ba5c7632b..e066bc88c 100644 --- a/postfix/html/mysql_table.5.html +++ b/postfix/html/mysql_table.5.html @@ -101,47 +101,52 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) a substitute for the address Postfix is trying to resolve, e.g. query = SELECT replacement FROM aliases WHERE mailbox = '%s' + By default, every query must return a result set (instead of + storing its results in a table); with "require_result_set = no" + (Postfix 3.2 and later), the absence of a result set is treated + as "not found". + This parameter supports the following '%' expansions: %% This is replaced by a literal '%' character. - %s This is replaced by the input key. SQL quoting is used - to make sure that the input key does not add unexpected + %s This is replaced by the input key. SQL quoting is used + to make sure that the input key does not add unexpected metacharacters. %u When the input key is an address of the form user@domain, - %u is replaced by the SQL quoted local part of the - address. Otherwise, %u is replaced by the entire search - string. If the localpart is empty, the query is sup- + %u is replaced by the SQL quoted local part of the + address. Otherwise, %u is replaced by the entire search + string. If the localpart is empty, the query is sup- pressed and returns no results. %d When the input key is an address of the form user@domain, - %d is replaced by the SQL quoted domain part of the - address. Otherwise, the query is suppressed and returns + %d is replaced by the SQL quoted domain part of the + address. Otherwise, the query is suppressed and returns no results. %[SUD] The upper-case equivalents of the above expansions behave - in the query parameter identically to their lower-case - counter-parts. With the result_format parameter (see - below), they expand the input key rather than the result + in the query parameter identically to their lower-case + counter-parts. With the result_format parameter (see + below), they expand the input key rather than the result value. - %[1-9] The patterns %1, %2, ... %9 are replaced by the corre- - sponding most significant component of the input key's - domain. If the input key is user@mail.example.com, then + %[1-9] The patterns %1, %2, ... %9 are replaced by the corre- + sponding most significant component of the input key's + domain. If the input key is user@mail.example.com, then %1 is com, %2 is example and %3 is mail. If the input key - is unqualified or does not have enough domain components - to satisfy all the specified patterns, the query is sup- + is unqualified or does not have enough domain components + to satisfy all the specified patterns, the query is sup- pressed and returns no results. - The domain parameter described below limits the input keys to - addresses in matching domains. When the domain parameter is + The domain parameter described below limits the input keys to + addresses in matching domains. When the domain parameter is non-empty, SQL queries for unqualified addresses or addresses in non-matching domains are suppressed and return no results. - This parameter is available with Postfix 2.2. In prior releases - the SQL query was built from the separate parameters: - select_field, table, where_field and additional_conditions. The + This parameter is available with Postfix 2.2. In prior releases + the SQL query was built from the separate parameters: + select_field, table, where_field and additional_conditions. The mapping from the old parameters to the equivalent query is: SELECT [select_field] @@ -149,50 +154,50 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) WHERE [where_field] = '%s' [additional_conditions] - The '%s' in the WHERE clause expands to the escaped search - string. With Postfix 2.2 these legacy parameters are used if + The '%s' in the WHERE clause expands to the escaped search + string. With Postfix 2.2 these legacy parameters are used if the query parameter is not specified. NOTE: DO NOT put quotes around the query parameter. result_format (default: %s) Format template applied to result attributes. Most commonly used - to append (or prepend) text to the result. This parameter sup- + to append (or prepend) text to the result. This parameter sup- ports the following '%' expansions: %% This is replaced by a literal '%' character. - %s This is replaced by the value of the result attribute. + %s This is replaced by the value of the result attribute. When result is empty it is skipped. %u When the result attribute value is an address of the form - user@domain, %u is replaced by the local part of the - address. When the result has an empty localpart it is + user@domain, %u is replaced by the local part of the + address. When the result has an empty localpart it is skipped. - %d When a result attribute value is an address of the form - user@domain, %d is replaced by the domain part of the - attribute value. When the result is unqualified it is + %d When a result attribute value is an address of the form + user@domain, %d is replaced by the domain part of the + attribute value. When the result is unqualified it is skipped. %[SUD1-9] - The upper-case and decimal digit expansions interpolate - the parts of the input key rather than the result. Their - behavior is identical to that described with query, and - in fact because the input key is known in advance, - queries whose key does not contain all the information - specified in the result template are suppressed and + The upper-case and decimal digit expansions interpolate + the parts of the input key rather than the result. Their + behavior is identical to that described with query, and + in fact because the input key is known in advance, + queries whose key does not contain all the information + specified in the result template are suppressed and return no results. For example, using "result_format = smtp:[%s]" allows one to use a mailHost attribute as the basis of a transport(5) table. After - applying the result format, multiple values are concatenated as + applying the result format, multiple values are concatenated as comma separated strings. The expansion_limit and parameter - explained below allows one to restrict the number of values in + explained below allows one to restrict the number of values in the result, which is especially useful for maps that must return at most one value. - The default value %s specifies that each result value should be + The default value %s specifies that each result value should be used as is. This parameter is available with Postfix 2.2 and later. @@ -201,14 +206,14 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) domain (default: no domain list) This is a list of domain names, paths to files, or dictionaries. - When specified, only fully qualified search keys with a - *non-empty* localpart and a matching domain are eligible for + When specified, only fully qualified search keys with a + *non-empty* localpart and a matching domain are eligible for lookup: 'user' lookups, bare domain lookups and "@domain" - lookups are not performed. This can significantly reduce the + lookups are not performed. This can significantly reduce the query load on the MySQL server. domain = postfix.org, hash:/etc/postfix/searchdomains - It is best not to use SQL to store the domains eligible for SQL + It is best not to use SQL to store the domains eligible for SQL lookups. This parameter is available with Postfix 2.2 and later. @@ -217,35 +222,41 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) the input keys are always unqualified. expansion_limit (default: 0) - A limit on the total number of result elements returned (as a + A limit on the total number of result elements returned (as a comma separated list) by a lookup against the map. A setting of - zero disables the limit. Lookups fail with a temporary error if - the limit is exceeded. Setting the limit to 1 ensures that + zero disables the limit. Lookups fail with a temporary error if + the limit is exceeded. Setting the limit to 1 ensures that lookups do not return multiple values. option_file - Read options from the given file instead of the default my.cnf - location. This reads options from the [client] option group, + Read options from the given file instead of the default my.cnf + location. This reads options from the [client] option group, optionally followed by options from the group given with option_group. This parameter is available with Postfix 2.11 and later. option_group (default: Postfix >=3.2: client, <= 3.1: empty) - Read options from the given group of the mysql options file, + Read options from the given group of the mysql options file, after reading options from the [client] group. - Postfix 3.2 and later read [client] option group settings by - default. To disable this specify no option_file and specify + Postfix 3.2 and later read [client] option group settings by + default. To disable this specify no option_file and specify "option_group =" (i.e. an empty value). - Postfix 3.1 and earlier don't read [client] option group set- - tings unless a non-empty option_file or option_group value are + Postfix 3.1 and earlier don't read [client] option group set- + tings unless a non-empty option_file or option_group value are specified. To enable this, specify, for example, "option_group = client". This parameter is available with Postfix 2.11 and later. + require_result_set (default: yes) + If "yes", require that every query returns a result set. If + "no", treat the absence of a result set as "not found". + + This parameter is available with Postfix 3.2 and later. + tls_cert_file File containing client's X509 certificate. @@ -284,25 +295,18 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) The previously described '%' expansions can be used in the parameter(s) to the stored procedure. - The stored procedure must return data with a single result set, that is - the stored procedure must execute exactly one SELECT statement on every - code path. If you have complex logic and for some paths you want to - return no result you will need to include a SELECT statement that - returns no rows. One example of a query that returns no rows is + By default, every stored procedure call must return a result set, i.e. + every code path must execute a SELECT statement that returns a result + set (instead of storing its results in a table). With + "require_result_set = no", the absence of a result set is treated as + "not found". - SELECT 1 FROM DUAL WHERE FALSE + A stored procedure must not return multiple result sets. That is, + there must be no code path that executes multiple SELECT statements + that return a result (instead of storing their results in a table). - but you may use your own query. - - Stored procedures that return multiple result sets containing data, - that is stored procedures that execute multiple SELECT statements, are - not supported. Stored procedures in mysql produce an additional result - set without data which indicates the final status of the stored proce- - dure. If this final status is an error then any previous returned data - will not be used. - - The following is an example of a stored procedure returning a single - result set containing data: + The following is an example of a stored procedure returning a single + result set: CREATE [DEFINER=`user`@`host`] PROCEDURE `lookup`(IN `param` VARCHAR(255)) @@ -313,13 +317,13 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) END OBSOLETE QUERY INTERFACE - This section describes an interface that is deprecated as of Postfix - 2.2. It is replaced by the more general query interface described - above. If the query parameter is defined, the legacy parameters - described here ignored. Please migrate to the new interface as the + This section describes an interface that is deprecated as of Postfix + 2.2. It is replaced by the more general query interface described + above. If the query parameter is defined, the legacy parameters + described here ignored. Please migrate to the new interface as the legacy interface may be removed in a future release. - The following parameters can be used to fill in a SELECT template + The following parameters can be used to fill in a SELECT template statement of the form: SELECT [select_field] @@ -328,7 +332,7 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) [additional_conditions] The specifier %s is replaced by the search string, and is escaped so if - it contains single quotes or other odd characters, it will not cause a + it contains single quotes or other odd characters, it will not cause a parse error, or worse, a security problem. select_field @@ -376,5 +380,10 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) Stored-procedure support by John Fawcett. + Wietse Venema + Google, Inc. + 111 8th Avenue + New York, NY 10011, USA + MYSQL_TABLE(5) diff --git a/postfix/man/man5/mysql_table.5 b/postfix/man/man5/mysql_table.5 index 6a4657ff9..6a8464647 100644 --- a/postfix/man/man5/mysql_table.5 +++ b/postfix/man/man5/mysql_table.5 @@ -132,6 +132,11 @@ e.g. query = SELECT replacement FROM aliases WHERE mailbox = '%s' .fi +By default, every query must return a result set (instead +of storing its results in a table); with "\fBrequire_result_set += no\fR" (Postfix 3.2 and later), the absence of a result +set is treated as "not found". + This parameter supports the following '%' expansions: .RS .IP "\fB%%\fR" @@ -277,6 +282,12 @@ group settings unless a non\-empty \fBoption_file\fR or specify, for example, "\fBoption_group = client\fR". .sp This parameter is available with Postfix 2.11 and later. +.IP "\fBrequire_result_set (default: yes)\fR" +If "\fByes\fR", require that every query returns a result +set. If "\fBno\fR", treat the absence of a result set as +"not found". +.sp +This parameter is available with Postfix 3.2 and later. .IP "\fBtls_cert_file\fR" File containing client's X509 certificate. .sp @@ -316,29 +327,19 @@ instead of using a SELECT statement in the query, e.g. The previously described '%' expansions can be used in the parameter(s) to the stored procedure. -The stored procedure must return data with a single result -set, that is the stored procedure must execute exactly one -SELECT statement on every code path. If you have complex -logic and for some paths you want to return no result you -will need to include a SELECT statement that returns no -rows. One example of a query that returns no rows is +By default, every stored procedure call must return a result +set, i.e. every code path must execute a SELECT statement +that returns a result set (instead of storing its results +in a table). With "\fBrequire_result_set = no\fR", the +absence of a result set is treated as "not found". -.nf - SELECT 1 FROM DUAL WHERE FALSE -.fi - -but you may use your own query. - -Stored procedures that return multiple result sets containing -data, that is stored procedures that execute multiple SELECT -statements, are not supported. Stored procedures in mysql -produce an additional result set without data which indicates -the final status of the stored procedure. If this final -status is an error then any previous returned data will not -be used. +A stored procedure must not return multiple result sets. +That is, there must be no code path that executes multiple +SELECT statements that return a result (instead of storing +their results in a table). The following is an example of a stored procedure returning -a single result set containing data: +a single result set: .nf CREATE [DEFINER=`user`@`host`] PROCEDURE @@ -356,7 +357,7 @@ CREATE [DEFINER=`user`@`host`] PROCEDURE .fi This section describes an interface that is deprecated as of Postfix 2.2. It is replaced by the more general \fBquery\fR -interface described above. If the \fBquery\fR parameter +interface described above. If the \fBquery\fR parameter is defined, the legacy parameters described here ignored. Please migrate to the new interface as the legacy interface may be removed in a future release. @@ -437,3 +438,8 @@ P.O. BOX 1\-764 RO\-014700 Bucharest, ROMANIA Stored\-procedure support by John Fawcett. + +Wietse Venema +Google, Inc. +111 8th Avenue +New York, NY 10011, USA diff --git a/postfix/proto/mysql_table b/postfix/proto/mysql_table index 21899e60f..748b7f68a 100644 --- a/postfix/proto/mysql_table +++ b/postfix/proto/mysql_table @@ -120,6 +120,11 @@ # query = SELECT replacement FROM aliases WHERE mailbox = '%s' # .fi # +# By default, every query must return a result set (instead +# of storing its results in a table); with "\fBrequire_result_set +# = no\fR" (Postfix 3.2 and later), the absence of a result +# set is treated as "not found". +# # This parameter supports the following '%' expansions: # .RS # .IP "\fB%%\fR" @@ -265,6 +270,12 @@ # specify, for example, "\fBoption_group = client\fR". # .sp # This parameter is available with Postfix 2.11 and later. +# .IP "\fBrequire_result_set (default: yes)\fR" +# If "\fByes\fR", require that every query returns a result +# set. If "\fBno\fR", treat the absence of a result set as +# "not found". +# .sp +# This parameter is available with Postfix 3.2 and later. # .IP "\fBtls_cert_file\fR" # File containing client's X509 certificate. # .sp @@ -302,29 +313,19 @@ # The previously described '%' expansions can be used in the # parameter(s) to the stored procedure. # -# The stored procedure must return data with a single result -# set, that is the stored procedure must execute exactly one -# SELECT statement on every code path. If you have complex -# logic and for some paths you want to return no result you -# will need to include a SELECT statement that returns no -# rows. One example of a query that returns no rows is +# By default, every stored procedure call must return a result +# set, i.e. every code path must execute a SELECT statement +# that returns a result set (instead of storing its results +# in a table). With "\fBrequire_result_set = no\fR", the +# absence of a result set is treated as "not found". # -# .nf -# SELECT 1 FROM DUAL WHERE FALSE -# .fi -# -# but you may use your own query. -# -# Stored procedures that return multiple result sets containing -# data, that is stored procedures that execute multiple SELECT -# statements, are not supported. Stored procedures in mysql -# produce an additional result set without data which indicates -# the final status of the stored procedure. If this final -# status is an error then any previous returned data will not -# be used. +# A stored procedure must not return multiple result sets. +# That is, there must be no code path that executes multiple +# SELECT statements that return a result (instead of storing +# their results in a table). # # The following is an example of a stored procedure returning -# a single result set containing data: +# a single result set: # # .nf # CREATE [DEFINER=`user`@`host`] PROCEDURE @@ -340,7 +341,7 @@ # .fi # This section describes an interface that is deprecated as # of Postfix 2.2. It is replaced by the more general \fBquery\fR -# interface described above. If the \fBquery\fR parameter +# interface described above. If the \fBquery\fR parameter # is defined, the legacy parameters described here ignored. # Please migrate to the new interface as the legacy interface # may be removed in a future release. @@ -411,4 +412,9 @@ # RO-014700 Bucharest, ROMANIA # # Stored-procedure support by John Fawcett. +# +# Wietse Venema +# Google, Inc. +# 111 8th Avenue +# New York, NY 10011, USA #-- diff --git a/postfix/src/global/dict_mysql.c b/postfix/src/global/dict_mysql.c index 90add0b4e..8af8fef6f 100644 --- a/postfix/src/global/dict_mysql.c +++ b/postfix/src/global/dict_mysql.c @@ -96,6 +96,8 @@ /* location. /* .IP option_group /* Read options from the given group. +/* .IP require_result_set +/* Require that every query produces a result set. /* .IP tls_cert_file /* File containing client's X509 certificate. /* .IP tls_key_file @@ -132,44 +134,25 @@ /* where_field = alias /* .br /* hosts = host1.some.domain host2.some.domain -/* .IP additional_conditions -/* Backward compatibility when \fIquery\fR is not set, additional -/* conditions to the WHERE clause. -/* .IP hosts -/* List of hosts to connect to. -/* .PP -/* For example, if you want the map to reference databases of -/* the name "your_db" and execute a query like this: select -/* forw_addr from aliases where alias like '' -/* against any database called "vmailer_info" located on hosts -/* host1.some.domain and host2.some.domain, logging in as user -/* "vmailer" and password "passwd" then the configuration file -/* should read: -/* .PP -/* user = vmailer -/* .br -/* password = passwd -/* .br -/* dbname = vmailer_info -/* .br -/* table = aliases -/* .br -/* select_field = forw_addr -/* .br -/* where_field = alias -/* .br -/* hosts = host1.some.domain host2.some.domain /* .PP /* SEE ALSO /* dict(3) generic dictionary manager /* AUTHOR(S) -/* Scott Cotton +/* Scott Cotton, Joshua Marcus /* IC Group, Inc. /* scott@icgroup.com /* -/* Joshua Marcus -/* IC Group, Inc. -/* josh@icgroup.com +/* Liviu Daia +/* Institute of Mathematics of the Romanian Academy +/* P.O. BOX 1-764 +/* RO-014700 Bucharest, ROMANIA +/* +/* John Fawcett +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA /*--*/ /* System library. */ @@ -258,6 +241,7 @@ typedef struct { int tls_verify_cert; #endif #endif + int require_result_set; } DICT_MYSQL; #define STATACTIVE (1<<0) @@ -273,7 +257,7 @@ typedef struct { /* internal function declarations */ static PLMYSQL *plmysql_init(ARGV *); -static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *); +static int plmysql_query(DICT_MYSQL *, const char *, VSTRING *, MYSQL_RES **); static void plmysql_dealloc(PLMYSQL *); static void plmysql_close_host(HOST *); static void plmysql_down_host(HOST *); @@ -383,10 +367,12 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name) return (0); /* do the query - set dict->error & cleanup if there's an error */ - if ((query_res = plmysql_query(dict_mysql, name, query)) == 0) { + if (plmysql_query(dict_mysql, name, query, &query_res) == 0) { dict->error = DICT_ERR_RETRY; return (0); } + if (query_res == 0) + return (0); numrows = mysql_num_rows(query_res); if (msg_verbose) msg_info("%s: retrieved %d rows", myname, numrows); @@ -508,18 +494,20 @@ static void dict_mysql_event(int unused_event, void *context) } /* - * plmysql_query - process a MySQL query. Return MYSQL_RES* on success. + * plmysql_query - process a MySQL query. Return 'true' on success. * On failure, log failure and try other db instances. - * on failure of all db instances, return 0; + * on failure of all db instances, return 'false'; * close unnecessary active connections */ -static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql, - const char *name, - VSTRING *query) +static int plmysql_query(DICT_MYSQL *dict_mysql, + const char *name, + VSTRING *query, + MYSQL_RES **result) { HOST *host; - MYSQL_RES *res = 0; + MYSQL_RES *first_result = 0; + int query_error; /* * Helper to avoid spamming the log with warnings. @@ -533,7 +521,6 @@ static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql, } while (0) while ((host = dict_mysql_get_active(dict_mysql)) != NULL) { - int query_error = 0; #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000 @@ -549,56 +536,77 @@ static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql, dict_mysql->active_host = 0; #endif + query_error = 0; + errno = 0; + /* * The query must complete. */ if (mysql_query(host->db, vstring_str(query)) != 0) { query_error = 1; - msg_warn("mysql query failed: %s", mysql_error(host->db)); + msg_warn("%s:%s: query failed: %s", + dict_mysql->dict.type, dict_mysql->dict.name, + mysql_error(host->db)); } /* - * The query must return at least one result. - */ - else if ((res = mysql_store_result(host->db)) == 0) { - query_error = 1; - msg_warn("mysql query failed: no result set containing data"); - } - - /* - * Are there more results? -1 = no, 0 = yes, > 0 = error. We must - * collect all results to avoid synchronization errors. + * Collect all result sets to avoid synchronization errors. */ else { int next_res_status; - MYSQL_RES *temp_res; - while ((next_res_status = mysql_next_result(host->db)) >= 0) { - if (next_res_status > 0) { - SET_ERROR_AND_WARN_ONCE(query_error, - "mysql query failed (mysql_next_result): %s", - mysql_error(host->db)); - } + do { + MYSQL_RES *temp_result; /* - * The result must not contain data. + * Keep the first result set. Reject multiple result sets. */ - else if ((temp_res = mysql_store_result(host->db)) != 0) { - SET_ERROR_AND_WARN_ONCE(query_error, - "mysql query failed: multiple result sets " - "returning data are not supported"); - mysql_free_result(temp_res); + if ((temp_result = mysql_store_result(host->db)) != 0) { + if (first_result == 0) { + first_result = temp_result; + } else { + SET_ERROR_AND_WARN_ONCE(query_error, + "%s:%s: query failed: multiple result sets " + "returning data are not supported", + dict_mysql->dict.type, + dict_mysql->dict.name); + mysql_free_result(temp_result); + } } /* - * There must be no errors: mysql_field_count() must return 0 - * to indicate that the "no data" result was expected. + * No result: the mysql_field_count() function must return 0 + * to indicate that mysql_store_result() completed normally. */ else if (mysql_field_count(host->db) != 0) { SET_ERROR_AND_WARN_ONCE(query_error, - "mysql query failed (mysql_field_count): %s", + "%s:%s: query failed (mysql_store_result): %s", + dict_mysql->dict.type, + dict_mysql->dict.name, mysql_error(host->db)); } + + /* + * Are there more results? -1 = no, 0 = yes, > 0 = error. + */ + if ((next_res_status = mysql_next_result(host->db)) > 0) { + SET_ERROR_AND_WARN_ONCE(query_error, + "%s:%s: query failed (mysql_next_result): %s", + dict_mysql->dict.type, + dict_mysql->dict.name, + mysql_error(host->db)); + } + } while (next_res_status == 0); + + /* + * Enforce the require_result_set setting. + */ + if (first_result == 0 && dict_mysql->require_result_set) { + SET_ERROR_AND_WARN_ONCE(query_error, + "%s:%s: query failed: query returned no result set" + "(require_result_set = yes)", + dict_mysql->dict.type, + dict_mysql->dict.name); } } @@ -609,13 +617,14 @@ static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql, plmysql_down_host(host); if (errno == 0) errno = ENOTSUP; - if (res) { - mysql_free_result(res); - res = 0; + if (first_result) { + mysql_free_result(first_result); + first_result = 0; } } else { if (msg_verbose) - msg_info("dict_mysql: successful query result from host %s", + msg_info("%s:%s: successful query result from host %s", + dict_mysql->dict.type, dict_mysql->dict.name, host->hostname); event_request_timer(dict_mysql_event, (void *) host, IDLE_CONN_INTV); @@ -623,7 +632,8 @@ static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql, } } - return res; + *result = first_result; + return (query_error == 0); } /* @@ -717,6 +727,7 @@ static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf) dict_mysql->tls_verify_cert = cfg_get_bool(p, "tls_verify_cert", -1); #endif #endif + dict_mysql->require_result_set = cfg_get_bool(p, "require_result_set", 1); /* * XXX: The default should be non-zero for safety, but that is not diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 8044ed629..94b72fc52 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20161225" +#define MAIL_RELEASE_DATE "20161226" #define MAIL_VERSION_NUMBER "3.2" #ifdef SNAPSHOT diff --git a/postfix/src/postconf/postconf_dbms.c b/postfix/src/postconf/postconf_dbms.c index 2d1ab5c76..5e3aca2e3 100644 --- a/postfix/src/postconf/postconf_dbms.c +++ b/postfix/src/postconf/postconf_dbms.c @@ -100,8 +100,8 @@ static const char *pcf_ldap_suffixes[] = { static const char *pcf_mysql_suffixes[] = { "additional_conditions", "dbname", "domain", "expansion_limit", - "hosts", "password", "query", "result_format", "select_field", - "table", "user", "where_field", 0, + "hosts", "password", "query", "result_format", "require_result_set", + "select_field", "table", "user", "where_field", 0, }; /* See pgsql_table(5). */