changeset 383:19a5583a7b5f

Improved performance of ResultSetMetaData methods isAutoIncrement(), getPrecision() and getScale() significant for columns of specific data types as in some cases no costly meta data query is executed anymore.
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 08 Oct 2020 23:07:30 +0200 (2020-10-08)
parents 4f54264f29d7
children cc472ea19b3f
files ChangeLog src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
diffstat 2 files changed, 151 insertions(+), 90 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,9 @@
 # This file is updated with Maddlog
 
 * Thu Oct  8 2020 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
+- Improved performance of ResultSetMetaData methods isAutoIncrement(),
+  getPrecision() and getScale() significant for columns of specific data
+  types as in some cases no costly meta data query is executed anymore.
 - The connection properties  treat_clob_as_varchar  and  treat_blob_as_binary
   are now set to true by default within the JDBC driver.  This is done
   as it results by default in less memory usage, (much) faster response
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
@@ -1324,21 +1324,35 @@ public class MonetResultSet
 			/**
 			 * Indicates whether the designated column is automatically numbered.
 			 *
+			 * This method is currently very expensive for BIGINT,
+			 * INTEGER, SMALLINT and TINYINT result column types
+			 * as it needs to retrieve the information from the
+			 * database using an SQL meta data query.
+			 *
 			 * @param column the first column is 1, the second is 2, ...
 			 * @return true if so; false otherwise
 			 * @throws SQLException if a database access error occurs
 			 */
 			@Override
 			public boolean isAutoIncrement(final int column) throws SQLException {
-				checkColumnIndexValidity(column);
-				try {
-					if (_is_fetched[column] != true) {
-						fetchColumnInfo(column);
-					}
-					return _isAutoincrement[column];
-				} catch (IndexOutOfBoundsException e) {
-					throw MonetResultSet.newSQLInvalidColumnIndexException(column);
+				// only few integer types can be auto incrementable in MonetDB
+				// see: https://www.monetdb.org/Documentation/SQLReference/DataTypes/SerialDatatypes
+				switch (getColumnType(column)) {
+					case Types.BIGINT:
+					case Types.INTEGER:
+					case Types.SMALLINT:
+					case Types.TINYINT:
+						try {
+							if (_is_fetched[column] != true) {
+								fetchColumnInfo(column);
+							}
+							return _isAutoincrement[column];
+						} catch (IndexOutOfBoundsException e) {
+							throw MonetResultSet.newSQLInvalidColumnIndexException(column);
+						}
 				}
+
+				return false;
 			}
 
 			/**
@@ -1414,14 +1428,14 @@ public class MonetResultSet
 			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);
@@ -1434,10 +1448,10 @@ public class MonetResultSet
 						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;
@@ -1516,10 +1530,20 @@ public class MonetResultSet
 			}
 
 			/**
-			 * 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.
+			 *
+			 * This method is currently very expensive for DECIMAL, NUMERIC
+			 * CHAR, VARCHAR, CLOB, BLOB, VARBINARY and BINARY result
+			 * column types as it needs to retrieve the information
+			 * from the database using an SQL meta data query.
 			 *
 			 * @param column the first column is 1, the second is 2, ...
 			 * @return precision
@@ -1527,73 +1551,76 @@ public class MonetResultSet
 			 */
 			@Override
 			public int getPrecision(final int column) throws SQLException {
-				checkColumnIndexValidity(column);
-				try {
-					if (_is_fetched[column] != true) {
-						fetchColumnInfo(column);
-					}
-					if (_precision[column] == 0) {
-						// apparently no precision could be fetched
-						// use columnDisplaySize() value for variable length data types
-						switch (getColumnType(column)) {
-							case Types.CHAR:
-							case Types.VARCHAR:
-							case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
-							case Types.CLOB:
-							case Types.BLOB:
-							case Types.NUMERIC:
-							case Types.DECIMAL:
+				final int tpe = getColumnType(column);
+				switch (tpe) {
+					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 do not have a fixed precision, max precision however is 38
+						// we need to fetch the defined precision with an SQL query !
+					case Types.CHAR:
+					case Types.VARCHAR:
+					case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
+					case Types.CLOB:
+					case Types.BINARY:
+					case Types.VARBINARY:
+					case Types.BLOB:
+						// these data types also do not have a fixed length
+						try {
+							if (_is_fetched[column] != true) {
+								fetchColumnInfo(column);
+							}
+							if (_precision[column] == 0) {
+								// apparently no precision or max length could be fetched
+								// use columnDisplaySize() value as alternative
 								_precision[column] = getColumnDisplaySize(column);
-								break;
-							case Types.TINYINT:
-								_precision[column] = 3;
-								break;
-							case Types.SMALLINT:
-								_precision[column] = 5;
-								break;
-							case Types.INTEGER:
-								_precision[column] = 10;
-								break;
-							case Types.BIGINT:
-								_precision[column] = 19;
-								break;
-							case Types.REAL:
-								_precision[column] = 7;
-								break;
-							case Types.FLOAT:
-							case Types.DOUBLE:
-								_precision[column] = 15;
-								break;
-							case Types.BOOLEAN:
-								_precision[column] = 5;
-								break;
-							case Types.DATE:
-								_precision[column] = 10;
-								break;
-							case Types.TIME:
-							case Types.TIME_WITH_TIMEZONE:
-								_precision[column] = 8;
-								break;
-							case Types.TIMESTAMP:
-							case Types.TIMESTAMP_WITH_TIMEZONE:
-								_precision[column] = 19;
-								break;
-							default:
-								_precision[column] = 30;
-								break;
+								if (tpe == Types.BLOB || tpe == Types.VARBINARY || tpe == Types.BINARY)
+									// These expect number of bytes, not number of hex chars
+									_precision[column] = (_precision[column] / 2) +1;
+							}
+							return _precision[column];
+						} catch (IndexOutOfBoundsException e) {
+							throw MonetResultSet.newSQLInvalidColumnIndexException(column);
 						}
-					}
-					return _precision[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.
+			 *
+			 * This method is currently very expensive for DECIMAL and NUMERIC
+			 * result column types as it needs to retrieve the information
+			 * from the database using an SQL meta data query.
 			 *
 			 * @param column the first column is 1, the second is 2, ...
 			 * @return scale
@@ -1601,25 +1628,56 @@ public class MonetResultSet
 			 */
 			@Override
 			public int getScale(final int column) throws SQLException {
-				checkColumnIndexValidity(column);
-				try {
-					if (_is_fetched[column] != true) {
-						fetchColumnInfo(column);
-					}
-					return _scale[column];
-				} catch (IndexOutOfBoundsException e) {
-					throw MonetResultSet.newSQLInvalidColumnIndexException(column);
+				switch (getColumnType(column)) {
+					case Types.DECIMAL:
+					case Types.NUMERIC:
+						// these data types may have a scale, max scale is 38
+						try {
+							if (_is_fetched[column] != true) {
+								fetchColumnInfo(column);
+							}
+							return _scale[column];
+						} catch (IndexOutOfBoundsException e) {
+							throw MonetResultSet.newSQLInvalidColumnIndexException(column);
+						}
+					case Types.TIME:
+					case Types.TIME_WITH_TIMEZONE:
+					case Types.TIMESTAMP:
+					case Types.TIMESTAMP_WITH_TIMEZONE:
+						// support microseconds, so scale 6
+						return 6;	// 21:51:34.399753
+					// All other types should return 0
+				//	case Types.BIGINT:
+				//	case Types.INTEGER:
+				//	case Types.SMALLINT:
+				//	case Types.TINYINT:
+				//	case Types.REAL:
+				//	case Types.FLOAT:
+				//	case Types.DOUBLE:
+				//	case Types.CHAR:
+				//	case Types.VARCHAR:
+				//	case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
+				//	case Types.CLOB:
+				//	case Types.BINARY:
+				//	case Types.VARBINARY:
+				//	case Types.BLOB:
+				//	case Types.DATE:
+				//	case Types.BOOLEAN:
+					default:
+						return 0;
 				}
 			}
 
 			/**
-			 * 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.
+			 *
+			 * This method is currently very expensive as it needs to
+			 * retrieve the information from the database using an SQL
+			 * meta data query (for each column).
 			 *
 			 * @param column the first column is 1, the second is 2, ...
-			 * @return the nullability status of the given column; one of columnNoNulls, columnNullable or columnNullableUnknown
+			 * @return the nullability status of the given column; one of
+			 *	columnNoNulls, columnNullable or columnNullableUnknown
 			 * @throws SQLException if a database access error occurs
 			 */
 			@Override