/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright 2005 - 2009 Pentaho Corporation. All rights reserved. * * * Created Aug 31, 2005 * @author wseyler */ package org.pentaho.platform.plugin.services.connections.sql; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.commons.connection.IPeekable; import org.pentaho.commons.connection.IPentahoMetaData; import org.pentaho.commons.connection.IPentahoResultSet; import org.pentaho.commons.connection.memory.MemoryMetaData; import org.pentaho.commons.connection.memory.MemoryResultSet; import org.pentaho.platform.plugin.services.messages.Messages; /** * @author wseyler * * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code Templates */ public class SQLResultSet implements IPentahoResultSet, IPeekable { ResultSet nativeResultSet = null; SQLConnection connection; private static final int COUNT_NEVER_OBTAINED = -2; private int rowCount = SQLResultSet.COUNT_NEVER_OBTAINED; private int columnCount = SQLResultSet.COUNT_NEVER_OBTAINED; protected Object peekRow[]; private static final Log log = LogFactory.getLog(SQLResultSet.class); private IPentahoMetaData metadata; /** * */ public SQLResultSet(final ResultSet nativeResultSet, final SQLConnection nativeConnection) { super(); this.connection = nativeConnection; this.nativeResultSet = nativeResultSet; } public void setMetaData(final IPentahoMetaData metadata) { this.metadata = metadata; } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#getMetaData() */ public IPentahoMetaData getMetaData() { if (metadata == null) { try { metadata = new SQLMetaData(nativeResultSet.getMetaData()); } catch (SQLException e) { // TODO Auto-generated catch block SQLResultSet.log.error(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0004_GET_METADATA"), e); //$NON-NLS-1$ // log.error(null, e); throw new RuntimeException(e); } } return metadata; } public Object[] peek() { if( peekRow == null ) { peekRow = next(); } return peekRow; } /** * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#next() returns null if no more rows * * @throws SQLResultSetException */ public Object[] next() { if (peekRow != null) { Object row[] = peekRow; peekRow = null; return row; } try { int columns = nativeResultSet.getMetaData().getColumnCount(); if (nativeResultSet.next()) { Object currentRow[] = new Object[columns]; for (int column = 0; column < columns; column++) { currentRow[column] = nativeResultSet.getObject(column + 1); } return currentRow; } } catch (SQLException e) { // TODO surface this error SQLResultSet.log.error(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0005_NEXT"), e); //$NON-NLS-1$ throw new SQLResultSetException(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0005_NEXT"), e); //$NON-NLS-1$ } return null; } public void closeConnection() { close(); if (connection != null) { try { connection.close(); } catch (Exception ignored) { } } connection = null; } public void close() { if (nativeResultSet != null) { try { nativeResultSet.close(); } catch (SQLException e) { // TODO sbarkdull, localize this! SQLResultSet.log.warn(Messages.getInstance().getString("SQLResultSet.WARN_CONNECTION_NOT_CLOSED")); //$NON-NLS-1$ } rowCount = SQLResultSet.COUNT_NEVER_OBTAINED; } nativeResultSet = null; } public void dispose() { closeConnection(); } public boolean isScrollable() { int resultSetType = ResultSet.TYPE_FORWARD_ONLY; try { resultSetType = nativeResultSet.getType(); } catch (SQLException ex) { SQLResultSet.log.warn(Messages.getInstance().getString("SQLResultSet.WARN_RESULTSET_TYPE_UNDETERMINED")); //$NON-NLS-1$ } if (resultSetType == ResultSet.TYPE_FORWARD_ONLY) { return false; } else { return true; } } /** * Returns the column count from the result set. * * @return the column count. */ public int getColumnCount() { if (columnCount != SQLResultSet.COUNT_NEVER_OBTAINED) { // We have already calculated column count, return what we have. return columnCount; } if (nativeResultSet == null) { return 0; } try { columnCount = nativeResultSet.getMetaData().getColumnCount(); return columnCount; } catch (SQLException ex) { // TODO: Surfase this exception. SQLResultSet.log.error(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0006_GET_COLUMNCOUNT"), ex); //$NON-NLS-1$ } return 0; } /** * Get a rowCount from the resultset. If the resultset * * @return the row count. */ public int getRowCount() { if (rowCount != SQLResultSet.COUNT_NEVER_OBTAINED) { // We have already calculated rowcount, return what we have return rowCount; } // No resultset if (nativeResultSet == null) { return 0; } try { // Get current row in the resultset int curRow = nativeResultSet.getRow(); try { // Seek to the end of the resultset. This could be very // bad for performance if the cursor is client-side. if (nativeResultSet.last()) { // Get the rownumber of the last row rowCount = nativeResultSet.getRow(); // Boundary case if (rowCount <= 0) { rowCount = 0; } } else { // Couldn't seek to last row - Scrollable resultsets not // supported? // TODO: Possibly throw an exception in this case rowCount = 0; } } finally { // There is no row 0 - if the curRow was 0, go to before the // first row in the resultset if (curRow == 0) { nativeResultSet.beforeFirst(); } else { // Go back where we started nativeResultSet.absolute(curRow); } } } catch (SQLException sqle) { SQLResultSet.log.error(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0001_OBTAINING_ROWCOUNT"), sqle); //$NON-NLS-1$ rowCount = 0; } return rowCount; } /** * Returns the value of the specified row and the specified column from within the resultset. * * @param row * the row index. * @param column * the column index. * @return the value. */ public Object getValueAt(final int row, final int column) { if (nativeResultSet != null) { try { /* * Following code courtesy of contribution from Rui Goncalves * BISERVER-3213 */ int curNativeRow = nativeResultSet.getRow(); if (curNativeRow != (row + 1)) { if (curNativeRow == row) { nativeResultSet.next(); } else { nativeResultSet.absolute(row + 1); } } return nativeResultSet.getObject(column + 1); } catch (SQLException ex) { SQLResultSet.log.error(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0002_GET_VALUE"), ex); //$NON-NLS-1$ throw new IllegalStateException(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0008_FORWARDED_SQL_MSG"), ex); //$NON-NLS-1$ } } return null; } public IPentahoResultSet memoryCopy() { try { IPentahoMetaData meta = getMetaData(); Object columnHeaders[][] = meta.getColumnHeaders(); MemoryMetaData cachedMetaData = new MemoryMetaData(columnHeaders, null); MemoryResultSet cachedResultSet = new MemoryResultSet(cachedMetaData); Object[] rowObjects = next(); while (rowObjects != null) { cachedResultSet.addRow(rowObjects); rowObjects = next(); } return cachedResultSet; } finally { close(); } } public void beforeFirst() { try { if (nativeResultSet == null) { SQLResultSet.log.error(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0007_BEFORE_FIRST_CONNECTION_CLOSED")); //$NON-NLS-1$ } else { nativeResultSet.beforeFirst(); } } catch (SQLException e) { SQLResultSet.log.error(Messages.getInstance().getErrorString("SQLResultSet.ERROR_0003_BEFORE_FIRST"), e); //$NON-NLS-1$ } } public Object[] getDataColumn(final int column) { Object[] result = null; result = new Object[getRowCount()]; for (int row = 0; row < result.length; row++) { result[row] = getValueAt(row, column); } return result; } public Object[] getDataRow(final int row) { Object[] rowData = new Object[this.getColumnCount()]; for (int column = 0; column < rowData.length; column++) { rowData[column] = getValueAt(row, column); } return rowData; } }