/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.translator.hbase; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Struct; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TimeZone; import javax.resource.cci.ConnectionFactory; import javax.sql.rowset.serial.SerialStruct; import org.teiid.core.types.ArrayImpl; import org.teiid.core.types.BinaryType; import org.teiid.language.Call; import org.teiid.language.Command; import org.teiid.language.Literal; import org.teiid.language.QueryExpression; import org.teiid.metadata.RuntimeMetadata; import org.teiid.translator.ExecutionContext; import org.teiid.translator.ExecutionFactory; import org.teiid.translator.HBaseConnection; import org.teiid.translator.MetadataProcessor; import org.teiid.translator.ProcedureExecution; import org.teiid.translator.ResultSetExecution; import org.teiid.translator.Translator; import org.teiid.translator.TranslatorException; import org.teiid.translator.TranslatorProperty; import org.teiid.translator.TypeFacility; import org.teiid.translator.UpdateExecution; @Translator(name="hbase", description="HBase Translator, reads and writes the data to HBase") public class HBaseExecutionFactory extends ExecutionFactory<ConnectionFactory, HBaseConnection> { // use to store phoenix hbase table mapping ddl private Set<String> cacheSet = Collections.synchronizedSet(new HashSet<String>()); private int maxInsertBatchSize = 2048; private boolean useBindVariables = true; private static final Map<Class<?>, Integer> TYPE_CODE_MAP = new HashMap<Class<?>, Integer>(); private StructRetrieval structRetrieval = StructRetrieval.OBJECT; private static final int INTEGER_CODE = 0; private static final int LONG_CODE = 1; private static final int DOUBLE_CODE = 2; private static final int BIGDECIMAL_CODE = 3; private static final int SHORT_CODE = 4; private static final int FLOAT_CODE = 5; private static final int TIME_CODE = 6; private static final int DATE_CODE = 7; private static final int TIMESTAMP_CODE = 8; private static final int BLOB_CODE = 9; private static final int CLOB_CODE = 10; private static final int BOOLEAN_CODE = 11; static { TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.INTEGER, new Integer(INTEGER_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.LONG, new Integer(LONG_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.DOUBLE, new Integer(DOUBLE_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.BIG_DECIMAL, new Integer(BIGDECIMAL_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.SHORT, new Integer(SHORT_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.FLOAT, new Integer(FLOAT_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.TIME, new Integer(TIME_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.DATE, new Integer(DATE_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.TIMESTAMP, new Integer(TIMESTAMP_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.BLOB, new Integer(BLOB_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.CLOB, new Integer(CLOB_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.BOOLEAN, new Integer(BOOLEAN_CODE)); TYPE_CODE_MAP.put(TypeFacility.RUNTIME_TYPES.BYTE, new Integer(SHORT_CODE)); } public final static TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault(); public HBaseExecutionFactory() { } public enum StructRetrieval { OBJECT, COPY, ARRAY } @Override public void start() throws TranslatorException { super.start(); } @Override public ResultSetExecution createResultSetExecution(QueryExpression command , ExecutionContext executionContext , RuntimeMetadata metadata , HBaseConnection connection) throws TranslatorException { return new HBaseQueryExecution(this, command, executionContext, metadata, connection); } @Override public UpdateExecution createUpdateExecution(Command command , ExecutionContext executionContext , RuntimeMetadata metadata , HBaseConnection connection) throws TranslatorException { return new HBaseUpdateExecution(this, command, executionContext, metadata, connection); } @Override public ProcedureExecution createProcedureExecution(Call command , ExecutionContext executionContext , RuntimeMetadata metadata , HBaseConnection connection) throws TranslatorException { return super.createProcedureExecution(command, executionContext, metadata, connection); } public SQLConversionVisitor getSQLConversionVisitor() { return new SQLConversionVisitor(this); } public boolean usePreparedStatements() { return useBindVariables(); } @TranslatorProperty(display="Use Bind Variables", description="Use prepared statements and bind variables",advanced=true) public boolean useBindVariables() { return this.useBindVariables; } public void setUseBindVariables(boolean useBindVariables) { this.useBindVariables = useBindVariables; } /** * Get the max number of inserts to perform in one batch. * @return */ @TranslatorProperty(display="Max Prepared Insert Batch Size", description="The max size of a prepared insert batch. Default 2048.", advanced=true) public int getMaxPreparedInsertBatchSize() { return maxInsertBatchSize; } public void setMaxPreparedInsertBatchSize(int maxInsertBatchSize) { if (maxInsertBatchSize < 1) { throw new AssertionError("Max prepared batch insert size must be greater than 0"); //$NON-NLS-1$ } this.maxInsertBatchSize = maxInsertBatchSize; } public void bindValue(PreparedStatement pstmt, Object param, Class<?> paramType, int i) throws SQLException { int type = TypeFacility.getSQLTypeFromRuntimeType(paramType); if (param == null) { pstmt.setNull(i, type); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.STRING)) { pstmt.setString(i, String.valueOf(param)); return; } if (paramType.equals(TypeFacility.RUNTIME_TYPES.VARBINARY)) { byte[] bytes = ((BinaryType)param).getBytesDirect(); pstmt.setBytes(i, bytes); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.CHAR)) { pstmt.setString(i, String.valueOf(param)); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.BOOLEAN)) { pstmt.setBoolean(i, (Boolean)param); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.BYTE)) { pstmt.setByte(i, (Byte)param); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.SHORT)) { pstmt.setShort(i, (Short)param); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.INTEGER)) { pstmt.setInt(i, (Integer)param); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.LONG)) { pstmt.setLong(i, (Long)param); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.FLOAT)) { pstmt.setFloat(i, (Float)param); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.DOUBLE)) { pstmt.setDouble(i, (Double)param); return; } if(paramType.equals(TypeFacility.RUNTIME_TYPES.BIG_DECIMAL)) { pstmt.setBigDecimal(i, (BigDecimal)param); return; } if (paramType.equals(TypeFacility.RUNTIME_TYPES.DATE)) { pstmt.setDate(i,(java.sql.Date)param); return; } if (paramType.equals(TypeFacility.RUNTIME_TYPES.TIME)) { pstmt.setTime(i,(java.sql.Time)param); return; } if (paramType.equals(TypeFacility.RUNTIME_TYPES.TIMESTAMP)) { pstmt.setTimestamp(i,(java.sql.Timestamp)param); return; } if (TypeFacility.RUNTIME_TYPES.BIG_DECIMAL.equals(paramType)) { pstmt.setBigDecimal(i, (BigDecimal)param); return; } if (useStreamsForLobs()) { // Phonix current not support Blob, Clob, XML } pstmt.setObject(i, param, type); } private boolean useStreamsForLobs() { return false; } public Object retrieveValue(ResultSet results, int columnIndex, Class<?> expectedType) throws SQLException { Integer code = TYPE_CODE_MAP.get(expectedType); if(code != null) { switch(code.intValue()) { case INTEGER_CODE: { int value = results.getInt(columnIndex); if(results.wasNull()) { return null; } return Integer.valueOf(value); } case LONG_CODE: { long value = results.getLong(columnIndex); if(results.wasNull()) { return null; } return Long.valueOf(value); } case DOUBLE_CODE: { double value = results.getDouble(columnIndex); if(results.wasNull()) { return null; } return Double.valueOf(value); } case BIGDECIMAL_CODE: { return results.getBigDecimal(columnIndex); } case SHORT_CODE: { short value = results.getShort(columnIndex); if(results.wasNull()) { return null; } return Short.valueOf(value); } case FLOAT_CODE: { float value = results.getFloat(columnIndex); if(results.wasNull()) { return null; } return Float.valueOf(value); } case TIME_CODE: { return results.getTime(columnIndex); } case DATE_CODE: { return results.getDate(columnIndex); } case TIMESTAMP_CODE: { return results.getTimestamp(columnIndex); } case BLOB_CODE: { try { return results.getBlob(columnIndex); } catch (SQLException e) { // ignore } try { return results.getBytes(columnIndex); } catch (SQLException e) { // ignore } break; } case CLOB_CODE: { try { return results.getClob(columnIndex); } catch (SQLException e) { // ignore } break; } case BOOLEAN_CODE: { return results.getBoolean(columnIndex); } } } Object result = results.getObject(columnIndex); if (expectedType == TypeFacility.RUNTIME_TYPES.OBJECT) { return convertObject(result); } return result; } protected Object convertObject(Object object) throws SQLException { if (object instanceof Struct) { switch (structRetrieval) { case OBJECT: return object; case ARRAY: return new ArrayImpl(((Struct)object).getAttributes()); case COPY: return new SerialStruct((Struct)object, Collections.<String, Class<?>> emptyMap()); } } return object; } /* * Current Phoenix do not support XML, CLOB, BLOB, OBJECT */ protected boolean isBindEligible(Literal l) { return TypeFacility.RUNTIME_TYPES.XML.equals(l.getType()) || TypeFacility.RUNTIME_TYPES.CLOB.equals(l.getType()) || TypeFacility.RUNTIME_TYPES.BLOB.equals(l.getType()) || TypeFacility.RUNTIME_TYPES.OBJECT.equals(l.getType()); } public Set<String> getDDLCacheSet() { return cacheSet; } public void setFetchSize(Command command, ExecutionContext executionContext, Statement statement, int fetchSize) throws SQLException { statement.setFetchSize(fetchSize); } @Override public MetadataProcessor<HBaseConnection> getMetadataProcessor() { return new HBaseMetadataProcessor(); } }