blob: 9e2d2d286012056fdceeadea1002e9f115dd1817 [file]
/**
* Defines a loose parser, refer to ClickHouse(https://github.com/ClickHouse/clickhouse-jdbc/blob/master/clickhouse-jdbc/src/main/javacc/ClickHouseSqlParser.jj).
* It cannot be used to validate SQL on behalf of server, but only for the following purposes:
* 1) recognize type of each statement(DDL/DML/DCL/TCL, query or mutation etc.)
* 2) extract table and parameters from a statement
*/
options {
// DEBUG_LOOKAHEAD = true;
// DEBUG_PARSER = true;
// DEBUG_TOKEN_MANAGER = true;
ERROR_REPORTING = false;
UNICODE_INPUT = true;
COMMON_TOKEN_ACTION = true;
}
PARSER_BEGIN(CeresDBSqlParser)
package org.apache.horaedb.sql;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CeresDBSqlParser {
private static final boolean DEBUG = false;
private static final Logger log = LoggerFactory.getLogger(CeresDBSqlParser.class);
private final List<CeresDBSqlStatement> statements = new ArrayList<>();
private ParseHandler handler;
private boolean tokenIn(int tokenIndex, int... tokens) {
boolean matched = false;
int t = getToken(tokenIndex).kind;
if (tokens != null) {
for (int i : tokens) {
if (t == i) {
matched = true;
break;
}
}
}
return matched;
}
// FIXME ugly workaround but performs better than adding another lexical state for <BETWEEN>...<AND>
private boolean noAndWithinBetween() {
return !(getToken(1).kind == AND && token_source.parentToken == BETWEEN);
}
public static CeresDBSqlStatement[] parse(String sql) {
return parse(sql, null);
}
public static CeresDBSqlStatement[] parse(String sql, ParseHandler handler) {
CeresDBSqlStatement[] stmts = new CeresDBSqlStatement[] { new CeresDBSqlStatement(sql, StatementType.UNKNOWN) };
if (sql == null || sql.isEmpty()) {
return stmts;
}
CeresDBSqlParser p = new CeresDBSqlParser(sql, handler);
try {
stmts = p.sql();
} catch (Exception e) {
if (DEBUG) {
throw new IllegalArgumentException(e);
} else {
log.warn("{}. If you believe the SQL is valid, please feel free to open an issue on Github with this warning and the following SQL attached.\n{}", e.getMessage(), sql);
}
}
return stmts;
}
public CeresDBSqlParser(String sql, ParseHandler handler) {
this(new StringReader(sql));
this.handler = handler;
}
public void addStatement() {
if (token_source.isValid()) {
CeresDBSqlStatement sqlStmt = token_source.build(handler);
// FIXME remove the restriction once we can handle insertion with format well
if (statements.size() == 0 || sqlStmt.isRecognized()) {
statements.add(sqlStmt);
}
} else {
token_source.reset();
}
}
}
PARSER_END(CeresDBSqlParser)
TOKEN_MGR_DECLS: {
// whitespaces and comments are invalid
private int validTokens = 0;
// see http://www.engr.mun.ca/~theo/JavaCC-FAQ/javacc-faq-moz.htm#tth_sEc3.17
private int commentNestingDepth = 0;
final java.util.Deque<Integer> stack = new java.util.LinkedList<>();
int parentToken = -1;
final StringBuilder builder = new StringBuilder();
StatementType stmtType = StatementType.UNKNOWN;
String database = null;
final List<String> tables = new ArrayList<>();
final List<Integer> parameters = new ArrayList<>();
final Map<String, Integer> positions = new HashMap<>();
public void CommonTokenAction(Token t) {
if (t.kind != CeresDBSqlParserConstants.SEMICOLON) {
builder.append(t.image);
if (t.kind != CeresDBSqlParserConstants.EOF) {
validTokens++;
}
}
}
void enterToken(int tokenKind) {
if (tokenKind < 0) {
return;
}
stack.push(parentToken = tokenKind);
}
void leaveToken(int tokenKind) {
if (parentToken == tokenKind) {
stack.pop();
}
parentToken = stack.isEmpty() ? -1 : stack.getLast();
}
void processMacro(String name, List<String> params, ParseHandler handler) {
StringBuilder m = new StringBuilder();
m.append('#').append(name);
int startPos = builder.lastIndexOf(m.toString());
int endPos = params.size() > 0 ? builder.indexOf(")", startPos) + 1 : startPos + m.length();
builder.delete(startPos, endPos);
if (handler != null) {
String replacement = handler.handleMacro(name, params);
if (replacement != null && !replacement.isEmpty()) {
builder.insert(startPos, replacement);
}
}
}
void processParameter(String str, ParseHandler handler) {
int pos = builder.lastIndexOf(str);
parameters.add(pos);
if (handler != null) {
String replacement = handler.handleParameter(database, tables, parameters.size());
if (replacement != null && !replacement.isEmpty()) {
builder.deleteCharAt(pos);
builder.insert(pos, replacement);
}
}
}
void append(StringBuilder str) {
builder.append(str.toString());
}
void reset() {
stack.clear();
parentToken = -1;
builder.setLength(validTokens = 0);
stmtType = StatementType.UNKNOWN;
database = null;
tables.clear();
parameters.clear();
positions.clear();
}
CeresDBSqlStatement build(ParseHandler handler) {
String sqlStmt = builder.toString();
CeresDBSqlStatement s = null;
if (handler != null) {
s = handler.handleStatement(
sqlStmt, stmtType, database, tables, parameters, positions);
}
if (s == null) {
s = new CeresDBSqlStatement(
sqlStmt, stmtType, database, tables, parameters, positions);
}
// reset variables
reset();
return s;
}
boolean isValid() {
return validTokens > 0;
}
void setPosition(String keyword) {
if (keyword == null || keyword.isEmpty()) {
return;
}
this.positions.put(keyword, builder.lastIndexOf(keyword));
}
}
SKIP: {
<WHITESPACE: " " | "\t" | "\n" | "\r" | "\u000b" | "\u000c" | "\u0085" | "\u00a0" | "\u180e"
| "\u2000" | "\u200a" | "\u200b" | "\u200c" | "\u200d"
| "\u2028" | "\u2029" | "\u202f" | "\u205f" | "\u2060" | "\u3000" | "\ufeff">
{ append(image); }
| <SINGLE_LINE_COMMENT: ("--" | "//") (~["\r", "\n"])*> { append(image); }
| "/*" { commentNestingDepth = 1; append(image); }: MULTI_LINE_COMMENT
}
<MULTI_LINE_COMMENT> SKIP: {
"/*" { commentNestingDepth += 1; append(image); }
| "*/" { SwitchTo(--commentNestingDepth == 0 ? DEFAULT : MULTI_LINE_COMMENT); append(image); }
| < ~[] > { append(image); }
}
// top-level statements
CeresDBSqlStatement[] sql(): {} {
stmts()
{ addStatement(); }
(
(LOOKAHEAD(2) <SEMICOLON>)+
(stmts())?
{ addStatement(); }
)*
<EOF>
{ return statements.toArray(new CeresDBSqlStatement[statements.size()]); }
}
void stmts(): { Token t; } {
LOOKAHEAD(2) stmt()
| LOOKAHEAD(2) anyExprList() // in case there's anything new
}
void stmt(): {} {
alterStmt() { if (token_source.stmtType == StatementType.UNKNOWN) token_source.stmtType = StatementType.ALTER; }
| createStmt() { token_source.stmtType = StatementType.CREATE; }
| deleteStmt() { token_source.stmtType = StatementType.DELETE; }
| describeStmt() { token_source.stmtType = StatementType.DESCRIBE; }
| dropStmt() { token_source.stmtType = StatementType.DROP; }
| insertStmt() { token_source.stmtType = StatementType.INSERT; }
| selectStmt() { token_source.stmtType = StatementType.SELECT; }
| showStmt() { token_source.stmtType = StatementType.SHOW; }
| existsStmt() { token_source.stmtType = StatementType.EXISTS; }
}
void alterStmt(): {} {
<ALTER> <TABLE> tableIdentifier(true) anyExprList()
}
void createStmt(): { Token t; } {
<CREATE> (
LOOKAHEAD(2) <TABLE>
(
LOOKAHEAD(2)
<IF> <NOT> t = <EXISTS> { token_source.setPosition(t.image); }
)?
)?
tableIdentifier(true)
anyExprList() // not interested
}
void deleteStmt(): {} {
<DELETE> <FROM> tableIdentifier(true) (<WHERE> anyExprList())?
}
void describeStmt(): {} {
(<DESCRIBE> | <DESC>)
(LOOKAHEAD({ getToken(1).kind == TABLE }) <TABLE>)? tableIdentifier(true) (anyExprList())?
}
void dropStmt(): { Token t; } {
<DROP> (
LOOKAHEAD(2) <TABLE>
(
LOOKAHEAD(2)
<IF> t = <EXISTS> { token_source.setPosition(t.image); }
)?
)?
tableIdentifier(true) (anyExprList())?
}
void insertStmt(): {} {
<INSERT> <INTO>
(
LOOKAHEAD({ getToken(1).kind == FUNCTION }) <FUNCTION> functionExpr()
| (LOOKAHEAD(2) <TABLE>)? tableIdentifier(true)
)
(LOOKAHEAD(2) <LPAREN> columnExprList() <RPAREN>)?
dataClause()
}
void dataClause(): { Token t; } {
try {
anyIdentifier() (LOOKAHEAD(2) anyExprList())?
| LOOKAHEAD(2) t = <VALUES> { token_source.setPosition(t.image); }
<LPAREN> columnExprList() <RPAREN>
(
LOOKAHEAD(2)
(<COMMA>)?
<LPAREN> columnExprList() <RPAREN>
)*
| anyExprList() // not interested
} catch (ParseException e) {
Token nextToken;
do {
nextToken = getNextToken();
} while(nextToken.kind != SEMICOLON && nextToken.kind != EOF);
}
}
void selectStmt(): {} {
(withClause())?
<SELECT> (LOOKAHEAD(2) <DISTINCT>)? (LOOKAHEAD(2) topClause())? columnExprList()
(LOOKAHEAD(2) <FROM> tableClause(true) (<COMMA> tableClause(true))*)?
(LOOKAHEAD(2) <INNER> <JOIN> tableClause(true) (<COMMA> tableClause(true))*)?
(LOOKAHEAD(2) <LEFT> <JOIN> tableClause(true) (<COMMA> tableClause(true))*)?
(LOOKAHEAD(2) <RIGHT> <JOIN> tableClause(true) (<COMMA> tableClause(true))*)?
(LOOKAHEAD(2) <JOIN> tableClause(true) (<COMMA> tableClause(true))*)?
(LOOKAHEAD(2) anyExprList())? // everything else
}
void withClause(): {} {
<WITH> { token_source.enterToken(WITH); } withExpr() (<COMMA> withExpr())*
{ token_source.leaveToken(WITH); }
}
void topClause(): {} { <TOP> <DECIMAL_LITERAL> (LOOKAHEAD(2) <WITH> <TIES>)? }
void tableClause(boolean record): { Token t; } {
(LOOKAHEAD(2) tableIdentifier(record) | (<LPAREN> anyExprList() <RPAREN>))
(LOOKAHEAD(2) t = aliasExpr() { if (record && t != null) token_source.tables.add(SqlUtils.unescape(t.image)); } )?
(LOOKAHEAD(2) <FINAL>)?
(LOOKAHEAD(2) <SAMPLE> numberLiteral() (LOOKAHEAD(2) <SLASH> numberLiteral())?
(LOOKAHEAD(2) <OFFSET> numberLiteral() (LOOKAHEAD(2) <SLASH> numberLiteral())?)?
)?
}
void showStmt(): {} {
<SHOW>
(<CREATE> <TABLE>)? tableIdentifier(true)
(LOOKAHEAD(2) anyExprList())?
}
void existsStmt(): {} {
<EXISTS>
(<TABLE>)? tableIdentifier(true)
(LOOKAHEAD(2) anyExprList())?
}
// columns
void columnExprList(): {} {
columnsExpr() (<COMMA> columnsExpr())*
}
void withExpr(): {} {
nestedExpr()
(
(
LOOKAHEAD({ getToken(1).kind == FLOATING_LITERAL })
<FLOATING_LITERAL> | <DOT> <DECIMAL_LITERAL>
)+
| (LOOKAHEAD(2) <LBRACKET> anyExprList() <RBRACKET>)+
| LOOKAHEAD(2) <IS> (<NOT>)? <NULL>
| LOOKAHEAD(2) (<NOT>)? betweenExpr()
| LOOKAHEAD(2) (<NOT>)? (<ILIKE> | <LIKE>) nestedExpr()
| LOOKAHEAD(2, { noAndWithinBetween() }) (LOOKAHEAD(2, { noAndWithinBetween() }) calcExpr())+
| LOOKAHEAD(2) (<NOT>)? <IN> nestedExpr()
| LOOKAHEAD(2) <QUERY> nestedExpr() <COLON> nestedExpr()
| LOOKAHEAD(2) <AS> columnExpr()
)?
}
void columnsExpr(): {} {
LOOKAHEAD(allColumnsExpr()) allColumnsExpr()
(
LOOKAHEAD(2) (<APPLY> | <EXCEPT> | <REPLACE>) <LPAREN> anyExprList() <RPAREN>
)*
| nestedExpr()
(
(
LOOKAHEAD({ getToken(1).kind == FLOATING_LITERAL })
<FLOATING_LITERAL> | <DOT> <DECIMAL_LITERAL>
)+
| (LOOKAHEAD(2) <LBRACKET> anyExprList() <RBRACKET>)+
| LOOKAHEAD(2) <IS> (<NOT>)? <NULL>
| LOOKAHEAD(2) (<NOT>)? betweenExpr()
| LOOKAHEAD(2) (<NOT>)? (<ILIKE> | <LIKE>) nestedExpr()
| LOOKAHEAD(2, { noAndWithinBetween() }) (LOOKAHEAD(2, { noAndWithinBetween() }) calcExpr())+
| LOOKAHEAD(2) (<NOT>)? <IN> nestedExpr()
| LOOKAHEAD(2) <QUERY> nestedExpr() <COLON> nestedExpr()
| LOOKAHEAD(2) aliasExpr()
)?
}
void allColumnsExpr(): {} {
<ASTERISK> | anyIdentifier() <DOT> (LOOKAHEAD(2) anyIdentifier() <DOT>)? <ASTERISK>
}
void nestedExpr(): {} {
LOOKAHEAD(2) (<DASH> | <NOT>) nestedExpr()
| LOOKAHEAD(2) <CASE> (LOOKAHEAD({ getToken(1).kind != WHEN }) nestedExpr())?
(<WHEN> nestedExpr() <THEN> nestedExpr())+ (<ELSE> nestedExpr())? <END>
| LOOKAHEAD(2) <INTERVAL> (LOOKAHEAD(2) <STRING_LITERAL> | nestedExpr() interval())
| columnExpr()
(
(
<FLOATING_LITERAL> | <DOT> <DECIMAL_LITERAL>
)+
| (LOOKAHEAD(2) <LBRACKET> anyExprList() <RBRACKET>)+
| LOOKAHEAD(2) <IS> (<NOT>)? <NULL>
| LOOKAHEAD(2) (<NOT>)? betweenExpr()
| LOOKAHEAD(2) (<NOT>)? (<ILIKE> | <LIKE>) nestedExpr()
| LOOKAHEAD(2, { noAndWithinBetween() }) (LOOKAHEAD(2, { noAndWithinBetween() }) calcExpr())+
| LOOKAHEAD(2) (<NOT>)? <IN> nestedExpr()
| LOOKAHEAD(2) <QUERY> nestedExpr() <COLON> nestedExpr()
)?
}
void calcExpr(): {} {
(<AND> | <OR> | operator()) nestedExpr()
}
void betweenExpr(): {} {
<BETWEEN> { token_source.enterToken(BETWEEN); }
nestedExpr()
<AND> { token_source.leaveToken(BETWEEN); }
nestedExpr()
}
void functionExpr(): {} {
anyIdentifier() <LPAREN> (anyExprList())? <RPAREN>
(LOOKAHEAD(2) <LPAREN> (anyExprList())? <RPAREN>)?
}
void columnExpr(): { Token t; } {
t = <QUERY> { token_source.processParameter(t.image, handler); }
| <LBRACE> (LOOKAHEAD(2) anyExprList())? <RBRACE>
| <LBRACKET> (LOOKAHEAD(2) anyExprList())? <RBRACKET>
| <LPAREN> anyExprList() <RPAREN>
| (LOOKAHEAD(2) macro())+
| LOOKAHEAD(2, { !(tokenIn(1, INF, NAN, NULL) && tokenIn(2, DOT)) }) literal()
| LOOKAHEAD(2, { getToken(2).kind == LPAREN }) functionExpr()
| anyIdentifier() (LOOKAHEAD(2) <DOT> anyIdentifier())*
}
void withTotalPart(): { Token t; } {
<WITH> (LOOKAHEAD(2) t = <TOTALS> { token_source.setPosition(t.image); })?
}
// expressions
void anyExprList(): {} {
anyExpr() (LOOKAHEAD(2) <COMMA> | anyExpr())*
}
void anyExpr(): {} {
anyNestedExpr() (
LOOKAHEAD(2)
(
// TODO needs to extract parameters(and exclude ternary operator) here
<AT> | <AND> | <OR> | <COLON> | <QUERY> | operator()
)? anyNestedExpr()
)*
}
void anyNestedExpr(): {} {
LOOKAHEAD(2) withTotalPart()
| (LOOKAHEAD(2) <DASH>)? anyColumnExpr() (
LOOKAHEAD({ getToken(1).kind == FLOATING_LITERAL })
<FLOATING_LITERAL> | <DOT> <DECIMAL_LITERAL>
)*
}
void anyColumnExpr(): { Token t; } {
// <ASTERISK>
t = <QUERY> { token_source.processParameter(t.image, handler); }
| <LBRACE> (LOOKAHEAD(2) anyExprList())? <RBRACE>
| <LBRACKET> (LOOKAHEAD(2) anyExprList())? <RBRACKET>
| <LPAREN> (LOOKAHEAD(2) anyExprList())? <RPAREN>
| (LOOKAHEAD(2) macro())+
| LOOKAHEAD(2, { !(tokenIn(1, INF, NAN, NULL) && tokenIn(2, DOT)) }) literal()
// | (LOOKAHEAD(2, { !(tokenIn(1, INF, NAN, NULL)) }) <FLOATING_LITERAL> | literal())
| nestedIdentifier()
}
Token aliasExpr(): { Token t = null; } {
(
LOOKAHEAD(2) <AS> t = anyIdentifier()
| t = identifier()
)
{ return t; }
}
void nestedIdentifier(): {} {
(<ASTERISK> | anyIdentifier()) (LOOKAHEAD(2) <DOT> (<ASTERISK> | anyIdentifier()))*
}
void tableIdentifier(boolean record): { Token t; } {
(
(LOOKAHEAD(2) databaseIdentifier(record) <DOT>)? t = anyIdentifier()
(LOOKAHEAD(2) <LPAREN> anyExprList() <RPAREN>)?
)
{
if (record && t != null) {
token_source.tables.add(SqlUtils.unescape(t.image));
}
}
}
void databaseIdentifier(boolean record): { Token t; } {
t = anyIdentifier() { if (record) token_source.database = SqlUtils.unescape(t.image); }
}
void settingExprList(): {} {
settingExpr() (<COMMA> settingExpr())*
}
void settingExpr(): {} {
identifier() <EQ_SINGLE> literal()
}
// basics
Token anyIdentifier(): { Token t; } {
(
t = <BACK_QUOTED_NAME>
| t = <DOUBLE_QUOTED_NAME>
| t = variable()
| t = <IDENTIFIER>
| t = anyKeyword()
)
{ return t; }
}
Token identifier(): { Token t; } {
(
t = <BACK_QUOTED_NAME>
| t = <DOUBLE_QUOTED_NAME>
| t = variable()
| t = <IDENTIFIER>
| t = keyword()
)
{ return t; }
}
void interval(): {} {
<SECOND> | <MINUTE> | <HOUR> | <DAY> | <WEEK> | <MONTH> | <QUARTER> | <YEAR>
}
Token literal(): { Token t; } {
(
t = dateLiteral()
| t = numberLiteral()
| t = <STRING_LITERAL>
| t = <NULL>
)
{ return t; }
}
Token dateLiteral(): { Token t; String prefix; } {
(t = <DATE> | t = <TIMESTAMP>) { prefix = t.image; }
t = <STRING_LITERAL>
{ return Token.newToken(0, prefix + " " + t.image); }
}
Token numberLiteral(): { Token t = null; StringBuilder sb = new StringBuilder(); } {
(
(t = <PLUS> | t = <DASH>)? { if (t != null) sb.append(t.image); }
(
LOOKAHEAD(2)
t = <DECIMAL_LITERAL> | t = <HEXADECIMAL_LITERAL> | t = <FLOATING_LITERAL> | t = <INF> | t = <NAN>
) { sb.append(t.image); }
)
{ return Token.newToken(0, sb.toString()); }
}
void operator(): {} {
(<ARROW> | <ASTERISK> | <CONCAT> | <DASH> | <EQ_DOUBLE> | <EQ_SINGLE>
| <GE> | <GT> | <LE> | <LT> | <NOT_EQ> | <PERCENT> | <PLUS> | <SLASH>)
}
void macro(): {
Token t;
String name;
List<String> params = new ArrayList<>();
} {
(
<HASH>
(t = anyKeyword() | t = <IDENTIFIER>) { name = t.image; }
(
LOOKAHEAD(2) <LPAREN>
t = <STRING_LITERAL> { params.add(SqlUtils.unescape(t.image)); }
(<COMMA> t = <STRING_LITERAL> { params.add(SqlUtils.unescape(t.image)); })*
<RPAREN>
)?
)
{ token_source.processMacro(name, params, handler); }
}
Token variable(): { Token t; } {
(<AT> <AT> (t = anyKeyword() | t = <IDENTIFIER>))
{
return Token.newToken(0, "@@" + t.image);
}
}
Token anyKeyword(): { Token t; } {
(
// leading keywords(except with)
t = <ALTER> | t = <ATTACH> | t = <CHECK> | t = <CREATE> | t = <DELETE> | t = <DESC> | t = <DESCRIBE>
| t = <DETACH> | t = <DROP> | t = <EXISTS> | t = <EXPLAIN> | t = <GRANT> | t = <INSERT> | t = <KILL>
| t = <OPTIMIZE> | t = <RENAME> | t = <REVOKE> | t = <SELECT> | t = <SET> | t = <SHOW> | t = <SYSTEM>
| t = <TRUNCATE> | t = <UPDATE> | t = <USE> | t = <WATCH>
// others
| t = <ALL> | t = <AND> | t = <APPLY> | t = <ARRAY> | t = <AS> | t = <ASOF> | t = <BETWEEN> | t = <CASE>
| t = <DATE> | t = <DATABASE> | t = <DATABASES> | t = <DICTIONARY> | t = <DICTIONARIES>
| t = <DISTINCT> | t = <ELSE> | t = <END> | t = <EXCEPT> | t = <FROM> | t = <FINAL> | t = <FULL>
| t = <FUNCTION> | t = <GLOBAL> | t = <GROUP> | t = <HAVING> | t = <IF> | t = <ILIKE> | t = <IN> | t = <INNER>
| t = <INTERVAL> | t = <INTO> | t = <IS> | t = <JOIN> | t = <LEFT> | t = <LIKE> | t = <LIMIT> | t = <LIVE>
| t = <MATERIALIZED> | t = <NOT> | t = <OFFSET> | t = <ON> | t = <OR> | t = <ORDER> | t = <POLICY>
| t = <PREWHERE> | t = <PROFILE> | t = <QUOTA> | t = <REPLACE> | t = <RIGHT> | t = <ROLE> | t = <ROW> | t = <SAMPLE>
| t = <SETTINGS> | t = <TEMPORARY> | t = <TABLE> | t = <TABLES> | t = <THEN> | t = <TIES> | t = <TIMESTAMP>
| t = <TOP> | t = <TOTALS> | t = <VALUES> | t = <VIEW> | t = <USER> | t = <UNION> | t = <USING>
| t = <WHEN> | t = <WHERE> | t = <WITH>
// interval
| t = <SECOND> | t = <MINUTE> | t = <HOUR> | t = <DAY> | t = <WEEK> | t = <MONTH> | t = <QUARTER> | t = <YEAR>
// values
| t = <INF> | t = <NAN> | t = <NULL>
)
{ return t; }
}
Token keyword(): { Token t; } {
(
// leading keywords(except with)
t = <ALTER> | t = <ATTACH> | t = <CHECK> | t = <CREATE> | t = <DELETE> | t = <DESC> | t = <DESCRIBE>
| t = <DETACH> | t = <DROP> | t = <EXISTS> | t = <EXPLAIN> | t = <GRANT> | t = <INSERT> | t = <KILL>
| t = <OPTIMIZE> | t = <RENAME> | t = <REVOKE> | t = <SELECT> | t = <SET> | t = <SHOW> | t = <SYSTEM>
| t = <TRUNCATE> | t = <UPDATE> | t = <USE> | t = <WATCH>
// others
| t = <CASE> | t = <DATE> | t = <DATABASE> | t = <DATABASES> | t = <DICTIONARY>
| t = <DICTIONARIES> | t = <DISTINCT> | t = <ELSE> | t = <END> | t = <EXCEPT>| t = <FUNCTION>
| t = <IF> | t = <INTERVAL> | t = <IS> | t = <LIVE> | t = <MATERIALIZED> | t = <POLICY>
| t = <PROFILE> | t = <QUOTA> | t = <REPLACE> | t = <ROLE> | t = <ROW> | t = <TEMPORARY>
| t = <TABLE> | t = <TABLES> | t = <THEN> | t = <TIES> | t = <TIMESTAMP> | t = <TOP> | t = <TOTALS>
| t = <VALUES> | t = <VIEW> | t = <WHEN> | t = <USER>
// interval
| t = <SECOND> | t = <MINUTE> | t = <HOUR> | t = <DAY> | t = <WEEK> | t = <MONTH> | t = <QUARTER> | t = <YEAR>
// values
| t = <INF> | t = <NAN> | t = <NULL>
)
{ return t; }
}
// keywords
TOKEN: {
<ALTER : <A> <L> <T> <E> <R>>
| <ATTACH : <A> <T> <T> <A> <C> <H> >
| <CHECK : <C> <H> <E> <C> <K> >
| <CREATE : <C> <R> <E> <A> <T> <E> >
| <DELETE : <D> <E> <L> <E> <T> <E> >
| <DESC : <D> <E> <S> <C> >
| <DESCRIBE : <D> <E> <S> <C> <R> <I> <B> <E>>
| <DETACH : <D> <E> <T> <A> <C> <H> >
| <DROP : <D> <R> <O> <P> >
| <EXISTS : <E> <X> <I> <S> <T> <S> >
| <EXPLAIN : <E> <X> <P> <L> <A> <I> <N> >
| <GRANT : <G> <R> <A> <N> <T> >
| <INSERT : <I> <N> <S> <E> <R> <T> >
| <KILL : <K> <I> <L> <L> >
| <OPTIMIZE : <O> <P> <T> <I> <M> <I> <Z> <E>>
| <RENAME : <R> <E> <N> <A> <M> <E> >
| <REVOKE : <R> <E> <V> <O> <K> <E> >
| <SELECT : <S> <E> <L> <E> <C> <T> >
| <SET : <S> <E> <T> >
| <SHOW : <S> <H> <O> <W> >
| <SYSTEM : <S> <Y> <S> <T> <E> <M> >
| <TRUNCATE : <T> <R> <U> <N> <C> <A> <T> <E>>
| <UPDATE : <U> <P> <D> <A> <T> <E> >
| <USE : <U> <S> <E> >
| <WATCH : <W> <A> <T> <C> <H> >
| <ALL : <A> <L> <L> >
| <AND : <A> <N> <D> >
| <APPLY : <A> <P> <P> <L> <Y> >
| <ARRAY : <A> <R> <R> <A> <Y> >
| <AS : <A> <S> >
| <ASOF : <A> <S> <O> <F> >
| <BETWEEN : <B> <E> <T> <W> <E> <E> <N> >
| <CASE : <C> <A> <S> <E> >
| <DATE : <D> <A> <T> <E> >
| <DATABASE : <D> <A> <T> <A> <B> <A> <S> <E> >
| <DATABASES: <D> <A> <T> <A> <B> <A> <S> <E> <S>>
| <DICTIONARY : <D> <I> <C> <T> <I> <O> <N> <A> <R> <Y> >
| <DICTIONARIES: <D> <I> <C> <T> <I> <O> <N> <A> <R> <I> <E> <S>>
| <DISTINCT : <D> <I> <S> <T> <I> <N> <C> <T> >
| <ELSE : <E> <L> <S> <E> >
| <END : <E> <N> <D> >
| <EXCEPT : <E> <X> <C> <E> <P> <T> >
| <FINAL : <F> <I> <N> <A> <L> >
| <FROM : <F> <R> <O> <M> >
| <FULL : <F> <U> <L> <L> >
| <FUNCTION : <F> <U> <N> <C> <T> <I> <O> <N> >
| <GLOBAL : <G> <L> <O> <B> <A> <L> >
| <GROUP : <G> <R> <O> <U> <P> >
| <HAVING : <H> <A> <V> <I> <N> <G> >
| <IF : <I> <F> >
| <ILIKE : <I> <L> <I> <K> <E> >
| <IN : <I> <N> >
| <INNER : <I> <N> <N> <E> <R> >
| <INTERVAL : <I> <N> <T> <E> <R> <V> <A> <L> >
| <INTO : <I> <N> <T> <O> >
| <IS : <I> <S> >
| <JOIN : <J> <O> <I> <N> >
| <LEFT : <L> <E> <F> <T> >
| <LIKE : <L> <I> <K> <E> >
| <LIMIT : <L> <I> <M> <I> <T> >
| <LIVE : <L> <I> <V> <E> >
| <MATERIALIZED: <M> <A> <T> <E> <R> <I> <A> <L> <I> <Z> <E> <D>>
| <NOT : <N> <O> <T> >
| <OFFSET : <O> <F> <F> <S> <E> <T> >
| <ON : <O> <N> >
| <OR : <O> <R> >
| <ORDER : <O> <R> <D> <E> <R> >
| <POLICY : <P> <O> <L> <I> <C> <Y> >
| <PREWHERE : <P> <R> <E> <W> <H> <E> <R> <E> >
| <PROFILE : <P> <R> <O> <F> <I> <L> <E> >
| <QUOTA : <Q> <U> <O> <T> <A> >
| <REPLACE : <R> <E> <P> <L> <A> <C> <E> >
| <ROLE : <R> <O> <L> <E> >
| <ROW : <R> <O> <W> >
| <RIGHT : <R> <I> <G> <H> <T> >
| <SAMPLE : <S> <A> <M> <P> <L> <E> >
| <SETTINGS : <S> <E> <T> <T> <I> <N> <G> <S> >
| <TEMPORARY: <T> <E> <M> <P> <O> <R> <A> <R> <Y>>
| <TABLE : <T> <A> <B> <L> <E> >
| <TABLES : <T> <A> <B> <L> <E> <S> >
| <THEN : <T> <H> <E> <N> >
| <TIES : <T> <I> <E> <S> >
| <TIMESTAMP: <T> <I> <M> <E> <S> <T> <A> <M> <P>>
| <TOP : <T> <O> <P> >
| <TOTALS : <T> <O> <T> <A> <L> <S> >
| <UNION : <U> <N> <I> <O> <N> >
| <USER : <U> <S> <E> <R> >
| <USING : <U> <S> <I> <N> <G> >
| <VALUES : <V> <A> <L> <U> <E> <S> >
| <VIEW : <V> <I> <E> <W> >
| <WHEN : <W> <H> <E> <N> >
| <WHERE : <W> <H> <E> <R> <E> >
| <WITH : <W> <I> <T> <H> >
| <SECOND : <S> <E> <C> <O> <N> <D> >
| <MINUTE : <M> <I> <N> <U> <T> <E> >
| <HOUR : <H> <O> <U> <R> >
| <DAY : <D> <A> <Y> >
| <WEEK : <W> <E> <E> <K> >
| <MONTH : <M> <O> <N> <T> <H> >
| <QUARTER : <Q> <U> <A> <R> <T> <E> <R> >
| <YEAR : <Y> <E> <A> <R> >
| <INF : <I> <N> <F> >
| <NAN : <N> <A> <N> >
| <NULL : <N> <U> <L> <L> >
}
// letters
TOKEN: {
<#A: ["a", "A"]>
| <#B: ["b", "B"]>
| <#C: ["c", "C"]>
| <#D: ["d", "D"]>
| <#E: ["e", "E"]>
| <#F: ["f", "F"]>
| <#G: ["g", "G"]>
| <#H: ["h", "H"]>
| <#I: ["i", "I"]>
| <#J: ["j", "J"]>
| <#K: ["k", "K"]>
| <#L: ["l", "L"]>
| <#M: ["m", "M"]>
| <#N: ["n", "N"]>
| <#O: ["o", "O"]>
| <#P: ["p", "P"]>
| <#Q: ["q", "Q"]>
| <#R: ["r", "R"]>
| <#S: ["s", "S"]>
| <#T: ["t", "T"]>
| <#U: ["u", "U"]>
| <#V: ["v", "V"]>
| <#W: ["w", "W"]>
| <#X: ["x", "X"]>
| <#Y: ["y", "Y"]>
| <#Z: ["z", "Z"]>
| <#LETTER: ["a"-"z", "A"-"Z"]>
}
// numbers
TOKEN: {
<#ZERO: "0">
| <#DEC_DIGIT: ["0"-"9"]> // including octal digit
| <#HEX_DIGIT: ["0"-"9", "a"-"f", "A"-"F"]>
}
// symbols
TOKEN: {
<ARROW: "->">
| <ASTERISK: "*">
| <AT: "@">
| <BACK_QUOTE: "`">
| <BACK_SLASH: "\\">
| <COLON: ":">
| <COMMA: ",">
| <CONCAT: "||">
| <DASH: "-">
| <DOLLAR: "$">
| <DOT: ".">
| <EQ_DOUBLE: "==">
| <EQ_SINGLE: "=">
| <GE: ">=">
| <GT: ">">
| <HASH: "#">
| <LBRACE: "{">
| <LBRACKET: "[">
| <LE: "<=">
| <LPAREN: "(">
| <LT: "<">
| <NOT_EQ: "!=" | "<>">
| <PERCENT: "%">
| <PLUS: "+">
| <QUERY: "?">
| <DOUBLE_QUOTE: "\"">
| <SINGLE_QUOTE: "'">
| <RBRACE: "}">
| <RBRACKET: "]">
| <RPAREN: ")">
| <SEMICOLON: ";">
| <SLASH: "/">
| <#UNDERSCORE: "_">
}
// string literal
TOKEN: {
<STRING_LITERAL: <SINGLE_QUOTE> (<BACK_SLASH> ~[] | ~["'", "\\"] | "''")* <SINGLE_QUOTE>>
}
TOKEN: {
<IDENTIFIER:
(<LETTER> | <UNDERSCORE> | <DOLLAR>) (<LETTER> | <UNDERSCORE> | <DOLLAR> | <DEC_DIGIT>)*
| (<DEC_DIGIT>)+ (
<E> (<LETTER> | <UNDERSCORE>)*
| (
<A> | <B> | <C> | <D> | <F> | <G> | <H> | <I> | <J> | <K> | <L> | <M> | <N>
| <O> | <P> | <Q> | <R> | <S> | <T> | <U> | <V> | <W> | <X> | <Y> | <Z>
| <UNDERSCORE>
) (<LETTER> | <UNDERSCORE> | <DEC_DIGIT>)*
)>
| <BACK_QUOTED_NAME: <BACK_QUOTE> (<BACK_SLASH> ~[] | ~["`", "\\"] | "``")* <BACK_QUOTE>>
| <DOUBLE_QUOTED_NAME: <DOUBLE_QUOTE> (<BACK_SLASH> ~[] | ~["\"", "\\"] | "\"\"")* <DOUBLE_QUOTE>>
}
TOKEN: {
<FLOATING_LITERAL:
<HEXADECIMAL_LITERAL> <DOT> (<HEXADECIMAL_LITERAL>)? (<P> | <E>) (<PLUS> | <DASH>)? <DECIMAL_LITERAL>
| <HEXADECIMAL_LITERAL> (<P> | <E>) (<PLUS> | <DASH>)? <DECIMAL_LITERAL>
| <DECIMAL_LITERAL> <DOT> (<DECIMAL_LITERAL>)? (<E> (<PLUS> | <DASH>)? <DECIMAL_LITERAL>)?
| <DOT> <DECIMAL_LITERAL> (<E> (<PLUS> | <DASH>)? <DECIMAL_LITERAL>)?
| <DECIMAL_LITERAL> <E> (<PLUS> | <DASH>)? <DECIMAL_LITERAL>>
}
TOKEN: { <DECIMAL_LITERAL: (<DEC_DIGIT>)+> }
TOKEN: { <HEXADECIMAL_LITERAL: <ZERO> <X> (<HEX_DIGIT>)+> }