All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.apache.calcite.rel.type.RelDataTypeFactoryImpl Maven / Gradle / Ivy

/*
 * 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 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.org.apache.calcite.rel.type;

import com.hazelcast.org.apache.calcite.linq4j.tree.Primitive;
import com.hazelcast.org.apache.calcite.sql.SqlCollation;
import com.hazelcast.org.apache.calcite.sql.type.JavaToSqlTypeConversionRules;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeFamily;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeName;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeUtil;
import com.hazelcast.org.apache.calcite.util.Util;

import com.hazelcast.com.google.com.hazelcast.com.on.cache.CacheBuilder;
import com.hazelcast.com.google.com.hazelcast.com.on.cache.CacheLoader;
import com.hazelcast.com.google.com.hazelcast.com.on.cache.LoadingCache;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableList;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableMap;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Interner;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Interners;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;

/**
 * Abstract base for implementations of {@link RelDataTypeFactory}.
 */
public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
  //~ Instance fields --------------------------------------------------------

  /**
   * Global cache for Key to RelDataType. Uses soft values to allow GC.
   */
  private static final LoadingCache KEY2TYPE_CACHE =
      CacheBuilder.newBuilder()
          .softValues()
          .build(CacheLoader.from(RelDataTypeFactoryImpl::keyToType));

  /**
   * Global cache for RelDataType.
   */
  private static final Interner DATATYPE_CACHE =
      Interners.newWeakInterner();

  private static RelDataType keyToType(@Nonnull Key key) {
    final ImmutableList.Builder list =
        ImmutableList.builder();
    for (int i = 0; i < key.names.size(); i++) {
      list.add(
          new RelDataTypeFieldImpl(
              key.names.get(i), i, key.types.get(i)));
    }
    return new RelRecordType(key.kind, list.build(), key.nullable);
  }

  private static final Map CLASS_FAMILIES =
      ImmutableMap.builder()
          .put(String.class, SqlTypeFamily.CHARACTER)
          .put(byte[].class, SqlTypeFamily.BINARY)
          .put(boolean.class, SqlTypeFamily.BOOLEAN)
          .put(Boolean.class, SqlTypeFamily.BOOLEAN)
          .put(char.class, SqlTypeFamily.NUMERIC)
          .put(Character.class, SqlTypeFamily.NUMERIC)
          .put(short.class, SqlTypeFamily.NUMERIC)
          .put(Short.class, SqlTypeFamily.NUMERIC)
          .put(int.class, SqlTypeFamily.NUMERIC)
          .put(Integer.class, SqlTypeFamily.NUMERIC)
          .put(long.class, SqlTypeFamily.NUMERIC)
          .put(Long.class, SqlTypeFamily.NUMERIC)
          .put(float.class, SqlTypeFamily.APPROXIMATE_NUMERIC)
          .put(Float.class, SqlTypeFamily.APPROXIMATE_NUMERIC)
          .put(double.class, SqlTypeFamily.APPROXIMATE_NUMERIC)
          .put(Double.class, SqlTypeFamily.APPROXIMATE_NUMERIC)
          .put(java.sql.Date.class, SqlTypeFamily.DATE)
          .put(Time.class, SqlTypeFamily.TIME)
          .put(Timestamp.class, SqlTypeFamily.TIMESTAMP)
          .build();

  protected final RelDataTypeSystem typeSystem;

  //~ Constructors -----------------------------------------------------------

  /** Creates a type factory. */
  protected RelDataTypeFactoryImpl(RelDataTypeSystem typeSystem) {
    this.typeSystem = Objects.requireNonNull(typeSystem);
  }

  //~ Methods ----------------------------------------------------------------

  public RelDataTypeSystem getTypeSystem() {
    return typeSystem;
  }

  // implement RelDataTypeFactory
  public RelDataType createJavaType(Class clazz) {
    final JavaType javaType =
        clazz == String.class
            ? new JavaType(clazz, true, getDefaultCharset(),
                SqlCollation.IMPLICIT)
            : new JavaType(clazz);
    return canonize(javaType);
  }

  // implement RelDataTypeFactory
  public RelDataType createJoinType(RelDataType... types) {
    assert types != null;
    assert types.length >= 1;
    final List flattenedTypes = new ArrayList<>();
    getTypeList(ImmutableList.copyOf(types), flattenedTypes);
    return canonize(
        new RelCrossType(flattenedTypes, getFieldList(flattenedTypes)));
  }

  public RelDataType createStructType(
      final List typeList,
      final List fieldNameList) {
    return createStructType(StructKind.FULLY_QUALIFIED, typeList,
        fieldNameList);
  }

  public RelDataType createStructType(StructKind kind,
      final List typeList,
      final List fieldNameList) {
    return createStructType(kind, typeList,
        fieldNameList, false);
  }

  private RelDataType createStructType(StructKind kind,
      final List typeList,
      final List fieldNameList,
      final boolean nullable) {
    assert typeList.size() == fieldNameList.size();
    return canonize(kind, fieldNameList, typeList, nullable);
  }

  @SuppressWarnings("deprecation")
  public RelDataType createStructType(
      final RelDataTypeFactory.FieldInfo fieldInfo) {
    return canonize(StructKind.FULLY_QUALIFIED,
        new AbstractList() {
          @Override public String get(int index) {
            return fieldInfo.getFieldName(index);
          }

          @Override public int size() {
            return fieldInfo.getFieldCount();
          }
        },
        new AbstractList() {
          @Override public RelDataType get(int index) {
            return fieldInfo.getFieldType(index);
          }

          @Override public int size() {
            return fieldInfo.getFieldCount();
          }
        });
  }

  public final RelDataType createStructType(
      final List> fieldList) {
    return createStructType(fieldList, false);
  }

  private RelDataType createStructType(
      final List> fieldList, boolean nullable) {
    return canonize(StructKind.FULLY_QUALIFIED,
        new AbstractList() {
          @Override public String get(int index) {
            return fieldList.get(index).getKey();
          }

          @Override public int size() {
            return fieldList.size();
          }
        },
        new AbstractList() {
          @Override public RelDataType get(int index) {
            return fieldList.get(index).getValue();
          }

          @Override public int size() {
            return fieldList.size();
          }
        }, nullable);
  }

  public RelDataType leastRestrictive(List types) {
    assert types != null;
    assert types.size() >= 1;
    RelDataType type0 = types.get(0);
    if (type0.isStruct()) {
      return leastRestrictiveStructuredType(types);
    }
    return null;
  }

  protected RelDataType leastRestrictiveStructuredType(
      final List types) {
    final RelDataType type0 = types.get(0);
    final int fieldCount = type0.getFieldCount();

    // precheck that all types are structs with same number of fields
    // and register desired nullability for the result
    boolean isNullable = false;
    for (RelDataType type : types) {
      if (!type.isStruct()) {
        return null;
      }
      if (type.getFieldList().size() != fieldCount) {
        return null;
      }
      isNullable |= type.isNullable();
    }

    // recursively com.hazelcast.com.ute column-wise least restrictive
    final Builder builder = builder();
    for (int j = 0; j < fieldCount; ++j) {
      // REVIEW jvs 22-Jan-2004:  Always use the field name from the
      // first type?
      final int k = j;
      builder.add(
          type0.getFieldList().get(j).getName(),
          leastRestrictive(
              new AbstractList() {
                public RelDataType get(int index) {
                  return types.get(index).getFieldList().get(k).getType();
                }

                public int size() {
                  return types.size();
                }
              }));
    }
    return createTypeWithNullability(builder.build(), isNullable);
  }

  // copy a non-record type, setting nullability
  private RelDataType copySimpleType(
      RelDataType type,
      boolean nullable) {
    if (type instanceof JavaType) {
      JavaType javaType = (JavaType) type;
      if (SqlTypeUtil.inCharFamily(javaType)) {
        return new JavaType(
            javaType.clazz,
            nullable,
            javaType.charset,
            javaType.collation);
      } else {
        return new JavaType(
            nullable
                ? Primitive.box(javaType.clazz)
                : Primitive.unbox(javaType.clazz),
            nullable);
      }
    } else {
      // REVIEW: RelCrossType if it stays around; otherwise get rid of
      // this com.hazelcast.com.ent
      return type;
    }
  }

  // recursively copy a record type
  private RelDataType copyRecordType(
      final RelRecordType type,
      final boolean ignoreNullable,
      final boolean nullable) {
    // For flattening and outer joins, it is desirable to change
    // the nullability of the individual fields.
    return createStructType(type.getStructKind(),
        new AbstractList() {
          @Override public RelDataType get(int index) {
            RelDataType fieldType =
                type.getFieldList().get(index).getType();
            if (ignoreNullable) {
              return copyType(fieldType);
            } else {
              return createTypeWithNullability(fieldType, nullable);
            }
          }

          @Override public int size() {
            return type.getFieldCount();
          }
        },
        type.getFieldNames(), nullable);
  }

  // implement RelDataTypeFactory
  public RelDataType copyType(RelDataType type) {
    return createTypeWithNullability(type, type.isNullable());
  }

  // implement RelDataTypeFactory
  public RelDataType createTypeWithNullability(
      final RelDataType type,
      final boolean nullable) {
    Objects.requireNonNull(type);
    RelDataType newType;
    if (type.isNullable() == nullable) {
      newType = type;
    } else if (type instanceof RelRecordType) {
      // REVIEW: angel 18-Aug-2005 dtbug 336 workaround
      // Changed to ignore nullable parameter if nullable is false since
      // copyRecordType implementation is doubtful
      // - If nullable -> Do a deep copy, setting all fields of the record type
      // to be nullable regardless of initial nullability.
      // - If not nullable -> Do a deep copy, setting not nullable at top RelRecordType
      // level only, keeping its fields' nullability as before.
      // According to the SQL standard, nullability for struct types can be defined only for
      // columns, which translates to top level structs. Nested struct attributes are always
      // nullable, so in principle we could always set the nested attributes to be nullable.
      // However, this might create regressions so we will not do it and we will keep previous
      // behavior.
      newType = copyRecordType((RelRecordType) type, !nullable,  nullable);
    } else {
      newType = copySimpleType(type, nullable);
    }
    return canonize(newType);
  }

  /**
   * Registers a type, or returns the existing type if it is already
   * registered.
   *
   * @throws NullPointerException if type is null
   */
  protected RelDataType canonize(final RelDataType type) {
    return DATATYPE_CACHE.intern(type);
  }

  /**
   * Looks up a type using a temporary key, and if not present, creates
   * a permanent key and type.
   *
   * 

This approach allows us to use a cheap temporary key. A permanent * key is more expensive, because it must be immutable and not hold * references into other data structures.

*/ protected RelDataType canonize(final StructKind kind, final List names, final List types, final boolean nullable) { final RelDataType type = KEY2TYPE_CACHE.getIfPresent( new Key(kind, names, types, nullable)); if (type != null) { return type; } final ImmutableList names2 = ImmutableList.copyOf(names); final ImmutableList types2 = ImmutableList.copyOf(types); return KEY2TYPE_CACHE.getUnchecked(new Key(kind, names2, types2, nullable)); } protected RelDataType canonize(final StructKind kind, final List names, final List types) { return canonize(kind, names, types, false); } /** * Returns a list of the fields in a list of types. */ private static List getFieldList(List types) { final List fieldList = new ArrayList<>(); for (RelDataType type : types) { addFields(type, fieldList); } return fieldList; } /** * Returns a list of all atomic types in a list. */ private static void getTypeList( ImmutableList inTypes, List flatTypes) { for (RelDataType inType : inTypes) { if (inType instanceof RelCrossType) { getTypeList(((RelCrossType) inType).types, flatTypes); } else { flatTypes.add(inType); } } } /** * Adds all fields in type to fieldList, * renumbering the fields (if necessary) to ensure that their index * matches their position in the list. */ private static void addFields( RelDataType type, List fieldList) { if (type instanceof RelCrossType) { final RelCrossType crossType = (RelCrossType) type; for (RelDataType type1 : crossType.types) { addFields(type1, fieldList); } } else { List fields = type.getFieldList(); for (RelDataTypeField field : fields) { if (field.getIndex() != fieldList.size()) { field = new RelDataTypeFieldImpl(field.getName(), fieldList.size(), field.getType()); } fieldList.add(field); } } } public static boolean isJavaType(RelDataType t) { return t instanceof JavaType; } private List fieldsOf(Class clazz) { final List list = new ArrayList<>(); for (Field field : clazz.getFields()) { if (Modifier.isStatic(field.getModifiers())) { continue; } list.add( new RelDataTypeFieldImpl( field.getName(), list.size(), createJavaType(field.getType()))); } if (list.isEmpty()) { return null; } return list; } /** * Delegates to * {@link RelDataTypeSystem#deriveDecimalMultiplyType(RelDataTypeFactory, RelDataType, RelDataType)} * to get the return type for the operation. */ @Deprecated public RelDataType createDecimalProduct( RelDataType type1, RelDataType type2) { return typeSystem.deriveDecimalMultiplyType(this, type1, type2); } /** * Delegates to * {@link RelDataTypeSystem#shouldUseDoubleMultiplication(RelDataTypeFactory, RelDataType, RelDataType)} * to get if double should be used for multiplication. */ @Deprecated public boolean useDoubleMultiplication( RelDataType type1, RelDataType type2) { return typeSystem.shouldUseDoubleMultiplication(this, type1, type2); } /** * Delegates to * {@link RelDataTypeSystem#deriveDecimalDivideType(RelDataTypeFactory, RelDataType, RelDataType)} * to get the return type for the operation. */ @Deprecated public RelDataType createDecimalQuotient( RelDataType type1, RelDataType type2) { return typeSystem.deriveDecimalDivideType(this, type1, type2); } public RelDataType decimalOf(RelDataType type) { // create decimal type and sync nullability return createTypeWithNullability(decimalOf2(type), type.isNullable()); } /** Create decimal type equivalent with the given {@code type} while sans nullability. */ private RelDataType decimalOf2(RelDataType type) { assert SqlTypeUtil.isNumeric(type) || SqlTypeUtil.isNull(type); SqlTypeName typeName = type.getSqlTypeName(); assert typeName != null; switch (typeName) { case DECIMAL: // Fix the precision when the type is JavaType. return RelDataTypeFactoryImpl.isJavaType(type) ? SqlTypeUtil.getMaxPrecisionScaleDecimal(this) : type; case TINYINT: return createSqlType(SqlTypeName.DECIMAL, 3, 0); case SMALLINT: return createSqlType(SqlTypeName.DECIMAL, 5, 0); case INTEGER: return createSqlType(SqlTypeName.DECIMAL, 10, 0); case BIGINT: // the default max precision is 19, so this is actually DECIMAL(19, 0) // but derived system can override the max precision/scale. return createSqlType(SqlTypeName.DECIMAL, 38, 0); case REAL: return createSqlType(SqlTypeName.DECIMAL, 14, 7); case FLOAT: return createSqlType(SqlTypeName.DECIMAL, 14, 7); case DOUBLE: // the default max precision is 19, so this is actually DECIMAL(19, 15) // but derived system can override the max precision/scale. return createSqlType(SqlTypeName.DECIMAL, 30, 15); default: // default precision and scale. return createSqlType(SqlTypeName.DECIMAL); } } public Charset getDefaultCharset() { return Util.getDefaultCharset(); } @SuppressWarnings("deprecation") public FieldInfoBuilder builder() { return new FieldInfoBuilder(this); } //~ Inner Classes ---------------------------------------------------------- // TODO jvs 13-Dec-2004: move to OJTypeFactoryImpl? /** * Type which is based upon a Java class. */ public class JavaType extends RelDataTypeImpl { private final Class clazz; private final boolean nullable; private SqlCollation collation; private Charset charset; public JavaType(Class clazz) { this(clazz, !clazz.isPrimitive()); } public JavaType( Class clazz, boolean nullable) { this(clazz, nullable, null, null); } public JavaType( Class clazz, boolean nullable, Charset charset, SqlCollation collation) { super(fieldsOf(clazz)); this.clazz = clazz; this.nullable = nullable; assert (charset != null) == SqlTypeUtil.inCharFamily(this) : "Need to be a chartype"; this.charset = charset; this.collation = collation; com.hazelcast.com.uteDigest(); } public Class getJavaClass() { return clazz; } public boolean isNullable() { return nullable; } @Override public RelDataTypeFamily getFamily() { RelDataTypeFamily family = CLASS_FAMILIES.get(clazz); return family != null ? family : this; } protected void generateTypeString(StringBuilder sb, boolean withDetail) { sb.append("JavaType("); sb.append(clazz); sb.append(")"); } public RelDataType getComponentType() { final Class com.hazelcast.com.onentType = clazz.getComponentType(); if (com.hazelcast.com.onentType == null) { return null; } else { return createJavaType(com.hazelcast.com.onentType); } } /** * For {@link JavaType} created with {@link Map} class, * we cannot get the key type. Use ANY as key type. */ @Override public RelDataType getKeyType() { if (Map.class.isAssignableFrom(clazz)) { // Need to return a SQL type because the type inference needs SqlTypeName. return createSqlType(SqlTypeName.ANY); } else { return null; } } /** * For {@link JavaType} created with {@link Map} class, * we cannot get the value type. Use ANY as value type. */ @Override public RelDataType getValueType() { if (Map.class.isAssignableFrom(clazz)) { // Need to return a SQL type because the type inference needs SqlTypeName. return createSqlType(SqlTypeName.ANY); } else { return null; } } public Charset getCharset() { return this.charset; } public SqlCollation getCollation() { return this.collation; } public SqlTypeName getSqlTypeName() { final SqlTypeName typeName = JavaToSqlTypeConversionRules.instance().lookup(clazz); if (typeName == null) { return SqlTypeName.OTHER; } return typeName; } } /** Key to the data type cache. */ private static class Key { private final StructKind kind; private final List names; private final List types; private final boolean nullable; Key(StructKind kind, List names, List types, boolean nullable) { this.kind = kind; this.names = names; this.types = types; this.nullable = nullable; } @Override public int hashCode() { return Objects.hash(kind, names, types, nullable); } @Override public boolean equals(Object obj) { return obj == this || obj instanceof Key && kind == ((Key) obj).kind && names.equals(((Key) obj).names) && types.equals(((Key) obj).types) && nullable == ((Key) obj).nullable; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy