/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde 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.eigenbase.rex;
import java.io.*;
import java.math.*;
import java.nio.*;
import java.nio.charset.*;
import java.util.*;
import org.eigenbase.reltype.*;
import org.eigenbase.sql.*;
import org.eigenbase.sql.fun.*;
import org.eigenbase.sql.parser.*;
import org.eigenbase.sql.type.*;
import org.eigenbase.util.*;
import org.eigenbase.util14.*;
import net.hydromatic.avatica.ByteString;
/**
* Constant value in a row-expression.
*
* <p>There are several methods for creating literals in {@link RexBuilder}:
* {@link RexBuilder#makeLiteral(boolean)} and so forth.</p>
*
* <p>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}.</p>
*
* <p>The allowable types and combinations are:</p>
*
* <table>
* <caption>Allowable types for RexLiteral instances</caption>
* <tr>
* <th>TypeName</th>
* <th>Meaing</th>
* <th>Value type</th>
* </tr>
* <tr>
* <td>{@link SqlTypeName#NULL}</td>
* <td>The null value. It has its own special type.</td>
* <td>null</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#BOOLEAN}</td>
* <td>Boolean, namely <code>TRUE</code>, <code>FALSE</code> or <code>
* UNKNOWN</code>.</td>
* <td>{@link Boolean}, or null represents the UNKNOWN value</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#DECIMAL}</td>
* <td>Exact number, for example <code>0</code>, <code>-.5</code>, <code>
* 12345</code>.</td>
* <td>{@link BigDecimal}</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#DOUBLE}</td>
* <td>Approximate number, for example <code>6.023E-23</code>.</td>
* <td>{@link BigDecimal}</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#DATE}</td>
* <td>Date, for example <code>DATE '1969-04'29'</code></td>
* <td>{@link Calendar}</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#TIME}</td>
* <td>Time, for example <code>TIME '18:37:42.567'</code></td>
* <td>{@link Calendar}</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#TIMESTAMP}</td>
* <td>Timestamp, for example <code>TIMESTAMP '1969-04-29
* 18:37:42.567'</code></td>
* <td>{@link Calendar}</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#CHAR}</td>
* <td>Character constant, for example <code>'Hello, world!'</code>, <code>
* ''</code>, <code>_N'Bonjour'</code>, <code>_ISO-8859-1'It''s superman!'
* COLLATE SHIFT_JIS$ja_JP$2</code>. These are always CHAR, never VARCHAR.</td>
* <td>{@link NlsString}</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#BINARY}</td>
* <td>Binary constant, for example <code>X'7F34'</code>. (The number of hexits
* must be even; see above.) These constants are always BINARY, never
* VARBINARY.</td>
* <td>{@link ByteBuffer}</td>
* </tr>
* <tr>
* <td>{@link SqlTypeName#SYMBOL}</td>
* <td>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 <code>
* TRIM([LEADING|TRAILING|BOTH] chars FROM string)</code>.</td>
* <td>An enum class</td>
* </tr>
* </table>
*/
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;
//~ Constructors -----------------------------------------------------------
/**
* Creates a <code>RexLiteral</code>.
*/
RexLiteral(
Comparable value,
RelDataType type,
SqlTypeName typeName) {
assert type != null;
assert value == null || valueMatchesType(value, typeName, true);
assert (value == null) == type.isNullable();
assert typeName != SqlTypeName.ANY;
this.value = value;
this.type = type;
this.typeName = typeName;
this.digest = toJavaString(value, typeName);
}
//~ Methods ----------------------------------------------------------------
/**
* @return whether 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:
case TIME:
case TIMESTAMP:
return value instanceof Calendar;
case INTERVAL_DAY_TIME:
case INTERVAL_YEAR_MONTH:
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 SYMBOL:
return value instanceof Enum;
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);
}
}
private static String toJavaString(
Comparable value,
SqlTypeName typeName) {
if (value == null) {
return "null";
}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
printAsJava(value, pw, typeName, false);
pw.flush();
return sw.toString();
}
/**
* Prints the value this literal as a Java string constant.
*/
public void printAsJava(PrintWriter pw) {
printAsJava(value, pw, typeName, true);
}
/**
* Prints a value as a Java string. The value must be consistent with the
* type, as per {@link #valueMatchesType}.
*
* <p>Typical return values:
*
* <ul>
* <li>true</li>
* <li>null</li>
* <li>"Hello, world!"</li>
* <li>1.25</li>
* <li>1234ABCD</li>
* </ul>
*
* @param value Value
* @param pw Writer to write to
* @param typeName Type family
*/
private static void printAsJava(
Comparable value,
PrintWriter pw,
SqlTypeName typeName,
boolean java) {
switch (typeName) {
case CHAR:
NlsString nlsString = (NlsString) value;
if (java) {
Util.printJavaString(
pw,
nlsString.getValue(),
true);
} else {
boolean includeCharset =
(nlsString.getCharsetName() != null)
&& !nlsString.getCharsetName().equals(
SaffronProperties.instance().defaultCharset.get());
pw.print(nlsString.asSql(includeCharset, false));
}
break;
case BOOLEAN:
assert value instanceof Boolean;
pw.print(((Boolean) value).booleanValue());
break;
case DECIMAL:
assert value instanceof BigDecimal;
pw.print(value.toString());
break;
case DOUBLE:
assert value instanceof BigDecimal;
pw.print(Util.toScientificNotation((BigDecimal) value));
break;
case BIGINT:
assert value instanceof BigDecimal;
pw.print(((BigDecimal) value).longValue());
pw.print('L');
break;
case BINARY:
assert value instanceof ByteString;
pw.print("X'");
pw.print(((ByteString) value).toString(16));
pw.print("'");
break;
case NULL:
assert value == null;
pw.print("null");
break;
case SYMBOL:
assert value instanceof Enum;
pw.print("FLAG(");
pw.print(value);
pw.print(")");
break;
case DATE:
printDatetime(pw, new ZonelessDate(), value);
break;
case TIME:
printDatetime(pw, new ZonelessTime(), value);
break;
case TIMESTAMP:
printDatetime(pw, new ZonelessTimestamp(), value);
break;
case INTERVAL_DAY_TIME:
case INTERVAL_YEAR_MONTH:
if (value instanceof BigDecimal) {
pw.print(value.toString());
} else {
assert value == null;
pw.print("null");
}
break;
default:
assert valueMatchesType(value, typeName, true);
throw Util.needToImplement(typeName);
}
}
private static void printDatetime(
PrintWriter pw,
ZonelessDatetime datetime,
Comparable value) {
assert value instanceof Calendar;
datetime.setZonelessTime(
((Calendar) value).getTimeInMillis());
pw.print(datetime);
}
/**
* Converts a Jdbc string into a RexLiteral. This method accepts a string,
* as returned by the Jdbc method ResultSet.getString(), and restores the
* string into an equivalent RexLiteral. It allows one to use Jdbc strings
* as a common format for data.
*
* <p>If a null literal is provided, then a null pointer will be returned.
*
* @param type data type of literal to be read
* @param typeName type family of literal
* @param literal the (non-SQL encoded) string representation, as returned
* by the Jdbc call to return a column as a string
* @return a typed RexLiteral, or null
*/
public static RexLiteral fromJdbcString(
RelDataType type,
SqlTypeName typeName,
String literal) {
if (literal == null) {
return null;
}
switch (typeName) {
case CHAR:
Charset charset = type.getCharset();
SqlCollation collation = type.getCollation();
NlsString str =
new NlsString(
literal,
charset.name(),
collation);
return new RexLiteral(str, type, typeName);
case BOOLEAN:
boolean b = ConversionUtil.toBoolean(literal);
return new RexLiteral(b, type, typeName);
case DECIMAL:
case DOUBLE:
BigDecimal d = new BigDecimal(literal);
return new RexLiteral(d, type, typeName);
case BINARY:
byte[] bytes = ConversionUtil.toByteArrayFromString(literal, 16);
return new RexLiteral(new ByteString(bytes), type, typeName);
case NULL:
return new RexLiteral(null, type, typeName);
case INTERVAL_DAY_TIME:
long millis =
SqlParserUtil.intervalToMillis(
literal,
type.getIntervalQualifier());
return new RexLiteral(BigDecimal.valueOf(millis), type, typeName);
case INTERVAL_YEAR_MONTH:
long months =
SqlParserUtil.intervalToMonths(
literal,
type.getIntervalQualifier());
return new RexLiteral(BigDecimal.valueOf(months), type, typeName);
case DATE:
case TIME:
case TIMESTAMP:
String format = getCalendarFormat(typeName);
TimeZone tz = DateTimeUtil.GMT_ZONE;
Calendar cal = null;
if (typeName == SqlTypeName.DATE) {
cal =
DateTimeUtil.parseDateFormat(
literal,
format,
tz);
} else {
// Allow fractional seconds for times and timestamps
DateTimeUtil.PrecisionTime ts =
DateTimeUtil.parsePrecisionDateTimeLiteral(
literal,
format,
tz);
if (ts != null) {
cal = ts.getCalendar();
}
}
if (cal == null) {
throw Util.newInternal(
"fromJdbcString: invalid date/time value '"
+ literal + "'");
}
return new RexLiteral(cal, type, typeName);
case SYMBOL:
// Symbols are for internal use
default:
throw Util.newInternal("fromJdbcString: unsupported type");
}
}
private static String getCalendarFormat(SqlTypeName typeName) {
switch (typeName) {
case DATE:
return DateTimeUtil.DATE_FORMAT_STRING;
case TIME:
return DateTimeUtil.TIME_FORMAT_STRING;
case TIMESTAMP:
return DateTimeUtil.TIMESTAMP_FORMAT_STRING;
default:
throw Util.newInternal("getCalendarFormat: unknown type");
}
}
public SqlTypeName getTypeName() {
return typeName;
}
public RelDataType getType() {
return type;
}
@Override
public SqlKind getKind() {
return SqlKind.LITERAL;
}
/**
* Returns the value of this literal.
*/
public Comparable getValue() {
assert valueMatchesType(value, typeName, true) : value;
return value;
}
/**
* Returns the value of this literal, in the form that the calculator
* program builder wants it.
*/
public Object getValue2() {
if (value == null) {
return null;
}
switch (typeName) {
case BINARY:
return ((ByteBuffer) value).array();
case CHAR:
return ((NlsString) value).getValue();
case DECIMAL:
return ((BigDecimal) value).unscaledValue().longValue();
case DATE:
return (int) (((Calendar) value).getTimeInMillis()
/ DateTimeUtil.MILLIS_PER_DAY);
case TIME:
return (int) (((Calendar) value).getTimeInMillis()
% DateTimeUtil.MILLIS_PER_DAY);
case TIMESTAMP:
return ((Calendar) value).getTimeInMillis();
default:
return value;
}
}
/**
* Returns the value of this literal, in the form that the rex-to-lix
* translator wants it.
*/
public Object getValue3() {
switch (typeName) {
case DECIMAL:
assert value instanceof BigDecimal;
return value;
default:
return getValue2();
}
}
public static boolean booleanValue(RexNode node) {
return (Boolean) ((RexLiteral) node).value;
}
public boolean isAlwaysTrue() {
if (typeName != SqlTypeName.BOOLEAN) {
return false;
}
return booleanValue(this);
}
public boolean isAlwaysFalse() {
if (typeName != SqlTypeName.BOOLEAN) {
return false;
}
return !booleanValue(this);
}
public boolean equals(Object obj) {
return (obj instanceof RexLiteral)
&& equals(((RexLiteral) obj).value, value)
&& equals(((RexLiteral) obj).type, type);
}
public int hashCode() {
return Util.hashV(value, type);
}
public static int intValue(RexNode node) {
final Comparable value = findValue(node);
return ((Number) value).intValue();
}
public static String stringValue(RexNode node) {
final Comparable value = findValue(node);
return (value == null) ? null : ((NlsString) value).getValue();
}
private static Comparable findValue(RexNode node) {
if (node instanceof RexLiteral) {
return ((RexLiteral) node).value;
}
if (node instanceof RexCall) {
final RexCall call = (RexCall) node;
final SqlOperator operator = call.getOperator();
if (operator == SqlStdOperatorTable.CAST) {
return findValue(call.getOperands().get(0));
}
if (operator == SqlStdOperatorTable.UNARY_MINUS) {
final BigDecimal value =
(BigDecimal) findValue(call.getOperands().get(0));
return value.negate();
}
}
throw Util.newInternal("not a literal: " + node);
}
public static boolean isNullLiteral(RexNode node) {
return (node instanceof RexLiteral)
&& (((RexLiteral) node).value == null);
}
private static boolean equals(
Object o1,
Object o2) {
return (o1 == null) ? (o2 == null) : o1.equals(o2);
}
public <R> R accept(RexVisitor<R> visitor) {
return visitor.visitLiteral(this);
}
}
// End RexLiteral.java