Bug 439733 - Akonadi server triggers assert() in libmysql 5.7.34
Summary: Akonadi server triggers assert() in libmysql 5.7.34
Status: RESOLVED UPSTREAM
Alias: None
Product: Akonadi
Classification: Frameworks and Libraries
Component: server (show other bugs)
Version: 5.17.3
Platform: Other FreeBSD
: NOR normal
Target Milestone: ---
Assignee: kdepim bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-07-10 19:50 UTC by groot
Modified: 2021-07-12 10:43 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description groot 2021-07-10 19:50:18 UTC
I've got Akonadi 5.17.2 -- that's the version corresponding to tag v21.04.2 so it's a bit weird there's no Bugzilla version for that. The underlying MySQL library is mysql-client 5.7.34. Starting akonadiserver (e.g. on the command line) produces a bunch of output about tables, and then:

```
org.kde.pim.akonadiserver: Running DB initializer
org.kde.pim.akonadiserver: DB initializer done
Connecting to deprecated signal QDBusConnectionInterface::serviceOwnerChanged(QString,QString,QString)
Assertion failed: (param->buffer_length != 0), function setup_one_fetch_function, file /wrkdirs/usr/ports/databases/mysql57-client/work/mysql-5.7.34/libmysql/libmysql.c, line 4112.
QSocketNotifier: Invalid socket 6 and type 'Read', disabling...
```

Akonadi is completely non-functional (because it doesn't start) with this mysql client-library version. Library version 5.7.32 does work.
Comment 1 groot 2021-07-10 21:15:01 UTC
With some additional debugging added, and a mutex lock on QueryBuilder::exec() so it is singlethreaded, I get this output (as an indication what Akonadi is building up):

```
org.kde.pim.akonadiserver: Prepared new query for "SELECT PartTable.id, PartTable.pimItemId, PartTable.partTypeId, PartTable.data, PartTable.datasize, PartTable.version, PartTable.storage FROM PartTable WHERE ( PartTable.partTypeId = :0 AND storage = :1 AND data IS NOT NULL )"
org.kde.pim.akonadiserver: Bound 0 QVariant(qlonglong, 1)
org.kde.pim.akonadiserver: Bound 1 QVariant(qlonglong, 1)
Assertion failed: (param->buffer_length != 0), function setup_one_fetch_function, file /wrkdirs/usr/ports/databases/mysql57-client/work/mysql-5.7.34/libmysql/libmysql.c, line 4112.
```

and this slightly-more-detailed backtrace:

```
#0  thr_kill () at thr_kill.S:4
#1  0x0000000800ff71b4 in __raise (s=s@entry=6)
    at /usr/src/src/lib/libc/gen/raise.c:52
#2  0x00000008010ac129 in abort () at /usr/src/src/lib/libc/stdlib/abort.c:67
#3  0x0000000800fda0c1 in __assert (func=<optimized out>, file=<optimized out>, 
    line=<optimized out>, failedexpr=<optimized out>)
    at /usr/src/src/lib/libc/gen/assert.c:51
#4  0x0000000805efeb77 in ?? () from /usr/local/lib/mysql/libmysqlclient.so.20
#5  0x0000000805efe5ce in mysql_stmt_bind_result ()
   from /usr/local/lib/mysql/libmysqlclient.so.20
#6  0x000000080325fe6f in ?? ()
   from /usr/local/lib/qt5/plugins/sqldrivers/libqsqlmysql.so
#7  0x0000000800756da7 in QSqlQuery::exec() ()
   from /usr/local/lib/qt5/libQt5Sql.so.5
#8  0x00000000003b9745 in Akonadi::Server::QueryBuilder::exec (
    this=<optimized out>)
    at /zbigone/src/kde/invent/akonadi/src/server/storage/querybuilder.cpp:418
#9  0x00000000003b4d86 in Akonadi::Server::PartHelper::remove (column=..., 
    value=...)
    at /zbigone/src/kde/invent/akonadi/src/server/storage/parthelper.cpp:108
#10 0x0000000000359fbb in Akonadi::Server::DataStore::unhideAllPimItems (
    this=<optimized out>)
    at /zbigone/src/kde/invent/akonadi/src/server/storage/datastore.cpp:1177
#11 0x00000000002749f1 in Akonadi::Server::AkonadiServer::init (
    this=0x7fffffffd158)
    at /zbigone/src/kde/invent/akonadi/src/server/akonadi.cpp:152
```
Comment 2 groot 2021-07-11 12:26:24 UTC
Relevant code change between 5.7.33 and 5.7.34 is in libmysql.c, line 4112:

=== 5.7.33
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_BIT:
    DBUG_ASSERT(param->buffer_length != 0);

=== 5.7.34
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_BIT:
    assert(param->buffer_length != 0);

As you can see the DBUG_ASSERT has turned into a "real" assert.

By modifying the Qt MySQL drivers, e.g. void QMYSQLResultPrivate::bindBlobs() I can get the Qt layer to confirm that the value being passed in is 0. Apparently max_length for the field that is causing problems here, is indeed 0. By nudging that to 1, like so:

```
if (!bind->buffer_length) { bind->buffer_length=1;
            qWarning("QMYSQLResultPrivate::bindBlobs: field max_length was 0");
}
```

Akonadi works again (because of the patch in Qt). I'm just not convinced that's a good fix: why is there a BLOB field of length 0, anyway?
Comment 3 groot 2021-07-11 18:34:51 UTC
I ended up patching the mysql driver in Qt as follows:

```
-        if (qIsBlob(inBinds[i].buffer_type) && meta && fieldInfo) {
+        if (qIsBlob(inBinds[i].buffer_type) && meta && fieldInfo && fieldInfo->max_length) {
```

It **seems** to do the trick for akonadi, at least.
Comment 4 groot 2021-07-12 10:43:42 UTC
Closing because
- the problem seems to live somewhere other than Akonadi itself (even if Akonadi triggers it)
- as "upstream" because the issue is in the Qt MySQL plugin
- the problem is patched downstream.