blob: 20c1a4883c3141deecbd523af781a66cec40ed4d [file] [log] [blame]
/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import java.math.BigDecimal;
import java.util.Locale;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.CharArrayWriter;
import org.hsqldb.lib.HsqlByteArrayOutputStream;
import org.hsqldb.lib.OrderedIntHashSet;
import org.hsqldb.lib.java.JavaSystem;
import org.hsqldb.store.BitMap;
import org.hsqldb.store.ValuePool;
import org.hsqldb.types.BinaryData;
import org.hsqldb.types.BinaryType;
import org.hsqldb.types.BitType;
import org.hsqldb.types.CharacterType;
import org.hsqldb.types.DTIType;
import org.hsqldb.types.DateTimeType;
import org.hsqldb.types.IntervalMonthData;
import org.hsqldb.types.IntervalSecondData;
import org.hsqldb.types.IntervalType;
import org.hsqldb.types.NumberType;
import org.hsqldb.types.TimeData;
import org.hsqldb.types.TimestampData;
import org.hsqldb.types.Type;
import org.hsqldb.types.Types;
/**
* Scans for SQL tokens.
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class Scanner {
/*
<delimiter token> ::=
<character string literal>
| <date string>
| <time string>
| <timestamp string>
| <interval string>
| <delimited identifier>
| <SQL special character>
| <not equals operator>
| <greater than or equals operator>
| <less than or equals operator>
| <concatenation operator>
| <right arrow>
| <left bracket trigraph>
| <right bracket trigraph>
| <double colon>
| <double period>
*/
//J-
final static char[] specials = new char[] {
'"',
'%',
'&',
'\'',
'(',
')',
'*',
'+',
',',
'-',
'.',
'/',
'\\',
':',
';',
'<',
'=',
'>',
'?',
'[',
']',
'^',
'_',
'|',
'{',
'}'
};
final static String[] multi = new String[] {
"??(",
"??)",
"<>",
">=",
"<=",
"||",
"->",
"::",
"..",
"--",
"/*",
"*/",
};
final static char[] whitespace = {
// SQL extras
0x9,
0xA,
0xB,
0xC,
0xD,
0x20,
0x85,
// U Zs
0x0020,
0x00A0,
0x1680,
0x180E,
0x2000,
0x2001,
0x2002,
0x2003,
0x2004,
0x2005,
0x2006,
0x2007,
0x2008,
0x2009,
0x200A,
0x202F,
0x205F,
0x3000,
// U Zl
0x2028,
// U Zp
0x2029,
};
//J+
final static OrderedIntHashSet whiteSpaceSet = new OrderedIntHashSet(32);
static {
for (int i = 0; i < whitespace.length; i++) {
whiteSpaceSet.add(whitespace[i]);
}
}
// single token types
String sqlString;
int currentPosition;
int tokenPosition;
int limit;
Token token = new Token();
boolean nullAndBooleanAsValue;
//
private boolean hasNonSpaceSeparator;
private int eolPosition;
private int lineNumber;
private int eolCode;
//
private static final int maxPooledStringLength =
ValuePool.getMaxStringLength();
//
char[] charBuffer = new char[256];
CharArrayWriter charWriter = new CharArrayWriter(charBuffer);
//
byte[] byteBuffer = new byte[256];
HsqlByteArrayOutputStream byteOutputStream =
new HsqlByteArrayOutputStream(byteBuffer);
public Scanner() {}
Scanner(String sql) {
reset(sql);
}
public void reset(String sql) {
sqlString = sql;
currentPosition = 0;
tokenPosition = 0;
limit = sqlString.length();
hasNonSpaceSeparator = false;
eolPosition = -1;
lineNumber = 1;
token.reset();
token.tokenType = Tokens.X_STARTPARSE;
}
void resetState() {
tokenPosition = currentPosition;
token.reset();
}
public void setNullAndBooleanAsValue() {
nullAndBooleanAsValue = true;
}
public void scanNext() {
if (currentPosition == limit) {
resetState();
token.tokenType = Tokens.X_ENDPARSE;
return;
}
if (scanSeparator()) {
// token.isDelimiter = true;
}
if (currentPosition == limit) {
resetState();
token.tokenType = Tokens.X_ENDPARSE;
return;
}
boolean needsDelimiter = !token.isDelimiter;
scanToken();
if (needsDelimiter && !token.isDelimiter) {
// token.tokenType = Token.X_UNKNOWN_TOKEN;
}
if (token.isMalformed) {
token.fullString = getPart(tokenPosition, currentPosition);
}
}
public void scanEnd() {
if (currentPosition == limit) {
resetState();
token.tokenType = Tokens.X_ENDPARSE;
}
}
public Token getToken() {
return token;
}
public String getString() {
return token.tokenString;
}
public int getTokenType() {
return token.tokenType;
}
public Object getValue() {
return token.tokenValue;
}
public Type getDataType() {
return token.dataType;
}
public int getLineNumber() {
return lineNumber;
}
int getTokenPosition() {
return tokenPosition;
}
int getPosition() {
return tokenPosition;
}
void position(int position) {
currentPosition = tokenPosition = position;
}
String getPart(int start, int end) {
return sqlString.substring(start, end);
}
private int charAt(int i) {
if (i >= limit) {
return -1;
}
return sqlString.charAt(i);
}
void scanBinaryString() {
byteOutputStream.reset(byteBuffer);
while (true) {
scanBinaryStringPart();
if (token.isMalformed) {
return;
}
if (scanSeparator() && charAt(currentPosition) == '\'') {
continue;
}
break;
}
token.tokenValue = new BinaryData(byteOutputStream.toByteArray(),
false);
byteOutputStream.reset(byteBuffer);
}
/**
* returns hex value of a hex character, or 16 if not a hex character
*/
static int getHexValue(int c) {
if (c >= '0' && c <= '9') {
c -= '0';
} else if (c > 'z') {
c = 16;
} else if (c >= 'a') {
c -= ('a' - 10);
} else if (c > 'Z') {
c = 16;
} else if (c >= 'A') {
c -= ('A' - 10);
} else {
c = -1;
}
return c;
}
public void scanBinaryStringWithQuote() {
resetState();
scanSeparator();
if (charAt(currentPosition) != '\'') {
token.tokenType = Tokens.X_MALFORMED_BINARY_STRING;
token.isMalformed = true;
return;
}
scanBinaryString();
}
void scanBinaryStringPart() {
boolean complete = false;
boolean hi = true;
byte b = 0;
currentPosition++;
for (; currentPosition < limit; currentPosition++) {
int c = sqlString.charAt(currentPosition);
if (c == ' ') {
continue;
}
if (c == '\'') {
complete = true;
currentPosition++;
break;
}
c = getHexValue(c);
if (c == -1) {
// bad character
token.tokenType = Tokens.X_MALFORMED_BINARY_STRING;
token.isMalformed = true;
return;
}
if (hi) {
b = (byte) (c << 4);
hi = false;
} else {
b += (byte) c;
byteOutputStream.writeByte(b);
hi = true;
}
}
if (!hi) {
// odd nibbles
token.tokenType = Tokens.X_MALFORMED_BINARY_STRING;
token.isMalformed = true;
return;
}
if (!complete) {
// no end quote
token.tokenType = Tokens.X_MALFORMED_BINARY_STRING;
token.isMalformed = true;
return;
}
}
void scanBitString() {
BitMap map = new BitMap(32);
while (true) {
scanBitStringPart(map);
if (token.isMalformed) {
return;
}
if (scanSeparator() && charAt(currentPosition) == '\'') {
continue;
}
break;
}
token.tokenValue = BinaryData.getBitData(map.getBytes(), map.size());
}
public void scanBitStringWithQuote() {
resetState();
scanSeparator();
if (charAt(currentPosition) != '\'') {
token.tokenType = Tokens.X_MALFORMED_BIT_STRING;
token.isMalformed = true;
return;
}
scanBitString();
}
void scanBitStringPart(BitMap map) {
boolean complete = false;
int bitIndex = map.size();
currentPosition++;
for (; currentPosition < limit; currentPosition++) {
char c = sqlString.charAt(currentPosition);
if (c == ' ') {
continue;
}
if (c == '\'') {
complete = true;
currentPosition++;
break;
}
if (c == '0') {
bitIndex++;
} else if (c == '1') {
map.set(bitIndex);
bitIndex++;
} else {
token.tokenType = Tokens.X_MALFORMED_BIT_STRING;
token.isMalformed = true;
return;
}
}
if (!complete) {
token.tokenType = Tokens.X_MALFORMED_BIT_STRING;
token.isMalformed = true;
return;
}
map.setSize(bitIndex);
}
void convertUnicodeString(int escape) {
charWriter.reset(charBuffer);
int position = 0;
for (;;) {
int nextIndex = token.tokenString.indexOf(escape, position);
if (nextIndex < 0) {
nextIndex = token.tokenString.length();
}
charWriter.write(token.tokenString, position,
nextIndex - position);
if (nextIndex == token.tokenString.length()) {
break;
}
nextIndex++;
if (nextIndex == token.tokenString.length()) {
token.tokenType = Tokens.X_MALFORMED_UNICODE_STRING;
token.isMalformed = true;
return;
}
if (token.tokenString.charAt(nextIndex) == escape) {
charWriter.write(escape);
nextIndex++;
position = nextIndex;
continue;
}
if (nextIndex > token.tokenString.length() - 4) {
token.tokenType = Tokens.X_MALFORMED_UNICODE_STRING;
token.isMalformed = true;
return;
}
int hexCount = 4;
int hexIndex = 0;
int hexValue = 0;
if (token.tokenString.charAt(nextIndex) == '+') {
nextIndex++;
if (nextIndex > token.tokenString.length() - 6) {
token.tokenType = Tokens.X_MALFORMED_UNICODE_STRING;
token.isMalformed = true;
return;
}
hexIndex = 2;
hexCount = 8;
}
for (; hexIndex < hexCount; hexIndex++) {
int character = token.tokenString.charAt(position++);
character = getHexValue(character);
if (character == -1) {
token.tokenType = Tokens.X_MALFORMED_UNICODE_STRING;
token.isMalformed = true;
return;
}
hexValue |= character << ((hexCount - hexIndex - 1) * 4);
}
if (hexCount == 8) {
charWriter.write(hexValue >>> 16);
}
charWriter.write(hexValue & (hexValue & 0xffff));
token.tokenValue = charWriter.toString();
}
}
/**
* Only for identifiers that are part of known token sequences
*/
public boolean scanSpecialIdentifier(String identifier) {
int length = identifier.length();
if (limit - currentPosition < length) {
return false;
}
for (int i = 0; i < length; i++) {
int character = identifier.charAt(i);
if (character == sqlString.charAt(currentPosition + i)) {
continue;
}
if (character
== Character.toUpperCase(sqlString.charAt(currentPosition
+ i))) {
continue;
}
return false;
}
currentPosition += length;
return true;
}
private int scanEscapeDefinition() {
int c = charAt(currentPosition);
if (c == '\'') {
currentPosition++;
if (!scanWhitespace()) {
c = charAt(currentPosition);
if (getHexValue(c) == -1) {
if (c != '+' && c != '\'' && c != '\"') {
int escape = c;
currentPosition++;
c = charAt(currentPosition);
if (c == '\'') {
currentPosition++;
return escape;
}
}
}
}
}
return -1;
}
private void scanUnicodeString() {
int escape = '\\';
scanCharacterString();
scanSeparator();
int c = charAt(currentPosition);
if (c == 'u' || c == 'U') {
if (scanSpecialIdentifier(Tokens.T_UESCAPE)) {
scanSeparator();
escape = scanEscapeDefinition();
if (escape == -1) {
token.tokenType = Tokens.X_MALFORMED_UNICODE_ESCAPE;
token.isMalformed = true;
return;
}
}
}
convertUnicodeString(escape);
}
private boolean scanUnicodeIdentifier() {
int escape = '\\';
scanStringPart('"');
if (token.isMalformed) {
return false;
}
token.tokenString = charWriter.toString();
int c = charAt(currentPosition);
if (c == 'u' || c == 'U') {
if (scanSpecialIdentifier(Tokens.T_UESCAPE)) {
scanSeparator();
escape = scanEscapeDefinition();
if (escape == -1) {
token.tokenType = Tokens.X_MALFORMED_UNICODE_ESCAPE;
token.isMalformed = true;
return false;
}
}
}
convertUnicodeString(escape);
return !token.isMalformed;
}
boolean shiftPrefixes() {
if (token.namePrePrePrefix != null) {
return false;
}
token.namePrePrePrefix = token.namePrePrefix;
token.isDelimitedPrePrePrefix = token.isDelimitedPrePrefix;
token.namePrePrefix = token.namePrefix;
token.isDelimitedPrePrefix = token.isDelimitedPrefix;
token.namePrefix = token.tokenString;
token.isDelimitedPrefix = (token.tokenType
== Tokens.X_DELIMITED_IDENTIFIER);
return true;
}
private void scanIdentifierChain() {
int c = charAt(currentPosition);
switch (c) {
case '"' :
charWriter.reset(charBuffer);
scanStringPart('"');
if (token.isMalformed) {
return;
}
token.tokenType = Tokens.X_DELIMITED_IDENTIFIER;
token.tokenString = charWriter.toString();
token.isDelimiter = true;
break;
case 'u' :
case 'U' :
if (charAt(currentPosition + 1) == '&') {
if (charAt(currentPosition + 1) == '"') {
currentPosition += 3;
boolean result = scanUnicodeIdentifier();
if (!result) {
return;
}
token.tokenType = Tokens.X_DELIMITED_IDENTIFIER;
token.isDelimiter = false;
break;
}
}
// fall through
default :
boolean result = scanUndelimitedIdentifier();
if (!result) {
return;
}
token.tokenType = Tokens.X_IDENTIFIER;
token.isDelimiter = false;
}
c = charAt(currentPosition);
if (c == '.') {
currentPosition++;
c = charAt(currentPosition);
if (c == '*') {
currentPosition++;
shiftPrefixes();
token.tokenString = Tokens.T_ASTERISK;
token.tokenType = Tokens.ASTERISK;
} else {
shiftPrefixes();
scanIdentifierChain();
}
}
}
public boolean scanUndelimitedIdentifier() {
if (currentPosition == limit) {
return false;
}
char start = sqlString.charAt(currentPosition);
if (!Character.isLetter(start)) {
token.tokenString = Character.toString(start);
token.tokenType = Tokens.X_UNKNOWN_TOKEN;
token.isMalformed = true;
return false;
}
int i = currentPosition + 1;
for (; i < limit; i++) {
char c = sqlString.charAt(i);
if (c == '_' || Character.isLetterOrDigit(c)) {
continue;
}
break;
}
token.tokenString = sqlString.substring(currentPosition,
i).toUpperCase(Locale.ENGLISH);
currentPosition = i;
if (nullAndBooleanAsValue) {
int tokenLength = currentPosition - tokenPosition;
if (tokenLength == 4 || tokenLength == 5) {
switch (start) {
case 'T' :
case 't' :
if (Tokens.T_TRUE.equals(token.tokenString)) {
token.tokenString = Tokens.T_TRUE;
token.tokenType = Tokens.X_VALUE;
token.tokenValue = Boolean.TRUE;
token.dataType = Type.SQL_BOOLEAN;
return false;
}
break;
case 'F' :
case 'f' :
if (Tokens.T_FALSE.equals(token.tokenString)) {
token.tokenString = Tokens.T_FALSE;
token.tokenType = Tokens.X_VALUE;
token.tokenValue = Boolean.FALSE;
token.dataType = Type.SQL_BOOLEAN;
return false;
}
break;
case 'N' :
case 'n' :
if (Tokens.T_NULL.equals(token.tokenString)) {
token.tokenString = Tokens.T_NULL;
token.tokenType = Tokens.X_VALUE;
token.tokenValue = null;
return false;
}
break;
}
}
}
return true;
}
void scanNumber() {
int c;
boolean hasDigit = false;
boolean hasPoint = false;
int exponentIndex = -1;
token.tokenType = Tokens.X_VALUE;
token.dataType = Type.SQL_INTEGER;
int tokenStart = currentPosition;
for (; currentPosition < limit; currentPosition++) {
boolean end = false;
c = charAt(currentPosition);
switch (c) {
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
hasDigit = true;
break;
case '.' :
token.dataType = Type.SQL_NUMERIC;
if (hasPoint || exponentIndex != -1) {
token.tokenString = sqlString.substring(tokenStart,
currentPosition + 1);
token.tokenType = Tokens.X_MALFORMED_NUMERIC;
token.isMalformed = true;
return;
}
hasPoint = true;
break;
case 'E' :
case 'e' :
token.dataType = Type.SQL_DOUBLE;
if (exponentIndex != -1 || !hasDigit) {
token.tokenString = sqlString.substring(tokenStart,
currentPosition + 1);
token.tokenType = Tokens.X_MALFORMED_NUMERIC;
token.isMalformed = true;
return;
}
hasPoint = true;
exponentIndex = currentPosition;
break;
case '-' :
case '+' :
if (exponentIndex != currentPosition - 1) {
end = true;
}
break;
case 'K' :
case 'k' :
case 'M' :
case 'm' :
case 'G' :
case 'g' :
case 'T' :
case 't' :
case 'P' :
case 'p' :
if (!hasDigit || hasPoint) {
token.tokenType = Tokens.X_MALFORMED_NUMERIC;
token.isMalformed = true;
return;
}
String s = Character.toString((char) c).toUpperCase(
Locale.ENGLISH);
token.lobMultiplierType = Tokens.getNonKeywordID(s,
Tokens.X_MALFORMED_NUMERIC);
if (token.lobMultiplierType
== Tokens.X_MALFORMED_NUMERIC) {
token.tokenType = Tokens.X_MALFORMED_NUMERIC;
token.isMalformed = true;
return;
}
try {
token.tokenValue = ValuePool.getInt(
Integer.parseInt(
sqlString.substring(
tokenStart, currentPosition)));
token.tokenType = Tokens.X_LOB_SIZE;
currentPosition++;
token.fullString = getPart(tokenPosition,
currentPosition);
} catch (NumberFormatException e) {
token.tokenType = Tokens.X_MALFORMED_NUMERIC;
token.isMalformed = true;
}
return;
default :
end = true;
break;
}
if (end) {
break;
}
}
token.tokenString = sqlString.substring(tokenStart, currentPosition);
switch (token.dataType.typeCode) {
case Types.SQL_INTEGER :
// fredt - -Integer.MIN_VALUE or -Long.MIN_VALUE are promoted
// to a wider type.
if (token.tokenString.length() < 11) {
try {
token.tokenValue = ValuePool.getInt(
Integer.parseInt(token.tokenString));
return;
} catch (Exception e1) {}
}
if (this.token.tokenString.length() < 20) {
try {
token.dataType = Type.SQL_BIGINT;
token.tokenValue = ValuePool.getLong(
Long.parseLong(token.tokenString));
return;
} catch (Exception e2) {}
}
token.dataType = Type.SQL_NUMERIC;
// fall through
case Types.SQL_NUMERIC :
try {
BigDecimal decimal = new BigDecimal(token.tokenString);
token.tokenValue = decimal;
token.dataType = NumberType.getNumberType(Types.NUMERIC,
JavaSystem.precision(decimal), decimal.scale());
} catch (Exception e2) {
token.tokenType = Tokens.X_MALFORMED_NUMERIC;
token.isMalformed = true;
return;
}
return;
case Types.SQL_DOUBLE :
try {
double d = JavaSystem.parseDouble(token.tokenString);
long l = Double.doubleToLongBits(d);
token.tokenValue = ValuePool.getDouble(l);
} catch (Exception e2) {
token.tokenType = Tokens.X_MALFORMED_NUMERIC;
token.isMalformed = true;
return;
}
return;
}
}
boolean scanSeparator() {
boolean result = false;
while (true) {
boolean whiteSpace = scanWhitespace();
result |= whiteSpace;
if (scanCommentAsInlineSeparator()) {
result = true;
hasNonSpaceSeparator = true;
continue;
}
break;
}
// token.isDelimiter |= result;
return result;
}
boolean scanCommentAsInlineSeparator() {
int character = charAt(currentPosition);
if (character == '-' && charAt(currentPosition + 1) == '-') {
int pos = sqlString.indexOf('\r', currentPosition + 2);
if (pos == -1) {
pos = sqlString.indexOf('\n', currentPosition + 2);
} else if (charAt(pos + 1) == '\n') {
pos++;
}
if (pos == -1) {
currentPosition = limit;
} else {
currentPosition = pos + 1;
}
return true;
} else if (character == '/' && charAt(currentPosition + 1) == '*') {
int pos = sqlString.indexOf("*/", currentPosition + 2);
if (pos == -1) {
token.tokenString = sqlString.substring(currentPosition,
currentPosition + 2);
token.tokenType = Tokens.X_MALFORMED_COMMENT;
token.isMalformed = true;
return false;
}
currentPosition = pos + 2;
return true;
}
return false;
}
public boolean scanWhitespace() {
boolean result = false;
for (; currentPosition < limit; currentPosition++) {
char c = sqlString.charAt(currentPosition);
if (c == ' ') {
result = true;
continue;
}
if (whiteSpaceSet.contains(c)) {
hasNonSpaceSeparator = true;
result = true;
setLineNumber(c);
continue;
}
break;
}
return result;
}
private void setLineNumber(int c) {
if (c == '\r' || c == '\n') {
if (currentPosition == eolPosition + 1) {
if (c == '\n' && eolCode != c) {
//
} else {
lineNumber++;
}
} else {
lineNumber++;
}
eolPosition = currentPosition;
eolCode = c;
}
}
void scanCharacterString() {
charWriter.reset(charBuffer);
while (true) {
scanStringPart('\'');
if (token.isMalformed) {
return;
}
if (scanSeparator() && charAt(currentPosition) == '\'') {
continue;
}
break;
}
token.tokenString = charWriter.toString();
token.tokenValue = token.tokenString;
}
public void scanStringPart(char quoteChar) {
currentPosition++;
for (;;) {
int nextIndex = sqlString.indexOf(quoteChar, currentPosition);
if (nextIndex < 0) {
token.tokenString = sqlString.substring(currentPosition,
limit);
token.tokenType = quoteChar == '\'' ? Tokens.X_MALFORMED_STRING
: Tokens
.X_MALFORMED_IDENTIFIER;
token.isMalformed = true;
return;
}
if (charAt(nextIndex + 1) == quoteChar) {
nextIndex += 1;
charWriter.write(sqlString, currentPosition,
nextIndex - currentPosition);
currentPosition = nextIndex + 1;
continue;
} else {
charWriter.write(sqlString, currentPosition,
nextIndex - currentPosition);
currentPosition = nextIndex + 1;
break;
}
}
}
/**
* token [separator] , nondelimiter {delimiter | separator}
*/
void scanToken() {
int character = charAt(currentPosition);
resetState();
token.tokenType = Tokens.X_IDENTIFIER;
switch (character) {
/*
case '%' :
case '^' :
case '&' :
case ':' :
case '{' :
case '}' :
break;
*/
case '[' :
token.tokenString = Tokens.T_LEFTBRACKET;
token.tokenType = Tokens.LEFTBRACKET;
currentPosition++;
token.isDelimiter = true;
return;
case ']' :
token.tokenString = Tokens.T_RIGHTBRACKET;
token.tokenType = Tokens.RIGHTBRACKET;
currentPosition++;
token.isDelimiter = true;
return;
case '(' :
token.tokenString = Tokens.T_OPENBRACKET;
token.tokenType = Tokens.OPENBRACKET;
currentPosition++;
token.isDelimiter = true;
return;
case ')' :
token.tokenString = Tokens.T_CLOSEBRACKET;
token.tokenType = Tokens.CLOSEBRACKET;
currentPosition++;
token.isDelimiter = true;
return;
case ',' :
token.tokenString = Tokens.T_COMMA;
token.tokenType = Tokens.COMMA;
currentPosition++;
token.isDelimiter = true;
return;
case '*' :
token.tokenString = Tokens.T_ASTERISK;
token.tokenType = Tokens.ASTERISK;
currentPosition++;
token.isDelimiter = true;
return;
case '=' :
token.tokenString = Tokens.T_EQUALS;
token.tokenType = Tokens.EQUALS;
currentPosition++;
token.isDelimiter = true;
return;
case ';' :
token.tokenString = Tokens.T_SEMICOLON;
token.tokenType = Tokens.SEMICOLON;
currentPosition++;
token.isDelimiter = true;
return;
case '+' :
token.tokenString = Tokens.T_PLUS;
token.tokenType = Tokens.PLUS;
currentPosition++;
token.isDelimiter = true;
return;
case ':' :
if (charAt(currentPosition + 1) == ':') {
currentPosition += 2;
token.tokenString = Tokens.T_DOUBLE_COLON;
token.tokenType = Tokens.COLON;
token.isDelimiter = true;
return;
} else {
token.tokenString = Tokens.T_COLON;
token.tokenType = Tokens.COLON;
currentPosition++;
token.isDelimiter = true;
return;
}
case '?' :
if (charAt(currentPosition + 1) == '?') {
if (charAt(currentPosition + 2) == '(') {
token.tokenString = Tokens.T_OPENBRACKET;
token.tokenType = Tokens.OPENBRACKET;
currentPosition += 3;
token.isDelimiter = true;
return;
} else if (charAt(currentPosition + 2) == ')') {
token.tokenString = Tokens.T_CLOSEBRACKET;
token.tokenType = Tokens.CLOSEBRACKET;
currentPosition += 3;
token.isDelimiter = true;
return;
}
}
token.tokenString = Tokens.T_QUESTION;
token.tokenType = Tokens.QUESTION;
currentPosition++;
token.isDelimiter = true;
return;
case '!' :
if (charAt(currentPosition + 1) == '=') {
token.tokenString = Tokens.T_NOT_EQUALS;
token.tokenType = Tokens.NOT_EQUALS;
currentPosition += 2;
token.isDelimiter = true;
return;
}
token.tokenString = sqlString.substring(currentPosition,
currentPosition + 2);
token.tokenType = Tokens.X_UNKNOWN_TOKEN;
token.isDelimiter = true;
return;
case '<' :
if (charAt(currentPosition + 1) == '>') {
token.tokenString = Tokens.T_NOT_EQUALS;
token.tokenType = Tokens.NOT_EQUALS;
currentPosition += 2;
token.isDelimiter = true;
return;
}
if (charAt(currentPosition + 1) == '=') {
token.tokenString = Tokens.T_LESS_EQUALS;
token.tokenType = Tokens.LESS_EQUALS;
currentPosition += 2;
token.isDelimiter = true;
return;
}
token.tokenString = Tokens.T_LESS;
token.tokenType = Tokens.LESS;
currentPosition++;
token.isDelimiter = true;
return;
case '>' :
if (charAt(currentPosition + 1) == '=') {
token.tokenString = Tokens.T_GREATER_EQUALS;
token.tokenType = Tokens.GREATER_EQUALS;
currentPosition += 2;
token.isDelimiter = true;
return;
}
token.tokenString = Tokens.T_GREATER;
token.tokenType = Tokens.GREATER;
currentPosition++;
token.isDelimiter = true;
return;
case '|' :
if (charAt(currentPosition + 1) == '|') {
token.tokenString = Tokens.T_CONCAT;
token.tokenType = Tokens.CONCAT;
currentPosition += 2;
token.isDelimiter = true;
return;
}
token.tokenString = sqlString.substring(currentPosition,
currentPosition + 2);
token.tokenType = Tokens.X_UNKNOWN_TOKEN;
token.isDelimiter = true;
return;
case '/' :
if (charAt(currentPosition + 1) == '/') {
int pos = sqlString.indexOf('\r', currentPosition + 2);
if (pos == -1) {
pos = sqlString.indexOf('\n', currentPosition + 2);
}
if (pos == -1) {
pos = limit;
}
token.tokenString = sqlString.substring(currentPosition
+ 2, pos);
token.tokenType = Tokens.X_REMARK;
token.isDelimiter = true;
return;
} else if (charAt(currentPosition + 1) == '*') {
int pos = sqlString.indexOf("*/", currentPosition + 2);
if (pos == -1) {
token.tokenString =
sqlString.substring(currentPosition,
currentPosition + 2);
token.tokenType = Tokens.X_UNKNOWN_TOKEN;
token.isDelimiter = true;
return;
}
token.tokenString = sqlString.substring(currentPosition
+ 2, pos);
token.tokenType = Tokens.X_REMARK;
token.isDelimiter = true;
return;
}
token.tokenString = Tokens.T_DIVIDE;
token.tokenType = Tokens.DIVIDE;
currentPosition++;
token.isDelimiter = true;
return;
case '-' :
if (charAt(currentPosition + 1) == '-') {
int pos = sqlString.indexOf('\r', currentPosition + 2);
if (pos == -1) {
pos = sqlString.indexOf('\n', currentPosition + 2);
}
if (pos == -1) {
pos = limit;
}
token.tokenString = sqlString.substring(currentPosition
+ 2, pos);
token.tokenType = Tokens.X_REMARK;
token.isDelimiter = true;
return;
}
token.tokenString = Tokens.T_MINUS;
token.tokenType = Tokens.MINUS;
currentPosition++;
token.isDelimiter = true;
return;
case '\"' :
token.tokenType = Tokens.X_DELIMITED_IDENTIFIER;
break;
case '\'' :
scanCharacterString();
if (token.isMalformed) {
return;
}
token.dataType = CharacterType.getCharacterType(Types.SQL_CHAR,
token.tokenString.length());
token.tokenType = Tokens.X_VALUE;
token.isDelimiter = true;
return;
case 'x' :
case 'X' :
if (charAt(currentPosition + 1) == '\'') {
currentPosition++;
scanBinaryString();
if (token.isMalformed) {
return;
}
token.dataType = BinaryType.getBinaryType(
Types.SQL_VARBINARY,
((BinaryData) token.tokenValue).length(null));
token.tokenType = Tokens.X_VALUE;
return;
}
break;
case 'b' :
case 'B' :
if (charAt(currentPosition + 1) == '\'') {
currentPosition++;
scanBitString();
if (token.isMalformed) {
return;
}
token.dataType = BitType.getBitType(
Types.SQL_BIT,
((BinaryData) token.tokenValue).bitLength(null));
token.tokenType = Tokens.X_VALUE;
return;
}
break;
case 'n' :
case 'N' :
if (charAt(currentPosition + 1) == '\'') {
currentPosition++;
scanCharacterString();
if (token.isMalformed) {
return;
}
token.dataType = CharacterType.getCharacterType(
Types.SQL_CHAR, token.tokenString.length());
token.tokenType = Tokens.X_VALUE;
return;
}
break;
case 'u' :
case 'U' :
if (charAt(currentPosition + 1) == '&') {
if (charAt(currentPosition + 2) == '\'') {
currentPosition += 2;
token.dataType = Type.SQL_CHAR;
token.tokenType = Tokens.X_VALUE;
scanUnicodeString();
if (token.isMalformed) {
return;
}
token.dataType = CharacterType.getCharacterType(
Types.SQL_CHAR,
((String) token.tokenValue).length());
return;
}
}
break;
case '_' :
/**
* @todo 1.9.0 - review following
* identifier chain must not have catalog identifier
* character set specification to be included in the token.dataType
*/
currentPosition++;
scanIdentifierChain();
if (token.isMalformed) {
return;
}
if (token.tokenType != Tokens.X_IDENTIFIER
|| token.namePrePrefix != null) {
/** @todo 1.9.0 - review message malformed character set identifier */
token.tokenType = Tokens.X_MALFORMED_STRING;
token.isMalformed = true;
return;
}
token.charsetSchema = token.namePrefix;
token.charsetName = token.tokenString;
scanSeparator();
if (charAt(currentPosition) == '\'') {
scanCharacterString();
token.tokenType = Tokens.X_VALUE;
token.dataType = CharacterType.getCharacterType(
Types.SQL_CHAR, token.tokenString.length());
token.isDelimiter = true;
return;
}
break;
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
case '.' :
token.tokenType = Tokens.X_VALUE;
scanNumber();
return;
}
scanIdentifierChain();
if (token.tokenType == Tokens.X_IDENTIFIER) {
token.isUndelimitedIdentifier = true;
token.tokenType = Tokens.getKeywordID(token.tokenString,
Tokens.X_IDENTIFIER);
if (token.tokenType == Tokens.X_IDENTIFIER) {
token.tokenType = Tokens.getNonKeywordID(token.tokenString,
Tokens.X_IDENTIFIER);
} else {
token.isReservedIdentifier = true;
token.isCoreReservedIdentifier =
Tokens.isCoreKeyword(token.tokenType);
}
} else if (token.tokenType == Tokens.X_DELIMITED_IDENTIFIER) {
token.isDelimitedIdentifier = true;
}
}
public boolean scanNull() {
scanSeparator();
int character = charAt(currentPosition);
if (character == 'N' || character == 'n') {
if (scanSpecialIdentifier(Tokens.T_NULL)) {
return true;
}
}
return false;
}
//
private void scanNext(int error) {
scanNext();
if (token.isMalformed) {
throw Error.error(error);
}
}
/**
* Reads the type part of the INTERVAL
*/
IntervalType scanIntervalType() {
int precision = -1;
int scale = -1;
int startToken;
int endToken;
final int errorCode = ErrorCode.X_22006;
startToken = endToken = token.tokenType;
scanNext(errorCode);
if (token.tokenType == Tokens.OPENBRACKET) {
scanNext(errorCode);
if (token.dataType == null
|| token.dataType.typeCode != Types.SQL_INTEGER) {
throw Error.error(errorCode);
}
precision = ((Number) this.token.tokenValue).intValue();
scanNext(errorCode);
if (token.tokenType == Tokens.COMMA) {
if (startToken != Tokens.SECOND) {
throw Error.error(errorCode);
}
scanNext(errorCode);
if (token.dataType == null
|| token.dataType.typeCode != Types.SQL_INTEGER) {
throw Error.error(errorCode);
}
scale = ((Number) token.tokenValue).intValue();
scanNext(errorCode);
}
if (token.tokenType != Tokens.CLOSEBRACKET) {
throw Error.error(errorCode);
}
scanNext(errorCode);
}
if (token.tokenType == Tokens.TO) {
scanNext(errorCode);
endToken = token.tokenType;
scanNext(errorCode);
}
if (token.tokenType == Tokens.OPENBRACKET) {
if (endToken != Tokens.SECOND || endToken == startToken) {
throw Error.error(errorCode);
}
scanNext(errorCode);
if (token.dataType == null
|| token.dataType.typeCode != Types.SQL_INTEGER) {
throw Error.error(errorCode);
}
scale = ((Number) token.tokenValue).intValue();
scanNext(errorCode);
if (token.tokenType != Tokens.CLOSEBRACKET) {
throw Error.error(errorCode);
}
scanNext(errorCode);
}
int startIndex = ArrayUtil.find(Tokens.SQL_INTERVAL_FIELD_CODES,
startToken);
int endIndex = ArrayUtil.find(Tokens.SQL_INTERVAL_FIELD_CODES,
endToken);
return IntervalType.getIntervalType(startIndex, endIndex, precision,
scale);
}
private String intervalString;
private int intervalPosition;
private int intervalPrecision;
private int fractionPrecision;
Type dateTimeType;
public TimestampData newDate(String s) {
intervalPosition = 0;
fractionPrecision = 0;
dateTimeType = null;
intervalString = s;
scanDateParts(2);
if (intervalPosition != s.length()) {
throw Error.error(ErrorCode.X_22007);
}
long seconds = HsqlDateTime.getDateSeconds(s);
return new TimestampData(seconds);
}
/**
* @todo 1.9.0 - review the following
* - misses nano fractions
* - misses displacement
* - doesn't allow single digit components
*/
public TimestampData newTimestamp(String s) {
long zoneSeconds = 0;
long seconds;
int fraction = 0;
int endIndex = s.length();
boolean negate;
boolean hasZone = false;
intervalPosition = 0;
fractionPrecision = 0;
dateTimeType = null;
intervalString = s;
scanDateParts(5);
try {
seconds = HsqlDateTime.getTimestampSeconds(s.substring(0,
intervalPosition));
} catch (Throwable e) {
throw Error.error(ErrorCode.X_22007);
}
int position;
fraction = scanIntervalFraction(DTIType.maxFractionPrecision);
position = intervalPosition;
negate = scanIntervalSign();
if (negate || position != intervalPosition) {
zoneSeconds = scanIntervalValue(Type.SQL_INTERVAL_HOUR_TO_MINUTE);
hasZone = true;
if (negate) {
zoneSeconds = -zoneSeconds;
}
}
if (zoneSeconds >= DTIType.yearToSecondFactors[2]
|| zoneSeconds > DTIType.timezoneSecondsLimit
|| -zoneSeconds > DTIType.timezoneSecondsLimit) {
throw Error.error(ErrorCode.X_22009);
}
if (intervalPosition != endIndex) {
throw Error.error(ErrorCode.X_22007);
}
int type = hasZone ? Types.SQL_TIMESTAMP_WITH_TIME_ZONE
: Types.SQL_TIMESTAMP;
dateTimeType = DateTimeType.getDateTimeType(type, fractionPrecision);
if (hasZone) {
seconds -= zoneSeconds;
}
return new TimestampData(seconds, fraction, (int) zoneSeconds);
}
void scanDateParts(int lastPart) {
byte[] separators = DTIType.yearToSecondSeparators;
int i = intervalPosition;
final int firstPart = 0;
int currentPart = firstPart;
int currentDigits = 0;
for (; currentPart <= lastPart; ) {
boolean endOfPart = false;
if (i == intervalString.length()) {
if (currentPart == lastPart) {
endOfPart = true;
} else {
// parts missing
throw Error.error(ErrorCode.X_22007);
}
} else {
int character = intervalString.charAt(i);
if (character >= '0' && character <= '9') {
currentDigits++;
i++;
} else if (character == separators[currentPart]) {
endOfPart = true;
if (currentPart != lastPart) {
i++;
}
} else if (currentPart == lastPart) {
endOfPart = true;
} else {
throw Error.error(ErrorCode.X_22007);
}
}
if (endOfPart) {
if (currentPart == firstPart) {
if (currentDigits != 4) {
throw Error.error(ErrorCode.X_22007);
}
} else {
if (currentDigits != 2) {
throw Error.error(ErrorCode.X_22007);
}
}
currentPart++;
currentDigits = 0;
if (i == intervalString.length()) {
break;
}
}
}
intervalPosition = i;
}
public TimeData newTime(String s) {
intervalPosition = 0;
fractionPrecision = 0;
dateTimeType = null;
intervalString = s;
long seconds = scanIntervalValue(Type.SQL_INTERVAL_HOUR_TO_SECOND);
int fraction = scanIntervalFraction(DTIType.maxFractionPrecision);
long zoneSeconds = 0;
int position = intervalPosition;
boolean hasZone = false;
boolean negate = scanIntervalSign();
if (position != intervalPosition) {
zoneSeconds = scanIntervalValue(Type.SQL_INTERVAL_HOUR_TO_MINUTE);
hasZone = true;
}
if (intervalPosition != s.length()) {
throw Error.error(ErrorCode.X_22009);
}
if (seconds >= DTIType.yearToSecondFactors[2]) {
throw Error.error(ErrorCode.X_22008);
}
if (zoneSeconds > DTIType.timezoneSecondsLimit) {
throw Error.error(ErrorCode.X_22009);
}
if (negate) {
zoneSeconds = -zoneSeconds;
}
int type = hasZone ? Types.SQL_TIME_WITH_TIME_ZONE
: Types.SQL_TIME;
dateTimeType = DateTimeType.getDateTimeType(type, fractionPrecision);
if (hasZone) {
seconds -= zoneSeconds;
}
return new TimeData((int) seconds, fraction, (int) zoneSeconds);
}
public Object newInterval(String s, IntervalType type) {
intervalPosition = 0;
intervalString = s;
boolean negate = scanIntervalSign();
long units = scanIntervalValue(type);
int fraction = 0;
if (type.endIntervalType == Types.SQL_INTERVAL_SECOND) {
fraction = scanIntervalFraction(type.scale);
}
if (intervalPosition != s.length()) {
throw Error.error(ErrorCode.X_22006);
}
if (negate) {
units = -units;
fraction = -fraction;
}
dateTimeType = type;
if (type.defaultPrecision) {
dateTimeType = IntervalType.getIntervalType(type.typeCode,
type.startIntervalType, type.endIntervalType,
intervalPrecision, fractionPrecision, false);
}
if (type.endPartIndex <= DTIType.INTERVAL_MONTH_INDEX) {
return new IntervalMonthData(units);
} else {
return new IntervalSecondData(units, fraction);
}
}
public long scanIntervalValue(IntervalType type) {
byte[] separators = DTIType.yearToSecondSeparators;
int[] factors = DTIType.yearToSecondFactors;
int[] limits = DTIType.yearToSecondLimits;
int firstPart = type.startPartIndex;
int lastPart = type.endPartIndex;
long totalValue = 0;
int currentValue = 0;
int i = intervalPosition;
int currentPart = firstPart;
int currentDigits = 0;
for (; currentPart <= lastPart; ) {
boolean endOfPart = false;
if (i == intervalString.length()) {
if (currentPart == lastPart) {
endOfPart = true;
} else {
throw Error.error(ErrorCode.X_22006);
}
} else {
int character = intervalString.charAt(i);
if (character >= '0' && character <= '9') {
int digit = character - '0';
currentValue *= 10;
currentValue += digit;
currentDigits++;
i++;
} else if (character == separators[currentPart]) {
endOfPart = true;
if (currentPart != lastPart) {
i++;
}
} else if (currentPart == lastPart) {
endOfPart = true;
} else {
throw Error.error(ErrorCode.X_22006);
}
}
if (endOfPart) {
if (currentPart == firstPart) {
if (!type.defaultPrecision
&& currentDigits > type.precision) {
throw Error.error(ErrorCode.X_22015);
}
if (currentDigits == 0) {
throw Error.error(ErrorCode.X_22006);
}
int factor = factors[currentPart];
totalValue += (long) currentValue * factor;
intervalPrecision = currentDigits;
} else {
if (currentValue >= limits[currentPart]) {
throw Error.error(ErrorCode.X_22015);
}
if (currentDigits != 2) {
throw Error.error(ErrorCode.X_22006);
}
totalValue += currentValue * factors[currentPart];
}
currentPart++;
currentValue = 0;
currentDigits = 0;
if (i == intervalString.length()) {
break;
}
}
}
intervalPosition = i;
return totalValue;
}
boolean scanIntervalSign() {
boolean negate = false;
if (intervalPosition == intervalString.length()) {
return false;
}
if (intervalString.charAt(intervalPosition) == '-') {
negate = true;
intervalPosition++;
} else if (intervalString.charAt(intervalPosition) == '+') {
intervalPosition++;
}
return negate;
}
int scanIntervalFraction(int decimalPrecision) {
if (intervalPosition == intervalString.length()) {
return 0;
}
if (intervalString.charAt(intervalPosition) != '.') {
return 0;
}
intervalPosition++;
int currentValue = 0;
int currentDigits = 0;
for (; intervalPosition < intervalString.length(); ) {
int character = intervalString.charAt(intervalPosition);
if (character >= '0' && character <= '9') {
int digit = character - '0';
currentValue *= 10;
currentValue += digit;
intervalPosition++;
currentDigits++;
if (currentDigits == DTIType.maxFractionPrecision) {
break;
}
} else {
break;
}
}
fractionPrecision = currentDigits;
currentValue *= DTIType.nanoScaleFactors[currentDigits];
currentValue = DTIType.normaliseFraction(currentValue,
decimalPrecision);
return currentValue;
}
void scanIntervalSpaces() {
for (; intervalPosition < intervalString.length();
intervalPosition++) {
if (intervalString.charAt(intervalPosition) != ' ') {
break;
}
}
}
/*
* synchronized methods for use with shared Scanner objects used for type
* conversion
*/
public synchronized Number convertToNumber(String s,
NumberType numberType) {
Number number;
boolean minus = false;
Type type;
reset(s);
resetState();
scanWhitespace();
scanToken();
scanWhitespace();
if (token.tokenType == Tokens.PLUS) {
scanToken();
scanWhitespace();
} else if (token.tokenType == Tokens.MINUS) {
minus = true;
scanToken();
scanWhitespace();
}
if (!hasNonSpaceSeparator && token.tokenType == Tokens.X_VALUE
&& token.tokenValue instanceof Number) {
number = (Number) token.tokenValue;
type = token.dataType;
if (minus) {
number = (Number) token.dataType.negate(number);
}
scanEnd();
if (token.tokenType == Tokens.X_ENDPARSE) {
number = (Number) numberType.convertToType(null, number, type);
return number;
}
}
throw Error.error(ErrorCode.X_22018);
}
public synchronized BinaryData convertToBinary(String s) {
boolean hi = true;
byte b = 0;
reset(s);
resetState();
byteOutputStream.reset(byteBuffer);
for (; currentPosition < limit; currentPosition++, hi = !hi) {
int c = sqlString.charAt(currentPosition);
c = getHexValue(c);
if (c == -1) {
// bad character
token.tokenType = Tokens.X_MALFORMED_BINARY_STRING;
token.isMalformed = true;
break;
}
if (hi) {
b = (byte) (c << 4);
} else {
b += (byte) c;
byteOutputStream.writeByte(b);
}
}
if (!hi) {
// odd nibbles
token.tokenType = Tokens.X_MALFORMED_BINARY_STRING;
token.isMalformed = true;
}
if (token.isMalformed) {
throw Error.error(ErrorCode.X_22018);
}
BinaryData data = new BinaryData(byteOutputStream.toByteArray(),
false);
byteOutputStream.reset(byteBuffer);
return data;
}
public synchronized BinaryData convertToBit(String s) {
BitMap map = new BitMap(32);
int bitIndex = map.size();
reset(s);
resetState();
byteOutputStream.reset(byteBuffer);
for (; currentPosition < limit; currentPosition++) {
int c = sqlString.charAt(currentPosition);
if (c == '0') {
bitIndex++;
} else if (c == '1') {
map.set(bitIndex);
bitIndex++;
} else {
token.tokenType = Tokens.X_MALFORMED_BIT_STRING;
token.isMalformed = true;
throw Error.error(ErrorCode.X_22018);
}
}
map.setSize(bitIndex);
return BinaryData.getBitData(map.getBytes(), map.size());
}
// should perform range checks etc.
public synchronized Object convertToDatetimeInterval(
SessionInterface session, String s, DTIType type) {
Object value;
IntervalType intervalType = null;
int dateTimeToken = -1;
int errorCode = type.isDateTimeType() ? ErrorCode.X_22007
: ErrorCode.X_22006;
reset(s);
resetState();
scanToken();
scanWhitespace();
switch (token.tokenType) {
case Tokens.INTERVAL :
case Tokens.DATE :
case Tokens.TIME :
case Tokens.TIMESTAMP :
dateTimeToken = token.tokenType;
scanToken();
if (token.tokenType != Tokens.X_VALUE
|| token.dataType.typeCode != Types.SQL_CHAR) {
// error datetime bad literal
throw Error.error(errorCode);
}
s = token.tokenString;
scanNext(ErrorCode.X_22007);
if (type.isIntervalType()) {
intervalType = scanIntervalType();
}
if (token.tokenType != Tokens.X_ENDPARSE) {
throw Error.error(errorCode);
}
// fall through
default :
}
switch (type.typeCode) {
case Types.SQL_DATE : {
if (dateTimeToken != -1 && dateTimeToken != Tokens.DATE) {
throw Error.error(errorCode);
}
value = newDate(s);
return type.convertToType(session, value, Type.SQL_DATE);
}
case Types.SQL_TIME :
case Types.SQL_TIME_WITH_TIME_ZONE : {
if (dateTimeToken != -1 && dateTimeToken != Tokens.TIME) {
throw Error.error(errorCode);
}
Object o = newTime(s);
return type.convertToType(session, o, dateTimeType);
}
case Types.SQL_TIMESTAMP :
case Types.SQL_TIMESTAMP_WITH_TIME_ZONE : {
if (dateTimeToken != -1 && dateTimeToken != Tokens.TIMESTAMP) {
throw Error.error(errorCode);
}
value = newTimestamp(s);
return type.convertToType(session, value, dateTimeType);
}
default :
if (dateTimeToken != -1 && dateTimeToken != Tokens.INTERVAL) {
throw Error.error(errorCode);
}
if (type.isIntervalType()) {
value = newInterval(s, (IntervalType) type);
if (intervalType != null) {
if (intervalType.startIntervalType != type
.startIntervalType || intervalType
.endIntervalType != type.endIntervalType) {
throw Error.error(errorCode);
}
}
return type.convertToType(session, value, dateTimeType);
}
throw Error.runtimeError(ErrorCode.U_S0500, "Scanner");
}
}
}