blob: 847e5962869ed78e45388468eaafffa1d25febe7 [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.calcite.sql.type;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.util.SerializableCharset;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.nio.charset.Charset;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
/**
* BasicSqlType represents a standard atomic SQL type (excluding interval
* types).
*
* <p>Instances of this class are immutable.
*/
public class BasicSqlType extends AbstractSqlType {
//~ Static fields/initializers ---------------------------------------------
//~ Instance fields --------------------------------------------------------
private final int precision;
private final int scale;
protected final RelDataTypeSystem typeSystem;
private final @Nullable SqlCollation collation;
private final @Nullable SerializableCharset wrappedCharset;
//~ Constructors -----------------------------------------------------------
/**
* Constructs a type with no parameters. This should only be called from a
* factory method.
*
* @param typeSystem Type system
* @param typeName Type name
*/
public BasicSqlType(RelDataTypeSystem typeSystem, SqlTypeName typeName) {
this(typeSystem, typeName, false);
}
protected BasicSqlType(RelDataTypeSystem typeSystem, SqlTypeName typeName,
boolean nullable) {
this(typeSystem, typeName, nullable, PRECISION_NOT_SPECIFIED,
SCALE_NOT_SPECIFIED, null, null);
checkPrecScale(typeName, false, false);
}
/**
* Constructs a type with precision/length but no scale.
*
* @param typeSystem Type system
* @param typeName Type name
* @param precision Precision (called length for some types)
*/
public BasicSqlType(RelDataTypeSystem typeSystem, SqlTypeName typeName,
int precision) {
this(typeSystem, typeName, false, precision, SCALE_NOT_SPECIFIED, null,
null);
checkPrecScale(typeName, true, false);
}
/**
* Constructs a type with precision/length and scale.
*
* @param typeSystem Type system
* @param typeName Type name
* @param precision Precision (called length for some types)
* @param scale Scale
*/
public BasicSqlType(RelDataTypeSystem typeSystem, SqlTypeName typeName,
int precision, int scale) {
this(typeSystem, typeName, false, precision, scale, null, null);
checkPrecScale(typeName, true, true);
}
/** Internal constructor. */
private BasicSqlType(
RelDataTypeSystem typeSystem,
SqlTypeName typeName,
boolean nullable,
int precision,
int scale,
@Nullable SqlCollation collation,
@Nullable SerializableCharset wrappedCharset) {
super(typeName, nullable, null);
this.typeSystem = Objects.requireNonNull(typeSystem, "typeSystem");
this.precision = precision;
this.scale = scale;
this.collation = collation;
this.wrappedCharset = wrappedCharset;
computeDigest();
}
/** Throws if {@code typeName} does not allow the given combination of
* precision and scale. */
protected static void checkPrecScale(SqlTypeName typeName,
boolean precisionSpecified, boolean scaleSpecified) {
if (!typeName.allowsPrecScale(precisionSpecified, scaleSpecified)) {
throw new AssertionError("typeName.allowsPrecScale("
+ precisionSpecified + ", " + scaleSpecified + "): " + typeName);
}
}
//~ Methods ----------------------------------------------------------------
/**
* Constructs a type with nullablity.
*/
BasicSqlType createWithNullability(boolean nullable) {
if (nullable == this.isNullable) {
return this;
}
return new BasicSqlType(this.typeSystem, this.typeName, nullable,
this.precision, this.scale, this.collation, this.wrappedCharset);
}
/**
* Constructs a type with charset and collation.
*
* <p>This must be a character type.
*/
BasicSqlType createWithCharsetAndCollation(Charset charset,
SqlCollation collation) {
checkArgument(SqlTypeUtil.inCharFamily(this));
return new BasicSqlType(this.typeSystem, this.typeName, this.isNullable,
this.precision, this.scale, collation,
SerializableCharset.forCharset(charset));
}
@Override public int getPrecision() {
if (precision == PRECISION_NOT_SPECIFIED) {
return typeSystem.getDefaultPrecision(typeName);
}
return precision;
}
@Override public int getScale() {
if (scale == SCALE_NOT_SPECIFIED) {
switch (typeName) {
case TINYINT:
case SMALLINT:
case INTEGER:
case BIGINT:
case DECIMAL:
return 0;
default:
// fall through
}
}
return scale;
}
@Override public @Nullable Charset getCharset() {
return wrappedCharset == null ? null : wrappedCharset.getCharset();
}
@Override public @Nullable SqlCollation getCollation() {
return collation;
}
// implement RelDataTypeImpl
@Override protected void generateTypeString(StringBuilder sb, boolean withDetail) {
// Called to make the digest, which equals() compares;
// so equivalent data types must produce identical type strings.
sb.append(typeName.name());
boolean printPrecision = precision != PRECISION_NOT_SPECIFIED;
boolean printScale = scale != SCALE_NOT_SPECIFIED;
if (printPrecision) {
sb.append('(');
sb.append(getPrecision());
if (printScale) {
sb.append(", ");
sb.append(getScale());
}
sb.append(')');
}
if (!withDetail) {
return;
}
if (wrappedCharset != null
&& !SqlCollation.IMPLICIT.getCharset().equals(wrappedCharset.getCharset())) {
sb.append(" CHARACTER SET \"");
sb.append(wrappedCharset.getCharset().name());
sb.append("\"");
}
if (collation != null
&& collation != SqlCollation.IMPLICIT && collation != SqlCollation.COERCIBLE) {
sb.append(" COLLATE \"");
sb.append(collation.getCollationName());
sb.append("\"");
}
}
/**
* Returns a value which is a limit for this type.
*
* <p>For example,
*
* <table border="1">
* <caption>Limits</caption>
* <tr>
* <th>Datatype</th>
* <th>sign</th>
* <th>limit</th>
* <th>beyond</th>
* <th>precision</th>
* <th>scale</th>
* <th>Returns</th>
* </tr>
* <tr>
* <td>Integer</td>
* <td>true</td>
* <td>true</td>
* <td>false</td>
* <td>-1</td>
* <td>-1</td>
* <td>2147483647 (2 ^ 31 -1 = MAXINT)</td>
* </tr>
* <tr>
* <td>Integer</td>
* <td>true</td>
* <td>true</td>
* <td>true</td>
* <td>-1</td>
* <td>-1</td>
* <td>2147483648 (2 ^ 31 = MAXINT + 1)</td>
* </tr>
* <tr>
* <td>Integer</td>
* <td>false</td>
* <td>true</td>
* <td>false</td>
* <td>-1</td>
* <td>-1</td>
* <td>-2147483648 (-2 ^ 31 = MININT)</td>
* </tr>
* <tr>
* <td>Boolean</td>
* <td>true</td>
* <td>true</td>
* <td>false</td>
* <td>-1</td>
* <td>-1</td>
* <td>TRUE</td>
* </tr>
* <tr>
* <td>Varchar</td>
* <td>true</td>
* <td>true</td>
* <td>false</td>
* <td>10</td>
* <td>-1</td>
* <td>'ZZZZZZZZZZ'</td>
* </tr>
* </table>
*
* @param sign If true, returns upper limit, otherwise lower limit
* @param limit If true, returns value at or near to overflow; otherwise
* value at or near to underflow
* @param beyond If true, returns the value just beyond the limit, otherwise
* the value at the limit
* @return Limit value
*/
public @Nullable Object getLimit(
boolean sign,
SqlTypeName.Limit limit,
boolean beyond) {
int precision = typeName.allowsPrec() ? this.getPrecision() : -1;
int scale = typeName.allowsScale() ? this.getScale() : -1;
return typeName.getLimit(
sign,
limit,
beyond,
precision,
scale);
}
}