Changeset: 95cf74c7f51c for MonetDB
URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=95cf74c7f51c
Modified Files:
        java/ChangeLog.Jun2016
        java/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
        java/tests/Test_Rmetadata.java
        sql/jdbc/tests/Tests/Test_Rmetadata.stable.out
Branch: Jun2016
Log Message:

Fixed resource leak in ResultSetMetaData. It created and cached a ResultSet
object for each column but never closed the ResultSet objects.


diffs (truncated from 496 to 300 lines):

diff --git a/java/ChangeLog.Jun2016 b/java/ChangeLog.Jun2016
--- a/java/ChangeLog.Jun2016
+++ b/java/ChangeLog.Jun2016
@@ -1,6 +1,10 @@
 # ChangeLog file for java
 # This file is updated with Maddlog
 
+* Thu Apr 21 2016 Martin van Dinther <martin.van.dint...@monetdbsolutions.com>
+- Fixed resource leak in ResultSetMetaData. It created and cached a ResultSet
+  object for each column but never closed the ResultSet objects.
+
 * Thu Mar 31 2016 Martin van Dinther <martin.van.dint...@monetdbsolutions.com>
 - Corrected DatabaseMetaData methods which accept a catalog filter argument.
   Those methods will now filter the results on the specified catalog name,
diff --git a/java/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java 
b/java/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
--- a/java/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
+++ b/java/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
@@ -21,6 +21,7 @@ import java.net.URL;
 import java.sql.Array;
 import java.sql.Blob;
 import java.sql.Clob;
+import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.NClob;
 import java.sql.Ref;
@@ -683,7 +684,7 @@ public class MonetResultSet extends Mone
         * @throws SQLException if there is no such column
         */
        @Override
-       public boolean getBoolean(int columnIndex) throws SQLException{
+       public boolean getBoolean(int columnIndex) throws SQLException {
                switch (getJavaType(types[columnIndex - 1])) {
                        case Types.TINYINT:
                        case Types.SMALLINT:
@@ -1081,11 +1082,63 @@ public class MonetResultSet extends Mone
        public ResultSetMetaData getMetaData() {
                // return inner class which implements the ResultSetMetaData 
interface
                return new rsmdw() {
-                       // for the more expensive methods, we provide a simple 
cache
-                       // for the most expensive part; getting the ResultSet 
which
-                       // contains the data
+                       // for the more expensive methods (getPrecision(), 
getScale(), isNullable()), we provide a simple cache
+                       // caches to store precision, scale and isNullable 
values from getColumns()
+                       private boolean[] _is_fetched   = new 
boolean[columns.length +1];
+                       private int[] _precision        = new 
int[columns.length +1];
+                       private int[] _scale            = new 
int[columns.length +1];
+                       private int[] _isNullable       = new 
int[columns.length +1];
+                       private Connection conn = null;
                        private DatabaseMetaData dbmd = null;
-                       private ResultSet[] colrs = new 
ResultSet[columns.length];
+
+                       /**
+                        * A private method to fetch the precision, scale and 
isNuallble value for a fully qualified column.
+                        * As md.getColumns() is an expensive method we call it 
only once per column
+                        * and cache the precision, scale and isNullable values 
in the above array chaches.
+                        * Also we only call md.getColumns() when we have a non 
empty schema name and table name and column name.
+                        */
+                       private void fetchColumnInfo(int column) throws 
SQLException
+                       {
+                               if (column <= 0 || column > columns.length)
+                                       throw new SQLException("No such column 
" + column, "M1M05");
+
+                               _is_fetched[column] = true;
+                               _precision[column] = 0;
+                               _scale[column] = 0;
+                               _isNullable[column] = columnNullableUnknown;
+
+                               // we can only call dbmd.getColumns() when we 
have a specific schema name and table name and column name
+                               String schName = getSchemaName(column);
+                               if (schName != null && !"".equals(schName)) {
+                                       String tblName = getTableName(column);
+                                       if (tblName != null && 
!"".equals(tblName)) {
+                                               String colName = 
getColumnName(column);
+                                               if (colName != null && 
!"".equals(colName)) {
+                                                       if (conn == null) {
+                                                               // first time, 
get a Connection object and cache it for all next columns
+                                                               conn = 
getStatement().getConnection();
+                                                       }
+                                                       if (conn != null && 
dbmd == null) {
+                                                               // first time, 
get a MetaData object and cache it for all next columns
+                                                               dbmd = 
conn.getMetaData();
+                                                       }
+                                                       if (dbmd != null) {
+                                                               // for 
precision, scale and isNullable we query the information from data dictionary
+                                                               ResultSet 
colInfo = dbmd.getColumns(null, 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"
+                                                                       }
+                                                                       
colInfo.close();  // close the resultset to release resources
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
 
                        /**
                         * Returns the number of columns in this ResultSet 
object.
@@ -1098,8 +1151,7 @@ public class MonetResultSet extends Mone
                        }
 
                        /**
-                        * Indicates whether the designated column is 
automatically
-                        * numbered, thus read-only.
+                        * Indicates whether the designated column is 
automatically numbered.
                         * 
                         * @param column the first column is 1, the second is 
2, ...
                         * @return true if so; false otherwise
@@ -1118,15 +1170,21 @@ public class MonetResultSet extends Mone
                        }
 
                        /**
-                        * Indicates whether a column's case matters. This 
holds for all
-                        * columns in MonetDB resultsets since the mapping is 
done case
-                        * insensitive, therefore this method will always 
return false.
+                        * Indicates whether a column's case matters.
                         *
                         * @param column the first column is 1, the second is 
2, ...
-                        * @returns false
+                        * @returns true for all character string columns else 
false
                         */
                        @Override
-                       public boolean isCaseSensitive(int column) {
+                       public boolean isCaseSensitive(int column) throws 
SQLException {
+                               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:
+                                               return true;
+                               }
+
                                return false;
                        }
 
@@ -1290,19 +1348,62 @@ public class MonetResultSet extends Mone
                         */
                        @Override
                        public int getPrecision(int column) throws SQLException 
{
-                               int precision = 0;
-                               try {
-                                       ResultSet col = 
getColumnResultSet(column);
-
-                                       // the result has either zero or one 
results, as the
-                                       // schema, table and column should be 
unique...
-                                       if (col.next())
-                                               precision = 
col.getInt("COLUMN_SIZE");
-                               } catch (NullPointerException npe) {
-                                       /* do nothing */
+                               if (_is_fetched[column] != true) {
+                                       fetchColumnInfo(column);
                                }
-
-                               return precision;
+                               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:
+                                                       _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.BIT: // MonetDB 
doesn't use type BIT, it's here for completeness
+                                                       _precision[column] = 1;
+                                                       break;
+                                               case Types.BOOLEAN:
+                                                       _precision[column] = 5;
+                                                       break;
+                                               case Types.DATE:
+                                                       _precision[column] = 10;
+                                                       break;
+                                               case Types.TIME:
+                                                       _precision[column] = 8;
+                                                       break;
+                                               case Types.TIMESTAMP:
+                                                       _precision[column] = 19;
+                                                       break;
+                                               default:
+                                                       _precision[column] = 30;
+                                                       break;
+                                       }
+                               }
+                               return _precision[column];
                        }
 
                        /**
@@ -1317,19 +1418,10 @@ public class MonetResultSet extends Mone
                         */
                        @Override
                        public int getScale(int column) throws SQLException {
-                               int scale = 0;
-                               try {
-                                       ResultSet col = 
getColumnResultSet(column);
-
-                                       // the result has either zero or one 
results, as the
-                                       // schema, table and column should be 
unique...
-                                       if (col.next())
-                                               scale = 
col.getInt("DECIMAL_DIGITS");
-                               } catch (NullPointerException npe) {
-                                       /* do nothing */
+                               if (_is_fetched[column] != true) {
+                                       fetchColumnInfo(column);
                                }
-
-                               return scale;
+                               return _scale[column];
                        }
 
                        /**
@@ -1339,30 +1431,20 @@ public class MonetResultSet extends Mone
                         * an SQL query.
                         *
                         * @param column the first column is 1, the second is 
2, ...
-                        * @return scale
+                        * @return the nullability status of the given column; 
one of columnNoNulls, columnNullable or columnNullableUnknown
                         * @throws SQLException if a database access error 
occurs
                         */
                        @Override
                        public int isNullable(int column) throws SQLException {
-                               int ret = columnNullableUnknown;
-                               try {
-                                       ResultSet col = 
getColumnResultSet(column);
-
-                                       // the result has either zero or one 
results, as the
-                                       // schema, table and column should be 
unique...
-                                       if (col.next())
-                                               ret = col.getInt("NULLABLE");
-                               } catch (NullPointerException npe) {
-                                       /* do nothing */
+                               if (_is_fetched[column] != true) {
+                                       fetchColumnInfo(column);
                                }
-
-                               return ret;
+                               return _isNullable[column];
                        }
 
                        /**
                         * Gets the designated column's table's catalog name.
-                        * Because MonetDB handles only one catalog (dbfarm) at 
a
-                        * time, the current one is the one we deal with here.
+                        * MonetDB does not support the catalog naming concept 
as in: catalog.schema.table naming scheme
                         *
                         * @param column the first column is 1, the second is 
2, ...
                         * @return the name of the catalog for the table in 
which the given
@@ -1370,11 +1452,8 @@ public class MonetResultSet extends Mone
                         */
                        @Override
                        public String getCatalogName(int column) throws 
SQLException {
-                               if (getTableName(column) != "") {
-                                       return 
getStatement().getConnection().getCatalog();
-                               } else {
-                                       return "";
-                               }
+                               return null;    // MonetDB does NOT support 
catalogs
+
                        }
 
                        /**
@@ -1432,17 +1511,22 @@ public class MonetResultSet extends Mone
                        @Override
                        public String getColumnClassName(int column) throws 
SQLException {
                                try {
-                                       Class type;
-                                       Map map = 
getStatement().getConnection().getTypeMap();
-                                       if (map.containsKey(types[column - 1])) 
{
-                                               type = 
(Class)map.get(types[column - 1]);
-                                       } else {
-                                               type = 
getClassForType(getJavaType(types[column - 1]));
+                                       if (conn == null) {
+                                               // first time, get a Connection 
object and cache it for all next columns
+                                               conn = 
getStatement().getConnection();
_______________________________________________
checkin-list mailing list
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to