Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
7214 lines (6927 sloc) 187 KB
/*
* 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.
*/
<@pp.dropOutputFile />
<@pp.changeOutputFile name="javacc/Parser.jj" />
options {
STATIC = false;
IGNORE_CASE = true;
UNICODE_INPUT = true;
}
PARSER_BEGIN(${parser.class})
package ${parser.package};
<#list parser.imports as importStr>
import ${importStr};
</#list>
import org.apache.calcite.avatica.util.Casing;
import org.apache.calcite.avatica.util.DateTimeUtils;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.runtime.CalciteContextException;
import org.apache.calcite.sql.JoinConditionType;
import org.apache.calcite.sql.JoinType;
import org.apache.calcite.sql.SqlAlter;
import org.apache.calcite.sql.SqlBinaryOperator;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlCharStringLiteral;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlDataTypeSpec;
import org.apache.calcite.sql.SqlDateLiteral;
import org.apache.calcite.sql.SqlDelete;
import org.apache.calcite.sql.SqlDescribeSchema;
import org.apache.calcite.sql.SqlDescribeTable;
import org.apache.calcite.sql.SqlDynamicParam;
import org.apache.calcite.sql.SqlExplain;
import org.apache.calcite.sql.SqlExplainFormat;
import org.apache.calcite.sql.SqlExplainLevel;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlInsert;
import org.apache.calcite.sql.SqlInsertKeyword;
import org.apache.calcite.sql.SqlIntervalLiteral;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.SqlJdbcDataTypeName;
import org.apache.calcite.sql.SqlJdbcFunctionCall;
import org.apache.calcite.sql.SqlJoin;
import org.apache.calcite.sql.SqlJsonConstructorNullClause;
import org.apache.calcite.sql.SqlJsonEncoding;
import org.apache.calcite.sql.SqlJsonExistsErrorBehavior;
import org.apache.calcite.sql.SqlJsonEmptyOrError;
import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior;
import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior;
import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlMatchRecognize;
import org.apache.calcite.sql.SqlMerge;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlNumericLiteral;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlOrderBy;
import org.apache.calcite.sql.SqlPostfixOperator;
import org.apache.calcite.sql.SqlPrefixOperator;
import org.apache.calcite.sql.SqlSampleSpec;
import org.apache.calcite.sql.SqlSelect;
import org.apache.calcite.sql.SqlSelectKeyword;
import org.apache.calcite.sql.SqlSetOption;
import org.apache.calcite.sql.SqlSnapshot;
import org.apache.calcite.sql.SqlTimeLiteral;
import org.apache.calcite.sql.SqlTimestampLiteral;
import org.apache.calcite.sql.SqlUnnestOperator;
import org.apache.calcite.sql.SqlUpdate;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.SqlWindow;
import org.apache.calcite.sql.SqlWith;
import org.apache.calcite.sql.SqlWithItem;
import org.apache.calcite.sql.fun.SqlCase;
import org.apache.calcite.sql.fun.OracleSqlOperatorTable;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.fun.SqlTrimFunction;
import org.apache.calcite.sql.parser.Span;
import org.apache.calcite.sql.parser.SqlAbstractParserImpl;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.parser.SqlParserImplFactory;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.parser.SqlParserUtil;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlConformance;
import org.apache.calcite.util.Glossary;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.SourceStringReader;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;
import org.slf4j.Logger;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import static org.apache.calcite.util.Static.RESOURCE;
/**
* SQL parser, generated from Parser.jj by JavaCC.
*
* <p>The public wrapper for this parser is {@link SqlParser}.
*/
public class ${parser.class} extends SqlAbstractParserImpl
{
private static final Logger LOGGER = CalciteTrace.getParserTracer();
// Can't use quoted literal because of a bug in how JavaCC translates
// backslash-backslash.
private static final char BACKSLASH = 0x5c;
private static final char DOUBLE_QUOTE = 0x22;
private static final String DQ = DOUBLE_QUOTE + "";
private static final String DQDQ = DQ + DQ;
private static Metadata metadata;
private Casing unquotedCasing;
private Casing quotedCasing;
private int identifierMaxLength;
private SqlConformance conformance;
/**
* {@link SqlParserImplFactory} implementation for creating parser.
*/
public static final SqlParserImplFactory FACTORY = new SqlParserImplFactory() {
public SqlAbstractParserImpl getParser(Reader reader) {
final ${parser.class} parser = new ${parser.class}(reader);
if (reader instanceof SourceStringReader) {
final String sql =
((SourceStringReader) reader).getSourceString();
parser.setOriginalSql(sql);
}
return parser;
}
};
public SqlParseException normalizeException(Throwable ex) {
try {
if (ex instanceof ParseException) {
ex = cleanupParseException((ParseException) ex);
}
return convertException(ex);
} catch (ParseException e) {
throw new AssertionError(e);
}
}
public Metadata getMetadata() {
synchronized (${parser.class}.class) {
if (metadata == null) {
metadata = new MetadataImpl(
new ${parser.class}(new java.io.StringReader("")));
}
return metadata;
}
}
public void setTabSize(int tabSize) {
jj_input_stream.setTabSize(tabSize);
}
public void switchTo(String stateName) {
int state = Arrays.asList(${parser.class}TokenManager.lexStateNames)
.indexOf(stateName);
token_source.SwitchTo(state);
}
public void setQuotedCasing(Casing quotedCasing) {
this.quotedCasing = quotedCasing;
}
public void setUnquotedCasing(Casing unquotedCasing) {
this.unquotedCasing = unquotedCasing;
}
public void setIdentifierMaxLength(int identifierMaxLength) {
this.identifierMaxLength = identifierMaxLength;
}
public void setConformance(SqlConformance conformance) {
this.conformance = conformance;
}
public SqlNode parseSqlExpressionEof() throws Exception {
return SqlExpressionEof();
}
public SqlNode parseSqlStmtEof() throws Exception {
return SqlStmtEof();
}
public SqlNodeList parseSqlStmtList() throws Exception {
return SqlStmtList();
}
private SqlNode extend(SqlNode table, SqlNodeList extendList) {
return SqlStdOperatorTable.EXTEND.createCall(
Span.of(table, extendList).pos(), table, extendList);
}
}
PARSER_END(${parser.class})
/***************************************
* Utility Codes for Semantic Analysis *
***************************************/
/* For Debug */
JAVACODE
void debug_message1() {
LOGGER.info("{} , {}", getToken(0).image, getToken(1).image);
}
JAVACODE String unquotedIdentifier() {
return SqlParserUtil.strip(getToken(0).image, null, null, null,
unquotedCasing);
}
/**
* Allows parser to be extended with new types of table references. The
* default implementation of this production is empty.
*/
SqlNode ExtendedTableRef() :
{
}
{
UnusedExtension()
{
return null;
}
}
/**
* Allows an OVER clause following a table expression as an extension to
* standard SQL syntax. The default implementation of this production is empty.
*/
SqlNode TableOverOpt() :
{
}
{
{
return null;
}
}
/*
* Parses dialect-specific keywords immediately following the SELECT keyword.
*/
void SqlSelectKeywords(List<SqlLiteral> keywords) :
{}
{
E()
}
/*
* Parses dialect-specific keywords immediately following the INSERT keyword.
*/
void SqlInsertKeywords(List<SqlLiteral> keywords) :
{}
{
E()
}
SqlNode ExtendedBuiltinFunctionCall() :
{
}
{
UnusedExtension()
{
return null;
}
}
/*
* Parse Floor/Ceil function parameters
*/
SqlNode FloorCeilOptions(Span s, boolean floorFlag) :
{
SqlNode node;
}
{
node = StandardFloorCeilOptions(s, floorFlag) {
return node;
}
}
/*
// This file contains the heart of a parser for SQL SELECT statements.
// code can be shared between various parsers (for example, a DDL parser and a
// DML parser) but is not a standalone JavaCC file. You need to prepend a
// parser declaration (such as that in Parser.jj).
*/
/* Epsilon */
JAVACODE
void E() {}
JAVACODE List startList(Object o)
{
List list = new ArrayList();
list.add(o);
return list;
}
/*
* NOTE jvs 6-Feb-2004: The straightforward way to implement the SQL grammar is
* to keep query expressions (SELECT, UNION, etc) separate from row expressions
* (+, LIKE, etc). However, this is not possible with an LL(k) parser, because
* both kinds of expressions allow parenthesization, so no fixed amount of left
* context is ever good enough. A sub-query can be a leaf in a row expression,
* and can include operators like UNION, so it's not even possible to use a
* syntactic lookahead rule like "look past an indefinite number of parentheses
* until you see SELECT, VALUES, or TABLE" (since at that point we still
* don't know whether we're parsing a sub-query like ((select ...) + x)
* vs. (select ... union select ...).
*
* The somewhat messy solution is to unify the two kinds of expression,
* and to enforce syntax rules using parameterized context. This
* is the purpose of the ExprContext parameter. It is passed to
* most expression productions, which check the expressions encountered
* against the context for correctness. When a query
* element like SELECT is encountered, the production calls
* checkQueryExpression, which will throw an exception if
* a row expression was expected instead. When a row expression like
* IN is encountered, the production calls checkNonQueryExpression
* instead. It is very important to understand how this works
* when modifying the grammar.
*
* The commingling of expressions results in some bogus ambiguities which are
* resolved with LOOKAHEAD hints. The worst example is comma. SQL allows both
* (WHERE x IN (1,2)) and (WHERE x IN (select ...)). This means when we parse
* the right-hand-side of an IN, we have to allow any kind of expression inside
* the parentheses. Now consider the expression "WHERE x IN(SELECT a FROM b
* GROUP BY c,d)". When the parser gets to "c,d" it doesn't know whether the
* comma indicates the end of the GROUP BY or the end of one item in an IN
* list. Luckily, we know that select and comma-list are mutually exclusive
* within IN, so we use maximal munch for the GROUP BY comma. However, this
* usage of hints could easily mask unintended ambiguities resulting from
* future changes to the grammar, making it very brittle.
*/
JAVACODE protected SqlParserPos getPos()
{
return new SqlParserPos(
token.beginLine,
token.beginColumn,
token.endLine,
token.endColumn);
}
/** Starts a span at the current position. */
JAVACODE Span span()
{
return Span.of(getPos());
}
JAVACODE void checkQueryExpression(ExprContext exprContext)
{
switch (exprContext) {
case ACCEPT_NON_QUERY:
case ACCEPT_SUB_QUERY:
case ACCEPT_CURSOR:
throw SqlUtil.newContextException(getPos(),
RESOURCE.illegalQueryExpression());
}
}
JAVACODE void checkNonQueryExpression(ExprContext exprContext)
{
switch (exprContext) {
case ACCEPT_QUERY:
throw SqlUtil.newContextException(getPos(),
RESOURCE.illegalNonQueryExpression());
}
}
/**
* Converts a ParseException (local to this particular instantiation
* of the parser) into a SqlParseException (common to all parsers).
*/
JAVACODE SqlParseException convertException(Throwable ex)
{
if (ex instanceof SqlParseException) {
return (SqlParseException) ex;
}
SqlParserPos pos = null;
int[][] expectedTokenSequences = null;
String[] tokenImage = null;
if (ex instanceof ParseException) {
ParseException pex = (ParseException) ex;
expectedTokenSequences = pex.expectedTokenSequences;
tokenImage = pex.tokenImage;
if (pex.currentToken != null) {
final Token token = pex.currentToken.next;
pos = new SqlParserPos(
token.beginLine,
token.beginColumn,
token.endLine,
token.endColumn);
}
} else if (ex instanceof TokenMgrError) {
TokenMgrError tme = (TokenMgrError) ex;
expectedTokenSequences = null;
tokenImage = null;
// Example:
// Lexical error at line 3, column 24. Encountered "#" after "a".
final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
"(?s)Lexical error at line ([0-9]+), column ([0-9]+).*");
java.util.regex.Matcher matcher = pattern.matcher(ex.getMessage());
if (matcher.matches()) {
int line = Integer.parseInt(matcher.group(1));
int column = Integer.parseInt(matcher.group(2));
pos = new SqlParserPos(line, column, line, column);
}
} else if (ex instanceof CalciteContextException) {
// CalciteContextException is the standard wrapper for exceptions
// produced by the validator, but in the parser, the standard is
// SqlParseException; so, strip it away. In case you were wondering,
// the CalciteContextException appears because the parser
// occasionally calls into validator-style code such as
// SqlSpecialOperator.reduceExpr.
CalciteContextException ece =
(CalciteContextException) ex;
pos = new SqlParserPos(
ece.getPosLine(),
ece.getPosColumn(),
ece.getEndPosLine(),
ece.getEndPosColumn());
ex = ece.getCause();
}
return new SqlParseException(
ex.getMessage(), pos, expectedTokenSequences, tokenImage, ex);
}
/**
* Removes or transforms misleading information from a parse exception.
*
* @param e dirty excn
*
* @return clean excn
*/
JAVACODE ParseException cleanupParseException(ParseException ex)
{
if (ex.expectedTokenSequences == null) {
return ex;
}
int iIdentifier = Arrays.asList(ex.tokenImage).indexOf("<IDENTIFIER>");
// Find all sequences in the error which contain identifier. For
// example,
// {<IDENTIFIER>}
// {A}
// {B, C}
// {D, <IDENTIFIER>}
// {D, A}
// {D, B}
//
// would yield
// {}
// {D}
boolean id = false;
final List<int[]> prefixList = new ArrayList<int[]>();
for (int i = 0; i < ex.expectedTokenSequences.length; ++i) {
int[] seq = ex.expectedTokenSequences[i];
int j = seq.length - 1;
int i1 = seq[j];
if (i1 == iIdentifier) {
int[] prefix = new int[j];
System.arraycopy(seq, 0, prefix, 0, j);
prefixList.add(prefix);
}
}
if (prefixList.isEmpty()) {
return ex;
}
int[][] prefixes = (int[][])
prefixList.toArray(new int[prefixList.size()][]);
// Since <IDENTIFIER> was one of the possible productions,
// we know that the parser will also have included all
// of the non-reserved keywords (which are treated as
// identifiers in non-keyword contexts). So, now we need
// to clean those out, since they're totally irrelevant.
final List<int[]> list = new ArrayList<int[]>();
Metadata metadata = getMetadata();
for (int i = 0; i < ex.expectedTokenSequences.length; ++i) {
int [] seq = ex.expectedTokenSequences[i];
String tokenImage = ex.tokenImage[seq[seq.length - 1]];
String token = SqlParserUtil.getTokenVal(tokenImage);
if (token == null || !metadata.isNonReservedKeyword(token)) {
list.add(seq);
continue;
}
boolean match = matchesPrefix(seq, prefixes);
if (!match) {
list.add(seq);
}
}
ex.expectedTokenSequences =
(int [][]) list.toArray(new int [list.size()][]);
return ex;
}
JAVACODE boolean matchesPrefix(int[] seq, int[][] prefixes)
{
nextPrefix:
for (int[] prefix : prefixes) {
if (seq.length == prefix.length + 1) {
for (int k = 0; k < prefix.length; k++) {
if (prefix[k] != seq[k]) {
continue nextPrefix;
}
}
return true;
}
}
return false;
}
/*****************************************
* Syntactical Descriptions *
*****************************************/
/**
* Parses either a row expression or a query expression with an optional
* ORDER BY.
*
* <p>Postgres syntax for limit:
*
* <blockquote><pre>
* [ LIMIT { count | ALL } ]
* [ OFFSET start ]</pre>
* </blockquote>
*
* <p>MySQL syntax for limit:
*
* <blockquote><pre>
* [ LIMIT { count | start, count } ]</pre>
* </blockquote>
*
* <p>SQL:2008 syntax for limit:
*
* <blockquote><pre>
* [ OFFSET start { ROW | ROWS } ]
* [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]</pre>
* </blockquote>
*/
SqlNode OrderedQueryOrExpr(ExprContext exprContext) :
{
SqlNode e;
SqlNodeList orderBy = null;
SqlNode start = null;
SqlNode count = null;
}
{
(
e = QueryOrExpr(exprContext)
)
[
// use the syntactic type of the expression we just parsed
// to decide whether ORDER BY makes sense
orderBy = OrderBy(e.isA(SqlKind.QUERY))
]
[
// Postgres-style syntax. "LIMIT ... OFFSET ..."
<LIMIT>
(
// MySQL-style syntax. "LIMIT start, count"
LOOKAHEAD(2)
start = UnsignedNumericLiteralOrParam()
<COMMA> count = UnsignedNumericLiteralOrParam() {
if (!this.conformance.isLimitStartCountAllowed()) {
throw new ParseException(RESOURCE.limitStartCountNotAllowed().str());
}
}
|
count = UnsignedNumericLiteralOrParam()
|
<ALL>
)
]
[
// ROW or ROWS is required in SQL:2008 but we make it optional
// because it is not present in Postgres-style syntax.
// If you specify both LIMIT start and OFFSET, OFFSET wins.
<OFFSET> start = UnsignedNumericLiteralOrParam() [ <ROW> | <ROWS> ]
]
[
// SQL:2008-style syntax. "OFFSET ... FETCH ...".
// If you specify both LIMIT and FETCH, FETCH wins.
<FETCH> ( <FIRST> | <NEXT> ) count = UnsignedNumericLiteralOrParam()
( <ROW> | <ROWS> ) <ONLY>
]
{
if (orderBy != null || start != null || count != null) {
if (orderBy == null) {
orderBy = SqlNodeList.EMPTY;
}
e = new SqlOrderBy(getPos(), e, orderBy, start, count);
}
return e;
}
}
/**
* Parses a leaf in a query expression (SELECT, VALUES or TABLE).
*/
SqlNode LeafQuery(ExprContext exprContext) :
{
SqlNode e;
}
{
{
// ensure a query is legal in this context
checkQueryExpression(exprContext);
}
e = SqlSelect() { return e; }
|
e = TableConstructor() { return e; }
|
e = ExplicitTable(getPos()) { return e; }
}
/**
* Parses a parenthesized query or single row expression.
*/
SqlNode ParenthesizedExpression(ExprContext exprContext) :
{
SqlNode e;
}
{
<LPAREN>
{
// we've now seen left paren, so queries inside should
// be allowed as sub-queries
switch (exprContext) {
case ACCEPT_SUB_QUERY:
exprContext = ExprContext.ACCEPT_NONCURSOR;
break;
case ACCEPT_CURSOR:
exprContext = ExprContext.ACCEPT_ALL;
break;
}
}
e = OrderedQueryOrExpr(exprContext)
<RPAREN>
{
return e;
}
}
/**
* Parses a parenthesized query or comma-list of row expressions.
*
* <p>REVIEW jvs 8-Feb-2004: There's a small hole in this production. It can be
* used to construct something like
*
* <blockquote><pre>
* WHERE x IN (select count(*) from t where c=d,5)</pre>
* </blockquote>
*
* <p>which should be illegal. The above is interpreted as equivalent to
*
* <blockquote><pre>
* WHERE x IN ((select count(*) from t where c=d),5)</pre>
* </blockquote>
*
* <p>which is a legal use of a sub-query. The only way to fix the hole is to
* be able to remember whether a subexpression was parenthesized or not, which
* means preserving parentheses in the SqlNode tree. This is probably
* desirable anyway for use in purely syntactic parsing applications (e.g. SQL
* pretty-printer). However, if this is done, it's important to also make
* isA() on the paren node call down to its operand so that we can
* always correctly discriminate a query from a row expression.
*/
SqlNodeList ParenthesizedQueryOrCommaList(
ExprContext exprContext) :
{
SqlNode e;
List<SqlNode> list;
ExprContext firstExprContext = exprContext;
final Span s;
}
{
<LPAREN>
{
// we've now seen left paren, so a query by itself should
// be interpreted as a sub-query
s = span();
switch (exprContext) {
case ACCEPT_SUB_QUERY:
firstExprContext = ExprContext.ACCEPT_NONCURSOR;
break;
case ACCEPT_CURSOR:
firstExprContext = ExprContext.ACCEPT_ALL;
break;
}
}
e = OrderedQueryOrExpr(firstExprContext)
{
list = startList(e);
}
(
<COMMA>
{
// a comma-list can't appear where only a query is expected
checkNonQueryExpression(exprContext);
}
e = Expression(exprContext)
{
list.add(e);
}
)*
<RPAREN>
{
return new SqlNodeList(list, s.end(this));
}
}
/** As ParenthesizedQueryOrCommaList, but allows DEFAULT
* in place of any of the expressions. For example,
* {@code (x, DEFAULT, null, DEFAULT)}. */
SqlNodeList ParenthesizedQueryOrCommaListWithDefault(
ExprContext exprContext) :
{
SqlNode e;
List<SqlNode> list;
ExprContext firstExprContext = exprContext;
final Span s;
}
{
<LPAREN>
{
// we've now seen left paren, so a query by itself should
// be interpreted as a sub-query
s = span();
switch (exprContext) {
case ACCEPT_SUB_QUERY:
firstExprContext = ExprContext.ACCEPT_NONCURSOR;
break;
case ACCEPT_CURSOR:
firstExprContext = ExprContext.ACCEPT_ALL;
break;
}
}
(
e = OrderedQueryOrExpr(firstExprContext)
|
e = Default()
)
{
list = startList(e);
}
(
<COMMA>
{
// a comma-list can't appear where only a query is expected
checkNonQueryExpression(exprContext);
}
(
e = Expression(exprContext)
|
e = Default()
)
{
list.add(e);
}
)*
<RPAREN>
{
return new SqlNodeList(list, s.end(this));
}
}
/**
* Parses function parameter lists including DISTINCT keyword recognition,
* DEFAULT, and named argument assignment.
*/
List FunctionParameterList(
ExprContext exprContext) :
{
SqlNode e = null;
List list = new ArrayList();
}
{
<LPAREN>
[
<DISTINCT> {
e = SqlSelectKeyword.DISTINCT.symbol(getPos());
}
|
<ALL> {
e = SqlSelectKeyword.ALL.symbol(getPos());
}
]
{
list.add(e);
}
Arg0(list, exprContext)
(
<COMMA> {
// a comma-list can't appear where only a query is expected
checkNonQueryExpression(exprContext);
}
Arg(list, exprContext)
)*
<RPAREN>
{
return list;
}
}
void Arg0(List list, ExprContext exprContext) :
{
SqlIdentifier name = null;
SqlNode e = null;
final ExprContext firstExprContext;
{
// we've now seen left paren, so queries inside should
// be allowed as sub-queries
switch (exprContext) {
case ACCEPT_SUB_QUERY:
firstExprContext = ExprContext.ACCEPT_NONCURSOR;
break;
case ACCEPT_CURSOR:
firstExprContext = ExprContext.ACCEPT_ALL;
break;
default:
firstExprContext = exprContext;
break;
}
}
}
{
[
LOOKAHEAD(2) name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT>
]
(
e = Default()
|
e = OrderedQueryOrExpr(firstExprContext)
)
{
if (e != null) {
if (name != null) {
e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall(
Span.of(name, e).pos(), e, name);
}
list.add(e);
}
}
}
void Arg(List list, ExprContext exprContext) :
{
SqlIdentifier name = null;
SqlNode e = null;
}
{
[
LOOKAHEAD(2) name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT>
]
(
e = Default()
|
e = Expression(exprContext)
)
{
if (e != null) {
if (name != null) {
e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall(
Span.of(name, e).pos(), e, name);
}
list.add(e);
}
}
}
SqlNode Default() : {}
{
<DEFAULT_> {
return SqlStdOperatorTable.DEFAULT.createCall(getPos());
}
}
/**
* Parses a query (SELECT, UNION, INTERSECT, EXCEPT, VALUES, TABLE) followed by
* the end-of-file symbol.
*/
SqlNode SqlQueryEof() :
{
SqlNode query;
}
{
query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
<EOF>
{ return query; }
}
/**
* Parses a list of SQL statements separated by semicolon.
* The semicolon is required between statements, but is
* optional at the end.
*/
SqlNodeList SqlStmtList() :
{
final List<SqlNode> stmtList = new ArrayList<SqlNode>();
SqlNode stmt;
}
{
stmt = SqlStmt() {
stmtList.add(stmt);
}
(
<SEMICOLON>
[
stmt = SqlStmt() {
stmtList.add(stmt);
}
]
)*
<EOF>
{
return new SqlNodeList(stmtList, Span.of(stmtList).pos());
}
}
/**
* Parses an SQL statement.
*/
SqlNode SqlStmt() :
{
SqlNode stmt;
}
{
(
<#-- Add methods to parse additional statements here -->
<#list parser.statementParserMethods as method>
LOOKAHEAD(2) stmt = ${method}
|
</#list>
stmt = SqlSetOption(Span.of(), null)
|
stmt = SqlAlter()
|
<#if parser.createStatementParserMethods?size != 0>
stmt = SqlCreate()
|
</#if>
<#if parser.dropStatementParserMethods?size != 0>
stmt = SqlDrop()
|
</#if>
stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
|
stmt = SqlExplain()
|
stmt = SqlDescribe()
|
stmt = SqlInsert()
|
stmt = SqlDelete()
|
stmt = SqlUpdate()
|
stmt = SqlMerge()
|
stmt = SqlProcedureCall()
)
{
return stmt;
}
}
/**
* Parses an SQL statement followed by the end-of-file symbol.
*/
SqlNode SqlStmtEof() :
{
SqlNode stmt;
}
{
stmt = SqlStmt() <EOF>
{
return stmt;
}
}
<#-- Add implementations of additional parser statement calls here -->
<#list parser.implementationFiles as file>
<#include "/@includes/"+file />
</#list>
/**
* Parses a leaf SELECT expression without ORDER BY.
*/
SqlSelect SqlSelect() :
{
final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
final SqlNodeList keywordList;
List<SqlNode> selectList;
final SqlNode fromClause;
final SqlNode where;
final SqlNodeList groupBy;
final SqlNode having;
final SqlNodeList windowDecls;
final Span s;
}
{
<SELECT>
{
s = span();
}
SqlSelectKeywords(keywords)
(
<STREAM> {
keywords.add(SqlSelectKeyword.STREAM.symbol(getPos()));
}
)?
(
<DISTINCT> {
keywords.add(SqlSelectKeyword.DISTINCT.symbol(getPos()));
}
| <ALL> {
keywords.add(SqlSelectKeyword.ALL.symbol(getPos()));
}
)?
{
keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos());
}
selectList = SelectList()
(
<FROM> fromClause = FromClause()
where = WhereOpt()
groupBy = GroupByOpt()
having = HavingOpt()
windowDecls = WindowOpt()
|
E() {
fromClause = null;
where = null;
groupBy = null;
having = null;
windowDecls = null;
}
)
{
return new SqlSelect(s.end(this), keywordList,
new SqlNodeList(selectList, Span.of(selectList).pos()),
fromClause, where, groupBy, having, windowDecls, null, null, null);
}
}
/*
* Abstract production:
*
* void SqlSelectKeywords(List keywords)
*
* Parses dialect-specific keywords immediately following the SELECT keyword.
*/
/**
* Parses an EXPLAIN PLAN statement.
*/
SqlNode SqlExplain() :
{
SqlNode stmt;
SqlExplainLevel detailLevel = SqlExplainLevel.EXPPLAN_ATTRIBUTES;
SqlExplain.Depth depth;
final SqlExplainFormat format;
}
{
<EXPLAIN> <PLAN>
[ detailLevel = ExplainDetailLevel() ]
depth = ExplainDepth()
(
LOOKAHEAD(2)
<AS> <XML> { format = SqlExplainFormat.XML; }
|
<AS> <JSON> { format = SqlExplainFormat.JSON; }
|
{ format = SqlExplainFormat.TEXT; }
)
<FOR> stmt = SqlQueryOrDml() {
return new SqlExplain(getPos(),
stmt,
detailLevel.symbol(SqlParserPos.ZERO),
depth.symbol(SqlParserPos.ZERO),
format.symbol(SqlParserPos.ZERO),
nDynamicParams);
}
}
/** Parses a query (SELECT or VALUES)
* or DML statement (INSERT, UPDATE, DELETE, MERGE). */
SqlNode SqlQueryOrDml() :
{
SqlNode stmt;
}
{
(
stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
|
stmt = SqlInsert()
|
stmt = SqlDelete()
|
stmt = SqlUpdate()
|
stmt = SqlMerge()
) { return stmt; }
}
/**
* Parses WITH TYPE | WITH IMPLEMENTATION | WITHOUT IMPLEMENTATION modifier for
* EXPLAIN PLAN.
*/
SqlExplain.Depth ExplainDepth() :
{
}
{
(
LOOKAHEAD(2)
<WITH> <TYPE>
{
return SqlExplain.Depth.TYPE;
}
|
<WITH> <IMPLEMENTATION>
{
return SqlExplain.Depth.PHYSICAL;
}
|
<WITHOUT> <IMPLEMENTATION>
{
return SqlExplain.Depth.LOGICAL;
}
|
{
return SqlExplain.Depth.PHYSICAL;
}
)
}
/**
* Parses INCLUDING ALL ATTRIBUTES modifier for EXPLAIN PLAN.
*/
SqlExplainLevel ExplainDetailLevel() :
{
SqlExplainLevel level = SqlExplainLevel.EXPPLAN_ATTRIBUTES;
}
{
(
<EXCLUDING> <ATTRIBUTES>
{
level = SqlExplainLevel.NO_ATTRIBUTES;
}
|
<INCLUDING>
[ <ALL> { level = SqlExplainLevel.ALL_ATTRIBUTES; } ]
<ATTRIBUTES>
{
}
)
{
return level;
}
}
/**
* Parses a DESCRIBE statement.
*/
SqlNode SqlDescribe() :
{
final Span s;
final SqlIdentifier table;
final SqlIdentifier column;
final SqlIdentifier id;
final SqlNode stmt;
}
{
<DESCRIBE> { s = span(); }
(
LOOKAHEAD(2) (<DATABASE> | <CATALOG> | <SCHEMA>)
id = CompoundIdentifier() {
// DESCRIBE DATABASE and DESCRIBE CATALOG currently do the same as
// DESCRIBE SCHEMA but should be different. See
// [CALCITE-1221] Implement DESCRIBE DATABASE, CATALOG, STATEMENT
return new SqlDescribeSchema(s.end(id), id);
}
|
// Use syntactic lookahead to determine whether a table name is coming.
// We do not allow SimpleIdentifier() because that includes <STATEMENT>.
LOOKAHEAD( <TABLE> | <IDENTIFIER> | <QUOTED_IDENTIFIER>
| <BACK_QUOTED_IDENTIFIER> | <BRACKET_QUOTED_IDENTIFIER> )
(<TABLE>)?
table = CompoundIdentifier()
(
column = SimpleIdentifier()
|
E() { column = null; }
) {
return new SqlDescribeTable(s.add(table).addIf(column).pos(),
table, column);
}
|
(LOOKAHEAD(1) <STATEMENT>)?
stmt = SqlQueryOrDml() {
// DESCRIBE STATEMENT currently does the same as EXPLAIN. See
// [CALCITE-1221] Implement DESCRIBE DATABASE, CATALOG, STATEMENT
final SqlExplainLevel detailLevel = SqlExplainLevel.EXPPLAN_ATTRIBUTES;
final SqlExplain.Depth depth = SqlExplain.Depth.PHYSICAL;
final SqlExplainFormat format = SqlExplainFormat.TEXT;
return new SqlExplain(s.end(stmt),
stmt,
detailLevel.symbol(SqlParserPos.ZERO),
depth.symbol(SqlParserPos.ZERO),
format.symbol(SqlParserPos.ZERO),
nDynamicParams);
}
)
}
/**
* Parses a CALL statement.
*/
SqlNode SqlProcedureCall() :
{
final Span s;
SqlNode routineCall;
}
{
<CALL> {
s = span();
}
routineCall = NamedRoutineCall(
SqlFunctionCategory.USER_DEFINED_PROCEDURE,
ExprContext.ACCEPT_SUB_QUERY)
{
return SqlStdOperatorTable.PROCEDURE_CALL.createCall(
s.end(routineCall), routineCall);
}
}
SqlNode NamedRoutineCall(
SqlFunctionCategory routineType,
ExprContext exprContext) :
{
SqlIdentifier name;
final List<SqlNode> list = new ArrayList<SqlNode>();
final Span s;
}
{
name = CompoundIdentifier() {
s = span();
}
<LPAREN>
[
Arg0(list, exprContext)
(
<COMMA> {
// a comma-list can't appear where only a query is expected
checkNonQueryExpression(exprContext);
}
Arg(list, exprContext)
)*
]
<RPAREN>
{
return createCall(name, s.end(this), routineType, null, list);
}
}
/**
* Parses an INSERT statement.
*/
SqlNode SqlInsert() :
{
final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
final SqlNodeList keywordList;
SqlNode table;
SqlNodeList extendList = null;
SqlNode source;
SqlNodeList columnList = null;
final Span s;
}
{
(
<INSERT>
|
<UPSERT> { keywords.add(SqlInsertKeyword.UPSERT.symbol(getPos())); }
)
{ s = span(); }
SqlInsertKeywords(keywords) {
keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos());
}
<INTO> table = CompoundIdentifier()
[
LOOKAHEAD(5)
[ <EXTEND> ]
extendList = ExtendList() {
table = extend(table, extendList);
}
]
[
LOOKAHEAD(2)
{ final Pair<SqlNodeList, SqlNodeList> p; }
p = ParenthesizedCompoundIdentifierList() {
if (p.right.size() > 0) {
table = extend(table, p.right);
}
if (p.left.size() > 0) {
columnList = p.left;
}
}
]
source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) {
return new SqlInsert(s.end(source), keywordList, table, source,
columnList);
}
}
/*
* Abstract production:
*
* void SqlInsertKeywords(List keywords)
*
* Parses dialect-specific keywords immediately following the INSERT keyword.
*/
/**
* Parses a DELETE statement.
*/
SqlNode SqlDelete() :
{
SqlNode table;
SqlNodeList extendList = null;
SqlIdentifier alias = null;
final SqlNode condition;
final Span s;
}
{
<DELETE> {
s = span();
}
<FROM> table = CompoundIdentifier()
[
[ <EXTEND> ]
extendList = ExtendList() {
table = extend(table, extendList);
}
]
[ [ <AS> ] alias = SimpleIdentifier() ]
condition = WhereOpt()
{
return new SqlDelete(s.add(table).addIf(extendList).addIf(alias)
.addIf(condition).pos(), table, condition, null, alias);
}
}
/**
* Parses an UPDATE statement.
*/
SqlNode SqlUpdate() :
{
SqlNode table;
SqlNodeList extendList = null;
SqlIdentifier alias = null;
SqlNode condition;
SqlNodeList sourceExpressionList;
SqlNodeList targetColumnList;
SqlIdentifier id;
SqlNode exp;
final Span s;
}
{
<UPDATE> { s = span(); }
table = CompoundIdentifier() {
targetColumnList = new SqlNodeList(s.pos());
sourceExpressionList = new SqlNodeList(s.pos());
}
[
[ <EXTEND> ]
extendList = ExtendList() {
table = extend(table, extendList);
}
]
[ [ <AS> ] alias = SimpleIdentifier() ]
<SET> id = SimpleIdentifier() {
targetColumnList.add(id);
}
<EQ> exp = Expression(ExprContext.ACCEPT_SUB_QUERY) {
// TODO: support DEFAULT also
sourceExpressionList.add(exp);
}
(
<COMMA>
id = SimpleIdentifier()
{
targetColumnList.add(id);
}
<EQ> exp = Expression(ExprContext.ACCEPT_SUB_QUERY)
{
sourceExpressionList.add(exp);
}
)*
condition = WhereOpt()
{
return new SqlUpdate(s.addAll(targetColumnList)
.addAll(sourceExpressionList).addIf(condition).pos(), table,
targetColumnList, sourceExpressionList, condition, null, alias);
}
}
/**
* Parses a MERGE statement.
*/
SqlNode SqlMerge() :
{
SqlNode table;
SqlNodeList extendList = null;
SqlIdentifier alias = null;
SqlNode sourceTableRef;
SqlNode condition;
SqlUpdate updateCall = null;
SqlInsert insertCall = null;
final Span s;
}
{
<MERGE> { s = span(); } <INTO> table = CompoundIdentifier()
[
[ <EXTEND> ]
extendList = ExtendList() {
table = extend(table, extendList);
}
]
[ [ <AS> ] alias = SimpleIdentifier() ]
<USING> sourceTableRef = TableRef()
<ON> condition = Expression(ExprContext.ACCEPT_SUB_QUERY)
(
LOOKAHEAD(2)
updateCall = WhenMatchedClause(table, alias)
[ insertCall = WhenNotMatchedClause(table) ]
|
insertCall = WhenNotMatchedClause(table)
)
{
return new SqlMerge(s.addIf(updateCall).addIf(insertCall).pos(), table,
condition, sourceTableRef, updateCall, insertCall, null, alias);
}
}
SqlUpdate WhenMatchedClause(SqlNode table, SqlIdentifier alias) :
{
SqlIdentifier id;
final Span s;
final SqlNodeList updateColumnList = new SqlNodeList(SqlParserPos.ZERO);
SqlNode exp;
final SqlNodeList updateExprList = new SqlNodeList(SqlParserPos.ZERO);
}
{
<WHEN> { s = span(); } <MATCHED> <THEN>
<UPDATE> <SET> id = SimpleIdentifier() {
updateColumnList.add(id);
}
<EQ> exp = Expression(ExprContext.ACCEPT_SUB_QUERY) {
updateExprList.add(exp);
}
(
<COMMA>
id = SimpleIdentifier() {
updateColumnList.add(id);
}
<EQ> exp = Expression(ExprContext.ACCEPT_SUB_QUERY) {
updateExprList.add(exp);
}
)*
{
return new SqlUpdate(s.addAll(updateExprList).pos(), table,
updateColumnList, updateExprList, null, null, alias);
}
}
SqlInsert WhenNotMatchedClause(SqlNode table) :
{
final Span insertSpan, valuesSpan;
final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
final SqlNodeList keywordList;
SqlNodeList insertColumnList = null;
SqlNode rowConstructor;
SqlNode insertValues;
}
{
<WHEN> <NOT> <MATCHED> <THEN> <INSERT> {
insertSpan = span();
}
SqlInsertKeywords(keywords) {
keywordList = new SqlNodeList(keywords, insertSpan.end(this));
}
[
LOOKAHEAD(2)
insertColumnList = ParenthesizedSimpleIdentifierList()
]
[ <LPAREN> ]
<VALUES> { valuesSpan = span(); }
rowConstructor = RowConstructor()
[ <RPAREN> ]
{
// TODO zfong 5/26/06: note that extra parentheses are accepted above
// around the VALUES clause as a hack for unparse, but this is
// actually invalid SQL; should fix unparse
insertValues = SqlStdOperatorTable.VALUES.createCall(
valuesSpan.end(this), rowConstructor);
return new SqlInsert(insertSpan.end(this), keywordList,
table, insertValues, insertColumnList);
}
}
/**
* Parses the select list of a SELECT statement.
*/
List<SqlNode> SelectList() :
{
final List<SqlNode> list = new ArrayList<SqlNode>();
SqlNode item;
}
{
item = SelectItem() {
list.add(item);
}
(
<COMMA> item = SelectItem() {
list.add(item);
}
)*
{
return list;
}
}
/**
* Parses one item in a select list.
*/
SqlNode SelectItem() :
{
SqlNode e;
final SqlIdentifier id;
}
{
e = SelectExpression()
[
[ <AS> ]
id = SimpleIdentifier() {
e = SqlStdOperatorTable.AS.createCall(span().end(e), e, id);
}
]
{
return e;
}
}
/**
* Parses one unaliased expression in a select list.
*/
SqlNode SelectExpression() :
{
SqlNode e;
}
{
<STAR> {
return SqlIdentifier.star(getPos());
}
|
e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
return e;
}
}
SqlLiteral Natural() :
{
}
{
<NATURAL> { return SqlLiteral.createBoolean(true, getPos()); }
|
{ return SqlLiteral.createBoolean(false, getPos()); }
}
SqlLiteral JoinType() :
{
JoinType joinType;
}
{
(
<JOIN> { joinType = JoinType.INNER; }
|
<INNER> <JOIN> { joinType = JoinType.INNER; }
|
<LEFT> [ <OUTER> ] <JOIN> { joinType = JoinType.LEFT; }
|
<RIGHT> [ <OUTER> ] <JOIN> { joinType = JoinType.RIGHT; }
|
<FULL> [ <OUTER> ] <JOIN> { joinType = JoinType.FULL; }
|
<CROSS> <JOIN> { joinType = JoinType.CROSS; }
<#list parser.joinTypes as method>
|
joinType = ${method}()
</#list>
)
{
return joinType.symbol(getPos());
}
}
/** Matches "LEFT JOIN t ON ...", "RIGHT JOIN t USING ...", "JOIN t". */
SqlNode JoinTable(SqlNode e) :
{
SqlNode e2, condition;
final SqlLiteral natural, joinType, on, using;
SqlNodeList list;
}
{
natural = Natural()
joinType = JoinType()
e2 = TableRef()
(
<ON> { on = JoinConditionType.ON.symbol(getPos()); }
condition = Expression(ExprContext.ACCEPT_SUB_QUERY) {
return new SqlJoin(joinType.getParserPosition(),
e,
natural,
joinType,
e2,
on,
condition);
}
|
<USING> { using = JoinConditionType.USING.symbol(getPos()); }
list = ParenthesizedSimpleIdentifierList() {
return new SqlJoin(joinType.getParserPosition(),
e,
natural,
joinType,
e2,
using,
new SqlNodeList(list.getList(), Span.of(using).end(this)));
}
|
{
return new SqlJoin(joinType.getParserPosition(),
e,
natural,
joinType,
e2,
JoinConditionType.NONE.symbol(joinType.getParserPosition()),
null);
}
)
}
// TODO jvs 15-Nov-2003: SQL standard allows parentheses in the FROM list for
// building up non-linear join trees (e.g. OUTER JOIN two tables, and then INNER
// JOIN the result). Also note that aliases on parenthesized FROM expressions
// "hide" all table names inside the parentheses (without aliases, they're
// visible).
//
// We allow CROSS JOIN to have a join condition, even though that is not valid
// SQL; the validator will catch it.
/**
* Parses the FROM clause for a SELECT.
*
* <p>FROM is mandatory in standard SQL, optional in dialects such as MySQL,
* PostgreSQL. The parser allows SELECT without FROM, but the validator fails
* if conformance is, say, STRICT_2003.
*/
SqlNode FromClause() :
{
SqlNode e, e2, condition;
SqlLiteral natural, joinType, joinConditionType;
SqlNodeList list;
SqlParserPos pos;
}
{
e = TableRef()
(
LOOKAHEAD(2)
(
// Decide whether to read a JOIN clause or a comma, or to quit having
// seen a single entry FROM clause like 'FROM emps'. See comments
// elsewhere regarding <COMMA> lookahead.
//
// And LOOKAHEAD(3) is needed here rather than a LOOKAHEAD(2). Because currently JavaCC
// calculates minimum lookahead count incorrectly for choice that contains zero size
// child. For instance, with the generated code, "LOOKAHEAD(2, Natural(), JoinType())"
// returns true immediately if it sees a single "<CROSS>" token. Where we expect
// the lookahead succeeds after "<CROSS> <APPLY>".
//
// For more information about the issue, see https://github.com/javacc/javacc/issues/86
LOOKAHEAD(3)
natural = Natural()
joinType = JoinType()
e2 = TableRef()
(
<ON> {
joinConditionType = JoinConditionType.ON.symbol(getPos());
}
condition = Expression(ExprContext.ACCEPT_SUB_QUERY) {
e = new SqlJoin(joinType.getParserPosition(),
e,
natural,
joinType,
e2,
joinConditionType,
condition);
}
|
<USING> {
joinConditionType = JoinConditionType.USING.symbol(getPos());
}
list = ParenthesizedSimpleIdentifierList() {
e = new SqlJoin(joinType.getParserPosition(),
e,
natural,
joinType,
e2,
joinConditionType,
new SqlNodeList(list.getList(), Span.of(joinConditionType).end(this)));
}
|
{
e = new SqlJoin(joinType.getParserPosition(),
e,
natural,
joinType,
e2,
JoinConditionType.NONE.symbol(joinType.getParserPosition()),
null);
}
)
|
// NOTE jvs 6-Feb-2004: See comments at top of file for why
// hint is necessary here. I had to use this special semantic
// lookahead form to get JavaCC to shut up, which makes
// me even more uneasy.
//LOOKAHEAD({true})
<COMMA> { joinType = JoinType.COMMA.symbol(getPos()); }
e2 = TableRef() {
e = new SqlJoin(joinType.getParserPosition(),
e,
SqlLiteral.createBoolean(false, joinType.getParserPosition()),
joinType,
e2,
JoinConditionType.NONE.symbol(SqlParserPos.ZERO),
null);
}
|
<CROSS> { joinType = JoinType.CROSS.symbol(getPos()); } <APPLY>
e2 = TableRef2(true) {
if (!this.conformance.isApplyAllowed()) {
throw new ParseException(RESOURCE.applyNotAllowed().str());
}
e = new SqlJoin(joinType.getParserPosition(),
e,
SqlLiteral.createBoolean(false, joinType.getParserPosition()),
joinType,
e2,
JoinConditionType.NONE.symbol(SqlParserPos.ZERO),
null);
}
|
<OUTER> { joinType = JoinType.LEFT.symbol(getPos()); } <APPLY>
e2 = TableRef2(true) {
if (!this.conformance.isApplyAllowed()) {
throw new ParseException(RESOURCE.applyNotAllowed().str());
}
e = new SqlJoin(joinType.getParserPosition(),
e,
SqlLiteral.createBoolean(false, joinType.getParserPosition()),
joinType,
e2,
JoinConditionType.ON.symbol(SqlParserPos.ZERO),
SqlLiteral.createBoolean(true, joinType.getParserPosition()));
}
)
)*
{
return e;
}
}
/**
* Parses a table reference in a FROM clause, not lateral unless LATERAL
* is explicitly specified.
*/
SqlNode TableRef() :
{
final SqlNode e;
}
{
e = TableRef2(false) { return e; }
}
/**
* Parses a table reference in a FROM clause.
*/
SqlNode TableRef2(boolean lateral) :
{
SqlNode tableRef;
final SqlNode over;
final SqlNode snapshot;
final SqlNode match;
SqlNodeList extendList = null;
final SqlIdentifier alias;
final Span s, s2;
SqlNodeList args;
SqlNode sample;
boolean isBernoulli;
SqlNumericLiteral samplePercentage;
boolean isRepeatable = false;
int repeatableSeed = 0;
SqlNodeList columnAliasList = null;
SqlUnnestOperator unnestOp = SqlStdOperatorTable.UNNEST;
}
{
(
LOOKAHEAD(2)
tableRef = CompoundIdentifier()
[
[ <EXTEND> ]
extendList = ExtendList() {
tableRef = extend(tableRef, extendList);
}
]
over = TableOverOpt() {
if (over != null) {
tableRef = SqlStdOperatorTable.OVER.createCall(
getPos(), tableRef, over);
}
}
[
snapshot = Snapshot(tableRef) {
tableRef = SqlStdOperatorTable.LATERAL.createCall(
getPos(), snapshot);
}
]
[
tableRef = MatchRecognize(tableRef)
]
|
LOOKAHEAD(2)
[ <LATERAL> { lateral = true; } ]
tableRef = ParenthesizedExpression(ExprContext.ACCEPT_QUERY)
over = TableOverOpt()
{
if (over != null) {
tableRef = SqlStdOperatorTable.OVER.createCall(
getPos(), tableRef, over);
}
if (lateral) {
tableRef = SqlStdOperatorTable.LATERAL.createCall(
getPos(), tableRef);
}
}
[
tableRef = MatchRecognize(tableRef)
]
|
<UNNEST> { s = span(); }
args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY)
[
<WITH> <ORDINALITY> {
unnestOp = SqlStdOperatorTable.UNNEST_WITH_ORDINALITY;
}
]
{
tableRef = unnestOp.createCall(s.end(this), args.toArray());
}
|
[<LATERAL> { lateral = true; } ]
<TABLE> { s = span(); } <LPAREN>
tableRef = TableFunctionCall(s.pos())
<RPAREN>
{
if (lateral) {
tableRef = SqlStdOperatorTable.LATERAL.createCall(
s.end(this), tableRef);
}
}
|
tableRef = ExtendedTableRef()
)
[
[ <AS> ] alias = SimpleIdentifier()
[ columnAliasList = ParenthesizedSimpleIdentifierList() ]
{
if (columnAliasList == null) {
tableRef = SqlStdOperatorTable.AS.createCall(
Span.of(tableRef).end(this), tableRef, alias);
} else {
List<SqlNode> idList = new ArrayList<SqlNode>();
idList.add(tableRef);
idList.add(alias);
idList.addAll(columnAliasList.getList());
tableRef = SqlStdOperatorTable.AS.createCall(
Span.of(tableRef).end(this), idList);
}
}
]
[
<TABLESAMPLE> { s2 = span(); }
(
<SUBSTITUTE> <LPAREN> sample = StringLiteral() <RPAREN>
{
String sampleName =
SqlLiteral.unchain(sample).getValueAs(String.class);
SqlSampleSpec sampleSpec = SqlSampleSpec.createNamed(sampleName);
final SqlLiteral sampleLiteral =
SqlLiteral.createSample(sampleSpec, s2.end(this));
tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall(
s2.add(tableRef).end(this), tableRef, sampleLiteral);
}
|
(
<BERNOULLI>
{
isBernoulli = true;
}
|
<SYSTEM>
{
isBernoulli = false;
}
)
<LPAREN> samplePercentage = UnsignedNumericLiteral() <RPAREN>
[
<REPEATABLE> <LPAREN> repeatableSeed = IntLiteral() <RPAREN>
{
isRepeatable = true;
}
]
{
final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100L);
BigDecimal rate = samplePercentage.bigDecimalValue();
if (rate.compareTo(BigDecimal.ZERO) < 0
|| rate.compareTo(ONE_HUNDRED) > 0)
{
throw new ParseException(RESOURCE.invalidSampleSize().str());
}
// Treat TABLESAMPLE(0) and TABLESAMPLE(100) as no table
// sampling at all. Not strictly correct: TABLESAMPLE(0)
// should produce no output, but it simplifies implementation
// to know that some amount of sampling will occur.
// In practice values less than ~1E-43% are treated as 0.0 and
// values greater than ~99.999997% are treated as 1.0
float fRate = rate.divide(ONE_HUNDRED).floatValue();
if (fRate > 0.0f && fRate < 1.0f) {
SqlSampleSpec tableSampleSpec =
isRepeatable
? SqlSampleSpec.createTableSample(
isBernoulli, fRate, repeatableSeed)
: SqlSampleSpec.createTableSample(isBernoulli, fRate);
SqlLiteral tableSampleLiteral =
SqlLiteral.createSample(tableSampleSpec, s2.end(this));
tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall(
s2.end(this), tableRef, tableSampleLiteral);
}
}
)
]
{
return tableRef;
}
}
SqlNodeList ExtendList() :
{
final Span s;
List<SqlNode> list = new ArrayList<SqlNode>();
}
{
<LPAREN> { s = span(); }
ColumnType(list)
(
<COMMA> ColumnType(list)
)*
<RPAREN> {
return new SqlNodeList(list, s.end(this));
}
}
void ColumnType(List<SqlNode> list) :
{
SqlIdentifier name;
SqlDataTypeSpec type;
boolean nullable = true;
}
{
name = CompoundIdentifier()
type = DataType()
[
<NOT> <NULL> {
nullable = false;
}
]
{
list.add(name);
list.add(type.withNullable(nullable));
}
}
/**
* Parses a compound identifier with optional type.
*/
void CompoundIdentifierType(List<SqlNode> list, List<SqlNode> extendList) :
{
final SqlIdentifier name;
SqlDataTypeSpec type = null;
boolean nullable = true;
}
{
name = CompoundIdentifier()
[
type = DataType() {
if (!this.conformance.allowExtend()) {
throw new ParseException(RESOURCE.extendNotAllowed().str());
}
}
[
<NOT> <NULL> {
nullable = false;
}
]
]
{
if (type != null) {
extendList.add(name);
extendList.add(type.withNullable(nullable));
}
list.add(name);
}
}
SqlNode TableFunctionCall(SqlParserPos pos) :
{
SqlNode call;
SqlFunctionCategory funcType = SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION;
}
{
[
<SPECIFIC>
{
funcType = SqlFunctionCategory.USER_DEFINED_TABLE_SPECIFIC_FUNCTION;
}
]
call = NamedRoutineCall(funcType, ExprContext.ACCEPT_CURSOR)
{
return SqlStdOperatorTable.COLLECTION_TABLE.createCall(pos, call);
}
}
/**
* Abstract production:
* SqlNode ExtendedTableRef()
*
* <p>Allows parser to be extended with new types of table references. The
* default implementation of this production is empty.
*/
/*
* Abstract production:
*
* SqlNode TableOverOpt()
*
* Allows an OVER clause following a table expression as an extension to
* standard SQL syntax. The default implementation of this production is empty.
*/
/**
* Parses an explicit TABLE t reference.
*/
SqlNode ExplicitTable(SqlParserPos pos) :
{
SqlNode tableRef;
}
{
<TABLE> tableRef = CompoundIdentifier()
{
return SqlStdOperatorTable.EXPLICIT_TABLE.createCall(pos, tableRef);
}
}
/**
* Parses a VALUES leaf query expression.
*/
SqlNode TableConstructor() :
{
SqlNodeList rowConstructorList;
final Span s;
}
{
<VALUES> { s = span(); }
rowConstructorList = RowConstructorList(s)
{
return SqlStdOperatorTable.VALUES.createCall(
s.end(this), rowConstructorList.toArray());
}
}
/**
* Parses one or more rows in a VALUES expression.
*/
SqlNodeList RowConstructorList(Span s) :
{
List<SqlNode> list = new ArrayList<SqlNode>();
SqlNode rowConstructor;
}
{
rowConstructor = RowConstructor() { list.add(rowConstructor); }
(
LOOKAHEAD(2)
<COMMA> rowConstructor = RowConstructor() { list.add(rowConstructor); }
)*
{
return new SqlNodeList(list, s.end(this));
}
}
/**
* Parses a row constructor in the context of a VALUES expression.
*/
SqlNode RowConstructor() :
{
SqlNodeList valueList;
SqlNode value;
final Span s;
}
{
// hints are necessary here due to common LPAREN prefixes
(
// TODO jvs 8-Feb-2004: extra parentheses are accepted here as a hack
// for unparse, but this is actually invalid SQL; should
// fix unparse
LOOKAHEAD(3)
<LPAREN> { s = span(); }
<ROW>
valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR)
<RPAREN> { s.add(this); }
|
LOOKAHEAD(3)
(
<ROW> { s = span(); }
|
{ s = Span.of(); }
)
valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR)
|
value = Expression(ExprContext.ACCEPT_NONCURSOR)
{
// NOTE: A bare value here is standard SQL syntax, believe it or
// not. Taken together with multi-row table constructors, it leads
// to very easy mistakes if you forget the parentheses on a
// single-row constructor. This is also the reason for the
// LOOKAHEAD in RowConstructorList(). It would be so much more
// reasonable to require parentheses. Sigh.
s = Span.of(value);
valueList = new SqlNodeList(Collections.singletonList(value),
value.getParserPosition());
}
)
{
// REVIEW jvs 8-Feb-2004: Should we discriminate between scalar
// sub-queries inside of ROW and row sub-queries? The standard does,
// but the distinction seems to be purely syntactic.
return SqlStdOperatorTable.ROW.createCall(s.end(valueList),
valueList.toArray());
}
}
/**
* Parses the optional WHERE clause for SELECT, DELETE, and UPDATE.
*/
SqlNode WhereOpt() :
{
SqlNode condition;
}
{
<WHERE> condition = Expression(ExprContext.ACCEPT_SUB_QUERY)
{
return condition;
}
|
{
return null;
}
}
/**
* Parses the optional GROUP BY clause for SELECT.
*/
SqlNodeList GroupByOpt() :
{
List<SqlNode> list = new ArrayList<SqlNode>();
final Span s;
}
{
<GROUP> { s = span(); }
<BY> list = GroupingElementList() {
return new SqlNodeList(list, s.addAll(list).pos());
}
|
{
return null;
}
}
List<SqlNode> GroupingElementList() :
{
List<SqlNode> list = new ArrayList<SqlNode>();
SqlNode e;
}
{
e = GroupingElement() { list.add(e); }
(
LOOKAHEAD(2)
<COMMA>
e = GroupingElement() { list.add(e); }
)*
{ return list; }
}
SqlNode GroupingElement() :
{
List<SqlNode> list;
final SqlNodeList nodes;
final SqlNode e;
final Span s;
}
{
LOOKAHEAD(2)
<GROUPING> { s = span(); }
<SETS> <LPAREN> list = GroupingElementList() <RPAREN> {
return SqlStdOperatorTable.GROUPING_SETS.createCall(s.end(this), list);
}
| <ROLLUP> { s = span(); }
<LPAREN> nodes = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY)
<RPAREN> {
return SqlStdOperatorTable.ROLLUP.createCall(s.end(this),
nodes.getList());
}
| <CUBE> { s = span(); }
<LPAREN> nodes = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY)
<RPAREN> {
return SqlStdOperatorTable.CUBE.createCall(s.end(this),
nodes.getList());
}
| LOOKAHEAD(3)
<LPAREN> <RPAREN> {
return new SqlNodeList(getPos());
}
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
return e;
}
}
/**
* Parses a list of expressions separated by commas.
*/
SqlNodeList ExpressionCommaList(
final Span s,
ExprContext exprContext) :
{
List<SqlNode> list;
SqlNode e;
}
{
e = Expression(exprContext)
{
list = startList(e);
}
(
// NOTE jvs 6-Feb-2004: See comments at top of file for why
// hint is necessary here.
LOOKAHEAD(2)
<COMMA> e = Expression(ExprContext.ACCEPT_SUB_QUERY)
{
list.add(e);
}
)*
{
return new SqlNodeList(list, s.addAll(list).pos());
}
}
/**
* Parses the optional HAVING clause for SELECT.
*/
SqlNode HavingOpt() :
{
SqlNode e;
}
{
<HAVING> e = Expression(ExprContext.ACCEPT_SUB_QUERY) { return e; }
|
{ return null; }
}
/**
* Parses the optional WINDOW clause for SELECT
*/
SqlNodeList WindowOpt() :
{
SqlIdentifier id;
SqlWindow e;
List<SqlNode> list;
final Span s;
}
{
<WINDOW> { s = span(); }
id = SimpleIdentifier() <AS> e = WindowSpecification() {
e.setDeclName(id);
list = startList(e);
}
(
// NOTE jhyde 22-Oct-2004: See comments at top of file for why
// hint is necessary here.
LOOKAHEAD(2)
<COMMA> id = SimpleIdentifier() <AS> e = WindowSpecification() {
e.setDeclName(id);
list.add(e);
}
)*
{
return new SqlNodeList(list, s.addAll(list).pos());
}
|
{
return null;
}
}
/**
* Parses a window specification.
*/
SqlWindow WindowSpecification() :
{
SqlIdentifier id;
List list;
SqlNodeList partitionList;
SqlNodeList orderList;
SqlLiteral isRows = SqlLiteral.createBoolean(false, SqlParserPos.ZERO);
SqlNode lowerBound = null, upperBound = null;
SqlParserPos startPos;
final Span s, s1, s2;
SqlLiteral allowPartial = null;
}
{
<LPAREN> { s = span(); }
(
id = SimpleIdentifier()
|
{ id = null; }
)
(
<PARTITION> { s1 = span(); }
<BY>
partitionList = ExpressionCommaList(s1, ExprContext.ACCEPT_NON_QUERY)
|
{ partitionList = SqlNodeList.EMPTY; }
)
(
orderList = OrderBy(true)
|
{ orderList = SqlNodeList.EMPTY; }
)
[
(
<ROWS> { isRows = SqlLiteral.createBoolean(true, getPos()); }
|
<RANGE> { isRows = SqlLiteral.createBoolean(false, getPos()); }
)
(
<BETWEEN> lowerBound = WindowRange()
<AND> upperBound = WindowRange()
|
lowerBound = WindowRange()
)
]
[
<ALLOW> { s2 = span(); } <PARTIAL> {
allowPartial = SqlLiteral.createBoolean(true, s2.end(this));
}
|
<DISALLOW> { s2 = span(); } <PARTIAL> {
allowPartial = SqlLiteral.createBoolean(false, s2.end(this));
}
]
<RPAREN>
{
return SqlWindow.create(
null, id, partitionList, orderList,
isRows, lowerBound, upperBound, allowPartial, s.end(this));
}
}
SqlNode WindowRange() :
{
final SqlNode e;
final Span s;
}
{
LOOKAHEAD(2)
<CURRENT> { s = span(); } <ROW> {
return SqlWindow.createCurrentRow(s.end(this));
}
|
LOOKAHEAD(2)
<UNBOUNDED> { s = span(); }
(
<PRECEDING> {
return SqlWindow.createUnboundedPreceding(s.end(this));
}
|
<FOLLOWING> {
return SqlWindow.createUnboundedFollowing(s.end(this));
}
)
|
e = Expression(ExprContext.ACCEPT_NON_QUERY)
(
<PRECEDING> {
return SqlWindow.createPreceding(e, getPos());
}
|
<FOLLOWING> {
return SqlWindow.createFollowing(e, getPos());
}
)
}
/**
* Parses an ORDER BY clause.
*/
SqlNodeList OrderBy(boolean accept) :
{
List<SqlNode> list;
SqlNode e;
final Span s;
}
{
<ORDER> {
s = span();
if (!accept) {
// Someone told us ORDER BY wasn't allowed here. So why
// did they bother calling us? To get the correct
// parser position for error reporting.
throw SqlUtil.newContextException(s.pos(), RESOURCE.illegalOrderBy());
}
}
<BY> e = OrderItem() {
list = startList(e);
}
(
// NOTE jvs 6-Feb-2004: See comments at top of file for why
// hint is necessary here.
LOOKAHEAD(2) <COMMA> e = OrderItem() { list.add(e); }
)*
{
return new SqlNodeList(list, s.addAll(list).pos());
}
}
/**
* Parses one list item in an ORDER BY clause.
*/
SqlNode OrderItem() :
{
SqlNode e;
}
{
e = Expression(ExprContext.ACCEPT_SUB_QUERY)
(
<ASC>
| <DESC> {
e = SqlStdOperatorTable.DESC.createCall(getPos(), e);
}
)?
(
LOOKAHEAD(2)
<NULLS> <FIRST> {
e = SqlStdOperatorTable.NULLS_FIRST.createCall(getPos(), e);
}
|
<NULLS> <LAST> {
e = SqlStdOperatorTable.NULLS_LAST.createCall(getPos(), e);
}
)?
{
return e;
}
}
/**
* Parses a FOR SYSTEM_TIME clause following a table expression.
*/
SqlSnapshot Snapshot(SqlNode tableRef) :
{
final Span s;
final SqlNode e;
}
{
<FOR> { s = span(); } <SYSTEM_TIME> <AS> <OF>
e = Expression(ExprContext.ACCEPT_NON_QUERY) {
return new SqlSnapshot(s.end(this), tableRef, e);
}
}
/**
* Parses a MATCH_RECOGNIZE clause following a table expression.
*/
SqlMatchRecognize MatchRecognize(SqlNode tableRef) :
{
final Span s, s0, s1, s2;
SqlNodeList measureList = SqlNodeList.EMPTY;
SqlNodeList partitionList = SqlNodeList.EMPTY;
SqlNodeList orderList = SqlNodeList.EMPTY;
SqlNode pattern;
SqlLiteral interval;
SqlNodeList patternDefList;
final SqlNode after;
SqlParserPos pos;
final SqlNode var;
final SqlLiteral rowsPerMatch;
SqlNodeList subsetList = SqlNodeList.EMPTY;
SqlLiteral isStrictStarts = SqlLiteral.createBoolean(false, getPos());
SqlLiteral isStrictEnds = SqlLiteral.createBoolean(false, getPos());
}
{
<MATCH_RECOGNIZE> { s = span(); } <LPAREN>
[
<PARTITION> { s2 = span(); } <BY>
partitionList = ExpressionCommaList(s2, ExprContext.ACCEPT_NON_QUERY)
]
[
orderList = OrderBy(true)
]
[
<MEASURES>
measureList = MeasureColumnCommaList(span())
]
(
<ONE> { s0 = span(); } <ROW> <PER> <MATCH> {
rowsPerMatch = SqlMatchRecognize.RowsPerMatchOption.ONE_ROW.symbol(s0.end(this));
}
|
<ALL> { s0 = span(); } <ROWS> <PER> <MATCH> {
rowsPerMatch = SqlMatchRecognize.RowsPerMatchOption.ALL_ROWS.symbol(s0.end(this));
}
|
{
rowsPerMatch = null;
}
)
(
<AFTER> { s1 = span(); } <MATCH> <SKIP_>
(
<TO>
(
LOOKAHEAD(2)
<NEXT> <ROW> {
after = SqlMatchRecognize.AfterOption.SKIP_TO_NEXT_ROW
.symbol(s1.end(this));
}
|
LOOKAHEAD(2)
<FIRST> var = SimpleIdentifier() {
after = SqlMatchRecognize.SKIP_TO_FIRST.createCall(
s1.end(var), var);
}
|
// This "LOOKAHEAD({true})" is a workaround for Babel.
// Because of babel parser uses option "LOOKAHEAD=2" globally,
// JavaCC generates something like "LOOKAHEAD(2, [<LAST>] SimpleIdentifier())"
// here. But the correct LOOKAHEAD should be
// "LOOKAHEAD(2, [ LOOKAHEAD(2, <LAST> SimpleIdentifier()) <LAST> ]
// SimpleIdentifier())" which have the syntactic lookahead for <LAST> considered.
//
// Overall LOOKAHEAD({true}) is even better as this is the last branch in the
// choice.
LOOKAHEAD({true})
[ LOOKAHEAD(2, <LAST> SimpleIdentifier()) <LAST> ] var = SimpleIdentifier() {
after = SqlMatchRecognize.SKIP_TO_LAST.createCall(
s1.end(var), var);
}
)
|
<PAST> <LAST> <ROW> {
after = SqlMatchRecognize.AfterOption.SKIP_PAST_LAST_ROW
.symbol(s1.end(this));
}
)
|
{ after = null; }
)
<PATTERN>
<LPAREN>
(
<CARET> { isStrictStarts = SqlLiteral.createBoolean(true, getPos()); }
|
{ isStrictStarts = SqlLiteral.createBoolean(false, getPos()); }
)
pattern = PatternExpression()
(
<DOLLAR> { isStrictEnds = SqlLiteral.createBoolean(true, getPos()); }
|
{ isStrictEnds = SqlLiteral.createBoolean(false, getPos()); }
)
<RPAREN>
(
<WITHIN> interval = IntervalLiteral()
|
{ interval = null; }
)
[
<SUBSET>
subsetList = SubsetDefinitionCommaList(span())
]
<DEFINE>
patternDefList = PatternDefinitionCommaList(span())
<RPAREN> {
return new SqlMatchRecognize(s.end(this), tableRef,
pattern, isStrictStarts, isStrictEnds, patternDefList, measureList,
after, subsetList, rowsPerMatch, partitionList, orderList, interval);
}
}
SqlNodeList MeasureColumnCommaList(Span s) :
{
SqlNode e;
final List<SqlNode> eList = new ArrayList<SqlNode>();
}
{
e = MeasureColumn() {
eList.add(e);
}
(
<COMMA>
e = MeasureColumn() {
eList.add(e);
}
)*
{
return new SqlNodeList(eList, s.addAll(eList).pos());
}
}
SqlNode MeasureColumn() :
{
SqlNode e;
SqlIdentifier alias;
}
{
e = Expression(ExprContext.ACCEPT_NON_QUERY)
<AS>
alias = SimpleIdentifier() {
return SqlStdOperatorTable.AS.createCall(Span.of(e).end(this), e, alias);
}
}
SqlNode PatternExpression() :
{
SqlNode left;
SqlNode right;
}
{
left = PatternTerm()
(
<VERTICAL_BAR>
right = PatternTerm() {
left = SqlStdOperatorTable.PATTERN_ALTER.createCall(
Span.of(left).end(right), left, right);
}
)*
{
return left;
}
}
SqlNode PatternTerm() :
{
SqlNode left;
SqlNode right;
}
{
left = PatternFactor()
(
right = PatternFactor() {
left = SqlStdOperatorTable.PATTERN_CONCAT.createCall(
Span.of(left).end(right), left, right);
}
)*
{
return left;
}
}
SqlNode PatternFactor() :
{
SqlNode e;
SqlNode extra;
SqlLiteral startNum = null;
SqlLiteral endNum = null;
SqlLiteral reluctant = SqlLiteral.createBoolean(false, SqlParserPos.ZERO);
}
{
e = PatternPrimary()
[
LOOKAHEAD(1)
(
<STAR> {
startNum = SqlLiteral.createExactNumeric("0", SqlParserPos.ZERO);
endNum = SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO);
}
|
<PLUS> {
startNum = SqlLiteral.createExactNumeric("1", SqlParserPos.ZERO);
endNum = SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO);
}
|
<HOOK> {
startNum = SqlLiteral.createExactNumeric("0", SqlParserPos.ZERO);
endNum = SqlLiteral.createExactNumeric("1", SqlParserPos.ZERO);
}
|
<LBRACE>
(
startNum = UnsignedNumericLiteral() { endNum = startNum; }
[
<COMMA> {
endNum = SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO);
}
[
endNum = UnsignedNumericLiteral()
]
]
<RBRACE>
|
{
startNum = SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO);
}
<COMMA>
endNum = UnsignedNumericLiteral()
<RBRACE>
|
<MINUS> extra = PatternExpression() <MINUS> <RBRACE> {
extra = SqlStdOperatorTable.PATTERN_EXCLUDE.createCall(
Span.of(extra).end(this), extra);
e = SqlStdOperatorTable.PATTERN_CONCAT.createCall(
Span.of(e).end(this), e, extra);
return e;
}
)
)
[
<HOOK>
{
if (startNum.intValue(true) != endNum.intValue(true)) {
reluctant = SqlLiteral.createBoolean(true, SqlParserPos.ZERO);
}
}
]
]
{
if (startNum == null) {
return e;
} else {
return SqlStdOperatorTable.PATTERN_QUANTIFIER.createCall(
span().end(e), e, startNum, endNum, reluctant);
}
}
}
SqlNode PatternPrimary() :
{
final Span s;
SqlNode e;
List<SqlNode> eList;
}
{
(
e = SimpleIdentifier()
|
<LPAREN> e = PatternExpression() <RPAREN>
|
<LBRACE> { s = span(); }
<MINUS> e = PatternExpression()
<MINUS> <RBRACE> {
e = SqlStdOperatorTable.PATTERN_EXCLUDE.createCall(s.end(this), e);
}
|
(
<PERMUTE> { s = span(); }
<LPAREN>
e = PatternExpression() {
eList = new ArrayList<SqlNode>();
eList.add(e);
}
(
<COMMA>
e = PatternExpression()
{
eList.add(e);
}
)*
<RPAREN> {
e = SqlStdOperatorTable.PATTERN_PERMUTE.createCall(
s.end(this), eList);
}
)
)
{
return e;
}
}
SqlNodeList SubsetDefinitionCommaList(Span s) :
{
SqlNode e;
final List<SqlNode> eList = new ArrayList<SqlNode>();
}
{
e = SubsetDefinition() {
eList.add(e);
}
(
<COMMA>
e = SubsetDefinition() {
eList.add(e);
}
)*
{
return new SqlNodeList(eList, s.addAll(eList).pos());
}
}
SqlNode SubsetDefinition() :
{
final SqlNode var;
final SqlNodeList varList;
}
{
var = SimpleIdentifier()
<EQ>
<LPAREN>
varList = ExpressionCommaList(span(), ExprContext.ACCEPT_NON_QUERY)
<RPAREN> {
return SqlStdOperatorTable.EQUALS.createCall(span().end(var), var,
varList);
}
}
SqlNodeList PatternDefinitionCommaList(Span s) :
{
SqlNode e;
final List<SqlNode> eList = new ArrayList<SqlNode>();
}
{
e = PatternDefinition() {
eList.add(e);
}
(
<COMMA>
e = PatternDefinition() {
eList.add(e);
}
)*
{
return new SqlNodeList(eList, s.addAll(eList).pos());
}
}
SqlNode PatternDefinition() :
{
final SqlNode var;
final SqlNode e;
}
{
var = SimpleIdentifier()
<AS>
e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
return SqlStdOperatorTable.AS.createCall(Span.of(var, e).pos(), e, var);
}
}
// ----------------------------------------------------------------------------
// Expressions
/**
* Parses a SQL expression (such as might occur in a WHERE clause) followed by
* the end-of-file symbol.
*/
SqlNode SqlExpressionEof() :
{
SqlNode e;
}
{
e = Expression(ExprContext.ACCEPT_SUB_QUERY) (<EOF>)
{
return e;
}
}
/**
* Parses either a row expression or a query expression without ORDER BY.
*/
SqlNode QueryOrExpr(ExprContext exprContext) :
{
SqlNodeList withList = null;
SqlNode e;
SqlOperator op;
SqlParserPos pos;
SqlParserPos withPos;
List<Object> list;
}
{
[
withList = WithList()
]
e = LeafQueryOrExpr(exprContext) {
list = startList(e);
}
(
{
if (!e.isA(SqlKind.QUERY)) {
// whoops, expression we just parsed wasn't a query,
// but we're about to see something like UNION, so
// force an exception retroactively
checkNonQueryExpression(ExprContext.ACCEPT_QUERY);
}
}
op = BinaryQueryOperator() {
// ensure a query is legal in this context
pos = getPos();
checkQueryExpression(exprContext);
}
e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY) {
list.add(new SqlParserUtil.ToTreeListItem(op, pos));
list.add(e);
}
)*
{
e = SqlParserUtil.toTree(list);
if (withList != null) {
e = new SqlWith(withList.getParserPosition(), withList, e);
}
return e;
}
}
SqlNodeList WithList() :
{
SqlWithItem withItem;
SqlParserPos pos;
SqlNodeList list;
}
{
<WITH> { list = new SqlNodeList(getPos()); }
withItem = WithItem() {list.add(withItem);}
(
<COMMA> withItem = WithItem() {list.add(withItem);}
)*
{ return list; }
}
SqlWithItem WithItem() :
{
SqlIdentifier id;
SqlNodeList columnList = null;
SqlNode definition;
}
{
id = SimpleIdentifier()
[
LOOKAHEAD(2)
columnList = ParenthesizedSimpleIdentifierList()
]
<AS>
definition = ParenthesizedExpression(ExprContext.ACCEPT_QUERY)
{
return new SqlWithItem(id.getParserPosition(), id, columnList,
definition);
}
}
/**
* Parses either a row expression, a leaf query expression, or
* a parenthesized expression of any kind.
*/
SqlNode LeafQueryOrExpr(ExprContext exprContext) :
{
SqlNode e;
}
{
e = Expression(exprContext) { return e; }
|
e = LeafQuery(exprContext) { return e; }
}
/**
* Parses a row expression or a parenthesized expression of any kind.
*/
SqlNode Expression(ExprContext exprContext) :
{
List<Object> list;
SqlNode e;
}
{
list = Expression2(exprContext)
{
e = SqlParserUtil.toTree(list);
return e;
}
}
// TODO jvs 15-Nov-2003: ANY/ALL
void Expression2b(ExprContext exprContext, List<Object> list) :
{
SqlNode e;
SqlOperator op;
SqlIdentifier p;
}
{
(
LOOKAHEAD(1)
op = PrefixRowOperator() {
checkNonQueryExpression(exprContext);
list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
}
)*
e = Expression3(exprContext) {
list.add(e);
}
(
LOOKAHEAD(2) <DOT>
p = SimpleIdentifier() {
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.DOT, getPos()));
list.add(p);
}
)*
}
/**
* Parses a binary row expression, or a parenthesized expression of any
* kind.
*
* <p>The result is as a flat list of operators and operands. The top-level
* call to get an expression should call {@link #Expression}, but lower-level
* calls should call this, to give the parser the opportunity to associate
* operator calls.
*
* <p>For example 'a = b like c = d' should come out '((a = b) like c) = d'
* because LIKE and '=' have the same precedence, but tends to come out as '(a
* = b) like (c = d)' because (a = b) and (c = d) are parsed as separate
* expressions.
*/
List<Object> Expression2(ExprContext exprContext) :
{
final List<Object> list = new ArrayList();
List<Object> list2;
final List<Object> list3 = new ArrayList();
SqlNodeList nodeList;
SqlNode e;
SqlOperator op;
SqlIdentifier p;
final Span s = span();
}
{
Expression2b(exprContext, list)
(
LOOKAHEAD(2)
(
LOOKAHEAD(2)
(
// Special case for "IN", because RHS of "IN" is the only place
// that an expression-list is allowed ("exp IN (exp1, exp2)").
LOOKAHEAD(2) {
checkNonQueryExpression(exprContext);
}
(
<NOT> <IN> { op = SqlStdOperatorTable.NOT_IN; }
|
<IN> { op = SqlStdOperatorTable.IN; }
|
{ final SqlKind k; }
k = comp()
(
<SOME> { op = SqlStdOperatorTable.some(k); }
|
<ANY> { op = SqlStdOperatorTable.some(k); }
|
<ALL> { op = SqlStdOperatorTable.all(k); }
)
)
{ s.clear().add(this); }
nodeList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR)
{
list.add(new SqlParserUtil.ToTreeListItem(op, s.pos()));
s.add(nodeList);
// special case for stuff like IN (s1 UNION s2)
if (nodeList.size() == 1) {
SqlNode item = nodeList.get(0);
if (item.isA(SqlKind.QUERY)) {
list.add(item);
} else {
list.add(nodeList);
}
} else {
list.add(nodeList);
}
}
|
LOOKAHEAD(2) {
checkNonQueryExpression(exprContext);
}
(
<NOT> <BETWEEN> {
op = SqlStdOperatorTable.NOT_BETWEEN;
s.clear().add(this);
}
[
<SYMMETRIC> { op = SqlStdOperatorTable.SYMMETRIC_NOT_BETWEEN; }
|
<ASYMMETRIC>
]
|
<BETWEEN>
{
op = SqlStdOperatorTable.BETWEEN;
s.clear().add(this);
}
[
<SYMMETRIC> { op = SqlStdOperatorTable.SYMMETRIC_BETWEEN; }
|
<ASYMMETRIC>
]
)
Expression2b(ExprContext.ACCEPT_SUB_QUERY, list3) {
list.add(new SqlParserUtil.ToTreeListItem(op, s.pos()));
list.addAll(list3);
list3.clear();
}
|
LOOKAHEAD(2) {
checkNonQueryExpression(exprContext);
s.clear().add(this);
}
(
<NOT>
(
<LIKE> { op = SqlStdOperatorTable.NOT_LIKE; }
|
<SIMILAR> <TO> { op = SqlStdOperatorTable.NOT_SIMILAR_TO; }
)
|
<LIKE> { op = SqlStdOperatorTable.LIKE; }
|
<SIMILAR> <TO> { op = SqlStdOperatorTable.SIMILAR_TO; }
)
list2 = Expression2(ExprContext.ACCEPT_SUB_QUERY) {
list.add(new SqlParserUtil.ToTreeListItem(op, s.pos()));
list.addAll(list2);
}
[
LOOKAHEAD(2)
<ESCAPE> e = Expression3(ExprContext.ACCEPT_SUB_QUERY) {
s.clear().add(this);
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.ESCAPE, s.pos()));
list.add(e);
}
]
|
LOOKAHEAD(3) op = BinaryRowOperator() {
checkNonQueryExpression(exprContext);
list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
}
Expression2b(ExprContext.ACCEPT_SUB_QUERY, list)
|
<LBRACKET>
e = Expression(ExprContext.ACCEPT_SUB_QUERY)
<RBRACKET> {
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.ITEM, getPos()));
list.add(e);
}
(
LOOKAHEAD(2) <DOT>
p = SimpleIdentifier() {
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.DOT, getPos()));
list.add(p);
}
)*
|
{
checkNonQueryExpression(exprContext);
}
op = PostfixRowOperator() {
list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
}
)
)+
{
return list;
}
|
{
return list;
}
)
}
/** Parses a comparison operator inside a SOME / ALL predicate. */
SqlKind comp() :
{
}
{
<LT> { return SqlKind.LESS_THAN; }
|
<LE> { return SqlKind.LESS_THAN_OR_EQUAL; }
|
<GT> { return SqlKind.GREATER_THAN; }
|
<GE> { return SqlKind.GREATER_THAN_OR_EQUAL; }
|
<EQ> { return SqlKind.EQUALS; }
|
<NE> { return SqlKind.NOT_EQUALS; }
|
<NE2> {
if (!this.conformance.isBangEqualAllowed()) {
throw new ParseException(RESOURCE.bangEqualNotAllowed().str());
}
return SqlKind.NOT_EQUALS;
}
}
/**
* Parses a unary row expression, or a parenthesized expression of any
* kind.
*/
SqlNode Expression3(ExprContext exprContext) :
{
final SqlNode e;
final SqlNodeList list;
final SqlNodeList list1;
final SqlNodeList list2;
final SqlOperator op;
final Span s;
Span rowSpan = null;
}
{
LOOKAHEAD(2)
e = AtomicRowExpression()
{
checkNonQueryExpression(exprContext);
return e;
}
|
e = CursorExpression(exprContext) { return e; }
|
LOOKAHEAD(3)
<ROW> {
s = span();
}
list = ParenthesizedSimpleIdentifierList() {
if (exprContext != ExprContext.ACCEPT_ALL
&& exprContext != ExprContext.ACCEPT_CURSOR
&& !this.conformance.allowExplicitRowValueConstructor())
{
throw SqlUtil.newContextException(s.end(list),
RESOURCE.illegalRowExpression());
}
return SqlStdOperatorTable.ROW.createCall(list);
}
|
[
<ROW> { rowSpan = span(); }
]
list1 = ParenthesizedQueryOrCommaList(exprContext) {
if (rowSpan != null) {
// interpret as row constructor
return SqlStdOperatorTable.ROW.createCall(rowSpan.end(list1),
list1.toArray());
}
}
[
LOOKAHEAD(2)
/* TODO:
(
op = periodOperator()
list2 = ParenthesizedQueryOrCommaList(exprContext)
{
if (list1.size() != 2 || list2.size() != 2) {
throw SqlUtil.newContextException(
list1.getParserPosition().plus(
list2.getParserPosition()),
RESOURCE.illegalOverlaps());
}
for (SqlNode node : list2) {
list1.add(node);
}
return op.createCall(
list1.getParserPosition().plus(list2.getParserPosition()),
list1.toArray());
}
)
|
*/
(
e = IntervalQualifier()
{
if ((list1.size() == 1)
&& list1.get(0) instanceof SqlCall)
{
final SqlCall call = (SqlCall) list1.get(0);
if (call.getKind() == SqlKind.MINUS
&& call.operandCount() == 2) {
List<SqlNode> list3 = startList(call.operand(0));
list3.add(call.operand(1));
list3.add(e);
return SqlStdOperatorTable.MINUS_DATE.createCall(
Span.of(list1).end(this), list3);
}
}
throw SqlUtil.newContextException(span().end(list1),
RESOURCE.illegalMinusDate());
}
)
]
{
if (list1.size() == 1) {
// interpret as single value or query
return list1.get(0);
} else {
// interpret as row constructor
return SqlStdOperatorTable.ROW.createCall(span().end(list1),
list1.toArray());
}
}
}
SqlOperator periodOperator() :
{
}
{
<OVERLAPS> { return SqlStdOperatorTable.OVERLAPS; }
|
LOOKAHEAD(2)
<IMMEDIATELY> <PRECEDES> { return SqlStdOperatorTable.IMMEDIATELY_PRECEDES; }
|
<PRECEDES> { return SqlStdOperatorTable.PRECEDES; }
|
<IMMEDIATELY> <SUCCEEDS> { return SqlStdOperatorTable.IMMEDIATELY_SUCCEEDS; }
|
<SUCCEEDS> { return SqlStdOperatorTable.SUCCEEDS; }
|
<EQUALS> { return SqlStdOperatorTable.PERIOD_EQUALS; }
}
/**
* Parses a COLLATE clause
*/
SqlCollation CollateClause() :
{
}
{
<COLLATE> <COLLATION_ID>
{
return new SqlCollation(
getToken(0).image, SqlCollation.Coercibility.EXPLICIT);
}
}
/**
* Numeric literal or parameter; used in LIMIT, OFFSET and FETCH clauses.
*/
SqlNode UnsignedNumericLiteralOrParam() :
{
final SqlNode e;
}
{
(
e = UnsignedNumericLiteral()
|
e = DynamicParam()
)
{ return e; }
}
/**
* Parses an atomic row expression.
*/
SqlNode AtomicRowExpression() :
{
final SqlNode e;
}
{
(
e = Literal()
|
e = DynamicParam()
|
LOOKAHEAD(2)
e = BuiltinFunctionCall()
|
e = JdbcFunctionCall()
|
e = MultisetConstructor()
|
e = ArrayConstructor()
|
LOOKAHEAD(3)
e = MapConstructor()
|
e = PeriodConstructor()
|
// NOTE jvs 18-Jan-2005: use syntactic lookahead to discriminate
// compound identifiers from function calls in which the function
// name is a compound identifier
LOOKAHEAD( [<SPECIFIC>] FunctionName() <LPAREN>)
e = NamedFunctionCall()
|
e = ContextVariable()
|
e = CompoundIdentifier()
|
e = NewSpecification()
|
e = CaseExpression()
|
e = SequenceExpression()
)
{ return e; }
}
SqlNode CaseExpression() :
{
final Span whenSpan = Span.of();
final Span thenSpan = Span.of();
final Span s;
SqlNode e;
SqlNode caseIdentifier = null;
SqlNode elseClause = null;
List<SqlNode> whenList = new ArrayList<SqlNode>();
List<SqlNode> thenList = new ArrayList<SqlNode>();
}
{
<CASE> { s = span(); }
[
caseIdentifier = Expression(ExprContext.ACCEPT_SUB_QUERY)
]
(
<WHEN> { whenSpan.add(this); }
e = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) {
if (((SqlNodeList) e).size() == 1) {
e = ((SqlNodeList) e).get(0);
}
whenList.add(e);
}
<THEN> { thenSpan.add(this); }
e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
thenList.add(e);
}
)+
[
<ELSE> elseClause = Expression(ExprContext.ACCEPT_SUB_QUERY)
]
<END> {
return SqlCase.createSwitched(s.end(this), caseIdentifier,
new SqlNodeList(whenList, whenSpan.addAll(whenList).pos()),
new SqlNodeList(thenList, thenSpan.addAll(thenList).pos()),
elseClause);
}
}
SqlCall SequenceExpression() :
{
final Span s;
final SqlOperator f;
final SqlNode sequenceRef;
}
{
(
<NEXT> { f = SqlStdOperatorTable.NEXT_VALUE; s = span(); }
|
<CURRENT> { f = SqlStdOperatorTable.CURRENT_VALUE; s = span(); }
)
<VALUE> <FOR> sequenceRef = CompoundIdentifier() {
return f.createCall(s.end(sequenceRef), sequenceRef);
}
}
/**
* Parses "SET &lt;NAME&gt; = VALUE" or "RESET &lt;NAME&gt;", without a leading
* "ALTER &lt;SCOPE&gt;".
*/
SqlSetOption SqlSetOption(Span s, String scope) :
{
SqlIdentifier name;
final SqlNode val;
}
{
(
<SET> {
s.add(this);
}
name = CompoundIdentifier()
<EQ>
(
val = Literal()
|
val = SimpleIdentifier()
|
<ON> {
// OFF is handled by SimpleIdentifier, ON handled here.
val = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT),
getPos());
}
)
{
return new SqlSetOption(s.end(val), scope, name, val);
}
|
<RESET> {
s.add(this);
}
(
name = CompoundIdentifier()
|
<ALL> {
name = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT),
getPos());
}
)
{
return new SqlSetOption(s.end(name), scope, name, null);
}
)
}
/**
* Parses an expression for setting or resetting an option in SQL, such as QUOTED_IDENTIFIERS,
* or explain plan level (physical/logical).
*/
SqlAlter SqlAlter() :
{
final Span s;
final String scope;
final SqlAlter alterNode;
}
{
<ALTER> { s = span(); }
scope = Scope()
(
<#-- additional literal parser methods are included here -->
<#list parser.alterStatementParserMethods as method>
alterNode = ${method}(s, scope)
|
</#list>
alterNode = SqlSetOption(s, scope)
)
{
return alterNode;
}
}
String Scope() :
{
}
{
( <SYSTEM> | <SESSION> ) { return token.image.toUpperCase(Locale.ROOT); }
}
<#if parser.createStatementParserMethods?size != 0>
/**
* Parses a CREATE statement.
*/
SqlCreate SqlCreate() :
{
final Span s;
boolean replace = false;
final SqlCreate create;
}
{
<CREATE> { s = span(); }
[
<OR> <REPLACE> {
replace = true;
}
]
(
<#-- additional literal parser methods are included here -->
<#list parser.createStatementParserMethods as method>
create = ${method}(s, replace)
<#sep>|</#sep>
</#list>
)
{
return create;
}
}
</#if>
<#if parser.dropStatementParserMethods?size != 0>
/**
* Parses a DROP statement.
*/
SqlDrop SqlDrop() :
{
final Span s;
boolean replace = false;
final SqlDrop drop;
}
{
<DROP> { s = span(); }
(
<#-- additional literal parser methods are included here -->
<#list parser.dropStatementParserMethods as method>
drop = ${method}(s, replace)
<#sep>|</#sep>
</#list>
)
{
return drop;
}
}
</#if>
/**
* Parses a literal expression, allowing continued string literals.
* Usually returns an SqlLiteral, but a continued string literal
* is an SqlCall expression, which concatenates 2 or more string
* literals; the validator reduces this.
*/
SqlNode Literal() :
{
SqlNode e;
}
{
(
e = NumericLiteral()
|
e = StringLiteral()
|
e = SpecialLiteral()
|
e = DateTimeLiteral()
|
e = IntervalLiteral()
<#-- additional literal parser methods are included here -->
<#list parser.literalParserMethods as method>
|
e = ${method}
</#list>
)
{
return e;
}
}
/** Parses a unsigned numeric literal */
SqlNumericLiteral UnsignedNumericLiteral() :
{
}
{
<UNSIGNED_INTEGER_LITERAL> {
return SqlLiteral.createExactNumeric(token.image, getPos());
}
|
<DECIMAL_NUMERIC_LITERAL> {
return SqlLiteral.createExactNumeric(token.image, getPos());
}
|
<APPROX_NUMERIC_LITERAL> {
return SqlLiteral.createApproxNumeric(token.image, getPos());
}
}
/** Parses a numeric literal (can be signed) */
SqlLiteral NumericLiteral() :
{
final SqlNumericLiteral num;
final Span s;
}
{
<PLUS> num = UnsignedNumericLiteral() {
return num;
}
|
<MINUS> { s = span(); } num = UnsignedNumericLiteral() {
return SqlLiteral.createNegative(num, s.end(this));
}
|
num = UnsignedNumericLiteral() {
return num;
}
}
/** Parse a special literal keyword */
SqlLiteral SpecialLiteral() :
{
}
{
<TRUE> { return SqlLiteral.createBoolean(true, getPos()); }
|
<FALSE> { return SqlLiteral.createBoolean(false, getPos()); }
|
<UNKNOWN> { return SqlLiteral.createUnknown(getPos()); }
|
<NULL> { return SqlLiteral.createNull(getPos()); }
}
/**
* Parses a string literal. The literal may be continued onto several
* lines. For a simple literal, the result is an SqlLiteral. For a continued
* literal, the result is an SqlCall expression, which concatenates 2 or more
* string literals; the validator reduces this.
*
* @see SqlLiteral#unchain(SqlNode)
* @see SqlLiteral#stringValue(SqlNode)
*
* @return a literal expression
*/
SqlNode StringLiteral() :
{
String p;
int nfrags = 0;
List<SqlLiteral> frags = null;
char unicodeEscapeChar = 0;
}
{
// A continued string literal consists of a head fragment and one or more
// tail fragments. Since comments may occur between the fragments, and
// comments are special tokens, each fragment is a token. But since spaces
// or comments may not occur between the prefix and the first quote, the
// head fragment, with any prefix, is one token.
<BINARY_STRING_LITERAL>
{
try {
p = SqlParserUtil.trim(token.image, "xX'");
frags = startList(SqlLiteral.createBinaryString(p, getPos()));
nfrags++;
} catch (NumberFormatException ex) {
throw SqlUtil.newContextException(getPos(),
RESOURCE.illegalBinaryString(token.image));
}
}
(
<QUOTED_STRING>
{
try {
p = SqlParserUtil.trim(token.image, "'"); // no embedded quotes
frags.add(SqlLiteral.createBinaryString(p, getPos()));
nfrags++;
} catch (NumberFormatException ex) {
throw SqlUtil.newContextException(getPos(),
RESOURCE.illegalBinaryString(token.image));
}
}
)*
{
assert (nfrags > 0);
if (nfrags == 1) {
return frags.get(0); // just the head fragment
} else {
SqlParserPos pos2 = SqlParserPos.sum(frags);
return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, frags);
}
}
|
{
String charSet = null;
}
(
<PREFIXED_STRING_LITERAL>
{ charSet = SqlParserUtil.getCharacterSet(token.image); }
| <QUOTED_STRING>
| <UNICODE_STRING_LITERAL> {
// TODO jvs 2-Feb-2009: support the explicit specification of
// a character set for Unicode string literals, per SQL:2003
unicodeEscapeChar = BACKSLASH;
charSet = "UTF16";
}
)
{
p = SqlParserUtil.parseString(token.image);
SqlCharStringLiteral literal;
try {
literal = SqlLiteral.createCharString(p, charSet, getPos());
} catch (java.nio.charset.UnsupportedCharsetException e) {
throw SqlUtil.newContextException(getPos(),
RESOURCE.unknownCharacterSet(charSet));
}
frags = startList(literal);
nfrags++;
}
(
<QUOTED_STRING>
{
p = SqlParserUtil.parseString(token.image);
try {
literal = SqlLiteral.createCharString(p, charSet, getPos());
} catch (java.nio.charset.UnsupportedCharsetException e) {
throw SqlUtil.newContextException(getPos(),
RESOURCE.unknownCharacterSet(charSet));
}
frags.add(literal);
nfrags++;
}
)*
[
<UESCAPE> <QUOTED_STRING>
{
if (unicodeEscapeChar == 0) {
throw SqlUtil.newContextException(getPos(),
RESOURCE.unicodeEscapeUnexpected());
}
String s = SqlParserUtil.parseString(token.image);
unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s);
}
]
{
assert nfrags > 0;
if (nfrags == 1) {
// just the head fragment
SqlLiteral lit = (SqlLiteral) frags.get(0);
return lit.unescapeUnicode(unicodeEscapeChar);
} else {
SqlNode[] rands = (SqlNode[]) frags.toArray(new SqlNode[nfrags]);
for (int i = 0; i < rands.length; ++i) {
rands[i] = ((SqlLiteral) rands[i]).unescapeUnicode(
unicodeEscapeChar);
}
SqlParserPos pos2 = SqlParserPos.sum(rands);
return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, rands);
}
}
}
/**
* Parses a date/time literal.
*/
SqlLiteral DateTimeLiteral() :
{
final String p;
final Span s;
}
{
<LBRACE_D> <QUOTED_STRING> {
p = token.image;
}
<RBRACE> {
return SqlParserUtil.parseDateLiteral(p, getPos());
}
|
<LBRACE_T> <QUOTED_STRING> {
p = token.image;
}
<RBRACE> {
return SqlParserUtil.parseTimeLiteral(p, getPos());
}
|
<LBRACE_TS> { s = span(); } <QUOTED_STRING> {
p = token.image;
}
<RBRACE> {
return SqlParserUtil.parseTimestampLiteral(p, s.end(this));
}
|
<DATE> { s = span(); } <QUOTED_STRING> {
return SqlParserUtil.parseDateLiteral(token.image, s.end(this));
}
|
<TIME> { s = span(); } <QUOTED_STRING> {
return SqlParserUtil.parseTimeLiteral(token.image, s.end(this));
}
|
<TIMESTAMP> { s = span(); } <QUOTED_STRING> {
return SqlParserUtil.parseTimestampLiteral(token.image, s.end(this));
}
}
/** Parses a MULTISET constructor */
SqlNode MultisetConstructor() :
{
List<SqlNode> args;
SqlNode e;
final Span s;
}
{
<MULTISET> { s = span(); }
(
LOOKAHEAD(2)
<LPAREN>
// by sub query "MULTISET(SELECT * FROM T)"
e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
<RPAREN> {
return SqlStdOperatorTable.MULTISET_QUERY.createCall(
s.end(this), e);
}
|
// by enumeration "MULTISET[e0, e1, ..., eN]"
<LBRACKET> // TODO: do trigraph as well ??( ??)
e = Expression(ExprContext.ACCEPT_NON_QUERY) { args = startList(e); }
(
<COMMA> e = Expression(ExprContext.ACCEPT_NON_QUERY) { args.add(e); }
)*
<RBRACKET>
{
return SqlStdOperatorTable.MULTISET_VALUE.createCall(
s.end(this), args);
}
)
}
/** Parses an ARRAY constructor */
SqlNode ArrayConstructor() :
{
SqlNodeList args;
SqlNode e;
final Span s;
}
{
<ARRAY> { s = span(); }
(
LOOKAHEAD(1)
<LPAREN>
// by sub query "MULTISET(SELECT * FROM T)"
e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
<RPAREN>
{
return SqlStdOperatorTable.ARRAY_QUERY.createCall(
s.end(this), e);
}
|
// by enumeration "ARRAY[e0, e1, ..., eN]"
<LBRACKET> // TODO: do trigraph as well ??( ??)
(
args = ExpressionCommaList(s, ExprContext.ACCEPT_NON_QUERY)
|
{ args = SqlNodeList.EMPTY; }
)
<RBRACKET>
{
return SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(
s.end(this), args.getList());
}
)
}
/** Parses a MAP constructor */
SqlNode MapConstructor() :
{
SqlNodeList args;
SqlNode e;
final Span s;
}
{
<MAP> { s = span(); }
(
LOOKAHEAD(1)
<LPAREN>
// by sub query "MAP (SELECT empno, deptno FROM emp)"
e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
<RPAREN>
{
return SqlStdOperatorTable.MAP_QUERY.createCall(
s.end(this), e);
}
|
// by enumeration "MAP[k0, v0, ..., kN, vN]"
<LBRACKET> // TODO: do trigraph as well ??( ??)
(
args = ExpressionCommaList(s, ExprContext.ACCEPT_NON_QUERY)
|
{ args = SqlNodeList.EMPTY; }
)
<RBRACKET>
{
return SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR.createCall(
s.end(this), args.getList());
}
)
}
/** Parses a PERIOD constructor */
SqlNode PeriodConstructor() :
{
final SqlNode e0, e1;
final Span s;
}
{
<PERIOD> { s = span(); }
<LPAREN>
e0 = Expression(ExprContext.ACCEPT_SUB_QUERY)
<COMMA>
e1 = Expression(ExprContext.ACCEPT_SUB_QUERY)
<RPAREN> {
return SqlStdOperatorTable.ROW.createCall(s.end(this), e0, e1);
}
}
/**
* Parses an interval literal.
*/
SqlLiteral IntervalLiteral() :
{
final String p;
final SqlIntervalQualifier intervalQualifier;
int sign = 1;
final Span s;
}
{
<INTERVAL> { s = span(); }
[
<MINUS> { sign = -1; }
|
<PLUS> { sign = 1; }
]
<QUOTED_STRING> { p = token.image; }
intervalQualifier = IntervalQualifier() {
return SqlParserUtil.parseIntervalLiteral(s.end(intervalQualifier),
sign, p, intervalQualifier);
}
}
SqlIntervalQualifier IntervalQualifier() :
{
TimeUnit start;
TimeUnit end = null;
int startPrec = RelDataType.PRECISION_NOT_SPECIFIED;
int secondFracPrec = RelDataType.PRECISION_NOT_SPECIFIED;
}
{
(
<YEAR> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
[
LOOKAHEAD(2) <TO> <MONTH>
{
end = TimeUnit.MONTH;
}
]
{ start = TimeUnit.YEAR; }
|
<MONTH> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
{ start = TimeUnit.MONTH; }
|
<DAY> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
[ LOOKAHEAD(2) <TO>
(
<HOUR> { end = TimeUnit.HOUR; }
|
<MINUTE> { end = TimeUnit.MINUTE; }
|
<SECOND> { end = TimeUnit.SECOND; }
[ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
)
]
{ start = TimeUnit.DAY; }
|
<HOUR> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
[ LOOKAHEAD(2) <TO>
(
<MINUTE> { end = TimeUnit.MINUTE; }
|
<SECOND> { end = TimeUnit.SECOND; }
[ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
)
]
{ start = TimeUnit.HOUR; }
|
<MINUTE> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
[ LOOKAHEAD(2) <TO>
(
<SECOND> { end = TimeUnit.SECOND; }
[ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
)
]
{ start = TimeUnit.MINUTE; }
|
<SECOND>
[ <LPAREN> startPrec = UnsignedIntLiteral()
[ <COMMA> secondFracPrec = UnsignedIntLiteral() ]
<RPAREN>
]
{ start = TimeUnit.SECOND; }
)
{
return new SqlIntervalQualifier(start,
startPrec,
end,
secondFracPrec,
getPos());
}
}
/**
* Parses time unit for EXTRACT, CEIL and FLOOR functions.
*/
TimeUnit TimeUnit() :
{}
{
<MICROSECOND> { return TimeUnit.MICROSECOND; }
| <MILLISECOND> { return TimeUnit.MILLISECOND; }
| <SECOND> { return TimeUnit.SECOND; }
| <MINUTE> { return TimeUnit.MINUTE; }
| <HOUR> { return TimeUnit.HOUR; }
| <DAY> { return TimeUnit.DAY; }
| <DOW> { return TimeUnit.DOW; }
| <DOY> { return TimeUnit.DOY; }
| <ISODOW> { return TimeUnit.ISODOW; }
| <ISOYEAR> { return TimeUnit.ISOYEAR; }
| <WEEK> { return TimeUnit.WEEK; }
| <MONTH> { return TimeUnit.MONTH; }
| <QUARTER> { return TimeUnit.QUARTER; }
| <YEAR> { return TimeUnit.YEAR; }
| <EPOCH> { return TimeUnit.EPOCH; }
| <DECADE> { return TimeUnit.DECADE; }
| <CENTURY> { return TimeUnit.CENTURY; }
| <MILLENNIUM> { return TimeUnit.MILLENNIUM; }
}
TimeUnit TimestampInterval() :
{}
{
<FRAC_SECOND> { return TimeUnit.MICROSECOND; }
| <MICROSECOND> { return TimeUnit.MICROSECOND; }
| <NANOSECOND> { return TimeUnit.NANOSECOND; }
| <SQL_TSI_FRAC_SECOND> { return TimeUnit.NANOSECOND; }
| <SQL_TSI_MICROSECOND> { return TimeUnit.MICROSECOND; }
| <SECOND> { return TimeUnit.SECOND; }
| <SQL_TSI_SECOND> { return TimeUnit.SECOND; }
| <MINUTE> { return TimeUnit.MINUTE; }
| <SQL_TSI_MINUTE> { return TimeUnit.MINUTE; }
| <HOUR> { return TimeUnit.HOUR; }
| <SQL_TSI_HOUR> { return TimeUnit.HOUR; }
| <DAY> { return TimeUnit.DAY; }
| <SQL_TSI_DAY> { return TimeUnit.DAY; }
| <WEEK> { return TimeUnit.WEEK; }
| <SQL_TSI_WEEK> { return TimeUnit.WEEK; }
| <MONTH> { return TimeUnit.MONTH; }
| <SQL_TSI_MONTH> { return TimeUnit.MONTH; }
| <QUARTER> { return TimeUnit.QUARTER; }
| <SQL_TSI_QUARTER> { return TimeUnit.QUARTER; }
| <YEAR> { return TimeUnit.YEAR; }
| <SQL_TSI_YEAR> { return TimeUnit.YEAR; }
}
/**
* Parses a dynamic parameter marker.
*/
SqlDynamicParam DynamicParam() :
{
}
{
<HOOK> {
return new SqlDynamicParam(nDynamicParams++, getPos());
}
}
/**
* Parses one segment of an identifier that may be composite.
*
* <p>Each time it reads an identifier it writes one element to each list;
* the entry in {@code positions} records its position and whether the
* segment was quoted.
*/
void IdentifierSegment(List<String> names, List<SqlParserPos> positions) :
{
final String id;
char unicodeEscapeChar = BACKSLASH;
final SqlParserPos pos;
final Span span;
}
{
(
<IDENTIFIER> {
id = unquotedIdentifier();
pos = getPos();
}
|
<QUOTED_IDENTIFIER> {
id = SqlParserUtil.strip(getToken(0).image, DQ, DQ, DQDQ,
quotedCasing);
pos = getPos().withQuoting(true);
}
|
<BACK_QUOTED_IDENTIFIER> {
id = SqlParserUtil.strip(getToken(0).image, "`", "`", "``",
quotedCasing);
pos = getPos().withQuoting(true);
}
|
<BRACKET_QUOTED_IDENTIFIER> {
id = SqlParserUtil.strip(getToken(0).image, "[", "]", "]]",
quotedCasing);
pos = getPos().withQuoting(true);
}
|
<UNICODE_QUOTED_IDENTIFIER> {
span = span();
String image = getToken(0).image;
image = image.substring(image.indexOf('"'));
image = SqlParserUtil.strip(image, DQ, DQ, DQDQ, quotedCasing);
}
[
<UESCAPE> <QUOTED_STRING> {
String s = SqlParserUtil.parseString(token.image);
unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s);
}
]
{
pos = span.end(this).withQuoting(true);
SqlLiteral lit = SqlLiteral.createCharString(image, "UTF16", pos);
lit = lit.unescapeUnicode(unicodeEscapeChar);
id = lit.toValue();
}
|
id = NonReservedKeyWord() {
pos = getPos();
}
)
{
if (id.length() > this.identifierMaxLength) {
throw SqlUtil.newContextException(pos,
RESOURCE.identifierTooLong(id, this.identifierMaxLength));
}
names.add(id);
if (positions != null) {
positions.add(pos);
}
}
}
/**
* Parses a simple identifier as a String.
*/
String Identifier() :
{
final List<String> names = new ArrayList<String>();
}
{
IdentifierSegment(names, null) {
return names.get(0);
}
}
/**
* Parses a simple identifier as an SqlIdentifier.
*/
SqlIdentifier SimpleIdentifier() :
{
final List<String> names = new ArrayList<String>();
final List<SqlParserPos> positions = new ArrayList<SqlParserPos>();
}
{
IdentifierSegment(names, positions) {
return new SqlIdentifier(names.get(0), positions.get(0));
}
}
/**
* Parses a comma-separated list of simple identifiers.
*/
void SimpleIdentifierCommaList(List<SqlNode> list) :
{
SqlIdentifier id;
}
{
id = SimpleIdentifier() {list.add(id);}
(
<COMMA> id = SimpleIdentifier() {
list.add(id);
}
)*
}
/**
* List of simple identifiers in parentheses. The position extends from the
* open parenthesis to the close parenthesis.
*/
SqlNodeList ParenthesizedSimpleIdentifierList() :
{
final Span s;
final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
<LPAREN> { s = span(); }
SimpleIdentifierCommaList(list)
<RPAREN> {
return new SqlNodeList(list, s.end(this));
}
}
<#if parser.includeCompoundIdentifier >
/**
* Parses a compound identifier.
*/
SqlIdentifier CompoundIdentifier() :
{
final List<String> nameList = new ArrayList<String>();
final List<SqlParserPos> posList = new ArrayList<SqlParserPos>();
boolean star = false;
}
{
IdentifierSegment(nameList, posList)
(
LOOKAHEAD(2)
<DOT>
IdentifierSegment(nameList, posList)
)*
(
LOOKAHEAD(2)
<DOT>
<STAR> {
star = true;
nameList.add("");
posList.add(getPos());
}
)?
{
SqlParserPos pos = SqlParserPos.sum(posList);
if (star) {
return SqlIdentifier.star(nameList, pos, posList);
}
return new SqlIdentifier(nameList, null, pos, posList);
}
}
/**
* Parses a comma-separated list of compound identifiers.
*/
void CompoundIdentifierTypeCommaList(List<SqlNode> list, List<SqlNode> extendList) :
{
}
{
CompoundIdentifierType(list, extendList)
(<COMMA> CompoundIdentifierType(list, extendList))*
}
/**
* List of compound identifiers in parentheses. The position extends from the
* open parenthesis to the close parenthesis.
*/
Pair<SqlNodeList, SqlNodeList> ParenthesizedCompoundIdentifierList() :
{
final Span s;
final List<SqlNode> list = new ArrayList<SqlNode>();
final List<SqlNode> extendList = new ArrayList<SqlNode>();
}
{
<LPAREN> { s = span(); }
CompoundIdentifierTypeCommaList(list, extendList)
<RPAREN> {
return Pair.of(new SqlNodeList(list, s.end(this)), new SqlNodeList(extendList, s.end(this)));
}
}
<#else>
<#include "/@includes/compoundIdentifier.ftl" />
</#if>
/**
* Parses a NEW UDT(...) expression.
*/
SqlNode NewSpecification() :
{
final Span s;
final SqlNode routineCall;
}
{
<NEW> { s = span(); }
routineCall =
NamedRoutineCall(SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR,
ExprContext.ACCEPT_SUB_QUERY) {
return SqlStdOperatorTable.NEW.createCall(s.end(routineCall), routineCall);
}
}
//TODO: real parse errors.
int UnsignedIntLiteral() :
{
Token t;
}
{
t = <UNSIGNED_INTEGER_LITERAL>
{
try {
return Integer.parseInt(t.image);
} catch (NumberFormatException ex) {
throw generateParseException();
}
}
}
int IntLiteral() :
{
Token t;
}
{
(
t = <UNSIGNED_INTEGER_LITERAL>
|
<PLUS> t = <UNSIGNED_INTEGER_LITERAL>
)
{
try {
return Integer.parseInt(t.image);
} catch (NumberFormatException ex) {
throw generateParseException();
}
}
|
<MINUS> t = <UNSIGNED_INTEGER_LITERAL> {
try {
return -Integer.parseInt(t.image);
} catch (NumberFormatException ex) {
throw generateParseException();
}
}
}
// Type name with optional scale and precision
SqlDataTypeSpec DataType() :
{
final SqlIdentifier typeName;
SqlIdentifier collectionTypeName = null;
int scale = -1;
int precision = -1;
String charSetName = null;
final Span s;
}
{
typeName = TypeName() {
s = span();
}
[
<LPAREN>
precision = UnsignedIntLiteral()
[
<COMMA>
scale = UnsignedIntLiteral()
]
<RPAREN>
]
[
<CHARACTER> <SET>
charSetName = Identifier()
]
[
collectionTypeName = CollectionsTypeName()
]
{
if (null != collectionTypeName) {
return new SqlDataTypeSpec(
collectionTypeName,
typeName,
precision,
scale,
charSetName,
s.end(collectionTypeName));
}
return new SqlDataTypeSpec(
typeName,
precision,
scale,
charSetName,
null,
s.end(this));
}
}
// Some SQL type names need special handling due to the fact that they have
// spaces in them but are not quoted.
SqlIdentifier TypeName() :
{
final SqlTypeName sqlTypeName;
final SqlIdentifier typeName;
final Span s = Span.of();
}
{
(
LOOKAHEAD(2)
sqlTypeName = SqlTypeName(s) {
typeName = new SqlIdentifier(sqlTypeName.name(), s.end(this));
}
<#-- additional types are included here -->
<#list parser.dataTypeParserMethods as method>
|
typeName = ${method}
</#list>
|
typeName = CollectionsTypeName()
|
typeName = CompoundIdentifier()
)
{
return typeName;
}
}
// Types used for JDBC and ODBC scalar conversion function
SqlTypeName SqlTypeName(Span s) :
{
}
{
(<CHARACTER> | <CHAR>) { s.add(this); }
(
<VARYING> { return SqlTypeName.VARCHAR; }
|
{ return SqlTypeName.CHAR; }
)
|
<VARCHAR> { return SqlTypeName.VARCHAR; }
|
<DATE> { return SqlTypeName.DATE; }
|
<TIME> { return SqlTypeName.TIME; }
|
<TIMESTAMP> { return SqlTypeName.TIMESTAMP; }
|
<GEOMETRY> {
if (!this.conformance.allowGeometry()) {
throw new ParseException(RESOURCE.geometryDisabled().str());
}
return SqlTypeName.GEOMETRY;
}
|
(<DECIMAL> | <DEC> | <NUMERIC>) { return SqlTypeName.DECIMAL; }
|
<BOOLEAN> { return SqlTypeName.BOOLEAN; }
|
( <INTEGER> | <INT> ) { return SqlTypeName.INTEGER; }
|
<BINARY> { s.add(this); }
(
<VARYING> { return SqlTypeName.VARBINARY; }
|
{ return SqlTypeName.BINARY; }
)
|
<VARBINARY> { return SqlTypeName.VARBINARY; }
|
<TINYINT> { return SqlTypeName.TINYINT; }
|
<SMALLINT> { return SqlTypeName.SMALLINT; }
|
<BIGINT> { return SqlTypeName.BIGINT; }
|
<REAL> { return SqlTypeName.REAL; }
|
<DOUBLE> { s.add(this); }
[ <PRECISION> ] { return SqlTypeName.DOUBLE; }
|
<FLOAT> { return SqlTypeName.FLOAT; }
|
<ANY> { return SqlTypeName.ANY; }
}
// Types used for for JDBC and ODBC scalar conversion function
SqlJdbcDataTypeName JdbcOdbcDataTypeName() :
{
}
{
(<SQL_CHAR> | <CHAR>) { return SqlJdbcDataTypeName.SQL_CHAR; }
| (<SQL_VARCHAR> | <VARCHAR>) { return SqlJdbcDataTypeName.SQL_VARCHAR; }
| (<SQL_DATE> | <DATE>) { return SqlJdbcDataTypeName.SQL_DATE; }
| (<SQL_TIME> | <TIME>) { return SqlJdbcDataTypeName.SQL_TIME; }
| (<SQL_TIMESTAMP> | <TIMESTAMP>) { return SqlJdbcDataTypeName.SQL_TIMESTAMP; }