// "." : FIELD Expression
assert (children.size() == 2);
// Only allow constant field name for now
assert (children.get(1) instanceof ExprNodeConstantDesc);
ExprNodeDesc object = children.get(0);
ExprNodeConstantDesc fieldName = (ExprNodeConstantDesc) children.get(1);
assert (fieldName.getValue() instanceof String);
// Calculate result TypeInfo
String fieldNameString = (String) fieldName.getValue();
TypeInfo objectTypeInfo = object.getTypeInfo();
// Allow accessing a field of list element structs directly from a list
boolean isList = (object.getTypeInfo().getCategory() == ObjectInspector.Category.LIST);
if (isList) {
objectTypeInfo = ((ListTypeInfo) objectTypeInfo)
.getListElementTypeInfo();
}
if (objectTypeInfo.getCategory() != Category.STRUCT) {
throw new SemanticException(ErrorMsg.INVALID_DOT.getMsg(expr));
}
TypeInfo t = ((StructTypeInfo) objectTypeInfo)
.getStructFieldTypeInfo(fieldNameString);
if (isList) {
t = TypeInfoFactory.getListTypeInfo(t);
}
desc = new ExprNodeFieldDesc(t, children.get(0), fieldNameString,
isList);
} else if (funcText.equals("[")) {
// "[]" : LSQUARE/INDEX Expression
assert (children.size() == 2);
// Check whether this is a list or a map
TypeInfo myt = children.get(0).getTypeInfo();
if (myt.getCategory() == Category.LIST) {
// Only allow integer index for now
if (!(children.get(1) instanceof ExprNodeConstantDesc)
|| !(((ExprNodeConstantDesc) children.get(1)).getTypeInfo()
.equals(TypeInfoFactory.intTypeInfo))) {
throw new SemanticException(SemanticAnalyzer.generateErrorMessage(
expr,
ErrorMsg.INVALID_ARRAYINDEX_CONSTANT.getMsg()));
}
// Calculate TypeInfo
TypeInfo t = ((ListTypeInfo) myt).getListElementTypeInfo();
desc = new ExprNodeGenericFuncDesc(t, FunctionRegistry
.getGenericUDFForIndex(), children);
} else if (myt.getCategory() == Category.MAP) {
// Only allow constant map key for now
if (!(children.get(1) instanceof ExprNodeConstantDesc)) {
throw new SemanticException(SemanticAnalyzer.generateErrorMessage(
expr,
ErrorMsg.INVALID_MAPINDEX_CONSTANT.getMsg()));
}
if (!(((ExprNodeConstantDesc) children.get(1)).getTypeInfo()
.equals(((MapTypeInfo) myt).getMapKeyTypeInfo()))) {
throw new SemanticException(ErrorMsg.INVALID_MAPINDEX_TYPE
.getMsg(expr));
}
// Calculate TypeInfo
TypeInfo t = ((MapTypeInfo) myt).getMapValueTypeInfo();
desc = new ExprNodeGenericFuncDesc(t, FunctionRegistry
.getGenericUDFForIndex(), children);
} else {
throw new SemanticException(ErrorMsg.NON_COLLECTION_TYPE.getMsg(expr,
myt.getTypeName()));
}
} else {
// other operators or functions
FunctionInfo fi = FunctionRegistry.getFunctionInfo(funcText);
if (fi == null) {
if (isFunction) {
throw new SemanticException(ErrorMsg.INVALID_FUNCTION
.getMsg((ASTNode) expr.getChild(0)));
} else {
throw new SemanticException(ErrorMsg.INVALID_FUNCTION.getMsg(expr));
}
}
// getGenericUDF() actually clones the UDF. Just call it once and reuse.
GenericUDF genericUDF = fi.getGenericUDF();
if (!fi.isNative()) {
ctx.getUnparseTranslator().addIdentifierTranslation(
(ASTNode) expr.getChild(0));
}
// Handle type casts that may contain type parameters
if (isFunction) {
ASTNode funcNameNode = (ASTNode)expr.getChild(0);
switch (funcNameNode.getType()) {
case HiveParser.TOK_VARCHAR:
// Add type params
VarcharTypeParams varcharTypeParams = new VarcharTypeParams();
varcharTypeParams.length = Integer.valueOf((funcNameNode.getChild(0).getText()));
if (genericUDF != null) {
((SettableUDF)genericUDF).setParams(varcharTypeParams);
}
break;
default:
// Do nothing
break;
}
}
// Detect UDTF's in nested SELECT, GROUP BY, etc as they aren't
// supported
if (fi.getGenericUDTF() != null) {
throw new SemanticException(ErrorMsg.UDTF_INVALID_LOCATION.getMsg());
}
// UDAF in filter condition, group-by caluse, param of funtion, etc.
if (fi.getGenericUDAFResolver() != null) {
if (isFunction) {
throw new SemanticException(ErrorMsg.UDAF_INVALID_LOCATION.
getMsg((ASTNode) expr.getChild(0)));
} else {
throw new SemanticException(ErrorMsg.UDAF_INVALID_LOCATION.getMsg(expr));
}
}
if (!ctx.getAllowStatefulFunctions() && (genericUDF != null)) {
if (FunctionRegistry.isStateful(genericUDF)) {
throw new SemanticException(
ErrorMsg.UDF_STATEFUL_INVALID_LOCATION.getMsg());
}
}
// Try to infer the type of the constant only if there are two
// nodes, one of them is column and the other is numeric const
if (genericUDF instanceof GenericUDFBaseCompare
&& children.size() == 2
&& ((children.get(0) instanceof ExprNodeConstantDesc
&& children.get(1) instanceof ExprNodeColumnDesc)
|| (children.get(0) instanceof ExprNodeColumnDesc
&& children.get(1) instanceof ExprNodeConstantDesc))) {
int constIdx =
children.get(0) instanceof ExprNodeConstantDesc ? 0 : 1;
Set<String> inferTypes = new HashSet<String>(Arrays.asList(
serdeConstants.TINYINT_TYPE_NAME.toLowerCase(),
serdeConstants.SMALLINT_TYPE_NAME.toLowerCase(),
serdeConstants.INT_TYPE_NAME.toLowerCase(),
serdeConstants.BIGINT_TYPE_NAME.toLowerCase(),
serdeConstants.FLOAT_TYPE_NAME.toLowerCase(),
serdeConstants.DOUBLE_TYPE_NAME.toLowerCase(),
serdeConstants.STRING_TYPE_NAME.toLowerCase()
));
String constType = children.get(constIdx).getTypeString().toLowerCase();
String columnType = children.get(1 - constIdx).getTypeString().toLowerCase();
if (inferTypes.contains(constType) && inferTypes.contains(columnType)
&& !columnType.equalsIgnoreCase(constType)) {
Object originalValue = ((ExprNodeConstantDesc) children.get(constIdx)).getValue();
String constValue = originalValue.toString();
boolean triedDouble = false;
Number value = null;
try {
if (columnType.equalsIgnoreCase(serdeConstants.TINYINT_TYPE_NAME)) {
value = new Byte(constValue);
} else if (columnType.equalsIgnoreCase(serdeConstants.SMALLINT_TYPE_NAME)) {
value = new Short(constValue);
} else if (columnType.equalsIgnoreCase(serdeConstants.INT_TYPE_NAME)) {
value = new Integer(constValue);
} else if (columnType.equalsIgnoreCase(serdeConstants.BIGINT_TYPE_NAME)) {
value = new Long(constValue);
} else if (columnType.equalsIgnoreCase(serdeConstants.FLOAT_TYPE_NAME)) {
value = new Float(constValue);
} else if (columnType.equalsIgnoreCase(serdeConstants.DOUBLE_TYPE_NAME)) {
triedDouble = true;
value = new Double(constValue);
} else if (columnType.equalsIgnoreCase(serdeConstants.STRING_TYPE_NAME)) {
// Don't scramble the const type information if comparing to a string column,
// It's not useful to do so; as of now, there is also a hack in
// SemanticAnalyzer#genTablePlan that causes every column to look like a string
// a string down here, so number type information is always lost otherwise.
boolean isNumber = (originalValue instanceof Number);
triedDouble = !isNumber;
value = isNumber ? (Number)originalValue : new Double(constValue);
}
} catch (NumberFormatException nfe) {
// this exception suggests the precise type inference did not succeed
// we'll try again to convert it to double
// however, if we already tried this, or the column is NUMBER type and
// the operator is EQUAL, return false due to the type mismatch
if (triedDouble ||
(genericUDF instanceof GenericUDFOPEqual
&& !columnType.equals(serdeConstants.STRING_TYPE_NAME))) {
return new ExprNodeConstantDesc(false);
}
try {
value = new Double(constValue);
} catch (NumberFormatException ex) {
return new ExprNodeConstantDesc(false);
}
}
if (value != null) {
children.set(constIdx, new ExprNodeConstantDesc(value));
}
}
}
desc = ExprNodeGenericFuncDesc.newInstance(genericUDF, children);