Skip to content

Commit

Permalink
Fix broken DatabaseMetaData.getColumns() on older server versions.
Browse files Browse the repository at this point in the history
On older versions of MariaDB and MySQL, DatabaseMetaData.getColumns() will fail with the error "Unknown column 'DATETIME_PRECISION' in 'field list'". This is due to the DATETIME_PRECISION column not being present in the INFORMATION_SCHEMA.COLUMNS table. This column was introduced as part of the introduction of microsecond precision in TIME/DATETIME/TIMESTAMP types, in MariaDB 5.3.0 (I believe) and MySQL 5.6.4.

The proposed fix adjusts the metadata query for the relevant older server versions. The behavior is as if the DATETIME_PRECISION field had a value of 0, which is consistent with the fact that these older database versions did not support any fractional seconds digits.

Similar conditional logic already exist in the MySQL project's JDBC driver.
  • Loading branch information
eirikbakke committed Jan 11, 2023
1 parent 8ae1292 commit 38615eb
Showing 1 changed file with 55 additions and 37 deletions.
92 changes: 55 additions & 37 deletions src/main/java/org/mariadb/jdbc/DatabaseMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.text.ParseException;
import java.util.*;
import org.mariadb.jdbc.client.DataType;
import org.mariadb.jdbc.client.ServerVersion;
import org.mariadb.jdbc.client.result.CompleteResult;
import org.mariadb.jdbc.client.result.Result;
import org.mariadb.jdbc.util.VersionFactory;
Expand Down Expand Up @@ -823,43 +824,60 @@ public ResultSet getColumns(
String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern)
throws SQLException {

StringBuilder sb =
new StringBuilder(
"SELECT TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM, TABLE_NAME, COLUMN_NAME,"
+ dataTypeClause("COLUMN_TYPE")
+ " DATA_TYPE,"
+ DataTypeClause(conf)
+ " TYPE_NAME, "
+ " CASE DATA_TYPE"
+ " WHEN 'time' THEN "
+ "IF(DATETIME_PRECISION = 0, 10, CAST(11 + DATETIME_PRECISION as signed integer))"
+ " WHEN 'date' THEN 10"
+ " WHEN 'datetime' THEN "
+ "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))"
+ " WHEN 'timestamp' THEN "
+ "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))"
+ (conf.yearIsDateType() ? "" : " WHEN 'year' THEN 5")
+ " ELSE "
+ " IF(NUMERIC_PRECISION IS NULL, LEAST(CHARACTER_MAXIMUM_LENGTH,"
+ Integer.MAX_VALUE
+ "), NUMERIC_PRECISION) "
+ " END"
+ " COLUMN_SIZE, 65535 BUFFER_LENGTH, "
+ " CONVERT (CASE DATA_TYPE"
+ " WHEN 'year' THEN "
+ (conf.yearIsDateType() ? "NUMERIC_SCALE" : "0")
+ " WHEN 'tinyint' THEN "
+ (conf.tinyInt1isBit() ? "0" : "NUMERIC_SCALE")
+ " ELSE NUMERIC_SCALE END, UNSIGNED INTEGER) DECIMAL_DIGITS,"
+ " 10 NUM_PREC_RADIX, IF(IS_NULLABLE = 'yes',1,0) NULLABLE,COLUMN_COMMENT REMARKS,"
+ " COLUMN_DEFAULT COLUMN_DEF, 0 SQL_DATA_TYPE, 0 SQL_DATETIME_SUB, "
+ " LEAST(CHARACTER_OCTET_LENGTH,"
+ Integer.MAX_VALUE
+ ") CHAR_OCTET_LENGTH,"
+ " ORDINAL_POSITION, IS_NULLABLE, NULL SCOPE_CATALOG, NULL SCOPE_SCHEMA, NULL SCOPE_TABLE, NULL SOURCE_DATA_TYPE,"
+ " IF(EXTRA = 'auto_increment','YES','NO') IS_AUTOINCREMENT, "
+ " IF(EXTRA in ('VIRTUAL', 'PERSISTENT', 'VIRTUAL GENERATED', 'STORED GENERATED') ,'YES','NO') IS_GENERATEDCOLUMN "
+ " FROM INFORMATION_SCHEMA.COLUMNS");
ServerVersion version = connection.getContext().getVersion();
boolean supportsFractionalSeconds =
version.isMariaDBServer()
/* "In MariaDB 5.3 and later, the TIME, DATETIME, and TIMESTAMP types, along with the temporal
functions, CAST and dynamic columns, now support microseconds."
https://web.archive.org/web/20130928042640/https://mariadb.com/kb/en/microseconds-in-mariadb/
*/
? version.versionGreaterOrEqual(5, 3, 0)
// See https://dev.mysql.com/doc/relnotes/mysql/5.6/en/news-5-6-4.html
: version.versionGreaterOrEqual(5, 6, 4);
StringBuilder sb = new StringBuilder();
sb.append(
"SELECT TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM, TABLE_NAME, COLUMN_NAME,"
+ dataTypeClause("COLUMN_TYPE")
+ " DATA_TYPE,"
+ DataTypeClause(conf)
+ " TYPE_NAME, "
+ " CASE DATA_TYPE"
+ " WHEN 'date' THEN 10");
if (supportsFractionalSeconds) {
sb.append(
" WHEN 'time' THEN "
+ "IF(DATETIME_PRECISION = 0, 10, CAST(11 + DATETIME_PRECISION as signed integer))"
+ " WHEN 'datetime' THEN "
+ "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))"
+ " WHEN 'timestamp' THEN "
+ "IF(DATETIME_PRECISION = 0, 19, CAST(20 + DATETIME_PRECISION as signed integer))");
} else {
// Older versions do not include the DATETIME_PRECISION column in INFORMATION_SCHEMA.COLUMNS.
sb.append(" WHEN 'time' THEN 10 WHEN 'datetime' THEN 19 WHEN 'timestamp' THEN 19");
}
sb.append(
(conf.yearIsDateType() ? "" : " WHEN 'year' THEN 5")
+ " ELSE "
+ " IF(NUMERIC_PRECISION IS NULL, LEAST(CHARACTER_MAXIMUM_LENGTH,"
+ Integer.MAX_VALUE
+ "), NUMERIC_PRECISION) "
+ " END"
+ " COLUMN_SIZE, 65535 BUFFER_LENGTH, "
+ " CONVERT (CASE DATA_TYPE"
+ " WHEN 'year' THEN "
+ (conf.yearIsDateType() ? "NUMERIC_SCALE" : "0")
+ " WHEN 'tinyint' THEN "
+ (conf.tinyInt1isBit() ? "0" : "NUMERIC_SCALE")
+ " ELSE NUMERIC_SCALE END, UNSIGNED INTEGER) DECIMAL_DIGITS,"
+ " 10 NUM_PREC_RADIX, IF(IS_NULLABLE = 'yes',1,0) NULLABLE,COLUMN_COMMENT REMARKS,"
+ " COLUMN_DEFAULT COLUMN_DEF, 0 SQL_DATA_TYPE, 0 SQL_DATETIME_SUB, "
+ " LEAST(CHARACTER_OCTET_LENGTH,"
+ Integer.MAX_VALUE
+ ") CHAR_OCTET_LENGTH,"
+ " ORDINAL_POSITION, IS_NULLABLE, NULL SCOPE_CATALOG, NULL SCOPE_SCHEMA, NULL SCOPE_TABLE, NULL SOURCE_DATA_TYPE,"
+ " IF(EXTRA = 'auto_increment','YES','NO') IS_AUTOINCREMENT, "
+ " IF(EXTRA in ('VIRTUAL', 'PERSISTENT', 'VIRTUAL GENERATED', 'STORED GENERATED') ,'YES','NO') IS_GENERATEDCOLUMN "
+ " FROM INFORMATION_SCHEMA.COLUMNS");
boolean firstCondition = catalogCond(true, sb, "TABLE_SCHEMA", catalog);
firstCondition = patternCond(firstCondition, sb, "TABLE_NAME", tableNamePattern);
firstCondition = patternCond(firstCondition, sb, "COLUMN_NAME", columnNamePattern);
Expand Down

0 comments on commit 38615eb

Please sign in to comment.