org.apache.calcite.sql.SqlLiteral Maven / Gradle / Ivy
Show all versions of calcite-core 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.sql;
import org.apache.calcite.avatica.util.TimeUnitRange;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.sql.fun.SqlLiteralChainOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.parser.SqlParserUtil;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.util.SqlVisitor;
import org.apache.calcite.sql.validate.SqlMonotonicity;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.util.BitString;
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimestampString;
import org.apache.calcite.util.Util;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Calendar;
import java.util.Objects;
import static org.apache.calcite.util.Static.RESOURCE;
/**
* A SqlLiteral
is a constant. It is, appropriately, immutable.
*
* 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 #toSqlString(SqlDialect)} and
* {@link #toValue}.
*
* If you really need to access the value directly, you should switch on the
* value of the {@link #typeName} field, rather than making assumptions about
* the runtime type of the {@link #value}.
*
* The allowable types and combinations are:
*
*
* Allowable types for SqlLiteral
*
* TypeName
* Meaing
* 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}
*
*
* {@link SqlTypeName#TIME}
* Time, for example TIME '18:37:42.567'
* {@link Calendar}
*
*
* {@link SqlTypeName#TIMESTAMP}
* Timestamp, for example TIMESTAMP '1969-04-29
* 18:37:42.567'
* {@link Calendar}
*
*
* {@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}
*
*
* {@link SqlTypeName#BINARY}
* Binary constant, for example X'ABC'
, X'7F'
.
* Note that strings with an odd number of hexits will later become values of
* the BIT datatype, because they have an incomplete number of bytes. But here,
* they are all binary constants, because that's how they were written. These
* constants are always BINARY, never VARBINARY.
* {@link BitString}
*
*
* {@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
* symbol, such as the LEADING flag in a call to the function
* TRIM([LEADING|TRAILING|BOTH] chars FROM string)
.
* An {@link Enum}
*
*
* {@link SqlTypeName#INTERVAL_YEAR}
* .. {@link SqlTypeName#INTERVAL_SECOND}
* Interval, for example INTERVAL '1:34' HOUR
.
* {@link SqlIntervalLiteral.IntervalValue}.
*
*
*/
public class SqlLiteral extends SqlNode {
//~ Instance fields --------------------------------------------------------
/**
* The type with which this literal was declared. This type is very
* approximate: the literal may have a different type once validated. For
* example, all numeric literals have a type name of
* {@link SqlTypeName#DECIMAL}, but on validation may become
* {@link SqlTypeName#INTEGER}.
*/
private final SqlTypeName typeName;
/**
* The value of this literal. The type of the value must be appropriate for
* the typeName, as defined by the {@link #valueMatchesType} method.
*/
protected final Object value;
//~ Constructors -----------------------------------------------------------
/**
* Creates a SqlLiteral
.
*/
protected SqlLiteral(
Object value,
SqlTypeName typeName,
SqlParserPos pos) {
super(pos);
this.value = value;
this.typeName = typeName;
assert typeName != null;
assert valueMatchesType(value, typeName);
}
//~ Methods ----------------------------------------------------------------
/**
* @return value of {@link #typeName}
*/
public SqlTypeName getTypeName() {
return typeName;
}
/**
* @return whether value is appropriate for its type (we have rules about
* these things)
*/
public static boolean valueMatchesType(
Object value,
SqlTypeName typeName) {
switch (typeName) {
case BOOLEAN:
return (value == null) || (value instanceof Boolean);
case NULL:
return value == null;
case DECIMAL:
case DOUBLE:
return value instanceof BigDecimal;
case DATE:
return value instanceof DateString;
case TIME:
return value instanceof TimeString;
case TIMESTAMP:
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:
return value instanceof SqlIntervalLiteral.IntervalValue;
case BINARY:
return value instanceof BitString;
case CHAR:
return value instanceof NlsString;
case SYMBOL:
return (value instanceof Enum)
|| (value instanceof SqlSampleSpec);
case MULTISET:
return true;
case INTEGER: // not allowed -- use Decimal
case VARCHAR: // not allowed -- use Char
case VARBINARY: // not allowed -- use Binary
default:
throw Util.unexpected(typeName);
}
}
public SqlLiteral clone(SqlParserPos pos) {
return new SqlLiteral(value, typeName, pos);
}
public SqlKind getKind() {
return SqlKind.LITERAL;
}
/**
* Returns the value of this literal.
*
* Try not to use this method! There are so many different kinds of
* values, it's better to to let SqlLiteral do whatever it is you want to
* do.
*
* @see #booleanValue()
* @see #symbolValue(Class)
*/
public Object getValue() {
return value;
}
public T getValueAs(Class clazz) {
if (clazz.isInstance(value)) {
return clazz.cast(value);
}
switch (typeName) {
case CHAR:
if (clazz == String.class) {
return clazz.cast(((NlsString) value).getValue());
}
break;
case BINARY:
if (clazz == byte[].class) {
return clazz.cast(((BitString) value).getAsByteArray());
}
break;
case DECIMAL:
if (clazz == Long.class) {
return clazz.cast(((BigDecimal) value).unscaledValue().longValue());
}
// fall through
case BIGINT:
case INTEGER:
case SMALLINT:
case TINYINT:
case DOUBLE:
case REAL:
case FLOAT:
if (clazz == Long.class) {
return clazz.cast(((BigDecimal) value).longValue());
} else if (clazz == Integer.class) {
return clazz.cast(((BigDecimal) value).intValue());
} else if (clazz == Short.class) {
return clazz.cast(((BigDecimal) value).shortValue());
} else if (clazz == Byte.class) {
return clazz.cast(((BigDecimal) value).byteValue());
} else if (clazz == Double.class) {
return clazz.cast(((BigDecimal) value).doubleValue());
} else if (clazz == Float.class) {
return clazz.cast(((BigDecimal) value).floatValue());
}
break;
case DATE:
if (clazz == Calendar.class) {
return clazz.cast(((DateString) value).toCalendar());
}
break;
case TIME:
if (clazz == Calendar.class) {
return clazz.cast(((TimeString) value).toCalendar());
}
break;
case TIMESTAMP:
if (clazz == Calendar.class) {
return clazz.cast(((TimestampString) value).toCalendar());
}
break;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
final SqlIntervalLiteral.IntervalValue valMonth =
(SqlIntervalLiteral.IntervalValue) value;
if (clazz == Long.class) {
return clazz.cast(valMonth.getSign()
* SqlParserUtil.intervalToMonths(valMonth));
} else if (clazz == BigDecimal.class) {
return clazz.cast(BigDecimal.valueOf(getValueAs(Long.class)));
} else if (clazz == TimeUnitRange.class) {
return clazz.cast(valMonth.getIntervalQualifier().timeUnitRange);
}
break;
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:
final SqlIntervalLiteral.IntervalValue valTime =
(SqlIntervalLiteral.IntervalValue) value;
if (clazz == Long.class) {
return clazz.cast(valTime.getSign()
* SqlParserUtil.intervalToMillis(valTime));
} else if (clazz == BigDecimal.class) {
return clazz.cast(BigDecimal.valueOf(getValueAs(Long.class)));
} else if (clazz == TimeUnitRange.class) {
return clazz.cast(valTime.getIntervalQualifier().timeUnitRange);
}
break;
}
throw new AssertionError("cannot cast " + value + " as " + clazz);
}
/** Returns the value as a symbol. */
@Deprecated // to be removed before 2.0
public > E symbolValue_() {
//noinspection unchecked
return (E) value;
}
/** Returns the value as a symbol. */
public > E symbolValue(Class class_) {
return class_.cast(value);
}
/** Returns the value as a boolean. */
public boolean booleanValue() {
return (Boolean) value;
}
/**
* Extracts the {@link SqlSampleSpec} value from a symbol literal.
*
* @throws ClassCastException if the value is not a symbol literal
* @see #createSymbol(Enum, SqlParserPos)
*/
public static SqlSampleSpec sampleValue(SqlNode node) {
return (SqlSampleSpec) ((SqlLiteral) node).value;
}
/**
* Extracts the value from a literal.
*
* Cases:
*
* - If the node is a character literal, a chain of string
* literals, or a CAST of a character literal, returns the value as a
* {@link NlsString}.
*
*
- If the node is a numeric literal, or a negated numeric literal,
* returns the value as a {@link BigDecimal}.
*
*
- If the node is a {@link SqlIntervalQualifier},
* returns its {@link TimeUnitRange}.
*
*
- If the node is INTERVAL_DAY_TIME_ in {@link SqlTypeFamily},
* returns its sign multiplied by its millisecond equivalent value
*
*
- If the node is INTERVAL_YEAR_MONTH_ in {@link SqlTypeFamily},
* returns its sign multiplied by its months equivalent value
*
*
- Otherwise throws {@link IllegalArgumentException}.
*
*/
public static Comparable value(SqlNode node)
throws IllegalArgumentException {
if (node instanceof SqlLiteral) {
final SqlLiteral literal = (SqlLiteral) node;
if (literal.getTypeName() == SqlTypeName.SYMBOL) {
return (Enum) literal.value;
}
switch (literal.getTypeName().getFamily()) {
case CHARACTER:
return (NlsString) literal.value;
case NUMERIC:
return (BigDecimal) literal.value;
case INTERVAL_YEAR_MONTH:
final SqlIntervalLiteral.IntervalValue valMonth =
(SqlIntervalLiteral.IntervalValue) literal.value;
return valMonth.getSign() * SqlParserUtil.intervalToMonths(valMonth);
case INTERVAL_DAY_TIME:
final SqlIntervalLiteral.IntervalValue valTime =
(SqlIntervalLiteral.IntervalValue) literal.value;
return valTime.getSign() * SqlParserUtil.intervalToMillis(valTime);
}
}
if (SqlUtil.isLiteralChain(node)) {
assert node instanceof SqlCall;
final SqlLiteral literal =
SqlLiteralChainOperator.concatenateOperands((SqlCall) node);
assert SqlTypeUtil.inCharFamily(literal.getTypeName());
return (NlsString) literal.value;
}
if (node instanceof SqlIntervalQualifier) {
SqlIntervalQualifier qualifier = (SqlIntervalQualifier) node;
return qualifier.timeUnitRange;
}
switch (node.getKind()) {
case CAST:
assert node instanceof SqlCall;
return value(((SqlCall) node).operand(0));
case MINUS_PREFIX:
assert node instanceof SqlCall;
Comparable o = value(((SqlCall) node).operand(0));
if (o instanceof BigDecimal) {
BigDecimal bigDecimal = (BigDecimal) o;
return bigDecimal.negate();
}
// fall through
default:
throw new IllegalArgumentException("not a literal: " + node);
}
}
/**
* Extracts the string value from a string literal, a chain of string
* literals, or a CAST of a string literal.
*
* @deprecated Use {@link #value(SqlNode)}
*/
@Deprecated // to be removed before 2.0
public static String stringValue(SqlNode node) {
if (node instanceof SqlLiteral) {
SqlLiteral literal = (SqlLiteral) node;
assert SqlTypeUtil.inCharFamily(literal.getTypeName());
return literal.value.toString();
} else if (SqlUtil.isLiteralChain(node)) {
final SqlLiteral literal =
SqlLiteralChainOperator.concatenateOperands((SqlCall) node);
assert SqlTypeUtil.inCharFamily(literal.getTypeName());
return literal.value.toString();
} else if (node instanceof SqlCall
&& ((SqlCall) node).getOperator() == SqlStdOperatorTable.CAST) {
//noinspection deprecation
return stringValue(((SqlCall) node).operand(0));
} else {
throw new AssertionError("invalid string literal: " + node);
}
}
/**
* Converts a chained string literals into regular literals; returns regular
* literals unchanged.
*/
public static SqlLiteral unchain(SqlNode node) {
if (node instanceof SqlLiteral) {
return (SqlLiteral) node;
} else if (SqlUtil.isLiteralChain(node)) {
return SqlLiteralChainOperator.concatenateOperands((SqlCall) node);
} else if (node instanceof SqlIntervalQualifier) {
final SqlIntervalQualifier q = (SqlIntervalQualifier) node;
return new SqlLiteral(
new SqlIntervalLiteral.IntervalValue(q, 1, q.toString()),
q.typeName(), q.pos);
} else {
throw new AssertionError("invalid literal: " + node);
}
}
/**
* For calc program builder - value may be different than {@link #unparse}
* Typical values:
*
*
* - Hello, world!
* - 12.34
* - {null}
* - 1969-04-29
*
*
* @return string representation of the value
*/
public String toValue() {
if (value == null) {
return null;
}
switch (typeName) {
case CHAR:
// We want 'It''s superman!', not _ISO-8859-1'It''s superman!'
return ((NlsString) value).getValue();
default:
return value.toString();
}
}
public void validate(SqlValidator validator, SqlValidatorScope scope) {
validator.validateLiteral(this);
}
public R accept(SqlVisitor visitor) {
return visitor.visit(this);
}
public boolean equalsDeep(SqlNode node, Litmus litmus) {
if (!(node instanceof SqlLiteral)) {
return litmus.fail("{} != {}", this, node);
}
SqlLiteral that = (SqlLiteral) node;
if (!this.equals(that)) {
return litmus.fail("{} != {}", this, node);
}
return litmus.succeed();
}
public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) {
return SqlMonotonicity.CONSTANT;
}
/**
* Creates a NULL literal.
*
* There's no singleton constant for a NULL literal. Instead, nulls must
* be instantiated via createNull(), because different instances have
* different context-dependent types.
*/
public static SqlLiteral createNull(SqlParserPos pos) {
return new SqlLiteral(null, SqlTypeName.NULL, pos);
}
/**
* Creates a boolean literal.
*/
public static SqlLiteral createBoolean(
boolean b,
SqlParserPos pos) {
return b ? new SqlLiteral(Boolean.TRUE, SqlTypeName.BOOLEAN, pos)
: new SqlLiteral(Boolean.FALSE, SqlTypeName.BOOLEAN, pos);
}
public static SqlLiteral createUnknown(SqlParserPos pos) {
return new SqlLiteral(null, SqlTypeName.BOOLEAN, pos);
}
/**
* Creates a literal which represents a parser symbol, for example the
* TRAILING
keyword in the call Trim(TRAILING 'x' FROM
* 'Hello world!')
.
*
* @see #symbolValue(Class)
*/
public static SqlLiteral createSymbol(Enum> o, SqlParserPos pos) {
return new SqlLiteral(o, SqlTypeName.SYMBOL, pos);
}
/**
* Creates a literal which represents a sample specification.
*/
public static SqlLiteral createSample(
SqlSampleSpec sampleSpec,
SqlParserPos pos) {
return new SqlLiteral(sampleSpec, SqlTypeName.SYMBOL, pos);
}
public boolean equals(Object obj) {
if (!(obj instanceof SqlLiteral)) {
return false;
}
SqlLiteral that = (SqlLiteral) obj;
return Objects.equals(value, that.value);
}
public int hashCode() {
return (value == null) ? 0 : value.hashCode();
}
/**
* Returns the integer value of this literal.
*
* @param exact Whether the value has to be exact. If true, and the literal
* is a fraction (e.g. 3.14), throws. If false, discards the
* fractional part of the value.
* @return Integer value of this literal
*/
public int intValue(boolean exact) {
switch (typeName) {
case DECIMAL:
case DOUBLE:
BigDecimal bd = (BigDecimal) value;
if (exact) {
try {
return bd.intValueExact();
} catch (ArithmeticException e) {
throw SqlUtil.newContextException(getParserPosition(),
RESOURCE.numberLiteralOutOfRange(bd.toString()));
}
} else {
return bd.intValue();
}
default:
throw Util.unexpected(typeName);
}
}
/**
* Returns the long value of this literal.
*
* @param exact Whether the value has to be exact. If true, and the literal
* is a fraction (e.g. 3.14), throws. If false, discards the
* fractional part of the value.
* @return Long value of this literal
*/
public long longValue(boolean exact) {
switch (typeName) {
case DECIMAL:
case DOUBLE:
BigDecimal bd = (BigDecimal) value;
if (exact) {
try {
return bd.longValueExact();
} catch (ArithmeticException e) {
throw SqlUtil.newContextException(getParserPosition(),
RESOURCE.numberLiteralOutOfRange(bd.toString()));
}
} else {
return bd.longValue();
}
default:
throw Util.unexpected(typeName);
}
}
/**
* Returns sign of value.
*
* @return -1, 0 or 1
*/
@Deprecated // to be removed before 2.0
public int signum() {
return bigDecimalValue().compareTo(
BigDecimal.ZERO);
}
/**
* Returns a numeric literal's value as a {@link BigDecimal}.
*/
public BigDecimal bigDecimalValue() {
switch (typeName) {
case DECIMAL:
case DOUBLE:
return (BigDecimal) value;
default:
throw Util.unexpected(typeName);
}
}
@Deprecated // to be removed before 2.0
public String getStringValue() {
return ((NlsString) value).getValue();
}
public void unparse(
SqlWriter writer,
int leftPrec,
int rightPrec) {
switch (typeName) {
case BOOLEAN:
writer.keyword(
value == null ? "UNKNOWN" : (Boolean) value ? "TRUE" : "FALSE");
break;
case NULL:
writer.keyword("NULL");
break;
case CHAR:
case DECIMAL:
case DOUBLE:
case BINARY:
// should be handled in subtype
throw Util.unexpected(typeName);
case SYMBOL:
if (value instanceof Enum) {
Enum enumVal = (Enum) value;
writer.keyword(enumVal.toString());
} else {
writer.keyword(String.valueOf(value));
}
break;
default:
writer.literal(value.toString());
}
}
public RelDataType createSqlType(RelDataTypeFactory typeFactory) {
BitString bitString;
switch (typeName) {
case NULL:
case BOOLEAN:
RelDataType ret = typeFactory.createSqlType(typeName);
ret = typeFactory.createTypeWithNullability(ret, null == value);
return ret;
case BINARY:
bitString = (BitString) value;
int bitCount = bitString.getBitCount();
return typeFactory.createSqlType(SqlTypeName.BINARY, bitCount / 8);
case CHAR:
NlsString string = (NlsString) value;
Charset charset = string.getCharset();
if (null == charset) {
charset = typeFactory.getDefaultCharset();
}
SqlCollation collation = string.getCollation();
if (null == collation) {
collation = SqlCollation.COERCIBLE;
}
RelDataType type =
typeFactory.createSqlType(
SqlTypeName.CHAR,
string.getValue().length());
type =
typeFactory.createTypeWithCharsetAndCollation(
type,
charset,
collation);
return type;
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:
SqlIntervalLiteral.IntervalValue intervalValue =
(SqlIntervalLiteral.IntervalValue) value;
return typeFactory.createSqlIntervalType(
intervalValue.getIntervalQualifier());
case SYMBOL:
return typeFactory.createSqlType(SqlTypeName.SYMBOL);
case INTEGER: // handled in derived class
case TIME: // handled in derived class
case VARCHAR: // should never happen
case VARBINARY: // should never happen
default:
throw Util.needToImplement(toString() + ", operand=" + value);
}
}
@Deprecated // to be removed before 2.0
public static SqlDateLiteral createDate(
Calendar calendar,
SqlParserPos pos) {
return createDate(DateString.fromCalendarFields(calendar), pos);
}
public static SqlDateLiteral createDate(
DateString date,
SqlParserPos pos) {
return new SqlDateLiteral(date, pos);
}
@Deprecated // to be removed before 2.0
public static SqlTimestampLiteral createTimestamp(
Calendar calendar,
int precision,
SqlParserPos pos) {
return createTimestamp(TimestampString.fromCalendarFields(calendar),
precision, pos);
}
public static SqlTimestampLiteral createTimestamp(
TimestampString ts,
int precision,
SqlParserPos pos) {
return new SqlTimestampLiteral(ts, precision, false, pos);
}
@Deprecated // to be removed before 2.0
public static SqlTimeLiteral createTime(
Calendar calendar,
int precision,
SqlParserPos pos) {
return createTime(TimeString.fromCalendarFields(calendar), precision, pos);
}
public static SqlTimeLiteral createTime(
TimeString t,
int precision,
SqlParserPos pos) {
return new SqlTimeLiteral(t, precision, false, pos);
}
/**
* Creates an interval literal.
*
* @param intervalStr input string of '1:23:04'
* @param intervalQualifier describes the interval type and precision
* @param pos Parser position
*/
public static SqlIntervalLiteral createInterval(
int sign,
String intervalStr,
SqlIntervalQualifier intervalQualifier,
SqlParserPos pos) {
return new SqlIntervalLiteral(sign, intervalStr, intervalQualifier,
intervalQualifier.typeName(), pos);
}
public static SqlNumericLiteral createNegative(
SqlNumericLiteral num,
SqlParserPos pos) {
return new SqlNumericLiteral(
((BigDecimal) num.getValue()).negate(),
num.getPrec(),
num.getScale(),
num.isExact(),
pos);
}
public static SqlNumericLiteral createExactNumeric(
String s,
SqlParserPos pos) {
BigDecimal value;
int prec;
int scale;
int i = s.indexOf('.');
if ((i >= 0) && ((s.length() - 1) != i)) {
value = SqlParserUtil.parseDecimal(s);
scale = s.length() - i - 1;
assert scale == value.scale() : s;
prec = s.length() - 1;
} else if ((i >= 0) && ((s.length() - 1) == i)) {
value = SqlParserUtil.parseInteger(s.substring(0, i));
scale = 0;
prec = s.length() - 1;
} else {
value = SqlParserUtil.parseInteger(s);
scale = 0;
prec = s.length();
}
return new SqlNumericLiteral(
value,
prec,
scale,
true,
pos);
}
public static SqlNumericLiteral createApproxNumeric(
String s,
SqlParserPos pos) {
BigDecimal value = SqlParserUtil.parseDecimal(s);
return new SqlNumericLiteral(value, null, null, false, pos);
}
/**
* Creates a literal like X'ABAB'. Although it matters when we derive a type
* for this beastie, we don't care at this point whether the number of
* hexits is odd or even.
*/
public static SqlBinaryStringLiteral createBinaryString(
String s,
SqlParserPos pos) {
BitString bits;
try {
bits = BitString.createFromHexString(s);
} catch (NumberFormatException e) {
throw SqlUtil.newContextException(pos,
RESOURCE.binaryLiteralInvalid());
}
return new SqlBinaryStringLiteral(bits, pos);
}
/**
* Creates a literal like X'ABAB' from an array of bytes.
*
* @param bytes Contents of binary literal
* @param pos Parser position
* @return Binary string literal
*/
public static SqlBinaryStringLiteral createBinaryString(
byte[] bytes,
SqlParserPos pos) {
BitString bits;
try {
bits = BitString.createFromBytes(bytes);
} catch (NumberFormatException e) {
throw SqlUtil.newContextException(pos, RESOURCE.binaryLiteralInvalid());
}
return new SqlBinaryStringLiteral(bits, pos);
}
/**
* Creates a string literal in the system character set.
*
* @param s a string (without the sql single quotes)
* @param pos Parser position
*/
public static SqlCharStringLiteral createCharString(
String s,
SqlParserPos pos) {
// UnsupportedCharsetException not possible
return createCharString(s, null, pos);
}
/**
* Creates a string literal, with optional character-set.
*
* @param s a string (without the sql single quotes)
* @param charSet character set name, null means take system default
* @param pos Parser position
* @return A string literal
* @throws UnsupportedCharsetException if charSet is not null but there is
* no character set with that name in this
* environment
*/
public static SqlCharStringLiteral createCharString(
String s,
String charSet,
SqlParserPos pos) {
NlsString slit = new NlsString(s, charSet, null);
return new SqlCharStringLiteral(slit, pos);
}
/**
* Transforms this literal (which must be of type character) into a new one
* in which 4-digit Unicode escape sequences have been replaced with the
* corresponding Unicode characters.
*
* @param unicodeEscapeChar escape character (e.g. backslash) for Unicode
* numeric sequences; 0 implies no transformation
* @return transformed literal
*/
public SqlLiteral unescapeUnicode(char unicodeEscapeChar) {
if (unicodeEscapeChar == 0) {
return this;
}
assert SqlTypeUtil.inCharFamily(getTypeName());
NlsString ns = (NlsString) value;
String s = ns.getValue();
StringBuilder sb = new StringBuilder();
int n = s.length();
for (int i = 0; i < n; ++i) {
char c = s.charAt(i);
if (c == unicodeEscapeChar) {
if (n > (i + 1)) {
if (s.charAt(i + 1) == unicodeEscapeChar) {
sb.append(unicodeEscapeChar);
++i;
continue;
}
}
if ((i + 5) > n) {
throw SqlUtil.newContextException(getParserPosition(),
RESOURCE.unicodeEscapeMalformed(i));
}
final String u = s.substring(i + 1, i + 5);
final int v;
try {
v = Integer.parseInt(u, 16);
} catch (NumberFormatException ex) {
throw SqlUtil.newContextException(getParserPosition(),
RESOURCE.unicodeEscapeMalformed(i));
}
sb.append((char) (v & 0xFFFF));
// skip hexits
i += 4;
} else {
sb.append(c);
}
}
ns = new NlsString(
sb.toString(),
ns.getCharsetName(),
ns.getCollation());
return new SqlCharStringLiteral(ns, getParserPosition());
}
//~ Inner Interfaces -------------------------------------------------------
/**
* A value must implement this interface if it is to be embedded as a
* SqlLiteral of type SYMBOL. If the class is an {@link Enum} it trivially
* implements this interface.
*
*
The {@link #toString()} method should return how the symbol should be
* unparsed, which is sometimes not the same as the enumerated value's name
* (e.g. "UNBOUNDED PRECEDING" versus "UnboundedPreceeding").
*/
@Deprecated // to be removed before 2.0
public interface SqlSymbol {
String name();
int ordinal();
}
}
// End SqlLiteral.java