Skip to content
Permalink
master
Switch branches/tags
Go to file
In this change, we can parse and validate those functions,
but execution will be [CALCITE-4666].

Validation is based on new method SqlAggFunction.isPercentile()
(experimental and subject to change without notice).

There are connections between the RANGE clause in windowed
aggregate functions and interpolation used by PERCENTILE_CONT.
Therefore use the same logic to determine whether "x" is a
valid type in OVER (ORDER BY x RANGE r)" and
"PERCENTILE_CONT(f) WITHIN GROUP (ORER BY x)"

Close #2444
52 contributors

Users who have contributed to this file

@julianhyde @danny0405 @zhztheplayer @Zhiqiang-He @XuQianJin-Stars @snuyanzin @laurentgo @amaliujia @wuchong @vkorukanti @vlsi @michaelmior
/*
* 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!default.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.SqlBasicTypeNameSpec;
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.SqlCollectionTypeNameSpec;
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.SqlHint;
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.SqlJsonValueReturning;
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.SqlPivot;
import org.apache.calcite.sql.SqlPostfixOperator;
import org.apache.calcite.sql.SqlPrefixOperator;
import org.apache.calcite.sql.SqlRowTypeNameSpec;
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.SqlTableRef;
import org.apache.calcite.sql.SqlTimeLiteral;
import org.apache.calcite.sql.SqlTimestampLiteral;
import org.apache.calcite.sql.SqlTypeNameSpec;
import org.apache.calcite.sql.SqlUnnestOperator;
import org.apache.calcite.sql.SqlUnpivot;
import org.apache.calcite.sql.SqlUpdate;
import org.apache.calcite.sql.SqlUserDefinedTypeNameSpec;
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.SqlInternalOperators;
import org.apache.calcite.sql.fun.SqlLibraryOperators;
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(SqlAbstractParserImpl.LexicalState state) {
final int stateOrdinal =
Arrays.asList(${parser.class}TokenManager.lexStateNames)
.indexOf(state.name());
token_source.SwitchTo(stateOrdinal);
}
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);
}
/** Adds a warning that a token such as "HOURS" was used,
* whereas the SQL standard only allows "HOUR".
*
* <p>Currently, we silently add an exception to a list of warnings. In
* future, we may have better compliance checking, for example a strict
* compliance mode that throws if any non-standard features are used. */
private TimeUnit warn(TimeUnit timeUnit) throws ParseException {
final String token = getToken(0).image.toUpperCase(Locale.ROOT);
warnings.add(
SqlUtil.newContextException(getPos(),
RESOURCE.nonStandardFeatureUsed(token)));
return timeUnit;
}
}
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.toCase(getToken(0).image, 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()
}
/*
* 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;
// Checks token.image.equals("1") to avoid recursive call.
// The SqlAbstractParserImpl#MetadataImpl constructor uses constant "1" to
// throw intentionally to collect the expected tokens.
if (!token.image.equals("1")
&& getMetadata().isKeyword(token.image)
&& SqlParserUtil.allowsIdentifier(tokenImage, expectedTokenSequences)) {
// If the next token is a keyword, reformat the error message as:
// Incorrect syntax near the keyword '{keyword}' at line {line_number},
// column {column_number}.
final String expecting = ex.getMessage()
.substring(ex.getMessage().indexOf("Was expecting"));
final String errorMsg = String.format("Incorrect syntax near the keyword '%s' "
+ "at line %d, column %d.\n%s",
token.image,
token.beginLine,
token.beginColumn,
expecting);
// Replace the ParseException with explicit error message.
ex = new ParseException(errorMsg);
}
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 SqlUtil.newContextException(getPos(), RESOURCE.limitStartCountNotAllowed());
}
}
|
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.
* If the list starts with DISTINCT or ALL, it is discarded.
*/
List UnquantifiedFunctionParameterList(
ExprContext exprContext) :
{
final List args;
}
{
args = FunctionParameterList(exprContext) {
final SqlLiteral quantifier = (SqlLiteral) args.get(0);
args.remove(0); // remove DISTINCT or ALL, if present
return args;
}
}
/**
* Parses function parameter lists including DISTINCT keyword recognition,
* DEFAULT, and named argument assignment.
*/
List FunctionParameterList(
ExprContext exprContext) :
{
final SqlLiteral qualifier;
final List list = new ArrayList();
}
{
<LPAREN>
(
qualifier = AllOrDistinct() { list.add(qualifier); }
|
{ list.add(null); }
)
Arg0(list, exprContext)
(
<COMMA> {
// a comma-list can't appear where only a query is expected
checkNonQueryExpression(exprContext);
}
Arg(list, exprContext)
)*
<RPAREN>
{
return list;
}
}
SqlLiteral AllOrDistinct() :
{
}
{
<DISTINCT> { return SqlSelectKeyword.DISTINCT.symbol(getPos()); }
|
<ALL> { return SqlSelectKeyword.ALL.symbol(getPos()); }
}
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!default.parser.statementParserMethods) as method>
LOOKAHEAD(2) stmt = ${method}
|
</#list>
stmt = SqlSetOption(Span.of(), null)
|
stmt = SqlAlter()
|
<#if (parser.createStatementParserMethods!default.parser.createStatementParserMethods)?size != 0>
stmt = SqlCreate()
|
</#if>
<#if (parser.dropStatementParserMethods!default.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!default.parser.implementationFiles) as file>
<#include "/@includes/"+file />
</#list>
SqlNodeList ParenthesizedKeyValueOptionCommaList() :
{
final Span s;
final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
{ s = span(); }
<LPAREN>
KeyValueOption(list)
(
<COMMA>
KeyValueOption(list)
)*
<RPAREN> {
return new SqlNodeList(list, s.end(this));
}
}
/**
* Parses an option with format key=val whose key is a simple identifier or string literal
* and value is a string literal.
*/
void KeyValueOption(List<SqlNode> list) :
{
final SqlNode key;
final SqlNode value;
}
{
(
key = SimpleIdentifier()
|
key = StringLiteral()
)
<EQ>
value = StringLiteral() {
list.add(key);
list.add(value);
}
}
/**
* Parses an option value, it's either a string or a numeric.
*/
SqlNode OptionValue() :
{
final SqlNode value;
}
{
(
value = NumericLiteral()
|
value = StringLiteral()
)
{
return value;
}
}
/**
* Parses a literal list separated by comma. The literal is either a string or a numeric.
*/
SqlNodeList ParenthesizedLiteralOptionCommaList() :
{
final Span s;
final List<SqlNode> list = new ArrayList<SqlNode>();
SqlNode optionVal;
}
{
{ s = span(); }
<LPAREN>
optionVal = OptionValue()
{
list.add(optionVal);
}
(
<COMMA>
optionVal = OptionValue()
{
list.add(optionVal);
}
)*
<RPAREN> {
return new SqlNodeList(list, s.end(this));
}
}
void CommaSeparatedSqlHints(List<SqlNode> hints) :
{
SqlIdentifier hintName;
SqlNodeList hintOptions;
SqlNode optionVal;
SqlHint.HintOptionFormat optionFormat;
}
{
hintName = SimpleIdentifier()
( LOOKAHEAD(5)
hintOptions = ParenthesizedKeyValueOptionCommaList() {
optionFormat = SqlHint.HintOptionFormat.KV_LIST;
}
|
LOOKAHEAD(3)
hintOptions = ParenthesizedSimpleIdentifierList() {
optionFormat = SqlHint.HintOptionFormat.ID_LIST;
}
|
LOOKAHEAD(3)
hintOptions = ParenthesizedLiteralOptionCommaList() {
optionFormat = SqlHint.HintOptionFormat.LITERAL_LIST;
}
|
LOOKAHEAD(2)
[<LPAREN> <RPAREN>]
{
hintOptions = SqlNodeList.EMPTY;
optionFormat = SqlHint.HintOptionFormat.EMPTY;
}
)
{
hints.add(new SqlHint(Span.of(hintOptions).end(this), hintName, hintOptions, optionFormat));
}
(
<COMMA>
hintName = SimpleIdentifier()
(
LOOKAHEAD(5)
hintOptions = ParenthesizedKeyValueOptionCommaList() {
optionFormat = SqlHint.HintOptionFormat.KV_LIST;
}
|
LOOKAHEAD(3)
hintOptions = ParenthesizedSimpleIdentifierList() {
optionFormat = SqlHint.HintOptionFormat.ID_LIST;
}
|
LOOKAHEAD(3)
hintOptions = ParenthesizedLiteralOptionCommaList() {
optionFormat = SqlHint.HintOptionFormat.LITERAL_LIST;
}
|
LOOKAHEAD(2)
[<LPAREN> <RPAREN>]
{
hintOptions = SqlNodeList.EMPTY;
optionFormat = SqlHint.HintOptionFormat.EMPTY;
}
)
{
hints.add(new SqlHint(Span.of(hintOptions).end(this), hintName, hintOptions, optionFormat));
}
)*
}
/**
* Parses a table reference with optional hints.
*/
SqlNode TableRefWithHintsOpt() :
{
SqlNode tableRef;
SqlNodeList hintList;
final List<SqlNode> hints = new ArrayList<SqlNode>();
final Span s;
}
{
{ s = span(); }
tableRef = CompoundTableIdentifier()
[
LOOKAHEAD(2)
<HINT_BEG>
CommaSeparatedSqlHints(hints)
<COMMENT_END>
{
hintList = new SqlNodeList(hints, s.addAll(hints).end(this));
tableRef = new SqlTableRef(Span.of(tableRef, hintList).pos(),
(SqlIdentifier) tableRef, hintList);
}
]
{
return tableRef;
}
}
/**
* Parses a leaf SELECT expression without ORDER BY.
*/
SqlSelect SqlSelect() :
{
final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
final SqlLiteral keyword;
final SqlNodeList keywordList;
List<SqlNode> selectList;
final SqlNode fromClause;
final SqlNode where;
final SqlNodeList groupBy;
final SqlNode having;
final SqlNodeList windowDecls;
final List<SqlNode> hints = new ArrayList<SqlNode>();
final Span s;
}
{
<SELECT>
{
s = span();
}
[
<HINT_BEG>
CommaSeparatedSqlHints(hints)
<COMMENT_END>
]
SqlSelectKeywords(keywords)
(
<STREAM> {
keywords.add(SqlSelectKeyword.STREAM.symbol(getPos()));
}
)?
(
keyword = AllOrDistinct() { keywords.add(keyword); }
)?
{
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,
new SqlNodeList(hints, getPos()));
}
}
/*
* 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; }
|
LOOKAHEAD(2)
<AS> <JSON> { format = SqlExplainFormat.JSON; }
|
<AS> <DOT_FORMAT> { format = SqlExplainFormat.DOT; }
|
{ 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>
| <HYPHENATED_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 = TableRefWithHintsOpt()
[
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 = TableRefWithHintsOpt()
[
[ <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 = TableRefWithHintsOpt() {
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 = TableRefWithHintsOpt()
[
[ <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 = CompoundIdentifier() {
updateColumnList.add(id);
}
<EQ> exp = Expression(ExprContext.ACCEPT_SUB_QUERY) {
updateExprList.add(exp);
}
(
<COMMA>
id = CompoundIdentifier() {
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()
|
// Mute the warning about ambiguity between alias and continued
// string literal.
LOOKAHEAD(1)
id = SimpleIdentifierFromStringLiteral()
)
{
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;
}
{
(
LOOKAHEAD(3) // required for "LEFT SEMI JOIN" in Babel
<#list (parser.joinTypes!default.parser.joinTypes) as method>
joinType = ${method}()
|
</#list>
<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; }
)
{
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, 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 SqlUtil.newContextException(getPos(), RESOURCE.applyNotAllowed());
}
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 SqlUtil.newContextException(getPos(), RESOURCE.applyNotAllowed());
}
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 = TableRefWithHintsOpt()
[
[ <EXTEND> ]
extendList = ExtendList() {
tableRef = extend(tableRef, extendList);
}
]
over = TableOverOpt() {
if (over != null) {
tableRef = SqlStdOperatorTable.OVER.createCall(
getPos(), tableRef, over);
}
}
[
tableRef = Snapshot(tableRef)
]
[
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), (List<SqlNode>) args);
}
|
[<LATERAL> { lateral = true; } ]
<TABLE> { s = span(); } <LPAREN>
tableRef = TableFunctionCall(s.pos())
<RPAREN>
{
if (lateral) {
tableRef = SqlStdOperatorTable.LATERAL.createCall(
s.end(this), tableRef);
}
}
|
tableRef = ExtendedTableRef()
)
[
LOOKAHEAD(2)
tableRef = Pivot(tableRef)
]
[
LOOKAHEAD(2)
tableRef = Unpivot(tableRef)
]
[
[ <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 SqlUtil.newContextException(getPos(), RESOURCE.invalidSampleSize());
}
// 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, getPos()));
}
}
/**
* 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()
[
<NOT> <NULL> {
nullable = false;
}
]
]
{
if (type != null) {
if (!this.conformance.allowExtend()) {
throw SqlUtil.newContextException(type.getParserPosition(),
RESOURCE.extendNotAllowed());
}
extendList.add(name);
extendList.add(type.withNullable(nullable, getPos()));
}
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() :
{
final List<SqlNode> rowConstructorList = new ArrayList<SqlNode>();
final Span s;
}
{
<VALUES> { s = span(); }
RowConstructorList(rowConstructorList)
{
return SqlStdOperatorTable.VALUES.createCall(
s.end(this), rowConstructorList);
}
}
/**
* Parses one or more rows in a VALUES expression.
*/
void RowConstructorList(List<SqlNode> list) :
{
SqlNode rowConstructor;
}
{
rowConstructor = RowConstructor() { list.add(rowConstructor); }
(
LOOKAHEAD(2)
<COMMA> rowConstructor = RowConstructor() { list.add(rowConstructor); }
)*
}
/**
* 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),
(List<SqlNode>) valueList);
}
}
/**
* 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) :
{
final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
ExpressionCommaList2(list, exprContext) {
return new SqlNodeList(list, s.addAll(list).pos());
}
}
/**
* Parses a list of expressions separated by commas,
* appending expressions to a given list.
*/
void ExpressionCommaList2(List<SqlNode> list, ExprContext exprContext) :
{
SqlNode e;
}
{
e = Expression(exprContext) { list.add(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);
}
)*
}
/**
* 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;
}
{
{ s = span(); } <FOR> <SYSTEM_TIME> <AS> <OF>
// Syntax for temporal table in
// standard SQL 2011 IWD 9075-2:201?(E) 7.6 <table reference>
// supports grammar as following:
// 1. datetime literal
// 2. datetime value function, i.e. CURRENT_TIMESTAMP
// 3. datetime term in 1 or 2 +(or -) interval term
// We extend to support column reference, use Expression
// to simplify the parsing code.
e = Expression(ExprContext.ACCEPT_NON_QUERY) {
return new SqlSnapshot(s.end(this), tableRef, e);
}
}
/** Parses a PIVOT clause following a table expression. */
SqlNode Pivot(SqlNode tableRef) :
{
final Span s;
final Span s2;
final List<SqlNode> aggList = new ArrayList<SqlNode>();
final List<SqlNode> valueList = new ArrayList<SqlNode>();
final SqlNodeList axisList;
final SqlNodeList inList;
}
{
<PIVOT> { s = span(); }
<LPAREN>
PivotAgg(aggList) ( <COMMA> PivotAgg(aggList) )*
<FOR> axisList = SimpleIdentifierOrList()
<IN> <LPAREN> { s2 = span(); }
[ PivotValue(valueList) ( <COMMA> PivotValue(valueList) )* ]
<RPAREN> {
inList = new SqlNodeList(valueList, s2.end(this));
}
<RPAREN>
{
return new SqlPivot(s.end(this), tableRef,
new SqlNodeList(aggList, SqlParserPos.sum(aggList)),
axisList, inList);
}
}
void PivotAgg(List<SqlNode> list) :
{
final SqlNode e;
final SqlIdentifier alias;
}
{
e = NamedFunctionCall()
(
[ <AS> ] alias = SimpleIdentifier() {
list.add(
SqlStdOperatorTable.AS.createCall(Span.of(e).end(this), e,
alias));
}
|
{ list.add(e); }
)
}
void PivotValue(List<SqlNode> list) :
{
final SqlNode e;
final SqlNodeList tuple;
final SqlIdentifier alias;
}
{
e = RowConstructor() { tuple = SqlParserUtil.stripRow(e); }
(
[ <AS> ] alias = SimpleIdentifier() {
list.add(
SqlStdOperatorTable.AS.createCall(Span.of(tuple).end(this),
tuple, alias));
}
|
{ list.add(tuple); }
)
}
/** Parses an UNPIVOT clause following a table expression. */
SqlNode Unpivot(SqlNode tableRef) :
{
final Span s;
final boolean includeNulls;
final SqlNodeList measureList;
final SqlNodeList axisList;
final Span s2;
final List<SqlNode> values = new ArrayList<SqlNode>();
final SqlNodeList inList;
}
{
<UNPIVOT> { s = span(); }
(
<INCLUDE> <NULLS> { includeNulls = true; }
| <EXCLUDE> <NULLS> { includeNulls = false; }
| { includeNulls = false; }
)
<LPAREN>
measureList = SimpleIdentifierOrList()
<FOR> axisList = SimpleIdentifierOrList()
<IN>
<LPAREN> { s2 = span(); }
UnpivotValue(values) ( <COMMA> UnpivotValue(values) )*
<RPAREN>
{ inList = new SqlNodeList(values, s2.end(this)); }
<RPAREN> {
return new SqlUnpivot(s.end(this), tableRef, includeNulls, measureList,
axisList, inList);
}
}
void UnpivotValue(List<SqlNode> list) :
{
final SqlNodeList columnList;
final SqlNode values;
}
{
columnList = SimpleIdentifierOrList()
(
<AS> values = RowConstructor() {
final SqlNodeList valueList = SqlParserUtil.stripRow(values);
list.add(
SqlStdOperatorTable.AS.createCall(Span.of(columnList).end(this),
columnList, valueList));
}
|
{ list.add(columnList); }
)
}
/**
* 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;
SqlNode ext;
}
{
(
LOOKAHEAD(1)
op = PrefixRowOperator() {
checkNonQueryExpression(exprContext);
list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
}
)*
e = Expression3(exprContext) {
list.add(e);
}
(
LOOKAHEAD(2) <DOT>
ext = RowExpressionExtension() {
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.DOT, getPos()));
list.add(ext);
}
)*
}
/**
* 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; }
|
<ILIKE> { op = SqlLibraryOperators.NOT_ILIKE; }
|
<RLIKE> { op = SqlLibraryOperators.NOT_RLIKE; }
|
<SIMILAR> <TO> { op = SqlStdOperatorTable.NOT_SIMILAR_TO; }
)
|
<LIKE> { op = SqlStdOperatorTable.LIKE; }
|
<ILIKE> { op = SqlLibraryOperators.ILIKE; }
|
<RLIKE> { op = SqlLibraryOperators.RLIKE; }
|
<SIMILAR> <TO> { op = SqlStdOperatorTable.SIMILAR_TO; }
)
<#if (parser.includePosixOperators!default.parser.includePosixOperators)>
|
<NEGATE> <TILDE> { op = SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_SENSITIVE; }
[ <STAR> { op = SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_INSENSITIVE; } ]
|
<TILDE> { op = SqlStdOperatorTable.POSIX_REGEX_CASE_SENSITIVE; }
[ <STAR> { op = SqlStdOperatorTable.POSIX_REGEX_CASE_INSENSITIVE; } ]
</#if>
)
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);
}
]
|
<#list (parser.extraBinaryExpressions!default.parser.extraBinaryExpressions) as extra >
${extra}(list, exprContext, s)
|
</#list>
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 SqlUtil.newContextException(getPos(), RESOURCE.bangEqualNotAllowed());
}
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 = ParenthesizedQueryOrCommaList(exprContext) {
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),
(List<SqlNode>) list1);
}
}
[
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),
(List<SqlNode>) list1);
}
}
}
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 a row expression extension, it can be either an identifier,
* or a call to a named function.
*/
SqlNode RowExpressionExtension() :
{
final SqlFunctionCategory funcType;
final SqlIdentifier p;
final Span s;
final List<SqlNode> args;
SqlCall call;
SqlNode e;
SqlLiteral quantifier = null;
}
{
p = SimpleIdentifier() {
e = p;
}
(
LOOKAHEAD( <LPAREN> ) { s = span(); }
{
funcType = SqlFunctionCategory.USER_DEFINED_FUNCTION;
}
(
LOOKAHEAD(2) <LPAREN> <STAR> {
args = startList(SqlIdentifier.star(getPos()));
}
<RPAREN>
|
LOOKAHEAD(2) <LPAREN> <RPAREN> {
args = Collections.emptyList();
}
|
args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
quantifier = (SqlLiteral) args.get(0);
args.remove(0);
}
)
{
call = createCall(p, s.end(this), funcType, quantifier, args);
e = call;
}
)?
{
return e;
}
}
/**
* Parses a call to the STRING_AGG aggregate function (or to an aggregate
* function with similar syntax: ARRAY_AGG, ARRAY_CONCAT_AGG, GROUP_CONCAT).
*/
SqlCall StringAggFunctionCall() :
{
final Span s, s2;
final SqlOperator op;
final List<SqlNode> args = new ArrayList();
final SqlLiteral qualifier;
final SqlNodeList orderBy;
final Pair<SqlParserPos, SqlOperator> nullTreatment;
final SqlNode separator;
}
{
(
<ARRAY_AGG> { s = span(); op = SqlLibraryOperators.ARRAY_AGG; }
| <ARRAY_CONCAT_AGG> { s = span(); op = SqlLibraryOperators.ARRAY_CONCAT_AGG; }
| <GROUP_CONCAT> { s = span(); op = SqlLibraryOperators.GROUP_CONCAT; }
| <STRING_AGG> { s = span(); op = SqlLibraryOperators.STRING_AGG; }
)
<LPAREN>
(
qualifier = AllOrDistinct()
|
{ qualifier = null; }
)
Arg(args, ExprContext.ACCEPT_SUB_QUERY)
(
<COMMA> {
// a comma-list can't appear where only a query is expected
checkNonQueryExpression(ExprContext.ACCEPT_SUB_QUERY);
}
Arg(args, ExprContext.ACCEPT_SUB_QUERY)
)*
(
nullTreatment = NullTreatment()
|
{ nullTreatment = null; }
)
[
orderBy = OrderBy(true) {
args.add(orderBy);
}
]
[
<SEPARATOR> { s2 = span(); } separator = StringLiteral() {
args.add(SqlInternalOperators.SEPARATOR.createCall(s2.end(this), separator));
}
]
<RPAREN>
{
SqlCall call = op.createCall(qualifier, s.end(this), args);
if (nullTreatment != null) {
// Wrap in RESPECT_NULLS or IGNORE_NULLS.
call = nullTreatment.right.createCall(nullTreatment.left, call);
}
return call;
}
}
/**
* Parses an atomic row expression.
*/
SqlNode AtomicRowExpression() :
{
final SqlNode e;
}
{
(
e = LiteralOrIntervalExpression()
|
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(); }
|
LOOKAHEAD(3)
<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!default.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!default.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!default.parser.createStatementParserMethods) as method>
create = ${method}(s, replace)
<#sep>| LOOKAHEAD(2) </#sep>
</#list>
)
{
return create;
}
}
</#if>
<#if (parser.dropStatementParserMethods!default.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!default.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.
*
* <p>If the context allows both literals and expressions,
* use {@link #LiteralOrIntervalExpression}, which requires less
* lookahead.
*/
SqlNode Literal() :
{
SqlNode e;
}
{
(
e = NonIntervalLiteral()
|
e = IntervalLiteral()
)
{ return e; }
}
/** Parses a literal that is not an interval literal. */
SqlNode NonIntervalLiteral() :
{
final SqlNode e;
}
{
(
e = NumericLiteral()
|
e = StringLiteral()
|
e = SpecialLiteral()
|
e = DateTimeLiteral()
<#-- additional literal parser methods are included here -->
<#list (parser.literalParserMethods!default.parser.literalParserMethods) as method>
|
e = ${method}
</#list>
)
{
return e;
}
}
/** Parses a literal or an interval expression.
*
* <p>We include them in the same production because it is difficult to
* distinguish interval literals from interval expression (both of which
* start with the {@code INTERVAL} keyword); this way, we can use less
* LOOKAHEAD. */
SqlNode LiteralOrIntervalExpression() :
{
final SqlNode e;
}
{
(
e = IntervalLiteralOrExpression()
|
e = NonIntervalLiteral()
)
{ 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;
String charSet = null;
SqlCharStringLiteral literal;
}
{
// 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));
}
}
(
// The grammar is ambiguous when a continued literals and a character
// string alias are both possible. For example, in
// SELECT x'01'\n'ab'
// we prefer that 'ab' continues the literal, and is not an alias.
// The following LOOKAHEAD mutes the warning about ambiguity.
LOOKAHEAD(1)
<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);
}
}
|
(
<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";
}