Mercurial > hg > monetdb-java
changeset 667:ed3d0c1a60cf
Corrected PreparedStatement methods getMetaData().getColumnDisplaySize(), getMetaData().getPrecision() and getParameterMetaData().getPrecision().
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 27 Oct 2022 21:29:15 +0200 (2022-10-27) |
parents | 2448ce017593 |
children | 1d616f378ffc |
files | ChangeLog src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java tests/JDBC_API_Tester.java |
diffstat | 3 files changed, 197 insertions(+), 67 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ # ChangeLog file for monetdb-java # This file is updated with Maddlog +* Thu Oct 27 2022 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> +- Corrected PreparedStatement methods getMetaData().getColumnDisplaySize(), + getMetaData().getPrecision() and getParameterMetaData().getPrecision(). + * Thu Oct 20 2022 Martin van Dinther <martin.van.dinther@monetdbsolutions.com> - Improved implementation of method ResultSetMetaData.getPrecision(). It now returns more accurate values for columns of type DECIMAL, NUMERIC,
--- a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java +++ b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java @@ -376,7 +376,7 @@ public class MonetPreparedStatement * query call to pull the IS_AUTOINCREMENT value for this column. * See also ResultSetMetaData.isAutoIncrement() */ - // For now we simply allways return false. + // For now we simply always return false. return false; } @@ -384,7 +384,7 @@ public class MonetPreparedStatement * Indicates whether a column's case matters. * * @param column the first column is 1, the second is 2, ... - * @return false + * @return true for all character string columns (except inet and uuid) else false */ @Override public boolean isCaseSensitive(final int column) throws SQLException { @@ -395,7 +395,7 @@ public class MonetPreparedStatement return true; case Types.VARCHAR: final String monettype = getColumnTypeName(column); - if (monettype != null) { + if (monettype != null && monettype.length() == 4) { // data of type inet or uuid is not case sensitive if ("inet".equals(monettype) || "uuid".equals(monettype)) @@ -448,18 +448,19 @@ public class MonetPreparedStatement public boolean isSigned(final int column) throws SQLException { // we can hardcode this, based on the colum type switch (getColumnType(column)) { - case Types.NUMERIC: - case Types.DECIMAL: case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.REAL: case Types.FLOAT: case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: return true; case Types.BIGINT: final String monettype = getColumnTypeName(column); - if (monettype != null) { + if (monettype != null && monettype.length() == 3) { + // data of type oid or ptr is not signed if ("oid".equals(monettype) || "ptr".equals(monettype)) return false; @@ -467,10 +468,10 @@ public class MonetPreparedStatement return true; // All other types should return false // case Types.BOOLEAN: - // case Types.DATE: - // case Types.TIME: + // case Types.DATE: // can year be negative? + // case Types.TIME: // can time be negative? // case Types.TIME_WITH_TIMEZONE: - // case Types.TIMESTAMP: + // case Types.TIMESTAMP: // can year be negative? // case Types.TIMESTAMP_WITH_TIMEZONE: default: return false; @@ -488,15 +489,11 @@ public class MonetPreparedStatement */ @Override public int getColumnDisplaySize(final int column) throws SQLException { - try { - return digits[getColumnIdx(column)]; - } catch (IndexOutOfBoundsException e) { - throw MonetResultSet.newSQLInvalidColumnIndexException(column); - } + return getPrecision(column); } /** - * Get the designated column's table's schema. + * Get the designated column's schema name. * * @param column the first column is 1, the second is 2, ... * @return schema name or "" if not applicable @@ -527,10 +524,15 @@ public class MonetPreparedStatement } /** - * Get the designated column's number of decimal digits. - * This method is currently very expensive as it needs to - * retrieve the information from the database using an SQL - * query. + * Get the designated column's specified column size. + * For numeric data, this is the maximum precision. + * For character data, this is the length in characters. + * For datetime datatypes, this is the length in characters + * of the String representation (assuming the maximum + * allowed precision of the fractional seconds component). + * For binary data, this is the length in bytes. + * For the ROWID datatype, this is the length in bytes. + * 0 is returned for data types where the column size is not applicable. * * @param column the first column is 1, the second is 2, ... * @return precision @@ -538,18 +540,70 @@ public class MonetPreparedStatement */ @Override public int getPrecision(final int column) throws SQLException { - try { - return digits[getColumnIdx(column)]; - } catch (IndexOutOfBoundsException e) { - throw MonetResultSet.newSQLInvalidColumnIndexException(column); + switch (getColumnType(column)) { + case Types.BIGINT: + return 19; + case Types.INTEGER: + return 10; + case Types.SMALLINT: + return 5; + case Types.TINYINT: + return 3; + case Types.REAL: + return 7; + case Types.FLOAT: + case Types.DOUBLE: + return 15; + case Types.DECIMAL: + case Types.NUMERIC: + // these data types have a variable precision (max precision is 38) + try { + return digits[getColumnIdx(column)]; + } catch (IndexOutOfBoundsException e) { + throw MonetResultSet.newSQLInvalidColumnIndexException(column); + } + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness + case Types.CLOB: + // these data types have a variable length + try { + return digits[getColumnIdx(column)]; + } catch (IndexOutOfBoundsException e) { + throw MonetResultSet.newSQLInvalidColumnIndexException(column); + } + case Types.BINARY: + case Types.VARBINARY: + case Types.BLOB: + // these data types have a variable length + // It expect number of bytes, not number of hex chars + try { + return digits[getColumnIdx(column)]; + } catch (IndexOutOfBoundsException e) { + throw MonetResultSet.newSQLInvalidColumnIndexException(column); + } + case Types.DATE: + return 10; // 2020-10-08 + case Types.TIME: + return 15; // 21:51:34.399753 + case Types.TIME_WITH_TIMEZONE: + return 21; // 21:51:34.399753+02:00 + case Types.TIMESTAMP: + return 26; // 2020-10-08 21:51:34.399753 + case Types.TIMESTAMP_WITH_TIMEZONE: + return 32; // 2020-10-08 21:51:34.399753+02:00 + case Types.BOOLEAN: + return 1; + default: + // All other types should return 0 + return 0; } } /** * Gets the designated column's number of digits to right of - * the decimal point. This method is currently very - * expensive as it needs to retrieve the information from - * the database using an SQL query. + * the decimal point. + * 0 is returned for data types where the scale is not applicable. * * @param column the first column is 1, the second is 2, ... * @return scale @@ -565,17 +619,19 @@ public class MonetPreparedStatement } /** - * Indicates the nullability of values in the designated - * column. This method is currently very expensive as it - * needs to retrieve the information from the database using - * an SQL query. + * Indicates the nullability of values in the designated column. * * @param column the first column is 1, the second is 2, ... - * @return nullability + * @return columnNullableUnknown * @throws SQLException if a database access error occurs */ @Override public int isNullable(final int column) throws SQLException { + /* TODO: This requires an expensive dbmd.getColumns(null, schema, table, column) + * query call to pull the NULLABLE value for this column. + * See also ResultSetMetaData.isNullable(() + */ + // For now we simply always return columnNullableUnknown. return columnNullableUnknown; } @@ -589,7 +645,7 @@ public class MonetPreparedStatement */ @Override public String getCatalogName(final int column) throws SQLException { - return null; // MonetDB does NOT support catalogs + return null; // MonetDB does NOT support catalog qualifiers } /** @@ -646,21 +702,25 @@ public class MonetPreparedStatement */ @Override public String getColumnClassName(final int column) throws SQLException { - final String typeName = getColumnTypeName(column); + final String MonetDBType = getColumnTypeName(column); final Map<String,Class<?>> map = getConnection().getTypeMap(); final Class<?> c; - if (map.containsKey(typeName)) { - c = (Class)map.get(typeName); + if (map != null && map.containsKey(MonetDBType)) { + c = (Class)map.get(MonetDBType); } else { c = MonetResultSet.getClassForType(getColumnType(column)); } - return c.getName(); + if (c != null) + return c.getCanonicalName(); + throw new SQLException("column type mapping null: " + MonetDBType, "M0M03"); } /** * Gets the designated column's suggested title for use in - * printouts and displays. This is currently equal to - * getColumnName(). + * printouts and displays. The suggested title is usually + * specified by the SQL AS clause. If a SQL AS is not specified, + * the value returned from getColumnLabel will be the same as + * the value returned by the getColumnName method. * * @param column the first column is 1, the second is 2, ... * @return the suggested column title @@ -674,16 +734,16 @@ public class MonetPreparedStatement /** * Gets the designated column's name * - * @param colnr column number, the first column is 1, the second is 2, ... + * @param columnr the first column is 1, the second is 2, ... * @return the column name * @throws SQLException if there is no such column */ @Override - public String getColumnName(final int colnr) throws SQLException { + public String getColumnName(final int columnr) throws SQLException { try { - return column[getColumnIdx(colnr)]; + return column[getColumnIdx(columnr)]; } catch (IndexOutOfBoundsException e) { - throw MonetResultSet.newSQLInvalidColumnIndexException(colnr); + throw MonetResultSet.newSQLInvalidColumnIndexException(columnr); } } @@ -778,18 +838,19 @@ public class MonetPreparedStatement public boolean isSigned(final int param) throws SQLException { // we can hardcode this, based on the colum type switch (getParameterType(param)) { - case Types.NUMERIC: - case Types.DECIMAL: case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.REAL: case Types.FLOAT: case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: return true; case Types.BIGINT: final String monettype = getParameterTypeName(param); - if (monettype != null) { + if (monettype != null && monettype.length() == 3) { + // data of type oid or ptr is not signed if ("oid".equals(monettype) || "ptr".equals(monettype)) return false; @@ -797,10 +858,10 @@ public class MonetPreparedStatement return true; // All other types should return false // case Types.BOOLEAN: - // case Types.DATE: - // case Types.TIME: + // case Types.DATE: // can year be negative? + // case Types.TIME: // can time be negative? // case Types.TIME_WITH_TIMEZONE: - // case Types.TIMESTAMP: + // case Types.TIMESTAMP: // can year be negative? // case Types.TIMESTAMP_WITH_TIMEZONE: default: return false; @@ -808,8 +869,17 @@ public class MonetPreparedStatement } /** - * Retrieves the designated parameter's number of decimal - * digits. + * Retrieves the designated parameter's specified column size. + * The returned value represents the maximum column size for + * the given parameter. + * For numeric data, this is the maximum precision. + * For character data, this is the length in characters. + * For datetime datatypes, this is the length in characters + * of the String representation (assuming the maximum allowed + * precision of the fractional seconds component). + * For binary data, this is the length in bytes. + * For the ROWID datatype, this is the length in bytes. + * 0 is returned for data types where the column size is not applicable. * * @param param the first parameter is 1, the second is 2, ... * @return precision @@ -817,16 +887,70 @@ public class MonetPreparedStatement */ @Override public int getPrecision(final int param) throws SQLException { - try { - return digits[getParamIdx(param)]; - } catch (IndexOutOfBoundsException e) { - throw newSQLInvalidParameterIndexException(param); + switch (getParameterType(param)) { + case Types.BIGINT: + return 19; + case Types.INTEGER: + return 10; + case Types.SMALLINT: + return 5; + case Types.TINYINT: + return 3; + case Types.REAL: + return 7; + case Types.FLOAT: + case Types.DOUBLE: + return 15; + case Types.DECIMAL: + case Types.NUMERIC: + // these data types have a variable precision (max precision is 38) + try { + return digits[getParamIdx(param)]; + } catch (IndexOutOfBoundsException e) { + throw newSQLInvalidParameterIndexException(param); + } + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness + case Types.CLOB: + // these data types have a variable length + try { + return digits[getParamIdx(param)]; + } catch (IndexOutOfBoundsException e) { + throw newSQLInvalidParameterIndexException(param); + } + case Types.BINARY: + case Types.VARBINARY: + case Types.BLOB: + // these data types have a variable length + // It expect number of bytes, not number of hex chars + try { + return digits[getParamIdx(param)]; + } catch (IndexOutOfBoundsException e) { + throw newSQLInvalidParameterIndexException(param); + } + case Types.DATE: + return 10; // 2020-10-08 + case Types.TIME: + return 15; // 21:51:34.399753 + case Types.TIME_WITH_TIMEZONE: + return 21; // 21:51:34.399753+02:00 + case Types.TIMESTAMP: + return 26; // 2020-10-08 21:51:34.399753 + case Types.TIMESTAMP_WITH_TIMEZONE: + return 32; // 2020-10-08 21:51:34.399753+02:00 + case Types.BOOLEAN: + return 1; + default: + // All other types should return 0 + return 0; } } /** * Retrieves the designated parameter's number of digits to * right of the decimal point. + * 0 is returned for data types where the scale is not applicable. * * @param param the first parameter is 1, the second is 2, ... * @return scale @@ -891,15 +1015,17 @@ public class MonetPreparedStatement */ @Override public String getParameterClassName(final int param) throws SQLException { - final String typeName = getParameterTypeName(param); + final String MonetDBType = getParameterTypeName(param); final Map<String,Class<?>> map = getConnection().getTypeMap(); final Class<?> c; - if (map.containsKey(typeName)) { - c = (Class)map.get(typeName); + if (map != null && map.containsKey(MonetDBType)) { + c = (Class)map.get(MonetDBType); } else { c = MonetResultSet.getClassForType(getParameterType(param)); } - return c.getName(); + if (c != null) + return c.getCanonicalName(); + throw new SQLException("column type mapping null: " + MonetDBType, "M0M03"); } /**
--- a/tests/JDBC_API_Tester.java +++ b/tests/JDBC_API_Tester.java @@ -2030,12 +2030,12 @@ final public class JDBC_API_Tester { "rsmd. 6 columns:\n" + "RCol 1\n" + " classname java.lang.Short\n" + - " displaysize 8\n" + + " displaysize 3\n" + " label intnull\n" + " name intnull\n" + " type -6\n" + " typename tinyint\n" + - " precision 8\n" + + " precision 3\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + @@ -2051,12 +2051,12 @@ final public class JDBC_API_Tester { " writable false\n" + "RCol 2\n" + " classname java.lang.Integer\n" + - " displaysize 32\n" + + " displaysize 10\n" + " label myint\n" + " name myint\n" + " type 4\n" + " typename int\n" + - " precision 32\n" + + " precision 10\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + @@ -2072,12 +2072,12 @@ final public class JDBC_API_Tester { " writable false\n" + "RCol 3\n" + " classname java.lang.Double\n" + - " displaysize 53\n" + + " displaysize 15\n" + " label mydouble\n" + " name mydouble\n" + " type 8\n" + " typename double\n" + - " precision 53\n" + + " precision 15\n" + " scale 0\n" + " catalogname null\n" + " schemaname \n" + @@ -2158,7 +2158,7 @@ final public class JDBC_API_Tester { "Param 1\n" + " nullable 2 (UNKNOWN)\n" + " signed true\n" + - " precision 32\n" + + " precision 10\n" + " scale 0\n" + " type 4\n" + " typename int\n" + @@ -2853,7 +2853,7 @@ final public class JDBC_API_Tester { "Param 1\n" + " nullable 2 (UNKNOWN)\n" + " signed true\n" + - " precision 32\n" + + " precision 10\n" + " scale 0\n" + " type 4\n" + " typename int\n" + @@ -2862,7 +2862,7 @@ final public class JDBC_API_Tester { "Param 2\n" + " nullable 2 (UNKNOWN)\n" + " signed true\n" + - " precision 53\n" + + " precision 15\n" + " scale 0\n" + " type 8\n" + " typename double\n" +