org.apache.calcite.rex.RexLiteral Maven / Gradle / Ivy
Show all versions of flink-table-planner_2.11 Show documentation
/*
* 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.rex;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.avatica.util.DateTimeUtils;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.config.CalciteSystemProperty;
import org.apache.calcite.linq4j.function.Functions;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.runtime.FlatLists;
import org.apache.calcite.runtime.GeoFunctions;
import org.apache.calcite.runtime.Geometries;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserUtil;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.CompositeList;
import org.apache.calcite.util.ConversionUtil;
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.Sarg;
import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimestampString;
import org.apache.calcite.util.Util;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
/**
* Constant value in a row-expression.
*
* There are several methods for creating literals in {@link RexBuilder}: {@link
* RexBuilder#makeLiteral(boolean)} and so forth.
*
*
How is the value stored? In that respect, the class is somewhat of a black box. There is a
* {@link #getValue} method which returns the value as an object, but the type of that value is
* implementation detail, and it is best that your code does not depend upon that knowledge. It is
* better to use task-oriented methods such as {@link #getValue2} and {@link #toJavaString}.
*
*
The allowable types and combinations are:
*
*
* Allowable types for RexLiteral instances
*
* TypeName
* Meaning
* Value type
*
*
* {@link SqlTypeName#NULL}
* The null value. It has its own special type.
* null
*
*
* {@link SqlTypeName#BOOLEAN}
* Boolean, namely TRUE
, FALSE
or
* UNKNOWN
.
* {@link Boolean}, or null represents the UNKNOWN value
*
*
* {@link SqlTypeName#DECIMAL}
* Exact number, for example 0
, -.5
,
* 12345
.
* {@link BigDecimal}
*
*
* {@link SqlTypeName#DOUBLE}
* Approximate number, for example 6.023E-23
.
* {@link BigDecimal}
*
*
* {@link SqlTypeName#DATE}
* Date, for example DATE '1969-04'29'
* {@link Calendar};
* also {@link Calendar} (UTC time zone)
* and {@link Integer} (days since POSIX epoch)
*
*
* {@link SqlTypeName#TIME}
* Time, for example TIME '18:37:42.567'
* {@link Calendar};
* also {@link Calendar} (UTC time zone)
* and {@link Integer} (milliseconds since midnight)
*
*
* {@link SqlTypeName#TIMESTAMP}
* Timestamp, for example TIMESTAMP '1969-04-29
* 18:37:42.567'
* {@link TimestampString};
* also {@link Calendar} (UTC time zone)
* and {@link Long} (milliseconds since POSIX epoch)
*
*
* {@link SqlTypeName#INTERVAL_DAY},
* {@link SqlTypeName#INTERVAL_DAY_HOUR},
* {@link SqlTypeName#INTERVAL_DAY_MINUTE},
* {@link SqlTypeName#INTERVAL_DAY_SECOND},
* {@link SqlTypeName#INTERVAL_HOUR},
* {@link SqlTypeName#INTERVAL_HOUR_MINUTE},
* {@link SqlTypeName#INTERVAL_HOUR_SECOND},
* {@link SqlTypeName#INTERVAL_MINUTE},
* {@link SqlTypeName#INTERVAL_MINUTE_SECOND},
* {@link SqlTypeName#INTERVAL_SECOND}
* Interval, for example INTERVAL '4:3:2' HOUR TO SECOND
* {@link BigDecimal};
* also {@link Long} (milliseconds)
*
*
* {@link SqlTypeName#INTERVAL_YEAR},
* {@link SqlTypeName#INTERVAL_YEAR_MONTH},
* {@link SqlTypeName#INTERVAL_MONTH}
* Interval, for example INTERVAL '2-3' YEAR TO MONTH
* {@link BigDecimal};
* also {@link Integer} (months)
*
*
* {@link SqlTypeName#CHAR}
* Character constant, for example 'Hello, world!'
,
* ''
, _N'Bonjour'
, _ISO-8859-1'It''s superman!'
* COLLATE SHIFT_JIS$ja_JP$2
. These are always CHAR, never VARCHAR.
* {@link NlsString};
* also {@link String}
*
*
* {@link SqlTypeName#BINARY}
* Binary constant, for example X'7F34'
. (The number of hexits
* must be even; see above.) These constants are always BINARY, never
* VARBINARY.
* {@link ByteBuffer};
* also {@code byte[]}
*
*
* {@link SqlTypeName#SYMBOL}
* A symbol is a special type used to make parsing easier; it is not part of
* the SQL standard, and is not exposed to end-users. It is used to hold a flag,
* such as the LEADING flag in a call to the function
* TRIM([LEADING|TRAILING|BOTH] chars FROM string)
.
* An enum class
*
*
*
* Line 374 ~ 376: fix FLINK-20942, the file should be removed when upgrade to CALCITE 1.27.0
* release.
*/
public class RexLiteral extends RexNode {
// ~ Instance fields --------------------------------------------------------
/**
* The value of this literal. Must be consistent with its type, as per {@link
* #valueMatchesType}. For example, you can't store an {@link Integer} value here just because
* you feel like it -- all numbers are represented by a {@link BigDecimal}. But since this field
* is private, it doesn't really matter how the values are stored.
*/
private final Comparable value;
/** The real type of this literal, as reported by {@link #getType}. */
private final RelDataType type;
// TODO jvs 26-May-2006: Use SqlTypeFamily instead; it exists
// for exactly this purpose (to avoid the confusion which results
// from overloading SqlTypeName).
/**
* An indication of the broad type of this literal -- even if its type isn't a SQL type.
* Sometimes this will be different than the SQL type; for example, all exact numbers, including
* integers have typeName {@link SqlTypeName#DECIMAL}. See {@link #valueMatchesType} for the
* definitive story.
*/
private final SqlTypeName typeName;
private static final ImmutableList TIME_UNITS =
ImmutableList.copyOf(TimeUnit.values());
// ~ Constructors -----------------------------------------------------------
/** Creates a RexLiteral
. */
RexLiteral(Comparable value, RelDataType type, SqlTypeName typeName) {
this.value = value;
this.type = Objects.requireNonNull(type);
this.typeName = Objects.requireNonNull(typeName);
Preconditions.checkArgument(valueMatchesType(value, typeName, true));
Preconditions.checkArgument((value == null) == type.isNullable());
Preconditions.checkArgument(typeName != SqlTypeName.ANY);
this.digest = computeDigest(RexDigestIncludeType.OPTIONAL);
}
// ~ Methods ----------------------------------------------------------------
/**
* Returns a string which concisely describes the definition of this rex literal. Two literals
* are equivalent if and only if their digests are the same.
*
* The digest does not contain the expression's identity, but does include the identity of
* children.
*
*
Technically speaking 1:INT differs from 1:FLOAT, so we need data type in the literal's
* digest, however we want to avoid extra verbosity of the {@link RelNode#getDigest()} for
* readability purposes, so we omit type info in certain cases. For instance, 1:INT becomes 1
* (INT is implied by default), however 1:BIGINT always holds the type
*
*
Here's a non-exhaustive list of the "well known cases":
*
*
* - Hide "NOT NULL" for not null literals
*
- Hide INTEGER, BOOLEAN, SYMBOL, TIME(0), TIMESTAMP(0), DATE(0) types
*
- Hide collation when it matches IMPLICIT/COERCIBLE
*
- Hide charset when it matches default
*
- Hide CHAR(xx) when literal length is equal to the precision of the type. In other
* words, use 'Bob' instead of 'Bob':CHAR(3)
*
- Hide BOOL for AND/OR arguments. In other words, AND(true, null) means null is BOOL.
*
- Hide types for literals in simple binary operations (e.g. +, -, *, /, comparison) when
* type of the other argument is clear. See {@link RexCall#computeDigest(boolean)} For
* instance: =(true. null) means null is BOOL. =($0, null) means the type of null matches
* the type of $0.
*
*
* @param includeType whether the digest should include type or not
* @return digest
*/
public final String computeDigest(RexDigestIncludeType includeType) {
if (includeType == RexDigestIncludeType.OPTIONAL) {
if (digest != null) {
// digest is initialized with OPTIONAL, so cached value matches for
// includeType=OPTIONAL as well
return digest;
}
// Compute we should include the type or not
includeType = digestIncludesType();
} else if (digest != null && includeType == digestIncludesType()) {
// The digest is always computed with includeType=OPTIONAL
// If it happened to omit the type, we want to optimize computeDigest(NO_TYPE) as well
// If the digest includes the type, we want to optimize computeDigest(ALWAYS)
return digest;
}
return toJavaString(value, typeName, type, includeType);
}
/**
* Returns true if {@link RexDigestIncludeType#OPTIONAL} digest would include data type.
*
* @see RexCall#computeDigest(boolean)
* @return true if {@link RexDigestIncludeType#OPTIONAL} digest would include data type
*/
RexDigestIncludeType digestIncludesType() {
return shouldIncludeType(value, type);
}
/** Returns whether a value is appropriate for its type. (We have rules about these things!) */
public static boolean valueMatchesType(Comparable value, SqlTypeName typeName, boolean strict) {
if (value == null) {
return true;
}
switch (typeName) {
case BOOLEAN:
// Unlike SqlLiteral, we do not allow boolean null.
return value instanceof Boolean;
case NULL:
return false; // value should have been null
case INTEGER: // not allowed -- use Decimal
case TINYINT:
case SMALLINT:
if (strict) {
throw Util.unexpected(typeName);
}
// fall through
case DECIMAL:
case DOUBLE:
case FLOAT:
case REAL:
case BIGINT:
return value instanceof BigDecimal;
case DATE:
return value instanceof DateString;
case TIME:
return value instanceof TimeString;
case TIME_WITH_LOCAL_TIME_ZONE:
return value instanceof TimeString;
case TIMESTAMP:
return value instanceof TimestampString;
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return value instanceof TimestampString;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
// The value of a DAY-TIME interval (whatever the start and end units,
// even say HOUR TO MINUTE) is in milliseconds (perhaps fractional
// milliseconds). The value of a YEAR-MONTH interval is in months.
return value instanceof BigDecimal;
case VARBINARY: // not allowed -- use Binary
if (strict) {
throw Util.unexpected(typeName);
}
// fall through
case BINARY:
return value instanceof ByteString;
case VARCHAR: // not allowed -- use Char
if (strict) {
throw Util.unexpected(typeName);
}
// fall through
case CHAR:
// A SqlLiteral's charset and collation are optional; not so a
// RexLiteral.
return (value instanceof NlsString)
&& (((NlsString) value).getCharset() != null)
&& (((NlsString) value).getCollation() != null);
case SARG:
return value instanceof Sarg;
case SYMBOL:
return value instanceof Enum;
case ROW:
case MULTISET:
return value instanceof List;
case GEOMETRY:
return value instanceof Geometries.Geom;
case ANY:
// Literal of type ANY is not legal. "CAST(2 AS ANY)" remains
// an integer literal surrounded by a cast function.
return false;
default:
throw Util.unexpected(typeName);
}
}
/** Returns the strict literal type for a given type. */
public static SqlTypeName strictTypeName(RelDataType type) {
final SqlTypeName typeName = type.getSqlTypeName();
switch (typeName) {
case INTEGER:
case TINYINT:
case SMALLINT:
return SqlTypeName.DECIMAL;
case REAL:
case FLOAT:
return SqlTypeName.DOUBLE;
case VARBINARY:
return SqlTypeName.BINARY;
case VARCHAR:
return SqlTypeName.CHAR;
default:
return typeName;
}
}
private static String toJavaString(
Comparable value,
SqlTypeName typeName,
RelDataType type,
RexDigestIncludeType includeType) {
assert includeType != RexDigestIncludeType.OPTIONAL
: "toJavaString must not be called with includeType=OPTIONAL";
if (value == null) {
return includeType == RexDigestIncludeType.NO_TYPE
? "null"
: "null:" + type.getFullTypeString();
}
StringBuilder sb = new StringBuilder();
appendAsJava(value, sb, typeName, type, false, includeType);
if (includeType != RexDigestIncludeType.NO_TYPE) {
sb.append(':');
final String fullTypeString = type.getFullTypeString();
if (!fullTypeString.endsWith("NOT NULL")) {
sb.append(fullTypeString);
} else {
// Trim " NOT NULL". Apparently, the literal is not null, so we just print the data
// type.
sb.append(fullTypeString, 0, fullTypeString.length() - 9);
}
}
return sb.toString();
}
/**
* Computes if data type can be omitted from the digset.
*
* For instance, {@code 1:BIGINT} has to keep data type while {@code 1:INT} should be
* represented as just {@code 1}.
*
*
Implementation assumption: this method should be fast. In fact might call {@link
* NlsString#getValue()} which could decode the string, however we rely on the cache there.
*
* @see RexLiteral#computeDigest(RexDigestIncludeType)
* @param value value of the literal
* @param type type of the literal
* @return NO_TYPE when type can be omitted, ALWAYS otherwise
*/
private static RexDigestIncludeType shouldIncludeType(Comparable value, RelDataType type) {
if (type.isNullable()) {
// This means "null literal", so we require a type for it
// There might be exceptions like AND(null, true) which are handled by
// RexCall#computeDigest
return RexDigestIncludeType.ALWAYS;
}
// The variable here simplifies debugging (one can set a breakpoint at return)
// final ensures we set the value in all the branches, and it ensures the value is set just
// once
final RexDigestIncludeType includeType;
if (type.getSqlTypeName() == SqlTypeName.BOOLEAN
|| type.getSqlTypeName() == SqlTypeName.INTEGER
|| type.getSqlTypeName() == SqlTypeName.SYMBOL) {
// We don't want false:BOOLEAN NOT NULL, so we don't print type information for
// non-nullable BOOLEAN and INTEGER
includeType = RexDigestIncludeType.NO_TYPE;
} else if (type.getSqlTypeName() == SqlTypeName.CHAR && value instanceof NlsString) {
NlsString nlsString = (NlsString) value;
// Ignore type information for 'Bar':CHAR(3)
if (((nlsString.getCharset() != null
&& type.getCharset().equals(nlsString.getCharset()))
|| (nlsString.getCharset() == null
&& SqlCollation.IMPLICIT
.getCharset()
.equals(type.getCharset())))
&& nlsString.getCollation().equals(type.getCollation())
&& ((NlsString) value).getValue().length() == type.getPrecision()) {
includeType = RexDigestIncludeType.NO_TYPE;
} else {
includeType = RexDigestIncludeType.ALWAYS;
}
} else if (type.getPrecision() == 0
&& (type.getSqlTypeName() == SqlTypeName.TIME
|| type.getSqlTypeName() == SqlTypeName.TIMESTAMP
|| type.getSqlTypeName() == SqlTypeName.DATE)) {
// Ignore type information for '12:23:20':TIME(0)
// Note that '12:23:20':TIME WITH LOCAL TIME ZONE
includeType = RexDigestIncludeType.NO_TYPE;
} else {
includeType = RexDigestIncludeType.ALWAYS;
}
return includeType;
}
/**
* Returns whether a value is valid as a constant value, using the same criteria as {@link
* #valueMatchesType}.
*/
public static boolean validConstant(Object o, Litmus litmus) {
if (o == null
|| o instanceof BigDecimal
|| o instanceof NlsString
|| o instanceof ByteString
|| o instanceof Boolean) {
return litmus.succeed();
} else if (o instanceof List) {
List list = (List) o;
for (Object o1 : list) {
if (!validConstant(o1, litmus)) {
return litmus.fail("not a constant: {}", o1);
}
}
return litmus.succeed();
} else if (o instanceof Map) {
@SuppressWarnings("unchecked")
final Map