/*
* StreamCruncher: Copyright (c) 2006-2008, Ashwin Jayaprakash. All Rights Reserved.
* Contact: ashwin {dot} jayaprakash {at} gmail {dot} com
* Web: http://www.StreamCruncher.com
*
* This file is part of StreamCruncher.
*
* StreamCruncher is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* StreamCruncher 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with StreamCruncher. If not, see <http://www.gnu.org/licenses/>.
*/
package streamcruncher.innards.impl;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import streamcruncher.api.DBName;
import streamcruncher.api.artifact.IndexSpec;
import streamcruncher.boot.Registry;
import streamcruncher.innards.core.partition.aggregate.AbstractAggregatedColumnDDLHelper;
import streamcruncher.innards.db.DatabaseInterface;
import streamcruncher.innards.impl.artifact.AntsIndexSpec;
import streamcruncher.innards.impl.query.AntsDDLHelper;
import streamcruncher.innards.impl.query.AntsParser;
import streamcruncher.innards.impl.query.DDLHelper;
import streamcruncher.innards.query.Parser;
import streamcruncher.innards.util.CallableStatementWrapper;
import streamcruncher.innards.util.ConnectionWrapper;
import streamcruncher.innards.util.CustomDriver;
import streamcruncher.innards.util.PreparedStatementWrapper;
import streamcruncher.util.AtomicX;
import streamcruncher.util.LoggerManager;
/*
* Author: Ashwin Jayaprakash Date: Jul 22, 2006 Time: 7:01:25 PM
*/
public class AntsDatabaseInterface extends DatabaseInterface {
@Override
public void start(Object... params) throws Exception {
Properties props = (Properties) params[0];
AntsDriverAdapter adapter = new AntsDriverAdapter(props);
DriverManager.registerDriver(adapter);
// -----------
super.start(params);
}
@Override
public Class<? extends Parser> getParser() {
return AntsParser.class;
}
@Override
public DBName getDBName() {
return DBName.Ants;
}
@Override
public AbstractAggregatedColumnDDLHelper getAggregatedColumnDDLHelper() {
return new AntsDDLHelper();
}
@Override
public DDLHelper getDDLHelper() {
return new AntsDDLHelper();
}
@Override
public IndexSpec createIndexSpec(String schema, String name, String tableName, boolean unique,
String columnName, boolean ascending) {
return new AntsIndexSpec(schema, name, tableName, unique, columnName, ascending);
}
@Override
public IndexSpec createIndexSpec(String schema, String name, String tableName, boolean unique,
String[] columnNames, boolean[] ascending) {
return new AntsIndexSpec(schema, name, tableName, unique, columnNames, ascending);
}
/**
* <code>bigint</code> in ANTs is actually a Float. So, we use something
* smaller than Long. And, {@link Integer#MIN_VALUE} does not work either.
*
* @return
*/
@Override
public AtomicX createRowIdGenerator() {
/*
* todo AtomicInteger might not be needed - Change to AtomicLong and the
* other Number-Long changes in Function and AbstractTablePartitioner
* might then become unnecessary.
*/
return new AtomicX(new AtomicInteger(0));
}
// -----------
/**
* <p>
* doc The most bizarre thing about ANTs is that the Driver they have
* recommended (as of 3.6 GA), does not even get registered with the
* DriverManager. And so, every thing goes through the regular Sun JDBC-ODBC
* Bridge. And the Sun Driver is awful!!! Loads of bugs in it.
* </p>
* <p>
* <b>Bugs in Sun Driver with ANTs:</b>
* </p>
* <p>
* ResultSet bug - If "getString(..)" methods are accessed repeatedly for a
* Row or accessed in an order different from the Columns in the Select
* clause. The error thrown is "No data found".
* </p>
* <p>
* Batch Update does not work with ANTs. The Id columns gets assigned some
* random numbers and completely ignore the numbers set in the
* PreparedStatementWrapper.
* </p>
*/
public static class AntsDriverAdapter extends CustomDriver {
public AntsDriverAdapter(Properties properties) throws InstantiationException,
IllegalAccessException, ClassNotFoundException {
super(properties);
}
@Override
protected Connection wrapNewConnection(Connection connection) {
AntsConnectionWrapper wrapper = new AntsConnectionWrapper(connection);
return wrapper;
}
}
public static class AntsConnectionWrapper extends ConnectionWrapper {
public AntsConnectionWrapper(Connection realConnection) {
super(realConnection);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability) throws SQLException {
CallableStatement callableStatement = super.prepareCall(sql, resultSetType,
resultSetConcurrency, resultSetHoldability);
AntsCallableStatementWrapper wrapper = new AntsCallableStatementWrapper(this,
callableStatement);
return wrapper;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
CallableStatement callableStatement = super.prepareCall(sql, resultSetType,
resultSetConcurrency);
AntsCallableStatementWrapper wrapper = new AntsCallableStatementWrapper(this,
callableStatement);
return wrapper;
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
CallableStatement callableStatement = super.prepareCall(sql);
AntsCallableStatementWrapper wrapper = new AntsCallableStatementWrapper(this,
callableStatement);
return wrapper;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability) throws SQLException {
PreparedStatement preparedStatement = super.prepareStatement(sql, resultSetType,
resultSetConcurrency, resultSetHoldability);
AntsPreparedStatementWrapper wrapper = new AntsPreparedStatementWrapper(this,
preparedStatement);
return wrapper;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
PreparedStatement preparedStatement = super.prepareStatement(sql, resultSetType,
resultSetConcurrency);
AntsPreparedStatementWrapper wrapper = new AntsPreparedStatementWrapper(this,
preparedStatement);
return wrapper;
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException {
PreparedStatement preparedStatement = super.prepareStatement(sql, autoGeneratedKeys);
AntsPreparedStatementWrapper wrapper = new AntsPreparedStatementWrapper(this,
preparedStatement);
return wrapper;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException {
PreparedStatement preparedStatement = super.prepareStatement(sql, columnIndexes);
AntsPreparedStatementWrapper wrapper = new AntsPreparedStatementWrapper(this,
preparedStatement);
return wrapper;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException {
PreparedStatement preparedStatement = super.prepareStatement(sql, columnNames);
AntsPreparedStatementWrapper wrapper = new AntsPreparedStatementWrapper(this,
preparedStatement);
return wrapper;
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
PreparedStatement preparedStatement = super.prepareStatement(sql);
AntsPreparedStatementWrapper wrapper = new AntsPreparedStatementWrapper(this,
preparedStatement);
return wrapper;
}
}
private static final String ANTS_LOG_MSG = "ANTs Driver (3.6 GA) has a problem handling"
+ " Longs and -ve Integers less than or equal to " + Integer.MIN_VALUE
+ ", in PreparedStatements/CallableStatements."
+ " You can safely use only the setInt(..) method instead"
+ " and use numbers greater than or equal to " + (Integer.MIN_VALUE + 1);
public static class AntsPreparedStatementWrapper extends PreparedStatementWrapper {
protected static final String log_msg = ANTS_LOG_MSG;
protected final ParameterMetaData metaData;
protected final Logger logger;
public AntsPreparedStatementWrapper(Connection wrappedConnection,
PreparedStatement realStatement) throws SQLException {
super(wrappedConnection, realStatement);
this.metaData = realStatement.getParameterMetaData();
this.logger = Registry.getImplFor(LoggerManager.class).getLogger(
AntsDatabaseInterface.class.getName());
}
@Override
public void setLong(int parameterIndex, long x) throws SQLException {
int i = (int) x;
/*
* Replace the Long value with its Integer part. The Kernel uses
* Long everywhere for the Ids. But ANTs needs "Float" for the
* "bigint" type. Instead of re-coding the Kernel, this is being
* done.
*/
if (i == x && i > Integer.MIN_VALUE) {
super.setInt(parameterIndex, i);
}
/*
* If the long value was intentionally sent then, fall thru and let
* the DB handle it.
*/
else {
logger.log(Level.WARNING, log_msg);
super.setLong(parameterIndex, x);
}
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength)
throws SQLException {
if (targetSqlType == Types.BIGINT && x != null && x instanceof Long) {
setLong(parameterIndex, (Long) x);
}
else {
super.setObject(parameterIndex, x, targetSqlType, scaleOrLength);
}
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
if (targetSqlType == Types.BIGINT && x != null && x instanceof Long) {
setLong(parameterIndex, (Long) x);
}
else {
super.setObject(parameterIndex, x, targetSqlType);
}
}
@Override
public void setObject(int parameterIndex, Object x) throws SQLException {
if (metaData.getParameterType(parameterIndex) == Types.BIGINT && x != null
&& x instanceof Long) {
setLong(parameterIndex, (Long) x);
}
else {
super.setObject(parameterIndex, x);
}
}
}
public static class AntsCallableStatementWrapper extends CallableStatementWrapper {
protected static final String log_msg = ANTS_LOG_MSG;
protected final ParameterMetaData metaData;
protected final Logger logger;
public AntsCallableStatementWrapper(Connection wrappedConnection,
CallableStatement realStatement) throws SQLException {
super(wrappedConnection, realStatement);
this.metaData = realStatement.getParameterMetaData();
this.logger = Registry.getImplFor(LoggerManager.class).getLogger(
AntsDatabaseInterface.class.getName());
}
@Override
public void setLong(int parameterIndex, long x) throws SQLException {
int i = (int) x;
/*
* Replace the Long value with its Integer part. The Kernel uses
* Long everywhere for the Ids. But ANTs needs "Float" for the
* "bigint" type. Instead of re-coding the Kernel, this is being
* done.
*/
if (i == x && i > Integer.MIN_VALUE) {
super.setInt(parameterIndex, i);
}
/*
* If the long value was intentionally sent then, fall thru and let
* the DB handle it.
*/
else {
logger.log(Level.WARNING, log_msg);
super.setLong(parameterIndex, x);
}
}
@Override
public void setLong(String parameterName, long x) throws SQLException {
int i = (int) x;
if (i == x && i > Integer.MIN_VALUE) {
super.setInt(parameterName, i);
}
else {
logger.log(Level.WARNING, log_msg);
super.setLong(parameterName, x);
}
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength)
throws SQLException {
if (targetSqlType == Types.BIGINT && x != null && x instanceof Long) {
setLong(parameterIndex, (Long) x);
}
else {
super.setObject(parameterIndex, x, targetSqlType, scaleOrLength);
}
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
if (targetSqlType == Types.BIGINT && x != null && x instanceof Long) {
setLong(parameterIndex, (Long) x);
}
else {
super.setObject(parameterIndex, x, targetSqlType);
}
}
@Override
public void setObject(int parameterIndex, Object x) throws SQLException {
if (metaData.getParameterType(parameterIndex) == Types.BIGINT && x != null
&& x instanceof Long) {
setLong(parameterIndex, (Long) x);
}
else {
super.setObject(parameterIndex, x);
}
}
// todo How do we intercept this?
@Override
public void setObject(String parameterName, Object x, int targetSqlType, int scale)
throws SQLException {
super.setObject(parameterName, x, targetSqlType, scale);
}
// todo How do we intercept this?
@Override
public void setObject(String parameterName, Object x, int targetSqlType)
throws SQLException {
super.setObject(parameterName, x, targetSqlType);
}
// todo How do we intercept this?
@Override
public void setObject(String parameterName, Object x) throws SQLException {
super.setObject(parameterName, x);
}
}
}