Java Code Examples for org.apache.flink.table.types.logical.LogicalType#isNullable()

The following examples show how to use org.apache.flink.table.types.logical.LogicalType#isNullable() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: ArrowUtils.java    From flink with Apache License 2.0 6 votes vote down vote up
private static Field toArrowField(String fieldName, LogicalType logicalType) {
	FieldType fieldType = new FieldType(
		logicalType.isNullable(),
		logicalType.accept(LogicalTypeToArrowTypeConverter.INSTANCE),
		null);
	List<Field> children = null;
	if (logicalType instanceof ArrayType) {
		children = Collections.singletonList(toArrowField(
			"element", ((ArrayType) logicalType).getElementType()));
	} else if (logicalType instanceof RowType) {
		RowType rowType = (RowType) logicalType;
		children = new ArrayList<>(rowType.getFieldCount());
		for (RowType.RowField field : rowType.getFields()) {
			children.add(toArrowField(field.getName(), field.getType()));
		}
	}
	return new Field(fieldName, fieldType, children);
}
 
Example 2
Source File: LogicalTypeCasts.java    From flink with Apache License 2.0 6 votes vote down vote up
@Override
protected Boolean defaultMethod(LogicalType targetType) {
	// quick path
	if (sourceType == targetType) {
		return true;
	}

	if (sourceType.isNullable() && !targetType.isNullable() ||
			sourceType.getClass() != targetType.getClass() || // TODO drop this line once we remove legacy types
			sourceType.getTypeRoot() != targetType.getTypeRoot()) {
		return false;
	}

	final List<LogicalType> sourceChildren = sourceType.getChildren();
	final List<LogicalType> targetChildren = targetType.getChildren();
	if (sourceChildren.isEmpty()) {
		// handles all types that are not of family CONSTRUCTED or USER DEFINED
		return sourceType.equals(targetType) || sourceType.copy(true).equals(targetType);
	} else {
		// handles all types of CONSTRUCTED family as well as distinct types
		return supportsAvoidingCast(sourceChildren, targetChildren);
	}
}
 
Example 3
Source File: DataTypeUtils.java    From flink with Apache License 2.0 6 votes vote down vote up
@Override
public DataType visit(KeyValueDataType keyValueDataType) {
	DataType newKeyType = keyValueDataType.getKeyDataType().accept(this);
	DataType newValueType = keyValueDataType.getValueDataType().accept(this);
	LogicalType logicalType = keyValueDataType.getLogicalType();
	LogicalType newLogicalType;
	if (logicalType instanceof MapType) {
		newLogicalType = new MapType(
			logicalType.isNullable(),
			newKeyType.getLogicalType(),
			newValueType.getLogicalType());
	} else {
		throw new UnsupportedOperationException("Unsupported logical type : " + logicalType);
	}
	return transformation.transform(new KeyValueDataType(newLogicalType, newKeyType, newValueType));
}
 
Example 4
Source File: HBaseSerde.java    From flink with Apache License 2.0 6 votes vote down vote up
private static FieldEncoder createNullableFieldEncoder(LogicalType fieldType, final byte[] nullStringBytes) {
	final FieldEncoder encoder = createFieldEncoder(fieldType);
	if (fieldType.isNullable()) {
		if (hasFamily(fieldType, LogicalTypeFamily.CHARACTER_STRING)) {
			// special logic for null string values, because HBase can store empty bytes for string
			return (row, pos) -> {
				if (row.isNullAt(pos)) {
					return nullStringBytes;
				} else {
					return encoder.encode(row, pos);
				}
			};
		} else {
			// encode empty bytes for null values
			return (row, pos) -> {
				if (row.isNullAt(pos)) {
					return EMPTY_BYTES;
				} else {
					return encoder.encode(row, pos);
				}
			};
		}
	} else {
		return encoder;
	}
}
 
Example 5
Source File: HBaseSerde.java    From flink with Apache License 2.0 6 votes vote down vote up
private static FieldDecoder createNullableFieldDecoder(LogicalType fieldType, final byte[] nullStringBytes) {
	final FieldDecoder decoder = createFieldDecoder(fieldType);
	if (fieldType.isNullable()) {
		if (hasFamily(fieldType, LogicalTypeFamily.CHARACTER_STRING)) {
			return value -> {
				if (value == null || Arrays.equals(value, nullStringBytes)) {
					return null;
				} else {
					return decoder.decode(value);
				}
			};
		} else {
			return value -> {
				if (value == null || value.length == 0) {
					return null;
				} else {
					return decoder.decode(value);
				}
			};
		}
	} else {
		return decoder;
	}
}
 
Example 6
Source File: DataTypeUtils.java    From flink with Apache License 2.0 6 votes vote down vote up
@Override
public DataType visit(CollectionDataType collectionDataType) {
	DataType newElementType = collectionDataType.getElementDataType().accept(this);
	LogicalType logicalType = collectionDataType.getLogicalType();
	LogicalType newLogicalType;
	if (logicalType instanceof ArrayType) {
		newLogicalType = new ArrayType(
			logicalType.isNullable(),
			newElementType.getLogicalType());
	} else if (logicalType instanceof MultisetType){
		newLogicalType = new MultisetType(
			logicalType.isNullable(),
			newElementType.getLogicalType());
	} else {
		throw new UnsupportedOperationException("Unsupported logical type : " + logicalType);
	}
	return transformation.transform(new CollectionDataType(newLogicalType, newElementType));
}
 
Example 7
Source File: RootArgumentTypeStrategy.java    From flink with Apache License 2.0 6 votes vote down vote up
@Override
public Optional<DataType> inferArgumentType(CallContext callContext, int argumentPos, boolean throwOnFailure) {
	final DataType actualDataType = callContext.getArgumentDataTypes().get(argumentPos);
	final LogicalType actualType = actualDataType.getLogicalType();

	if (Objects.equals(expectedNullability, Boolean.FALSE) && actualType.isNullable()) {
		if (throwOnFailure) {
			throw callContext.newValidationError(
				"Unsupported argument type. Expected nullable type of root '%s' but actual type was '%s'.",
				expectedRoot,
				actualType);
		}
		return Optional.empty();
	}

	return findDataType(
		callContext,
		throwOnFailure,
		actualDataType,
		expectedRoot,
		expectedNullability);
}
 
Example 8
Source File: LogicalTypeMerging.java    From flink with Apache License 2.0 5 votes vote down vote up
/**
 * Returns the most common, more general {@link LogicalType} for a given set of types. If such
 * a type exists, all given types can be casted to this more general type.
 *
 * <p>For example: {@code [INT, BIGINT, DECIMAL(2, 2)]} would lead to {@code DECIMAL(21, 2)}.
 *
 * <p>This class aims to be compatible with the SQL standard. It is inspired by Apache Calcite's
 * {@code SqlTypeFactoryImpl#leastRestrictive} method.
 */
public static Optional<LogicalType> findCommonType(List<LogicalType> types) {
	Preconditions.checkArgument(types.size() > 0, "List of types must not be empty.");

	// collect statistics first
	boolean hasRawType = false;
	boolean hasNullType = false;
	boolean hasNullableTypes = false;
	for (LogicalType type : types) {
		final LogicalTypeRoot typeRoot = type.getTypeRoot();
		if (typeRoot == RAW) {
			hasRawType = true;
		} else if (typeRoot == NULL) {
			hasNullType = true;
		}
		if (type.isNullable()) {
			hasNullableTypes = true;
		}
	}

	final List<LogicalType> normalizedTypes = types.stream()
		.map(t -> t.copy(true))
		.collect(Collectors.toList());

	LogicalType foundType = findCommonNullableType(normalizedTypes, hasRawType, hasNullType);
	if (foundType == null) {
		foundType = findCommonCastableType(normalizedTypes);
	}

	if (foundType != null) {
		final LogicalType typeWithNullability = foundType.copy(hasNullableTypes);
		// NULL is reserved for untyped literals only
		if (hasRoot(typeWithNullability, NULL)) {
			return Optional.empty();
		}
		return Optional.of(typeWithNullability);
	}
	return Optional.empty();
}
 
Example 9
Source File: HiveFunctionUtil.java    From flink with Apache License 2.0 5 votes vote down vote up
private static boolean isPrimitiveArray(DataType dataType) {
	if (isArrayType(dataType)) {
		ArrayType arrayType = (ArrayType) dataType.getLogicalType();

		LogicalType elementType = arrayType.getElementType();
		return !(elementType.isNullable() || !isPrimitive(elementType));
	} else {
		return false;
	}
}
 
Example 10
Source File: LegacyDecimalTypeTransformation.java    From flink with Apache License 2.0 5 votes vote down vote up
@Override
public DataType transform(DataType typeToTransform) {
	LogicalType logicalType = typeToTransform.getLogicalType();
	if (logicalType instanceof LegacyTypeInformationType && logicalType.getTypeRoot() == LogicalTypeRoot.DECIMAL) {
		DataType decimalType = DataTypes
			.DECIMAL(DecimalType.MAX_PRECISION, 18)
			.bridgedTo(typeToTransform.getConversionClass());
		return logicalType.isNullable() ? decimalType : decimalType.notNull();
	}
	return typeToTransform;
}
 
Example 11
Source File: LegacyRawTypeTransformation.java    From flink with Apache License 2.0 5 votes vote down vote up
@Override
public DataType transform(DataType typeToTransform) {
	LogicalType logicalType = typeToTransform.getLogicalType();
	if (logicalType instanceof LegacyTypeInformationType && logicalType.getTypeRoot() == LogicalTypeRoot.RAW) {
		TypeInformation<?> typeInfo = ((LegacyTypeInformationType<?>) logicalType).getTypeInformation();
		DataType rawType = DataTypes.RAW(typeInfo).bridgedTo(typeInfo.getTypeClass());
		return logicalType.isNullable() ? rawType : rawType.notNull();
	}
	return typeToTransform;
}
 
Example 12
Source File: ValueLiteralExpression.java    From flink with Apache License 2.0 5 votes vote down vote up
private static void validateValueDataType(Object value, DataType dataType) {
	final LogicalType logicalType = dataType.getLogicalType();
	if (value == null) {
		if (!logicalType.isNullable()) {
			throw new ValidationException(
				String.format(
					"Data type '%s' does not support null values.",
					dataType));
		}
		return;
	}

	if (logicalType.isNullable()) {
		throw new ValidationException("Literals that have a non-null value must not have a nullable data type.");
	}

	final Class<?> candidate = value.getClass();
	// ensure value and data type match
	if (!dataType.getConversionClass().isAssignableFrom(candidate)) {
		throw new ValidationException(
			String.format(
				"Data type '%s' with conversion class '%s' does not support a value literal of class '%s'.",
				dataType,
				dataType.getConversionClass().getName(),
				value.getClass().getName()));
	}
	// check for proper input as this cannot be checked in data type
	if (!logicalType.supportsInputConversion(candidate)) {
		throw new ValidationException(
			String.format(
				"Data type '%s' does not support a conversion from class '%s'.",
				dataType,
				candidate.getName()));
	}
}
 
Example 13
Source File: HiveFunctionUtil.java    From flink with Apache License 2.0 5 votes vote down vote up
private static boolean isPrimitiveArray(DataType dataType) {
	if (isArrayType(dataType)) {
		ArrayType arrayType = (ArrayType) dataType.getLogicalType();

		LogicalType elementType = arrayType.getElementType();
		return !(elementType.isNullable() || !isPrimitive(elementType));
	} else {
		return false;
	}
}
 
Example 14
Source File: LogicalTypeGeneralization.java    From flink with Apache License 2.0 5 votes vote down vote up
/**
 * Returns the most common type of a set of types. It determines a type to which all given types
 * can be casted.
 *
 * <p>For example: {@code [INT, BIGINT, DECIMAL(2, 2)]} would lead to {@code DECIMAL(21, 2)}.
 */
public static Optional<LogicalType> findCommonType(List<LogicalType> types) {
	Preconditions.checkArgument(types.size() > 0, "List of types must not be empty.");

	// collect statistics first
	boolean hasAnyType = false;
	boolean hasNullType = false;
	boolean hasNullableTypes = false;
	for (LogicalType type : types) {
		final LogicalTypeRoot typeRoot = type.getTypeRoot();
		if (typeRoot == ANY) {
			hasAnyType = true;
		} else if (typeRoot == NULL) {
			hasNullType = true;
		}
		if (type.isNullable()) {
			hasNullableTypes = true;
		}
	}

	final List<LogicalType> normalizedTypes = types.stream()
		.map(t -> t.copy(true))
		.collect(Collectors.toList());

	LogicalType foundType = findCommonNullableType(normalizedTypes, hasAnyType, hasNullType);
	if (foundType == null) {
		foundType = findCommonCastableType(normalizedTypes);
	}

	if (foundType != null) {
		final LogicalType typeWithNullability = foundType.copy(hasNullableTypes);
		return Optional.of(typeWithNullability);
	}
	return Optional.empty();
}
 
Example 15
Source File: LogicalTypeChecks.java    From flink with Apache License 2.0 5 votes vote down vote up
@Override
protected Boolean defaultMethod(LogicalType thatType) {
	checkNotNull(thatType);
	if (thisType == thatType) {
		return true;
	}
	if (thisType.getClass() != thatType.getClass() ||
		thisType.isNullable() != thatType.isNullable() ||
		thisType.getTypeRoot() != thatType.getTypeRoot()) {
		return false;
	}

	List<LogicalType> thisChildren = thisType.getChildren();
	List<LogicalType> thatChildren = thatType.getChildren();
	if (thisChildren.size() != thatChildren.size()) {
		return false;
	}
	if (thisChildren.isEmpty()) {
		// if it is an atomic type, delegate to equals method.
		return thisType.equals(thatType);
	} else {
		// if it is composite type, only need to check children types
		for (int i = 0; i < thisChildren.size(); i++) {
			LogicalType thisChild = thisChildren.get(i);
			LogicalType thatChild = thatChildren.get(i);
			if (!areTypesCompatible(thisChild, thatChild)) {
				return false;
			}
		}
		return true;
	}
}
 
Example 16
Source File: ArrayData.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * Creates an accessor for getting elements in an internal array data structure at the
 * given position.
 *
 * @param elementType the element type of the array
 */
static ElementGetter createElementGetter(LogicalType elementType) {
	final ElementGetter elementGetter;
	// ordered by type root definition
	switch (elementType.getTypeRoot()) {
		case CHAR:
		case VARCHAR:
			elementGetter = ArrayData::getString;
			break;
		case BOOLEAN:
			elementGetter = ArrayData::getBoolean;
			break;
		case BINARY:
		case VARBINARY:
			elementGetter = ArrayData::getBinary;
			break;
		case DECIMAL:
			final int decimalPrecision = getPrecision(elementType);
			final int decimalScale = getScale(elementType);
			elementGetter = (array, pos) -> array.getDecimal(pos, decimalPrecision, decimalScale);
			break;
		case TINYINT:
			elementGetter = ArrayData::getByte;
			break;
		case SMALLINT:
			elementGetter = ArrayData::getShort;
			break;
		case INTEGER:
		case DATE:
		case TIME_WITHOUT_TIME_ZONE:
		case INTERVAL_YEAR_MONTH:
			elementGetter = ArrayData::getInt;
			break;
		case BIGINT:
		case INTERVAL_DAY_TIME:
			elementGetter = ArrayData::getLong;
			break;
		case FLOAT:
			elementGetter = ArrayData::getFloat;
			break;
		case DOUBLE:
			elementGetter = ArrayData::getDouble;
			break;
		case TIMESTAMP_WITHOUT_TIME_ZONE:
		case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
			final int timestampPrecision = getPrecision(elementType);
			elementGetter = (array, pos) -> array.getTimestamp(pos, timestampPrecision);
			break;
		case TIMESTAMP_WITH_TIME_ZONE:
			throw new UnsupportedOperationException();
		case ARRAY:
			elementGetter = ArrayData::getArray;
			break;
		case MULTISET:
		case MAP:
			elementGetter = ArrayData::getMap;
			break;
		case ROW:
		case STRUCTURED_TYPE:
			final int rowFieldCount = getFieldCount(elementType);
			elementGetter = (array, pos) -> array.getRow(pos, rowFieldCount);
			break;
		case DISTINCT_TYPE:
			elementGetter = createElementGetter(((DistinctType) elementType).getSourceType());
			break;
		case RAW:
			elementGetter = ArrayData::getRawValue;
			break;
		case NULL:
		case SYMBOL:
		case UNRESOLVED:
		default:
			throw new IllegalArgumentException();
	}
	if (!elementType.isNullable()) {
		return elementGetter;
	}
	return (array, pos) -> {
		if (array.isNullAt(pos)) {
			return null;
		}
		return elementGetter.getElementOrNull(array, pos);
	};
}
 
Example 17
Source File: RowData.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * Creates an accessor for getting elements in an internal row data structure at the
 * given position.
 *
 * @param fieldType the element type of the row
 * @param fieldPos the element type of the row
 */
static FieldGetter createFieldGetter(LogicalType fieldType, int fieldPos) {
	final FieldGetter fieldGetter;
	// ordered by type root definition
	switch (fieldType.getTypeRoot()) {
		case CHAR:
		case VARCHAR:
			fieldGetter = row -> row.getString(fieldPos);
			break;
		case BOOLEAN:
			fieldGetter = row -> row.getBoolean(fieldPos);
			break;
		case BINARY:
		case VARBINARY:
			fieldGetter = row -> row.getBinary(fieldPos);
			break;
		case DECIMAL:
			final int decimalPrecision = getPrecision(fieldType);
			final int decimalScale = getScale(fieldType);
			fieldGetter = row -> row.getDecimal(fieldPos, decimalPrecision, decimalScale);
			break;
		case TINYINT:
			fieldGetter = row -> row.getByte(fieldPos);
			break;
		case SMALLINT:
			fieldGetter = row -> row.getShort(fieldPos);
			break;
		case INTEGER:
		case DATE:
		case TIME_WITHOUT_TIME_ZONE:
		case INTERVAL_YEAR_MONTH:
			fieldGetter = row -> row.getInt(fieldPos);
			break;
		case BIGINT:
		case INTERVAL_DAY_TIME:
			fieldGetter = row -> row.getLong(fieldPos);
			break;
		case FLOAT:
			fieldGetter = row -> row.getFloat(fieldPos);
			break;
		case DOUBLE:
			fieldGetter = row -> row.getDouble(fieldPos);
			break;
		case TIMESTAMP_WITHOUT_TIME_ZONE:
		case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
			final int timestampPrecision = getPrecision(fieldType);
			fieldGetter = row -> row.getTimestamp(fieldPos, timestampPrecision);
			break;
		case TIMESTAMP_WITH_TIME_ZONE:
			throw new UnsupportedOperationException();
		case ARRAY:
			fieldGetter = row -> row.getArray(fieldPos);
			break;
		case MULTISET:
		case MAP:
			fieldGetter = row -> row.getMap(fieldPos);
			break;
		case ROW:
		case STRUCTURED_TYPE:
			final int rowFieldCount = getFieldCount(fieldType);
			fieldGetter = row -> row.getRow(fieldPos, rowFieldCount);
			break;
		case DISTINCT_TYPE:
			fieldGetter = createFieldGetter(((DistinctType) fieldType).getSourceType(), fieldPos);
			break;
		case RAW:
			fieldGetter = row -> row.getRawValue(fieldPos);
			break;
		case NULL:
		case SYMBOL:
		case UNRESOLVED:
		default:
			throw new IllegalArgumentException();
	}
	if (!fieldType.isNullable()) {
		return fieldGetter;
	}
	return row -> {
		if (row.isNullAt(fieldPos)) {
			return null;
		}
		return fieldGetter.getFieldOrNull(row);
	};
}
 
Example 18
Source File: FamilyArgumentTypeStrategy.java    From flink with Apache License 2.0 4 votes vote down vote up
@Override
public Optional<DataType> inferArgumentType(CallContext callContext, int argumentPos, boolean throwOnFailure) {
	final DataType actualDataType = callContext.getArgumentDataTypes().get(argumentPos);
	final LogicalType actualType = actualDataType.getLogicalType();

	// a hack to make legacy types possible until we drop them
	if (actualType instanceof LegacyTypeInformationType) {
		return Optional.of(actualDataType);
	}

	if (Objects.equals(expectedNullability, Boolean.FALSE) && actualType.isNullable()) {
		if (throwOnFailure) {
			throw callContext.newValidationError(
				"Unsupported argument type. Expected nullable type of family '%s' but actual type was '%s'.",
				expectedFamily,
				actualType);
		}
		return Optional.empty();
	}

	// type is part of the family
	if (actualType.getTypeRoot().getFamilies().contains(expectedFamily)) {
		return Optional.of(actualDataType);
	}

	// find a type for the family
	final LogicalTypeRoot expectedRoot = familyToRoot.get(expectedFamily);
	final Optional<DataType> inferredDataType;
	if (expectedRoot == null) {
		inferredDataType = Optional.empty();
	} else {
		inferredDataType = findDataType(
			callContext,
			false,
			actualDataType,
			expectedRoot,
			expectedNullability);
	}
	if (!inferredDataType.isPresent() && throwOnFailure) {
		throw callContext.newValidationError(
				"Unsupported argument type. Expected type of family '%s' but actual type was '%s'.",
				expectedFamily,
				actualType);
	}
	return inferredDataType;
}
 
Example 19
Source File: ValuesOperationFactory.java    From flink with Apache License 2.0 4 votes vote down vote up
private Optional<ResolvedExpression> convertToExpectedType(
		ResolvedExpression sourceExpression,
		DataType targetDataType,
		ExpressionResolver.PostResolverFactory postResolverFactory) {

	LogicalType sourceLogicalType = sourceExpression.getOutputDataType().getLogicalType();
	LogicalType targetLogicalType = targetDataType.getLogicalType();

	// if the expression is a literal try converting the literal in place instead of casting
	if (sourceExpression instanceof ValueLiteralExpression) {
		// Assign a type to a null literal
		if (hasRoot(sourceLogicalType, LogicalTypeRoot.NULL)) {
			return Optional.of(valueLiteral(null, targetDataType));
		}

		// Check if the source value class is a valid input conversion class of the target type
		// It may happen that a user wanted to use a secondary input conversion class as a value for
		// a different type than what we derived.
		//
		// Example: we interpreted 1L as BIGINT, but user wanted to interpret it as a TIMESTAMP
		// In this case long is a valid conversion class for TIMESTAMP, but a
		// cast from BIGINT to TIMESTAMP is an invalid operation.
		Optional<Object> value = ((ValueLiteralExpression) sourceExpression).getValueAs(Object.class);
		if (value.isPresent() && targetLogicalType.supportsInputConversion(value.get().getClass())) {
			ValueLiteralExpression convertedLiteral = valueLiteral(
				value.get(),
				targetDataType.notNull().bridgedTo(value.get().getClass()));
			if (targetLogicalType.isNullable()) {
				return Optional.of(postResolverFactory.cast(convertedLiteral, targetDataType));
			} else {
				return Optional.of(convertedLiteral);
			}
		}
	}

	if (sourceExpression instanceof CallExpression) {
		FunctionDefinition functionDefinition = ((CallExpression) sourceExpression).getFunctionDefinition();
		if (functionDefinition == BuiltInFunctionDefinitions.ROW &&
				hasRoot(targetLogicalType, LogicalTypeRoot.ROW)) {
			return convertRowToExpectedType(sourceExpression, (FieldsDataType) targetDataType, postResolverFactory);
		} else if (functionDefinition == BuiltInFunctionDefinitions.ARRAY &&
					hasRoot(targetLogicalType, LogicalTypeRoot.ARRAY)) {
			return convertArrayToExpectedType(
				sourceExpression,
				(CollectionDataType) targetDataType,
				postResolverFactory);
		} else if (functionDefinition == BuiltInFunctionDefinitions.MAP &&
					hasRoot(targetLogicalType, LogicalTypeRoot.MAP)) {
			return convertMapToExpectedType(
				sourceExpression,
				(KeyValueDataType) targetDataType,
				postResolverFactory);
		}
	}

	// We might not be able to cast to the expected type if the expected type was provided by the user
	// we ignore nullability constraints here, as we let users override what we expect there, e.g. they
	// might know that a certain function will not produce nullable values for a given input
	if (supportsExplicitCast(
			sourceLogicalType.copy(true),
			targetLogicalType.copy(true))) {
		return Optional.of(postResolverFactory.cast(sourceExpression, targetDataType));
	} else {
		return Optional.empty();
	}
}
 
Example 20
Source File: LogicalTypeCasts.java    From flink with Apache License 2.0 4 votes vote down vote up
private static boolean supportsCasting(
		LogicalType sourceType,
		LogicalType targetType,
		boolean allowExplicit) {
	if (sourceType.isNullable() && !targetType.isNullable()) {
		return false;
	}
	// ignore nullability during compare
	if (sourceType.copy(true).equals(targetType.copy(true))) {
		return true;
	}

	final LogicalTypeRoot sourceRoot = sourceType.getTypeRoot();
	final LogicalTypeRoot targetRoot = targetType.getTypeRoot();

	if (hasFamily(sourceType, INTERVAL) && hasFamily(targetType, EXACT_NUMERIC)) {
		// cast between interval and exact numeric is only supported if interval has a single field
		return isSingleFieldInterval(sourceType);
	} else if (hasFamily(sourceType, EXACT_NUMERIC) && hasFamily(targetType, INTERVAL)) {
		// cast between interval and exact numeric is only supported if interval has a single field
		return isSingleFieldInterval(targetType);
	} else if (hasFamily(sourceType, CONSTRUCTED) || hasFamily(targetType, CONSTRUCTED)) {
		return supportsConstructedCasting(sourceType, targetType, allowExplicit);
	} else if (sourceRoot == DISTINCT_TYPE && targetRoot == DISTINCT_TYPE) {
		// the two distinct types are not equal (from initial invariant), casting is not possible
		return false;
	} else if (sourceRoot == DISTINCT_TYPE) {
		return supportsCasting(((DistinctType) sourceType).getSourceType(), targetType, allowExplicit);
	} else if (targetRoot == DISTINCT_TYPE) {
		return supportsCasting(sourceType, ((DistinctType) targetType).getSourceType(), allowExplicit);
	} else if (sourceRoot == STRUCTURED_TYPE || targetRoot == STRUCTURED_TYPE) {
		// TODO structured types are not supported yet
		return false;
	} else if (sourceRoot == NULL) {
		// null can be cast to an arbitrary type
		return true;
	} else if (sourceRoot == ANY || targetRoot == ANY) {
		// the two any types are not equal (from initial invariant), casting is not possible
		return false;
	} else if (sourceRoot == SYMBOL || targetRoot == SYMBOL) {
		// the two symbol types are not equal (from initial invariant), casting is not possible
		return false;
	}

	if (implicitCastingRules.get(targetRoot).contains(sourceRoot)) {
		return true;
	}
	if (allowExplicit) {
		return explicitCastingRules.get(targetRoot).contains(sourceRoot);
	}
	return false;
}