diff tests/JDBC_API_Tester.java @ 713:c3c424a90a42

Improve implementation of ResultSet.getMetaData(). The current implementation creates a new ResultSetMetaData each time this method is called which is quite costly if it is called from inside a fetch-loop such as in the example on: https://en.wikipedia.org/wiki/Java_Database_Connectivity#Examples try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM MyTable")) { while (rs.next()) { int numColumns = rs.getMetaData().getColumnCount(); for (int i = 1; i <= numColumns; i++) { // Column numbers start at 1. // Also there are many methods on the result set to return // the column as a particular type. Refer to the Sun documentation // for the list of valid conversions. System.out.println( "COLUMN " + i + " = " + rs.getObject(i)); } } } As the ResultSetMetaData is static for a ResultSet it is better to create it once, cache it in the ResultSet object and return the cached object for next calls to ResultSet.getMetaData().
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Wed, 04 Jan 2023 23:34:14 +0100 (2023-01-04)
parents 5244af37a8e2
children aeb268156580
line wrap: on
line diff
--- a/tests/JDBC_API_Tester.java
+++ b/tests/JDBC_API_Tester.java
@@ -30,7 +30,7 @@ import org.monetdb.jdbc.types.URL;
  * This speeds up testing considerably as the overhead of starting a JVM and
  * loading the java test program class and MonetDB JDBC driver is now reduced
  * to only one time instead of 40+ times.
- * Also all output is no longer send to system out/err but collected in a StringBuilder.
+ * Also all output is no longer send to system out/err but collected in a global StringBuilder.
  * The contents of it is compared with the expected output at the end of each test.
  * Only when it deviates the output is sent to system err, see compareExpectedOutput().
  *
@@ -89,6 +89,7 @@ final public class JDBC_API_Tester {
 		jt.Test_Rpositioning();
 		jt.Test_Rsqldata();
 		jt.Test_Rtimedate();
+		jt.Test_RSgetMetaData();
 		jt.Test_Sbatching();
 		jt.Test_Smoreresults();
 		jt.Test_Wrapper();
@@ -3501,6 +3502,204 @@ final public class JDBC_API_Tester {
 			"0. true	true\n");
 	}
 
+	private void Test_RSgetMetaData() {
+		sb.setLength(0);	// clear the output log buffer
+
+		Statement stmt = null;
+		ResultSet rs = null;
+		try {
+			con.setAutoCommit(false);
+			// >> false: auto commit should be off now
+			sb.append("0. false\t").append(con.getAutoCommit()).append("\n");
+
+			stmt = con.createStatement();
+			stmt.executeUpdate("CREATE TABLE Test_RSmetadata ( myint int, mydouble double, mybool boolean, myvarchar varchar(15), myclob clob )");
+
+			// all NULLs
+			stmt.executeUpdate("INSERT INTO Test_RSmetadata VALUES (NULL, NULL, NULL, NULL, NULL)");
+			// all filled in
+			stmt.executeUpdate("INSERT INTO Test_RSmetadata VALUES (2 , 3.0, true, 'A string', 'bla bla bla')");
+
+			rs = stmt.executeQuery("SELECT * FROM Test_RSmetadata");
+
+			sb.append("0. ").append(rs.getMetaData().getColumnCount()).append(" columns:\n");
+			for (int col = 1; col <= rs.getMetaData().getColumnCount(); col++) {
+				sb.append("Colnr ").append(col).append(".\n");
+				sb.append("\tclassname     ").append(rs.getMetaData().getColumnClassName(col)).append("\n");
+				sb.append("\tdisplaysize   ").append(rs.getMetaData().getColumnDisplaySize(col)).append("\n");
+				sb.append("\tlabel         ").append(rs.getMetaData().getColumnLabel(col)).append("\n");
+				sb.append("\tname          ").append(rs.getMetaData().getColumnName(col)).append("\n");
+				sb.append("\ttype          ").append(rs.getMetaData().getColumnType(col)).append("\n");
+				sb.append("\ttypename      ").append(rs.getMetaData().getColumnTypeName(col)).append("\n");
+				sb.append("\tprecision     ").append(rs.getMetaData().getPrecision(col)).append("\n");
+				sb.append("\tscale         ").append(rs.getMetaData().getScale(col)).append("\n");
+				sb.append("\tcatalogname   ").append(rs.getMetaData().getCatalogName(col)).append("\n");
+				sb.append("\tschemaname    ").append(rs.getMetaData().getSchemaName(col)).append("\n");
+				sb.append("\ttablename     ").append(rs.getMetaData().getTableName(col)).append("\n");
+				sb.append("\tautoincrement ").append(rs.getMetaData().isAutoIncrement(col)).append("\n");
+				sb.append("\tcasesensitive ").append(rs.getMetaData().isCaseSensitive(col)).append("\n");
+				sb.append("\tcurrency      ").append(rs.getMetaData().isCurrency(col)).append("\n");
+				sb.append("\tdefwritable   ").append(rs.getMetaData().isDefinitelyWritable(col)).append("\n");
+				sb.append("\tnullable      ").append(rs.getMetaData().isNullable(col)).append("\n");
+				sb.append("\treadonly      ").append(rs.getMetaData().isReadOnly(col)).append("\n");
+				sb.append("\tsearchable    ").append(rs.getMetaData().isSearchable(col)).append("\n");
+				sb.append("\tsigned        ").append(rs.getMetaData().isSigned(col)).append("\n");
+				sb.append("\twritable      ").append(rs.getMetaData().isWritable(col)).append("\n");
+			}
+
+			for (int i = 6; rs.next(); i++) {
+				for (int col = 1; col <= rs.getMetaData().getColumnCount(); col++) {
+					Object obj = rs.getObject(col);
+					String type = rs.getMetaData().getColumnClassName(col);
+					String isInstance = "(null)";
+					if (obj != null && type != null) {
+						try {
+							Class<?> c = Class.forName(type);
+							if (c.isInstance(obj)) {
+								isInstance = (obj.getClass().getName() + " is an instance of " + type);
+							} else {
+								isInstance = (obj.getClass().getName() + " is NOT an instance of " + type);
+							}
+						} catch (ClassNotFoundException e) {
+							isInstance = "No such class: " + type;
+						}
+					}
+					sb.append(i).append(".\t").append(isInstance).append("\n");
+				}
+			}
+			rs.close();
+
+			con.rollback();
+			con.setAutoCommit(true);
+			// >> true: auto commit was just switched on
+			sb.append("0. true\t").append(con.getAutoCommit()).append("\n");
+		} catch (SQLException e) {
+			sb.append("FAILED: ").append(e.getMessage()).append("\n");
+		}
+
+		closeStmtResSet(stmt, rs);
+
+		compareExpectedOutput("Test_RSgetMetaData",
+			"0. false	false\n" +
+			"0. 5 columns:\n" +
+			"Colnr 1.\n" +
+			"	classname     java.lang.Integer\n" +
+			"	displaysize   1\n" +
+			"	label         myint\n" +
+			"	name          myint\n" +
+			"	type          4\n" +
+			"	typename      int\n" +
+			"	precision     10\n" +
+			"	scale         0\n" +
+			"	catalogname   null\n" +
+			"	schemaname    sys\n" +
+			"	tablename     test_rsmetadata\n" +
+			"	autoincrement false\n" +
+			"	casesensitive false\n" +
+			"	currency      false\n" +
+			"	defwritable   false\n" +
+			"	nullable      1\n" +
+			"	readonly      true\n" +
+			"	searchable    true\n" +
+			"	signed        true\n" +
+			"	writable      false\n" +
+			"Colnr 2.\n" +
+			"	classname     java.lang.Double\n" +
+			"	displaysize   24\n" +
+			"	label         mydouble\n" +
+			"	name          mydouble\n" +
+			"	type          8\n" +
+			"	typename      double\n" +
+			"	precision     15\n" +
+			"	scale         0\n" +
+			"	catalogname   null\n" +
+			"	schemaname    sys\n" +
+			"	tablename     test_rsmetadata\n" +
+			"	autoincrement false\n" +
+			"	casesensitive false\n" +
+			"	currency      false\n" +
+			"	defwritable   false\n" +
+			"	nullable      1\n" +
+			"	readonly      true\n" +
+			"	searchable    true\n" +
+			"	signed        true\n" +
+			"	writable      false\n" +
+			"Colnr 3.\n" +
+			"	classname     java.lang.Boolean\n" +
+			"	displaysize   5\n" +
+			"	label         mybool\n" +
+			"	name          mybool\n" +
+			"	type          16\n" +
+			"	typename      boolean\n" +
+			"	precision     1\n" +
+			"	scale         0\n" +
+			"	catalogname   null\n" +
+			"	schemaname    sys\n" +
+			"	tablename     test_rsmetadata\n" +
+			"	autoincrement false\n" +
+			"	casesensitive false\n" +
+			"	currency      false\n" +
+			"	defwritable   false\n" +
+			"	nullable      1\n" +
+			"	readonly      true\n" +
+			"	searchable    true\n" +
+			"	signed        false\n" +
+			"	writable      false\n" +
+			"Colnr 4.\n" +
+			"	classname     java.lang.String\n" +
+			"	displaysize   8\n" +
+			"	label         myvarchar\n" +
+			"	name          myvarchar\n" +
+			"	type          12\n" +
+			"	typename      varchar\n" +
+			"	precision     15\n" +
+			"	scale         0\n" +
+			"	catalogname   null\n" +
+			"	schemaname    sys\n" +
+			"	tablename     test_rsmetadata\n" +
+			"	autoincrement false\n" +
+			"	casesensitive true\n" +
+			"	currency      false\n" +
+			"	defwritable   false\n" +
+			"	nullable      1\n" +
+			"	readonly      true\n" +
+			"	searchable    true\n" +
+			"	signed        false\n" +
+			"	writable      false\n" +
+			"Colnr 5.\n" +
+			"	classname     java.lang.String\n" +
+			"	displaysize   11\n" +
+			"	label         myclob\n" +
+			"	name          myclob\n" +
+			"	type          12\n" +
+			"	typename      clob\n" +
+			"	precision     11\n" +
+			"	scale         0\n" +
+			"	catalogname   null\n" +
+			"	schemaname    sys\n" +
+			"	tablename     test_rsmetadata\n" +
+			"	autoincrement false\n" +
+			"	casesensitive true\n" +
+			"	currency      false\n" +
+			"	defwritable   false\n" +
+			"	nullable      1\n" +
+			"	readonly      true\n" +
+			"	searchable    true\n" +
+			"	signed        false\n" +
+			"	writable      false\n" +
+			"6.	(null)\n" +
+			"6.	(null)\n" +
+			"6.	(null)\n" +
+			"6.	(null)\n" +
+			"6.	(null)\n" +
+			"7.	java.lang.Integer is an instance of java.lang.Integer\n" +
+			"7.	java.lang.Double is an instance of java.lang.Double\n" +
+			"7.	java.lang.Boolean is an instance of java.lang.Boolean\n" +
+			"7.	java.lang.String is an instance of java.lang.String\n" +
+			"7.	java.lang.String is an instance of java.lang.String\n" +
+			"0. true	true\n");
+	}
+
 	private void Test_RfetchManyColumnsInfo() {
 		sb.setLength(0);	// clear the output log buffer
 
@@ -4195,7 +4394,7 @@ final public class JDBC_API_Tester {
 			ResultSetMetaData rsmd = rs.getMetaData();
 			checkIsWrapperFor("ResultSetMetaData", rsmd, jdbc_pkg, "ResultSetMetaData");
 			checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetResultSet");
-			checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetResultSet$rsmdw");  // it is a private class of MonetResultSet
+			checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetResultSetMetaData");
 			checkIsWrapperFor("ResultSetMetaData", rsmd, jdbc_pkg, "Statement");
 			checkIsWrapperFor("ResultSetMetaData", rsmd, monetdb_jdbc_pkg, "MonetStatement");
 
@@ -4253,7 +4452,7 @@ final public class JDBC_API_Tester {
 				"ResultSet. isWrapperFor(MonetStatement) returns: false\n" +
 				"ResultSetMetaData. isWrapperFor(ResultSetMetaData) returns: true	Called unwrap(). Returned object is not null, so oke\n" +
 				"ResultSetMetaData. isWrapperFor(MonetResultSet) returns: false\n" +
-				"ResultSetMetaData. isWrapperFor(MonetResultSet$rsmdw) returns: true	Called unwrap(). Returned object is not null, so oke\n" +
+				"ResultSetMetaData. isWrapperFor(MonetResultSetMetaData) returns: true	Called unwrap(). Returned object is not null, so oke\n" +
 				"ResultSetMetaData. isWrapperFor(Statement) returns: false\n" +
 				"ResultSetMetaData. isWrapperFor(MonetStatement) returns: false\n" +
 				"Statement. isWrapperFor(Statement) returns: true	Called unwrap(). Returned object is not null, so oke\n" +