All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javacc.Parser.jj Maven / Gradle / Ivy

The newest version!

options {
    STATIC = false;
    IGNORE_CASE = true;
    UNICODE_INPUT = true;
}


PARSER_BEGIN(SqlDdlParserImpl)

package org.apache.calcite.sql.parser.ddl;

import org.apache.calcite.schema.ColumnStrategy;
import org.apache.calcite.sql.SqlCreate;
import org.apache.calcite.sql.SqlDrop;
import org.apache.calcite.sql.ddl.SqlDdlNodes;
import java.util.Map;
import java.util.HashMap;

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.SqlIdentifier;
import org.apache.calcite.sql.SqlInsert;
import org.apache.calcite.sql.SqlInsertKeyword;
import org.apache.calcite.sql.SqlIntervalLiteral;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.SqlJdbcDataTypeName;
import org.apache.calcite.sql.SqlJdbcFunctionCall;
import org.apache.calcite.sql.SqlJoin;
import org.apache.calcite.sql.SqlJsonConstructorNullClause;
import org.apache.calcite.sql.SqlJsonEncoding;
import org.apache.calcite.sql.SqlJsonExistsErrorBehavior;
import org.apache.calcite.sql.SqlJsonEmptyOrError;
import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior;
import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior;
import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlMatchRecognize;
import org.apache.calcite.sql.SqlMerge;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlNumericLiteral;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlOrderBy;
import org.apache.calcite.sql.SqlPostfixOperator;
import org.apache.calcite.sql.SqlPrefixOperator;
import org.apache.calcite.sql.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.SqlTimeLiteral;
import org.apache.calcite.sql.SqlTimestampLiteral;
import org.apache.calcite.sql.SqlTypeNameSpec;
import org.apache.calcite.sql.SqlUnnestOperator;
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.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.
 *
 * 

The public wrapper for this parser is {@link SqlParser}. */ public class SqlDdlParserImpl 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 SqlDdlParserImpl parser = new SqlDdlParserImpl(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 (SqlDdlParserImpl.class) { if (metadata == null) { metadata = new MetadataImpl( new SqlDdlParserImpl(new java.io.StringReader(""))); } return metadata; } } public void setTabSize(int tabSize) { jj_input_stream.setTabSize(tabSize); } public void switchTo(String stateName) { int state = Arrays.asList(SqlDdlParserImplTokenManager.lexStateNames) .indexOf(stateName); token_source.SwitchTo(state); } public void setQuotedCasing(Casing quotedCasing) { this.quotedCasing = quotedCasing; } public void setUnquotedCasing(Casing unquotedCasing) { this.unquotedCasing = unquotedCasing; } public void setIdentifierMaxLength(int identifierMaxLength) { this.identifierMaxLength = identifierMaxLength; } public void setConformance(SqlConformance conformance) { this.conformance = conformance; } public SqlNode parseSqlExpressionEof() throws Exception { return SqlExpressionEof(); } public SqlNode parseSqlStmtEof() throws Exception { return SqlStmtEof(); } public SqlNodeList parseSqlStmtList() throws Exception { return SqlStmtList(); } private SqlNode extend(SqlNode table, SqlNodeList extendList) { return SqlStdOperatorTable.EXTEND.createCall( Span.of(table, extendList).pos(), table, extendList); } } PARSER_END(SqlDdlParserImpl) /*************************************** * Utility Codes for Semantic Analysis * ***************************************/ /* For Debug */ JAVACODE void debug_message1() { LOGGER.info("{} , {}", getToken(0).image, getToken(1).image); } JAVACODE String unquotedIdentifier() { return SqlParserUtil.strip(getToken(0).image, null, null, null, unquotedCasing); } /** * Allows parser to be extended with new types of table references. The * default implementation of this production is empty. */ SqlNode ExtendedTableRef() : { } { UnusedExtension() { return null; } } /** * Allows an OVER clause following a table expression as an extension to * standard SQL syntax. The default implementation of this production is empty. */ SqlNode TableOverOpt() : { } { { return null; } } /* * Parses dialect-specific keywords immediately following the SELECT keyword. */ void SqlSelectKeywords(List keywords) : {} { E() } /* * Parses dialect-specific keywords immediately following the INSERT keyword. */ void SqlInsertKeywords(List 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; 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(""); // Find all sequences in the error which contain identifier. For // example, // {} // {A} // {B, C} // {D, } // {D, A} // {D, B} // // would yield // {} // {D} boolean id = false; final List prefixList = new ArrayList(); 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 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 list = new ArrayList(); 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. * *

Postgres syntax for limit: * *

 *    [ LIMIT { count | ALL } ]
 *    [ OFFSET start ]
*
* *

MySQL syntax for limit: * *

 *    [ LIMIT { count | start, count } ]
*
* *

SQL:2008 syntax for limit: * *

 *    [ OFFSET start { ROW | ROWS } ]
 *    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]
*
*/ 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 ..." ( // MySQL-style syntax. "LIMIT start, count" LOOKAHEAD(2) start = UnsignedNumericLiteralOrParam() count = UnsignedNumericLiteralOrParam() { if (!this.conformance.isLimitStartCountAllowed()) { throw SqlUtil.newContextException(getPos(), RESOURCE.limitStartCountNotAllowed()); } } | count = UnsignedNumericLiteralOrParam() | ) ] [ // 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. start = UnsignedNumericLiteralOrParam() [ | ] ] [ // SQL:2008-style syntax. "OFFSET ... FETCH ...". // If you specify both LIMIT and FETCH, FETCH wins. ( | ) count = UnsignedNumericLiteralOrParam() ( | ) ] { 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; } { { // 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) { return e; } } /** * Parses a parenthesized query or comma-list of row expressions. * *

REVIEW jvs 8-Feb-2004: There's a small hole in this production. It can be * used to construct something like * *

 * WHERE x IN (select count(*) from t where c=d,5)
*
* *

which should be illegal. The above is interpreted as equivalent to * *

 * WHERE x IN ((select count(*) from t where c=d),5)
*
* *

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 list; ExprContext firstExprContext = exprContext; final Span s; } { { // 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); } ( { // a comma-list can't appear where only a query is expected checkNonQueryExpression(exprContext); } e = Expression(exprContext) { list.add(e); } )* { 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 list; ExprContext firstExprContext = exprContext; final Span s; } { { // 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); } ( { // a comma-list can't appear where only a query is expected checkNonQueryExpression(exprContext); } ( e = Expression(exprContext) | e = Default() ) { list.add(e); } )* { return new SqlNodeList(list, s.end(this)); } } /** * Parses function parameter lists including DISTINCT keyword recognition, * DEFAULT, and named argument assignment. */ List FunctionParameterList( ExprContext exprContext) : { SqlNode e = null; List list = new ArrayList(); } { [ { e = SqlSelectKeyword.DISTINCT.symbol(getPos()); } | { e = SqlSelectKeyword.ALL.symbol(getPos()); } ] { list.add(e); } Arg0(list, exprContext) ( { // a comma-list can't appear where only a query is expected checkNonQueryExpression(exprContext); } Arg(list, exprContext) )* { return list; } } void Arg0(List list, ExprContext exprContext) : { SqlIdentifier name = null; SqlNode e = null; final ExprContext firstExprContext; { // we've now seen left paren, so queries inside should // be allowed as sub-queries switch (exprContext) { case ACCEPT_SUB_QUERY: firstExprContext = ExprContext.ACCEPT_NONCURSOR; break; case ACCEPT_CURSOR: firstExprContext = ExprContext.ACCEPT_ALL; break; default: firstExprContext = exprContext; break; } } } { [ LOOKAHEAD(2) name = SimpleIdentifier() ] ( 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() ] ( 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() : {} { { 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) { 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 stmtList = new ArrayList(); SqlNode stmt; } { stmt = SqlStmt() { stmtList.add(stmt); } ( [ stmt = SqlStmt() { stmtList.add(stmt); } ] )* { return new SqlNodeList(stmtList, Span.of(stmtList).pos()); } } /** * Parses an SQL statement. */ SqlNode SqlStmt() : { SqlNode stmt; } { ( stmt = SqlSetOption(Span.of(), null) | stmt = SqlAlter() | stmt = SqlCreate() | stmt = SqlDrop() | 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() { return stmt; } } boolean IfNotExistsOpt() : { } { { return true; } | { return false; } } boolean IfExistsOpt() : { } { { return true; } | { return false; } } SqlCreate SqlCreateSchema(Span s, boolean replace) : { final boolean ifNotExists; final SqlIdentifier id; } { ifNotExists = IfNotExistsOpt() id = CompoundIdentifier() { return SqlDdlNodes.createSchema(s.end(this), replace, ifNotExists, id); } } SqlCreate SqlCreateForeignSchema(Span s, boolean replace) : { final boolean ifNotExists; final SqlIdentifier id; SqlNode type = null; SqlNode library = null; SqlNodeList optionList = null; } { ifNotExists = IfNotExistsOpt() id = CompoundIdentifier() ( type = StringLiteral() | library = StringLiteral() ) [ optionList = Options() ] { return SqlDdlNodes.createForeignSchema(s.end(this), replace, ifNotExists, id, type, library, optionList); } } SqlNodeList Options() : { final Span s; final List list = new ArrayList(); } { { s = span(); } [ Option(list) ( Option(list) )* ] { return new SqlNodeList(list, s.end(this)); } } void Option(List list) : { final SqlIdentifier id; final SqlNode value; } { id = SimpleIdentifier() value = Literal() { list.add(id); list.add(value); } } SqlNodeList TableElementList() : { final Span s; final List list = new ArrayList(); } { { s = span(); } TableElement(list) ( TableElement(list) )* { return new SqlNodeList(list, s.end(this)); } } void TableElement(List list) : { final SqlIdentifier id; final SqlDataTypeSpec type; final boolean nullable; final SqlNode e; final SqlNode constraint; SqlIdentifier name = null; final SqlNodeList columnList; final Span s = Span.of(); final ColumnStrategy strategy; } { LOOKAHEAD(2) id = SimpleIdentifier() ( type = DataType() nullable = NullableOptDefaultTrue() ( [ ] e = Expression(ExprContext.ACCEPT_SUB_QUERY) ( { strategy = ColumnStrategy.VIRTUAL; } | { strategy = ColumnStrategy.STORED; } | { strategy = ColumnStrategy.VIRTUAL; } ) | e = Expression(ExprContext.ACCEPT_SUB_QUERY) { strategy = ColumnStrategy.DEFAULT; } | { e = null; strategy = nullable ? ColumnStrategy.NULLABLE : ColumnStrategy.NOT_NULLABLE; } ) { list.add( SqlDdlNodes.column(s.add(id).end(this), id, type.withNullable(nullable), e, strategy)); } | { list.add(id); } ) | id = SimpleIdentifier() { list.add(id); } | [ { s.add(this); } name = SimpleIdentifier() ] ( { s.add(this); } e = Expression(ExprContext.ACCEPT_SUB_QUERY) { list.add(SqlDdlNodes.check(s.end(this), name, e)); } | { s.add(this); } columnList = ParenthesizedSimpleIdentifierList() { list.add(SqlDdlNodes.unique(s.end(columnList), name, columnList)); } | { s.add(this); } columnList = ParenthesizedSimpleIdentifierList() { list.add(SqlDdlNodes.primary(s.end(columnList), name, columnList)); } ) } SqlNodeList AttributeDefList() : { final Span s; final List list = new ArrayList(); } { { s = span(); } AttributeDef(list) ( AttributeDef(list) )* { return new SqlNodeList(list, s.end(this)); } } void AttributeDef(List list) : { final SqlIdentifier id; final SqlDataTypeSpec type; final boolean nullable; SqlNode e = null; final Span s = Span.of(); } { id = SimpleIdentifier() ( type = DataType() nullable = NullableOptDefaultTrue() ) [ e = Expression(ExprContext.ACCEPT_SUB_QUERY) ] { list.add(SqlDdlNodes.attribute(s.add(id).end(this), id, type.withNullable(nullable), e, null)); } } SqlCreate SqlCreateType(Span s, boolean replace) : { final SqlIdentifier id; SqlNodeList attributeDefList = null; SqlDataTypeSpec type = null; } { id = CompoundIdentifier() ( attributeDefList = AttributeDefList() | type = DataType() ) { return SqlDdlNodes.createType(s.end(this), replace, id, attributeDefList, type); } } SqlCreate SqlCreateTable(Span s, boolean replace) : { final boolean ifNotExists; final SqlIdentifier id; SqlNodeList tableElementList = null; SqlNode query = null; } {

ifNotExists = IfNotExistsOpt() id = CompoundIdentifier() [ tableElementList = TableElementList() ] [ query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) ] { return SqlDdlNodes.createTable(s.end(this), replace, ifNotExists, id, tableElementList, query); } } SqlCreate SqlCreateView(Span s, boolean replace) : { final SqlIdentifier id; SqlNodeList columnList = null; final SqlNode query; } { id = CompoundIdentifier() [ columnList = ParenthesizedSimpleIdentifierList() ] query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) { return SqlDdlNodes.createView(s.end(this), replace, id, columnList, query); } } SqlCreate SqlCreateMaterializedView(Span s, boolean replace) : { final boolean ifNotExists; final SqlIdentifier id; SqlNodeList columnList = null; final SqlNode query; } { ifNotExists = IfNotExistsOpt() id = CompoundIdentifier() [ columnList = ParenthesizedSimpleIdentifierList() ] query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) { return SqlDdlNodes.createMaterializedView(s.end(this), replace, ifNotExists, id, columnList, query); } } private void FunctionJarDef(SqlNodeList usingList) : { final SqlDdlNodes.FileType fileType; final SqlNode uri; } { ( { fileType = SqlDdlNodes.FileType.ARCHIVE; } | { fileType = SqlDdlNodes.FileType.FILE; } | { fileType = SqlDdlNodes.FileType.JAR; } ) { usingList.add(SqlLiteral.createSymbol(fileType, getPos())); } uri = StringLiteral() { usingList.add(uri); } } SqlCreate SqlCreateFunction(Span s, boolean replace) : { final boolean ifNotExists; final SqlIdentifier id; final SqlNode className; SqlNodeList usingList = SqlNodeList.EMPTY; } { ifNotExists = IfNotExistsOpt() id = CompoundIdentifier() className = StringLiteral() [ { usingList = new SqlNodeList(getPos()); } FunctionJarDef(usingList) ( FunctionJarDef(usingList) )* ] { return SqlDdlNodes.createFunction(s.end(this), replace, ifNotExists, id, className, usingList); } } SqlDrop SqlDropSchema(Span s, boolean replace) : { final boolean ifExists; final SqlIdentifier id; final boolean foreign; } { ( { foreign = true; } | { foreign = false; } ) ifExists = IfExistsOpt() id = CompoundIdentifier() { return SqlDdlNodes.dropSchema(s.end(this), foreign, ifExists, id); } } SqlDrop SqlDropType(Span s, boolean replace) : { final boolean ifExists; final SqlIdentifier id; } { ifExists = IfExistsOpt() id = CompoundIdentifier() { return SqlDdlNodes.dropType(s.end(this), ifExists, id); } } SqlDrop SqlDropTable(Span s, boolean replace) : { final boolean ifExists; final SqlIdentifier id; } {
ifExists = IfExistsOpt() id = CompoundIdentifier() { return SqlDdlNodes.dropTable(s.end(this), ifExists, id); } } SqlDrop SqlDropView(Span s, boolean replace) : { final boolean ifExists; final SqlIdentifier id; } { ifExists = IfExistsOpt() id = CompoundIdentifier() { return SqlDdlNodes.dropView(s.end(this), ifExists, id); } } SqlDrop SqlDropMaterializedView(Span s, boolean replace) : { final boolean ifExists; final SqlIdentifier id; } { ifExists = IfExistsOpt() id = CompoundIdentifier() { return SqlDdlNodes.dropMaterializedView(s.end(this), ifExists, id); } } SqlDrop SqlDropFunction(Span s, boolean replace) : { final boolean ifExists; final SqlIdentifier id; } { ifExists = IfExistsOpt() id = CompoundIdentifier() { return SqlDdlNodes.dropFunction(s.end(this), ifExists, id); } } // End parserImpls.ftl /** * Parses a leaf SELECT expression without ORDER BY. */ SqlSelect SqlSelect() : { final List keywords = new ArrayList(); final SqlNodeList keywordList; List selectList; final SqlNode fromClause; final SqlNode where; final SqlNodeList groupBy; final SqlNode having; final SqlNodeList windowDecls; final Span s; } {
| | | | ) (
)? table = CompoundIdentifier() ( column = SimpleIdentifier() | E() { column = null; } ) { return new SqlDescribeTable(s.add(table).addIf(column).pos(), table, column); } | (LOOKAHEAD(1) )? 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; } { { 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 list = new ArrayList(); final Span s; } { name = CompoundIdentifier() { s = span(); } [ Arg0(list, exprContext) ( { // a comma-list can't appear where only a query is expected checkNonQueryExpression(exprContext); } Arg(list, exprContext) )* ] { return createCall(name, s.end(this), routineType, null, list); } } /** * Parses an INSERT statement. */ SqlNode SqlInsert() : { final List keywords = new ArrayList(); final SqlNodeList keywordList; SqlNode table; SqlNodeList extendList = null; SqlNode source; SqlNodeList columnList = null; final Span s; } { ( | { keywords.add(SqlInsertKeyword.UPSERT.symbol(getPos())); } ) { s = span(); } SqlInsertKeywords(keywords) { keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos()); } table = CompoundIdentifier() [ LOOKAHEAD(5) [ ] extendList = ExtendList() { table = extend(table, extendList); } ] [ LOOKAHEAD(2) { final Pair 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; } { { s = span(); } table = CompoundIdentifier() [ [ ] extendList = ExtendList() { table = extend(table, extendList); } ] [ [ ] 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; } { { s = span(); } table = CompoundIdentifier() { targetColumnList = new SqlNodeList(s.pos()); sourceExpressionList = new SqlNodeList(s.pos()); } [ [ ] extendList = ExtendList() { table = extend(table, extendList); } ] [ [ ] alias = SimpleIdentifier() ] id = SimpleIdentifier() { targetColumnList.add(id); } exp = Expression(ExprContext.ACCEPT_SUB_QUERY) { // TODO: support DEFAULT also sourceExpressionList.add(exp); } ( id = SimpleIdentifier() { targetColumnList.add(id); } 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; } { { s = span(); } table = CompoundIdentifier() [ [ ] extendList = ExtendList() { table = extend(table, extendList); } ] [ [ ] alias = SimpleIdentifier() ] sourceTableRef = TableRef() 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); } { { s = span(); } id = CompoundIdentifier() { updateColumnList.add(id); } exp = Expression(ExprContext.ACCEPT_SUB_QUERY) { updateExprList.add(exp); } ( id = CompoundIdentifier() { updateColumnList.add(id); } 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 keywords = new ArrayList(); final SqlNodeList keywordList; SqlNodeList insertColumnList = null; SqlNode rowConstructor; SqlNode insertValues; } { { insertSpan = span(); } SqlInsertKeywords(keywords) { keywordList = new SqlNodeList(keywords, insertSpan.end(this)); } [ LOOKAHEAD(2) insertColumnList = ParenthesizedSimpleIdentifierList() ] [ ] { valuesSpan = span(); } rowConstructor = RowConstructor() [ ] { // 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 SelectList() : { final List list = new ArrayList(); SqlNode item; } { item = SelectItem() { list.add(item); } ( item = SelectItem() { list.add(item); } )* { return list; } } /** * Parses one item in a select list. */ SqlNode SelectItem() : { SqlNode e; final SqlIdentifier id; } { e = SelectExpression() [ [ ] id = SimpleIdentifier() { e = SqlStdOperatorTable.AS.createCall(span().end(e), e, id); } ] { return e; } } /** * Parses one unaliased expression in a select list. */ SqlNode SelectExpression() : { SqlNode e; } { { return SqlIdentifier.star(getPos()); } | e = Expression(ExprContext.ACCEPT_SUB_QUERY) { return e; } } SqlLiteral Natural() : { } { { return SqlLiteral.createBoolean(true, getPos()); } | { return SqlLiteral.createBoolean(false, getPos()); } } SqlLiteral JoinType() : { JoinType joinType; } { ( { joinType = JoinType.INNER; } | { joinType = JoinType.INNER; } | [ ] { joinType = JoinType.LEFT; } | [ ] { joinType = JoinType.RIGHT; } | [ ] { joinType = JoinType.FULL; } | { 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 = JoinConditionType.ON.symbol(getPos()); } condition = Expression(ExprContext.ACCEPT_SUB_QUERY) { return new SqlJoin(joinType.getParserPosition(), e, natural, joinType, e2, on, condition); } | { using = JoinConditionType.USING.symbol(getPos()); } list = ParenthesizedSimpleIdentifierList() { return new SqlJoin(joinType.getParserPosition(), e, natural, joinType, e2, using, new SqlNodeList(list.getList(), Span.of(using).end(this))); } | { return new SqlJoin(joinType.getParserPosition(), e, natural, joinType, e2, JoinConditionType.NONE.symbol(joinType.getParserPosition()), null); } ) } // TODO jvs 15-Nov-2003: SQL standard allows parentheses in the FROM list for // building up non-linear join trees (e.g. OUTER JOIN two tables, and then INNER // JOIN the result). Also note that aliases on parenthesized FROM expressions // "hide" all table names inside the parentheses (without aliases, they're // visible). // // We allow CROSS JOIN to have a join condition, even though that is not valid // SQL; the validator will catch it. /** * Parses the FROM clause for a SELECT. * *

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 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 "" token. Where we expect // the lookahead succeeds after " ". // // For more information about the issue, see https://github.com/javacc/javacc/issues/86 LOOKAHEAD(3) natural = Natural() joinType = JoinType() e2 = TableRef() ( { joinConditionType = JoinConditionType.ON.symbol(getPos()); } condition = Expression(ExprContext.ACCEPT_SUB_QUERY) { e = new SqlJoin(joinType.getParserPosition(), e, natural, joinType, e2, joinConditionType, condition); } | { 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}) { 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); } | { joinType = JoinType.CROSS.symbol(getPos()); } 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); } | { joinType = JoinType.LEFT.symbol(getPos()); } 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 = CompoundIdentifier() [ [ ] extendList = ExtendList() { tableRef = extend(tableRef, extendList); } ] over = TableOverOpt() { if (over != null) { tableRef = SqlStdOperatorTable.OVER.createCall( getPos(), tableRef, over); } } [ snapshot = Snapshot(tableRef) { tableRef = SqlStdOperatorTable.LATERAL.createCall( getPos(), snapshot); } ] [ tableRef = MatchRecognize(tableRef) ] | LOOKAHEAD(2) [ { lateral = 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) ] | { s = span(); } args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY) [ { unnestOp = SqlStdOperatorTable.UNNEST_WITH_ORDINALITY; } ] { tableRef = unnestOp.createCall(s.end(this), args.toArray()); } | [ { lateral = true; } ]

{ s = span(); } tableRef = TableFunctionCall(s.pos()) { if (lateral) { tableRef = SqlStdOperatorTable.LATERAL.createCall( s.end(this), tableRef); } } | tableRef = ExtendedTableRef() ) [ [ ] alias = SimpleIdentifier() [ columnAliasList = ParenthesizedSimpleIdentifierList() ] { if (columnAliasList == null) { tableRef = SqlStdOperatorTable.AS.createCall( Span.of(tableRef).end(this), tableRef, alias); } else { List idList = new ArrayList(); idList.add(tableRef); idList.add(alias); idList.addAll(columnAliasList.getList()); tableRef = SqlStdOperatorTable.AS.createCall( Span.of(tableRef).end(this), idList); } } ] [ { s2 = span(); } ( sample = StringLiteral() { 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); } | ( { isBernoulli = true; } | { isBernoulli = false; } ) samplePercentage = UnsignedNumericLiteral() [ repeatableSeed = IntLiteral() { 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 list = new ArrayList(); } { { s = span(); } ColumnType(list) ( ColumnType(list) )* { return new SqlNodeList(list, s.end(this)); } } void ColumnType(List list) : { SqlIdentifier name; SqlDataTypeSpec type; boolean nullable = true; } { name = CompoundIdentifier() type = DataType() [ { nullable = false; } ] { list.add(name); list.add(type.withNullable(nullable)); } } /** * Parses a compound identifier with optional type. */ void CompoundIdentifierType(List list, List extendList) : { final SqlIdentifier name; SqlDataTypeSpec type = null; boolean nullable = true; } { name = CompoundIdentifier() [ type = DataType() { if (!this.conformance.allowExtend()) { throw SqlUtil.newContextException(getPos(), RESOURCE.extendNotAllowed()); } } [ { nullable = false; } ] ] { if (type != null) { extendList.add(name); extendList.add(type.withNullable(nullable)); } list.add(name); } } SqlNode TableFunctionCall(SqlParserPos pos) : { SqlNode call; SqlFunctionCategory funcType = SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION; } { [ { funcType = SqlFunctionCategory.USER_DEFINED_TABLE_SPECIFIC_FUNCTION; } ] call = NamedRoutineCall(funcType, ExprContext.ACCEPT_CURSOR) { return SqlStdOperatorTable.COLLECTION_TABLE.createCall(pos, call); } } /** * Abstract production: * SqlNode ExtendedTableRef() * *

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; } {

tableRef = CompoundIdentifier() { return SqlStdOperatorTable.EXPLICIT_TABLE.createCall(pos, tableRef); } } /** * Parses a VALUES leaf query expression. */ SqlNode TableConstructor() : { SqlNodeList rowConstructorList; final Span s; } { { s = span(); } rowConstructorList = RowConstructorList(s) { return SqlStdOperatorTable.VALUES.createCall( s.end(this), rowConstructorList.toArray()); } } /** * Parses one or more rows in a VALUES expression. */ SqlNodeList RowConstructorList(Span s) : { List list = new ArrayList(); SqlNode rowConstructor; } { rowConstructor = RowConstructor() { list.add(rowConstructor); } ( LOOKAHEAD(2) rowConstructor = RowConstructor() { list.add(rowConstructor); } )* { return new SqlNodeList(list, s.end(this)); } } /** * Parses a row constructor in the context of a VALUES expression. */ SqlNode RowConstructor() : { SqlNodeList valueList; SqlNode value; final Span s; } { // hints are necessary here due to common LPAREN prefixes ( // TODO jvs 8-Feb-2004: extra parentheses are accepted here as a hack // for unparse, but this is actually invalid SQL; should // fix unparse LOOKAHEAD(3) { s = span(); } valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR) { s.add(this); } | LOOKAHEAD(3) ( { s = span(); } | { s = Span.of(); } ) valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR) | value = Expression(ExprContext.ACCEPT_NONCURSOR) { // NOTE: A bare value here is standard SQL syntax, believe it or // not. Taken together with multi-row table constructors, it leads // to very easy mistakes if you forget the parentheses on a // single-row constructor. This is also the reason for the // LOOKAHEAD in RowConstructorList(). It would be so much more // reasonable to require parentheses. Sigh. s = Span.of(value); valueList = new SqlNodeList(Collections.singletonList(value), value.getParserPosition()); } ) { // REVIEW jvs 8-Feb-2004: Should we discriminate between scalar // sub-queries inside of ROW and row sub-queries? The standard does, // but the distinction seems to be purely syntactic. return SqlStdOperatorTable.ROW.createCall(s.end(valueList), valueList.toArray()); } } /** * Parses the optional WHERE clause for SELECT, DELETE, and UPDATE. */ SqlNode WhereOpt() : { SqlNode condition; } { condition = Expression(ExprContext.ACCEPT_SUB_QUERY) { return condition; } | { return null; } } /** * Parses the optional GROUP BY clause for SELECT. */ SqlNodeList GroupByOpt() : { List list = new ArrayList(); final Span s; } { { s = span(); } list = GroupingElementList() { return new SqlNodeList(list, s.addAll(list).pos()); } | { return null; } } List GroupingElementList() : { List list = new ArrayList(); SqlNode e; } { e = GroupingElement() { list.add(e); } ( LOOKAHEAD(2) e = GroupingElement() { list.add(e); } )* { return list; } } SqlNode GroupingElement() : { List list; final SqlNodeList nodes; final SqlNode e; final Span s; } { LOOKAHEAD(2) { s = span(); } list = GroupingElementList() { return SqlStdOperatorTable.GROUPING_SETS.createCall(s.end(this), list); } | { s = span(); } nodes = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) { return SqlStdOperatorTable.ROLLUP.createCall(s.end(this), nodes.getList()); } | { s = span(); } nodes = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) { return SqlStdOperatorTable.CUBE.createCall(s.end(this), nodes.getList()); } | LOOKAHEAD(3) { return new SqlNodeList(getPos()); } | e = Expression(ExprContext.ACCEPT_SUB_QUERY) { return e; } } /** * Parses a list of expressions separated by commas. */ SqlNodeList ExpressionCommaList( final Span s, ExprContext exprContext) : { List list; SqlNode e; } { e = Expression(exprContext) { list = startList(e); } ( // NOTE jvs 6-Feb-2004: See comments at top of file for why // hint is necessary here. LOOKAHEAD(2) e = Expression(ExprContext.ACCEPT_SUB_QUERY) { list.add(e); } )* { return new SqlNodeList(list, s.addAll(list).pos()); } } /** * Parses the optional HAVING clause for SELECT. */ SqlNode HavingOpt() : { SqlNode e; } { e = Expression(ExprContext.ACCEPT_SUB_QUERY) { return e; } | { return null; } } /** * Parses the optional WINDOW clause for SELECT */ SqlNodeList WindowOpt() : { SqlIdentifier id; SqlWindow e; List list; final Span s; } { { s = span(); } id = SimpleIdentifier() 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) id = SimpleIdentifier() 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; } { { s = span(); } ( id = SimpleIdentifier() | { id = null; } ) ( { s1 = span(); } partitionList = ExpressionCommaList(s1, ExprContext.ACCEPT_NON_QUERY) | { partitionList = SqlNodeList.EMPTY; } ) ( orderList = OrderBy(true) | { orderList = SqlNodeList.EMPTY; } ) [ ( { isRows = SqlLiteral.createBoolean(true, getPos()); } | { isRows = SqlLiteral.createBoolean(false, getPos()); } ) ( lowerBound = WindowRange() upperBound = WindowRange() | lowerBound = WindowRange() ) ] [ { s2 = span(); } { allowPartial = SqlLiteral.createBoolean(true, s2.end(this)); } | { s2 = span(); } { allowPartial = SqlLiteral.createBoolean(false, s2.end(this)); } ] { return SqlWindow.create( null, id, partitionList, orderList, isRows, lowerBound, upperBound, allowPartial, s.end(this)); } } SqlNode WindowRange() : { final SqlNode e; final Span s; } { LOOKAHEAD(2) { s = span(); } { return SqlWindow.createCurrentRow(s.end(this)); } | LOOKAHEAD(2) { s = span(); } ( { return SqlWindow.createUnboundedPreceding(s.end(this)); } | { return SqlWindow.createUnboundedFollowing(s.end(this)); } ) | e = Expression(ExprContext.ACCEPT_NON_QUERY) ( { return SqlWindow.createPreceding(e, getPos()); } | { return SqlWindow.createFollowing(e, getPos()); } ) } /** * Parses an ORDER BY clause. */ SqlNodeList OrderBy(boolean accept) : { List list; SqlNode e; final Span s; } { { 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()); } } e = OrderItem() { list = startList(e); } ( // NOTE jvs 6-Feb-2004: See comments at top of file for why // hint is necessary here. LOOKAHEAD(2) 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) ( | { e = SqlStdOperatorTable.DESC.createCall(getPos(), e); } )? ( LOOKAHEAD(2) { e = SqlStdOperatorTable.NULLS_FIRST.createCall(getPos(), e); } | { 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(); } e = Expression(ExprContext.ACCEPT_NON_QUERY) { return new SqlSnapshot(s.end(this), tableRef, e); } } /** * Parses a MATCH_RECOGNIZE clause following a table expression. */ SqlMatchRecognize MatchRecognize(SqlNode tableRef) : { final Span s, s0, s1, s2; SqlNodeList measureList = SqlNodeList.EMPTY; SqlNodeList partitionList = SqlNodeList.EMPTY; SqlNodeList orderList = SqlNodeList.EMPTY; SqlNode pattern; SqlLiteral interval; SqlNodeList patternDefList; final SqlNode after; SqlParserPos pos; final SqlNode var; final SqlLiteral rowsPerMatch; SqlNodeList subsetList = SqlNodeList.EMPTY; SqlLiteral isStrictStarts = SqlLiteral.createBoolean(false, getPos()); SqlLiteral isStrictEnds = SqlLiteral.createBoolean(false, getPos()); } { { s = span(); } [ { s2 = span(); } partitionList = ExpressionCommaList(s2, ExprContext.ACCEPT_NON_QUERY) ] [ orderList = OrderBy(true) ] [ measureList = MeasureColumnCommaList(span()) ] ( { s0 = span(); } { rowsPerMatch = SqlMatchRecognize.RowsPerMatchOption.ONE_ROW.symbol(s0.end(this)); } | { s0 = span(); } { rowsPerMatch = SqlMatchRecognize.RowsPerMatchOption.ALL_ROWS.symbol(s0.end(this)); } | { rowsPerMatch = null; } ) ( { s1 = span(); } ( ( LOOKAHEAD(2) { after = SqlMatchRecognize.AfterOption.SKIP_TO_NEXT_ROW .symbol(s1.end(this)); } | LOOKAHEAD(2) 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, [] SimpleIdentifier())" // here. But the correct LOOKAHEAD should be // "LOOKAHEAD(2, [ LOOKAHEAD(2, SimpleIdentifier()) ] // SimpleIdentifier())" which have the syntactic lookahead for considered. // // Overall LOOKAHEAD({true}) is even better as this is the last branch in the // choice. LOOKAHEAD({true}) [ LOOKAHEAD(2, SimpleIdentifier()) ] var = SimpleIdentifier() { after = SqlMatchRecognize.SKIP_TO_LAST.createCall( s1.end(var), var); } ) | { after = SqlMatchRecognize.AfterOption.SKIP_PAST_LAST_ROW .symbol(s1.end(this)); } ) | { after = null; } ) ( { isStrictStarts = SqlLiteral.createBoolean(true, getPos()); } | { isStrictStarts = SqlLiteral.createBoolean(false, getPos()); } ) pattern = PatternExpression() ( { isStrictEnds = SqlLiteral.createBoolean(true, getPos()); } | { isStrictEnds = SqlLiteral.createBoolean(false, getPos()); } ) ( interval = IntervalLiteral() | { interval = null; } ) [ subsetList = SubsetDefinitionCommaList(span()) ] patternDefList = PatternDefinitionCommaList(span()) { 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 eList = new ArrayList(); } { e = MeasureColumn() { eList.add(e); } ( 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) alias = SimpleIdentifier() { return SqlStdOperatorTable.AS.createCall(Span.of(e).end(this), e, alias); } } SqlNode PatternExpression() : { SqlNode left; SqlNode right; } { left = PatternTerm() ( 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) ( { startNum = SqlLiteral.createExactNumeric("0", SqlParserPos.ZERO); endNum = SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO); } | { startNum = SqlLiteral.createExactNumeric("1", SqlParserPos.ZERO); endNum = SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO); } | { startNum = SqlLiteral.createExactNumeric("0", SqlParserPos.ZERO); endNum = SqlLiteral.createExactNumeric("1", SqlParserPos.ZERO); } | ( startNum = UnsignedNumericLiteral() { endNum = startNum; } [ { endNum = SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO); } [ endNum = UnsignedNumericLiteral() ] ] | { startNum = SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO); } endNum = UnsignedNumericLiteral() | extra = PatternExpression() { 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; } ) ) [ { 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 eList; } { ( e = SimpleIdentifier() | e = PatternExpression() | { s = span(); } e = PatternExpression() { e = SqlStdOperatorTable.PATTERN_EXCLUDE.createCall(s.end(this), e); } | ( { s = span(); } e = PatternExpression() { eList = new ArrayList(); eList.add(e); } ( e = PatternExpression() { eList.add(e); } )* { e = SqlStdOperatorTable.PATTERN_PERMUTE.createCall( s.end(this), eList); } ) ) { return e; } } SqlNodeList SubsetDefinitionCommaList(Span s) : { SqlNode e; final List eList = new ArrayList(); } { e = SubsetDefinition() { eList.add(e); } ( e = SubsetDefinition() { eList.add(e); } )* { return new SqlNodeList(eList, s.addAll(eList).pos()); } } SqlNode SubsetDefinition() : { final SqlNode var; final SqlNodeList varList; } { var = SimpleIdentifier() varList = ExpressionCommaList(span(), ExprContext.ACCEPT_NON_QUERY) { return SqlStdOperatorTable.EQUALS.createCall(span().end(var), var, varList); } } SqlNodeList PatternDefinitionCommaList(Span s) : { SqlNode e; final List eList = new ArrayList(); } { e = PatternDefinition() { eList.add(e); } ( e = PatternDefinition() { eList.add(e); } )* { return new SqlNodeList(eList, s.addAll(eList).pos()); } } SqlNode PatternDefinition() : { final SqlNode var; final SqlNode e; } { var = SimpleIdentifier() 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) () { 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 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; } { { list = new SqlNodeList(getPos()); } withItem = WithItem() {list.add(withItem);} ( withItem = WithItem() {list.add(withItem);} )* { return list; } } SqlWithItem WithItem() : { SqlIdentifier id; SqlNodeList columnList = null; SqlNode definition; } { id = SimpleIdentifier() [ LOOKAHEAD(2) columnList = ParenthesizedSimpleIdentifierList() ] 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 list; SqlNode e; } { list = Expression2(exprContext) { e = SqlParserUtil.toTree(list); return e; } } // TODO jvs 15-Nov-2003: ANY/ALL void Expression2b(ExprContext exprContext, List 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) 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. * *

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. * *

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 Expression2(ExprContext exprContext) : { final List list = new ArrayList(); List list2; final List 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); } ( { op = SqlStdOperatorTable.NOT_IN; } | { op = SqlStdOperatorTable.IN; } | { final SqlKind k; } k = comp() ( { op = SqlStdOperatorTable.some(k); } | { op = SqlStdOperatorTable.some(k); } | { 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); } ( { op = SqlStdOperatorTable.NOT_BETWEEN; s.clear().add(this); } [ { op = SqlStdOperatorTable.SYMMETRIC_NOT_BETWEEN; } | ] | { op = SqlStdOperatorTable.BETWEEN; s.clear().add(this); } [ { op = SqlStdOperatorTable.SYMMETRIC_BETWEEN; } | ] ) 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); } ( ( ( { op = SqlStdOperatorTable.NOT_LIKE; } | { op = SqlStdOperatorTable.NOT_SIMILAR_TO; } ) | { op = SqlStdOperatorTable.LIKE; } | { op = SqlStdOperatorTable.SIMILAR_TO; } ) ) list2 = Expression2(ExprContext.ACCEPT_SUB_QUERY) { list.add(new SqlParserUtil.ToTreeListItem(op, s.pos())); list.addAll(list2); } [ LOOKAHEAD(2) e = Expression3(ExprContext.ACCEPT_SUB_QUERY) { s.clear().add(this); list.add( new SqlParserUtil.ToTreeListItem( SqlStdOperatorTable.ESCAPE, s.pos())); list.add(e); } ] | LOOKAHEAD(3) op = BinaryRowOperator() { checkNonQueryExpression(exprContext); list.add(new SqlParserUtil.ToTreeListItem(op, getPos())); } Expression2b(ExprContext.ACCEPT_SUB_QUERY, list) | e = Expression(ExprContext.ACCEPT_SUB_QUERY) { list.add( new SqlParserUtil.ToTreeListItem( SqlStdOperatorTable.ITEM, getPos())); list.add(e); } ( LOOKAHEAD(2) 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() : { } { { return SqlKind.LESS_THAN; } | { return SqlKind.LESS_THAN_OR_EQUAL; } | { return SqlKind.GREATER_THAN; } | { return SqlKind.GREATER_THAN_OR_EQUAL; } | { return SqlKind.EQUALS; } | { return SqlKind.NOT_EQUALS; } | { 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) { s = span(); } list = ParenthesizedSimpleIdentifierList() { if (exprContext != ExprContext.ACCEPT_ALL && exprContext != ExprContext.ACCEPT_CURSOR && !this.conformance.allowExplicitRowValueConstructor()) { throw SqlUtil.newContextException(s.end(list), RESOURCE.illegalRowExpression()); } return SqlStdOperatorTable.ROW.createCall(list); } | [ { rowSpan = span(); } ] list1 = ParenthesizedQueryOrCommaList(exprContext) { if (rowSpan != null) { // interpret as row constructor return SqlStdOperatorTable.ROW.createCall(rowSpan.end(list1), list1.toArray()); } } [ LOOKAHEAD(2) /* TODO: ( op = periodOperator() list2 = ParenthesizedQueryOrCommaList(exprContext) { if (list1.size() != 2 || list2.size() != 2) { throw SqlUtil.newContextException( list1.getParserPosition().plus( list2.getParserPosition()), RESOURCE.illegalOverlaps()); } for (SqlNode node : list2) { list1.add(node); } return op.createCall( list1.getParserPosition().plus(list2.getParserPosition()), list1.toArray()); } ) | */ ( e = IntervalQualifier() { if ((list1.size() == 1) && list1.get(0) instanceof SqlCall) { final SqlCall call = (SqlCall) list1.get(0); if (call.getKind() == SqlKind.MINUS && call.operandCount() == 2) { List list3 = startList(call.operand(0)); list3.add(call.operand(1)); list3.add(e); return SqlStdOperatorTable.MINUS_DATE.createCall( Span.of(list1).end(this), list3); } } throw SqlUtil.newContextException(span().end(list1), RESOURCE.illegalMinusDate()); } ) ] { if (list1.size() == 1) { // interpret as single value or query return list1.get(0); } else { // interpret as row constructor return SqlStdOperatorTable.ROW.createCall(span().end(list1), list1.toArray()); } } } SqlOperator periodOperator() : { } { { return SqlStdOperatorTable.OVERLAPS; } | LOOKAHEAD(2) { return SqlStdOperatorTable.IMMEDIATELY_PRECEDES; } | { return SqlStdOperatorTable.PRECEDES; } | { return SqlStdOperatorTable.IMMEDIATELY_SUCCEEDS; } | { return SqlStdOperatorTable.SUCCEEDS; } | { return SqlStdOperatorTable.PERIOD_EQUALS; } } /** * Parses a COLLATE clause */ SqlCollation CollateClause() : { } { { 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 args; SqlCall call; SqlNode e; SqlLiteral quantifier = null; } { p = SimpleIdentifier() { e = p; } ( LOOKAHEAD( ) { s = span(); } { funcType = SqlFunctionCategory.USER_DEFINED_FUNCTION; } ( LOOKAHEAD(2) { args = startList(SqlIdentifier.star(getPos())); } | LOOKAHEAD(2) { 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 an atomic row expression. */ SqlNode AtomicRowExpression() : { final SqlNode e; } { ( e = Literal() | e = DynamicParam() | LOOKAHEAD(2) e = BuiltinFunctionCall() | e = JdbcFunctionCall() | e = MultisetConstructor() | e = ArrayConstructor() | LOOKAHEAD(3) e = MapConstructor() | e = PeriodConstructor() | // NOTE jvs 18-Jan-2005: use syntactic lookahead to discriminate // compound identifiers from function calls in which the function // name is a compound identifier LOOKAHEAD( [] FunctionName() ) 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 whenList = new ArrayList(); List thenList = new ArrayList(); } { { s = span(); } [ caseIdentifier = Expression(ExprContext.ACCEPT_SUB_QUERY) ] ( { whenSpan.add(this); } e = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) { if (((SqlNodeList) e).size() == 1) { e = ((SqlNodeList) e).get(0); } whenList.add(e); } { thenSpan.add(this); } e = Expression(ExprContext.ACCEPT_SUB_QUERY) { thenList.add(e); } )+ [ elseClause = Expression(ExprContext.ACCEPT_SUB_QUERY) ] { 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; } { ( { f = SqlStdOperatorTable.NEXT_VALUE; s = span(); } | { f = SqlStdOperatorTable.CURRENT_VALUE; s = span(); } ) sequenceRef = CompoundIdentifier() { return f.createCall(s.end(sequenceRef), sequenceRef); } } /** * Parses "SET <NAME> = VALUE" or "RESET <NAME>", without a leading * "ALTER <SCOPE>". */ SqlSetOption SqlSetOption(Span s, String scope) : { SqlIdentifier name; final SqlNode val; } { ( { s.add(this); } name = CompoundIdentifier() ( val = Literal() | val = SimpleIdentifier() | { // 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); } | { s.add(this); } ( name = CompoundIdentifier() | { 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; } { { s = span(); } scope = Scope() ( alterNode = SqlSetOption(s, scope) ) { return alterNode; } } String Scope() : { } { ( | ) { return token.image.toUpperCase(Locale.ROOT); } } /** * Parses a CREATE statement. */ SqlCreate SqlCreate() : { final Span s; boolean replace = false; final SqlCreate create; } { { s = span(); } [ { replace = true; } ] ( create = SqlCreateForeignSchema(s, replace) | create = SqlCreateMaterializedView(s, replace) | create = SqlCreateSchema(s, replace) | create = SqlCreateTable(s, replace) | create = SqlCreateType(s, replace) | create = SqlCreateView(s, replace) | create = SqlCreateFunction(s, replace) ) { return create; } } /** * Parses a DROP statement. */ SqlDrop SqlDrop() : { final Span s; boolean replace = false; final SqlDrop drop; } { { s = span(); } ( drop = SqlDropMaterializedView(s, replace) | drop = SqlDropSchema(s, replace) | drop = SqlDropTable(s, replace) | drop = SqlDropType(s, replace) | drop = SqlDropView(s, replace) | drop = SqlDropFunction(s, replace) ) { return drop; } } /** * Parses a literal expression, allowing continued string literals. * Usually returns an SqlLiteral, but a continued string literal * is an SqlCall expression, which concatenates 2 or more string * literals; the validator reduces this. */ SqlNode Literal() : { SqlNode e; } { ( e = NumericLiteral() | e = StringLiteral() | e = SpecialLiteral() | e = DateTimeLiteral() | e = IntervalLiteral() ) { return e; } } /** Parses a unsigned numeric literal */ SqlNumericLiteral UnsignedNumericLiteral() : { } { { return SqlLiteral.createExactNumeric(token.image, getPos()); } | { return SqlLiteral.createExactNumeric(token.image, getPos()); } | { return SqlLiteral.createApproxNumeric(token.image, getPos()); } } /** Parses a numeric literal (can be signed) */ SqlLiteral NumericLiteral() : { final SqlNumericLiteral num; final Span s; } { num = UnsignedNumericLiteral() { return num; } | { s = span(); } num = UnsignedNumericLiteral() { return SqlLiteral.createNegative(num, s.end(this)); } | num = UnsignedNumericLiteral() { return num; } } /** Parse a special literal keyword */ SqlLiteral SpecialLiteral() : { } { { return SqlLiteral.createBoolean(true, getPos()); } | { return SqlLiteral.createBoolean(false, getPos()); } | { return SqlLiteral.createUnknown(getPos()); } | { 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 frags = null; char unicodeEscapeChar = 0; } { // A continued string literal consists of a head fragment and one or more // tail fragments. Since comments may occur between the fragments, and // comments are special tokens, each fragment is a token. But since spaces // or comments may not occur between the prefix and the first quote, the // head fragment, with any prefix, is one token. { 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)); } } ( { try { p = SqlParserUtil.trim(token.image, "'"); // no embedded quotes frags.add(SqlLiteral.createBinaryString(p, getPos())); nfrags++; } catch (NumberFormatException ex) { throw SqlUtil.newContextException(getPos(), RESOURCE.illegalBinaryString(token.image)); } } )* { assert (nfrags > 0); if (nfrags == 1) { return frags.get(0); // just the head fragment } else { SqlParserPos pos2 = SqlParserPos.sum(frags); return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, frags); } } | { String charSet = null; } ( { charSet = SqlParserUtil.getCharacterSet(token.image); } | | { // TODO jvs 2-Feb-2009: support the explicit specification of // a character set for Unicode string literals, per SQL:2003 unicodeEscapeChar = BACKSLASH; charSet = "UTF16"; } ) { p = SqlParserUtil.parseString(token.image); SqlCharStringLiteral literal; try { literal = SqlLiteral.createCharString(p, charSet, getPos()); } catch (java.nio.charset.UnsupportedCharsetException e) { throw SqlUtil.newContextException(getPos(), RESOURCE.unknownCharacterSet(charSet)); } frags = startList(literal); nfrags++; } ( { p = SqlParserUtil.parseString(token.image); try { literal = SqlLiteral.createCharString(p, charSet, getPos()); } catch (java.nio.charset.UnsupportedCharsetException e) { throw SqlUtil.newContextException(getPos(), RESOURCE.unknownCharacterSet(charSet)); } frags.add(literal); nfrags++; } )* [ { if (unicodeEscapeChar == 0) { throw SqlUtil.newContextException(getPos(), RESOURCE.unicodeEscapeUnexpected()); } String s = SqlParserUtil.parseString(token.image); unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s); } ] { assert nfrags > 0; if (nfrags == 1) { // just the head fragment SqlLiteral lit = (SqlLiteral) frags.get(0); return lit.unescapeUnicode(unicodeEscapeChar); } else { SqlNode[] rands = (SqlNode[]) frags.toArray(new SqlNode[nfrags]); for (int i = 0; i < rands.length; ++i) { rands[i] = ((SqlLiteral) rands[i]).unescapeUnicode( unicodeEscapeChar); } SqlParserPos pos2 = SqlParserPos.sum(rands); return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, rands); } } } /** * Parses a date/time literal. */ SqlLiteral DateTimeLiteral() : { final String p; final Span s; } { { p = token.image; } { return SqlParserUtil.parseDateLiteral(p, getPos()); } | { p = token.image; } { return SqlParserUtil.parseTimeLiteral(p, getPos()); } | { s = span(); } { p = token.image; } { return SqlParserUtil.parseTimestampLiteral(p, s.end(this)); } | { s = span(); } { return SqlParserUtil.parseDateLiteral(token.image, s.end(this)); } |