All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.phoenix.expression.LiteralExpression Maven / Gradle / Ivy

The newest version!
/*
 * 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.phoenix.expression;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;

import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.WritableUtils;
import org.apache.phoenix.expression.visitor.ExpressionVisitor;
import org.apache.phoenix.schema.IllegalDataException;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.TypeMismatchException;
import org.apache.phoenix.schema.tuple.Tuple;
import org.apache.phoenix.schema.types.PArrayDataType;
import org.apache.phoenix.schema.types.PBoolean;
import org.apache.phoenix.schema.types.PChar;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PDate;
import org.apache.phoenix.schema.types.PTime;
import org.apache.phoenix.schema.types.PTimestamp;
import org.apache.phoenix.schema.types.PVarchar;
import org.apache.phoenix.schema.types.PhoenixArray;
import org.apache.phoenix.util.ByteUtil;
import org.apache.phoenix.util.StringUtil;

import com.google.common.base.Preconditions;



/**
 * 
 * Accessor for a literal value.
 *
 * 
 * @since 0.1
 */
public class LiteralExpression extends BaseTerminalExpression {
	private static final LiteralExpression[] NULL_EXPRESSIONS = new LiteralExpression[Determinism.values().length];
    private static final LiteralExpression[] TYPED_NULL_EXPRESSIONS = new LiteralExpression[PDataType.values().length * Determinism.values().length];
    private static final LiteralExpression[] BOOLEAN_EXPRESSIONS = new LiteralExpression[2 * Determinism.values().length];
    
    static {
    	for (Determinism determinism : Determinism.values()) {
    		NULL_EXPRESSIONS[determinism.ordinal()] = new LiteralExpression(null, determinism);
	        for (int i = 0; i < PDataType.values().length; i++) {
	            TYPED_NULL_EXPRESSIONS[i+PDataType.values().length*determinism.ordinal()] = new LiteralExpression(PDataType.values()[i], determinism);
	        }        
	        BOOLEAN_EXPRESSIONS[determinism.ordinal()] = new LiteralExpression(Boolean.FALSE,
              PBoolean.INSTANCE, PBoolean.INSTANCE.toBytes(Boolean.FALSE), determinism);
	        BOOLEAN_EXPRESSIONS[Determinism.values().length+determinism.ordinal()] = new LiteralExpression(Boolean.TRUE, PBoolean.INSTANCE, PBoolean.INSTANCE.toBytes(Boolean.TRUE), determinism);
    	}
    }
    
    private Object value;
    private PDataType type;
    private Determinism determinism;
    private byte[] byteValue;
    private Integer maxLength;
    private Integer scale;
    private SortOrder sortOrder;
    
    private static LiteralExpression getNullLiteralExpression(Determinism determinism) {
    	return NULL_EXPRESSIONS[determinism.ordinal()] ;
    }
    
    private static LiteralExpression getTypedNullLiteralExpression(PDataType type, Determinism determinism){
    	return TYPED_NULL_EXPRESSIONS[type.ordinal()+PDataType.values().length*determinism.ordinal()];
    }
    
    private static LiteralExpression getBooleanLiteralExpression(Boolean bool, Determinism determinism){
    	return BOOLEAN_EXPRESSIONS[ (Boolean.FALSE.equals(bool) ?  0 : Determinism.values().length) + determinism.ordinal()];
    }

    public static boolean isFalse(Expression child) {
        if (child!=null) {
            return child == BOOLEAN_EXPRESSIONS[child.getDeterminism().ordinal()];
        }
        return false;
    }
    
    public static boolean isTrue(Expression child) {
    	if (child!=null) {
    		return child == BOOLEAN_EXPRESSIONS[Determinism.values().length+child.getDeterminism().ordinal()];
    	}
    	return false;
    }

    public static boolean isBooleanNull(Expression child) {
    	if (child!=null) {
    		return child == TYPED_NULL_EXPRESSIONS[PBoolean.INSTANCE.ordinal()+PDataType.values().length*child.getDeterminism().ordinal()];
    	}
    	return false;
    }

    public static boolean isBooleanFalseOrNull(Expression child) {
        if (child!=null) {
            return child == BOOLEAN_EXPRESSIONS[child.getDeterminism().ordinal()]
                    || child == TYPED_NULL_EXPRESSIONS[PBoolean.INSTANCE.ordinal()+PDataType.values().length*child.getDeterminism().ordinal()];
        }
        return false;
    }
    
    public static LiteralExpression newConstant(Object value) {
        return newConstant(value, Determinism.ALWAYS);
    }
    
    // TODO: cache?
    public static LiteralExpression newConstant(Object value, Determinism determinism) {
        if (value instanceof Boolean) {
            return getBooleanLiteralExpression((Boolean)value, determinism);
        }
        else if (value == null) {
            return getNullLiteralExpression(determinism);
        }
        PDataType type = PDataType.fromLiteral(value);
        byte[] b = type.toBytes(value);
        if (type.isNull(b)) {
            return getTypedNullLiteralExpression(type, determinism);
        }
        if (type == PVarchar.INSTANCE) {
            String s = (String) value;
            if (s.length() == b.length) { // single byte characters only
                type = PChar.INSTANCE;
            }
        }
        return new LiteralExpression(value, type, b, determinism);
    }

    public static LiteralExpression newConstant(Object value, PDataType type) throws SQLException {
        return newConstant(value, type, Determinism.ALWAYS);
    }
    
    public static LiteralExpression newConstant(Object value, PDataType type, Determinism determinism) throws SQLException {
        return newConstant(value, type, SortOrder.getDefault(), determinism);
    }
    
    public static LiteralExpression newConstant(Object value, PDataType type, SortOrder sortOrder) throws SQLException {
        return newConstant(value, type, null, null, sortOrder, Determinism.ALWAYS);
    }
    
    public static LiteralExpression newConstant(Object value, PDataType type, SortOrder sortOrder, Determinism determinism) throws SQLException {
        return newConstant(value, type, null, null, sortOrder, determinism);
    }
    
    public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale) throws SQLException {
        return newConstant(value, type, maxLength, scale, SortOrder.getDefault(), Determinism.ALWAYS);
    }
    
    public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale, Determinism determinism) throws SQLException { // remove?
        return newConstant(value, type, maxLength, scale, SortOrder.getDefault(), determinism);
    }

    public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale, SortOrder sortOrder, Determinism determinism) 
            throws SQLException {
        return newConstant(value, type, maxLength, scale, sortOrder, determinism, true);
    }
    
    // TODO: cache?
    public static LiteralExpression newConstant(Object value, PDataType type, Integer maxLength, Integer scale, SortOrder sortOrder, Determinism determinism, boolean rowKeyOrderOptimizable)
            throws SQLException {
        if (value == null) {
            return  (type == null) ?  getNullLiteralExpression(determinism) : getTypedNullLiteralExpression(type, determinism);
        }
        else if (value instanceof Boolean) {
            return getBooleanLiteralExpression((Boolean)value, determinism);
        }
        PDataType actualType = PDataType.fromLiteral(value);
        type = type == null ? actualType : type;
        try {
            value = type.toObject(value, actualType);
        } catch (IllegalDataException e) {
            throw TypeMismatchException.newException(type, actualType, value.toString());
        }
        byte[] b = type.isArrayType() ? ((PArrayDataType)type).toBytes(value, PArrayDataType.arrayBaseType(type), sortOrder, rowKeyOrderOptimizable) :
                type.toBytes(value, sortOrder);
        if (type == PVarchar.INSTANCE || type == PChar.INSTANCE) {
            if (type == PChar.INSTANCE && maxLength != null  && b.length < maxLength) {
                if (rowKeyOrderOptimizable) {
                    b = type.pad(b, maxLength, sortOrder);
                } else {
                    b = StringUtil.padChar(b, maxLength);
                }
            } else if (value != null) {
                maxLength = ((String)value).length();
            }
        } else if (type.isArrayType()) {
            maxLength = ((PhoenixArray)value).getMaxLength();
        }
        if (b.length == 0) {
            return getTypedNullLiteralExpression(type, determinism);
        }
        if (maxLength == null) {
            maxLength = type.isFixedWidth() ? type.getMaxLength(value) : null;
        }
        return new LiteralExpression(value, type, b, maxLength, scale, sortOrder, determinism);
    }

    public LiteralExpression() {
    }
    
    public LiteralExpression(byte[] byteValue) {
        this.byteValue = byteValue!=null ? byteValue : ByteUtil.EMPTY_BYTE_ARRAY;
        this.determinism = Determinism.ALWAYS;
    }

    private LiteralExpression(PDataType type, Determinism determinism) {
        this(null, type, ByteUtil.EMPTY_BYTE_ARRAY, determinism);
    }

    private LiteralExpression(Object value, PDataType type, byte[] byteValue, Determinism determinism) {
        this(value, type, byteValue, type == null || !type.isFixedWidth() ? null : type.getMaxLength(value), null, SortOrder.getDefault(), determinism);
    }

    private LiteralExpression(Object value, PDataType type, byte[] byteValue,
            Integer maxLength, Integer scale, SortOrder sortOrder, Determinism deterministic) {
    	Preconditions.checkNotNull(sortOrder);
        this.value = value;
        this.type = type;
        this.byteValue = byteValue;
        this.maxLength = maxLength;
        this.scale = scale != null ? scale : type == null ? null : type.getScale(value);
        this.sortOrder = sortOrder;
        this.determinism = deterministic;
    }

    @Override
    public Determinism getDeterminism() {
        return determinism;
    }
    
    @Override
    public String toString() {
        if (value == null && byteValue!=null) {
            return Bytes.toStringBinary(byteValue);
        }
        else if (value == null) {
            return "null";
        }
        // TODO: move into PDataType?
        if (type.isCoercibleTo(PTimestamp.INSTANCE)) {
            return type + " " + type.toStringLiteral(value, null);
        }
        return type.toStringLiteral(value, null);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((value == null) ? 0 : value.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        LiteralExpression other = (LiteralExpression)obj;
        if (value == null) {
            if (other.value != null) return false;
        } else if (!value.equals(other.value)) return false;
        return true;
    }

    @Override
    public void readFields(DataInput input) throws IOException {
    	int encodedByteLengthAndBool = WritableUtils.readVInt(input);
        int byteLength = Math.abs(encodedByteLengthAndBool)-1;
        this.byteValue = new byte[byteLength];
        input.readFully(byteValue, 0, byteLength);
        int sortOrderAndDeterminism = WritableUtils.readVInt(input);
        if (sortOrderAndDeterminism<=2) {
        	//client is on an older version
        	this.determinism = encodedByteLengthAndBool > 0 ? Determinism.ALWAYS : Determinism.PER_ROW;  	
        	this.sortOrder = SortOrder.fromSystemValue(sortOrderAndDeterminism);;
        }
        else {
        	int determinismOrdinal = (sortOrderAndDeterminism>>2)-1;
        	this.determinism = Determinism.values()[determinismOrdinal];
        	int sortOrderValue = sortOrderAndDeterminism & ((1 << 2) - 1); //get the least 2 significant bits
        	this.sortOrder = SortOrder.fromSystemValue(sortOrderValue);
        } 
        int typeOrdinal = WritableUtils.readVInt(input);
        if (typeOrdinal < 0) {
            this.type = null;
        } else {
            this.type = PDataType.values()[typeOrdinal];
        }
        if (this.byteValue.length == 0) {
            this.value = null;
        } else {
            this.value = this.type.toObject(byteValue, 0, byteValue.length, this.type, sortOrder);
        }
    }

    @Override
    public void write(DataOutput output) throws IOException {
    	WritableUtils.writeVInt(output, (byteValue.length + 1) * (this.determinism==Determinism.ALWAYS ? 1 : -1));
        output.write(byteValue);
        // since we need to support clients of a lower version, serialize the determinism enum ordinal in the int used to 
        // serialize sort order system value (which is either 1 or 2)
        int sortOrderAndDeterminism = ((this.determinism.ordinal()+1)<<2) + sortOrder.getSystemValue();
        WritableUtils.writeVInt(output, sortOrderAndDeterminism);
        WritableUtils.writeVInt(output, this.type == null ? -1 : this.type.ordinal());
    }

    @Override
    public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
        // Literal always evaluates, even when it returns null
        ptr.set(byteValue);
        return true;
    }

    @Override
    public PDataType getDataType() {
        return type;
    }

    @Override
    public Integer getMaxLength() {
        // For literals representing arrays of CHAR or BINARY, the byte size is null and the max
        // length of the expression is also null, so we must get the max length of the
        // actual underlying array
        if (maxLength == null && getDataType() != null && getDataType().isArrayType() &&
                PDataType.arrayBaseType(getDataType()).getByteSize() == null) {
            Object value = getValue();
            if (value instanceof PhoenixArray) {
                // Return the max length of the underlying PhoenixArray data
                return ((PhoenixArray) value).getMaxLength();
            }
        }
        return maxLength;
    }

    @Override
    public Integer getScale() {
        return scale;
    }
    
    @Override
    public SortOrder getSortOrder() {
        return sortOrder;
    }

    @Override
    public boolean isNullable() {
        return value == null;
    }

    public Object getValue() {
        return value;
    }

    public byte[] getBytes() {
        return byteValue;
    }
    
    @Override
    public final  T accept(ExpressionVisitor visitor) {
        return visitor.visit(this);
    }
    
    @Override
    public boolean isStateless() {
        return true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy