blob: 6f0f26297034e8acd3f1a7edb27bfe2ada783362 [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.phoenix.expression.function;
import java.util.List;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.parse.FunctionParseNode.Argument;
import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.tuple.Tuple;
import org.apache.phoenix.schema.types.PChar;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PInteger;
import org.apache.phoenix.schema.types.PVarchar;
import org.apache.phoenix.util.ByteUtil;
import org.apache.phoenix.util.StringUtil;
/**
* Implementation of LPAD(input string, length int [, fill string])
*
* Fills up the input to length (number of characters) by prepending characters in fill (space by default). If the input
* is already longer than length then it is truncated on the right.
*/
@BuiltInFunction(name = LpadFunction.NAME, args = { @Argument(allowedTypes = { PVarchar.class }),
@Argument(allowedTypes = { PInteger.class }),
@Argument(allowedTypes = { PVarchar.class }, defaultValue = "' '") })
public class LpadFunction extends ScalarFunction {
public static final String NAME = "LPAD";
public LpadFunction() {
}
public LpadFunction(List<Expression> children) {
super(children);
}
/**
* Helper function to get the utf8 length of CHAR or VARCHAR
*
* @param ptr
* points to the string
* @param sortOrder
* sortOrder of the string
* @param isCharType
* whether the string is of char type
* @return utf8 length of the string
*/
private int getUTF8Length(ImmutableBytesWritable ptr, SortOrder sortOrder, boolean isCharType) {
return isCharType ? ptr.getLength() : StringUtil.calculateUTF8Length(ptr.get(), ptr.getOffset(),
ptr.getLength(), sortOrder);
}
/**
* Helper function to get the byte length of a utf8 encoded string
*
* @param ptr
* points to the string
* @param sortOrder
* sortOrder of the string
* @param isCharType
* whether the string is of char type
* @return byte length of the string
*/
private int getSubstringByteLength(ImmutableBytesWritable ptr, int length, SortOrder sortOrder, boolean isCharType) {
return isCharType ? length : StringUtil.getByteLengthForUtf8SubStr(ptr.get(), ptr.getOffset(), length,
sortOrder);
}
/**
* Left pads a string with with the given fill expression.
*/
@Override
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
Expression outputStrLenExpr = getOutputStrLenExpr();
if (!outputStrLenExpr.evaluate(tuple, ptr)) {
return false;
}
if (ptr.getLength()==0) {
return true;
}
int outputStrLen = outputStrLenExpr.getDataType().getCodec().decodeInt(ptr, outputStrLenExpr.getSortOrder());
if (outputStrLen < 0) {
return false;
}
Expression strExp = getStrExpr();
if (!strExp.evaluate(tuple, ptr)) {
return false;
}
boolean isStrCharType = getStrExpr().getDataType() == PChar.INSTANCE;
boolean isFillCharType = getFillExpr().getDataType() == PChar.INSTANCE;
SortOrder strSortOrder = getStrExpr().getSortOrder();
SortOrder fillSortOrder = getFillExpr().getSortOrder();
int inputStrLen = getUTF8Length(ptr, strSortOrder, isStrCharType);
if (outputStrLen == inputStrLen) {
// nothing to do
return true;
}
if (outputStrLen < inputStrLen) {
// truncate the string from the right
int subStrByteLength = getSubstringByteLength(ptr, outputStrLen, strSortOrder, isStrCharType);
ptr.set(ptr.get(), ptr.getOffset(), subStrByteLength);
return true;
}
// left pad the input string with the fill chars
Expression fillExpr = getFillExpr();
ImmutableBytesWritable fillPtr = new ImmutableBytesWritable();
if (!fillExpr.evaluate(tuple, fillPtr)) {
return false;
}
if (fillPtr.getLength()==0) {
ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
return true;
}
// if the padding to be added is not a multiple of the length of the
// fill string then we need to use part of the fill string to pad
// LPAD(ab, 5, xy) = xyxab
// padLen = 3
// numFillsPrepended = 1
// numFillCharsPrepended = 1
// length of the fill string
int fillLen = getUTF8Length(fillPtr, fillSortOrder, isFillCharType);
// length of padding to be added
int padLen = outputStrLen - inputStrLen;
// number of fill strings to be prepended
int numFillsPrepended = padLen / fillLen;
// number of chars from fill string to be prepended
int numFillCharsPrepended = padLen % fillLen;
// byte length of the input string
int strByteLength = ptr.getLength();
// byte length of the fill string
int fillByteLength = getSubstringByteLength(fillPtr, fillPtr.getLength(), fillSortOrder, isFillCharType);
// byte length of the full fills to be prepended
int fullFillsByteLength = numFillsPrepended * fillByteLength;
// byte length of the chars of fill string to be prepended
int fillCharsByteLength = getSubstringByteLength(fillPtr, numFillCharsPrepended, fillSortOrder, isFillCharType);
// byte length of the padded string =
int strWithPaddingByteLength = fullFillsByteLength + fillCharsByteLength + strByteLength;
// need to invert the fill string if the sort order of fill and
// input are different
boolean invertFill = fillSortOrder != strSortOrder;
byte[] paddedStr =
StringUtil.lpad(ptr.get(), ptr.getOffset(), ptr.getLength(), fillPtr.get(), fillPtr.getOffset(),
fillPtr.getLength(), invertFill, strWithPaddingByteLength);
ptr.set(paddedStr);
return true;
}
@Override
public PDataType getDataType() {
return PVarchar.INSTANCE;
}
@Override
public String getName() {
return NAME;
}
@Override
public SortOrder getSortOrder() {
return getStrExpr().getSortOrder();
}
private Expression getStrExpr() {
return children.get(0);
}
private Expression getFillExpr() {
return children.get(2);
}
private Expression getOutputStrLenExpr() {
return children.get(1);
}
}