Changeset: 56ca5fe0526b for MonetDB URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=56ca5fe0526b Modified Files: java/ChangeLog.Dec2011 java/src/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java Branch: Dec2011 Log Message:
PreparedStatement: use PREPARERESULT to obtain info about result columns Implemented extra ResultSetMetaData for PreparedStatement with use of the extra data emitted by PREPARERESULT. Drawback of this commit is that it makes the server segfault for two tests at least, compared to using plain PREPARE. Backwards-compatability is prepared for once we rename PREPARERESULT to PREPARE. diffs (truncated from 495 to 300 lines): diff --git a/java/ChangeLog.Dec2011 b/java/ChangeLog.Dec2011 --- a/java/ChangeLog.Dec2011 +++ b/java/ChangeLog.Dec2011 @@ -1,6 +1,9 @@ # ChangeLog file for java # This file is updated with Maddlog +* Mon Jan 2 2012 Fabian Groffen <[email protected]> +- Implemented getMetaData() method of PreparedStatement. + * Tue Dec 27 2011 Fabian Groffen <[email protected]> - Fixed an AssertionError for special results from e.g. EXPLAIN queries. diff --git a/java/src/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java b/java/src/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java --- a/java/src/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java +++ b/java/src/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java @@ -58,8 +58,12 @@ public class MonetPreparedStatement private final int[] javaType; private final int[] digits; private final int[] scale; + private final String[] schema; + private final String[] table; + private final String[] column; private final int id; private final int size; + private final int rscolcnt; private final String[] values; private final StringBuffer buf; @@ -111,18 +115,22 @@ public class MonetPreparedStatement resultSetHoldability ); - if (!super.execute("PREPARE " + prepareQuery)) + if (!super.execute("PREPARERESULT " + prepareQuery)) throw new SQLException("Unexpected server response", "M0M10"); // cheat a bit to get the ID and the number of columns id = ((MonetConnection.ResultSetResponse)header).id; size = ((MonetConnection.ResultSetResponse)header).tuplecount; + rscolcnt = ((MonetConnection.ResultSetResponse)header).columncount; // initialise blank finals monetdbType = new String[size]; javaType = new int[size]; digits = new int[size]; scale = new int[size]; + schema = new String[size]; + table = new String[size]; + column = new String[size]; values = new String[size]; buf = new StringBuffer(6 + 12 * size); @@ -135,6 +143,11 @@ public class MonetPreparedStatement javaType[i] = MonetDriver.getJavaType(monetdbType[i]); digits[i] = rs.getInt("digits"); scale[i] = rs.getInt("scale"); + if (rscolcnt == 3) + continue; + schema[i] = rs.getString("schema"); + table[i] = rs.getString("table"); + column[i] = rs.getString("column"); } rs.close(); @@ -169,10 +182,14 @@ public class MonetPreparedStatement javaType = null; digits = null; scale = null; + schema = null; + table = null; + column = null; values = null; buf = null; id = -1; size = -1; + rscolcnt = -1; this.connection = connection; } @@ -279,6 +296,24 @@ public class MonetPreparedStatement } /** + * Returns the index in the backing arrays for the given + * resultset column number + */ + private int getColumnIdx(int colnr) throws SQLException { + int curcol = 0; + for (int i = 0; i < size; i++) { + if (!column[i].equals("")) + continue; + curcol++; + if (curcol == colnr) + return(i); + } + throw new SQLException("No such column with index: " + colnr, "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 * when this PreparedStatement object is executed. @@ -289,16 +324,354 @@ public class MonetPreparedStatement * 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. - * <br /><br /> - * NOTE: Using this method is expensive for this driver due to the - * lack of underlying DBMS support. Currently not implemented * * @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 */ - public ResultSetMetaData getMetaData() throws SQLException { - return(null); + public ResultSetMetaData getMetaData() { + if (rscolcnt == 3) + return(null); // not sufficient data with pre-Dec2011 PREPARE + + // 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 + private DatabaseMetaData dbmd = null; + private Map colrs = new HashMap(); + + /** + * Returns the number of columns in this ResultSet object. + * + * @returns the number of columns + */ + public int getColumnCount() { + int cnt = 0; + + for (int i = 0; i < size; i++) { + if (!column[i].equals("")) + cnt++; + } + + return(cnt); + } + + /** + * Indicates whether the designated column is automatically + * numbered, thus read-only. + * + * @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 + */ + public boolean isAutoIncrement(int column) throws SQLException { + // the only column I know of is a 'secret' column called rowid + // with datatype oid + // avoid nullpointer exception here + if ("oid".equals(monetdbType[getColumnIdx(column)])) { + return(true); + } else { + return(false); + } + } + + /** + * 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. + * + * @param column the first column is 1, the second is 2, ... + * @returns false + */ + public boolean isCaseSensitive(int column) { + return(false); + } + + /** + * Indicates whether the designated column can be used in a + * where clause. + * It is unknown to me what kind ot columns they regard to, + * as I think all columns are useable in a where clause. + * Returning true for all here, for the time being. + * Possible thought; maybe they want to know here if it's a + * real column existing in a table or not... + * + * @param column the first column is 1, the second is 2, ... + * @returns true + */ + public boolean isSearchable(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, ... + * @returns false + */ + public boolean isCurrency(int column) { + return(false); + } + + /** + * Indicates whether values in the designated column are signed + * numbers. + * Within MonetDB all numeric types are signed. + * + * @param column the first column is 1, the second is 2, ... + * @return true if so; false otherwise + */ + public boolean isSigned(int column) throws SQLException { + // we can hardcode this, based on the colum type + switch (javaType[getColumnIdx(column)]) { + case Types.NUMERIC: + case Types.DECIMAL: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + return(true); + case Types.BIT: // we don't use type BIT, it's here for completeness + case Types.BOOLEAN: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + 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 + */ + public int getColumnDisplaySize(int column) throws SQLException { + return(digits[getColumnIdx(column)]); + } + + /** + * Get the designated column's table's schema. + * + * @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 + */ + public String getSchemaName(int column) throws SQLException { + return(schema[getColumnIdx(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 + */ + public String getTableName(int col) throws SQLException { + return(column[getColumnIdx(col)]); + } + + /** + * 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. + * + * @param column the first column is 1, the second is 2, ... + * @return precision + * @throws SQLException if a database access error occurs + */ + public int getPrecision(int column) throws SQLException { + return(digits[getColumnIdx(column)]); + } + + /** + * 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. + * + * @param column the first column is 1, the second is 2, ... + * @return scale + * @throws SQLException if a database access error occurs + */ + public int getScale(int column) throws SQLException { + return(scale[getColumnIdx(column)]); + } _______________________________________________ Checkin-list mailing list [email protected] http://mail.monetdb.org/mailman/listinfo/checkin-list
