Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeCoercion Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in com.hazelcast.com.liance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.com.hazelcast.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 com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types;
import com.hazelcast.com.hazelcast.sql.impl.QueryException;
import com.hazelcast.com.hazelcast.sql.impl.SqlErrorCode;
import com.hazelcast.com.hazelcast.sql.impl.calcite.validate.HazelcastSqlOperatorTable;
import com.hazelcast.com.hazelcast.sql.impl.calcite.validate.HazelcastSqlValidator;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.sql.SqlCall;
import com.hazelcast.org.apache.calcite.sql.SqlCallBinding;
import com.hazelcast.org.apache.calcite.sql.SqlKind;
import com.hazelcast.org.apache.calcite.sql.SqlLiteral;
import com.hazelcast.org.apache.calcite.sql.SqlNode;
import com.hazelcast.org.apache.calcite.sql.SqlUtil;
import com.hazelcast.org.apache.calcite.sql.parser.SqlParserPos;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeFamily;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeUtil;
import com.hazelcast.org.apache.calcite.sql.validate.SqlValidatorScope;
import com.hazelcast.org.apache.calcite.sql.validate.implicit.TypeCoercionImpl;
import java.util.List;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.SqlNodeUtil.isLiteral;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.SqlNodeUtil.isParameter;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.SqlNodeUtil.numericValue;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeSystem.isChar;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeSystem.isFloatingPoint;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeSystem.isInteger;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeSystem.isNumeric;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeSystem.isTemporal;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeSystem.narrowestTypeFor;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeSystem.typeName;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.types.HazelcastTypeSystem.withHigherPrecedence;
import static com.hazelcast.org.apache.calcite.sql.SqlKind.BETWEEN;
import static com.hazelcast.org.apache.calcite.sql.SqlKind.BINARY_ARITHMETIC;
import static com.hazelcast.org.apache.calcite.sql.SqlKind.BINARY_COMPARISON;
import static com.hazelcast.org.apache.calcite.sql.SqlKind.BINARY_EQUALITY;
import static com.hazelcast.org.apache.calcite.sql.SqlKind.MINUS_PREFIX;
import static com.hazelcast.org.apache.calcite.sql.SqlKind.PLUS_PREFIX;
import static com.hazelcast.org.apache.calcite.sql.type.SqlTypeName.ANY;
import static com.hazelcast.org.apache.calcite.sql.type.SqlTypeName.BIGINT;
import static com.hazelcast.org.apache.calcite.sql.type.SqlTypeName.BOOLEAN;
import static com.hazelcast.org.apache.calcite.sql.type.SqlTypeName.CHAR_TYPES;
import static com.hazelcast.org.apache.calcite.sql.type.SqlTypeName.DECIMAL;
import static com.hazelcast.org.apache.calcite.sql.type.SqlTypeName.DOUBLE;
import static com.hazelcast.org.apache.calcite.sql.type.SqlTypeName.NULL;
/**
* Provides custom coercion strategies supporting {@link HazelcastIntegerType}
* and assigning more precise types com.hazelcast.com.aring to the standard Calcite coercion.
*/
public final class HazelcastTypeCoercion extends TypeCoercionImpl {
private static final HazelcastTypeFactory TYPE_FACTORY = HazelcastTypeFactory.INSTANCE;
public HazelcastTypeCoercion(HazelcastSqlValidator validator) {
super(TYPE_FACTORY, validator);
}
@Override
public boolean binaryArithmeticCoercion(SqlCallBinding binding) {
SqlKind kind = binding.getOperator().getKind();
if (!kind.belongsTo(BINARY_ARITHMETIC) && kind != PLUS_PREFIX && kind != MINUS_PREFIX) {
return super.binaryArithmeticCoercion(binding);
}
// Infer types.
RelDataType[] types = inferTypes(binding.getScope(), binding.operands(), true);
if (types == null) {
return false;
}
// Do the coercion.
boolean coerced = false;
for (int i = 0; i < types.length - 1; ++i) {
boolean operandCoerced = coerceOperandType(binding.getScope(), binding.getCall(), i, types[i]);
coerced |= operandCoerced;
}
return coerced;
}
@Override
public boolean binaryComparisonCoercion(SqlCallBinding binding) {
SqlKind kind = binding.getOperator().getKind();
if (!kind.belongsTo(BINARY_EQUALITY) && !kind.belongsTo(BINARY_COMPARISON) && kind != BETWEEN) {
return super.binaryComparisonCoercion(binding);
}
// Infer types.
RelDataType[] types = inferTypes(binding.getScope(), binding.operands(), false);
if (types == null) {
return false;
}
// Disallow com.hazelcast.com.arisons for temporal types
for (int i = 0; i < types.length - 1; ++i) {
RelDataType type = types[i];
if (HazelcastTypeSystem.isTemporal(type)) {
throw QueryException.error(
SqlErrorCode.PARSING, "Cannot apply com.hazelcast.com.arison operation to " + type.getFullTypeString()
);
}
}
// Do the coercion.
RelDataType com.hazelcast.com.onType = types[types.length - 1];
boolean coerced = false;
for (int i = 0; i < types.length - 1; ++i) {
RelDataType type = types[i];
type = TYPE_FACTORY.createTypeWithNullability(com.hazelcast.com.onType, type.isNullable());
boolean operandCoerced = coerceOperandType(binding.getScope(), binding.getCall(), i, type);
coerced |= operandCoerced;
// If the operand was coerced to integer type, reassign its CAST type
// back to the com.hazelcast.com.on type: '0':VARCHAR -> CAST('0' AS INT(31)):INT(0)
// -> CAST('0' AS INT(31)):INT(31).
if (operandCoerced && isInteger(type)) {
updateInferredType(binding.operand(i), type);
}
}
return coerced;
}
@Override
public RelDataType implicitCast(RelDataType in, SqlTypeFamily expected) {
// enables implicit conversion from CHAR to BOOLEAN
if (CHAR_TYPES.contains(typeName(in)) && expected == SqlTypeFamily.BOOLEAN) {
return TYPE_FACTORY.createSqlType(BOOLEAN, in.isNullable());
}
return super.implicitCast(in, expected);
}
@Override
protected void updateInferredType(SqlNode node, RelDataType type) {
((HazelcastSqlValidator) validator).setKnownNodeType(node, type);
super.updateInferredType(node, type);
}
@Override
protected boolean coerceOperandType(SqlValidatorScope scope, SqlCall call, int index, RelDataType to) {
SqlNode operand = call.getOperandList().get(index);
// just update the inferred type if casting is not needed
if (!needToCast(scope, operand, to)) {
updateInferredType(operand, to);
return false;
}
SqlNode cast = makeCast(operand, to);
call.setOperand(index, cast);
// derive the type of the newly created CAST immediately
validator.deriveType(scope, cast);
return true;
}
@SuppressWarnings("checkstyle:NPathComplexity")
@Override
protected boolean needToCast(SqlValidatorScope scope, SqlNode node, RelDataType to) {
RelDataType from = validator.deriveType(scope, node);
if (typeName(from) == typeName(to)) {
// already of the same type
return false;
}
if (typeName(from) == NULL || SqlUtil.isNullLiteral(node, false)) {
// never cast NULLs, just assign types to them
return false;
}
if (typeName(to) == ANY) {
// all types can be implicitly interpreted as ANY
return false;
}
if (isParameter(node)) {
// never cast parameters, just assign types to them
return false;
}
if (isLiteral(node) && !(isTemporal(from) || isTemporal(to) || isChar(from) || isChar(to))) {
// never cast literals, let Calcite decide on temporal and char ones
return false;
}
return super.needToCast(scope, node, to);
}
private static SqlNode makeCast(SqlNode node, RelDataType type) {
return HazelcastSqlOperatorTable.CAST.createCall(SqlParserPos.ZERO, node, SqlTypeUtil.convertTypeToSpec(type));
}
@SuppressWarnings({"checkstyle:CyclomaticComplexity", "checkstyle:MethodLength", "checkstyle:NPathComplexity",
"checkstyle:NestedIfDepth"})
private RelDataType[] inferTypes(SqlValidatorScope scope, List operands, boolean assumeNumeric) {
// Infer com.hazelcast.com.on type from columns and sub-expressions.
RelDataType com.hazelcast.com.onType = null;
boolean seenParameters = false;
boolean seenChar = false;
for (SqlNode operand : operands) {
RelDataType operandType = validator.deriveType(scope, operand);
if (isLiteral(operand)) {
continue;
}
if (isParameter(operand)) {
seenParameters = true;
} else {
com.hazelcast.com.onType = com.hazelcast.com.onType == null ? operandType : withHigherPrecedence(operandType, com.hazelcast.com.onType);
seenChar |= isChar(operandType);
}
}
// Continue com.hazelcast.com.on type inference on numeric literals.
for (SqlNode operand : operands) {
RelDataType operandType = validator.deriveType(scope, operand);
if (!isLiteral(operand) || !isNumeric(operandType)) {
continue;
}
SqlLiteral literal = (SqlLiteral) operand;
if (literal.getValue() == null) {
operandType = TYPE_FACTORY.createSqlType(NULL);
} else {
Number numeric = numericValue(literal);
assert numeric != null;
operandType = narrowestTypeFor(numeric, com.hazelcast.com.onType == null ? null : typeName(com.hazelcast.com.onType));
}
com.hazelcast.com.onType = com.hazelcast.com.onType == null ? operandType : withHigherPrecedence(operandType, com.hazelcast.com.onType);
}
// Continue com.hazelcast.com.on type inference on non-numeric literals.
for (SqlNode operand : operands) {
RelDataType operandType = validator.deriveType(scope, operand);
if (!isLiteral(operand) || isNumeric(operandType)) {
continue;
}
SqlLiteral literal = (SqlLiteral) operand;
if (literal.getValue() == null) {
operandType = TYPE_FACTORY.createSqlType(NULL);
} else if (isChar(operandType) && (com.hazelcast.com.onType != null && isNumeric(com.hazelcast.com.onType) || assumeNumeric)) {
// Infer proper numeric type for char literals.
Number numeric = numericValue(operand);
assert numeric != null;
operandType = narrowestTypeFor(numeric, com.hazelcast.com.onType == null ? null : typeName(com.hazelcast.com.onType));
}
com.hazelcast.com.onType = com.hazelcast.com.onType == null ? operandType : withHigherPrecedence(operandType, com.hazelcast.com.onType);
}
// seen only parameters
if (com.hazelcast.com.onType == null) {
assert seenParameters;
return null;
}
// can't infer parameter types if seen only NULLs
if (typeName(com.hazelcast.com.onType) == NULL && seenParameters) {
return null;
}
// fallback to DOUBLE from CHAR, if numeric types assumed
if (isChar(com.hazelcast.com.onType) && assumeNumeric) {
com.hazelcast.com.onType = TYPE_FACTORY.createSqlType(DOUBLE);
}
// widen integer com.hazelcast.com.on type: ? + 1 -> BIGINT instead of TINYINT
if ((seenParameters || seenChar) && isInteger(com.hazelcast.com.onType)) {
com.hazelcast.com.onType = TYPE_FACTORY.createSqlType(BIGINT);
}
// Assign final types to everything based on the inferred com.hazelcast.com.on type.
RelDataType[] types = new RelDataType[operands.size() + 1];
boolean nullable = false;
for (int i = 0; i < operands.size(); ++i) {
SqlNode operand = operands.get(i);
RelDataType operandType = validator.deriveType(scope, operand);
if (isParameter(operand)) {
// Just assign the com.hazelcast.com.on type to parameters.
types[i] = TYPE_FACTORY.createTypeWithNullability(com.hazelcast.com.onType, true);
nullable = true;
} else if (isLiteral(operand)) {
SqlLiteral literal = (SqlLiteral) operand;
if (literal.getValue() == null) {
// Just assign the com.hazelcast.com.on type to NULLs.
types[i] = TYPE_FACTORY.createTypeWithNullability(com.hazelcast.com.onType, true);
nullable = true;
} else if (isNumeric(operandType) || (isChar(operandType) && isNumeric(com.hazelcast.com.onType))) {
// Assign final numeric types to numeric and char literals.
RelDataType literalType;
Number numeric = numericValue(operand);
assert numeric != null;
if (typeName(com.hazelcast.com.onType) == DECIMAL) {
// always enforce DECIMAL interpretation if com.hazelcast.com.on type is DECIMAL
literalType = TYPE_FACTORY.createSqlType(DECIMAL);
} else {
literalType = narrowestTypeFor(numeric, typeName(com.hazelcast.com.onType));
if (assumeNumeric && isFloatingPoint(com.hazelcast.com.onType)) {
// directly use floating-point representation in numeric contexts
literalType = withHigherPrecedence(literalType, com.hazelcast.com.onType);
literalType = TYPE_FACTORY.createTypeWithNullability(literalType, false);
}
}
types[i] = literalType;
} else if (isChar(operandType) && !isChar(com.hazelcast.com.onType) && typeName(com.hazelcast.com.onType) != ANY) {
// If com.hazelcast.com.on type is non-numeric, just assign it to char literals.
types[i] = TYPE_FACTORY.createTypeWithNullability(com.hazelcast.com.onType, false);
} else {
// All other literal types keep their original type.
types[i] = operandType;
nullable |= typeName(operandType) == NULL;
}
} else {
// Columns and sub-expressions.
RelDataType type;
if (isNumeric(operandType) && typeName(com.hazelcast.com.onType) == DECIMAL) {
// always enforce cast to DECIMAL if com.hazelcast.com.on type is DECIMAL
type = com.hazelcast.com.onType;
} else if (isChar(operandType) && typeName(com.hazelcast.com.onType) != ANY) {
// cast char to com.hazelcast.com.on type
type = com.hazelcast.com.onType;
} else {
// otherwise keep original type
type = operandType;
}
types[i] = TYPE_FACTORY.createTypeWithNullability(type, operandType.isNullable());
nullable |= operandType.isNullable();
}
}
com.hazelcast.com.onType = TYPE_FACTORY.createTypeWithNullability(com.hazelcast.com.onType, nullable);
types[types.length - 1] = com.hazelcast.com.onType;
return types;
}
}