blob: 81bc14226a8a760a3db1b5a7c6b31a0b03cb9db2 [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.beam.sdk.extensions.sql.zetasql;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_BOOL;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_BYTES;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_DATE;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_DATETIME;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_DOUBLE;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_INT64;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_NUMERIC;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_STRING;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_TIME;
import static com.google.zetasql.ZetaSQLType.TypeKind.TYPE_TIMESTAMP;
import static java.util.stream.Collectors.toList;
import com.google.zetasql.ArrayType;
import com.google.zetasql.StructType;
import com.google.zetasql.StructType.StructField;
import com.google.zetasql.Type;
import com.google.zetasql.TypeFactory;
import com.google.zetasql.ZetaSQLType.TypeKind;
import java.util.List;
import java.util.function.Function;
import org.apache.beam.sdk.annotations.Internal;
import org.apache.beam.vendor.calcite.v1_20_0.org.apache.calcite.rel.type.RelDataType;
import org.apache.beam.vendor.calcite.v1_20_0.org.apache.calcite.rex.RexBuilder;
import org.apache.beam.vendor.calcite.v1_20_0.org.apache.calcite.sql.type.SqlTypeName;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.collect.ImmutableList;
/**
* Utility methods for ZetaSQL <=> Calcite translation.
*
* <p>Unsupported ZetaSQL types: INT32, UINT32, UINT64, FLOAT, ENUM, PROTO, GEOGRAPHY
*/
@Internal
public final class ZetaSqlCalciteTranslationUtils {
private ZetaSqlCalciteTranslationUtils() {}
// Type conversion: Calcite => ZetaSQL
public static Type toZetaType(RelDataType calciteType) {
switch (calciteType.getSqlTypeName()) {
case BIGINT:
return TypeFactory.createSimpleType(TYPE_INT64);
case DOUBLE:
return TypeFactory.createSimpleType(TYPE_DOUBLE);
case BOOLEAN:
return TypeFactory.createSimpleType(TYPE_BOOL);
case VARCHAR:
return TypeFactory.createSimpleType(TYPE_STRING);
case VARBINARY:
return TypeFactory.createSimpleType(TYPE_BYTES);
case DECIMAL:
return TypeFactory.createSimpleType(TYPE_NUMERIC);
case DATE:
return TypeFactory.createSimpleType(TYPE_DATE);
case TIME:
return TypeFactory.createSimpleType(TYPE_TIME);
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return TypeFactory.createSimpleType(TYPE_DATETIME);
case TIMESTAMP:
return TypeFactory.createSimpleType(TYPE_TIMESTAMP);
case ARRAY:
return TypeFactory.createArrayType(toZetaType(calciteType.getComponentType()));
case ROW:
List<StructField> structFields =
calciteType.getFieldList().stream()
.map(f -> new StructField(f.getName(), toZetaType(f.getType())))
.collect(toList());
return TypeFactory.createStructType(structFields);
default:
throw new UnsupportedOperationException("Unsupported RelDataType: " + calciteType);
}
}
// Type conversion: ZetaSQL => Calcite
public static SqlTypeName toCalciteTypeName(TypeKind type) {
switch (type) {
case TYPE_INT64:
return SqlTypeName.BIGINT;
case TYPE_DOUBLE:
return SqlTypeName.DOUBLE;
case TYPE_BOOL:
return SqlTypeName.BOOLEAN;
case TYPE_STRING:
return SqlTypeName.VARCHAR;
case TYPE_BYTES:
return SqlTypeName.VARBINARY;
case TYPE_NUMERIC:
return SqlTypeName.DECIMAL;
case TYPE_DATE:
return SqlTypeName.DATE;
case TYPE_TIME:
return SqlTypeName.TIME;
case TYPE_DATETIME:
return SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE;
case TYPE_TIMESTAMP:
// TODO: handle timestamp with time zone.
return SqlTypeName.TIMESTAMP;
// TODO[BEAM-9179] Add conversion code for ARRAY and ROW types
default:
throw new UnsupportedOperationException("Unknown ZetaSQL type: " + type.name());
}
}
public static RelDataType toRelDataType(RexBuilder rexBuilder, Type type, boolean isNullable) {
if (type.getKind().equals(TypeKind.TYPE_ARRAY)) {
return toArrayRelDataType(rexBuilder, type.asArray(), isNullable);
} else if (type.getKind().equals(TypeKind.TYPE_STRUCT)) {
return toStructRelDataType(rexBuilder, type.asStruct(), isNullable);
} else {
// TODO: Check type's nullability?
return toSimpleRelDataType(type.getKind(), rexBuilder, isNullable);
}
}
public static RelDataType toArrayRelDataType(
RexBuilder rexBuilder, ArrayType arrayType, boolean isNullable) {
// -1 cardinality means unlimited array size.
// TODO: is unlimited array size right for general case?
// TODO: whether isNullable should be ArrayType's nullablity (not its element type's?)
return rexBuilder
.getTypeFactory()
.createArrayType(toRelDataType(rexBuilder, arrayType.getElementType(), isNullable), -1);
}
private static List<String> toNameList(List<StructField> fields) {
ImmutableList.Builder<String> b = ImmutableList.builder();
for (int i = 0; i < fields.size(); i++) {
String name = fields.get(i).getName();
if ("".equals(name)) {
name = "$col" + i;
}
b.add(name);
}
return b.build();
}
public static RelDataType toStructRelDataType(
RexBuilder rexBuilder, StructType structType, boolean isNullable) {
List<StructField> fields = structType.getFieldList();
List<String> fieldNames = toNameList(fields);
List<RelDataType> fieldTypes =
fields.stream()
.map(f -> toRelDataType(rexBuilder, f.getType(), isNullable))
.collect(toList());
return rexBuilder.getTypeFactory().createStructType(fieldTypes, fieldNames);
}
// TODO: convert TIMESTAMP with/without TIMEZONE and DATETIME.
public static RelDataType toSimpleRelDataType(TypeKind kind, RexBuilder rexBuilder) {
return toSimpleRelDataType(kind, rexBuilder, true);
}
public static RelDataType toSimpleRelDataType(
TypeKind kind, RexBuilder rexBuilder, boolean isNullable) {
RelDataType relDataType = relDataTypeFactory(toCalciteTypeName(kind)).apply(rexBuilder);
return nullable(rexBuilder, relDataType, isNullable);
}
private static RelDataType nullable(RexBuilder r, RelDataType relDataType, boolean isNullable) {
return r.getTypeFactory().createTypeWithNullability(relDataType, isNullable);
}
private static Function<RexBuilder, RelDataType> relDataTypeFactory(SqlTypeName typeName) {
return (RexBuilder r) -> r.getTypeFactory().createSqlType(typeName);
}
}