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

javacc.Parser.jj Maven / Gradle / Ivy


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


PARSER_BEGIN(DrillParserImpl)

package org.apache.drill.exec.planner.sql.parser.impl;

import org.apache.drill.exec.planner.sql.parser.*;
import org.apache.calcite.util.*;
import java.util.*;


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.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.SqlDynamicParam;
import org.apache.calcite.sql.SqlExplain;
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.SqlJdbcFunctionCall;
import org.apache.calcite.sql.SqlJoin;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlLiteral;
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.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.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.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

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.logging.Level;
import java.util.logging.Logger;

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 DrillParserImpl 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; /** * {@link SqlParserImplFactory} implementation for creating parser. */ public static final SqlParserImplFactory FACTORY = new SqlParserImplFactory() { public SqlAbstractParserImpl getParser(Reader stream) { return new DrillParserImpl(stream); } }; // implement SqlAbstractParserImpl public SqlParseException normalizeException(Throwable ex) { try { if (ex instanceof ParseException) { ex = cleanupParseException((ParseException) ex); } return convertException(ex); } catch (ParseException e) { throw new AssertionError(e); } } // implement SqlAbstractParserImpl public Metadata getMetadata() { synchronized (DrillParserImpl.class) { if (metadata == null) { metadata = new MetadataImpl( new DrillParserImpl(new java.io.StringReader(""))); } return metadata; } } // implement SqlAbstractParserImpl public void setTabSize(int tabSize) { jj_input_stream.setTabSize(tabSize); } // implement SqlAbstractParserImpl public void switchTo(String stateName) { int state = Arrays.asList(DrillParserImplTokenManager.lexStateNames) .indexOf(stateName); token_source.SwitchTo(state); } // implement SqlAbstractParserImpl public void setQuotedCasing(Casing quotedCasing) { this.quotedCasing = quotedCasing; } // implement SqlAbstractParserImpl public void setUnquotedCasing(Casing unquotedCasing) { this.unquotedCasing = unquotedCasing; } // implement SqlAbstractParserImpl public void setIdentifierMaxLength(int identifierMaxLength) { this.identifierMaxLength = identifierMaxLength; } // implement SqlAbstractParserImpl public SqlNode parseSqlExpressionEof() throws Exception { return SqlExpressionEof(); } // implement SqlAbstractParserImpl 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(DrillParserImpl) /***************************************** * Utility Codes for Semantical Analysis * *****************************************/ /* For Debug */ JAVACODE void debug_message1() { LOGGER.log(Level.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 keywords) : {} { E() } /* * Parses dialect-specific keywords immediately following the INSERT keyword. */ void SqlInsertKeywords(List 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; } } // End Parser.jj /* // 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 subquery 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 subquery 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_NONQUERY: case ACCEPT_SUBQUERY: 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(""); // 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 ] * *

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 ..." ( count = UnsignedNumericLiteral() | ) ] [ // ROW or ROWS is required in SQL:2008 but we make it optional // because it is not present in Postgres-style syntax. start = UnsignedNumericLiteral() [ | ] ] [ // SQL:2008-style syntax. "OFFSET ... FETCH ...". // If you specify both LIMIT and FETCH, FETCH wins. ( | ) count = UnsignedNumericLiteral() ( | ) ] { 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; } { { // we've now seen left paren, so queries inside should // be allowed as subqueries switch (exprContext) { case ACCEPT_SUBQUERY: 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 subquery. 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; SqlParserPos pos; } { { // we've now seen left paren, so a query by itself should // be interpreted as a subquery pos = getPos(); switch (exprContext) { case ACCEPT_SUBQUERY: 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, 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(); } { [ { 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 subqueries switch (exprContext) { case ACCEPT_SUBQUERY: firstExprContext = ExprContext.ACCEPT_NONCURSOR; break; case ACCEPT_CURSOR: firstExprContext = ExprContext.ACCEPT_ALL; break; default: firstExprContext = exprContext; break; } } } { [ name = SimpleIdentifier() ] ( { 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() ] ( { 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) { return query; } } /** * Parses an SQL statement. */ SqlNode SqlStmt() : { SqlNode stmt; } { ( stmt = SqlSetOption() | stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) | stmt = SqlExplain() | stmt = SqlInsert() | stmt = SqlDelete() | stmt = SqlUpdate() | stmt = SqlMerge() | stmt = SqlProcedureCall() | stmt = SqlShowTables() | stmt = SqlShowSchemas() | stmt = SqlDescribeTable() | stmt = SqlUseSchema() | stmt = SqlCreateOrReplaceView() | stmt = SqlDropView() | stmt = SqlShowFiles() | stmt = SqlCreateTable() | stmt = SqlDropTable() | stmt = SqlRefreshMetadata() ) { return stmt; } } /** * Parses an SQL statement followed by the end-of-file symbol. */ SqlNode SqlStmtEof() : { SqlNode stmt; } { stmt = SqlStmt() { return stmt; } } /** * Parses statement * SHOW TABLES [{FROM | IN} db_name] [LIKE 'pattern' | WHERE expr] */ SqlNode SqlShowTables() : { SqlParserPos pos; SqlIdentifier db = null; SqlNode likePattern = null; SqlNode where = null; } { { pos = getPos(); } [ ( | ) { db = CompoundIdentifier(); } ] [ { likePattern = StringLiteral(); } | { where = Expression(ExprContext.ACCEPT_SUBQUERY); } ] { return new SqlShowTables(pos, db, likePattern, where); } } /** * Parses statement * SHOW FILES [{FROM | IN} schema] */ SqlNode SqlShowFiles() : { SqlParserPos pos = null; SqlIdentifier db = null; } { { pos = getPos(); } [ ( | ) { db = CompoundIdentifier(); } ] { return new SqlShowFiles(pos, db); } } /** * Parses statement SHOW {DATABASES | SCHEMAS} [LIKE 'pattern' | WHERE expr] */ SqlNode SqlShowSchemas() : { SqlParserPos pos; SqlNode likePattern = null; SqlNode where = null; } { { pos = getPos(); } ( | ) [ { likePattern = StringLiteral(); } | { where = Expression(ExprContext.ACCEPT_SUBQUERY); } ] { return new SqlShowSchemas(pos, likePattern, where); } } /** * Parses statement * { DESCRIBE | DESC } tblname [col_name | wildcard ] */ SqlNode SqlDescribeTable() : { SqlParserPos pos; SqlIdentifier table; SqlIdentifier column = null; SqlNode columnPattern = null; } { ( | ) { pos = getPos(); } table = CompoundIdentifier() ( column = CompoundIdentifier() | columnPattern = StringLiteral() | E() ) { return new SqlDescribeTable(pos, table, column, columnPattern); } } SqlNode SqlUseSchema(): { SqlIdentifier schema; SqlParserPos pos; } { { pos = getPos(); } schema = CompoundIdentifier() { return new SqlUseSchema(pos, schema); } } /** Parses an optional field list and makes sure no field is a "*". */ SqlNodeList ParseOptionalFieldList(String relType) : { SqlNodeList fieldList; } { fieldList = ParseRequiredFieldList(relType) { return fieldList; } | { return SqlNodeList.EMPTY; } } /** Parses a required field list and makes sure no field is a "*". */ SqlNodeList ParseRequiredFieldList(String relType) : { SqlNodeList fieldList; } { fieldList = SimpleIdentifierCommaList() { for(SqlNode node : fieldList) { if (((SqlIdentifier)node).isStar()) throw new ParseException(String.format("%s's field list has a '*', which is invalid.", relType)); } return fieldList; } } /** * Parses a create view or replace existing view statement. * CREATE [OR REPLACE] VIEW view_name [ (field1, field2 ...) ] AS select_statement */ SqlNode SqlCreateOrReplaceView() : { SqlParserPos pos; boolean replaceView = false; SqlIdentifier viewName; SqlNode query; SqlNodeList fieldList; } { { pos = getPos(); } [ { replaceView = true; } ] viewName = CompoundIdentifier() fieldList = ParseOptionalFieldList("View") query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) { return new SqlCreateView(pos, viewName, fieldList, query, replaceView); } } /** * Parses a drop view statement. * DROP VIEW view_name; */ SqlNode SqlDropView() : { SqlParserPos pos; } { { pos = getPos(); } { return new SqlDropView(pos, CompoundIdentifier()); } } /** * Parses a CTAS statement. * CREATE TABLE tblname [ (field1, field2, ...) ] AS select_statement. */ SqlNode SqlCreateTable() : { SqlParserPos pos; SqlIdentifier tblName; SqlNodeList fieldList; SqlNodeList partitionFieldList; SqlNode query; } { { partitionFieldList = SqlNodeList.EMPTY; } { pos = getPos(); }

tblName = CompoundIdentifier() fieldList = ParseOptionalFieldList("Table") ( partitionFieldList = ParseRequiredFieldList("Partition") )? query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) { return new SqlCreateTable(pos, tblName, fieldList, partitionFieldList, query); } } /** * Parses a drop table statement. * DROP TABLE table_name; */ SqlNode SqlDropTable() : { SqlParserPos pos; } { { pos = getPos(); }
{ return new SqlDropTable(pos, CompoundIdentifier()); } } /** * Parse refresh table metadata statement. * REFRESH TABLE METADATA tblname */ SqlNode SqlRefreshMetadata() : { SqlParserPos pos; SqlIdentifier tblName; SqlNodeList fieldList; SqlNode query; } { { pos = getPos(); }
tblName = CompoundIdentifier() { return new SqlRefreshMetadata(pos, tblName); } } /** * Parses a leaf SELECT expression without ORDER BY. */ SqlSelect SqlSelect() : { final List keywords = Lists.newArrayList(); List selectList; SqlNode fromClause; SqlNode where; SqlNodeList groupBy; SqlNode having; SqlNodeList windowDecls; SqlParserPos pos; SqlParserPos selectListPos; SqlNode selectItem; } {
{ pos = getPos(); } tableRef = TableFunctionCall(pos) { } | tableRef = ExtendedTableRef() ) [ [ ] alias = Identifier() [ columnAliasList = ParenthesizedSimpleIdentifierList() ] { pos = getPos(); if (columnAliasList == null) { tableRef = SqlStdOperatorTable.AS.createCall( pos, tableRef, new SqlIdentifier(alias, pos)); } else { List idList = new ArrayList(); idList.add(tableRef); idList.add(new SqlIdentifier(alias, pos)); idList.addAll(columnAliasList.getList()); tableRef = SqlStdOperatorTable.AS.createCall(pos, idList); } } ] [ { pos = getPos(); } ( sample = StringLiteral() { String sampleName = SqlLiteral.stringValue(sample); SqlSampleSpec sampleSpec = SqlSampleSpec.createNamed(sampleName); SqlLiteral sampleLiteral = SqlLiteral.createSample(sampleSpec, pos); tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall( pos.plus(getPos()), 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 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 list = Lists.newArrayList(); } { { pos = getPos(); } ColumnType(list) ( ColumnType(list) )* { return new SqlNodeList(list, pos.plus(getPos())); } } void ColumnType(List list) : { SqlIdentifier name; SqlDataTypeSpec type; } { name = SimpleIdentifier() type = DataType() [ { type = type.withNullable(false); } ] { list.add(name); list.add(type); } } 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; SqlParserPos pos; } { { 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 list = new ArrayList(); SqlNode rowConstructor; } { rowConstructor = RowConstructor() { list.add(rowConstructor); } ( LOOKAHEAD(2) 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) { pos = getPos(); } valueList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR) { pos = pos.plus(getPos()); } | LOOKAHEAD(3) { pos = getPos(); } [ ] 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 // subqueries inside of ROW and row subqueries? 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; } { condition = Expression(ExprContext.ACCEPT_SUBQUERY) { return condition; } | { return null; } } /** * Parses the optional GROUP BY clause for SELECT. */ SqlNodeList GroupByOpt() : { List list = Lists.newArrayList(); SqlNode e; SqlParserPos pos; } { { pos = getPos(); } list = GroupingElementList() { return new SqlNodeList(list, pos.plusAll(list)); } | { return null; } } List GroupingElementList() : { List list = Lists.newArrayList(); SqlNode e; } { e = GroupingElement() { list.add(e); } ( e = GroupingElement() { list.add(e); } )* { return list; } } SqlNode GroupingElement() : { List list; SqlNodeList nlist; SqlNode e; SqlParserPos pos; } { { pos = getPos(); } list = GroupingElementList() { return SqlStdOperatorTable.GROUPING_SETS.createCall(pos, list); } | { pos = getPos(); } nlist = ExpressionCommaList(pos, ExprContext.ACCEPT_SUBQUERY) { return SqlStdOperatorTable.ROLLUP.createCall(nlist); } | { pos = getPos(); } nlist = ExpressionCommaList(pos, ExprContext.ACCEPT_SUBQUERY) { return SqlStdOperatorTable.CUBE.createCall(nlist); } | LOOKAHEAD(3) { return new SqlNodeList(getPos()); } | e = Expression(ExprContext.ACCEPT_SUBQUERY) { return e; } } /** * Parses a list of expressions separated by commas. */ SqlNodeList ExpressionCommaList( SqlParserPos pos, ExprContext exprContext) : { List 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) e = Expression(ExprContext.ACCEPT_SUBQUERY) { list.add(e); pos = pos.plus(getPos()); } ) * { return new SqlNodeList(list, pos); } } /** * Parses the optional HAVING clause for SELECT. */ SqlNode HavingOpt() : { SqlNode e; } { e = Expression(ExprContext.ACCEPT_SUBQUERY) { return e; } | { return null; } } /** * Parses the optional WINDOW clause for SELECT */ SqlNodeList WindowOpt() : { SqlIdentifier id; SqlWindow e; List list; SqlParserPos pos; } { id = SimpleIdentifier() 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) id = SimpleIdentifier() 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; } { { startPos = pos = getPos(); } ( id = SimpleIdentifier() | { id = null; } ) ( { pos = getPos(); } partitionList = ExpressionCommaList(pos, ExprContext.ACCEPT_NONQUERY) | { 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() ) ] [ { pos = getPos(); } { allowPartial = SqlLiteral.createBoolean(true, pos.plus(getPos())); } | { pos = getPos(); } { allowPartial = SqlLiteral.createBoolean(false, pos.plus(getPos())); } ] { 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; } { {pos = getPos();} { endPos = getPos(); return SqlWindow.createCurrentRow(pos.plus(endPos)); } | { pos = getPos();} ( { endPos = getPos(); return SqlWindow.createUnboundedPreceding(pos.plus(endPos)); } | { endPos = getPos(); return SqlWindow.createUnboundedFollowing(pos.plus(endPos)); } ) | e = Expression(ExprContext.ACCEPT_NONQUERY) ( { return SqlWindow.createPreceding( e, getPos()); } | { return SqlWindow.createFollowing( e, getPos()); } ) } /** * Parses an ORDER BY clause. */ SqlNodeList OrderBy(boolean accept) : { List list; SqlNode e; SqlParserPos pos; } { { 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()); } } 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, pos.plusAll(list)); } } /** * Parses one list item in an ORDER BY clause. */ SqlNode OrderItem() : { SqlNode e; } { e = Expression(ExprContext.ACCEPT_SUBQUERY) ( | { e = SqlStdOperatorTable.DESC.createCall(getPos(), e); } )? ( { e = SqlStdOperatorTable.NULLS_FIRST.createCall(getPos(), e); } | { e = SqlStdOperatorTable.NULLS_LAST.createCall(getPos(), e); } )? { return e; } } // ---------------------------------------------------------------------------- // 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_SUBQUERY) () { 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 /** * 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 opos 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) : { List list, list2; SqlNodeList nodeList; SqlNode e; SqlOperator op; SqlParserPos pos; } { e = Expression3(exprContext) { list = startList(e); } ( ( 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; pos = getPos(); } | { 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); } ( { op = SqlStdOperatorTable.NOT_BETWEEN; pos = getPos(); } [ { op = SqlStdOperatorTable.SYMMETRIC_NOT_BETWEEN; } | ] | { op = SqlStdOperatorTable.BETWEEN; pos = getPos(); } [ { op = SqlStdOperatorTable.SYMMETRIC_BETWEEN; } | ] ) e = Expression3(ExprContext.ACCEPT_SUBQUERY) { list.add(new SqlParserUtil.ToTreeListItem(op, pos)); list.add(e); } | { checkNonQueryExpression(exprContext); pos = getPos(); } ( ( { op = SqlStdOperatorTable.NOT_LIKE; } | { op = SqlStdOperatorTable.NOT_SIMILAR_TO; } ) | { op = SqlStdOperatorTable.LIKE; } | { op = SqlStdOperatorTable.SIMILAR_TO; } ) list2 = Expression2(ExprContext.ACCEPT_SUBQUERY) { list.add(new SqlParserUtil.ToTreeListItem(op, pos)); list.addAll(list2); } [ LOOKAHEAD(2) e = Expression3(ExprContext.ACCEPT_SUBQUERY) { pos = getPos(); list.add( new SqlParserUtil.ToTreeListItem( SqlStdOperatorTable.ESCAPE, pos)); list.add(e); } ] | LOOKAHEAD(3) op = BinaryRowOperator() { checkNonQueryExpression(exprContext); } e = Expression3(ExprContext.ACCEPT_SUBQUERY) { list.add(new SqlParserUtil.ToTreeListItem(op, getPos())); list.add(e); } | e = Expression(ExprContext.ACCEPT_SUBQUERY) { 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) list = ParenthesizedSimpleIdentifierList() { pos = getPos(); if (exprContext != ExprContext.ACCEPT_ALL && exprContext != ExprContext.ACCEPT_CURSOR) { throw SqlUtil.newContextException(pos, RESOURCE.illegalRowExpression()); } return SqlStdOperatorTable.ROW.createCall(list); } | op = PrefixRowOperator() { prefixRowOpPos = getPos(); checkNonQueryExpression(exprContext); } e = Expression3(ExprContext.ACCEPT_SUBQUERY) { SqlParserPos callPos = prefixRowOpPos.plus(e.getParserPosition()); return op.createCall(callPos, e); } | { pos = getPos(); } [ { pos = getPos(); rowSeen = true; } ] list1 = ParenthesizedQueryOrCommaList(exprContext) { if (rowSeen) { // interpret as row constructor return SqlStdOperatorTable.ROW.createCall(pos, list1.toArray()); } } [ ( 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 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() : { } { { 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( [] FunctionName() ) e = NamedFunctionCall() { return e; } | e = ContextVariable() { return e; } | e = CompoundIdentifier() { return e; } | e = NewSpecification() { return e; } | e = CaseExpression() { return e; } | e = SequenceExpression() { return e; } } SqlNode CaseExpression() : { SqlParserPos whenPos; SqlParserPos thenPos; SqlParserPos pos; SqlNode e; SqlNode caseIdentifier = null; SqlNode elseClause = null; List whenList = new ArrayList(); List thenList = new ArrayList(); } { { pos = getPos(); } [ caseIdentifier = Expression(ExprContext.ACCEPT_SUBQUERY) ] ( { whenPos = getPos(); } e = ExpressionCommaList(pos, ExprContext.ACCEPT_SUBQUERY) { if (((SqlNodeList) e).size() == 1) { e = ((SqlNodeList) e).get(0); } whenList.add(e); } e = Expression(ExprContext.ACCEPT_SUBQUERY) { thenPos = getPos(); thenList.add(e); } ) + [ elseClause = Expression(ExprContext.ACCEPT_SUBQUERY) ] { pos = pos.plus(getPos()); return SqlCase.createSwitched(pos, caseIdentifier, new SqlNodeList(whenList, whenPos), new SqlNodeList(thenList, thenPos), elseClause); } } SqlCall SequenceExpression() : { final SqlParserPos pos; final SqlOperator f; final SqlNode sequenceRef; } { ( { f = SqlStdOperatorTable.NEXT_VALUE; pos = getPos(); } | { f = SqlStdOperatorTable.CURRENT_VALUE; pos = getPos(); } ) sequenceRef = CompoundIdentifier() { return f.createCall(pos, sequenceRef); } } /** * Parses an expression for setting or resetting an option in SQL, such as QUOTED_IDENTIFIERS, * or explain plan level (physical/logical). */ SqlSetOption SqlSetOption() : { SqlParserPos pos = null; String scope = null; SqlIdentifier name; SqlNode val = null; } { ( { pos = getPos(); } scope = Scope() )? ( { pos = pos == null ? getPos() : pos; } name = CompoundIdentifier() ( val = Literal() | val = SimpleIdentifier() | { // OFF is handled by SimpleIdentifier, ON handled here. val = new SqlIdentifier(token.image.toUpperCase(), getPos()); } ) | { pos = pos == null ? getPos() : pos; } ( name = CompoundIdentifier() | { name = new SqlIdentifier(token.image.toUpperCase(), getPos()); } ) ) { return new SqlSetOption(pos.plus(getPos()), scope, name, val); } } String Scope() : { } { ( | ) { return token.image.toUpperCase(); } } /** * 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() : { SqlNumericLiteral num; SqlParserPos pos; } { num = UnsignedNumericLiteral() { return num; } | { pos = getPos(); } num = UnsignedNumericLiteral() { return SqlLiteral.createNegative(num, pos.plus(getPos())); } | 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() : { String p; SqlParserPos pos; } { { p = token.image; } { return parseDateLiteral(p, getPos()); } | { p = token.image; } { return parseTimeLiteral(p, getPos()); } | { p = token.image; } { return parseTimestampLiteral(p, getPos()); } | { pos = getPos(); } { return parseDateLiteral(token.image, pos.plus(getPos())); } |