Skip to content

Commit

Permalink
feat(jdbc): DatabaseMetaData.getTypeInfo() returns more accurate values
Browse files Browse the repository at this point in the history
relates to #786
  • Loading branch information
gotson committed Sep 16, 2022
1 parent 8c78a66 commit fcb321e
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 49 deletions.
160 changes: 121 additions & 39 deletions src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.sql.Struct;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
Expand All @@ -22,6 +23,7 @@
import org.sqlite.SQLiteConnection;
import org.sqlite.core.CoreStatement;
import org.sqlite.jdbc3.JDBC3DatabaseMetaData.ImportedKeyFinder.ForeignKey;
import org.sqlite.util.QueryUtils;
import org.sqlite.util.StringUtils;

public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabaseMetaData {
Expand Down Expand Up @@ -1760,45 +1762,125 @@ public ResultSet getTableTypes() throws SQLException {
/** @see java.sql.DatabaseMetaData#getTypeInfo() */
public ResultSet getTypeInfo() throws SQLException {
if (getTypeInfo == null) {
getTypeInfo =
conn.prepareStatement(
"select "
+ "tn as TYPE_NAME, "
+ "dt as DATA_TYPE, "
+ "0 as PRECISION, "
+ "null as LITERAL_PREFIX, "
+ "null as LITERAL_SUFFIX, "
+ "null as CREATE_PARAMS, "
+ DatabaseMetaData.typeNullable
+ " as NULLABLE, "
+ "1 as CASE_SENSITIVE, "
+ DatabaseMetaData.typeSearchable
+ " as SEARCHABLE, "
+ "0 as UNSIGNED_ATTRIBUTE, "
+ "0 as FIXED_PREC_SCALE, "
+ "0 as AUTO_INCREMENT, "
+ "null as LOCAL_TYPE_NAME, "
+ "0 as MINIMUM_SCALE, "
+ "0 as MAXIMUM_SCALE, "
+ "0 as SQL_DATA_TYPE, "
+ "0 as SQL_DATETIME_SUB, "
+ "10 as NUM_PREC_RADIX from ("
+ " select 'BLOB' as tn, "
+ Types.BLOB
+ " as dt union"
+ " select 'NULL' as tn, "
+ Types.NULL
+ " as dt union"
+ " select 'REAL' as tn, "
+ Types.REAL
+ " as dt union"
+ " select 'TEXT' as tn, "
+ Types.VARCHAR
+ " as dt union"
+ " select 'INTEGER' as tn, "
+ Types.INTEGER
+ " as dt"
+ ") order by TYPE_NAME;");
String sql =
QueryUtils.valuesQuery(
Arrays.asList(
"TYPE_NAME",
"DATA_TYPE",
"PRECISION",
"LITERAL_PREFIX",
"LITERAL_SUFFIX",
"CREATE_PARAMS",
"NULLABLE",
"CASE_SENSITIVE",
"SEARCHABLE",
"UNSIGNED_ATTRIBUTE",
"FIXED_PREC_SCALE",
"AUTO_INCREMENT",
"LOCAL_TYPE_NAME",
"MINIMUM_SCALE",
"MAXIMUM_SCALE",
"SQL_DATA_TYPE",
"SQL_DATETIME_SUB",
"NUM_PREC_RADIX"),
Arrays.asList(
Arrays.asList(
"BLOB",
Types.BLOB,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
0,
DatabaseMetaData.typeSearchable,
1,
0,
0,
null,
0,
0,
0,
0,
10),
Arrays.asList(
"INTEGER",
Types.INTEGER,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
0,
DatabaseMetaData.typeSearchable,
0,
0,
1,
null,
0,
0,
0,
0,
10),
Arrays.asList(
"NULL",
Types.NULL,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
0,
DatabaseMetaData.typeSearchable,
1,
0,
0,
null,
0,
0,
0,
0,
10),
Arrays.asList(
"REAL",
Types.REAL,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
0,
DatabaseMetaData.typeSearchable,
0,
0,
0,
null,
0,
0,
0,
0,
10),
Arrays.asList(
"TEXT",
Types.VARCHAR,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
1,
DatabaseMetaData.typeSearchable,
1,
0,
0,
null,
0,
0,
0,
0,
10)))
+ " order by TYPE_NAME";
getTypeInfo = conn.prepareStatement(sql);
}

getTypeInfo.clearParameters();
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/org/sqlite/util/QueryUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.sqlite.util;

import java.util.List;
import java.util.stream.Collectors;

public class QueryUtils {
/**
* Build a SQLite query using the VALUES clause to return arbitrary values.
*
* @param columns list of column names
* @param valuesList values to return as rows
* @return SQL query as string
*/
public static String valuesQuery(List<String> columns, List<List<Object>> valuesList) {
valuesList.forEach(
(list) -> {
if (list.size() != columns.size())
throw new IllegalArgumentException(
"values and columns must have the same size");
});
return "with cte("
+ String.join(",", columns)
+ ") as (values "
+ valuesList.stream()
.map(
(values) ->
"("
+ values.stream()
.map(
(o -> {
if (o instanceof String)
return "'" + o + "'";
if (o == null) return "null";
return o.toString();
}))
.collect(Collectors.joining(","))
+ ")")
.collect(Collectors.joining(","))
+ ") select * from cte";
}
}
136 changes: 126 additions & 10 deletions src/test/java/org/sqlite/DBMetaDataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,135 @@ public void getTableTypes() throws SQLException {
public void getTypeInfo() throws SQLException {
ResultSet rs = meta.getTypeInfo();
assertThat(rs).isNotNull();
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TYPE_NAME")).isEqualTo("BLOB");
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TYPE_NAME")).isEqualTo("INTEGER");
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TYPE_NAME")).isEqualTo("NULL");
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TYPE_NAME")).isEqualTo("REAL");
assertThat(rs.next()).isTrue();
assertThat(rs.getString("TYPE_NAME")).isEqualTo("TEXT");

testTypeInfo(
rs,
"BLOB",
Types.BLOB,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
false,
DatabaseMetaData.typeSearchable,
true,
false,
false,
0,
0,
10);
testTypeInfo(
rs,
"INTEGER",
Types.INTEGER,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
false,
DatabaseMetaData.typeSearchable,
false,
false,
true,
0,
0,
10);
testTypeInfo(
rs,
"NULL",
Types.NULL,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
false,
DatabaseMetaData.typeSearchable,
true,
false,
false,
0,
0,
10);
testTypeInfo(
rs,
"REAL",
Types.REAL,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
false,
DatabaseMetaData.typeSearchable,
false,
false,
false,
0,
0,
10);
testTypeInfo(
rs,
"TEXT",
Types.VARCHAR,
0,
null,
null,
null,
DatabaseMetaData.typeNullable,
true,
DatabaseMetaData.typeSearchable,
true,
false,
false,
0,
0,
10);

assertThat(rs.next()).isFalse();
}

private void testTypeInfo(
ResultSet rs,
String name,
int type,
int precision,
String literalPrefix,
String literalSuffix,
String createParams,
int nullable,
boolean caseSensitive,
int searchable,
boolean unsigned,
boolean fixedPrecScale,
boolean autoIncrement,
int minScale,
int maxScale,
int radix)
throws SQLException {
assertThat(rs.next()).isTrue();
assertThat(rs.getString(1)).isEqualTo(name);
assertThat(rs.getInt(2)).isEqualTo(type);
assertThat(rs.getInt(3)).isEqualTo(precision);
assertThat(rs.getString(4)).isEqualTo(literalPrefix);
assertThat(rs.getString(5)).isEqualTo(literalSuffix);
assertThat(rs.getString(6)).isEqualTo(createParams);
assertThat(rs.getShort(7)).isEqualTo((short) nullable);
assertThat(rs.getBoolean(8)).isEqualTo(caseSensitive);
assertThat(rs.getShort(9)).isEqualTo((short) searchable);
assertThat(rs.getBoolean(10)).isEqualTo(unsigned);
assertThat(rs.getBoolean(11)).isEqualTo(fixedPrecScale);
assertThat(rs.getBoolean(12)).isEqualTo(autoIncrement);
assertThat(rs.getString(13)).isEqualTo(null);
assertThat(rs.getShort(14)).isEqualTo((short) minScale);
assertThat(rs.getShort(15)).isEqualTo((short) maxScale);
assertThat(rs.getInt(16)).isEqualTo(0);
assertThat(rs.getInt(17)).isEqualTo(0);
assertThat(rs.getInt(18)).isEqualTo(radix);
}

@Test
public void getColumns() throws SQLException {
ResultSet rs = meta.getColumns(null, null, "test", "id");
Expand Down

0 comments on commit fcb321e

Please sign in to comment.