http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/codegen/templates/Parser.jj ---------------------------------------------------------------------- diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj deleted file mode 100644 index c30794d..0000000 --- a/core/src/main/codegen/templates/Parser.jj +++ /dev/null @@ -1,6623 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -<@pp.dropOutputFile /> - -<@pp.changeOutputFile name="javacc/Parser.jj" /> - -options { - STATIC = false; - IGNORE_CASE = true; - UNICODE_INPUT = true; -} - - -PARSER_BEGIN(${parser.class}) - -package ${parser.package}; - -<#list parser.imports as importStr> -import ${importStr}; -</#list> - - -import org.apache.calcite.avatica.util.Casing; -import org.apache.calcite.avatica.util.DateTimeUtils; -import org.apache.calcite.avatica.util.TimeUnit; -import org.apache.calcite.rel.type.RelDataType; -import org.apache.calcite.runtime.CalciteContextException; -import org.apache.calcite.sql.JoinConditionType; -import org.apache.calcite.sql.JoinType; -import org.apache.calcite.sql.SqlAlter; -import org.apache.calcite.sql.SqlBinaryOperator; -import org.apache.calcite.sql.SqlCall; -import org.apache.calcite.sql.SqlCharStringLiteral; -import org.apache.calcite.sql.SqlCollation; -import org.apache.calcite.sql.SqlDataTypeSpec; -import org.apache.calcite.sql.SqlDateLiteral; -import org.apache.calcite.sql.SqlDelete; -import org.apache.calcite.sql.SqlDescribeSchema; -import org.apache.calcite.sql.SqlDescribeTable; -import org.apache.calcite.sql.SqlDynamicParam; -import org.apache.calcite.sql.SqlExplain; -import org.apache.calcite.sql.SqlExplainFormat; -import org.apache.calcite.sql.SqlExplainLevel; -import org.apache.calcite.sql.SqlFunction; -import org.apache.calcite.sql.SqlFunctionCategory; -import org.apache.calcite.sql.SqlIdentifier; -import org.apache.calcite.sql.SqlInsert; -import org.apache.calcite.sql.SqlInsertKeyword; -import org.apache.calcite.sql.SqlIntervalLiteral; -import org.apache.calcite.sql.SqlIntervalQualifier; -import org.apache.calcite.sql.SqlJdbcDataTypeName; -import org.apache.calcite.sql.SqlJdbcFunctionCall; -import org.apache.calcite.sql.SqlJoin; -import org.apache.calcite.sql.SqlKind; -import org.apache.calcite.sql.SqlLiteral; -import org.apache.calcite.sql.SqlMatchRecognize; -import org.apache.calcite.sql.SqlMerge; -import org.apache.calcite.sql.SqlNode; -import org.apache.calcite.sql.SqlNodeList; -import org.apache.calcite.sql.SqlNumericLiteral; -import org.apache.calcite.sql.SqlOperator; -import org.apache.calcite.sql.SqlOrderBy; -import org.apache.calcite.sql.SqlPostfixOperator; -import org.apache.calcite.sql.SqlPrefixOperator; -import org.apache.calcite.sql.SqlSampleSpec; -import org.apache.calcite.sql.SqlSelect; -import org.apache.calcite.sql.SqlSelectKeyword; -import org.apache.calcite.sql.SqlSetOption; -import org.apache.calcite.sql.SqlTimeLiteral; -import org.apache.calcite.sql.SqlTimestampLiteral; -import org.apache.calcite.sql.SqlUnnestOperator; -import org.apache.calcite.sql.SqlUpdate; -import org.apache.calcite.sql.SqlUtil; -import org.apache.calcite.sql.SqlWindow; -import org.apache.calcite.sql.SqlWith; -import org.apache.calcite.sql.SqlWithItem; -import org.apache.calcite.sql.fun.SqlCase; -import org.apache.calcite.sql.fun.OracleSqlOperatorTable; -import org.apache.calcite.sql.fun.SqlStdOperatorTable; -import org.apache.calcite.sql.fun.SqlTrimFunction; -import org.apache.calcite.sql.parser.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.Util; -import org.apache.calcite.util.trace.CalciteTrace; - -import com.google.common.collect.Lists; - -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 stream) { - return new ${parser.class}(stream); - } - }; - - public SqlParseException normalizeException(Throwable ex) - { - try { - if (ex instanceof ParseException) { - ex = cleanupParseException((ParseException) ex); - } - return convertException(ex); - } catch (ParseException e) { - throw new AssertionError(e); - } - } - - public Metadata getMetadata() - { - synchronized (${parser.class}.class) { - if (metadata == null) { - metadata = new MetadataImpl( - new ${parser.class}(new java.io.StringReader(""))); - } - return metadata; - } - } - - public void setTabSize(int tabSize) - { - jj_input_stream.setTabSize(tabSize); - } - - public void switchTo(String stateName) - { - int state = Arrays.asList(${parser.class}TokenManager.lexStateNames) - .indexOf(stateName); - token_source.SwitchTo(state); - } - - public void setQuotedCasing(Casing quotedCasing) - { - this.quotedCasing = quotedCasing; - } - - public void setUnquotedCasing(Casing unquotedCasing) - { - this.unquotedCasing = unquotedCasing; - } - - public void setIdentifierMaxLength(int identifierMaxLength) - { - this.identifierMaxLength = identifierMaxLength; - } - - public void setConformance(SqlConformance conformance) - { - this.conformance = conformance; - } - - public SqlNode parseSqlExpressionEof() throws Exception - { - return SqlExpressionEof(); - } - - public SqlNode parseSqlStmtEof() throws Exception - { - return SqlStmtEof(); - } - - private SqlNode extend(SqlNode table, SqlNodeList extendList) { - return SqlStdOperatorTable.EXTEND.createCall( - table.getParserPosition().plus(extendList.getParserPosition()), - table, extendList); - } -} - -PARSER_END(${parser.class}) - - -/*************************************** - * Utility Codes for Semantic Analysis * - ***************************************/ - -/* For Debug */ -JAVACODE -void debug_message1() { - LOGGER.info("{} , {}", getToken(0).image, getToken(1).image); -} - -JAVACODE String unquotedIdentifier() { - return SqlParserUtil.strip(getToken(0).image, null, null, null, - unquotedCasing); -} - -String NonReservedKeyWord() : -{ - String kw; -} -{ - kw = CommonNonReservedKeyWord() - { - return kw; - } -} - -/** - * Allows parser to be extended with new types of table references. The - * default implementation of this production is empty. - */ -SqlNode ExtendedTableRef() : -{ -} -{ - UnusedExtension() - { - return null; - } -} - -/** - * Allows an OVER clause following a table expression as an extension to - * standard SQL syntax. The default implementation of this production is empty. - */ -SqlNode TableOverOpt() : -{ -} -{ - { - return null; - } -} - -/* - * Parses dialect-specific keywords immediately following the SELECT keyword. - */ -void SqlSelectKeywords(List<SqlLiteral> keywords) : -{} -{ - E() -} - -/* - * Parses dialect-specific keywords immediately following the INSERT keyword. - */ -void SqlInsertKeywords(List<SqlLiteral> keywords) : -{} -{ - E() -} - -SqlNode ExtendedBuiltinFunctionCall() : -{ -} -{ - UnusedExtension() - { - return null; - } -} - -/* -* Parse Floor/Ceil function parameters -*/ -SqlNode FloorCeilOptions(SqlParserPos pos, boolean floorFlag) : -{ - SqlNode node; -} -{ - node = StandardFloorCeilOptions(pos, 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 SqlParserPos getPos() -{ - return new SqlParserPos( - token.beginLine, - token.beginColumn, - token.endLine, - token.endColumn); -} - -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()); - } -} - -// The date/time parse utilities have to live here, instead of in the -// SqlParserUtil class because ParseException is ambiguous, and -// CommonParser has to live in multiple packages. - -JAVACODE SqlDateLiteral parseDateLiteral(String s, SqlParserPos pos) { - String dateStr = SqlParserUtil.parseString(s); - Calendar cal = DateTimeUtils.parseDateFormat( - dateStr, DateTimeUtils.DATE_FORMAT_STRING, DateTimeUtils.GMT_ZONE); - if (null == cal) { - throw SqlUtil.newContextException(pos, - RESOURCE.illegalLiteral("DATE", s, - RESOURCE.badFormat(DateTimeUtils.DATE_FORMAT_STRING).str())); - } - return SqlLiteral.createDate(cal, pos); -} - -JAVACODE SqlTimeLiteral parseTimeLiteral(String s, SqlParserPos pos) { - String dateStr = SqlParserUtil.parseString(s); - DateTimeUtils.PrecisionTime pt = - DateTimeUtils.parsePrecisionDateTimeLiteral( - dateStr, DateTimeUtils.TIME_FORMAT_STRING, DateTimeUtils.GMT_ZONE); - if (null == pt) { - throw SqlUtil.newContextException(pos, - RESOURCE.illegalLiteral("TIME", s, - RESOURCE.badFormat(DateTimeUtils.TIME_FORMAT_STRING).str())); - } - return SqlLiteral.createTime(pt.getCalendar(), pt.getPrecision(), pos); -} - -JAVACODE SqlTimestampLiteral parseTimestampLiteral(String s, SqlParserPos pos) { - String dateStr = SqlParserUtil.parseString(s); - DateTimeUtils.PrecisionTime pt = - DateTimeUtils.parsePrecisionDateTimeLiteral( - dateStr, DateTimeUtils.TIMESTAMP_FORMAT_STRING, DateTimeUtils.GMT_ZONE); - if (null == pt) { - throw SqlUtil.newContextException(pos, - RESOURCE.illegalLiteral("TIMESTAMP", s, - RESOURCE.badFormat(DateTimeUtils.TIMESTAMP_FORMAT_STRING).str())); - } - return SqlLiteral.createTimestamp(pt.getCalendar(), pt.getPrecision(), pos); -} - -JAVACODE SqlIntervalLiteral parseIntervalLiteral( - SqlParserPos pos, - int sign, - String s, - SqlIntervalQualifier intervalQualifier) throws ParseException -{ - String intervalStr = SqlParserUtil.parseString(s); - if ("".equals(intervalStr)) { - throw new ParseException( - RESOURCE.illegalIntervalLiteral(s + " " - + intervalQualifier.toString(), pos.toString()).str()); - } - return SqlLiteral.createInterval(sign, intervalStr, intervalQualifier, pos); -} - -/** - * 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 = java.util.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: - * - * [ LIMIT { count | ALL } ] - * [ OFFSET start ] - * - * <p>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; - SqlParserPos pos = 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> ( count = UnsignedNumericLiteral() | <ALL> ) - ] - [ - // ROW or ROWS is required in SQL:2008 but we make it optional - // because it is not present in Postgres-style syntax. - <OFFSET> start = UnsignedNumericLiteral() [ <ROW> | <ROWS> ] - ] - [ - // SQL:2008-style syntax. "OFFSET ... FETCH ...". - // If you specify both LIMIT and FETCH, FETCH wins. - <FETCH> ( <FIRST> | <NEXT> ) count = UnsignedNumericLiteral() ( <ROW> | <ROWS> ) <ONLY> - ] - { - if (orderBy != null || start != null || count != null) { - pos = getPos(); - if (orderBy == null) { - orderBy = SqlNodeList.EMPTY; - } - e = new SqlOrderBy(pos, 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 - * - * <code>WHERE x IN (select count(*) from t where c=d,5)</code>, - * - * which should be illegal. The above is interpreted as equivalent to - * - * <code>WHERE x IN ((select count(*) from t where c=d),5)</code>, - * - * 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; - SqlParserPos pos; -} -{ - <LPAREN> - { - // we've now seen left paren, so a query by itself should - // be interpreted as a sub-query - pos = getPos(); - 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, pos.plus(getPos())); - } -} - -/** - * Parses function parameter lists including DISTINCT keyword recognition, - * DEFAULT, and named argument assignment. - */ -List FunctionParameterList( - ExprContext exprContext) : -{ - SqlNode e = null; - List list = new ArrayList(); -} -{ - <LPAREN> - [ - <DISTINCT> { - e = SqlSelectKeyword.DISTINCT.symbol(getPos()); - } - | - <ALL> { - e = SqlSelectKeyword.ALL.symbol(getPos()); - } - ] - { - list.add(e); - } - Arg0(list, exprContext) - ( - <COMMA> { - // a comma-list can't appear where only a query is expected - checkNonQueryExpression(exprContext); - } - Arg(list, exprContext) - )* - <RPAREN> - { - return list; - } -} - -void Arg0(List list, ExprContext exprContext) : -{ - SqlIdentifier name = null; - SqlNode e = null; - final ExprContext firstExprContext; - { - // we've now seen left paren, so queries inside should - // be allowed as sub-queries - switch (exprContext) { - case ACCEPT_SUB_QUERY: - firstExprContext = ExprContext.ACCEPT_NONCURSOR; - break; - case ACCEPT_CURSOR: - firstExprContext = ExprContext.ACCEPT_ALL; - break; - default: - firstExprContext = exprContext; - break; - } - } -} -{ - [ - name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT> - ] - ( - <DEFAULT_KW> { - e = SqlStdOperatorTable.DEFAULT.createCall(getPos()); - } - | - e = OrderedQueryOrExpr(firstExprContext) - ) - { - if (e != null) { - if (name != null) { - e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall( - name.getParserPosition().plus(e.getParserPosition()), - e, name); - } - list.add(e); - } - } -} - -void Arg(List list, ExprContext exprContext) : -{ - SqlIdentifier name = null; - SqlNode e = null; -} -{ - [ - name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT> - ] - ( - <DEFAULT_KW> { - e = SqlStdOperatorTable.DEFAULT.createCall(getPos()); - } - | - e = Expression(exprContext) - ) - { - if (e != null) { - if (name != null) { - e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall( - name.getParserPosition().plus(e.getParserPosition()), - e, name); - } - list.add(e); - } - } -} - -/** - * 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 an SQL statement. - */ -SqlNode SqlStmt() : -{ - SqlNode stmt; -} -{ - ( - stmt = SqlSetOption(null, null) - | - stmt = SqlAlter() - | -<#if parser.createStatementParserMethods?size != 0> - stmt = SqlCreate() - | -</#if> - stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) - | - stmt = SqlExplain() - | - stmt = SqlDescribe() - | - stmt = SqlInsert() - | - stmt = SqlDelete() - | - stmt = SqlUpdate() - | - stmt = SqlMerge() - | - stmt = SqlProcedureCall() - - <#-- Add methods to parse additional statements here --> - <#list parser.statementParserMethods as method> - | - stmt = ${method} - </#list> - ) - { - return stmt; - } -} - -/** - * Parses an SQL statement followed by the end-of-file symbol. - */ -SqlNode SqlStmtEof() : -{ - SqlNode stmt; -} -{ - stmt = SqlStmt() <EOF> - { - return stmt; - } -} - -<#-- Add implementations of additional parser statement calls here --> -<#list parser.implementationFiles as file> - <#include "/@includes/"+file /> -</#list> - -/** - * Parses a leaf SELECT expression without ORDER BY. - */ -SqlSelect SqlSelect() : -{ - final List<SqlLiteral> keywords = Lists.newArrayList(); - List<SqlNode> selectList; - final SqlNode fromClause; - final SqlNode where; - final SqlNodeList groupBy; - final SqlNode having; - final SqlNodeList windowDecls; - SqlParserPos pos; -} -{ - <SELECT> - { - pos = getPos(); - } - SqlSelectKeywords(keywords) - ( - <STREAM> { - keywords.add(SqlSelectKeyword.STREAM.symbol(getPos())); - } - )? - ( - <DISTINCT> { - keywords.add(SqlSelectKeyword.DISTINCT.symbol(getPos())); - } - | <ALL> { - keywords.add(SqlSelectKeyword.ALL.symbol(getPos())); - } - )? - selectList = SelectList() - ( - <FROM> fromClause = FromClause() - where = WhereOpt() - groupBy = GroupByOpt() - having = HavingOpt() - windowDecls = WindowOpt() - | - E() { - fromClause = null; - where = null; - groupBy = null; - having = null; - windowDecls = null; - } - ) - { - final SqlNode selectItem = (SqlNode)selectList.get(0); - final SqlParserPos selectListPos = selectItem.getParserPosition(); - return new SqlSelect(pos.plus(getPos()), - new SqlNodeList(keywords, pos), - new SqlNodeList(selectList, selectListPos.plusAll(selectList)), - fromClause, where, groupBy, having, windowDecls, null, null, null); - } -} - -/* - * Abstract production: - * - * void SqlSelectKeywords(List keywords) - * - * Parses dialect-specific keywords immediately following the SELECT keyword. - */ - -/** - * Parses an EXPLAIN PLAN statement. - */ -SqlNode SqlExplain() : -{ - SqlNode stmt; - SqlExplainLevel detailLevel = SqlExplainLevel.EXPPLAN_ATTRIBUTES; - SqlExplain.Depth depth; - SqlParserPos pos; - final SqlExplainFormat format; -} -{ - <EXPLAIN> <PLAN> - [ detailLevel = ExplainDetailLevel() ] - depth = ExplainDepth() - ( - <AS> <XML> { format = SqlExplainFormat.XML; } - | - <AS> <JSON> { format = SqlExplainFormat.JSON; } - | - { format = SqlExplainFormat.TEXT; } - ) - <FOR> stmt = SqlQueryOrDml() { - pos = getPos(); - return new SqlExplain(pos, - 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 SqlParserPos pos; - final SqlIdentifier table; - final SqlIdentifier column; - final SqlNode stmt; -} -{ - <DESCRIBE> { pos = getPos(); } - ( - (<DATABASE> | <CATALOG> | <SCHEMA>) { - // 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(pos, CompoundIdentifier()); - } - | - // Use syntactic lookahead to determine whether a table name is coming. - // We do not allow SimpleIdentifier() because that includes <STATEMENT>. - LOOKAHEAD( <TABLE> | <IDENTIFIER> | <QUOTED_IDENTIFIER> - | <BACK_QUOTED_IDENTIFIER> | <BRACKET_QUOTED_IDENTIFIER> ) - (<TABLE>)? - table = CompoundIdentifier() - ( - column = SimpleIdentifier() - | - E() { column = null; } - ) { - return new SqlDescribeTable(pos, table, column); - } - | - (<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(pos, - stmt, - detailLevel.symbol(SqlParserPos.ZERO), - depth.symbol(SqlParserPos.ZERO), - format.symbol(SqlParserPos.ZERO), - nDynamicParams); - } - ) -} - -/** - * Parses a CALL statement. - */ -SqlNode SqlProcedureCall() : -{ - SqlParserPos callPos; - SqlNode routineCall; -} -{ - <CALL> - { - callPos = getPos(); - } - routineCall = NamedRoutineCall( - SqlFunctionCategory.USER_DEFINED_PROCEDURE, - ExprContext.ACCEPT_SUB_QUERY) - { - return SqlStdOperatorTable.PROCEDURE_CALL.createCall( - callPos, routineCall); - } -} - -SqlNode NamedRoutineCall( - SqlFunctionCategory routineType, - ExprContext exprContext) : -{ - SqlIdentifier name; - final List<SqlNode> list = Lists.newArrayList(); - final SqlParserPos pos; -} -{ - name = CompoundIdentifier() { - pos = getPos(); - } - <LPAREN> - [ - Arg0(list, exprContext) - ( - <COMMA> { - // a comma-list can't appear where only a query is expected - checkNonQueryExpression(exprContext); - } - Arg(list, exprContext) - )* - ] - <RPAREN> - { - SqlNode function = createCall( - name, pos.plus(getPos()), routineType, null, SqlParserUtil.toNodeArray(list)); - return function; - } -} - -/** - * Parses an INSERT statement. - */ -SqlNode SqlInsert() : -{ - final List<SqlLiteral> keywords = Lists.newArrayList(); - SqlNode table; - SqlNodeList extendList = null; - SqlNode source; - SqlNodeList columnList = null; - SqlParserPos pos; -} -{ - ( - <INSERT> - | - <UPSERT> { keywords.add(SqlInsertKeyword.UPSERT.symbol(getPos())); } - ) - SqlInsertKeywords(keywords) - <INTO> table = CompoundIdentifier() - [ - LOOKAHEAD(3) - [ <EXTEND> ] - extendList = ExtendList() { - table = extend(table, extendList); - } - ] - { - pos = getPos(); - } - [ - LOOKAHEAD(2) - columnList = ParenthesizedCompoundIdentifierList() - ] - source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) - { - return new SqlInsert(pos, new SqlNodeList(keywords, pos), 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; - SqlNode condition; - SqlParserPos pos; -} -{ - <DELETE> - { - pos = getPos(); - } - <FROM> table = CompoundIdentifier() - { - - } - [ - [ <EXTEND> ] - extendList = ExtendList() { - table = extend(table, extendList); - } - ] - [ [ <AS> ] alias = SimpleIdentifier() ] - condition = WhereOpt() - { - return new SqlDelete(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; - SqlParserPos pos; -} -{ - <UPDATE> table = CompoundIdentifier() - { - pos = getPos(); - targetColumnList = new SqlNodeList(pos); - sourceExpressionList = new SqlNodeList(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(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; - SqlParserPos mergePos; -} -{ - <MERGE> <INTO> table = CompoundIdentifier() - { - mergePos = getPos(); - } - [ - [ <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(mergePos, table, condition, sourceTableRef, - updateCall, insertCall, null, alias); - } -} - -SqlUpdate WhenMatchedClause(SqlNode table, SqlIdentifier alias) : -{ - SqlIdentifier id; - SqlParserPos pos; - SqlNodeList updateColumnList; - SqlNode exp; - SqlNodeList updateExprList; -} -{ - <WHEN> <MATCHED> <THEN> - <UPDATE> <SET> id = SimpleIdentifier() - { - pos = getPos(); - updateColumnList = new SqlNodeList(pos); - updateExprList = new SqlNodeList(pos); - updateColumnList.add(id); - } - <EQ> exp = Expression(ExprContext.ACCEPT_SUB_QUERY) - { - updateExprList.add(exp); - } - ( - <COMMA> - id = SimpleIdentifier() - { - updateColumnList.add(id); - } - <EQ> exp = Expression(ExprContext.ACCEPT_SUB_QUERY) - { - updateExprList.add(exp); - } - ) * - { - return new SqlUpdate(pos, table, updateColumnList, updateExprList, null, - null, alias); - } -} - -SqlInsert WhenNotMatchedClause(SqlNode table) : -{ - SqlParserPos pos, insertPos; - List<SqlLiteral> keywords = Lists.newArrayList(); - SqlNodeList insertColumnList = null; - SqlNode rowConstructor; - SqlNode insertValues; -} -{ - <WHEN> <NOT> <MATCHED> <THEN> - <INSERT> - { - insertPos = getPos(); - } - SqlInsertKeywords(keywords) - [ - LOOKAHEAD(2) - insertColumnList = ParenthesizedSimpleIdentifierList() - ] - [ <LPAREN> ] - <VALUES> { pos = getPos(); } - 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( - pos.plus(rowConstructor.getParserPosition()), - rowConstructor); - return new SqlInsert(insertPos, new SqlNodeList(keywords, insertPos), - table, insertValues, insertColumnList); - } -} - -/** - * Parses the select list of a SELECT statement. - */ -List<SqlNode> SelectList() : -{ - 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; - SqlIdentifier id; - SqlParserPos pos; -} -{ - e = SelectExpression() - [ - [ <AS> ] - id = SimpleIdentifier() - { - pos = e.getParserPosition().plus(getPos()); - e = SqlStdOperatorTable.AS.createCall(pos, e, id); - } - ] - { - return e; - } -} - -/** - * Parses one unaliased expression in a select list. - */ -SqlNode SelectExpression() : -{ - SqlNode e; -} -{ - <STAR> - { - return SqlIdentifier.star(getPos()); - } - | - e = Expression(ExprContext.ACCEPT_SUB_QUERY) - { - return e; - } -} - -SqlLiteral Natural() : -{ -} -{ - ( - <NATURAL> { return SqlLiteral.createBoolean(true, getPos()); } - | - { return SqlLiteral.createBoolean(false, getPos()); } - ) -} - -SqlLiteral JoinType() : -{ - JoinType joinType; -} -{ - ( - <JOIN> { joinType = JoinType.INNER; } - | - <INNER> <JOIN> { joinType = JoinType.INNER; } - | - <LEFT> [ <OUTER> ] <JOIN> { joinType = JoinType.LEFT; } - | - <RIGHT> [ <OUTER> ] <JOIN> { joinType = JoinType.RIGHT; } - | - <FULL> [ <OUTER> ] <JOIN> { joinType = JoinType.FULL; } - | - <CROSS> <JOIN> { joinType = JoinType.CROSS; } - ) - { - return joinType.symbol(getPos()); - } -} - -/** Matches "LEFT JOIN t ON ...", "RIGHT JOIN t USING ...", "JOIN t". */ -SqlNode JoinTable(SqlNode e) : -{ - SqlNode e2, condition; - SqlLiteral natural, joinType; - SqlNodeList list; - SqlParserPos pos; -} -{ - natural = Natural() - joinType = JoinType() - e2 = TableRef() - ( - <ON> { pos = getPos(); } - condition = Expression(ExprContext.ACCEPT_SUB_QUERY) { - SqlParserPos onPos = pos.plus(getPos()); - return new SqlJoin(joinType.getParserPosition(), - e, - natural, - joinType, - e2, - JoinConditionType.ON.symbol(onPos), - condition); - } - | - <USING> { pos = getPos(); } - list = ParenthesizedSimpleIdentifierList() { - SqlParserPos usingPos = pos.plus(getPos()); - return new SqlJoin(joinType.getParserPosition(), - e, - natural, - joinType, - e2, - JoinConditionType.USING.symbol(usingPos), - new SqlNodeList(list.getList(), usingPos)); - } - | - { - 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; - SqlNodeList list; - SqlParserPos pos; -} -{ - e = TableRef() - ( - // 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. - LOOKAHEAD(2) - natural = Natural() - joinType = JoinType() - e2 = TableRef() - ( - <ON> { pos = getPos(); } - condition = Expression(ExprContext.ACCEPT_SUB_QUERY) { - SqlParserPos onPos = pos.plus(getPos()); - e = new SqlJoin(joinType.getParserPosition(), - e, - natural, - joinType, - e2, - JoinConditionType.ON.symbol(onPos), - condition); - } - | - <USING> { pos = getPos(); } - list = ParenthesizedSimpleIdentifierList() { - SqlParserPos usingPos = pos.plus(getPos()); - e = new SqlJoin(joinType.getParserPosition(), - e, - natural, - joinType, - e2, - JoinConditionType.USING.symbol(usingPos), - new SqlNodeList(list.getList(), usingPos)); - } - | - { - 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> { pos = getPos(); } - e2 = TableRef() { - e = new SqlJoin(pos, - e, - SqlLiteral.createBoolean(false, pos), - JoinType.COMMA.symbol(SqlParserPos.ZERO), - e2, - JoinConditionType.NONE.symbol(SqlParserPos.ZERO), - null); - } - | - <CROSS> { pos = getPos(); } <APPLY> - e2 = TableRef2(true) { - if (!this.conformance.isApplyAllowed()) { - throw new ParseException(RESOURCE.applyNotAllowed().str()); - } - e = new SqlJoin(pos, - e, - SqlLiteral.createBoolean(false, pos), - JoinType.CROSS.symbol(SqlParserPos.ZERO), - e2, - JoinConditionType.NONE.symbol(SqlParserPos.ZERO), - null); - } - | - <OUTER> { pos = getPos(); } <APPLY> - e2 = TableRef2(true) { - if (!this.conformance.isApplyAllowed()) { - throw new ParseException(RESOURCE.applyNotAllowed().str()); - } - e = new SqlJoin(pos, - e, - SqlLiteral.createBoolean(false, pos), - JoinType.LEFT.symbol(SqlParserPos.ZERO), - e2, - JoinConditionType.ON.symbol(SqlParserPos.ZERO), - SqlLiteral.createBoolean(true, pos)); - } - ) * - { - return e; - } -} - -// TODO jvs 15-Nov-2003: SQL standard allows column aliases on table -// references, e.g. DEPTS AS D1(DEPTNO1,DNAME1); I guess this is syntactic -// sugar to make it easier for query writers to conform to the column name -// uniqueness rules without requiring them to write a nested SELECT, but it -// seems pretty useless for non-trivial tables, since you have to supply names -// for ALL columns at once. -/** - * 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; - SqlNode over; - SqlNodeList extendList = null; - String alias; - SqlParserPos pos; - SqlNodeList args; - SqlNode sample; - boolean isBernoulli; - SqlNumericLiteral samplePercentage; - boolean isRepeatable = false; - int repeatableSeed = 0; - SqlNodeList columnAliasList = null; - SqlUnnestOperator unnestOp = SqlStdOperatorTable.UNNEST; -} -{ - ( - LOOKAHEAD(2) - tableRef = CompoundIdentifier() - [ - [ <EXTEND> ] - extendList = ExtendList() { - tableRef = extend(tableRef, extendList); - } - ] - over = TableOverOpt() - { - if (over != null) { - pos = getPos(); - tableRef = SqlStdOperatorTable.OVER.createCall( - pos, tableRef, over); - } - } - [ - over = MatchRecognizeOpt(tableRef) - { - if (over != null) { - tableRef = over; - } - } - ] - | - [ <LATERAL> { lateral = true; } ] - tableRef = ParenthesizedExpression(ExprContext.ACCEPT_QUERY) - over = TableOverOpt() - { - if (over != null) { - pos = getPos(); - tableRef = SqlStdOperatorTable.OVER.createCall( - pos, tableRef, over); - } - if (lateral) { - tableRef = SqlStdOperatorTable.LATERAL.createCall( - getPos(), tableRef); - } - } - ( - [ over = MatchRecognizeOpt(tableRef) ] - { - if (over != null) { - tableRef = over; - } - } - ) - | - <UNNEST> { pos = getPos(); } - args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY) - [ - <WITH> <ORDINALITY> { - unnestOp = SqlStdOperatorTable.UNNEST_WITH_ORDINALITY; - } - ] - { - tableRef = unnestOp.createCall(pos.plus(getPos()), args.toArray()); - } - | - [ <LATERAL> { lateral = true; } ] - <TABLE> { pos = getPos(); } <LPAREN> - tableRef = TableFunctionCall(pos) - <RPAREN> - { - if (lateral) { - tableRef = SqlStdOperatorTable.LATERAL.createCall( - getPos(), tableRef); - } - } - | - tableRef = ExtendedTableRef() - ) - [ - [ <AS> ] alias = Identifier() - [ columnAliasList = ParenthesizedSimpleIdentifierList() ] - { - pos = getPos(); - if (columnAliasList == null) { - tableRef = SqlStdOperatorTable.AS.createCall( - pos, tableRef, new SqlIdentifier(alias, pos)); - } else { - List<SqlNode> idList = new ArrayList<SqlNode>(); - idList.add(tableRef); - idList.add(new SqlIdentifier(alias, pos)); - idList.addAll(columnAliasList.getList()); - tableRef = SqlStdOperatorTable.AS.createCall(pos, idList); - } - } - ] - [ - <TABLESAMPLE> { pos = getPos(); } - ( - <SUBSTITUTE> <LPAREN> sample = StringLiteral() <RPAREN> - { - String sampleName = - ((NlsString) SqlLiteral.value(sample)).getValue(); - SqlSampleSpec sampleSpec = SqlSampleSpec.createNamed(sampleName); - SqlLiteral sampleLiteral = SqlLiteral.createSample(sampleSpec, pos); - tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall( - pos.plus(getPos()), tableRef, sampleLiteral); - } - | - ( - <BERNOULLI> - { - isBernoulli = true; - } - | - <SYSTEM> - { - isBernoulli = false; - } - ) - <LPAREN> samplePercentage = UnsignedNumericLiteral() <RPAREN> - [ - <REPEATABLE> <LPAREN> repeatableSeed = IntLiteral() <RPAREN> - { - isRepeatable = true; - } - ] - { - final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100L); - BigDecimal rate = samplePercentage.bigDecimalValue(); - if (rate.compareTo(BigDecimal.ZERO) < 0 - || rate.compareTo(ONE_HUNDRED) > 0) - { - throw new ParseException(RESOURCE.invalidSampleSize().str()); - } - - // Treat TABLESAMPLE(0) and TABLESAMPLE(100) as no table - // sampling at all. Not strictly correct: TABLESAMPLE(0) - // should produce no output, but it simplifies implementation - // to know that some amount of sampling will occur. - // In practice values less than ~1E-43% are treated as 0.0 and - // values greater than ~99.999997% are treated as 1.0 - float fRate = rate.divide(ONE_HUNDRED).floatValue(); - if (fRate > 0.0f && fRate < 1.0f) { - SqlSampleSpec tableSampleSpec = - isRepeatable - ? SqlSampleSpec.createTableSample( - isBernoulli, fRate, repeatableSeed) - : SqlSampleSpec.createTableSample(isBernoulli, fRate); - - SqlLiteral tableSampleLiteral = - SqlLiteral.createSample(tableSampleSpec, pos); - tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall( - pos.plus(getPos()), tableRef, tableSampleLiteral); - } - } - ) - ] - { - return tableRef; - } -} - -SqlNodeList ExtendList() : -{ - SqlParserPos pos; - List<SqlNode> list = Lists.newArrayList(); -} -{ - <LPAREN> { pos = getPos(); } - ColumnType(list) - ( - <COMMA> ColumnType(list) - )* - <RPAREN> { - return new SqlNodeList(list, pos.plus(getPos())); - } -} - -void ColumnType(List<SqlNode> list) : -{ - SqlIdentifier name; - SqlDataTypeSpec type; - boolean nullable = true; -} -{ - name = SimpleIdentifier() - type = DataType() - [ - <NOT> <NULL> { - nullable = false; - } - ] - { - list.add(name); - list.add(type.withNullable(nullable)); - } -} - -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() - * - * Allows parser to be extended with new types of table references. The - * default implementation of this production is empty. - */ - -/* - * Abstract production: - * - * SqlNode TableOverOpt() - * - * Allows an OVER clause following a table expression as an extension to - * standard SQL syntax. The default implementation of this production is empty. - */ - -/** - * Parses an explicit TABLE t reference. - */ -SqlNode ExplicitTable(SqlParserPos pos) : -{ - SqlNode tableRef; -} -{ - <TABLE> tableRef = CompoundIdentifier() - { - return SqlStdOperatorTable.EXPLICIT_TABLE.createCall(pos, tableRef); - } -} - -/** - * Parses a VALUES leaf query expression. - */ -SqlNode TableConstructor() : -{ - SqlNodeList rowConstructorList; - SqlParserPos pos; -} -{ - <VALUES> - { - pos = getPos(); - } - rowConstructorList = RowConstructorList(pos) - { - return SqlStdOperatorTable.VALUES.createCall( - pos.plus(getPos()), rowConstructorList.toArray()); - } -} - -/** - * Parses one or more rows in a VALUES expression. - */ -SqlNodeList RowConstructorList(SqlParserPos pos) : -{ - List<SqlNode> list = new ArrayList<SqlNode>(); - SqlNode rowConstructor; -} -{ - rowConstructor = RowConstructor() { list.add(rowConstructor); } - ( - LOOKAHEAD(2) - <COMMA> rowConstructor = RowConstructor() { list.add(rowConstructor); } - ) * - { - return new SqlNodeList(list, pos.plus(getPos())); - } -} - -/** - * Parses a row constructor in the context of a VALUES expression. - */ -SqlNode RowConstructor() : -{ - SqlNodeList valueList; - SqlNode value; - SqlParserPos pos; -} -{ - // 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> { pos = getPos(); } - <ROW> - valueList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR) - <RPAREN> { pos = pos.plus(getPos()); } - | - LOOKAHEAD(3) - { pos = getPos(); } - [ - <ROW> - ] - valueList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR) - { pos = pos.plus(getPos()); } - | - 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. - pos = value.getParserPosition(); - valueList = new SqlNodeList(Collections.singletonList(value), pos); - } - ) - { - // 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(pos, valueList.toArray()); - } -} - -/** - * Parses the optional WHERE clause for SELECT, DELETE, and UPDATE. - */ -SqlNode WhereOpt() : -{ - SqlNode condition; -} -{ - <WHERE> condition = Expression(ExprContext.ACCEPT_SUB_QUERY) - { - return condition; - } - | - { - return null; - } -} - -/** - * Parses the optional GROUP BY clause for SELECT. - */ -SqlNodeList GroupByOpt() : -{ - List<SqlNode> list = Lists.newArrayList(); - SqlNode e; - SqlParserPos pos; -} -{ - <GROUP> { pos = getPos(); } - <BY> list = GroupingElementList() { - return new SqlNodeList(list, pos.plusAll(list)); - } -| - { - return null; - } -} - -List<SqlNode> GroupingElementList() : -{ - List<SqlNode> list = Lists.newArrayList(); - SqlNode e; -} -{ - e = GroupingElement() { list.add(e); } - ( - <COMMA> - e = GroupingElement() { list.add(e); } - )* - { return list; } -} - -SqlNode GroupingElement() : -{ - List<SqlNode> list; - SqlNodeList nlist; - SqlNode e; - SqlParserPos pos; -} -{ - <GROUPING> { pos = getPos(); } - <SETS> <LPAREN> list = GroupingElementList() <RPAREN> { - return SqlStdOperatorTable.GROUPING_SETS.createCall(pos, list); - } -| <ROLLUP> { pos = getPos(); } - <LPAREN> nlist = ExpressionCommaList(pos, ExprContext.ACCEPT_SUB_QUERY) - <RPAREN> { - return SqlStdOperatorTable.ROLLUP.createCall(nlist); - } -| <CUBE> { pos = getPos(); } - <LPAREN> nlist = ExpressionCommaList(pos, ExprContext.ACCEPT_SUB_QUERY) - <RPAREN> { - return SqlStdOperatorTable.CUBE.createCall(nlist); - } -| 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( - SqlParserPos pos, - ExprContext exprContext) : -{ - List<SqlNode> list; - SqlNode e; -} -{ - e = Expression(exprContext) - { - if (pos == null) { - pos = getPos(); - } - pos = pos.plus(getPos()); - list = startList(e); - } - ( - // NOTE jvs 6-Feb-2004: See comments at top of file for why - // hint is necessary here. - LOOKAHEAD(2) - <COMMA> e = Expression(ExprContext.ACCEPT_SUB_QUERY) - { - list.add(e); - pos = pos.plus(getPos()); - } - ) * - { - return new SqlNodeList(list, pos); - } -} - -/** - * Parses the optional HAVING clause for SELECT. - */ -SqlNode HavingOpt() : -{ - SqlNode e; -} -{ - <HAVING> e = Expression(ExprContext.ACCEPT_SUB_QUERY) - { - return e; - } - | - { - return null; - } -} - -/** - * Parses the optional WINDOW clause for SELECT - */ -SqlNodeList WindowOpt() : -{ - SqlIdentifier id; - SqlWindow e; - List<SqlNode> list; - SqlParserPos pos; -} -{ - <WINDOW> id = SimpleIdentifier() <AS> e = WindowSpecification() - { - pos = getPos(); - 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, 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; - SqlParserPos endPos; - SqlParserPos pos; - SqlLiteral allowPartial = null; -} -{ - <LPAREN> { startPos = pos = getPos(); } - ( - id = SimpleIdentifier() - | - { id = null; } - ) - ( - <PARTITION> - { pos = getPos(); } - <BY> - partitionList = ExpressionCommaList(pos, 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> { pos = getPos(); } <PARTIAL> { - allowPartial = SqlLiteral.createBoolean(true, pos.plus(getPos())); - } - | - <DISALLOW> { pos = getPos(); } <PARTIAL> { - allowPartial = SqlLiteral.createBoolean(false, pos.plus(getPos())); - } - ] - <RPAREN> - { - endPos = getPos(); - return SqlWindow.create( - null, id, partitionList, orderList, - isRows, lowerBound, upperBound, allowPartial, - startPos.plus(endPos)); - } -} - -SqlNode WindowRange() : -{ - SqlNode e; - SqlParserPos pos = null; - SqlParserPos endPos; -} -{ - <CURRENT> {pos = getPos();} <ROW> - { - endPos = getPos(); - return SqlWindow.createCurrentRow(pos.plus(endPos)); - } - | - <UNBOUNDED> - { pos = getPos();} - ( - <PRECEDING> - { - endPos = getPos(); - return SqlWindow.createUnboundedPreceding(pos.plus(endPos)); - } - | - <FOLLOWING> - { - endPos = getPos(); - return SqlWindow.createUnboundedFollowing(pos.plus(endPos)); - } - ) - | - 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; - SqlParserPos pos; -} -{ - <ORDER> { - pos = getPos(); - 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(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, pos.plusAll(list)); - } -} - -/** - * 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); - } - )? - ( - <NULLS> <FIRST> { - e = SqlStdOperatorTable.NULLS_FIRST.createCall(getPos(), e); - } - | - <NULLS> <LAST> { - e = SqlStdOperatorTable.NULLS_LAST.createCall(getPos(), e); - } - )? - { - return e; - } -} - -/** - * Parses a MATCH_RECOGNIZE clause following a table expression. - */ -SqlMatchRecognize MatchRecognizeOpt(SqlNode tableRef) : -{ - final SqlParserPos startPos; - SqlParserPos pos; - SqlNode pattern; - SqlNodeList patternDefList; - SqlLiteral isStrictStarts = SqlLiteral.createBoolean(false, getPos()); - SqlLiteral isStrictEnds = SqlLiteral.createBoolean(false, getPos()); -} -{ - <MATCH_RECOGNIZE> { startPos = getPos(); } <LPAREN> - <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> - <DEFINE> { pos = getPos(); } - patternDefList = PatternDefinitionCommaList(pos) - <RPAREN> { - return new SqlMatchRecognize(startPos.plus(getPos()), tableRef, - pattern, isStrictStarts, isStrictEnds, patternDefList); - } -} - -SqlNode PatternExpression() : -{ - SqlNode left; - SqlNode right; -} -{ - left = PatternTerm() - ( - <VERTICAL_BAR> - right = PatternTerm() { - left = SqlStdOperatorTable.PATTERN_ALTER.createCall( - left.getParserPosition().plus(getPos()), left, right); - } - )* - { - return left; - } -} - -SqlNode PatternTerm() : -{ - SqlNode left; - SqlNode right; -} -{ - left = PatternFactor() - ( - right = PatternFactor() { - left = SqlStdOperatorTable.PATTERN_CONCAT.createCall( - left.getParserPosition().plus(getPos()), 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() - [ - ( - <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( - extra.getParserPosition().plus(getPos()), extra); - e = SqlStdOperatorTable.PATTERN_CONCAT.createCall( - e.getParserPosition().plus(getPos()), 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( - e.getParserPosition().plus(getPos()), - e, startNum, endNum, reluctant); - } - } -} - -SqlNode PatternPrimary() : -{ - SqlParserPos pos; - SqlNode e; - List<SqlNode> eList; -} -{ - ( - e = SimpleIdentifier() - | - <LPAREN> e = PatternExpression() <RPAREN> - | - <LBRACE> { pos = getPos(); } - <MINUS> e = PatternExpression() - <MINUS> <RBRACE> { - e = SqlStdOperatorTable.PATTERN_EXCLUDE.createCall( - pos.plus(getPos()), e); - } - | - ( - <PERMUTE> { pos = getPos(); } - <LPAREN> - e = PatternExpression() { - eList = new ArrayList<SqlNode>(); - eList.add(e); - } - ( - <COMMA> - e = PatternExpression() - { - eList.add(e); - } - )* - <RPAREN> { - e = SqlStdOperatorTable.PATTERN_PERMUTE.createCall( - pos.plus(getPos()), eList); - } - ) - ) - { - return e; - } -} - -SqlNodeList PatternDefinitionCommaList(SqlParserPos pos) : -{ - SqlNode e; - final List<SqlNode> eList = new ArrayList<SqlNode>(); -} -{ - e = PatternDefinition() { - if (pos == null) { - pos = e.getParserPosition(); - } - eList.add(e); - } - ( - <COMMA> - e = PatternDefinition() { - eList.add(e); - } - )* - { - return new SqlNodeList(eList, pos.plus(getPos())); - } -} - -SqlNode PatternDefinition() : -{ - SqlNode var; - SqlNode e; -} -{ - var = SimpleIdentifier() - <AS> - e = Expression(ExprContext.ACCEPT_SUB_QUERY) { - return SqlStdOperatorTable.AS.createCall( - var.getParserPosition().plus(getPos()), 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; -} -{ - ( - op = PrefixRowOperator() { - checkNonQueryExpression(exprContext); - list.add(new SqlParserUtil.ToTreeListItem(op, getPos())); - } - )* - e = Expression3(exprContext) { - list.add(e); - } -} - -/** - * 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; - SqlNodeList nodeList; - SqlNode e; - SqlOperator op; - SqlParserPos pos = getPos(); -} -{ - Expression2b(exprContext, list) - ( - ( - 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; - pos = getPos(); - } - | - <IN> - { - op = SqlStdOperatorTable.IN; - pos = getPos(); - } - ) - nodeList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR) - { - list.add(new SqlParserUtil.ToTreeListItem(op, pos)); - pos = pos.plus(getPos()); - // 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; - pos = getPos(); - } - [ - <SYMMETRIC> { op = SqlStdOperatorTable.SYMMETRIC_NOT_BETWEEN; } - | - <ASYMMETRIC> - ] - | - <BETWEEN> - { - op = SqlStdOperatorTable.BETWEEN; - pos = getPos(); - } - [ - <SYMMETRIC> { op = SqlStdOperatorTable.SYMMETRIC_BETWEEN; } - | - <ASYMMETRIC> - ] - ) - e = Expression3(ExprContext.ACCEPT_SUB_QUERY) - { - list.add(new SqlParserUtil.ToTreeListItem(op, pos)); - list.add(e); - } - | - { - checkNonQueryExpression(exprContext); - pos = getPos(); - } - ( - <NOT> - ( - <LIKE> { op = SqlStdOperatorTable.NOT_LIKE; } - | - <SIMILAR> <TO> { op = SqlStdOperatorTable.NOT_SIMILAR_TO; } - ) - | - <LIKE> { op = SqlStdOperatorTable.LIKE; } - | - <SIMILAR> <TO> { op = SqlStdOperatorTable.SIMILAR_TO; } - ) - list2 = Expression2(ExprContext.ACCEPT_SUB_QUERY) - { - list.add(new SqlParserUtil.ToTreeListItem(op, pos)); - list.addAll(list2); - } - [ - LOOKAHEAD(2) - <ESCAPE> e = Expression3(ExprContext.ACCEPT_SUB_QUERY) - { - pos = getPos(); - list.add( - new SqlParserUtil.ToTreeListItem( - SqlStdOperatorTable.ESCAPE, pos)); - list.add(e); - } - ] - | - LOOKAHEAD(3) op = BinaryRowOperator() - { - checkNonQueryExpression(exprContext); - list.add(new SqlParserUtil.ToTreeListItem(op, getPos())); - } - Expression2b(ExprContext.ACCEPT_SUB_QUERY, list) - | - <LBRACKET> - e = Expression(ExprContext.ACCEPT_SUB_QUERY) - <RBRACKET> - { - list.add( - new SqlParserUtil.ToTreeListItem( - SqlStdOperatorTable.ITEM, getPos())); - list.add(e); - } - | - { - checkNonQueryExpression(exprContext); - } - op = PostfixRowOperator() - { - list.add(new SqlParserUtil.ToTreeListItem(op, getPos())); - } - ) - ) + - { - return list; - } - | - { - return list; - } - ) -} - -/** - * Parses a unary row expression, or a parenthesized expression of any - * kind. - */ -SqlNode Expression3(ExprContext exprContext) : -{ - SqlNode e; - SqlNodeList list; - SqlNodeList list1; - SqlNodeList list2; - SqlPrefixOperator op; - boolean rowSeen = false; - SqlParserPos pos; - SqlParserPos prefixRowOpPos; -} -{ - LOOKAHEAD(2) - e = AtomicRowExpression() - { - checkNonQueryExpression(exprContext); - return e; - } - | - e = CursorExpression(exprContext) { return e; } - | - LOOKAHEAD(3) - <ROW> list = ParenthesizedSimpleIdentifierList() - { - pos = getPos(); - if (exprContext != ExprContext.ACCEPT_ALL - && exprContext != ExprContext.ACCEPT_CURSOR) - { - throw SqlUtil.newContextException(pos, - RESOURCE.illegalRowExpression()); - } - return SqlStdOperatorTable.ROW.createCall(list); - } - | - { - pos = getPos(); - } - [ - <ROW> - { - pos = getPos(); rowSeen = true; - } - ] - list1 = ParenthesizedQueryOrCommaList(exprContext) { - if (rowSeen) { - // interpret as row constructor - return SqlStdOperatorTable.ROW.createCall(pos, list1.toArray()); - - } - } - [ - ( - <OVERLAPS> - 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 SqlStdOperatorTable.OVERLAPS.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( - list1.getParserPosition().plus(getPos()), - SqlParserUtil.toNodeArray(list3)); - } - } - throw SqlUtil.newContextException( - list1.getParserPosition().plus(getPos()), - 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(pos, list1.toArray()); - } - } -} - -/** - * Parses a COLLATE clause - */ -SqlCollation CollateClause() : -{ -} -{ - <COLLATE> <COLLATION_ID> - { - return new SqlCollation( - getToken(0).image, SqlCollation.Coercibility.EXPLICIT); - } -} - -/** - * Parses an atomic row expression. - */ -SqlNode AtomicRowExpression() : -{ - SqlNode e; - SqlParserPos pos; -} -{ - LOOKAHEAD(1) - e = Literal() { return e; } - | - e = DynamicParam() { return e; } - | - e = BuiltinFunctionCall() { return e; } - | - e = JdbcFunctionCall() { return e; } - | - e = MultisetConstructor() { return e; } - | - e = ArrayConstructor() { return e; } - | - e = MapConstructor() { return e; } - | - // 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() { return e; } - | - e = ContextVariable() { return e; } - | - e = CompoundIdentifier() { return e; } - | - e = NewSpecification(
<TRUNCATED>
