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

com.hazelcast.org.apache.calcite.sql.SqlLiteral Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * 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;

import com.hazelcast.org.apache.calcite.avatica.util.TimeUnitRange;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeFactory;
import com.hazelcast.org.apache.calcite.sql.fun.SqlLiteralChainOperator;
import com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable;
import com.hazelcast.org.apache.calcite.sql.parser.SqlParserPos;
import com.hazelcast.org.apache.calcite.sql.parser.SqlParserUtil;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeFamily;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeName;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeUtil;
import com.hazelcast.org.apache.calcite.sql.util.SqlVisitor;
import com.hazelcast.org.apache.calcite.sql.validate.SqlMonotonicity;
import com.hazelcast.org.apache.calcite.sql.validate.SqlValidator;
import com.hazelcast.org.apache.calcite.sql.validate.SqlValidatorScope;
import com.hazelcast.org.apache.calcite.util.BitString;
import com.hazelcast.org.apache.calcite.util.DateString;
import com.hazelcast.org.apache.calcite.util.Litmus;
import com.hazelcast.org.apache.calcite.util.NlsString;
import com.hazelcast.org.apache.calcite.util.TimeString;
import com.hazelcast.org.apache.calcite.util.TimestampString;
import com.hazelcast.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 com.hazelcast.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 com.hazelcast.com.inations are: * *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Allowable types for SqlLiteral
TypeNameMeaingValue 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. * @throws IllegalArgumentException if {@code node} is not a string literal * and cannot be unchained. */ 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 IllegalArgumentException("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().com.hazelcast.com.areTo( 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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy