/* Copyright (c) 1997 George Reese */
package COM.imaginary.sql.msql;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Hashtable;
import java.util.Vector;
/**
* The MsqlResultSet class implements the JDBC ResultSet interface.
* This class represents a SQL result set whose values can be retrieved
* through the class' methods. This class should never be directly
* referenced. Instead, you should use the JDBC API.<BR>
* Last modified %D%
* @version %A%
* @author George Reese (borg@imaginary.com)
*/
public class MsqlResultSet implements ResultSet, Runnable {
private final int column_count;
private Hashtable column_map = null;
private boolean complete = false;
private int current_row = -1;
private byte[][] current_row_data = null;
private MsqlResultSet field_info = null;
private byte[] last_column = null;
private MsqlInputStream input = null;
private ResultSetMetaData meta_data = null;
private boolean meta_set = false;
private MsqlException read_exception = null;
private Vector rows = new Vector();
private MsqlStatement statement = null;
private SQLWarning warnings = null;
// Constructs the result set
MsqlResultSet(MsqlStatement s, MsqlInputStream in, int count)
throws SQLException {
this(s, in, count, false);
column_count = count;
}
// constructs the result set where the last arg signifies that this
// result set is simply the meta data information
MsqlResultSet(MsqlStatement s, MsqlInputStream in, int count, boolean meta)
throws SQLException {
super();
statement = s;
input = in;
column_count = count;
meta_set = meta;
(new Thread(this)).start();
}
/**
* @return true if the last value read was null
* @exception java.sql.SQLException never thrown
*/
public synchronized boolean wasNull() throws SQLException {
if( last_column == null ) {
return true;
}
else {
return false;
}
}
/**
* For performance reasons, you should get values by column
* number when at all possible.
* @param cname the name of the desired column
* @return an ASCII input stream for the column
* @exception java.sql.SQLException thrown when the column cannot be read
*/
public synchronized InputStream getAsciiStream(String cname)
throws SQLException {
return getAsciiStream(findColumn(cname));
}
/**
* @param column the column number for the desired column
* @return an ASCII input stream for the desired column
* @exception java.sql.SQLException thrown when the columb cannot be read
*/
public synchronized InputStream getAsciiStream(int column)
throws SQLException {
getColumn(column);
return new MsqlAsciiInputStream(last_column);
}
public synchronized BigDecimal getBigDecimal(String cname, int scale)
throws SQLException {
return getBigDecimal(findColumn(cname), scale);
}
public synchronized BigDecimal getBigDecimal(int column, int scale)
throws SQLException {
String tmp = getString(column);
if( tmp == null ) {
return new BigDecimal(new BigInteger("0"), scale);
}
else {
return new BigDecimal(new BigInteger(tmp), scale);
}
}
public synchronized InputStream getBinaryStream(String cname)
throws SQLException {
return getBinaryStream(findColumn(cname));
}
public synchronized InputStream getBinaryStream(int column)
throws SQLException {
getColumn(column);
return new ByteArrayInputStream(last_column);
}
public synchronized boolean getBoolean(String cname) throws SQLException {
return getBoolean(findColumn(cname));
}
public synchronized boolean getBoolean(int column) throws SQLException {
getColumn(column);
if( wasNull() ) {
return false;
}
if( last_column.length == 0 ) {
return false;
}
else if( last_column[0] == '0' || last_column[0] == '\0' ) {
return false;
}
else {
return true;
}
}
public synchronized byte getByte(String cname) throws SQLException {
return getByte(findColumn(cname));
}
public synchronized byte getByte(int column) throws SQLException {
getColumn(column);
if( wasNull() ) {
return (byte)0;
}
else {
return last_column[0];
}
}
public synchronized byte[] getBytes(String cname) throws SQLException {
return getBytes(findColumn(cname));
}
public synchronized byte[] getBytes(int column) throws SQLException {
getColumn(column);
return last_column;
}
private synchronized void getColumn(int column) throws SQLException {
try {
last_column = current_row_data[column-1];
}
catch( Exception e ) {
if( current_row_data == null ) {
throw new MsqlException("-1:Result set positioned before " +
"first row.");
}
throw new MsqlException(e);
}
}
public String getCursorName() throws SQLException {
throw new SQLException("mSQL does not support cursors.");
}
public synchronized Date getDate(String cname) throws SQLException {
return getDate(findColumn(cname));
}
public synchronized Date getDate(int column) throws SQLException {
long time = getTimeAsLong(column);
if( wasNull() ) {
return null;
}
else {
return new Date(time);
}
}
public synchronized double getDouble(String cname) throws SQLException {
return getDouble(findColumn(cname));
}
public synchronized double getDouble(int column) throws SQLException {
String tmp = getString(column);
if( tmp == null ) return 0;
try {
return Double.valueOf(tmp).doubleValue();
}
catch( NumberFormatException e ) {
throw new MsqlException(e);
}
}
public synchronized float getFloat(String cname) throws SQLException {
return getFloat(findColumn(cname));
}
public synchronized float getFloat(int column) throws SQLException {
String tmp = getString(column);
if( tmp == null ) {
return 0;
}
try {
return Float.valueOf(tmp).floatValue();
}
catch( NumberFormatException e ) {
throw new MsqlException(e);
}
}
public synchronized int getInt(String cname) throws SQLException {
return getInt(findColumn(cname));
}
public synchronized int getInt(int column) throws SQLException {
String tmp = getString(column);
if( tmp == null ) {
return 0;
}
try {
return Integer.parseInt(tmp);
}
catch( NumberFormatException e ) {
throw new MsqlException(e);
}
}
public synchronized long getLong(String cname) throws SQLException {
return getLong(findColumn(cname));
}
public synchronized long getLong(int column) throws SQLException {
String tmp = getString(column);
if( tmp == null ) {
return 0;
}
try {
return Long.parseLong(tmp);
}
catch( NumberFormatException e ) {
throw new MsqlException(e);
}
}
public ResultSetMetaData getMetaData() throws SQLException {
ResultSetMetaData meta;
synchronized( this ) {
if( meta_data != null ) {
return meta_data;
}
}
synchronized( rows ) {
while( field_info == null ) {
try {
rows.wait();
}
catch( InterruptedException e ) {
if( field_info == null ) {
throw new MsqlException(e);
}
}
}
meta = new MsqlResultSetMetaData(field_info);
}
synchronized( this ) {
meta_data = meta;
}
return meta_data;
}
public synchronized Object getObject(String cname) throws SQLException {
return getObject(findColumn(cname));
}
public synchronized Object getObject(int column) throws SQLException {
ResultSetMetaData meta = getMetaData();
int type = meta.getColumnType(column);
switch(type) {
case Types.BIT:
return new Boolean(getBoolean(column));
case Types.TINYINT:
return new Character((char)getInt(column));
case Types.SMALLINT:
return new Integer(getShort(column));
case Types.INTEGER:
return new Integer(getInt(column));
case Types.BIGINT:
return new Long(getLong(column));
case Types.FLOAT:
return new Float(getFloat(column));
case Types.REAL:
return new Float(getFloat(column));
case Types.DOUBLE:
return new Double(getDouble(column));
case Types.NUMERIC:
return getBigDecimal(column, 0);
case Types.DECIMAL:
return getBigDecimal(column, 0);
case Types.CHAR:
return getString(column);
case Types.VARCHAR:
return getString(column);
case Types.LONGVARCHAR:
return getString(column);
case Types.DATE:
return getDate(column);
case Types.TIME:
return getTime(column);
case Types.TIMESTAMP:
return getTimestamp(column);
case Types.BINARY:
return getBytes(column);
case Types.VARBINARY:
return getBytes(column);
case Types.LONGVARBINARY:
return getBytes(column);
default:
return getString(column);
}
}
private byte[][] getRow(int row) throws SQLException {
if( row < 0 ) {
throw new SQLException("Attempt to access a non-existent row.");
}
synchronized( rows ) {
if( read_exception != null ) {
throw read_exception;
}
while( rows.size() <= row ) {
if( complete ) {
throw new SQLException("Attempt to access a " +
"non-existent row.");
}
else {
try {
rows.wait();
}
catch( InterruptedException e ) {
throw new MsqlException(e);
}
}
}
}
return (byte[][])rows.elementAt(row);
}
public synchronized short getShort(String cname) throws SQLException {
return getShort(findColumn(cname));
}
public synchronized short getShort(int column) throws SQLException {
String tmp = getString(column);
if( tmp == null ) {
return 0;
}
try {
return (short)Short.parseShort(tmp);
}
catch( NumberFormatException e ) {
throw new MsqlException(e);
}
}
MsqlStatement getStatement() {
return statement;
}
public synchronized String getString(String cname) throws SQLException {
return getString(findColumn(cname));
}
public synchronized String getString(int column) throws SQLException {
getColumn(column);
if( wasNull() ) {
return null;
}
else {
try {
return new String(last_column, "8859_9");
}
catch( UnsupportedEncodingException e ) {
throw new MsqlException(e);
}
}
}
public synchronized Time getTime(String cname) throws SQLException {
return getTime(findColumn(cname));
}
public synchronized Time getTime(int column) throws SQLException {
long time = getTimeAsLong(column);
if( wasNull() ) {
return null;
}
else {
return new Time(time);
}
}
private long getTimeAsLong(int column) throws SQLException {
String tmp = getString(column);
if( tmp == null ) {
return 0;
}
try {
return Long.parseLong(tmp);
}
catch( NumberFormatException e ) {
try {
SimpleDateFormat format;
java.util.Date date;
format = new SimpleDateFormat("EEE MMM dd hh:mm:ss z yyyy");
date = format.parse(tmp, new ParsePosition(0));
return date.getTime();
}
catch( Exception real_e ) {
throw new SQLException("Invalid date format.");
}
}
}
public synchronized Timestamp getTimestamp(String cname)
throws SQLException {
return getTimestamp(findColumn(cname));
}
public synchronized Timestamp getTimestamp(int column)
throws SQLException {
long time = getTimeAsLong(column);
if( wasNull() ) {
return null;
}
else {
return new Timestamp(time);
}
}
public synchronized InputStream getUnicodeStream(String cname)
throws SQLException {
return getUnicodeStream(findColumn(cname));
}
public synchronized InputStream getUnicodeStream(int column)
throws SQLException {
getColumn(column);
return new MsqlUnicodeInputStream(last_column);
}
public synchronized SQLWarning getWarnings() throws SQLException {
return warnings;
}
public synchronized void clearWarnings() throws SQLException {
warnings = null;
}
/**
* Closes the result set.
* @exception java.sql.SQLException thrown for errors on closing
*/
public void close() throws SQLException {
synchronized( rows ) {
while( !complete ) {
try {
rows.wait();
}
catch( InterruptedException e ) {
}
}
if( !meta_set && field_info != null ) {
field_info.close();
}
input = null;
}
}
/**
* @param name the name of the desired column
* @return the column number for the specified column name
* @exception java.sql.SQLException thrown on a read error
*/
public synchronized int findColumn(String name) throws SQLException {
Integer num;
if( column_map == null ) {
ResultSetMetaData meta;
int maxi;
// Thanks to Joern Kellermann for this fix
meta = getMetaData();
column_map = new Hashtable(maxi = meta.getColumnCount());
for(int i=1; i<=maxi; i++) {
column_map.put(meta.getColumnName(i), new Integer(i));
}
}
num = (Integer)column_map.get(name);
if( num == null ) {
throw new SQLException("Invalid column name: " + name);
}
return num.intValue();
}
/**
* Moves to the next row of data for processing. If there are no
* more rows to be processed, then it will return false.
* @return true if there are results to be processed, false otherwise
* @exception java.sql.SQLException thrown if a read error occurs
*/
public synchronized boolean next() throws SQLException {
current_row++;
try {
current_row_data = getRow(current_row);
}
catch( SQLException e ) {
return false;
}
return true;
}
// reads a single row of data
private void readRow(byte[] data) throws SQLException {
byte[][] cols = new byte[column_count][];
String ascii;
try {
ascii = new String(data, "8859_9");
}
catch( UnsupportedEncodingException e ) {
throw new MsqlException(e);
}
for(int i=0; i<column_count; i++) {
// we can get away with this since ascii arabics == unicode arabics
int colon = ascii.indexOf(':');
byte[] column;
int size;
try {
size = Integer.parseInt(ascii.substring(0, colon));
}
catch( NumberFormatException e ) {
throw new SQLException("Invalid row data format from mSQL.");
}
if( size == -2 ) {
column = null;
size = 0;
}
else {
String tmp = ascii.substring(colon+1, colon+size+1);
try {
column = tmp.getBytes("8859_9");
}
catch( UnsupportedEncodingException e ) {
throw new MsqlException(e);
}
}
cols[i] = column;
ascii = ascii.substring(colon+size+1);
}
synchronized( rows ) {
rows.addElement(cols);
rows.notify();
}
}
// loads the results
public void run() {
while( true ) {
byte[] data;
String tmp;
synchronized( input ) {
try {
data = input.read();
}
catch( IOException e ) {
synchronized( rows ) {
//statement.passIOException(e);
read_exception = new MsqlException(e);
complete = true;
input = null;
return;
}
}
}
try {
tmp = new String(data, "8859_9");
}
catch( UnsupportedEncodingException e ) {
e.printStackTrace();
synchronized( rows ) {
//statement.passIOException(e);
read_exception = new MsqlException(e);
complete = true;
rows.notify();
}
return;
}
if( tmp.startsWith("-1:") ) {
synchronized( rows ) {
read_exception = new MsqlException(tmp);
complete = true;
rows.notify();
}
return;
}
else if( tmp.startsWith("-100") ) {
break;
}
else {
try {
readRow(data);
}
catch( SQLException e ) {
synchronized( rows ) {
read_exception = new MsqlException(e);
complete = true;
rows.notify();
}
return;
}
}
}
synchronized( rows ) {
if( !meta_set ) {
try {
field_info = new MsqlResultSet(statement, input, 6, true);
}
catch( SQLException e ) {
e.printStackTrace();
field_info = null;
}
}
complete = true;
rows.notify();
}
}
}
|