Wietse:
> Before I forget, did you verify that:
>
> - The 'old' code reproduces the problem (postmap -q fails to look
>   up a database key that contains UTF8) and the 'new' code fixes it.

John Fawcett:
> The fix is not specifically about utf8 lookups, but for the issue
> that Postfix was not reading the default mysql options file.
> This is as per mysql documentation. I can confirm that from
> my own tests on unpatched Postfix the default mysql options file
> was not being read.

Summary
=======
I think I found a libmysqlclient bug. According to documentation:

    The [client] option group is read by all client programs (but
    not by mysqld). This enables you to specify options that apply
    to all clients.

The analysis below shows that in reality, the mysql_real_connect()
function does not read options unless the application has specified
1) an option file name or 2) an option group name. Otherwise it won't
even read the default options file.

Step 1) is the original poster's workaround in this thread, and 2)
is what your patch does.

Details
=======
I was intrigued because your only Postfix code change was this:

-    dict_mysql->option_group = cfg_get_str(p, "option_group", NULL, 0, 0);
+    dict_mysql->option_group = cfg_get_str(p, "option_group", "client", 0, 0);

Here the old Postfix option_group default value was NULL, and your
new default value is "client".

Since with your patch the default Postfix option_group value is no
longer NULL, the Postfix mysql client no longer skips the following
call that passes the option_group name to the libmysqlclient library:

    if (dict_mysql->option_group)
        mysql_options(host->db, MYSQL_READ_DEFAULT_GROUP,
                      dict_mysql->option_group);

Why does the patch "fix" the problem? Here is what happens when
mysql_options() is called to pass the non-NULL options group name.

    int STDCALL
    mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
    {
        ...
      switch (option) {
        ...
      case MYSQL_READ_DEFAULT_GROUP:
        my_free(mysql->options.my_cnf_group);
        mysql->options.my_cnf_group=my_strdup(arg,MYF(MY_WME));
        break;
        ...

And how is mysql->options.my_cnf_group used? Here is the relevant
part of the mysql_real_connect() library function.

    MYSQL * STDCALL 
    CLI_MYSQL_REAL_CONNECT(MYSQL *mysql, ... other args...
    {
      /* use default options */
BUG->>if (mysql->options.my_cnf_file || mysql->options.my_cnf_group)<<-BUG
      {
        mysql_read_default_options(&mysql->options,
                                   (mysql->options.my_cnf_file ?
                                    mysql->options.my_cnf_file : "my"),
                                   mysql->options.my_cnf_group);

As indicated, mysql_real_connect() reads no option unless the
application has specified an explicit options group name or options
file name.

The fix is obvious: remove the test indicated above. Then,
mysql_real_connect() will behave as promised in documentation: it
will always call mysql_read_default_options(), and therefore it
will always read the "client" options group as promised.

For completeness, here's the relevant part of mysql_read_default_options():

    void mysql_read_default_options(struct st_mysql_options *options,
                                    const char *filename,const char *group)
    {
        ...
      groups[0]= (char*) "client"; groups[1]= (char*) group; groups[2]=0;

      my_load_defaults(filename, groups, &argc, &argv, NULL);

The code suggests that the library reads the "client" options group
and any group that was specified by the application (Postfix). But
mysql_read_default_options() is never called unless the application
has called mysql_options() with an options file name or options
group name. Otherwise it would not even read the client options
group, in contradiction with the documentation.

        Wietse

Reply via email to