/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.hadoop.hive.hbase; import org.apache.hadoop.hive.ql.index.IndexPredicateAnalyzer; import org.apache.hadoop.hive.ql.index.IndexSearchCondition; import org.apache.hadoop.hive.ql.plan.ExprNodeDesc; import org.apache.hadoop.hive.ql.plan.ExprNodeGenericFuncDesc; import org.apache.hadoop.hive.serde2.BaseStructObjectInspector; import org.apache.hadoop.hive.serde2.ByteStream; import org.apache.hadoop.hive.serde2.Deserializer; import org.apache.hadoop.hive.serde2.SerDeException; import org.apache.hadoop.hive.serde2.lazy.ByteArrayRef; import org.apache.hadoop.hive.serde2.lazy.LazyObjectBase; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.StructField; import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; import org.apache.hadoop.hive.serde2.typeinfo.StructTypeInfo; import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo; import org.apache.hadoop.mapred.JobConf; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class SampleHBaseKeyFactory2 extends AbstractHBaseKeyFactory { private static final int FIXED_LENGTH = 10; @Override public ObjectInspector createKeyObjectInspector(TypeInfo type) { return new StringArrayOI((StructTypeInfo)type); } @Override public LazyObjectBase createKey(ObjectInspector inspector) throws SerDeException { return new FixedLengthed(FIXED_LENGTH); } private final ByteStream.Output output = new ByteStream.Output(); @Override public byte[] serializeKey(Object object, StructField field) throws IOException { ObjectInspector inspector = field.getFieldObjectInspector(); if (inspector.getCategory() != ObjectInspector.Category.STRUCT) { throw new IllegalStateException("invalid type value " + inspector.getTypeName()); } output.reset(); for (Object element : ((StructObjectInspector)inspector).getStructFieldsDataAsList(object)) { output.write(toBinary(String.valueOf(element).getBytes(), FIXED_LENGTH, false, false)); } return output.getLength() > 0 ? output.toByteArray() : null; } private byte[] toBinary(String value, int max, boolean end, boolean nextBA) { return toBinary(value.getBytes(), max, end, nextBA); } private byte[] toBinary(byte[] value, int max, boolean end, boolean nextBA) { byte[] bytes = new byte[max + 1]; System.arraycopy(value, 0, bytes, 0, Math.min(value.length, max)); if (end) { Arrays.fill(bytes, value.length, max, (byte)0xff); } if (nextBA) { bytes[max] = 0x01; } return bytes; } @Override public DecomposedPredicate decomposePredicate(JobConf jobConf, Deserializer deserializer, ExprNodeDesc predicate) { String keyColName = keyMapping.columnName; IndexPredicateAnalyzer analyzer = IndexPredicateAnalyzer.createAnalyzer(false); analyzer.allowColumnName(keyColName); analyzer.setAcceptsFields(true); DecomposedPredicate decomposed = new DecomposedPredicate(); List<IndexSearchCondition> searchConditions = new ArrayList<IndexSearchCondition>(); decomposed.residualPredicate = (ExprNodeGenericFuncDesc)analyzer.analyzePredicate(predicate, searchConditions); if (!searchConditions.isEmpty()) { decomposed.pushedPredicate = analyzer.translateSearchConditions(searchConditions); try { decomposed.pushedPredicateObject = setupFilter(keyColName, searchConditions); } catch (IOException e) { throw new RuntimeException(e); } } return decomposed; } private HBaseScanRange setupFilter(String keyColName, List<IndexSearchCondition> conditions) throws IOException { Map<String, List<IndexSearchCondition>> fieldConds = new HashMap<String, List<IndexSearchCondition>>(); for (IndexSearchCondition condition : conditions) { assert keyColName.equals(condition.getColumnDesc().getColumn()); String fieldName = condition.getFields()[0]; List<IndexSearchCondition> fieldCond = fieldConds.get(fieldName); if (fieldCond == null) { fieldConds.put(fieldName, fieldCond = new ArrayList<IndexSearchCondition>()); } fieldCond.add(condition); } HBaseScanRange range = new HBaseScanRange(); ByteArrayOutputStream startRow = new ByteArrayOutputStream(); ByteArrayOutputStream stopRow = new ByteArrayOutputStream(); StructTypeInfo type = (StructTypeInfo) keyMapping.columnType; for (String name : type.getAllStructFieldNames()) { List<IndexSearchCondition> fieldCond = fieldConds.get(name); if (fieldCond == null || fieldCond.size() > 2) { continue; } byte[] startElement = null; byte[] stopElement = null; for (IndexSearchCondition condition : fieldCond) { if (condition.getConstantDesc().getValue() == null) { continue; } String comparisonOp = condition.getComparisonOp(); String constantVal = String.valueOf(condition.getConstantDesc().getValue()); if (comparisonOp.endsWith("UDFOPEqual")) { startElement = toBinary(constantVal, FIXED_LENGTH, false, false); stopElement = toBinary(constantVal, FIXED_LENGTH, true, true); } else if (comparisonOp.endsWith("UDFOPEqualOrGreaterThan")) { startElement = toBinary(constantVal, FIXED_LENGTH, false, false); } else if (comparisonOp.endsWith("UDFOPGreaterThan")) { startElement = toBinary(constantVal, FIXED_LENGTH, false, true); } else if (comparisonOp.endsWith("UDFOPEqualOrLessThan")) { stopElement = toBinary(constantVal, FIXED_LENGTH, true, false); } else if (comparisonOp.endsWith("UDFOPLessThan")) { stopElement = toBinary(constantVal, FIXED_LENGTH, true, true); } else { throw new IOException(comparisonOp + " is not a supported comparison operator"); } } if (startRow != null) { if (startElement != null) { startRow.write(startElement); } else { if (startRow.size() > 0) { range.setStartRow(startRow.toByteArray()); } startRow = null; } } if (stopRow != null) { if (stopElement != null) { stopRow.write(stopElement); } else { if (stopRow.size() > 0) { range.setStopRow(stopRow.toByteArray()); } stopRow = null; } } if (startElement == null && stopElement == null) { break; } } if (startRow != null && startRow.size() > 0) { range.setStartRow(startRow.toByteArray()); } if (stopRow != null && stopRow.size() > 0) { range.setStopRow(stopRow.toByteArray()); } return range; } private static class FixedLengthed implements LazyObjectBase { private final int fixedLength; private final List<Object> fields = new ArrayList<Object>(); private transient boolean isNull; public FixedLengthed(int fixedLength) { this.fixedLength = fixedLength; } @Override public void init(ByteArrayRef bytes, int start, int length) { fields.clear(); byte[] data = bytes.getData(); int rowStart = start; int rowStop = rowStart + fixedLength; for (; rowStart < length; rowStart = rowStop + 1, rowStop = rowStart + fixedLength) { fields.add(new String(data, rowStart, rowStop - rowStart).trim()); } isNull = false; } @Override public void setNull() { isNull = true; } @Override public Object getObject() { return isNull ? null : this; } } private static class StringArrayOI extends BaseStructObjectInspector { private int length; private StringArrayOI(StructTypeInfo type) { List<String> names = type.getAllStructFieldNames(); List<ObjectInspector> ois = new ArrayList<ObjectInspector>(); for (int i = 0; i < names.size(); i++) { ois.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); } init(names, ois, null); } @Override public Object getStructFieldData(Object data, StructField fieldRef) { return ((FixedLengthed)data).fields.get(((MyField)fieldRef).getFieldID()); } @Override public List<Object> getStructFieldsDataAsList(Object data) { return ((FixedLengthed)data).fields; } } }