Changeset: b3c876a0d61f for monetdb-java
URL: https://dev.monetdb.org/hg/monetdb-java?cmd=changeset;node=b3c876a0d61f
Modified Files:
        src/main/java/org/monetdb/jdbc/MonetConnection.java
        src/main/java/org/monetdb/jdbc/MonetResultSet.java
Branch: default
Log Message:

Improved performance of ResultSetMetaData methods getSchemaName(), 
getTableName(), getPrecision(), getScale(), isNullable() and isAutoIncrement().
Previously getSchemaName() and getTableName() would extract the schema name or 
the table name from a single string containing both, separated by a dot.
Now this is done once at a higher level (ResultSetResponse) and the values can 
be accessed directly.
The methods getPrecision(), getScale(), isNullable() and isAutoIncrement() used 
to call fetchColumnInfo() which created a MonetDatabaseMetaData object and next 
call MonetDatabaseMetaData.getColumns() method.
However this getColumns() method queries and returns much more info than really 
needed for the methods in ResultSetMetaData. It is a costly (and slow) method.
Hence it is now replaced with a smaller and faster custom query (based on the 
query from getColumns()) which runs much faster.
Also the creation of a MonetDatabaseMetaData object is no longer needed and has 
been removed.


diffs (236 lines):

diff --git a/src/main/java/org/monetdb/jdbc/MonetConnection.java 
b/src/main/java/org/monetdb/jdbc/MonetConnection.java
--- a/src/main/java/org/monetdb/jdbc/MonetConnection.java
+++ b/src/main/java/org/monetdb/jdbc/MonetConnection.java
@@ -64,7 +64,7 @@ import org.monetdb.mcl.parser.StartOfHea
  *
  * @author Fabian Groffen
  * @author Martin van Dinther
- * @version 1.5
+ * @version 1.6
  */
 public class MonetConnection
        extends MonetWrapper
@@ -2100,6 +2100,8 @@ public class MonetConnection
                private int[] columnLengths;
                /** The table for each column in this result */
                private String[] tableNames;
+               /** The schema for each column in this result */
+               private String[] schemaNames;
                /** The query sequence number */
                private final int seqnr;
                /** A List of result blocks (chunks of size 
fetchSize/cacheSize) */
@@ -2222,8 +2224,28 @@ public class MonetConnection
                                                isSet[TYPES] = true;
                                        break;
                                        case HeaderLineParser.TABLE:
+                                       {
                                                tableNames = hlp.values.clone();
+                                               final int array_size = 
tableNames.length;
+                                               schemaNames = new 
String[array_size];
+                                               // split the schema and table 
names from the cloned values array
+                                               for (int i = 0; i < array_size; 
i++) {
+                                                       String qtable = 
tableNames[i];
+                                                       if (qtable != null) {
+                                                               int dot = 
qtable.indexOf('.');
+                                                               if (dot >= 0) {
+                                                                       
schemaNames[i] = qtable.substring(0, dot);
+                                                                       
tableNames[i] = qtable.substring(dot +1);
+                                                               } else {
+                                                                       
schemaNames[i] = "";
+                                                               }
+                                                       } else {
+                                                               schemaNames[i] 
= "";
+                                                               tableNames[i] = 
"";
+                                                       }
+                                               }
                                                isSet[TABLES] = true;
+                                       }
                                        break;
                                }
                        } catch (MCLParseException e) {
@@ -2335,6 +2357,15 @@ public class MonetConnection
                }
 
                /**
+                * Returns the schemas of the columns
+                *
+                * @return the schemas of the columns
+                */
+               String[] getSchemaNames() {
+                       return schemaNames;
+               }
+
+               /**
                 * Returns the lengths of the columns
                 *
                 * @return the lengths of the columns
diff --git a/src/main/java/org/monetdb/jdbc/MonetResultSet.java 
b/src/main/java/org/monetdb/jdbc/MonetResultSet.java
--- a/src/main/java/org/monetdb/jdbc/MonetResultSet.java
+++ b/src/main/java/org/monetdb/jdbc/MonetResultSet.java
@@ -61,7 +61,7 @@ import java.util.TimeZone;
  *
  * @author Fabian Groffen
  * @author Martin van Dinther
- * @version 0.9
+ * @version 1.0
  */
 public class MonetResultSet
        extends MonetWrapper
@@ -127,7 +127,6 @@ public class MonetResultSet
                concurrency = header.getRSConcur();
                /* the fetchSize used for this result set is the header's 
cacheSize */
                fetchSize = header.getCacheSize();
-
                columns = header.getNames();
                types = header.getTypes();
                if (columns == null || types == null) {
@@ -1247,16 +1246,17 @@ public class MonetResultSet
        public ResultSetMetaData getMetaData() throws SQLException {
                // return inner class which implements the ResultSetMetaData 
interface
                return new rsmdw() {
+                       private final String[] schemas = (header != null) ? 
header.getSchemaNames() : null;
+                       private final String[] tables = (header != null) ? 
header.getTableNames() : null;
+                       private final MonetConnection conn = 
(MonetConnection)getStatement().getConnection();
                        // for the more expensive methods (getPrecision(), 
getScale(), isNullable(), isAutoIncrement()), we
-                       // use caches to store precision, scale and isNullable 
values from getColumns() combined per fully qualified column.
+                       // use caches to store precision, scale and isNullable 
values from getColumnInfo() combined per fully qualified column.
                        private final int array_size = columns.length + 1;  // 
add 1 as in JDBC columns start from 1 (array from 0).
                        private final boolean[] _is_fetched = new 
boolean[array_size];
                        private final int[] _precision  = new int[array_size];
                        private final int[] _scale      = new int[array_size];
                        private final int[] _isNullable = new int[array_size];
                        private final boolean[] _isAutoincrement = new 
boolean[array_size];
-                       private final Connection conn = 
getStatement().getConnection();
-                       private DatabaseMetaData dbmd = null;   // it will be 
assigned at first need and reused for other columns
 
                        /**
                         * A private utility method to check validity of column 
index number
@@ -1269,9 +1269,9 @@ public class MonetResultSet
 
                        /**
                         * A private method to fetch the precision, scale, 
isNullable and isAutoincrement value for a fully qualified column.
-                        * As md.getColumns() is an expensive method we call it 
only once per column and store
+                        * As getColumnInfo() is an expensive method we call it 
only once per column and store
                         * the precision, scale, isNullable and isAutoincrement 
values in the above array caches.
-                        * Also we only call md.getColumns() when we have a non 
empty schema name and table name and column name.
+                        * Also we only call getColumnInfo() when we have a non 
empty schema name and table name and column name.
                         */
                        private final void fetchColumnInfo(final int column) 
throws SQLException
                        {
@@ -1283,14 +1283,7 @@ public class MonetResultSet
                                _isNullable[column] = columnNullableUnknown;
                                _isAutoincrement[column] = false;
 
-                               if (dbmd == null) {
-                                       // first time usage
-                                       dbmd = conn.getMetaData();
-                                       if (dbmd == null)
-                                               return;
-                               }
-
-                               // we will only call dbmd.getColumns() when we 
have a specific schema name and table name and column name
+                               // we will only call getColumnInfo() when we 
have a specific schema name, table name and column name
                                final String schName = getSchemaName(column);
                                if (schName != null && !schName.isEmpty()) {
                                        final String tblName = 
getTableName(column);
@@ -1298,16 +1291,14 @@ public class MonetResultSet
                                                final String colName = 
getColumnName(column);
                                                if (colName != null && 
!colName.isEmpty()) {
                                                        // for precision, 
scale, isNullable and isAutoincrement we query the information from data 
dictionary
-                                                       final ResultSet colInfo 
= dbmd.getColumns(null, schName, tblName, colName);
+                                                       final ResultSet colInfo 
= getColumnInfo(schName, tblName, colName);
                                                        if (colInfo != null) {
                                                                // we expect 
exactly one row in the resultset
                                                                if 
(colInfo.next()) {
-                                                                       
_precision[column] = colInfo.getInt(7);  // col 7 is "COLUMN_SIZE"
-                                                                       
_scale[column] = colInfo.getInt(9);  // col 9 is "DECIMAL_DIGITS"
-                                                                       
_isNullable[column] = colInfo.getInt(11);  // col 11 is "NULLABLE"
-                                                                       final 
String strVal = colInfo.getString(23);  // col 23 is "IS_AUTOINCREMENT"
-                                                                       if 
(strVal != null && "YES".equals(strVal))
-                                                                               
_isAutoincrement[column] = true;
+                                                                       
_precision[column] = colInfo.getInt(1);  // col 1 (was 7) is "COLUMN_SIZE"
+                                                                       
_scale[column] = colInfo.getInt(2);  // col 2 (was 9) is "DECIMAL_DIGITS"
+                                                                       
_isNullable[column] = colInfo.getInt(3);  // col 3 (was 11) is "NULLABLE"
+                                                                       
_isAutoincrement[column] = colInfo.getBoolean(4);  // col 4 (was 23) is 
"IS_AUTOINCREMENT"
                                                                }
                                                                
colInfo.close();  // close the resultset to release resources
                                                        }
@@ -1316,6 +1307,44 @@ public class MonetResultSet
                                }
                        }
 
+                       /* private simplified copy of 
MonetDatabaseMetaData.getColumns() method to fetch only 4 needed attributes of 
a specific column */
+                       private final ResultSet getColumnInfo(final String 
schemaName, final String tableName, final String columnName) throws SQLException
+                       {
+                               final StringBuilder query = new 
StringBuilder(700);
+                               query.append("SELECT " +
+                                       "c.\"type_digits\" AS \"COLUMN_SIZE\", 
" +
+                                       "c.\"type_scale\" AS 
\"DECIMAL_DIGITS\", " +
+                                       "cast(CASE c.\"null\" WHEN true THEN 
").append(ResultSetMetaData.columnNullable)
+                                               .append(" WHEN false THEN 
").append(ResultSetMetaData.columnNoNulls)
+                                               .append(" ELSE 
").append(columnNullableUnknown)
+                                               .append(" END AS int) AS 
\"NULLABLE\", ").append(
+                                       "cast(CASE WHEN c.\"default\" IS NOT 
NULL AND c.\"default\" LIKE 'next value for %' THEN true ELSE false END AS 
boolean) AS \"IS_AUTOINCREMENT\" " +
+                                       // ", s.\"name\" AS \"TABLE_SCHEM\", 
t.\"name\" AS \"TABLE_NAME\", c.\"name\" AS \"COLUMN_NAME\" " +
+                               "FROM \"sys\".\"columns\" c " +
+                               "JOIN \"sys\".\"tables\" t ON c.\"table_id\" = 
t.\"id\" " +
+                               "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" 
= s.\"id\" ");
+
+                               query.append("WHERE s.\"name\" = 
'").append(conn.escapeSpecialChars(schemaName)).append("'");
+                               query.append(" AND t.\"name\" = 
'").append(conn.escapeSpecialChars(tableName)).append("'");
+                               query.append(" AND c.\"name\" = 
'").append(conn.escapeSpecialChars(columnName)).append("'");
+                               // query.append(" ORDER BY \"TABLE_SCHEM\", 
\"TABLE_NAME\"");
+
+                               ResultSet rs = null;
+                               final Statement stmt = conn.createStatement();
+                               if (stmt != null) {
+                                       // for debug: System.out.println("SQL 
(len " + query.length() + "): " + query.toString());
+                                       rs = 
stmt.executeQuery(query.toString());
+                                       if (rs != null) {
+                                               /* we want the statement object 
to be closed also when the resultset is closed by the caller */
+                                               stmt.closeOnCompletion();
+                                       } else {
+                                               /* failed to produce a 
resultset, so release resources for created statement object now */
+                                               stmt.close();
+                                       }
+                               }
+                               return rs;
+                       }
+
                        /**
                         * Returns the number of columns in this ResultSet 
object.
                         *
@@ -1495,14 +1524,9 @@ public class MonetResultSet
                        @Override
                        public String getSchemaName(final int column) throws 
SQLException {
                                checkColumnIndexValidity(column);
-                               if (header != null) {
-                                       // figure the name out
+                               if (schemas != null) {
                                        try {
-                                               final String schema = 
header.getTableNames()[column - 1];
-                                               if (schema != null) {
-                                                       final int dot = 
schema.indexOf('.');
-                                                       return (dot >= 0) ? 
schema.substring(0, dot) : "";
-                                               }
+                                               return schemas[column - 1];
                                        } catch (IndexOutOfBoundsException e) {
                                                throw 
MonetResultSet.newSQLInvalidColumnIndexException(column);
                                        }
@@ -1519,14 +1543,9 @@ public class MonetResultSet
                        @Override
                        public String getTableName(final int column) throws 
SQLException {
                                checkColumnIndexValidity(column);
-                               if (header != null) {
-                                       // figure the name out
+                               if (tables != null) {
                                        try {
-                                               final String table = 
header.getTableNames()[column - 1];
-                                               if (table != null) {
-                                                       final int dot = 
table.indexOf('.');
-                                                       return (dot >= 0) ? 
table.substring(dot + 1) : table;
-                                               }
+                                               return tables[column - 1];
                                        } catch (IndexOutOfBoundsException e) {
                                                throw 
MonetResultSet.newSQLInvalidColumnIndexException(column);
                                        }
_______________________________________________
checkin-list mailing list
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to