/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.hadoop.shim.common.invocationhandler;
/**
* User: Dzmitry Stsiapanau Date: 01/17/2017 Time: 15:18
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
/**
* ResultSetMetaDataInvocationHandler is a proxy handler class for java.sql.ResultSetMetaData. However the code in this
* file is specifically for handling Hive JDBC calls, and therefore should not be used to proxy any other JDBC objects
* besides those provided by Hive.
*/
public class ResultSetMetaDataInvocationHandler implements InvocationHandler {
/**
* The "real" ResultSetMetaData object.
*/
ResultSetMetaData rsmd;
/**
* Instantiates a new result set meta data invocation handler.
*
* @param r the r
*/
public ResultSetMetaDataInvocationHandler( ResultSetMetaData r ) {
rsmd = r;
}
/**
* Intercepts methods called on the ResultSetMetaData object to possibly perform alternate processing.
*
* @param proxy the proxy
* @param method the method
* @param args the args
* @return the object
* @throws Throwable the throwable
*/
@Override
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {
try {
String methodName = method.getName();
if ( ( "getColumnName".equals( methodName ) || ( "getColumnLabel".equals( methodName ) ) ) && ( args != null )
&& ( args.length == 1 ) ) {
return getColumnName( (Integer) args[ 0 ] );
}
return method.invoke( this.rsmd, args );
} catch ( Throwable t ) {
if ( ( t instanceof InvocationTargetException ) ) {
Throwable cause = t.getCause();
if ( ( cause instanceof SQLException ) ) {
if ( cause.getMessage().equals( "Method not supported" ) ) {
String methodName = method.getName();
if ( "isSigned".equals( methodName ) ) {
if ( args != null ) {
return isSigned( (Integer) args[ 0 ] );
}
}
throw cause;
}
throw cause;
}
throw cause;
}
throw t;
}
}
private String getColumnName( Integer column ) throws SQLException {
String columnName = null;
columnName = this.rsmd.getColumnName( column );
if ( columnName != null ) {
int dotIndex = columnName.indexOf( '.' );
if ( dotIndex != -1 ) {
return columnName.substring( dotIndex + 1 );
}
}
return columnName;
}
/**
* Returns a true if values in the column are signed, false if not.
* <p>
* This method checks the type of the passed column. If that type is not numerical, then the result is false. If the
* type is a numeric then a true is returned.
*
* @param column the index of the column to test
* @return boolean
* @throws SQLException the sQL exception
*/
public boolean isSigned( int column ) throws SQLException {
int numCols = rsmd.getColumnCount();
if ( column < 1 || column > numCols ) {
throw new SQLException( "Invalid column value: " + column );
}
// we need to convert the thrift type to the SQL type
int type = rsmd.getColumnType( column );
switch ( type ) {
case Types.DOUBLE:
case Types.DECIMAL:
case Types.FLOAT:
case Types.INTEGER:
case Types.REAL:
case Types.SMALLINT:
case Types.TINYINT:
case Types.BIGINT:
return true;
}
return false;
}
}