blob: 361bd43e0d2025e0ad8bcd0ef6bd0b64db5c29ae [file] [log] [blame]
/*
* 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.ignite.internal.sql;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
/**
* SQL lexer.
*/
public class SqlLexer implements SqlLexerToken {
/** Original input. */
private final String sql;
/** Input characters. */
private final char[] inputChars;
/** Current position. */
private int pos;
/** Current token start. */
private int tokenPos;
/** Current token. */
private String token;
/** Token type. */
private SqlLexerTokenType tokenTyp;
/**
* Constructor.
*
* @param sql Input.
*/
public SqlLexer(String sql) {
assert sql != null;
this.sql = sql;
// Additional slot for look-ahead convenience.
inputChars = new char[sql.length() + 1];
for (int i = 0; i < sql.length(); i++)
inputChars[i] = sql.charAt(i);
}
/**
* Get next token without lexer state change.
*
* @return Next token.
*/
public SqlLexerToken lookAhead() {
int pos0 = pos;
String token0 = token;
int tokenPos0 = tokenPos;
SqlLexerTokenType tokenTyp0 = tokenTyp;
try {
if (shift())
return new SqlLexerLookAheadToken(sql, token, tokenPos, tokenTyp);
else
return new SqlLexerLookAheadToken(sql, null, tokenPos, SqlLexerTokenType.EOF);
}
finally {
pos = pos0;
token = token0;
tokenPos = tokenPos0;
tokenTyp = tokenTyp0;
}
}
/**
* Shift lexer to the next position.
*
* @return {@code True} if next token was found, {@code false} in case of end-of-file.
*/
public boolean shift() {
while (!eod()) {
int tokenStartPos0 = pos;
String token0 = null;
SqlLexerTokenType tokenTyp0 = null;
char c = inputChars[pos++];
switch (c) {
case '-':
if (inputChars[pos] == '-') {
// Full-line comment.
pos++;
while (!eod()) {
char c1 = inputChars[pos];
if (c1 == '\n' || c1 == '\r')
break;
pos++;
}
}
else {
// Minus.
token0 = "-";
tokenTyp0 = SqlLexerTokenType.MINUS;
}
break;
case '\"':
while (true) {
if (eod()) {
throw new SqlParseException(sql, tokenStartPos0, IgniteQueryErrorCode.PARSING,
"Unclosed quoted identifier.");
}
char c1 = inputChars[pos];
pos++;
if (c1 == '\"')
break;
}
token0 = sql.substring(tokenStartPos0 + 1, pos - 1);
tokenTyp0 = SqlLexerTokenType.QUOTED;
break;
case '\'':
while (true) {
if (eod()) {
throw new SqlParseException(sql, tokenStartPos0, IgniteQueryErrorCode.PARSING,
"Unclosed string constant.");
}
char c1 = inputChars[pos];
pos++;
if (c1 == '\'') {
char c2 = inputChars[pos];
if (c2 == '\'')
pos++;
else
break;
}
}
token0 = sql.substring(tokenStartPos0 + 1, pos - 1).replaceAll("''", "'");
tokenTyp0 = SqlLexerTokenType.STRING;
break;
case '.':
case ',':
case ';':
case '(':
case ')':
token0 = Character.toString(c);
tokenTyp0 = SqlLexerTokenType.forChar(c);
break;
default:
if (c <= ' ' || Character.isSpaceChar(c))
continue;
while (!eod()) {
char c1 = inputChars[pos];
if (!Character.isJavaIdentifierPart(c1))
break;
pos++;
}
token0 = sql.substring(tokenStartPos0, pos).toUpperCase();
tokenTyp0 = SqlLexerTokenType.DEFAULT;
}
if (tokenTyp0 != null) {
token = token0;
tokenPos = tokenStartPos0;
tokenTyp = tokenTyp0;
return true;
}
}
token = null;
tokenPos = pos;
tokenTyp = SqlLexerTokenType.EOF;
return false;
}
/** {@inheritDoc} */
@Override public String sql() {
return sql;
}
/** {@inheritDoc} */
@Override public String token() {
return token;
}
/** {@inheritDoc} */
@Override public char tokenFirstChar() {
assert tokenTyp != SqlLexerTokenType.EOF;
return token.charAt(0);
}
/** {@inheritDoc} */
@Override public int tokenPosition() {
return tokenPos;
}
/** {@inheritDoc} */
@Override public SqlLexerTokenType tokenType() {
return tokenTyp;
}
/**
* @return {@code True} if end of data is reached.
*/
private boolean eod() {
return pos == inputChars.length - 1;
}
}