Changeset: 7240d2dd369f for monetdb-java
URL: https://dev.monetdb.org/hg/monetdb-java/rev/7240d2dd369f
Modified Files:
src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
src/main/java/org/monetdb/jdbc/MonetResultSetMetaData.java
tests/JDBC_API_Tester.java
Branch: default
Log Message:
Improved implementation of PreparedStatement.getMetaData().
The previous implementation created a new ResultSetMetaData object each time
this method is called
which is quite costly if it is called from inside a fetch-loop.
As the ResultSetMetaData is static for a PreparedStatement it is better to
create it once,
cache it in the PreparedStatement object and return the cached object for next
calls to PreparedStatement.getMetaData().
Also it reuses now all the methods of ResultSetMetaData class, which implies
less code and easier future maintenance.
The private method getColumnIdx(int column) also is no longer needed and has
been removed.
diffs (truncated from 790 to 300 lines):
diff --git a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
--- a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
+++ b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
@@ -82,6 +82,9 @@ public class MonetPreparedStatement
private int paramCount = 0;
private final String[] paramValues;
+ /** A cache to reduce the number of ResultSetMetaData objects created
by getMetaData() to maximum 1 per PreparedStatement */
+ private ResultSetMetaData rsmd;
+
/* placeholders for date/time pattern formats created once (only when
needed), used multiple times */
/** Format of a timestamp with RFC822 time zone */
private SimpleDateFormat mTimestampZ;
@@ -186,7 +189,7 @@ public class MonetPreparedStatement
table[i] = rs.getString(table_colnr);
column[i] = rs.getString(column_colnr);
// System.out.println("column " + i + " has
value: " + column[i]);
- /* when column[i] != null it is a result column
of the prepared query, see getColumnIdx(int),
+ /* when column[i] != null it is a result column
of the prepared query,
when column[i] == null it is a parameter for
the prepared statement, see getParamIdx(int). */
if (column[i] == null)
paramCount++;
@@ -312,26 +315,6 @@ public class MonetPreparedStatement
/**
* Returns the index (0..size-1) in the backing arrays for the given
- * resultset column number or an SQLException when not found
- *
- * @param colnr the output column number
- * @return the internal column array index number
- * @throws SQLException if column number can not be found in the
internal array
- */
- private final int getColumnIdx(final int colnr) throws SQLException {
- int curcol = 0;
- for (int i = 0; i < size; i++) {
- /* when column[i] == null it is a parameter, when
column[i] != null it is a result column of the prepared query */
- if (column[i] == null)
- continue;
- curcol++;
- if (curcol == colnr)
- return i;
- }
- throw new SQLException("No such column with index: " + colnr,
"M1M05");
- }
- /**
- * Returns the index (0..size-1) in the backing arrays for the given
* parameter number or an SQLException when not found
*
* @param paramnr the parameter number
@@ -351,9 +334,6 @@ public class MonetPreparedStatement
throw new SQLException("No such parameter with index: " +
paramnr, "M1M05");
}
-
- /* helper for the anonymous class inside getMetaData */
- private abstract class rsmdw extends MonetWrapper implements
ResultSetMetaData {}
/**
* Retrieves a ResultSetMetaData object that contains information
* about the columns of the ResultSet object that will be returned
@@ -361,466 +341,103 @@ public class MonetPreparedStatement
*
* Because a PreparedStatement object is precompiled, it is possible
* to know about the ResultSet object that it will return without
- * having to execute it. Consequently, it is possible to invoke the
+ * having to execute it. Consequently, it is possible to invoke the
* method getMetaData on a PreparedStatement object rather than
* waiting to execute it and then invoking the ResultSet.getMetaData
* method on the ResultSet object that is returned.
*
- * @return the description of a ResultSet object's columns or null if
the
- * driver cannot return a ResultSetMetaData object
+ * @return the description of a ResultSet object's columns or null if
+ * the driver cannot return a ResultSetMetaData object
* @throws SQLException if a database access error occurs
*/
@Override
public ResultSetMetaData getMetaData() throws SQLException {
- // return inner class which implements the ResultSetMetaData
interface
- return new rsmdw() {
- /**
- * Returns the number of columns in this ResultSet
object.
- *
- * @return the number of columns
- */
- @Override
- public int getColumnCount() {
- int cnt = 0;
-
- for (int i = 0; i < size; i++) {
- if (column[i] != null)
- cnt++;
- }
- return cnt;
- }
-
- /**
- * 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
- * @throws SQLException if a database access error
occurs
- */
- @Override
- public boolean isAutoIncrement(final int column) throws
SQLException {
- /* In MonetDB only integer (int, bigint,
smallint, tinyint) columns can be autoincrement/serial
- * TODO: This however requires an expensive
dbmd.getColumns(null, schema, table, column)
- * query call to pull the IS_AUTOINCREMENT
value for this column.
- * See also ResultSetMetaData.isAutoIncrement()
- */
- // For now we simply always return false.
- return false;
+ if (rsmd == null) {
+ // first use, construct the arrays with metadata and a
+ // ResultSetMetaData object once and reuse it for all
next calls
+ int rescolcount = 0;
+ for (int i = 0; i < size; i++) {
+ /* when column[i] == null it is a parameter,
when column[i] != null it is a result column of the prepared query */
+ if (column[i] == null)
+ continue;
+ rescolcount++;
}
-
- /**
- * Indicates whether a column's case matters.
- *
- * @param column the first column is 1, the second is
2, ...
- * @return true for all character string columns
(except inet and uuid) else false
- */
- @Override
- public boolean isCaseSensitive(final int column) throws
SQLException {
- switch (getColumnType(column)) {
- case Types.CHAR:
- case Types.LONGVARCHAR: // MonetDB
doesn't use type LONGVARCHAR, it's here for completeness
- case Types.CLOB:
- return true;
- case Types.VARCHAR:
- final String monettype =
getColumnTypeName(column);
- if (monettype != null &&
monettype.length() == 4) {
- // data of type inet or
uuid is not case sensitive
- if
("inet".equals(monettype)
- ||
"uuid".equals(monettype))
- return false;
- }
- return true;
- }
-
- return false;
- }
-
- /**
- * Indicates whether the designated column can be used
in a
- * where clause.
- *
- * Returning true for all here, even for CLOB, BLOB.
- *
- * @param column the first column is 1, the second is
2, ...
- * @return true
- */
- @Override
- public boolean isSearchable(final int column) {
- return true;
- }
-
- /**
- * Indicates whether the designated column is a cash
value.
- * From the MonetDB database perspective it is by
definition
- * unknown whether the value is a currency, because
there are
- * no currency datatypes such as MONEY. With this
knowledge
- * we can always return false here.
- *
- * @param column the first column is 1, the second is
2, ...
- * @return false
- */
- @Override
- public boolean isCurrency(final int column) {
- return false;
+ int array_size = rescolcount;
+ if (array_size == 0) {
+ // there are no resultset columns for this
prepared statement
+ // we can not create arrays of size 0, so use:
+ array_size = 1;
}
-
- /**
- * Indicates whether values in the designated column
are signed
- * numbers.
- * Within MonetDB all numeric types (except oid and
ptr) are signed.
- *
- * @param column the first column is 1, the second is
2, ...
- * @return true if so; false otherwise
- */
- @Override
- public boolean isSigned(final int column) throws
SQLException {
- // we can hardcode this, based on the colum type
- switch (getColumnType(column)) {
- 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;
+ // create arrays for storing only the result columns
meta data
+ final String[] schemas = new String[array_size];
+ final String[] tables = new String[array_size];
+ final String[] columns = new String[array_size];
+ final String[] types = new String[array_size];
+ final int[] jdbcTypes = new int[array_size];
+ final int[] lengths = new int[array_size];
+ final int[] precisions = new int[array_size];
+ final int[] scales = new int[array_size];
+ // now fill the arrays with only the resultset columns
metadata
+ rescolcount = 0;
+ for (int i = 0; i < size; i++) {
+ /* when column[i] == null it is a parameter,
when column[i] != null it is a result column of the prepared query */
+ if (column[i] == null)
+ continue;
+ schemas[rescolcount] = schema[i];
+ tables[rescolcount] = table[i];
+ columns[rescolcount] = column[i];
+ types[rescolcount] = monetdbType[i];
+ jdbcTypes[rescolcount] = javaType[i];
+ switch (jdbcTypes[rescolcount]) {
case Types.BIGINT:
- final String monettype =
getColumnTypeName(column);
- if (monettype != null &&
monettype.length() == 3) {
- // data of type oid or
ptr is not signed
- if
("oid".equals(monettype)
- ||
"ptr".equals(monettype))
- return false;
- }
- return true;
- // All other types should return false
- // case Types.BOOLEAN:
- // case Types.DATE: // can year be
negative?
- // case Types.TIME: // can time be
negative?
- // case Types.TIME_WITH_TIMEZONE:
- // case Types.TIMESTAMP: // can year be
negative?
- // case Types.TIMESTAMP_WITH_TIMEZONE:
- default:
- return false;
- }
- }
-
- /**
- * Indicates the designated column's normal maximum
width in
- * characters.
- *
- * @param column the first column is 1, the second is
2, ...
- * @return the normal maximum number of characters
allowed as the
- * width of the designated column
- * @throws SQLException if there is no such column
- */
- @Override
- public int getColumnDisplaySize(final int column)
throws SQLException {
- return getPrecision(column);
- }
-
- /**
- * 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
- * @throws SQLException if a database access error
occurs
- */
- @Override
- public String getSchemaName(final int column) throws
SQLException {
- try {
- return schema[getColumnIdx(column)];
- } catch (IndexOutOfBoundsException e) {
- throw
MonetResultSet.newSQLInvalidColumnIndexException(column);
- }
- }
-
- /**
- * Gets the designated column's table name.
- *
- * @param column the first column is 1, the second is
2, ...
- * @return table name or "" if not applicable
- */
- @Override
- public String getTableName(final int column) throws
SQLException {
- try {
- return table[getColumnIdx(column)];
- } catch (IndexOutOfBoundsException e) {
- throw
MonetResultSet.newSQLInvalidColumnIndexException(column);
- }
- }
-
- /**
- * 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.
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]