Fresh install giving unusual "Unexpected datatype NULL"

Before posting this, I just had an idea… I’m running the OpenJDK:
$ java --version
openjdk 11.0.13 2021-10-19
OpenJDK Runtime Environment 18.9 (build 11.0.13+8)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8, mixed mode, sharing)

I’ve a strong hunch installing the Oracle JDK will fix this problem, so I’ll go try that now, but here’s my problem report anyway…

I’ve had to reinstall a Fedora system (now on F35) but copied my old workspace files so it’s a carbon copy of the Liquibase installation I had previously, but a fresh install of the MariaDB database and JDBC drivers being used. I’m now seeing an unexpected error at what seems to be just after the database lock is released.

INFO: Successfully released change log lock
Unexpected error running Liquibase: Unexpected datatype NULL
For more information, please use the --logLevel flag
Nov 18, 2021 4:13:20 PM liquibase.integration
SEVERE: Unexpected error running Liquibase: Unexpected datatype NULL
liquibase.exception.LiquibaseException: java.lang.IllegalArgumentException: Unexpected datatype NULL
at liquibase.Liquibase.runInScope(Liquibase.java:2409)
at liquibase.Liquibase.update(Liquibase.java:211)
at liquibase.Liquibase.update(Liquibase.java:197)
at liquibase.integration.commandline.Main.doMigration(Main.java:1875)
at liquibase.integration.commandline.Main$1.lambda$run$0(Main.java:397)
at liquibase.Scope.lambda$child$0(Scope.java:177)
at liquibase.Scope.child(Scope.java:186)
at liquibase.Scope.child(Scope.java:176)
at liquibase.Scope.child(Scope.java:155)
at liquibase.integration.commandline.Main$1.run(Main.java:396)
at liquibase.integration.commandline.Main$1.run(Main.java:220)
at liquibase.Scope.child(Scope.java:186)
at liquibase.Scope.child(Scope.java:162)
at liquibase.integration.commandline.Main.run(Main.java:220)
at liquibase.integration.commandline.Main.main(Main.java:163)
Caused by: java.lang.IllegalArgumentException: Unexpected datatype NULL
at org.mariadb.jdbc.message.server.ColumnDefinitionPacket.getDefaultCodec(ColumnDefinitionPacket.java:371)
at org.mariadb.jdbc.client.result.Result.getObject(Result.java:463)
at liquibase.util.JdbcUtil.getResultSetValue(JdbcUtil.java:80)
at liquibase.executor.jvm.ColumnMapRowMapper.getColumnValue(ColumnMapRowMapper.java:72)
at liquibase.snapshot.ResultSetCache$ResultSetExtractor$1.getColumnValue(ResultSetCache.java:320)
at liquibase.executor.jvm.ColumnMapRowMapper.mapRow(ColumnMapRowMapper.java:34)
at liquibase.executor.jvm.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:72)
at liquibase.snapshot.ResultSetCache$ResultSetExtractor.extract(ResultSetCache.java:343)
at liquibase.snapshot.ResultSetCache$ResultSetExtractor.extract(ResultSetCache.java:308)
at liquibase.snapshot.JdbcDatabaseSnapshot$CachingDatabaseMetaData$2.fastFetchQuery(JdbcDatabaseSnapshot.java:1011)
at liquibase.snapshot.ResultSetCache$SingleResultSetExtractor.fastFetch(ResultSetCache.java:367)
at liquibase.snapshot.ResultSetCache.get(ResultSetCache.java:72)
at liquibase.snapshot.JdbcDatabaseSnapshot$CachingDatabaseMetaData.getTables(JdbcDatabaseSnapshot.java:968)
at liquibase.snapshot.jvm.TableSnapshotGenerator.snapshotObject(TableSnapshotGenerator.java:34)
at liquibase.snapshot.jvm.JdbcSnapshotGenerator.snapshot(JdbcSnapshotGenerator.java:65)
at liquibase.snapshot.SnapshotGeneratorChain.snapshot(SnapshotGeneratorChain.java:49)
at liquibase.snapshot.jvm.JdbcSnapshotGenerator.snapshot(JdbcSnapshotGenerator.java:68)
at liquibase.snapshot.SnapshotGeneratorChain.snapshot(SnapshotGeneratorChain.java:49)
at liquibase.snapshot.jvm.JdbcSnapshotGenerator.snapshot(JdbcSnapshotGenerator.java:68)
at liquibase.snapshot.SnapshotGeneratorChain.snapshot(SnapshotGeneratorChain.java:49)
at liquibase.snapshot.jvm.JdbcSnapshotGenerator.snapshot(JdbcSnapshotGenerator.java:68)
at liquibase.snapshot.SnapshotGeneratorChain.snapshot(SnapshotGeneratorChain.java:49)
at liquibase.snapshot.jvm.JdbcSnapshotGenerator.snapshot(JdbcSnapshotGenerator.java:68)
at liquibase.snapshot.SnapshotGeneratorChain.snapshot(SnapshotGeneratorChain.java:49)
at liquibase.snapshot.jvm.JdbcSnapshotGenerator.snapshot(JdbcSnapshotGenerator.java:68)
at liquibase.snapshot.SnapshotGeneratorChain.snapshot(SnapshotGeneratorChain.java:49)
at liquibase.snapshot.jvm.JdbcSnapshotGenerator.snapshot(JdbcSnapshotGenerator.java:68)
at liquibase.snapshot.SnapshotGeneratorChain.snapshot(SnapshotGeneratorChain.java:49)
at liquibase.snapshot.DatabaseSnapshot.include(DatabaseSnapshot.java:314)
at liquibase.snapshot.DatabaseSnapshot.init(DatabaseSnapshot.java:105)
at liquibase.snapshot.DatabaseSnapshot.(DatabaseSnapshot.java:58)
at liquibase.snapshot.JdbcDatabaseSnapshot.(JdbcDatabaseSnapshot.java:38)
at liquibase.snapshot.SnapshotGeneratorFactory.createSnapshot(SnapshotGeneratorFactory.java:215)
at liquibase.snapshot.SnapshotGeneratorFactory.createSnapshot(SnapshotGeneratorFactory.java:244)
at liquibase.snapshot.SnapshotGeneratorFactory.getDatabaseChangeLogTable(SnapshotGeneratorFactory.java:252)
at liquibase.changelog.StandardChangeLogHistoryService.init(StandardChangeLogHistoryService.java:109)
at liquibase.Liquibase.checkLiquibaseTables(Liquibase.java:1913)
at liquibase.Liquibase.lambda$update$1(Liquibase.java:226)
at liquibase.Scope.lambda$child$0(Scope.java:177)
at liquibase.Scope.child(Scope.java:186)
at liquibase.Scope.child(Scope.java:176)
at liquibase.Scope.child(Scope.java:155)
at liquibase.Liquibase.runInScope(Liquibase.java:2404)
… 14 more

A gist of the full output with logLevel=DEBUG: gist:05aedb73729f6ad7451e79d6c59a5571 · GitHub

My changelog.xml is fairly standard:

<?xml version="1.0" encoding="UTF-8"?>

<include file="one-of-100-files.xml"/>

and the properties file used is simple enough:

#liquibase.properties
driver: org.mariadb.jdbc.Driver
classpath: /usr/lib/java/mariadb-java-client.jar
url: jdbc:mariadb://127.0.0.1/premier2
username: uuu
password: ppp
parameter.table_prefix:

I can open an SQL connection to the database and copy/paste the database lock commands to create and release the lock just fine, and the website using the database is functioning normally, so I don’t think there’s a fault with the database.

There are exceptions to do with “Unable to get public no-arg constructor” but according to Puzzling liquibase error after upgrading to traccar 4.13, openjdk-11 - Traccar this is not an issue.

Where can I look for more debugging info please?

The Oracle JDK did not fix the problem. I think I traced it to the use of the mariadb-java-client-3.0.1-1 on my system. When I instead use mysql-connector-java-8.0.25-3 (and adjust the connection string from “jdbc:mariadb://…” to “jdbc:mysql://…”) the error goes away.

I’m running mariadb-server-10.5.12-1 on Fedora, why would the mariadb connector result in an exception when the mysql one does not?

While I was still stuck I tried getting the liquibase source and stepping through it. It seems the error is that the call to “getTables()” to fetch the schema of DATABASECHANGELOG returns a resultset whose first column is a value but all subsequent columns are null, this is given to extract() which ends up using liquibase.util.JdbcUtil.getResultSetValue() to call ResultSet.getObject() for the second field which throws IllegalArgumentException “Unexpected datatype NULL”. Because it’s not an SQLException, it’s not caught in the catch handler of getResultSetValue().

JdbcDatabaseSnapshot() has the lines:

                return extract(databaseMetaData.getTables(catalog, schema, ((table == null) ?
                    SQL_FILTER_MATCH_ALL : table), new String[]{"TABLE"}));

The actual metadata on the table is fetched by a class implemented in the JDBC driver, DatabaseMetaData.getTables(). The MariaDB ResultSet implementation has a ‘data’ member where the first element has a value but others are null, whereas the MySQL ResultSet object clearly has a different implementation.

I saw the actual SQL issued to the database for the “get table schema” call, something like this:

SELECT TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM, TABLE_NAME, IF(TABLE_TYPE=‘BASE TABLE’ or TABLE_TYPE=‘SYSTEM VERSIONED’, ‘TABLE’, TABLE_TYPE) as TABLE_TYPE, TABLE_COMMENT REMARKS, NULL TYPE_CAT, NULL TYPE_SCHEM, NULL TYPE_NAME, NULL SELF_REFERENCING_COL_NAME, NULL REF_GENERATION FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = ‘DATABASECHANGELOG’

The second result field is clearly hardcoded as a NULL value and I think it’s this that the exception happens on, inside getResultSetValue mentioned above). When I run this against the same database from SquirrelSQL using the same MariaDB connector it gives a sensible resultset.

It doesn’t seem right that a MariaDB connector ResultSet object would throw IllegalArgumentException “Unexpected datatype NULL” when getObject() is called. If the field at the index requested is null it should just return null. So, I’m a bit lost. I’ll stick to the mysql connector for now!