blob: 7f398503bcef2ba915513e7c09f319ac3cea11ff [file] [log] [blame]
/**
* 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.tajo.engine.planner.physical;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;
import com.google.common.primitives.UnsignedInts;
import org.apache.tajo.catalog.Schema;
import org.apache.tajo.catalog.SortSpec;
import org.apache.tajo.common.TajoDataTypes;
import org.apache.tajo.datum.Datum;
import org.apache.tajo.datum.DatumFactory;
import org.apache.tajo.datum.NullDatum;
import org.apache.tajo.datum.TextDatum;
import org.apache.tajo.exception.TajoRuntimeException;
import org.apache.tajo.exception.UnsupportedException;
import org.apache.tajo.storage.Tuple;
import org.apache.tajo.storage.VTuple;
import java.util.Arrays;
import java.util.BitSet;
/**
* Extract raw level values (primitive or String/byte[]) from each of key columns for compare
*/
public class ComparableVector {
protected final Tuple[] tuples; // source tuples
protected final TupleVector[] vectors; // values of key columns
protected final int[] keyIndex;
public ComparableVector(int length, SortSpec[] sortKeys, int[] keyIndex) {
tuples = new Tuple[length];
vectors = new TupleVector[sortKeys.length];
for (int i = 0; i < vectors.length; i++) {
TajoDataTypes.Type type = sortKeys[i].getSortKey().getDataType().getType();
boolean nullFirst = sortKeys[i].isNullFirst();
boolean ascending = sortKeys[i].isAscending();
boolean nullInvert = nullFirst && ascending || !nullFirst && !ascending;
vectors[i] = new TupleVector(vectorType(type), tuples.length, nullInvert, ascending);
}
this.keyIndex = keyIndex;
}
public int compare(final int i1, final int i2) {
for (TupleVector vector : vectors) {
int compare = vector.compare(i1, i2);
if (compare != 0) {
return compare;
}
}
return 0;
}
public void set(int index, Tuple tuple) {
for (int i = 0; i < vectors.length; i++) {
vectors[i].set(index, tuple, keyIndex[i]);
}
}
protected static class TupleVector {
private final int type;
private final BitSet nulls;
private final boolean nullInvert;
private final boolean ascending;
private boolean[] booleans;
private byte[] bits;
private short[] shorts;
private int[] ints;
private long[] longs;
private float[] floats;
private double[] doubles;
private byte[][] bytes;
private int index;
private TupleVector(int type, int length, boolean nullInvert, boolean ascending) {
this.type = type;
this.nulls = new BitSet(length);
this.nullInvert = nullInvert;
this.ascending = ascending;
switch (type) {
case 0: booleans = new boolean[length]; break;
case 1: bits = new byte[length]; break;
case 2: shorts = new short[length]; break;
case 3: ints = new int[length]; break;
case 4: longs = new long[length]; break;
case 5: floats = new float[length]; break;
case 6: doubles = new double[length]; break;
case 7: bytes = new byte[length][]; break;
case 8: ints = new int[length]; break;
case -1: break;
default:
throw new IllegalArgumentException();
}
}
protected final void append(Tuple tuple, int field) {
set(index++, tuple, field);
}
protected final void set(int index, Tuple tuple, int field) {
if (tuple.isBlankOrNull(field)) {
nulls.set(index);
return;
}
nulls.clear(index);
switch (type) {
case 0: booleans[index] = tuple.getBool(field); break;
case 1: bits[index] = tuple.getByte(field); break;
case 2: shorts[index] = tuple.getInt2(field); break;
case 3: ints[index] = tuple.getInt4(field); break;
case 4: longs[index] = tuple.getInt8(field); break;
case 5: floats[index] = tuple.getFloat4(field); break;
case 6: doubles[index] = tuple.getFloat8(field); break;
case 7: bytes[index] = tuple.getBytes(field); break;
case 8: ints[index] = tuple.getInt4(field); break;
default:
throw new IllegalArgumentException();
}
}
protected final int compare(int index1, int index2) {
final boolean n1 = nulls.get(index1);
final boolean n2 = nulls.get(index2);
if (n1 && n2) {
return 0;
}
if (n1 ^ n2) {
int compVal = n1 ? 1 : -1;
return nullInvert ? -compVal : compVal;
}
int compare;
switch (type) {
case 0: compare = Booleans.compare(booleans[index1], booleans[index2]); break;
case 1: compare = bits[index1] - bits[index2]; break;
case 2: compare = Shorts.compare(shorts[index1], shorts[index2]); break;
case 3: compare = Ints.compare(ints[index1], ints[index2]); break;
case 4: compare = Longs.compare(longs[index1], longs[index2]); break;
case 5: compare = Floats.compare(floats[index1], floats[index2]); break;
case 6: compare = Doubles.compare(doubles[index1], doubles[index2]); break;
case 7: compare = TextDatum.COMPARATOR.compare(bytes[index1], bytes[index2]); break;
case 8: compare = UnsignedInts.compare(ints[index1], ints[index2]); break;
default:
throw new IllegalArgumentException();
}
return ascending ? compare : -compare;
}
}
public static class ComparableTuple {
private final TupleType[] keyTypes;
private final int[] keyIndex;
private final Object[] keys;
public ComparableTuple(Schema schema, int[] keyIndex) {
this(tupleTypes(schema, keyIndex), keyIndex);
}
public ComparableTuple(Schema schema, int start, int end) {
this(schema, toKeyIndex(start, end));
}
private ComparableTuple(TupleType[] keyTypes, int[] keyIndex) {
this.keyTypes = keyTypes;
this.keyIndex = keyIndex;
this.keys = new Object[keyIndex.length];
}
public int size() {
return keyIndex.length;
}
public void set(Tuple tuple) {
for (int i = 0; i < keyTypes.length; i++) {
final int field = keyIndex[i];
if (tuple.isBlankOrNull(field)) {
keys[i] = null;
continue;
}
switch (keyTypes[i]) {
case BOOLEAN: keys[i] = tuple.getBool(field); break;
case BIT: keys[i] = tuple.getByte(field); break;
case INT1:
case INT2: keys[i] = tuple.getInt2(field); break;
case INT4:
case DATE:
case INET4: keys[i] = tuple.getInt4(field); break;
case INT8:
case TIME:
case TIMESTAMP: keys[i] = tuple.getInt8(field); break;
case FLOAT4: keys[i] = tuple.getFloat4(field); break;
case FLOAT8: keys[i] = tuple.getFloat8(field); break;
case TEXT:
case CHAR:
case BLOB: keys[i] = tuple.getBytes(field); break;
case DATUM: keys[i] = tuple.asDatum(field); break;
default:
throw new IllegalArgumentException();
}
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ComparableTuple) {
ComparableTuple other = (ComparableTuple)obj;
for (int i = 0; i < keys.length; i++) {
final boolean n1 = keys[i] == null;
final boolean n2 = other.keys[i] == null;
if (n1 && n2) {
continue;
}
if (n1 ^ n2) {
return false;
}
switch (keyTypes[i]) {
case TEXT:
case CHAR:
case BLOB: if (!Arrays.equals((byte[])keys[i], (byte[])other.keys[i])) return false; continue;
default: if (!keys[i].equals(other.keys[i])) return false; continue;
}
}
return true;
}
return false;
}
public boolean equals(Tuple tuple) {
for (int i = 0; i < keys.length; i++) {
final int field = keyIndex[i];
final boolean n1 = keys[i] == null;
final boolean n2 = tuple.isBlankOrNull(field);
if (n1 && n2) {
continue;
}
if (n1 ^ n2) {
return false;
}
switch (keyTypes[i]) {
case BOOLEAN: if ((Boolean)keys[i] != tuple.getBool(field)) return false; continue;
case BIT: if ((Byte)keys[i] != tuple.getByte(field)) return false; continue;
case INT1:
case INT2: if ((Short)keys[i] != tuple.getInt2(field)) return false; continue;
case INT4:
case DATE:
case INET4: if ((Integer)keys[i] != tuple.getInt4(field)) return false; continue;
case INT8:
case TIME:
case TIMESTAMP: if ((Long)keys[i] != tuple.getInt8(field)) return false; continue;
case FLOAT4: if ((Float)keys[i] != tuple.getFloat4(field)) return false; continue;
case FLOAT8: if ((Double)keys[i] != tuple.getFloat8(field)) return false; continue;
case TEXT:
case CHAR:
case BLOB: if (!Arrays.equals((byte[])keys[i], tuple.getBytes(field))) return false; continue;
case DATUM: if (!keys[i].equals(tuple.asDatum(field))) return false; continue;
}
}
return true;
}
@Override
public int hashCode() {
int result = 1;
for (Object key : keys) {
int hash = key == null ? 0 :
key instanceof byte[] ? Arrays.hashCode((byte[])key) : key.hashCode();
result = 31 * result + hash;
}
return result;
}
public ComparableTuple copy() {
ComparableTuple copy = emptyCopy();
System.arraycopy(keys, 0, copy.keys, 0, keys.length);
return copy;
}
public ComparableTuple emptyCopy() {
return new ComparableTuple(keyTypes, keyIndex);
}
public Datum toDatum(int i) {
if (keys[i] == null) {
return NullDatum.get();
}
switch (keyTypes[i]) {
case NULL_TYPE: return NullDatum.get();
case BOOLEAN: return DatumFactory.createBool((Boolean) keys[i]);
case BIT: return DatumFactory.createBit((Byte)keys[i]);
case INT1:
case INT2: return DatumFactory.createInt2((Short) keys[i]);
case INT4: return DatumFactory.createInt4((Integer) keys[i]);
case DATE: return DatumFactory.createDate((Integer) keys[i]);
case INET4: return DatumFactory.createInet4((Integer) keys[i]);
case INT8: return DatumFactory.createInt8((Long) keys[i]);
case TIME: return DatumFactory.createTime((Long) keys[i]);
case TIMESTAMP: return DatumFactory.createTimestamp((Long) keys[i]);
case FLOAT4: return DatumFactory.createFloat4((Float) keys[i]);
case FLOAT8: return DatumFactory.createFloat8((Double) keys[i]);
case TEXT: return DatumFactory.createText((byte[]) keys[i]);
case CHAR: return DatumFactory.createChar((byte[]) keys[i]);
case BLOB: return DatumFactory.createBlob((byte[]) keys[i]);
case DATUM: return (Datum)keys[i];
default:
throw new IllegalArgumentException();
}
}
}
public static boolean isVectorizable(SortSpec[] sortKeys) {
if (sortKeys.length == 0) {
return false;
}
for (SortSpec spec : sortKeys) {
try {
vectorType(spec.getSortKey().getDataType().getType());
} catch (Exception e) {
return false;
}
}
return true;
}
private static int vectorType(TajoDataTypes.Type type) {
switch (type) {
case BOOLEAN: return 0;
case BIT: return 1;
case INT1: case INT2: return 2;
case INT4: case DATE: return 3;
case INT8: case TIME: case TIMESTAMP: case INTERVAL: return 4;
case FLOAT4: return 5;
case FLOAT8: return 6;
case TEXT: case CHAR: case BLOB: return 7;
case INET4: return 8;
case NULL_TYPE: return -1;
default:
throw new TajoRuntimeException(new UnsupportedException("data type '" + type.name() + "'"));
}
}
private static TupleType[] tupleTypes(Schema schema, int[] keyIndex) {
TupleType[] types = new TupleType[keyIndex.length];
for (int i = 0; i < keyIndex.length; i++) {
types[i] = tupleType(schema.getColumn(keyIndex[i]).getDataType().getType());
}
return types;
}
private static TupleType tupleType(TajoDataTypes.Type type) {
switch (type) {
case BOOLEAN: return TupleType.BOOLEAN;
case BIT: return TupleType.BIT;
case INT1: return TupleType.INT1;
case INT2: return TupleType.INT2;
case INT4: return TupleType.INT4;
case DATE: return TupleType.DATE;
case INT8: return TupleType.INT8;
case TIME: return TupleType.TIME;
case TIMESTAMP: return TupleType.TIMESTAMP;
case FLOAT4: return TupleType.FLOAT4;
case FLOAT8: return TupleType.FLOAT8;
case TEXT: return TupleType.TEXT;
case CHAR: return TupleType.CHAR;
case BLOB: return TupleType.BLOB;
case INET4: return TupleType.INET4;
case NULL_TYPE: return TupleType.NULL_TYPE;
default: return TupleType.DATUM;
}
}
private static int[] toKeyIndex(int start, int end) {
int[] keyIndex = new int[end - start];
for (int i = 0; i < keyIndex.length; i++) {
keyIndex[i] = start + i;
}
return keyIndex;
}
private static enum TupleType {
NULL_TYPE, BOOLEAN, BIT, INT1, INT2, INT4, DATE, INET4, INT8, TIME, TIMESTAMP,
FLOAT4, FLOAT8, TEXT, CHAR, BLOB, DATUM
}
}