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

com.hazelcast.org.apache.calcite.sql.type.SqlTypeUtil 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.sql.type;

import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeFactory;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeFamily;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeField;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeFieldImpl;
import com.hazelcast.org.apache.calcite.rex.RexUtil;
import com.hazelcast.org.apache.calcite.sql.SqlBasicTypeNameSpec;
import com.hazelcast.org.apache.calcite.sql.SqlCall;
import com.hazelcast.org.apache.calcite.sql.SqlCallBinding;
import com.hazelcast.org.apache.calcite.sql.SqlCollation;
import com.hazelcast.org.apache.calcite.sql.SqlDataTypeSpec;
import com.hazelcast.org.apache.calcite.sql.SqlNode;
import com.hazelcast.org.apache.calcite.sql.parser.SqlParserPos;
import com.hazelcast.org.apache.calcite.sql.validate.SqlNameMatcher;
import com.hazelcast.org.apache.calcite.sql.validate.SqlValidator;
import com.hazelcast.org.apache.calcite.sql.validate.SqlValidatorScope;
import com.hazelcast.org.apache.calcite.sql.validate.SqlValidatorUtil;
import com.hazelcast.org.apache.calcite.util.NumberUtil;
import com.hazelcast.org.apache.calcite.util.Pair;
import com.hazelcast.org.apache.calcite.util.Util;

import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableList;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Sets;

import java.nio.charset.Charset;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.hazelcast.org.apache.calcite.util.Static.RESOURCE;

/**
 * Contains utility methods used during SQL validation or type derivation.
 */
public abstract class SqlTypeUtil {
  //~ Methods ----------------------------------------------------------------

  /**
   * Checks whether two types or more are char com.hazelcast.com.arable.
   *
   * @return Returns true if all operands are of char type and if they are
   * com.hazelcast.com.arable, i.e. of the same charset and collation of same charset
   */
  public static boolean isCharTypeComparable(List argTypes) {
    assert argTypes != null;
    assert argTypes.size() >= 2;

    // Filter out ANY and NULL elements.
    List argTypes2 = new ArrayList<>();
    for (RelDataType t : argTypes) {
      if (!isAny(t) && !isNull(t)) {
        argTypes2.add(t);
      }
    }

    for (Pair pair : Pair.adjacents(argTypes2)) {
      RelDataType t0 = pair.left;
      RelDataType t1 = pair.right;

      if (!inCharFamily(t0) || !inCharFamily(t1)) {
        return false;
      }

      if (t0.getCharset() == null) {
        throw new AssertionError("RelDataType object should have been assigned "
            + "a (default) charset when calling deriveType");
      } else if (!t0.getCharset().equals(t1.getCharset())) {
        return false;
      }

      if (t0.getCollation() == null) {
        throw new AssertionError("RelDataType object should have been assigned "
            + "a (default) collation when calling deriveType");
      } else if (!t0.getCollation().getCharset().equals(
          t1.getCollation().getCharset())) {
        return false;
      }
    }

    return true;
  }

  /**
   * Returns whether the operands to a call are char type-com.hazelcast.com.arable.
   *
   * @param binding        Binding of call to operands
   * @param operands       Operands to check for com.hazelcast.com.atibility; usually the
   *                       operands of the bound call, but not always
   * @param throwOnFailure Whether to throw an exception on failure
   * @return whether operands are valid
   */
  public static boolean isCharTypeComparable(
      SqlCallBinding binding,
      List operands,
      boolean throwOnFailure) {
    final SqlValidator validator = binding.getValidator();
    final SqlValidatorScope scope = binding.getScope();
    assert operands != null;
    assert operands.size() >= 2;

    if (!isCharTypeComparable(
        deriveAndCollectTypes(validator, scope, operands))) {
      if (throwOnFailure) {
        String msg = "";
        for (int i = 0; i < operands.size(); i++) {
          if (i > 0) {
            msg += ", ";
          }
          msg += operands.get(i).toString();
        }
        throw binding.newError(RESOURCE.operandNotComparable(msg));
      }
      return false;
    }
    return true;
  }

  /**
   * Iterates over all operands, derives their types, and collects them into
   * a list.
   */
  public static List deriveAndCollectTypes(
      SqlValidator validator,
      SqlValidatorScope scope,
      List operands) {
    // NOTE: Do not use an AbstractList. Don't want to be lazy. We want
    // errors.
    List types = new ArrayList<>();
    for (SqlNode operand : operands) {
      types.add(validator.deriveType(scope, operand));
    }
    return types;
  }

  /**
   * Promotes a type to a row type (does nothing if it already is one).
   *
   * @param type      type to be promoted
   * @param fieldName name to give field in row type; null for default of
   *                  "ROW_VALUE"
   * @return row type
   */
  public static RelDataType promoteToRowType(
      RelDataTypeFactory typeFactory,
      RelDataType type,
      String fieldName) {
    if (!type.isStruct()) {
      if (fieldName == null) {
        fieldName = "ROW_VALUE";
      }
      type = typeFactory.builder().add(fieldName, type).build();
    }
    return type;
  }

  /**
   * Recreates a given RelDataType with nullability iff any of the operands
   * of a call are nullable.
   */
  public static RelDataType makeNullableIfOperandsAre(
      final SqlValidator validator,
      final SqlValidatorScope scope,
      final SqlCall call,
      RelDataType type) {
    for (SqlNode operand : call.getOperandList()) {
      RelDataType operandType = validator.deriveType(scope, operand);

      if (containsNullable(operandType)) {
        RelDataTypeFactory typeFactory = validator.getTypeFactory();
        type = typeFactory.createTypeWithNullability(type, true);
        break;
      }
    }
    return type;
  }

  /**
   * Recreates a given RelDataType with nullability iff any of the param
   * argTypes are nullable.
   */
  public static RelDataType makeNullableIfOperandsAre(
      final RelDataTypeFactory typeFactory,
      final List argTypes,
      RelDataType type) {
    Objects.requireNonNull(type);
    if (containsNullable(argTypes)) {
      type = typeFactory.createTypeWithNullability(type, true);
    }
    return type;
  }

  /**
   * Returns whether all of array of types are nullable.
   */
  public static boolean allNullable(List types) {
    for (RelDataType type : types) {
      if (!containsNullable(type)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns whether one or more of an array of types is nullable.
   */
  public static boolean containsNullable(List types) {
    for (RelDataType type : types) {
      if (containsNullable(type)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Determines whether a type or any of its fields (if a structured type) are
   * nullable.
   */
  public static boolean containsNullable(RelDataType type) {
    if (type.isNullable()) {
      return true;
    }
    if (!type.isStruct()) {
      return false;
    }
    for (RelDataTypeField field : type.getFieldList()) {
      if (containsNullable(field.getType())) {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns typeName.equals(type.getSqlTypeName()). If
   * typeName.equals(SqlTypeName.Any) true is always returned.
   */
  public static boolean isOfSameTypeName(
      SqlTypeName typeName,
      RelDataType type) {
    return SqlTypeName.ANY == typeName
        || typeName == type.getSqlTypeName();
  }

  /**
   * Returns true if any element in typeNames matches
   * type.getSqlTypeName().
   *
   * @see #isOfSameTypeName(SqlTypeName, RelDataType)
   */
  public static boolean isOfSameTypeName(
      Collection typeNames,
      RelDataType type) {
    for (SqlTypeName typeName : typeNames) {
      if (isOfSameTypeName(typeName, type)) {
        return true;
      }
    }
    return false;
  }

  /**
   * @return true if type is DATE, TIME, or TIMESTAMP
   */
  public static boolean isDatetime(RelDataType type) {
    return SqlTypeFamily.DATETIME.contains(type);
  }

  /**
   * @return true if type is DATE
   */
  public static boolean isDate(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }

    return type.getSqlTypeName() == SqlTypeName.DATE;
  }

  /**
   * @return true if type is TIMESTAMP
   */
  public static boolean isTimestamp(RelDataType type) {
    return SqlTypeFamily.TIMESTAMP.contains(type);
  }

  /**
   * @return true if type is some kind of INTERVAL
   */
  public static boolean isInterval(RelDataType type) {
    return SqlTypeFamily.DATETIME_INTERVAL.contains(type);
  }

  /**
   * @return true if type is in SqlTypeFamily.Character
   */
  public static boolean inCharFamily(RelDataType type) {
    return type.getFamily() == SqlTypeFamily.CHARACTER;
  }

  /**
   * @return true if type is in SqlTypeFamily.Character
   */
  public static boolean inCharFamily(SqlTypeName typeName) {
    return typeName.getFamily() == SqlTypeFamily.CHARACTER;
  }

  /**
   * @return true if type is in SqlTypeFamily.Boolean
   */
  public static boolean inBooleanFamily(RelDataType type) {
    return type.getFamily() == SqlTypeFamily.BOOLEAN;
  }

  /**
   * @return true if two types are in same type family
   */
  public static boolean inSameFamily(RelDataType t1, RelDataType t2) {
    return t1.getFamily() == t2.getFamily();
  }

  /**
   * @return true if two types are in same type family, or one or the other is
   * of type {@link SqlTypeName#NULL}.
   */
  public static boolean inSameFamilyOrNull(RelDataType t1, RelDataType t2) {
    return (t1.getSqlTypeName() == SqlTypeName.NULL)
        || (t2.getSqlTypeName() == SqlTypeName.NULL)
        || (t1.getFamily() == t2.getFamily());
  }

  /**
   * @return true if type family is either character or binary
   */
  public static boolean inCharOrBinaryFamilies(RelDataType type) {
    return (type.getFamily() == SqlTypeFamily.CHARACTER)
        || (type.getFamily() == SqlTypeFamily.BINARY);
  }

  /**
   * @return true if type is a LOB of some kind
   */
  public static boolean isLob(RelDataType type) {
    // TODO jvs 9-Dec-2004:  once we support LOB types
    return false;
  }

  /**
   * @return true if type is variable width with bounded precision
   */
  public static boolean isBoundedVariableWidth(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }
    switch (typeName) {
    case VARCHAR:
    case VARBINARY:

      // TODO angel 8-June-2005: Multiset should be LOB
    case MULTISET:
      return true;
    default:
      return false;
    }
  }

  /**
   * @return true if type is one of the integer types
   */
  public static boolean isIntType(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }
    switch (typeName) {
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
      return true;
    default:
      return false;
    }
  }

  /**
   * @return true if type is decimal
   */
  public static boolean isDecimal(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }
    return typeName == SqlTypeName.DECIMAL;
  }

  /**
   * @return true if type is double
   */
  public static boolean isDouble(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }
    return typeName == SqlTypeName.DOUBLE;
  }

  /**
   * @return true if type is bigint
   */
  public static boolean isBigint(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }
    return typeName == SqlTypeName.BIGINT;
  }

  /**
   * @return true if type is numeric with exact precision
   */
  public static boolean isExactNumeric(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }
    switch (typeName) {
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
    case DECIMAL:
      return true;
    default:
      return false;
    }
  }

  /** Returns whether a type's scale is set. */
  public static boolean hasScale(RelDataType type) {
    return type.getScale() != Integer.MIN_VALUE;
  }

  /**
   * Returns the maximum value of an integral type, as a long value
   */
  public static long maxValue(RelDataType type) {
    assert SqlTypeUtil.isIntType(type);
    switch (type.getSqlTypeName()) {
    case TINYINT:
      return Byte.MAX_VALUE;
    case SMALLINT:
      return Short.MAX_VALUE;
    case INTEGER:
      return Integer.MAX_VALUE;
    case BIGINT:
      return Long.MAX_VALUE;
    default:
      throw Util.unexpected(type.getSqlTypeName());
    }
  }

  /**
   * @return true if type is numeric with approximate precision
   */
  public static boolean isApproximateNumeric(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }
    switch (typeName) {
    case FLOAT:
    case REAL:
    case DOUBLE:
      return true;
    default:
      return false;
    }
  }

  /**
   * @return true if type is numeric
   */
  public static boolean isNumeric(RelDataType type) {
    return isExactNumeric(type) || isApproximateNumeric(type);
  }

  /**
   * @return true if type is null.
   */
  public static boolean isNull(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();

    if (typeName == null) {
      return false;
    }

    return typeName == SqlTypeName.NULL;
  }

  /**
   * Tests whether two types have the same name and structure, possibly with
   * differing modifiers. For example, VARCHAR(1) and VARCHAR(10) are
   * considered the same, while VARCHAR(1) and CHAR(1) are considered
   * different. Likewise, VARCHAR(1) MULTISET and VARCHAR(10) MULTISET are
   * considered the same.
   *
   * @return true if types have same name and structure
   */
  public static boolean sameNamedType(RelDataType t1, RelDataType t2) {
    if (t1.isStruct() || t2.isStruct()) {
      if (!t1.isStruct() || !t2.isStruct()) {
        return false;
      }
      if (t1.getFieldCount() != t2.getFieldCount()) {
        return false;
      }
      List fields1 = t1.getFieldList();
      List fields2 = t2.getFieldList();
      for (int i = 0; i < fields1.size(); ++i) {
        if (!sameNamedType(
            fields1.get(i).getType(),
            fields2.get(i).getType())) {
          return false;
        }
      }
      return true;
    }
    RelDataType com.hazelcast.com.1 = t1.getComponentType();
    RelDataType com.hazelcast.com.2 = t2.getComponentType();
    if ((com.hazelcast.com.1 != null) || (com.hazelcast.com.2 != null)) {
      if ((com.hazelcast.com.1 == null) || (com.hazelcast.com.2 == null)) {
        return false;
      }
      if (!sameNamedType(com.hazelcast.com.1, com.hazelcast.com.2)) {
        return false;
      }
    }
    return t1.getSqlTypeName() == t2.getSqlTypeName();
  }

  /**
   * Computes the maximum number of bytes required to represent a value of a
   * type having user-defined precision. This com.hazelcast.com.utation assumes no overhead
   * such as length indicators and NUL-terminators. Complex types for which
   * multiple representations are possible (e.g. DECIMAL or TIMESTAMP) return
   * 0.
   *
   * @param type type for which to com.hazelcast.com.ute storage
   * @return maximum bytes, or 0 for a fixed-width type or type with unknown
   * maximum
   */
  public static int getMaxByteSize(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();

    if (typeName == null) {
      return 0;
    }

    switch (typeName) {
    case CHAR:
    case VARCHAR:
      return (int) Math.ceil(
          ((double) type.getPrecision())
              * type.getCharset().newEncoder().maxBytesPerChar());

    case BINARY:
    case VARBINARY:
      return type.getPrecision();

    case MULTISET:

      // TODO Wael Jan-24-2005: Need a better way to tell fennel this
      // number. This a very generic place and implementation details like
      // this doesnt belong here. Waiting to change this once we have blob
      // support
      return 4096;

    default:
      return 0;
    }
  }

  /**
   * Determines the minimum unscaled value of a numeric type
   *
   * @param type a numeric type
   */
  public static long getMinValue(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    switch (typeName) {
    case TINYINT:
      return Byte.MIN_VALUE;
    case SMALLINT:
      return Short.MIN_VALUE;
    case INTEGER:
      return Integer.MIN_VALUE;
    case BIGINT:
    case DECIMAL:
      return NumberUtil.getMinUnscaled(type.getPrecision()).longValue();
    default:
      throw new AssertionError("getMinValue(" + typeName + ")");
    }
  }

  /**
   * Determines the maximum unscaled value of a numeric type
   *
   * @param type a numeric type
   */
  public static long getMaxValue(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    switch (typeName) {
    case TINYINT:
      return Byte.MAX_VALUE;
    case SMALLINT:
      return Short.MAX_VALUE;
    case INTEGER:
      return Integer.MAX_VALUE;
    case BIGINT:
    case DECIMAL:
      return NumberUtil.getMaxUnscaled(type.getPrecision()).longValue();
    default:
      throw new AssertionError("getMaxValue(" + typeName + ")");
    }
  }

  /**
   * @return true if type has a representation as a Java primitive (ignoring
   * nullability)
   */
  @Deprecated // to be removed before 2.0
  public static boolean isJavaPrimitive(RelDataType type) {
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return false;
    }

    switch (typeName) {
    case BOOLEAN:
    case TINYINT:
    case SMALLINT:
    case INTEGER:
    case BIGINT:
    case FLOAT:
    case REAL:
    case DOUBLE:
    case SYMBOL:
      return true;
    default:
      return false;
    }
  }

  /**
   * @return class name of the wrapper for the primitive data type.
   */
  @Deprecated // to be removed before 2.0
  public static String getPrimitiveWrapperJavaClassName(RelDataType type) {
    if (type == null) {
      return null;
    }
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return null;
    }

    switch (typeName) {
    case BOOLEAN:
      return "Boolean";
    default:
      //noinspection deprecation
      return getNumericJavaClassName(type);
    }
  }

  /**
   * @return class name of the numeric data type.
   */
  @Deprecated // to be removed before 2.0
  public static String getNumericJavaClassName(RelDataType type) {
    if (type == null) {
      return null;
    }
    SqlTypeName typeName = type.getSqlTypeName();
    if (typeName == null) {
      return null;
    }

    switch (typeName) {
    case TINYINT:
      return "Byte";
    case SMALLINT:
      return "Short";
    case INTEGER:
      return "Integer";
    case BIGINT:
      return "Long";
    case REAL:
      return "Float";
    case DECIMAL:
    case FLOAT:
    case DOUBLE:
      return "Double";
    default:
      return null;
    }
  }

  private static boolean isAny(RelDataType t) {
    return t.getFamily() == SqlTypeFamily.ANY;
  }

  /**
   * Tests whether a value can be assigned to a site.
   *
   * @param toType   type of the target site
   * @param fromType type of the source value
   * @return true iff assignable
   */
  public static boolean canAssignFrom(
      RelDataType toType,
      RelDataType fromType) {
    if (isAny(toType) || isAny(fromType)) {
      return true;
    }

    // TODO jvs 2-Jan-2005:  handle all the other cases like
    // rows, collections, UDT's
    if (fromType.getSqlTypeName() == SqlTypeName.NULL) {
      // REVIEW jvs 4-Dec-2008: We allow assignment from NULL to any
      // type, including NOT NULL types, since in the case where no
      // rows are actually processed, the assignment is legal
      // (FRG-365).  However, it would be better if the validator's
      // NULL type inference guaranteed that we had already
      // assigned a real (nullable) type to every NULL literal.
      return true;
    }

    if (fromType.getSqlTypeName() == SqlTypeName.ARRAY) {
      if (toType.getSqlTypeName() != SqlTypeName.ARRAY) {
        return false;
      }
      return canAssignFrom(toType.getComponentType(), fromType.getComponentType());
    }

    if (areCharacterSetsMismatched(toType, fromType)) {
      return false;
    }

    return toType.getFamily() == fromType.getFamily();
  }

  /**
   * Determines whether two types both have different character sets. If one
   * or the other type has no character set (e.g. in cast from INT to
   * VARCHAR), that is not a mismatch.
   *
   * @param t1 first type
   * @param t2 second type
   * @return true iff mismatched
   */
  public static boolean areCharacterSetsMismatched(
      RelDataType t1,
      RelDataType t2) {
    if (isAny(t1) || isAny(t2)) {
      return false;
    }

    Charset cs1 = t1.getCharset();
    Charset cs2 = t2.getCharset();
    if ((cs1 != null) && (cs2 != null)) {
      if (!cs1.equals(cs2)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Compares two types and returns true if fromType can be cast to toType.
   *
   * 

REVIEW jvs 17-Dec-2004: the coerce param below shouldn't really be * necessary. We're using it as a hack because * {@link SqlTypeFactoryImpl#leastRestrictive} isn't com.hazelcast.com.lete enough * yet. Once it is, this param (and the non-coerce rules of * {@link SqlTypeAssignmentRule}) should go away. * * @param toType target of assignment * @param fromType source of assignment * @param coerce if true, the SQL rules for CAST are used; if false, the * rules are similar to Java; e.g. you can't assign short x = * (int) y, and you can't assign int x = (String) z. * @return true iff cast is legal */ public static boolean canCastFrom( RelDataType toType, RelDataType fromType, boolean coerce) { if (toType.equals(fromType)) { return true; } if (isAny(toType) || isAny(fromType)) { return true; } final SqlTypeName fromTypeName = fromType.getSqlTypeName(); final SqlTypeName toTypeName = toType.getSqlTypeName(); if (toType.isStruct() || fromType.isStruct()) { if (toTypeName == SqlTypeName.DISTINCT) { if (fromTypeName == SqlTypeName.DISTINCT) { // can't cast between different distinct types return false; } return canCastFrom( toType.getFieldList().get(0).getType(), fromType, coerce); } else if (fromTypeName == SqlTypeName.DISTINCT) { return canCastFrom( toType, fromType.getFieldList().get(0).getType(), coerce); } else if (toTypeName == SqlTypeName.ROW) { if (fromTypeName != SqlTypeName.ROW) { return false; } int n = toType.getFieldCount(); if (fromType.getFieldCount() != n) { return false; } for (int i = 0; i < n; ++i) { RelDataTypeField toField = toType.getFieldList().get(i); RelDataTypeField fromField = fromType.getFieldList().get(i); if (!canCastFrom( toField.getType(), fromField.getType(), coerce)) { return false; } } return true; } else if (toTypeName == SqlTypeName.MULTISET) { if (!fromType.isStruct()) { return false; } if (fromTypeName != SqlTypeName.MULTISET) { return false; } return canCastFrom( toType.getComponentType(), fromType.getComponentType(), coerce); } else if (fromTypeName == SqlTypeName.MULTISET) { return false; } else { return toType.getFamily() == fromType.getFamily(); } } RelDataType c1 = toType.getComponentType(); if (c1 != null) { RelDataType c2 = fromType.getComponentType(); if (c2 == null) { return false; } return canCastFrom(c1, c2, coerce); } if ((isInterval(fromType) && isExactNumeric(toType)) || (isInterval(toType) && isExactNumeric(fromType))) { IntervalSqlType intervalType = (IntervalSqlType) (isInterval(fromType) ? fromType : toType); if (!intervalType.getIntervalQualifier().isSingleDatetimeField()) { // Casts between intervals and exact numerics must involve // intervals with a single datetime field. return false; } } if (toTypeName == null || fromTypeName == null) { return false; } // REVIEW jvs 9-Feb-2009: we don't impose SQL rules for character sets // here; instead, we do that in SqlCastFunction. The reason is that // this method is called from at least one place (MedJdbcNameDirectory) // where internally a cast across character repertoires is OK. Should // probably clean that up. SqlTypeMappingRule rules = SqlTypeMappingRules.instance(coerce); return rules.canApplyFrom(toTypeName, fromTypeName); } /** * Flattens a record type by recursively expanding any fields which are * themselves record types. For each record type, a representative null * value field is also prepended (with state NULL for a null value and FALSE * for non-null), and all com.hazelcast.com.onent types are asserted to be nullable, since * SQL doesn't allow NOT NULL to be specified on attributes. * * @param typeFactory factory which should produced flattened type * @param recordType type with possible nesting * @param flatteningMap if non-null, receives map from unflattened ordinal * to flattened ordinal (must have length at least * recordType.getFieldList().size()) * @return flattened equivalent */ public static RelDataType flattenRecordType( RelDataTypeFactory typeFactory, RelDataType recordType, int[] flatteningMap) { if (!recordType.isStruct()) { return recordType; } List fieldList = new ArrayList<>(); boolean nested = flattenFields( typeFactory, recordType, fieldList, flatteningMap); if (!nested) { return recordType; } List types = new ArrayList<>(); List fieldNames = new ArrayList<>(); Map fieldCnt = fieldList.stream() .map(RelDataTypeField::getName) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); int i = -1; for (RelDataTypeField field : fieldList) { ++i; types.add(field.getType()); String oriFieldName = field.getName(); // Patch up the field name with index if there are duplicates. // There is still possibility that the patched name conflicts with existing ones, // but that should be rare case. String fieldName = fieldCnt.get(oriFieldName) > 1 ? oriFieldName + "_" + i : oriFieldName; fieldNames.add(fieldName); } return typeFactory.createStructType(types, fieldNames); } public static boolean needsNullIndicator(RelDataType recordType) { // NOTE jvs 9-Mar-2005: It would be more storage-efficient to say that // no null indicator is required for structured type columns declared // as NOT NULL. However, the uniformity of always having a null // indicator makes things cleaner in many places. return recordType.getSqlTypeName() == SqlTypeName.STRUCTURED; } private static boolean flattenFields( RelDataTypeFactory typeFactory, RelDataType type, List list, int[] flatteningMap) { boolean nested = false; for (RelDataTypeField field : type.getFieldList()) { if (flatteningMap != null) { flatteningMap[field.getIndex()] = list.size(); } if (field.getType().isStruct()) { nested = true; flattenFields( typeFactory, field.getType(), list, null); } else if (field.getType().getComponentType() != null) { nested = true; // TODO jvs 14-Feb-2005: generalize to any kind of // collection type RelDataType flattenedCollectionType = typeFactory.createMultisetType( flattenRecordType( typeFactory, field.getType().getComponentType(), null), -1); if (field.getType() instanceof ArraySqlType) { flattenedCollectionType = typeFactory.createArrayType( flattenRecordType( typeFactory, field.getType().getComponentType(), null), -1); } field = new RelDataTypeFieldImpl( field.getName(), field.getIndex(), flattenedCollectionType); list.add(field); } else { list.add(field); } } return nested; } /** * Converts an instance of RelDataType to an instance of SqlDataTypeSpec. * * @param type type descriptor * @param charSetName charSet name * @param maxPrecision The max allowed precision. * @return corresponding parse representation */ public static SqlDataTypeSpec convertTypeToSpec(RelDataType type, String charSetName, int maxPrecision) { SqlTypeName typeName = type.getSqlTypeName(); // TODO jvs 28-Dec-2004: support row types, user-defined types, // interval types, multiset types, etc assert typeName != null; int precision = typeName.allowsPrec() ? type.getPrecision() : -1; // fix up the precision. if (maxPrecision > 0 && precision > maxPrecision) { precision = maxPrecision; } int scale = typeName.allowsScale() ? type.getScale() : -1; final SqlBasicTypeNameSpec typeNameSpec = new SqlBasicTypeNameSpec( typeName, precision, scale, charSetName, SqlParserPos.ZERO); // REVIEW jvs 28-Dec-2004: discriminate between precision/scale // zero and unspecified? // REVIEW angel 11-Jan-2006: // Use neg numbers to indicate unspecified precision/scale return new SqlDataTypeSpec(typeNameSpec, SqlParserPos.ZERO); } /** * Converts an instance of RelDataType to an instance of SqlDataTypeSpec. * * @param type type descriptor * @return corresponding parse representation */ public static SqlDataTypeSpec convertTypeToSpec(RelDataType type) { // TODO jvs 28-Dec-2004: collation String charSetName = inCharFamily(type) ? type.getCharset().name() : null; return convertTypeToSpec(type, charSetName, -1); } public static RelDataType createMultisetType( RelDataTypeFactory typeFactory, RelDataType type, boolean nullable) { RelDataType ret = typeFactory.createMultisetType(type, -1); return typeFactory.createTypeWithNullability(ret, nullable); } public static RelDataType createArrayType( RelDataTypeFactory typeFactory, RelDataType type, boolean nullable) { RelDataType ret = typeFactory.createArrayType(type, -1); return typeFactory.createTypeWithNullability(ret, nullable); } public static RelDataType createMapType( RelDataTypeFactory typeFactory, RelDataType keyType, RelDataType valueType, boolean nullable) { RelDataType ret = typeFactory.createMapType(keyType, valueType); return typeFactory.createTypeWithNullability(ret, nullable); } /** * Adds collation and charset to a character type, returns other types * unchanged. * * @param type Type * @param typeFactory Type factory * @return Type with added charset and collation, or unchanged type if it is * not a char type. */ public static RelDataType addCharsetAndCollation( RelDataType type, RelDataTypeFactory typeFactory) { if (!inCharFamily(type)) { return type; } Charset charset = type.getCharset(); if (charset == null) { charset = typeFactory.getDefaultCharset(); } SqlCollation collation = type.getCollation(); if (collation == null) { collation = SqlCollation.IMPLICIT; } // todo: should get the implicit collation from repository // instead of null type = typeFactory.createTypeWithCharsetAndCollation( type, charset, collation); SqlValidatorUtil.checkCharsetAndCollateConsistentIfCharType(type); return type; } /** * Returns whether two types are equal, ignoring nullability. * *

They need not com.hazelcast.com. from the same factory. * * @param factory Type factory * @param type1 First type * @param type2 Second type * @return whether types are equal, ignoring nullability */ public static boolean equalSansNullability( RelDataTypeFactory factory, RelDataType type1, RelDataType type2) { if (type1.equals(type2)) { return true; } if (type1.isNullable() == type2.isNullable()) { // If types have the same nullability and they weren't equal above, // they must be different. return false; } return type1.equals( factory.createTypeWithNullability(type2, type1.isNullable())); } /** * Returns whether two struct types are equal, ignoring nullability. * *

They do not need to com.hazelcast.com. from the same factory. * * @param factory Type factory * @param type1 First type * @param type2 Second type * @param nameMatcher Name matcher used to com.hazelcast.com.are the field names, if null, * the field names are also ignored * * @return Whether types are equal, ignoring nullability */ public static boolean equalAsStructSansNullability( RelDataTypeFactory factory, RelDataType type1, RelDataType type2, SqlNameMatcher nameMatcher) { assert type1.isStruct(); assert type2.isStruct(); if (type1.getFieldCount() != type2.getFieldCount()) { return false; } for (Pair pair : Pair.zip(type1.getFieldList(), type2.getFieldList())) { if (nameMatcher != null && !nameMatcher.matches(pair.left.getName(), pair.right.getName())) { return false; } if (!equalSansNullability(factory, pair.left.getType(), pair.right.getType())) { return false; } } return true; } /** * Returns the ordinal of a given field in a record type, or -1 if the field * is not found. * * @param type Record type * @param fieldName Name of field * @return Ordinal of field */ public static int findField(RelDataType type, String fieldName) { List fields = type.getFieldList(); for (int i = 0; i < fields.size(); i++) { RelDataTypeField field = fields.get(i); if (field.getName().equals(fieldName)) { return i; } } return -1; } /** * Selects data types of the specified fields from an input row type. * This is useful when identifying data types of a function that is going * to operate on inputs that are specified as field ordinals (e.g. * aggregate calls). * * @param rowType input row type * @param requiredFields ordinals of the projected fields * @return list of data types that are requested by requiredFields */ public static List projectTypes(final RelDataType rowType, final List requiredFields) { final List fields = rowType.getFieldList(); return new AbstractList() { @Override public RelDataType get(int index) { return fields.get(requiredFields.get(index).intValue()).getType(); } @Override public int size() { return requiredFields.size(); } }; } /** * Records a struct type with no fields. * * @param typeFactory Type factory * @return Struct type with no fields */ public static RelDataType createEmptyStructType( RelDataTypeFactory typeFactory) { return typeFactory.createStructType( ImmutableList.of(), ImmutableList.of()); } /** Returns whether a type is flat. It is not flat if it is a record type that * has one or more fields that are themselves record types. */ public static boolean isFlat(RelDataType type) { if (type.isStruct()) { for (RelDataTypeField field : type.getFieldList()) { if (field.getType().isStruct()) { return false; } } } return true; } /** * Returns whether two types are com.hazelcast.com.arable. They need to be scalar types of * the same family, or struct types whose fields are pairwise com.hazelcast.com.arable. * * @param type1 First type * @param type2 Second type * @return Whether types are com.hazelcast.com.arable */ public static boolean isComparable(RelDataType type1, RelDataType type2) { if (type1.isStruct() != type2.isStruct()) { return false; } if (type1.isStruct()) { int n = type1.getFieldCount(); if (n != type2.getFieldCount()) { return false; } for (Pair pair : Pair.zip(type1.getFieldList(), type2.getFieldList())) { if (!isComparable(pair.left.getType(), pair.right.getType())) { return false; } } return true; } final RelDataTypeFamily family1 = family(type1); final RelDataTypeFamily family2 = family(type2); if (family1 == family2) { return true; } // If one of the arguments is of type 'ANY', return true. if (family1 == SqlTypeFamily.ANY || family2 == SqlTypeFamily.ANY) { return true; } // If one of the arguments is of type 'NULL', return true. if (family1 == SqlTypeFamily.NULL || family2 == SqlTypeFamily.NULL) { return true; } // We can implicitly convert from character to date if (family1 == SqlTypeFamily.CHARACTER && canConvertStringInCompare(family2) || family2 == SqlTypeFamily.CHARACTER && canConvertStringInCompare(family1)) { return true; } return false; } /** Returns the least restrictive type T, such that a value of type T can be * com.hazelcast.com.ared with values of type {@code type0} and {@code type1} using * {@code =}. */ public static RelDataType leastRestrictiveForComparison( RelDataTypeFactory typeFactory, RelDataType type1, RelDataType type2) { final RelDataType type = typeFactory.leastRestrictive(ImmutableList.of(type1, type2)); if (type != null) { return type; } final RelDataTypeFamily family1 = family(type1); final RelDataTypeFamily family2 = family(type2); // If one of the arguments is of type 'ANY', we can com.hazelcast.com.are. if (family1 == SqlTypeFamily.ANY) { return type2; } if (family2 == SqlTypeFamily.ANY) { return type1; } // If one of the arguments is of type 'NULL', we can com.hazelcast.com.are. if (family1 == SqlTypeFamily.NULL) { return type2; } if (family2 == SqlTypeFamily.NULL) { return type1; } // We can implicitly convert from character to date, numeric, etc. if (family1 == SqlTypeFamily.CHARACTER && canConvertStringInCompare(family2)) { return type2; } if (family2 == SqlTypeFamily.CHARACTER && canConvertStringInCompare(family1)) { return type1; } return null; } protected static RelDataTypeFamily family(RelDataType type) { // REVIEW jvs 2-June-2005: This is needed to keep // the Saffron type system happy. RelDataTypeFamily family = null; if (type.getSqlTypeName() != null) { family = type.getSqlTypeName().getFamily(); } if (family == null) { family = type.getFamily(); } return family; } /** * Returns whether all types in a collection have the same family, as * determined by {@link #isSameFamily(RelDataType, RelDataType)}. * * @param types Types to check * @return true if all types are of the same family */ public static boolean areSameFamily(Iterable types) { final List typeList = ImmutableList.copyOf(types); if (Sets.newHashSet(RexUtil.families(typeList)).size() < 2) { return true; } for (Pair adjacent : Pair.adjacents(typeList)) { if (!isSameFamily(adjacent.left, adjacent.right)) { return false; } } return true; } /** * Returns whether two types are scalar types of the same family, or struct types whose fields * are pairwise of the same family. * * @param type1 First type * @param type2 Second type * @return Whether types have the same family */ private static boolean isSameFamily(RelDataType type1, RelDataType type2) { if (type1.isStruct() != type2.isStruct()) { return false; } if (type1.isStruct()) { int n = type1.getFieldCount(); if (n != type2.getFieldCount()) { return false; } for (Pair pair : Pair.zip(type1.getFieldList(), type2.getFieldList())) { if (!isSameFamily(pair.left.getType(), pair.right.getType())) { return false; } } return true; } final RelDataTypeFamily family1 = family(type1); final RelDataTypeFamily family2 = family(type2); return family1 == family2; } /** Returns whether a character data type can be implicitly converted to a * given family in a com.hazelcast.com.are operation. */ private static boolean canConvertStringInCompare(RelDataTypeFamily family) { if (family instanceof SqlTypeFamily) { SqlTypeFamily sqlTypeFamily = (SqlTypeFamily) family; switch (sqlTypeFamily) { case DATE: case TIME: case TIMESTAMP: case INTERVAL_DAY_TIME: case INTERVAL_YEAR_MONTH: case NUMERIC: case APPROXIMATE_NUMERIC: case EXACT_NUMERIC: case INTEGER: case BOOLEAN: return true; } } return false; } /** * Checks whether a type represents Unicode character data. * * @param type type to test * @return whether type represents Unicode character data */ public static boolean isUnicode(RelDataType type) { Charset charset = type.getCharset(); if (charset == null) { return false; } return charset.name().startsWith("UTF"); } /** Returns the larger of two precisions, treating * {@link RelDataType#PRECISION_NOT_SPECIFIED} as infinity. */ public static int maxPrecision(int p0, int p1) { return (p0 == RelDataType.PRECISION_NOT_SPECIFIED || p0 >= p1 && p1 != RelDataType.PRECISION_NOT_SPECIFIED) ? p0 : p1; } /** Returns whether a precision is greater or equal than another, * treating {@link RelDataType#PRECISION_NOT_SPECIFIED} as infinity. */ public static int com.hazelcast.com.arePrecision(int p0, int p1) { if (p0 == p1) { return 0; } if (p0 == RelDataType.PRECISION_NOT_SPECIFIED) { return 1; } if (p1 == RelDataType.PRECISION_NOT_SPECIFIED) { return -1; } return Integer.com.hazelcast.com.are(p0, p1); } /** * @return true if type is ARRAY */ public static boolean isArray(RelDataType type) { return type.getSqlTypeName() == SqlTypeName.ARRAY; } /** * @return true if type is MAP */ public static boolean isMap(RelDataType type) { SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return false; } return type.getSqlTypeName() == SqlTypeName.MAP; } /** * @return true if type is CHARACTER */ public static boolean isCharacter(RelDataType type) { SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return false; } return SqlTypeFamily.CHARACTER.contains(type); } /** * @return true if the type is a CHARACTER or contains a CHARACTER type */ public static boolean hasCharactor(RelDataType type) { if (isCharacter(type)) { return true; } if (isArray(type)) { return hasCharactor(type.getComponentType()); } return false; } /** * @return true if type is STRING */ public static boolean isString(RelDataType type) { SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return false; } return SqlTypeFamily.STRING.contains(type); } /** * @return true if type is BOOLEAN */ public static boolean isBoolean(RelDataType type) { SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return false; } return SqlTypeFamily.BOOLEAN.contains(type); } /** * @return true if type is BINARY */ public static boolean isBinary(RelDataType type) { SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return false; } return SqlTypeFamily.BINARY.contains(type); } /** * @return true if type is Atomic */ public static boolean isAtomic(RelDataType type) { SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return false; } return SqlTypeUtil.isDatetime(type) || SqlTypeUtil.isNumeric(type) || SqlTypeUtil.isString(type) || SqlTypeUtil.isBoolean(type); } /** Get decimal with max precision/scale for the current type system. */ public static RelDataType getMaxPrecisionScaleDecimal(RelDataTypeFactory factory) { int maxPrecision = factory.getTypeSystem().getMaxNumericPrecision(); int maxScale = factory.getTypeSystem().getMaxNumericScale(); return factory.createSqlType(SqlTypeName.DECIMAL, maxPrecision, maxScale); } /** * Keeps only the last N fields and returns the new struct type. */ public static RelDataType extractLastNFields(RelDataTypeFactory typeFactory, RelDataType type, int numToKeep) { assert type.isStruct(); assert type.getFieldCount() >= numToKeep; final int fieldsCnt = type.getFieldCount(); return typeFactory.createStructType( type.getFieldList().subList(fieldsCnt - numToKeep, fieldsCnt)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy