generated parser files using javacc
diff --git a/WORKSPACE b/WORKSPACE
index 22c75f8..871d713 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -495,6 +495,15 @@
 )
 # end Pulsar Client
 
+# java cc
+new_http_archive(
+  name = "javacc",
+  url = "https://javacc.org/downloads/javacc-5.0.tar.gz",
+  strip_prefix = "javacc-5.0",
+  build_file = "third_party/java/javacc/javacc.BUILD",
+)
+# end java cc
+
 # Kubernetes java client
 kubernetes_client_version = "1.0.0-beta1"
 squareup_okhttp_version = "2.7.5"
@@ -891,4 +900,4 @@
 )
 
 load("@io_bazel_rules_scala//scala:scala.bzl", "scala_repositories")
-scala_repositories()
\ No newline at end of file
+scala_repositories()
diff --git a/heron/sql/src/java/BUILD b/heron/sql/src/java/BUILD
new file mode 100644
index 0000000..2c9ea2e
--- /dev/null
+++ b/heron/sql/src/java/BUILD
@@ -0,0 +1,26 @@
+licenses(["notice"])
+
+package(default_visibility = ["//visibility:public"])
+
+generated_files = [
+    "ParseException.java",
+    "Token.java",
+    "TokenMgrError.java",
+    "SimpleCharStream.java",
+    "HeronSqlParserImpl.java",
+    "HeronSqlParserImplConstants.java",
+    "HeronSqlParserImplTokenManager.java",
+]
+
+genrule(
+    name = "parser-srcs",
+    srcs = ["com/twitter/heron/sql/parser/impl/Parser.jj"],
+    outs = generated_files, 
+    tools = [
+        "//third_party/java:javacc",
+    ],
+    cmd = "\n".join([
+        'export INSTALL_DIR=$$(pwd)/$(@D)',
+        '$(location //third_party/java:javacc) -OUTPUT_DIRECTORY=$$INSTALL_DIR $<',
+    ]),
+)
diff --git a/heron/sql/src/java/com/twitter/heron/sql/parser/impl/Parser.jj b/heron/sql/src/java/com/twitter/heron/sql/parser/impl/Parser.jj
new file mode 100644
index 0000000..a9bb686
--- /dev/null
+++ b/heron/sql/src/java/com/twitter/heron/sql/parser/impl/Parser.jj
@@ -0,0 +1,6240 @@
+
+options {
+    STATIC = false;
+    IGNORE_CASE = true;
+    UNICODE_INPUT = true;
+}
+
+
+PARSER_BEGIN(HeronSqlParserImpl)
+
+package com.twitter.heron.sql.parser.impl;
+
+import org.apache.calcite.sql.validate.*;
+import org.apache.calcite.util.*;
+import com.twitter.heron.sql.parser.*;
+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.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.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.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 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 HeronSqlParserImpl 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 HeronSqlParserImpl(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 (HeronSqlParserImpl.class) {
+            if (metadata == null) {
+                metadata = new MetadataImpl(
+                    new HeronSqlParserImpl(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(HeronSqlParserImpl.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(HeronSqlParserImpl)
+
+
+/*****************************************
+ * Utility Codes for Semantical 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;
+    }
+}
+
+
+
+// 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 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()
+        |
+        stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
+        |
+        stmt = SqlExplain()
+        |
+        stmt = SqlDescribe()
+        |
+        stmt = SqlInsert()
+        |
+        stmt = SqlDelete()
+        |
+        stmt = SqlUpdate()
+        |
+        stmt = SqlMerge()
+        |
+        stmt = SqlProcedureCall()
+
+        |
+        stmt = SqlCreateTable()
+        |
+        stmt = SqlCreateFunction()
+    )
+    {
+        return stmt;
+    }
+}
+
+/**
+ * Parses an SQL statement followed by the end-of-file symbol.
+ */
+SqlNode SqlStmtEof() :
+{
+    SqlNode stmt;
+}
+{
+    stmt = SqlStmt() <EOF>
+    {
+        return stmt;
+    }
+}
+
+
+
+private void ColumnDef(List<ColumnDefinition> list) :
+{
+    SqlParserPos pos;
+    SqlIdentifier name;
+    SqlDataTypeSpec type;
+    ColumnConstraint constraint = null;
+    SqlMonotonicity monotonicity = SqlMonotonicity.NOT_MONOTONIC;
+}
+{
+    name = SimpleIdentifier() { pos = getPos(); }
+    type = DataType()
+    [
+      <PRIMARY> <KEY>
+      [ <ASC>   { monotonicity = SqlMonotonicity.INCREASING; }
+      | <DESC>  { monotonicity = SqlMonotonicity.DECREASING; }
+      ]
+      { constraint = new ColumnConstraint.PrimaryKey(monotonicity, getPos()); }
+    ]
+    {
+        list.add(new ColumnDefinition(name, type, constraint, pos));
+    }
+}
+
+SqlNodeList ColumnDefinitionList() :
+{
+    SqlParserPos pos;
+    List<ColumnDefinition> list = Lists.newArrayList();
+}
+{
+    <LPAREN> { pos = getPos(); }
+    ColumnDef(list)
+    ( <COMMA> ColumnDef(list) )*
+    <RPAREN> {
+        return new SqlNodeList(list, pos.plus(getPos()));
+    }
+}
+
+/**
+ * CREATE EXTERNAL TABLE ( IF NOT EXISTS )?
+ *   ( database_name '.' )? table_name ( '(' column_def ( ',' column_def )* ')'
+ *   ( STORED AS INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname )?
+ *   LOCATION location_uri
+ *   ( TBLPROPERTIES tbl_properties )?
+ *   ( AS select_stmt )
+ */
+SqlNode SqlCreateTable() :
+{
+    SqlParserPos pos;
+    SqlIdentifier tblName;
+    SqlNodeList fieldList;
+    SqlNode location;
+    SqlNode parallelism = null;
+    SqlNode input_format_class_name = null, output_format_class_name = null;
+    SqlNode tbl_properties = null;
+    SqlNode select = null;
+}
+{
+    <CREATE> { pos = getPos(); }
+    <EXTERNAL> <TABLE>
+    tblName = CompoundIdentifier()
+    fieldList = ColumnDefinitionList()
+    [
+      <STORED> <AS>
+      <INPUTFORMAT> input_format_class_name = StringLiteral()
+      <OUTPUTFORMAT> output_format_class_name = StringLiteral()
+    ]
+    <LOCATION>
+    location = StringLiteral()
+    [ <PARALLELISM> parallelism = UnsignedNumericLiteral() ]
+    [ <TBLPROPERTIES> tbl_properties = StringLiteral() ]
+    [ <AS> select = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) ] {
+        return new SqlCreateTable(pos, tblName, fieldList,
+        input_format_class_name, output_format_class_name, location,
+        parallelism, tbl_properties, select);
+    }
+}
+
+/**
+ * CREATE FUNCTION functionname AS 'classname'
+ */
+SqlNode SqlCreateFunction() :
+{
+    SqlParserPos pos;
+    SqlIdentifier functionName;
+    SqlNode className;
+    SqlNode jarName = null;
+}
+{
+    <CREATE> { pos = getPos(); }
+    <FUNCTION>
+        functionName = CompoundIdentifier()
+    <AS>
+        className = StringLiteral()
+    [
+      <USING> <JAR>
+      jarName = StringLiteral()
+    ]
+    {
+      return new SqlCreateFunction(pos, functionName, className, jarName);
+    }
+}
+/**
+ * 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();
+    SqlIdentifier table;
+    SqlNode source;
+    SqlNodeList columnList = null;
+    SqlParserPos pos;
+}
+{
+    (
+        <INSERT>
+    |
+        <UPSERT> { keywords.add(SqlInsertKeyword.UPSERT.symbol(getPos())); }
+    )
+    SqlInsertKeywords(keywords)
+    <INTO> table = CompoundIdentifier()
+    {
+        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);
+            }
+        }
+    |
+        [ <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);
+            }
+        }
+    |
+        <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 = SqlLiteral.stringValue(sample);
+                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;
+}
+{
+    name = SimpleIdentifier()
+    type = DataType()
+    [ <NOT> <NULL> { type = type.withNullable(false); } ]
+    {
+        list.add(name);
+        list.add(type);
+    }
+}
+
+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;
+    }
+}
+
+// ----------------------------------------------------------------------------
+// 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() { 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<SqlNode> whenList = new ArrayList<SqlNode>();
+    List<SqlNode> thenList = new ArrayList<SqlNode>();
+}
+{
+    <CASE>
+    {
+        pos = getPos();
+    }
+    [
+        caseIdentifier = Expression(ExprContext.ACCEPT_SUB_QUERY)
+    ]
+    (
+        <WHEN>
+        { whenPos = getPos(); }
+        e = ExpressionCommaList(pos, ExprContext.ACCEPT_SUB_QUERY)
+        {
+            if (((SqlNodeList) e).size() == 1) {
+                e = ((SqlNodeList) e).get(0);
+            }
+            whenList.add(e);
+        }
+        <THEN> e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        {  thenPos = getPos(); thenList.add(e); }
+    ) +
+    [
+        <ELSE> elseClause = Expression(ExprContext.ACCEPT_SUB_QUERY)
+    ]
+    <END>
+    {
+        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;
+}
+{
+    (
+        <NEXT> { f = SqlStdOperatorTable.NEXT_VALUE; pos = getPos(); }
+    |
+        <CURRENT> { f = SqlStdOperatorTable.CURRENT_VALUE; pos = getPos(); }
+    )
+    <VALUE> <FOR> sequenceRef = CompoundIdentifier() {
+        return f.createCall(pos, sequenceRef);
+    }
+}
+
+/**
+ * Parses "SET &lt;NAME&gt; = VALUE" or "RESET &lt;NAME&gt;", without a leading
+ * "ALTER &lt;SCOPE&gt;".
+ */
+SqlSetOption SqlSetOption(SqlParserPos pos, String scope) :
+{
+    SqlIdentifier name;
+    final SqlNode val;
+}
+{
+    (
+        <SET> {
+            pos = pos == null ? getPos() : pos;
+        }
+        name = CompoundIdentifier()
+        <EQ>
+        (
+            val = Literal()
+        |
+            val = SimpleIdentifier()
+        |
+            <ON> {
+                // OFF is handled by SimpleIdentifier, ON handled here.
+                val = new SqlIdentifier(token.image.toUpperCase(), getPos());
+            }
+        )
+        {
+            return new SqlSetOption(pos.plus(getPos()), scope, name, val);
+        }
+    |
+        <RESET> {
+            pos = pos == null ? getPos() : pos;
+        }
+        (
+            name = CompoundIdentifier()
+        |
+            <ALL> {
+                name = new SqlIdentifier(token.image.toUpperCase(), getPos());
+            }
+        )
+        {
+            return new SqlSetOption(pos.plus(getPos()), scope, name, null);
+        }
+    )
+}
+
+/**
+ * Parses an expression for setting or resetting an option in SQL, such as QUOTED_IDENTIFIERS,
+ * or explain plan level (physical/logical).
+ */
+SqlAlter SqlAlter() :
+{
+    final SqlParserPos pos;
+    final String scope;
+    final SqlAlter alterNode;
+}
+{
+    (
+        <ALTER> { pos = getPos(); }
+        scope = Scope()
+    )
+    (
+        alterNode = SqlSetOption(pos, scope)
+
+    )
+    {
+        return alterNode;
+    }
+}
+
+String Scope() :
+{
+}
+{
+    ( <SYSTEM> | <SESSION> ) { 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() :
+{
+}
+{
+    <UNSIGNED_INTEGER_LITERAL>
+    {
+        return SqlLiteral.createExactNumeric(token.image, getPos());
+    }
+    |
+    <DECIMAL_NUMERIC_LITERAL>
+    {
+        return SqlLiteral.createExactNumeric(token.image, getPos());
+    }
+    |
+    <APPROX_NUMERIC_LITERAL>
+    {
+        return SqlLiteral.createApproxNumeric(token.image, getPos());
+    }
+}
+
+/** Parses a numeric literal (can be signed) */
+SqlLiteral NumericLiteral() :
+{
+    SqlNumericLiteral num;
+    SqlParserPos pos;
+}
+{
+    <PLUS> num = UnsignedNumericLiteral()
+    {
+        return num;
+    }
+    |
+    <MINUS> { pos = getPos(); } num = UnsignedNumericLiteral()
+    {
+        return SqlLiteral.createNegative(num, pos.plus(getPos()));
+    }
+    |
+    num = UnsignedNumericLiteral()
+    {
+        return num;
+    }
+}
+
+/** Parse a special literal keyword */
+SqlLiteral SpecialLiteral() :
+{
+}
+{
+    <TRUE> { return SqlLiteral.createBoolean(true, getPos()); }
+    |
+    <FALSE> { return SqlLiteral.createBoolean(false, getPos()); }
+    |
+    <UNKNOWN> { return SqlLiteral.createUnknown(getPos()); }
+    |
+    <NULL> { return SqlLiteral.createNull(getPos()); }
+}
+
+/**
+ * Parses a string literal. The literal may be continued onto several
+ * lines.  For a simple literal, the result is an SqlLiteral.  For a continued
+ * literal, the result is an SqlCall expression, which concatenates 2 or more
+ * string literals; the validator reduces this.
+ *
+ * @see SqlLiteral#unchain(SqlNode)
+ * @see SqlLiteral#stringValue(SqlNode)
+ *
+ * @return a literal expression
+ */
+SqlNode StringLiteral() :
+{
+    String p;
+    int nfrags = 0;
+    List<SqlLiteral> frags = null;
+    char unicodeEscapeChar = 0;
+}
+{
+    // A continued string literal consists of a head fragment and one or more
+    // tail fragments. Since comments may occur between the fragments, and
+    // comments are special tokens, each fragment is a token. But since spaces
+    // or comments may not occur between the prefix and the first quote, the
+    // head fragment, with any prefix, is one token.
+
+    <BINARY_STRING_LITERAL>
+    {
+        try {
+            p = SqlParserUtil.trim(token.image, "xX'");
+            frags = startList(SqlLiteral.createBinaryString(p, getPos()));
+            nfrags++;
+        } catch (NumberFormatException ex) {
+            throw SqlUtil.newContextException(getPos(),
+                RESOURCE.illegalBinaryString(token.image));
+        }
+    }
+    (
+        <QUOTED_STRING>
+        {
+            try {
+                p = SqlParserUtil.trim(token.image, "'"); // no embedded quotes
+                frags.add(SqlLiteral.createBinaryString(p, getPos()));
+                nfrags++;
+            } catch (NumberFormatException ex) {
+                throw SqlUtil.newContextException(getPos(),
+                    RESOURCE.illegalBinaryString(token.image));
+            }
+        }
+    ) *
+    {
+        assert (nfrags > 0);
+        if (nfrags == 1) {
+            return frags.get(0); // just the head fragment
+        } else {
+            SqlParserPos pos2 = SqlParserPos.sum(frags);
+            return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, frags);
+        }
+    }
+    |
+    {
+        String charSet = null;
+    }
+    (
+        <PREFIXED_STRING_LITERAL>
+        { charSet = SqlParserUtil.getCharacterSet(token.image); }
+        | <QUOTED_STRING>
+        | <UNICODE_STRING_LITERAL>
+        {
+            // TODO jvs 2-Feb-2009:  support the explicit specification of
+            // a character set for Unicode string literals, per SQL:2003
+            unicodeEscapeChar = BACKSLASH;
+            charSet = "UTF16";
+        }
+    )
+    {
+        p = SqlParserUtil.parseString(token.image);
+        SqlCharStringLiteral literal;
+        try {
+            literal = SqlLiteral.createCharString(p, charSet, getPos());
+        } catch (java.nio.charset.UnsupportedCharsetException e) {
+            throw SqlUtil.newContextException(getPos(),
+                RESOURCE.unknownCharacterSet(charSet));
+        }
+        frags = startList(literal);
+        nfrags++;
+    }
+    (
+        <QUOTED_STRING>
+        {
+            p = SqlParserUtil.parseString(token.image);
+            try {
+                literal = SqlLiteral.createCharString(p, charSet, getPos());
+            } catch (java.nio.charset.UnsupportedCharsetException e) {
+                throw SqlUtil.newContextException(getPos(),
+                    RESOURCE.unknownCharacterSet(charSet));
+            }
+            frags.add(literal);
+            nfrags++;
+        }
+    ) *
+    {
+    }
+    [
+        <UESCAPE> <QUOTED_STRING>
+        {
+            if (unicodeEscapeChar == 0) {
+                throw SqlUtil.newContextException(getPos(),
+                    RESOURCE.unicodeEscapeUnexpected());
+            }
+            String s = SqlParserUtil.parseString(token.image);
+            unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s);
+        }
+    ]
+    {
+    }
+    {
+        assert nfrags > 0;
+        if (nfrags == 1) {
+            // just the head fragment
+            SqlLiteral lit = (SqlLiteral) frags.get(0);
+            return lit.unescapeUnicode(unicodeEscapeChar);
+        } else {
+            SqlNode[] rands = (SqlNode[]) frags.toArray(new SqlNode[nfrags]);
+            for (int i = 0; i < rands.length; ++i) {
+                rands[i] = ((SqlLiteral) rands[i]).unescapeUnicode(
+                    unicodeEscapeChar);
+            }
+            SqlParserPos pos2 = SqlParserPos.sum(rands);
+            return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, rands);
+        }
+    }
+}
+
+
+/**
+ * Parses a date/time literal.
+ */
+SqlLiteral DateTimeLiteral() :
+{
+    String  p;
+    SqlParserPos pos;
+}
+{
+    <LBRACE_D> <QUOTED_STRING>
+    {
+        p = token.image;
+    }
+    <RBRACE>
+    {
+        return parseDateLiteral(p, getPos());
+    }
+    |
+    <LBRACE_T> <QUOTED_STRING>
+    {
+        p = token.image;
+    }
+    <RBRACE>
+    {
+        return parseTimeLiteral(p, getPos());
+    }
+    |
+    <LBRACE_TS> <QUOTED_STRING>
+    {
+        p = token.image;
+    }
+    <RBRACE>
+    {
+        return parseTimestampLiteral(p, getPos());
+    }
+    |
+    <DATE> { pos = getPos(); } <QUOTED_STRING>
+    {
+        return parseDateLiteral(token.image, pos.plus(getPos()));
+    }
+    |
+    <TIME> { pos = getPos(); } <QUOTED_STRING>
+    {
+        return parseTimeLiteral(token.image, pos.plus(getPos()));
+    }
+    |
+    <TIMESTAMP> { pos = getPos(); } <QUOTED_STRING>
+    {
+        return parseTimestampLiteral(token.image, pos.plus(getPos()));
+    }
+}
+
+/** Parses a MULTISET constructor */
+SqlNode MultisetConstructor() :
+{
+    List<SqlNode> args;
+    SqlNode e;
+    SqlParserPos pos;
+}
+{
+    <MULTISET> { pos = getPos(); }
+    (
+        LOOKAHEAD(1)
+        <LPAREN>
+        // by sub query "MULTISET(SELECT * FROM T)"
+        e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.MULTISET_QUERY.createCall(
+                pos.plus(getPos()), e);
+        }
+    |
+        // by enumeration "MULTISET[e0, e1, ..., eN]"
+        <LBRACKET> // TODO: do trigraph as well ??( ??)
+        e = Expression(ExprContext.ACCEPT_NON_QUERY) { args = startList(e); }
+        (
+            <COMMA> e = Expression(ExprContext.ACCEPT_NON_QUERY) { args.add(e); }
+        ) *
+        <RBRACKET>
+        {
+            return SqlStdOperatorTable.MULTISET_VALUE.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+}
+
+/** Parses an ARRAY constructor */
+SqlNode ArrayConstructor() :
+{
+    SqlNodeList args;
+    SqlNode e;
+    SqlParserPos pos;
+}
+{
+    <ARRAY> { pos = getPos(); }
+    (
+        LOOKAHEAD(1)
+        <LPAREN>
+        // by sub query "MULTISET(SELECT * FROM T)"
+        e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.ARRAY_QUERY.createCall(
+                pos.plus(getPos()), e);
+        }
+    |
+        // by enumeration "ARRAY[e0, e1, ..., eN]"
+        <LBRACKET> // TODO: do trigraph as well ??( ??)
+        (
+            args = ExpressionCommaList(pos, ExprContext.ACCEPT_NON_QUERY)
+        |
+            { args = new SqlNodeList(getPos()); }
+        )
+        <RBRACKET>
+        {
+            return SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+}
+
+/** Parses an MAP constructor */
+SqlNode MapConstructor() :
+{
+    SqlNodeList args;
+    SqlNode e;
+    SqlParserPos pos;
+}
+{
+    <MAP> { pos = getPos(); }
+    (
+        LOOKAHEAD(1)
+        <LPAREN>
+        // by sub query "MAP (SELECT empno, deptno FROM emp)"
+        e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.MAP_QUERY.createCall(
+                pos.plus(getPos()), e);
+        }
+    |
+        // by enumeration "MAP[k0, v0, ..., kN, vN]"
+        <LBRACKET> // TODO: do trigraph as well ??( ??)
+        (
+            args = ExpressionCommaList(pos, ExprContext.ACCEPT_NON_QUERY)
+        |
+            { args = new SqlNodeList(getPos()); }
+        )
+        <RBRACKET>
+        {
+            return SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+}
+
+/**
+ * Parses an interval literal.
+ */
+SqlLiteral IntervalLiteral() :
+{
+    String p;
+    SqlIntervalQualifier intervalQualifier;
+    int sign = 1;
+    SqlParserPos pos;
+}
+{
+    <INTERVAL> { pos = getPos(); }
+    [
+        <MINUS> { sign = -1; }
+    |
+        <PLUS> { sign = 1; }
+    ]
+    <QUOTED_STRING> { p = token.image; }
+    intervalQualifier = IntervalQualifier()
+    {
+        return parseIntervalLiteral(pos.plus(getPos()), sign, p, intervalQualifier);
+    }
+}
+
+SqlIntervalQualifier IntervalQualifier() :
+{
+    TimeUnit start;
+    TimeUnit end = null;
+    int startPrec = RelDataType.PRECISION_NOT_SPECIFIED;
+    int secondFracPrec = RelDataType.PRECISION_NOT_SPECIFIED;
+}
+{
+    (
+        <YEAR> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
+        [
+            LOOKAHEAD(2) <TO> <MONTH>
+            {
+                end = TimeUnit.MONTH;
+            }
+        ]
+        { start = TimeUnit.YEAR; }
+        |
+        <MONTH> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
+        { start = TimeUnit.MONTH; }
+        |
+        <DAY> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
+        [ LOOKAHEAD(2) <TO>
+            (
+                <HOUR> { end = TimeUnit.HOUR; }
+                | <MINUTE> { end = TimeUnit.MINUTE; }
+                | <SECOND> { end = TimeUnit.SECOND; }
+                [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
+            )
+        ]
+        { start = TimeUnit.DAY; }
+        |
+        <HOUR> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
+        [ LOOKAHEAD(2) <TO>
+            (
+                <MINUTE> { end = TimeUnit.MINUTE; }
+                | <SECOND> { end = TimeUnit.SECOND; }
+                [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
+            )
+        ]
+        { start = TimeUnit.HOUR; }
+        |
+        <MINUTE> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
+        [ LOOKAHEAD(2) <TO>
+            (
+                <SECOND> { end = TimeUnit.SECOND; }
+                [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
+            )
+        ]
+        { start = TimeUnit.MINUTE; }
+        |
+        <SECOND>
+        [   <LPAREN> startPrec = UnsignedIntLiteral()
+            [ <COMMA> secondFracPrec = UnsignedIntLiteral() ]
+            <RPAREN>
+        ]
+        { start = TimeUnit.SECOND; }
+    )
+    {
+        return new SqlIntervalQualifier(start,
+            startPrec,
+            end,
+            secondFracPrec,
+            getPos());
+    }
+}
+
+/**
+ * Parses time unit for EXTRACT, CEIL and FLOOR functions.
+ */
+TimeUnit TimeUnit() :
+{}
+{
+    <SECOND> { return TimeUnit.SECOND; }
+|   <MINUTE> { return TimeUnit.MINUTE; }
+|   <HOUR> { return TimeUnit.HOUR; }
+|   <DAY> { return TimeUnit.DAY; }
+|   <DOW> { return TimeUnit.DOW; }
+|   <DOY> { return TimeUnit.DOY; }
+|   <WEEK> { return TimeUnit.WEEK; }
+|   <MONTH> { return TimeUnit.MONTH; }
+|   <QUARTER> { return TimeUnit.QUARTER; }
+|   <YEAR> { return TimeUnit.YEAR; }
+|   <EPOCH> { return TimeUnit.EPOCH; }
+|   <DECADE> { return TimeUnit.DECADE; }
+|   <CENTURY> { return TimeUnit.CENTURY; }
+|   <MILLENNIUM> { return TimeUnit.MILLENNIUM; }
+}
+
+TimeUnit TimestampInterval() :
+{}
+{
+    <FRAC_SECOND> { return TimeUnit.MICROSECOND; }
+|   <MICROSECOND> { return TimeUnit.MICROSECOND; }
+|   <SQL_TSI_FRAC_SECOND> { return TimeUnit.MICROSECOND; }
+|   <SQL_TSI_MICROSECOND> { return TimeUnit.MICROSECOND; }
+|   <SECOND> { return TimeUnit.SECOND; }
+|   <SQL_TSI_SECOND> { return TimeUnit.SECOND; }
+|   <MINUTE> { return TimeUnit.MINUTE; }
+|   <SQL_TSI_MINUTE> { return TimeUnit.MINUTE; }
+|   <HOUR> { return TimeUnit.HOUR; }
+|   <SQL_TSI_HOUR> { return TimeUnit.HOUR; }
+|   <DAY> { return TimeUnit.DAY; }
+|   <SQL_TSI_DAY> { return TimeUnit.DAY; }
+|   <WEEK> { return TimeUnit.WEEK; }
+|   <SQL_TSI_WEEK> { return TimeUnit.WEEK; }
+|   <MONTH> { return TimeUnit.MONTH; }
+|   <SQL_TSI_MONTH> { return TimeUnit.MONTH; }
+|   <QUARTER> { return TimeUnit.QUARTER; }
+|   <SQL_TSI_QUARTER> { return TimeUnit.QUARTER; }
+|   <YEAR> { return TimeUnit.YEAR; }
+|   <SQL_TSI_YEAR> { return TimeUnit.YEAR; }
+}
+
+
+
+/**
+ * Parses a dynamic parameter marker.
+ */
+SqlDynamicParam DynamicParam() :
+{
+    SqlParserPos pos;
+}
+{
+    <HOOK>
+    {
+        pos = getPos();
+        return new SqlDynamicParam(nDynamicParams++, pos);
+    }
+}
+
+
+/**
+ * Parses a simple identifier as a string.
+ */
+String Identifier() :
+{
+    String id;
+    char unicodeEscapeChar = BACKSLASH;
+}
+{
+    (
+        <IDENTIFIER>
+        {
+            id = unquotedIdentifier();
+        }
+        | <QUOTED_IDENTIFIER>
+        {
+            id = SqlParserUtil.strip(getToken(0).image, DQ, DQ, DQDQ,
+                quotedCasing);
+        }
+        | <BACK_QUOTED_IDENTIFIER>
+        {
+            id = SqlParserUtil.strip(getToken(0).image, "`", "`", "``",
+                quotedCasing);
+        }
+        | <BRACKET_QUOTED_IDENTIFIER>
+        {
+            id = SqlParserUtil.strip(getToken(0).image, "[", "]", "]]",
+                quotedCasing);
+        }
+        | <UNICODE_QUOTED_IDENTIFIER>
+        {
+            id = getToken(0).image;
+            id = id.substring(id.indexOf('"'));
+            id = SqlParserUtil.strip(id, DQ, DQ, DQDQ, quotedCasing);
+        }
+        [ <UESCAPE> <QUOTED_STRING>
+            {
+                String s = SqlParserUtil.parseString(token.image);
+                unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s);
+            }
+        ]
+        {
+        }
+        {
+            SqlLiteral lit = SqlLiteral.createCharString(id, "UTF16", getPos());
+            lit = lit.unescapeUnicode(unicodeEscapeChar);
+            return lit.toValue();
+        }
+        | id = NonReservedKeyWord()
+    )
+    {
+        if (id.length() > this.identifierMaxLength) {
+            throw SqlUtil.newContextException(getPos(),
+                RESOURCE.identifierTooLong(id, this.identifierMaxLength));
+        }
+        return id;
+    }
+}
+
+/**
+ * Parses a simple identifier as an SqlIdentifier.
+ */
+SqlIdentifier SimpleIdentifier() :
+{
+    String p;
+    SqlParserPos pos;
+}
+{
+    p = Identifier(){pos = getPos();}
+    {
+        return new SqlIdentifier(p,pos);
+    }
+}
+
+/**
+ * Parses a comma-separated list of simple identifiers.
+ */
+void SimpleIdentifierCommaList(List<SqlNode> list) :
+{
+    SqlIdentifier id;
+}
+{
+    id = SimpleIdentifier() {list.add(id);}
+    (<COMMA> id = SimpleIdentifier() {list.add(id);}) *
+}
+
+/**
+  * List of simple identifiers in parentheses. The position extends from the
+  * open parenthesis to the close parenthesis.
+  */
+SqlNodeList ParenthesizedSimpleIdentifierList() :
+{
+    SqlParserPos pos;
+    List<SqlNode> list = new ArrayList<SqlNode>();
+}
+{
+    <LPAREN> { pos = getPos(); }
+    SimpleIdentifierCommaList(list)
+    <RPAREN>
+    {
+        return new SqlNodeList(list, pos.plus(getPos()));
+    }
+}
+
+/**
+ * Parses a compound identifier.
+ */
+SqlIdentifier CompoundIdentifier() :
+{
+    List<String> list = new ArrayList<String>();
+    List<SqlParserPos> posList = new ArrayList<SqlParserPos>();
+    String p;
+    boolean star = false;
+}
+{
+    p = Identifier()
+    {
+        posList.add(getPos());
+        list.add(p);
+    }
+    (
+        <DOT>
+        p = Identifier() {
+            list.add(p);
+            posList.add(getPos());
+        }
+    ) *
+    (
+        <DOT>
+        <STAR> {
+            star = true;
+            list.add("");
+            posList.add(getPos());
+        }
+    )?
+    {
+        SqlParserPos pos = SqlParserPos.sum(posList);
+        if (star) {
+            return SqlIdentifier.star(list, pos, posList);
+        }
+        return new SqlIdentifier(list, null, pos, posList);
+    }
+}
+
+/**
+ * Parses a comma-separated list of compound identifiers.
+ */
+void CompoundIdentifierCommaList(List<SqlNode> list) :
+{
+    SqlIdentifier id;
+}
+{
+    id = CompoundIdentifier() {list.add(id);}
+    (<COMMA> id = CompoundIdentifier() {list.add(id);}) *
+}
+
+/**
+  * List of compound identifiers in parentheses. The position extends from the
+  * open parenthesis to the close parenthesis.
+  */
+SqlNodeList ParenthesizedCompoundIdentifierList() :
+{
+    SqlParserPos pos;
+    List<SqlNode> list = new ArrayList<SqlNode>();
+}
+{
+    <LPAREN> { pos = getPos(); }
+    CompoundIdentifierCommaList(list)
+    <RPAREN>
+    {
+        return new SqlNodeList(list, pos.plus(getPos()));
+    }
+}
+
+/**
+ * Parses a NEW UDT(...) expression.
+ */
+SqlNode NewSpecification() :
+{
+    SqlParserPos callPos;
+    SqlNode routineCall;
+}
+{
+    <NEW>
+    {
+        callPos = getPos();
+    }
+    routineCall =
+    NamedRoutineCall(
+        SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR,
+        ExprContext.ACCEPT_SUB_QUERY)
+    {
+        return SqlStdOperatorTable.NEW.createCall(callPos, routineCall);
+    }
+}
+
+//TODO: real parse errors.
+int UnsignedIntLiteral() :
+{
+    Token t;
+}
+{
+    t = <UNSIGNED_INTEGER_LITERAL>
+    {
+        try {
+            return Integer.parseInt(t.image);
+        } catch (NumberFormatException ex) {
+            throw generateParseException();
+        }
+    }
+}
+
+int IntLiteral() :
+{
+    Token t;
+}
+{
+    ( t = <UNSIGNED_INTEGER_LITERAL> | <PLUS> t = <UNSIGNED_INTEGER_LITERAL> )
+    {
+        try {
+            return Integer.parseInt(t.image);
+        } catch (NumberFormatException ex) {
+            throw generateParseException();
+        }
+    }
+    |
+    <MINUS> t = <UNSIGNED_INTEGER_LITERAL>
+    {
+        try {
+            return -Integer.parseInt(t.image);
+        } catch (NumberFormatException ex) {
+            throw generateParseException();
+        }
+    }
+}
+
+// Type name with optional scale and precision
+SqlDataTypeSpec DataType() :
+{
+    SqlIdentifier typeName;
+    SqlIdentifier collectionTypeName = null;
+    int scale = -1;
+    int precision = -1;
+    String charSetName = null;
+    SqlParserPos pos;
+}
+{
+    (
+        typeName = TypeName()
+        {
+            pos = getPos();
+        }
+        [
+            <LPAREN>
+            precision = UnsignedIntLiteral()
+            [
+                <COMMA>
+                scale = UnsignedIntLiteral()
+            ]
+            <RPAREN>
+        ]
+        [
+            <CHARACTER> <SET>
+            charSetName = Identifier()
+        ]
+        [
+            collectionTypeName = CollectionsTypeName()
+        ]
+    )
+    {
+        if (null != collectionTypeName) {
+            return new SqlDataTypeSpec(
+                collectionTypeName,
+                typeName,
+                precision,
+                scale,
+                charSetName,
+                pos);
+        }
+        return new SqlDataTypeSpec(
+            typeName,
+            precision,
+            scale,
+            charSetName,
+            null,
+            pos);
+    }
+}
+
+// Some SQL type names need special handling due to the fact that they have
+// spaces in them but are not quoted.
+SqlIdentifier TypeName() :
+{
+    SqlIdentifier typeName = null;
+    SqlParserPos pos = getPos();
+}
+{
+    (
+        (<CHARACTER> | <CHAR>)
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.CHAR.name(), pos);
+        }
+        [
+            <VARYING>
+            {
+                typeName = new SqlIdentifier(
+                    SqlTypeName.VARCHAR.name(), pos);
+            }
+        ]
+        | <VARCHAR>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.VARCHAR.name(), pos);
+        }
+        | <DATE>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.DATE.name(),pos);
+        }
+        | <TIME>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.TIME.name(),pos);
+        }
+        | <TIMESTAMP>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.TIMESTAMP.name(),pos);
+        }
+        | (<DECIMAL> | <DEC> | <NUMERIC>)
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.DECIMAL.name(),pos);
+        }
+        | <BOOLEAN>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.BOOLEAN.name(),pos);
+        }
+        | <INTEGER>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.INTEGER.name(),pos);
+        }
+        | <INT>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.INTEGER.name(),pos);
+        }
+        |
+        (
+            <BINARY>
+            {
+                typeName = new SqlIdentifier(
+                    SqlTypeName.BINARY.name(),pos);
+            }
+        )
+        [
+            <VARYING>
+            {
+                typeName = new SqlIdentifier(
+                    SqlTypeName.VARBINARY.name(), pos);
+            }
+        ]
+        | <VARBINARY>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.VARBINARY.name(),pos);
+        }
+        | <TINYINT>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.TINYINT.name(),pos);
+        }
+        | <SMALLINT>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.SMALLINT.name(),pos);
+        }
+        | <BIGINT>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.BIGINT.name(),pos);
+        }
+        | <REAL>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.REAL.name(),pos);
+        }
+        | <DOUBLE> [ <PRECISION> ]
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.DOUBLE.name(),pos);
+        }
+        | <FLOAT>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.FLOAT.name(),pos);
+        }
+        | <ANY>
+        {
+            typeName = new SqlIdentifier(
+                SqlTypeName.ANY.name(),pos);
+        }
+        |   typeName = CollectionsTypeName()
+        |   typeName = CompoundIdentifier()
+    )
+    {
+        return typeName;
+    }
+}
+
+// Types used for for JDBC and ODBC scalar conversion function
+SqlJdbcDataTypeName JdbcOdbcDataTypeName() :
+{
+}
+{
+    (<SQL_CHAR> | <CHAR>) { return SqlJdbcDataTypeName.SQL_CHAR; }
+|   (<SQL_VARCHAR> | <VARCHAR>) { return SqlJdbcDataTypeName.SQL_VARCHAR; }
+|   (<SQL_DATE> | <DATE>) { return SqlJdbcDataTypeName.SQL_DATE; }
+|   (<SQL_TIME> | <TIME>) { return SqlJdbcDataTypeName.SQL_TIME; }
+|   (<SQL_TIMESTAMP> | <TIMESTAMP>) { return SqlJdbcDataTypeName.SQL_TIMESTAMP; }
+|   (<SQL_DECIMAL> | <DECIMAL>) { return SqlJdbcDataTypeName.SQL_DECIMAL; }
+|   (<SQL_NUMERIC> | <NUMERIC>) { return SqlJdbcDataTypeName.SQL_NUMERIC; }
+|   (<SQL_BOOLEAN> | <BOOLEAN>) { return SqlJdbcDataTypeName.SQL_BOOLEAN; }
+|   (<SQL_INTEGER> | <INTEGER>) { return SqlJdbcDataTypeName.SQL_INTEGER; }
+|   (<SQL_BINARY> | <BINARY>) { return SqlJdbcDataTypeName.SQL_BINARY; }
+|   (<SQL_VARBINARY> | <VARBINARY>) { return SqlJdbcDataTypeName.SQL_VARBINARY; }
+|   (<SQL_TINYINT> | <TINYINT>) { return SqlJdbcDataTypeName.SQL_TINYINT; }
+|   (<SQL_SMALLINT> | <SMALLINT>) { return SqlJdbcDataTypeName.SQL_SMALLINT; }
+|   (<SQL_BIGINT> | <BIGINT>) { return SqlJdbcDataTypeName.SQL_BIGINT; }
+|   (<SQL_REAL>| <REAL>) { return SqlJdbcDataTypeName.SQL_REAL; }
+|   (<SQL_DOUBLE> | <DOUBLE>) { return SqlJdbcDataTypeName.SQL_DOUBLE; }
+|   (<SQL_FLOAT> | <FLOAT>) { return SqlJdbcDataTypeName.SQL_FLOAT; }
+|   <SQL_INTERVAL_YEAR> { return SqlJdbcDataTypeName.SQL_INTERVAL_YEAR; }
+|   <SQL_INTERVAL_YEAR_TO_MONTH> { return SqlJdbcDataTypeName.SQL_INTERVAL_YEAR_TO_MONTH; }
+|   <SQL_INTERVAL_MONTH> { return SqlJdbcDataTypeName.SQL_INTERVAL_MONTH; }
+|   <SQL_INTERVAL_DAY> { return SqlJdbcDataTypeName.SQL_INTERVAL_DAY; }
+|   <SQL_INTERVAL_DAY_TO_HOUR> { return SqlJdbcDataTypeName.SQL_INTERVAL_DAY_TO_HOUR; }
+|   <SQL_INTERVAL_DAY_TO_MINUTE> { return SqlJdbcDataTypeName.SQL_INTERVAL_DAY_TO_MINUTE; }
+|   <SQL_INTERVAL_DAY_TO_SECOND> { return SqlJdbcDataTypeName.SQL_INTERVAL_DAY_TO_SECOND; }
+|   <SQL_INTERVAL_HOUR> { return SqlJdbcDataTypeName.SQL_INTERVAL_HOUR; }
+|   <SQL_INTERVAL_HOUR_TO_MINUTE> { return SqlJdbcDataTypeName.SQL_INTERVAL_HOUR_TO_MINUTE; }
+|   <SQL_INTERVAL_HOUR_TO_SECOND> { return SqlJdbcDataTypeName.SQL_INTERVAL_HOUR_TO_SECOND; }
+|   <SQL_INTERVAL_MINUTE> { return SqlJdbcDataTypeName.SQL_INTERVAL_MINUTE; }
+|   <SQL_INTERVAL_MINUTE_TO_SECOND> { return SqlJdbcDataTypeName.SQL_INTERVAL_MINUTE_TO_SECOND; }
+|   <SQL_INTERVAL_SECOND> { return SqlJdbcDataTypeName.SQL_INTERVAL_SECOND; }
+}
+
+SqlLiteral JdbcOdbcDataType() :
+{
+    SqlJdbcDataTypeName typeName;
+}
+{
+    typeName = JdbcOdbcDataTypeName() {
+        return typeName.symbol(getPos());
+    }
+}
+
+SqlIdentifier CollectionsTypeName() :
+{
+    SqlIdentifier typeName = null;
+    SqlParserPos pos = getPos();
+}
+{
+    <MULTISET>
+    {
+        typeName = new SqlIdentifier(
+            SqlTypeName.MULTISET.name(),pos);
+        return typeName;
+    }
+}
+
+/**
+ * Parses a CURSOR(query) expression.  The parser allows these
+ * anywhere, but the validator restricts them to appear only as
+ * arguments to table functions.
+ */
+SqlNode CursorExpression(ExprContext exprContext) :
+{
+    SqlNode e;
+    SqlParserPos pos;
+}
+{
+    <CURSOR>
+    {
+        pos = getPos();
+        if (exprContext != ExprContext.ACCEPT_ALL
+            && exprContext != ExprContext.ACCEPT_CURSOR)
+        {
+            throw SqlUtil.newContextException(pos,
+                RESOURCE.illegalCursorExpression());
+        }
+    }
+    e = Expression(ExprContext.ACCEPT_QUERY)
+    {
+        return SqlStdOperatorTable.CURSOR.createCall(pos, e);
+    }
+}
+
+/**
+ * Parses a call to a builtin function with special syntax.
+ */
+SqlNode BuiltinFunctionCall() :
+{
+    String name;
+    List<SqlNode> args = null;
+    SqlNode e = null;
+    SqlParserPos pos;
+    SqlParserPos starPos;
+    SqlParserPos namePos;
+    SqlDataTypeSpec dt;
+    TimeUnit interval;
+    SqlNode node;
+}
+{
+    //~ FUNCTIONS WITH SPECIAL SYNTAX ---------------------------------------
+    (
+        <CAST>
+        {
+            pos = getPos();
+        }
+        <LPAREN> e = Expression(ExprContext.ACCEPT_SUB_QUERY) { args = startList(e); }
+        <AS>
+        (
+            dt = DataType() { args.add(dt); }
+            |  <INTERVAL> e = IntervalQualifier() { args.add(e); }
+        )
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.CAST.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+    |
+    (
+        <EXTRACT>
+        {
+            pos = getPos();
+            TimeUnit unit;
+        }
+        <LPAREN>
+        unit = TimeUnit()
+        { args = startList(new SqlIntervalQualifier(unit, null, getPos())); }
+        <FROM>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY) { args.add(e); }
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.EXTRACT.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+    |
+    (
+        <POSITION>
+        {
+            pos = getPos();
+        }
+        <LPAREN>
+        // FIXME jvs 31-Aug-2006:  FRG-192:  This should be
+        // Expression(ExprContext.ACCEPT_SUB_QUERY), but that doesn't work
+        // because it matches the other kind of IN.
+        e = AtomicRowExpression() { args = startList(e); }
+        <IN>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY) { args.add(e);}
+        [
+            <FROM>
+            e = Expression(ExprContext.ACCEPT_SUB_QUERY) { args.add(e); }
+        ]
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.POSITION.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+    |
+    (
+        <CONVERT>
+        {
+            pos = getPos();
+        }
+        <LPAREN>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        {
+            args = startList(e);
+        }
+        <USING> name = Identifier()
+        {
+            namePos = getPos();
+            args.add(new SqlIdentifier(name, namePos));
+        }
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.CONVERT.createCall(
+                pos, SqlParserUtil.toNodeArray(args));
+        }
+    )
+    |
+    (
+        <TRANSLATE>
+        {
+            pos = getPos();
+        }
+        <LPAREN>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        {
+            args = startList(e);
+        }
+        (
+            <USING> name = Identifier() {
+                namePos = getPos();
+                args.add(new SqlIdentifier(name, namePos));
+            }
+            <RPAREN> {
+                return SqlStdOperatorTable.TRANSLATE.createCall(
+                    pos, SqlParserUtil.toNodeArray(args));
+            }
+        |
+            (
+                <COMMA> e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
+                    args.add(e);
+                }
+            )*
+            <RPAREN> {
+                return OracleSqlOperatorTable.TRANSLATE3.createCall(
+                    pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+            }
+        )
+    )
+    |
+    (
+        <OVERLAY>
+        {
+            pos = getPos();
+        }
+        <LPAREN>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        {
+            args = startList(e);
+        }
+        <PLACING>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        {
+            args.add(e);
+        }
+        <FROM>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        {
+            args.add(e);
+        }
+        [
+            <FOR>
+            e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+            {
+                args.add(e);
+            }
+        ]
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.OVERLAY.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+    |
+    (
+        <FLOOR>
+        e = FloorCeilOptions(getPos(), true)
+        { return e; }
+    )
+    |
+    (
+        ( <CEIL> | <CEILING>)
+        e = FloorCeilOptions(getPos(), false)
+        { return e; }
+    )
+    |
+    (
+        <SUBSTRING>
+        {
+            pos = getPos();
+        }
+        <LPAREN>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        { args = startList(e); }
+        ( <FROM> | <COMMA>)
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        { args.add(e); }
+        [
+            (<FOR> | <COMMA>)
+            e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+            { args.add(e); }
+        ]
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.SUBSTRING.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+    |
+    (
+        <TRIM>
+        {
+            SqlLiteral flag = null;
+            SqlNode trimChars = null;
+            pos = getPos();
+        }
+        <LPAREN>
+        [
+            LOOKAHEAD(2)
+            [
+                <BOTH>
+                {
+                    pos = getPos();
+                    flag = SqlTrimFunction.Flag.BOTH.symbol(pos);
+                }
+                |
+                <TRAILING>
+                {
+                    pos = getPos();
+                    flag = SqlTrimFunction.Flag.TRAILING.symbol(pos);
+                }
+                |
+                <LEADING>
+                {
+                    pos = getPos();
+                    flag = SqlTrimFunction.Flag.LEADING.symbol(pos);
+                }
+            ]
+            [ trimChars = Expression(ExprContext.ACCEPT_SUB_QUERY) ]
+            (
+                <FROM>
+                {
+                    if (null == flag && null == trimChars) {
+                        throw SqlUtil.newContextException(getPos(),
+                            RESOURCE.illegalFromEmpty());
+                    }
+                }
+                | <RPAREN>
+                {
+                    // This is to handle the case of TRIM(x)
+                    // (FRG-191).
+                    if (flag == null) {
+                        flag = SqlTrimFunction.Flag.BOTH.symbol(SqlParserPos.ZERO);
+                    }
+                    args = startList(flag);
+                    args.add(null); // no trim chars
+                    args.add(trimChars); // reinterpret trimChars as source
+                    return SqlStdOperatorTable.TRIM.createCall(
+                        pos, SqlParserUtil.toNodeArray(args));
+                }
+            )
+        ]
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+        {
+            if (flag == null) {
+                flag = SqlTrimFunction.Flag.BOTH.symbol(SqlParserPos.ZERO);
+            }
+            args = startList(flag);
+            args.add(trimChars);
+            args.add(e);
+        }
+        <RPAREN>
+        {
+            return SqlStdOperatorTable.TRIM.createCall(
+                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+        }
+    )
+    |
+    node = TimestampAddFunctionCall() { return node; }
+    |
+    node = TimestampDiffFunctionCall() { return node; }
+    |
+    node = ExtendedBuiltinFunctionCall() { return node; }
+}
+
+/**
+ * Parses a call to TIMESTAMPADD.
+ */
+SqlCall TimestampAddFunctionCall() :
+{
+    List<SqlNode> args;
+    SqlNode e;
+    SqlParserPos pos;
+    TimeUnit interval;
+    SqlNode node;
+}
+{
+    <TIMESTAMPADD> {
+        pos = getPos();
+    }
+    <LPAREN>
+    interval = TimestampInterval() {
+        args = startList(SqlLiteral.createSymbol(interval, getPos()));
+    }
+    <COMMA>
+    e = Expression(ExprContext.ACCEPT_SUB_QUERY) { args.add(e); }
+    <COMMA>
+    e = Expression(ExprContext.ACCEPT_SUB_QUERY) { args.add(e); }
+    <RPAREN> {
+        return SqlStdOperatorTable.TIMESTAMP_ADD.createCall(
+            pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+    }
+}
+
+/**
+ * Parses a call to TIMESTAMPDIFF.
+ */
+SqlCall TimestampDiffFunctionCall() :
+{
+    List<SqlNode> args;
+    SqlNode e;
+    SqlParserPos pos;
+    TimeUnit interval;
+    SqlNode node;
+}
+{
+    <TIMESTAMPDIFF> {
+        pos = getPos();
+    }
+    <LPAREN>
+    interval = TimestampInterval() {
+        args = startList(SqlLiteral.createSymbol(interval, getPos()));
+    }
+    <COMMA>
+    e = Expression(ExprContext.ACCEPT_SUB_QUERY) { args.add(e); }
+    <COMMA>
+    e = Expression(ExprContext.ACCEPT_SUB_QUERY) { args.add(e); }
+    <RPAREN> {
+        return SqlStdOperatorTable.TIMESTAMP_DIFF.createCall(
+            pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+    }
+}
+
+/**
+ * Parses a call to a named function (could be a builtin with regular
+ * syntax, or else a UDF).
+ *
+ * <p>NOTE: every UDF has two names: an <em>invocation name</em> and a
+ * <em>specific name</em>.  Normally, function calls are resolved via overload
+ * resolution and invocation names.  The SPECIFIC prefix allows overload
+ * resolution to be bypassed.  Note that usage of the SPECIFIC prefix in
+ * queries is non-standard; it is used internally by Farrago, e.g. in stored
+ * view definitions to permanently bind references to a particular function
+ * after the overload resolution performed by view creation.
+ *
+ * <p>TODO jvs 25-Mar-2005:  Once we have SQL-Flagger support, flag SPECIFIC
+ * as non-standard.
+ */
+SqlNode NamedFunctionCall() :
+{
+    SqlIdentifier qualifiedName;
+    SqlIdentifier id = null;
+    List<SqlNode> args;
+    SqlParserPos pos;
+    SqlParserPos starPos;
+    SqlParserPos filterPos = null;
+    SqlNode filter = null;
+    SqlFunctionCategory funcType = SqlFunctionCategory.USER_DEFINED_FUNCTION;
+    SqlNode e = null;
+    SqlLiteral quantifier = null;
+}
+{
+    [
+        <SPECIFIC>
+        {
+            funcType = SqlFunctionCategory.USER_DEFINED_SPECIFIC_FUNCTION;
+        }
+    ]
+    (
+        qualifiedName = FunctionName()
+        {
+            pos = getPos();
+        }
+        (
+            LOOKAHEAD(2) <LPAREN> <STAR> { starPos = getPos(); } <RPAREN>
+            {
+                args = startList(SqlIdentifier.star(starPos));
+                pos = pos.plus(getPos());
+            }
+            | LOOKAHEAD(2) <LPAREN> <RPAREN>
+            {
+                args = Collections.emptyList();
+                pos = pos.plus(getPos());
+            }
+            | args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY)
+            {
+                pos = pos.plus(getPos());
+                quantifier = (SqlLiteral) args.get(0);
+                args.remove(0);
+            }
+        )
+        [
+            <FILTER> { filterPos = getPos(); }
+            <LPAREN>
+            <WHERE>
+            filter = Expression(ExprContext.ACCEPT_SUB_QUERY)
+            <RPAREN>  { filterPos = filterPos.plus(getPos()); }
+        ]
+        [
+            <OVER>
+            (
+                id = SimpleIdentifier()
+            |   e = WindowSpecification()
+            )
+            {
+                pos = pos.plus(getPos());
+            }
+        ]
+        {
+            SqlCall function = createCall(
+                qualifiedName, pos, funcType, quantifier,
+                SqlParserUtil.toNodeArray(args));
+
+            if (filter != null) {
+                function = SqlStdOperatorTable.FILTER.createCall(filterPos,
+                    function, filter);
+            }
+            if (id != null) {
+                return SqlStdOperatorTable.OVER.createCall(pos, function, id);
+            }
+            if (e != null) {
+                return SqlStdOperatorTable.OVER.createCall(pos, function, e);
+            }
+            return function;
+        }
+    )
+}
+
+
+/*
+* Parse Floor/Ceil function parameters
+*/
+SqlNode StandardFloorCeilOptions(SqlParserPos pos, boolean floorFlag) :
+{
+    SqlIdentifier name;
+    SqlParserPos overPos = null;
+    SqlIdentifier id = null;
+    SqlNode e;
+    List<SqlNode> args;
+    TimeUnit unit;
+    boolean over = false;
+}
+{
+    <LPAREN> e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
+        args = startList(e);
+    }
+    (
+        <TO>
+        unit = TimeUnit() {
+            args.add(new SqlIntervalQualifier(unit, null, getPos()));
+        }
+    )?
+    <RPAREN>
+    [
+        <OVER>
+        {
+            overPos = getPos();
+            over = true;
+        }
+        (
+            id = SimpleIdentifier()
+            | e = WindowSpecification()
+        )
+    ]
+    {
+        SqlOperator op = floorFlag
+            ? SqlStdOperatorTable.FLOOR
+            : SqlStdOperatorTable.CEIL;
+        final SqlCall function =  op.createCall(pos.plus(getPos()), args);
+        if (over) {
+            return SqlStdOperatorTable.OVER.createCall(overPos, function,
+                Util.first(id, e));
+        } else {
+            return function;
+        }
+    }
+}
+
+/**
+ * Parses the name of a JDBC function that is a token but is not reserved.
+ */
+String NonReservedJdbcFunctionName() :
+{
+}
+{
+    (
+        <SUBSTRING>
+    )
+    {
+        return unquotedIdentifier();
+    }
+}
+
+/**
+ * Parses the name of a function (either a compound identifier or
+ * a reserved word which can be used as a function name).
+ */
+SqlIdentifier FunctionName() :
+{
+    SqlIdentifier qualifiedName;
+}
+{
+    (
+        qualifiedName = CompoundIdentifier()
+        | qualifiedName = ReservedFunctionName()
+    )
+    {
+        return qualifiedName;
+    }
+}
+
+/**
+ * Parses a reserved word which is used as the name of a function.
+ */
+SqlIdentifier ReservedFunctionName() :
+{
+}
+{
+    (
+        <ABS>
+        | <AVG>
+        | <CARDINALITY>
+        | <CHAR_LENGTH>
+        | <CHARACTER_LENGTH>
+        | <COALESCE>
+        | <COLLECT>
+        | <COVAR_POP>
+        | <COVAR_SAMP>
+        | <CUME_DIST>
+        | <COUNT>
+        | <CURRENT_DATE>
+        | <CURRENT_TIME>
+        | <CURRENT_TIMESTAMP>
+        | <DENSE_RANK>
+        | <ELEMENT>
+        | <EXP>
+        | <FIRST_VALUE>
+        | <FUSION>
+        | <GROUPING>
+        | <LAST_VALUE>
+        | <LN>
+        | <LOCALTIME>
+        | <LOCALTIMESTAMP>
+        | <LOWER>
+        | <MAX>
+        | <MIN>
+        | <MOD>
+        | <NULLIF>
+        | <OCTET_LENGTH>
+        | <PERCENT_RANK>
+        | <POWER>
+        | <RANK>
+        | <REGR_SXX>
+        | <REGR_SYY>
+        | <ROW_NUMBER>
+        | <SQRT>
+        | <STDDEV_POP>
+        | <STDDEV_SAMP>
+        | <SUM>
+        | <UPPER>
+        | <VAR_POP>
+        | <VAR_SAMP>
+    )
+    {
+        return new SqlIdentifier(unquotedIdentifier(), getPos());
+    }
+}
+
+SqlIdentifier ContextVariable() :
+{
+}
+{
+    (
+        <CURRENT_CATALOG>
+        | <CURRENT_DATE>
+        | <CURRENT_DEFAULT_TRANSFORM_GROUP>
+        | <CURRENT_PATH>
+        | <CURRENT_ROLE>
+        | <CURRENT_SCHEMA>
+        | <CURRENT_TIME>
+        | <CURRENT_TIMESTAMP>
+        | <CURRENT_USER>
+        | <LOCALTIME>
+        | <LOCALTIMESTAMP>
+        | <SESSION_USER>
+        | <SYSTEM_USER>
+        | <USER>
+    )
+    {
+        return new SqlIdentifier(unquotedIdentifier(), getPos());
+    }
+}
+
+/**
+ * Parses a function call expression with JDBC syntax.
+ */
+SqlNode JdbcFunctionCall() :
+{
+    String name;
+    SqlIdentifier id;
+    SqlNode e;
+    SqlLiteral tl;
+    SqlNodeList args;
+    SqlCall call;
+    SqlParserPos pos;
+    SqlParserPos starPos;
+}
+{
+    <LBRACE_FN>
+    {
+        pos = getPos();
+    }
+    (
+        LOOKAHEAD(1)
+        call = TimestampAddFunctionCall() {
+            name = call.getOperator().getName();
+            args = new SqlNodeList(call.getOperandList(), getPos());
+        }
+    |
+        call = TimestampDiffFunctionCall() {
+            name = call.getOperator().getName();
+            args = new SqlNodeList(call.getOperandList(), getPos());
+        }
+    |
+        <CONVERT> { name = unquotedIdentifier(); }
+        <LPAREN>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
+            args = new SqlNodeList(getPos());
+            args.add(e);
+        }
+        <COMMA>
+        (
+            tl = JdbcOdbcDataType() { args.add(tl); }
+        )
+        <RPAREN>
+    |
+        (
+            // INSERT is a reserved word, but we need to handle {fn insert}
+            <INSERT> { name = unquotedIdentifier(); }
+        |
+            // For cases like {fn power(1,2)} and {fn lower('a')}
+            id = ReservedFunctionName() { name = id.getSimple(); }
+        |
+            // For cases like {fn substring('foo', 1,2)}
+            name = NonReservedJdbcFunctionName()
+        |
+            name = Identifier()
+        )
+        (
+            LOOKAHEAD(2) <LPAREN> <STAR> {starPos = getPos();} <RPAREN>
+            {
+                args = new SqlNodeList(starPos);
+                args.add(SqlIdentifier.star(starPos));
+            }
+        |
+            LOOKAHEAD(2) <LPAREN> <RPAREN> { args = new SqlNodeList(pos); }
+        |
+            args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY)
+        )
+    )
+    <RBRACE> {
+        return new SqlJdbcFunctionCall(name).createCall(
+            pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
+    }
+}
+
+/**
+ * Parses a binary query operator like UNION.
+ */
+SqlBinaryOperator BinaryQueryOperator() :
+{
+}
+{
+    // If both the ALL or DISTINCT keywords are missing, DISTINCT is implicit.
+    (
+        <UNION>
+        (
+            <ALL> { return SqlStdOperatorTable.UNION_ALL; }
+        |   <DISTINCT> { return SqlStdOperatorTable.UNION; }
+        |   { return SqlStdOperatorTable.UNION; }
+        )
+    |
+        <INTERSECT>
+        (
+            <ALL> { return SqlStdOperatorTable.INTERSECT_ALL; }
+        |   <DISTINCT> { return SqlStdOperatorTable.INTERSECT; }
+        |   { return SqlStdOperatorTable.INTERSECT; }
+        )
+    |
+        (
+            <EXCEPT>
+        |
+            <SET_MINUS> {
+                if (!this.conformance.isMinusAllowed()) {
+                    throw new ParseException(RESOURCE.minusNotAllowed().str());
+                }
+            }
+        )
+        (
+            <ALL> { return SqlStdOperatorTable.EXCEPT_ALL; }
+        |   <DISTINCT> { return SqlStdOperatorTable.EXCEPT; }
+        |   { return SqlStdOperatorTable.EXCEPT; }
+        )
+    )
+}
+
+/**
+ * Parses a binary multiset operator.
+ */
+SqlBinaryOperator BinaryMultisetOperator() :
+{
+    SqlBinaryOperator op;
+}
+{
+    /* If both the ALL or DISTINCT keywords are missing, DISTINCT is implicit */
+    (
+        <MULTISET>
+        (
+            <UNION> { op = SqlStdOperatorTable.MULTISET_UNION; }
+            [
+                <ALL> { op = SqlStdOperatorTable.MULTISET_UNION_ALL; }
+                | <DISTINCT> { op = SqlStdOperatorTable.MULTISET_UNION; }
+            ]
+            |
+            <INTERSECT> { op = SqlStdOperatorTable.MULTISET_INTERSECT; }
+            [
+                <ALL> { op = SqlStdOperatorTable.MULTISET_INTERSECT_ALL; }
+                | <DISTINCT> { op = SqlStdOperatorTable.MULTISET_INTERSECT; }
+            ]
+            |
+            <EXCEPT> { op = SqlStdOperatorTable.MULTISET_EXCEPT; }
+            [
+                <ALL> { op = SqlStdOperatorTable.MULTISET_EXCEPT_ALL; }
+                | <DISTINCT> { op = SqlStdOperatorTable.MULTISET_EXCEPT; }
+            ]
+        )
+    )
+    { return op; }
+}
+
+/**
+ * Parses a binary row operator like AND.
+ */
+SqlBinaryOperator BinaryRowOperator() :
+{
+    SqlBinaryOperator op;
+}
+{
+    <EQ>
+    { return SqlStdOperatorTable.EQUALS; }
+    | <GT>
+    { return SqlStdOperatorTable.GREATER_THAN; }
+    | <LT>
+    { return SqlStdOperatorTable.LESS_THAN; }
+    | <LE>
+    { return SqlStdOperatorTable.LESS_THAN_OR_EQUAL; }
+    | <GE>
+    { return SqlStdOperatorTable.GREATER_THAN_OR_EQUAL; }
+    | <NE>
+    { return SqlStdOperatorTable.NOT_EQUALS; }
+    | <NE2>
+    {
+        if (!this.conformance.isBangEqualAllowed()) {
+            throw new ParseException(RESOURCE.bangEqualNotAllowed().str());
+        }
+        return SqlStdOperatorTable.NOT_EQUALS;
+    }
+    | <PLUS>
+    { return SqlStdOperatorTable.PLUS; }
+    | <MINUS>
+    { return SqlStdOperatorTable.MINUS; }
+    | <STAR>
+    { return SqlStdOperatorTable.MULTIPLY; }
+    | <SLASH>
+    { return SqlStdOperatorTable.DIVIDE; }
+    | <CONCAT>
+    { return SqlStdOperatorTable.CONCAT; }
+    | <AND>
+    { return SqlStdOperatorTable.AND; }
+    | <OR>
+    { return SqlStdOperatorTable.OR; }
+    | LOOKAHEAD(2) <IS> <DISTINCT> <FROM>
+    { return SqlStdOperatorTable.IS_DISTINCT_FROM; }
+    | <IS> <NOT> <DISTINCT> <FROM>
+    { return SqlStdOperatorTable.IS_NOT_DISTINCT_FROM; }
+    // <IN> is handled as a special case
+    | <MEMBER> <OF>
+    { return SqlStdOperatorTable.MEMBER_OF; }
+    | <SUBMULTISET> <OF>
+    { return SqlStdOperatorTable.SUBMULTISET_OF; }
+    | op = BinaryMultisetOperator()
+    { return op; }
+}
+
+/**
+ * Parses a prefix row operator like NOT.
+ */
+SqlPrefixOperator PrefixRowOperator() :
+{}
+{
+    <PLUS> { return SqlStdOperatorTable.UNARY_PLUS; }
+    | <MINUS> { return SqlStdOperatorTable.UNARY_MINUS; }
+    | <NOT> { return SqlStdOperatorTable.NOT; }
+    | <EXISTS> { return SqlStdOperatorTable.EXISTS; }
+}
+
+/**
+ * Parses a postfix row operator like IS NOT NULL.
+ */
+SqlPostfixOperator PostfixRowOperator() :
+{}
+{
+    <IS>
+    (
+        <A> <SET> { return SqlStdOperatorTable.IS_A_SET; }
+        |
+        <NOT>
+        (
+            <NULL> { return SqlStdOperatorTable.IS_NOT_NULL; }
+            | <TRUE> { return SqlStdOperatorTable.IS_NOT_TRUE; }
+            | <FALSE> { return SqlStdOperatorTable.IS_NOT_FALSE; }
+            | <UNKNOWN> { return SqlStdOperatorTable.IS_NOT_UNKNOWN; }
+        )
+        |
+        (
+            <NULL> { return SqlStdOperatorTable.IS_NULL; }
+            | <TRUE> { return SqlStdOperatorTable.IS_TRUE; }
+            | <FALSE> { return SqlStdOperatorTable.IS_FALSE; }
+            | <UNKNOWN> { return SqlStdOperatorTable.IS_UNKNOWN; }
+        )
+    )
+}
+
+
+/* KEYWORDS:  anything in this list is a reserved word unless it appears
+   in the NonReservedKeyWord() production. */
+
+<DEFAULT, DQID, BTID> TOKEN :
+{
+    < A: "A" >
+    | < ABS: "ABS" >
+    | < ABSOLUTE: "ABSOLUTE" >
+    | < ACTION: "ACTION" >
+    | < ADA: "ADA" >
+    | < ADD: "ADD" >
+    | < ADMIN: "ADMIN" >
+    | < AFTER: "AFTER" >
+    | < ALL: "ALL" >
+    | < ALLOCATE: "ALLOCATE" >
+    | < ALLOW: "ALLOW" >
+    | < ALWAYS: "ALWAYS" >
+    | < ALTER: "ALTER" >
+    | < AND: "AND" >
+    | < ANY: "ANY" >
+    | < ARE: "ARE" >
+    | < ARRAY: "ARRAY" >
+    | < AS: "AS" >
+    | < ASC: "ASC" >
+    | < ASENSITIVE: "ASENSITIVE" >
+    | < ASSERTION: "ASSERTION" >
+    | < ASSIGNMENT: "ASSIGNMENT" >
+    | < ASYMMETRIC: "ASYMMETRIC" >
+    | < AT: "AT" >
+    | < ATOMIC: "ATOMIC" >
+    | < ATTRIBUTE: "ATTRIBUTE" >
+    | < ATTRIBUTES: "ATTRIBUTES" >
+    | < AUTHORIZATION: "AUTHORIZATION" >
+    | < AVG: "AVG" >
+    | < BEFORE: "BEFORE" >
+    | < BEGIN: "BEGIN" >
+    | < BERNOULLI: "BERNOULLI" >
+    | < BETWEEN: "BETWEEN" >
+    | < BIGINT: "BIGINT" >
+    | < BINARY: "BINARY" >
+    | < BIT: "BIT" >
+    | < BLOB: "BLOB" >
+    | < BOOLEAN: "BOOLEAN" >
+    | < BOTH: "BOTH" >
+    | < BREADTH: "BREADTH" >
+    | < BY: "BY" >
+    | < C: "C" >
+    | < CALL: "CALL" >
+    | < CALLED: "CALLED" >
+    | < CARDINALITY: "CARDINALITY" >
+    | < CASCADE: "CASCADE" >
+    | < CASCADED: "CASCADED" >
+    | < CASE: "CASE" >
+    | < CAST: "CAST" >
+    | < CATALOG: "CATALOG" >
+    | < CATALOG_NAME: "CATALOG_NAME" >
+    | < CEIL: "CEIL" >
+    | < CEILING: "CEILING" >
+    | < CENTURY: "CENTURY" >
+    | < CHAIN: "CHAIN" >
+    | < CHAR: "CHAR" >
+    | < CHAR_LENGTH: "CHAR_LENGTH" >
+    | < CHARACTER: "CHARACTER" >
+    | < CHARACTER_LENGTH: "CHARACTER_LENGTH" >
+    | < CHARACTER_SET_CATALOG: "CHARACTER_SET_CATALOG" >
+    | < CHARACTER_SET_NAME: "CHARACTER_SET_NAME" >
+    | < CHARACTER_SET_SCHEMA: "CHARACTER_SET_SCHEMA" >
+    | < CHARACTERISTICS: "CHARACTERISTICS" >
+    | < CHARACTERS: "CHARACTERS" >
+    | < CHECK: "CHECK" >
+    | < CLASS_ORIGIN: "CLASS_ORIGIN" >
+    | < CLOB: "CLOB" >
+    | < CLOSE: "CLOSE" >
+    | < COALESCE: "COALESCE" >
+    | < COBOL: "COBOL" >
+    | < COLLATE: "COLLATE" >
+    | < COLLATION: "COLLATION" >
+    | < COLLATION_CATALOG: "COLLATION_CATALOG" >
+    | < COLLATION_NAME: "COLLATION_NAME" >
+    | < COLLATION_SCHEMA: "COLLATION_SCHEMA" >
+    | < COLLECT: "COLLECT" >
+    | < COLUMN: "COLUMN" >
+    | < COLUMN_NAME: "COLUMN_NAME" >
+    | < COMMAND_FUNCTION: "COMMAND_FUNCTION" >
+    | < COMMAND_FUNCTION_CODE: "COMMAND_FUNCTION_CODE" >
+    | < COMMIT: "COMMIT" >
+    | < COMMITTED: "COMMITTED" >
+    | < CONDITION: "CONDITION" >
+    | < CONDITION_NUMBER: "CONDITION_NUMBER" >
+    | < CONNECT: "CONNECT" >
+    | < CONNECTION: "CONNECTION" >
+    | < CONNECTION_NAME: "CONNECTION_NAME" >
+    | < CONSTRAINT: "CONSTRAINT" >
+    | < CONSTRAINT_CATALOG: "CONSTRAINT_CATALOG" >
+    | < CONSTRAINT_NAME: "CONSTRAINT_NAME" >
+    | < CONSTRAINT_SCHEMA: "CONSTRAINT_SCHEMA" >
+    | < CONSTRAINTS: "CONSTRAINTS" >
+    | < CONSTRUCTOR: "CONSTRUCTOR" >
+    | < CONTAINS: "CONTAINS" >
+    | < CONVERT: "CONVERT" >
+    | < CONTINUE: "CONTINUE" >
+    | < CORR: "CORR" >
+    | < CORRESPONDING: "CORRESPONDING" >
+    | < COUNT: "COUNT" >
+    | < COVAR_POP: "COVAR_POP" >
+    | < COVAR_SAMP: "COVAR_SAMP" >
+    | < CREATE: "CREATE" >
+    | < CROSS: "CROSS" >
+    | < CUBE: "CUBE" >
+    | < CUME_DIST: "CUME_DIST" >
+    | < CURRENT: "CURRENT" >
+    | < CURRENT_CATALOG: "CURRENT_CATALOG" >
+    | < CURRENT_DATE: "CURRENT_DATE" >
+    | < CURRENT_DEFAULT_TRANSFORM_GROUP: "CURRENT_DEFAULT_TRANSFORM_GROUP" >
+    | < CURRENT_PATH: "CURRENT_PATH" >
+    | < CURRENT_ROLE: "CURRENT_ROLE" >
+    | < CURRENT_SCHEMA: "CURRENT_SCHEMA" >
+    | < CURRENT_TIME: "CURRENT_TIME" >
+    | < CURRENT_TIMESTAMP: "CURRENT_TIMESTAMP" >
+    | < CURRENT_TRANSFORM_GROUP_FOR_TYPE: "CURRENT_TRANSFORM_GROUP_FOR_TYPE" >
+    | < CURRENT_USER: "CURRENT_USER" >
+    | < CURSOR: "CURSOR" >
+    | < CURSOR_NAME: "CURSOR_NAME" >
+    | < CYCLE: "CYCLE" >
+    | < DATA: "DATA" >
+    | < DATABASE: "DATABASE" >
+    | < DATE: "DATE" >
+    | < DATETIME_INTERVAL_CODE: "DATETIME_INTERVAL_CODE" >
+    | < DATETIME_INTERVAL_PRECISION: "DATETIME_INTERVAL_PRECISION" >
+    | < DAY: "DAY" >
+    | < DEALLOCATE: "DEALLOCATE" >
+    | < DEC: "DEC" >
+    | < DECADE: "DECADE" >
+    | < DECIMAL: "DECIMAL" >
+    | < DECLARE: "DECLARE" >
+    | < DEFAULT_KW: "DEFAULT" >
+    | < DEFAULTS: "DEFAULTS" >
+    | < DEFERRABLE: "DEFERRABLE" >
+    | < DEFERRED: "DEFERRED" >
+    | < DEFINED: "DEFINED" >
+    | < DEFINER: "DEFINER" >
+    | < DEGREE: "DEGREE" >
+    | < DELETE: "DELETE" >
+    | < DENSE_RANK: "DENSE_RANK" >
+    | < DEPTH: "DEPTH" >
+    | < DEREF: "DEREF" >
+    | < DERIVED: "DERIVED" >
+    | < DESCRIBE: "DESCRIBE" >
+    | < DESC: "DESC" >
+    | < DESCRIPTION: "DESCRIPTION" >
+    | < DESCRIPTOR: "DESCRIPTOR" >
+    | < DETERMINISTIC: "DETERMINISTIC" >
+    | < DIAGNOSTICS: "DIAGNOSTICS" >
+    | < DISALLOW: "DISALLOW" >
+    | < DISCONNECT: "DISCONNECT" >
+    | < DISPATCH: "DISPATCH" >
+    | < DISTINCT: "DISTINCT" >
+    | < DOMAIN: "DOMAIN" >
+    | < DOUBLE: "DOUBLE" >
+    | < DOW: "DOW" >
+    | < DOY: "DOY" >
+    | < DROP: "DROP" >
+    | < DYNAMIC: "DYNAMIC" >
+    | < DYNAMIC_FUNCTION: "DYNAMIC_FUNCTION" >
+    | < DYNAMIC_FUNCTION_CODE: "DYNAMIC_FUNCTION_CODE" >
+    | < EACH: "EACH" >
+    | < ELEMENT: "ELEMENT" >
+    | < ELSE: "ELSE" >
+    | < END: "END" >
+    | < END_EXEC: "END-EXEC" >
+    | < EPOCH: "EPOCH" >
+    | < EQUALS: "EQUALS" >
+    | < ESCAPE: "ESCAPE" >
+    | < EVERY: "EVERY" >
+    | < EXCEPT: "EXCEPT" >
+    | < EXCEPTION: "EXCEPTION" >
+    | < EXCLUDE: "EXCLUDE" >
+    | < EXCLUDING: "EXCLUDING" >
+    | < EXEC: "EXEC" >
+    | < EXECUTE: "EXECUTE" >
+    | < EXISTS: "EXISTS" >
+    | < EXP: "EXP" >
+    | < EXPLAIN: "EXPLAIN" >
+    | < EXTERNAL: "EXTERNAL" >
+    | < EXTEND: "EXTEND" >
+    | < EXTRACT: "EXTRACT" >
+    | < FALSE: "FALSE" >
+    | < FETCH: "FETCH" >
+    | < FILTER: "FILTER" >
+    | < FINAL: "FINAL" >
+    | < FIRST: "FIRST" >
+    | < FIRST_VALUE: "FIRST_VALUE">
+    | < FLOAT: "FLOAT" >
+    | < FLOOR: "FLOOR" >
+    | < FOLLOWING: "FOLLOWING" >
+    | < FOR: "FOR" >
+    | < FOREIGN: "FOREIGN" >
+    | < FORTRAN: "FORTRAN" >
+    | < FOUND: "FOUND" >
+    | < FRAC_SECOND: "FRAC_SECOND" >
+    | < FREE: "FREE" >
+    | < FROM: "FROM" >
+    | < FULL: "FULL" >
+    | < FUNCTION: "FUNCTION" >
+    | < FUSION: "FUSION" >
+    | < G: "G" >
+    | < GENERAL: "GENERAL" >
+    | < GENERATED: "GENERATED" >
+    | < GET: "GET" >
+    | < GLOBAL: "GLOBAL" >
+    | < GO: "GO" >
+    | < GOTO: "GOTO" >
+    | < GRANT: "GRANT" >
+    | < GRANTED: "GRANTED" >
+    | < GROUP: "GROUP" >
+    | < GROUPING: "GROUPING" >
+    | < HAVING: "HAVING" >
+    | < HIERARCHY: "HIERARCHY" >
+    | < HOLD: "HOLD" >
+    | < HOUR: "HOUR" >
+    | < IDENTITY: "IDENTITY" >
+    | < IMMEDIATE: "IMMEDIATE" >
+    | < IMPLEMENTATION: "IMPLEMENTATION" >
+    | < IMPORT: "IMPORT" >
+    | < IN: "IN" >
+    | < INDICATOR: "INDICATOR" >
+    | < INCLUDING: "INCLUDING" >
+    | < INCREMENT: "INCREMENT" >
+    | < INITIALLY: "INITIALLY" >
+    | < INNER: "INNER" >
+    | < INOUT: "INOUT" >
+    | < INPUT: "INPUT" >
+    | < INSENSITIVE: "INSENSITIVE" >
+    | < INSERT: "INSERT" >
+    | < INSTANCE: "INSTANCE" >
+    | < INSTANTIABLE: "INSTANTIABLE" >
+    | < INT: "INT" >
+    | < INTEGER: "INTEGER" >
+    | < INTERSECT: "INTERSECT" >
+    | < INTERSECTION: "INTERSECTION" >
+    | < INTERVAL: "INTERVAL" >
+    | < INTO: "INTO" >
+    | < INVOKER: "INVOKER" >
+    | < IS: "IS" >
+    | < ISOLATION: "ISOLATION" >
+    | < JAVA: "JAVA" >
+    | < JOIN: "JOIN" >
+    | < JSON: "JSON" >
+    | < K: "K" >
+    | < KEY: "KEY" >
+    | < KEY_MEMBER: "KEY_MEMBER" >
+    | < KEY_TYPE: "KEY_TYPE" >
+    | < LABEL: "LABEL" >
+    | < LANGUAGE: "LANGUAGE" >
+    | < LARGE: "LARGE" >
+    | < LAST: "LAST" >
+    | < LAST_VALUE: "LAST_VALUE" >
+    | < LATERAL: "LATERAL" >
+    | < LEADING: "LEADING" >
+    | < LEFT: "LEFT" >
+    | < LENGTH: "LENGTH" >
+    | < LEVEL: "LEVEL" >
+    | < LIBRARY: "LIBRARY" >
+    | < LIKE: "LIKE" >
+    | < LIMIT: "LIMIT" >
+    | < LN: "LN" >
+    | < LOCAL: "LOCAL" >
+    | < LOCALTIME: "LOCALTIME" >
+    | < LOCALTIMESTAMP: "LOCALTIMESTAMP" >
+    | < LOCATOR: "LOCATOR" >
+    | < LOWER: "LOWER" >
+    | < M: "M" >
+    | < MAP: "MAP" >
+    | < MATCH: "MATCH" >
+    | < MATCHED: "MATCHED" >
+    | < MAX: "MAX" >
+    | < MAXVALUE: "MAXVALUE" >
+    | < MEMBER: "MEMBER" >
+    | < MERGE: "MERGE" >
+    | < MESSAGE_LENGTH: "MESSAGE_LENGTH" >
+    | < MESSAGE_OCTET_LENGTH: "MESSAGE_OCTET_LENGTH" >
+    | < MESSAGE_TEXT: "MESSAGE_TEXT" >
+    | < METHOD: "METHOD" >
+    | < MICROSECOND: "MICROSECOND" >
+    | < MILLENNIUM: "MILLENNIUM" >
+    | < MIN: "MIN" >
+    | < MINUTE: "MINUTE" >
+    | < MINVALUE: "MINVALUE" >
+    | < MOD: "MOD" >
+    | < MODIFIES: "MODIFIES" >
+    | < MODULE: "MODULE" >
+    | < MONTH: "MONTH" >
+    | < MORE_KW: "MORE" >
+    | < MULTISET: "MULTISET" >
+    | < MUMPS: "MUMPS" >
+    | < NAME: "NAME" >
+    | < NAMES: "NAMES" >
+    | < NATIONAL: "NATIONAL" >
+    | < NATURAL: "NATURAL" >
+    | < NCHAR: "NCHAR" >
+    | < NCLOB: "NCLOB" >
+    | < NESTING: "NESTING" >
+    | < NEW: "NEW" >
+    | < NEXT: "NEXT" >
+    | < NO: "NO" >
+    | < NONE: "NONE" >
+    | < NORMALIZE: "NORMALIZE" >
+    | < NORMALIZED: "NORMALIZED" >
+    | < NOT: "NOT" >
+    | < NULL: "NULL" >
+    | < NULLABLE: "NULLABLE" >
+    | < NULLIF: "NULLIF" >
+    | < NULLS: "NULLS" >
+    | < NUMBER: "NUMBER" >
+    | < NUMERIC: "NUMERIC" >
+    | < OBJECT: "OBJECT" >
+    | < OCTET_LENGTH: "OCTET_LENGTH" >
+    | < OCTETS: "OCTETS" >
+    | < OF: "OF" >
+    | < OFFSET: "OFFSET" >
+    | < OLD: "OLD" >
+    | < ON: "ON" >
+    | < ONLY: "ONLY" >
+    | < OPEN: "OPEN" >
+    | < OPTION: "OPTION" >
+    | < OPTIONS: "OPTIONS" >
+    | < OR: "OR" >
+    | < ORDER: "ORDER" >
+    | < ORDERING: "ORDERING" >
+    | < ORDINALITY: "ORDINALITY" >
+    | < OTHERS: "OTHERS" >
+    | < OUT: "OUT" >
+    | < OUTER: "OUTER" >
+    | < OUTPUT: "OUTPUT" >
+    | < OVER: "OVER" >
+    | < OVERLAPS: "OVERLAPS" >
+    | < OVERLAY: "OVERLAY" >
+    | < OVERRIDING: "OVERRIDING" >
+    | < PAD: "PAD" >
+    | < PARAMETER: "PARAMETER" >
+    | < PARAMETER_MODE: "PARAMETER_MODE" >
+    | < PARAMETER_NAME: "PARAMETER_NAME" >
+    | < PARAMETER_ORDINAL_POSITION: "PARAMETER_ORDINAL_POSITION" >
+    | < PARAMETER_SPECIFIC_CATALOG: "PARAMETER_SPECIFIC_CATALOG" >
+    | < PARAMETER_SPECIFIC_NAME: "PARAMETER_SPECIFIC_NAME" >
+    | < PARAMETER_SPECIFIC_SCHEMA: "PARAMETER_SPECIFIC_SCHEMA" >
+    | < PARTIAL: "PARTIAL" >
+    | < PARTITION: "PARTITION" >
+    | < PASCAL: "PASCAL" >
+    | < PASSTHROUGH: "PASSTHROUGH" >
+    | < PATH: "PATH" >
+    | < PERCENT_RANK: "PERCENT_RANK" >
+    | < PERCENTILE_CONT: "PERCENTILE_CONT" >
+    | < PERCENTILE_DISC: "PERCENTILE_DISC" >
+    | < PLACING: "PLACING" >
+    | < PLAN: "PLAN" >
+    | < PLI: "PLI" >
+    | < POSITION: "POSITION" >
+    | < POWER: "POWER" >
+    | < PRECEDING: "PRECEDING" >
+    | < PRECISION: "PRECISION" >
+    | < PREPARE: "PREPARE" >
+    | < PRESERVE: "PRESERVE" >
+    | < PRIMARY: "PRIMARY" >
+    | < PRIOR: "PRIOR" >
+    | < PRIVILEGES: "PRIVILEGES" >
+    | < PROCEDURE: "PROCEDURE" >
+    | < PUBLIC: "PUBLIC" >
+    | < QUARTER: "QUARTER" >
+    | < RANGE: "RANGE" >
+    | < RANK: "RANK" >
+    | < READ: "READ" >
+    | < READS: "READS" >
+    | < REAL: "REAL" >
+    | < RECURSIVE: "RECURSIVE" >
+    | < REF: "REF" >
+    | < REFERENCES: "REFERENCES" >
+    | < REFERENCING: "REFERENCING" >
+    | < REGR_AVGX: "REGR_AVGX" >
+    | < REGR_AVGY: "REGR_AVGY" >
+    | < REGR_COUNT: "REGR_COUNT" >
+    | < REGR_INTERCEPT: "REGR_INTERCEPT" >
+    | < REGR_R2: "REGR_R2" >
+    | < REGR_SLOPE: "REGR_SLOPE" >
+    | < REGR_SXX: "REGR_SXX" >
+    | < REGR_SXY: "REGR_SXY" >
+    | < REGR_SYY: "REGR_SYY" >
+    | < RELATIVE: "RELATIVE" >
+    | < RELEASE: "RELEASE" >
+    | < REPEATABLE: "REPEATABLE" >
+    | < REPLACE: "REPLACE" >
+    | < RESET: "RESET" >
+    | < RESTART: "RESTART" >
+    | < RESTRICT: "RESTRICT" >
+    | < RESULT: "RESULT" >
+    | < RETURN: "RETURN" >
+    | < RETURNED_CARDINALITY: "RETURNED_CARDINALITY" >
+    | < RETURNED_LENGTH: "RETURNED_LENGTH" >
+    | < RETURNED_OCTET_LENGTH: "RETURNED_OCTET_LENGTH" >
+    | < RETURNED_SQLSTATE: "RETURNED_SQLSTATE" >
+    | < RETURNS: "RETURNS" >
+    | < REVOKE: "REVOKE" >
+    | < RIGHT: "RIGHT" >
+    | < ROLE: "ROLE" >
+    | < ROLLBACK: "ROLLBACK" >
+    | < ROLLUP: "ROLLUP" >
+    | < ROUTINE: "ROUTINE" >
+    | < ROUTINE_CATALOG: "ROUTINE_CATALOG" >
+    | < ROUTINE_NAME: "ROUTINE_NAME" >
+    | < ROUTINE_SCHEMA: "ROUTINE_SCHEMA" >
+    | < ROW: "ROW" >
+    | < ROW_COUNT: "ROW_COUNT" >
+    | < ROW_NUMBER: "ROW_NUMBER" >
+    | < ROWS: "ROWS" >
+    | < SAVEPOINT: "SAVEPOINT" >
+    | < SCALE: "SCALE" >
+    | < SCHEMA: "SCHEMA" >
+    | < SCHEMA_NAME: "SCHEMA_NAME" >
+    | < SCOPE: "SCOPE" >
+    | < SCOPE_CATALOGS: "SCOPE_CATALOGS" >
+    | < SCOPE_NAME: "SCOPE_NAME" >
+    | < SCOPE_SCHEMA: "SCOPE_SCHEMA" >
+    | < SCROLL: "SCROLL" >
+    | < SEARCH: "SEARCH" >
+    | < SECOND: "SECOND" >
+    | < SECTION: "SECTION" >
+    | < SECURITY: "SECURITY" >
+    | < SELECT: "SELECT" >
+    | < SELF: "SELF" >
+    | < SENSITIVE: "SENSITIVE" >
+    | < SEQUENCE: "SEQUENCE" >
+    | < SERIALIZABLE: "SERIALIZABLE" >
+    | < SERVER: "SERVER" >
+    | < SERVER_NAME: "SERVER_NAME" >
+    | < SESSION: "SESSION" >
+    | < SESSION_USER: "SESSION_USER" >
+    | < SET: "SET" >
+    | < SETS: "SETS" >
+    | < SET_MINUS: "MINUS">
+    | < SIMILAR: "SIMILAR" >
+    | < SIMPLE: "SIMPLE" >
+    | < SIZE: "SIZE" >
+    | < SMALLINT: "SMALLINT" >
+    | < SOME: "SOME" >
+    | < SOURCE: "SOURCE" >
+    | < SPACE: "SPACE" >
+    | < SPECIFIC: "SPECIFIC" >
+    | < SPECIFIC_NAME: "SPECIFIC_NAME" >
+    | < SPECIFICTYPE: "SPECIFICTYPE" >
+    | < SQL: "SQL" >
+    | < SQLEXCEPTION: "SQLEXCEPTION" >
+    | < SQLSTATE: "SQLSTATE" >
+    | < SQLWARNING: "SQLWARNING" >
+    | < SQL_BIGINT: "SQL_BIGINT" >
+    | < SQL_BINARY: "SQL_BINARY" >
+    | < SQL_BIT: "SQL_BIT" >
+    | < SQL_BLOB: "SQL_BLOB" >
+    | < SQL_BOOLEAN: "SQL_BOOLEAN" >
+    | < SQL_CHAR: "SQL_CHAR" >
+    | < SQL_CLOB: "SQL_CLOB" >
+    | < SQL_DATE: "SQL_DATE" >
+    | < SQL_DECIMAL: "SQL_DECIMAL" >
+    | < SQL_DOUBLE: "SQL_DOUBLE" >
+    | < SQL_FLOAT: "SQL_FLOAT" >
+    | < SQL_INTEGER: "SQL_INTEGER" >
+    | < SQL_INTERVAL_DAY: "SQL_INTERVAL_DAY" >
+    | < SQL_INTERVAL_DAY_TO_HOUR: "SQL_INTERVAL_DAY_TO_HOUR" >
+    | < SQL_INTERVAL_DAY_TO_MINUTE: "SQL_INTERVAL_DAY_TO_MINUTE" >
+    | < SQL_INTERVAL_DAY_TO_SECOND: "SQL_INTERVAL_DAY_TO_SECOND" >
+    | < SQL_INTERVAL_HOUR: "SQL_INTERVAL_HOUR" >
+    | < SQL_INTERVAL_HOUR_TO_MINUTE: "SQL_INTERVAL_HOUR_TO_MINUTE" >
+    | < SQL_INTERVAL_HOUR_TO_SECOND: "SQL_INTERVAL_HOUR_TO_SECOND" >
+    | < SQL_INTERVAL_MINUTE: "SQL_INTERVAL_MINUTE" >
+    | < SQL_INTERVAL_MINUTE_TO_SECOND: "SQL_INTERVAL_MINUTE_TO_SECOND" >
+    | < SQL_INTERVAL_MONTH: "SQL_INTERVAL_MONTH" >
+    | < SQL_INTERVAL_SECOND: "SQL_INTERVAL_SECOND" >
+    | < SQL_INTERVAL_YEAR: "SQL_INTERVAL_YEAR" >
+    | < SQL_INTERVAL_YEAR_TO_MONTH: "SQL_INTERVAL_YEAR_TO_MONTH" >
+    | < SQL_LONGVARBINARY: "SQL_LONGVARBINARY" >
+    | < SQL_LONGVARCHAR: "SQL_LONGVARCHAR" >
+    | < SQL_LONGVARNCHAR: "SQL_LONGVARNCHAR" >
+    | < SQL_NCHAR: "SQL_NCHAR" >
+    | < SQL_NCLOB: "SQL_NCLOB" >
+    | < SQL_NUMERIC: "SQL_NUMERIC" >
+    | < SQL_NVARCHAR: "SQL_NVARCHAR" >
+    | < SQL_REAL: "SQL_REAL" >
+    | < SQL_SMALLINT: "SQL_SMALLINT" >
+    | < SQL_TIME: "SQL_TIME" >
+    | < SQL_TIMESTAMP: "SQL_TIMESTAMP" >
+    | < SQL_TINYINT: "SQL_TINYINT" >
+    | < SQL_TSI_DAY: "SQL_TSI_DAY" >
+    | < SQL_TSI_FRAC_SECOND: "SQL_TSI_FRAC_SECOND" >
+    | < SQL_TSI_HOUR: "SQL_TSI_HOUR" >
+    | < SQL_TSI_MICROSECOND: "SQL_TSI_MICROSECOND" >
+    | < SQL_TSI_MINUTE: "SQL_TSI_MINUTE" >
+    | < SQL_TSI_MONTH: "SQL_TSI_MONTH" >
+    | < SQL_TSI_QUARTER: "SQL_TSI_QUARTER" >
+    | < SQL_TSI_SECOND: "SQL_TSI_SECOND" >
+    | < SQL_TSI_WEEK: "SQL_TSI_WEEK" >
+    | < SQL_TSI_YEAR: "SQL_TSI_YEAR" >
+    | < SQL_VARBINARY: "SQL_VARBINARY" >
+    | < SQL_VARCHAR: "SQL_VARCHAR" >
+    | < SQRT: "SQRT" >
+    | < START: "START" >
+    | < STATE: "STATE" >
+    | < STATEMENT: "STATEMENT" >
+    | < STATIC: "STATIC" >
+    | < STDDEV_POP: "STDDEV_POP" >
+    | < STDDEV_SAMP: "STDDEV_SAMP" >
+    | < STREAM: "STREAM" >
+    | < STRUCTURE: "STRUCTURE" >
+    | < STYLE: "STYLE" >
+    | < SUBCLASS_ORIGIN: "SUBCLASS_ORIGIN" >
+    | < SUBMULTISET: "SUBMULTISET" >
+    | < SUBSTRING: "SUBSTRING" >
+    | < SUBSTITUTE: "SUBSTITUTE" >
+    | < SUM: "SUM" >
+    | < SYMMETRIC: "SYMMETRIC" >
+    | < SYSTEM: "SYSTEM" >
+    | < SYSTEM_USER: "SYSTEM_USER" >
+    | < TABLE: "TABLE" >
+    | < TABLE_NAME: "TABLE_NAME" >
+    | < TABLESAMPLE: "TABLESAMPLE" >
+    | < TEMPORARY: "TEMPORARY" >
+    | < THEN: "THEN" >
+    | < TIES: "TIES" >
+    | < TIME: "TIME" >
+    | < TIMESTAMP: "TIMESTAMP" >
+    | < TIMESTAMPADD: "TIMESTAMPADD" >
+    | < TIMESTAMPDIFF: "TIMESTAMPDIFF" >
+    | < TIMEZONE_HOUR: "TIMEZONE_HOUR" >
+    | < TIMEZONE_MINUTE: "TIMEZONE_MINUTE" >
+    | < TINYINT: "TINYINT" >
+    | < TO: "TO" >
+    | < TOP_LEVEL_COUNT: "TOP_LEVEL_COUNT" >
+    | < TRAILING: "TRAILING" >
+    | < TRANSACTION: "TRANSACTION" >
+    | < TRANSACTIONS_ACTIVE: "TRANSACTIONS_ACTIVE" >
+    | < TRANSACTIONS_COMMITTED: "TRANSACTIONS_COMMITTED" >
+    | < TRANSACTIONS_ROLLED_BACK: "TRANSACTIONS_ROLLED_BACK" >
+    | < TRANSFORM: "TRANSFORM" >
+    | < TRANSFORMS: "TRANSFORMS" >
+    | < TRANSLATE: "TRANSLATE" >
+    | < TRANSLATION: "TRANSLATION" >
+    | < TREAT: "TREAT" >
+    | < TRIGGER: "TRIGGER" >
+    | < TRIGGER_CATALOG: "TRIGGER_CATALOG" >
+    | < TRIGGER_NAME: "TRIGGER_NAME" >
+    | < TRIGGER_SCHEMA: "TRIGGER_SCHEMA" >
+    | < TRIM: "TRIM" >
+    | < TRUE: "TRUE" >
+    | < TYPE: "TYPE" >
+    | < UESCAPE: "UESCAPE" >
+    | < UNBOUNDED: "UNBOUNDED" >
+    | < UNCOMMITTED: "UNCOMMITTED" >
+    | < UNDER: "UNDER" >
+    | < UNION: "UNION" >
+    | < UNIQUE: "UNIQUE" >
+    | < UNKNOWN: "UNKNOWN" >
+    | < UNNAMED: "UNNAMED" >
+    | < UNNEST: "UNNEST" >
+    | < UPDATE: "UPDATE" >
+    | < UPPER: "UPPER" >
+    | < UPSERT: "UPSERT" >
+    | < USAGE: "USAGE" >
+    | < USER: "USER" >
+    | < USER_DEFINED_TYPE_CATALOG: "USER_DEFINED_TYPE_CATALOG" >
+    | < USER_DEFINED_TYPE_CODE: "USER_DEFINED_TYPE_CODE" >
+    | < USER_DEFINED_TYPE_NAME: "USER_DEFINED_TYPE_NAME" >
+    | < USER_DEFINED_TYPE_SCHEMA: "USER_DEFINED_TYPE_SCHEMA" >
+    | < USING: "USING" >
+    | < VALUE: "VALUE" >
+    | < VALUES: "VALUES" >
+    | < VAR_POP: "VAR_POP" >
+    | < VAR_SAMP: "VAR_SAMP" >
+    | < VARCHAR: "VARCHAR" >
+    | < VARBINARY: "VARBINARY" >
+    | < VARYING: "VARYING" >
+    | < VERSION: "VERSION" >
+    | < VIEW: "VIEW" >
+    | < WEEK: "WEEK" >
+    | < WHEN: "WHEN" >
+    | < WHENEVER: "WHENEVER" >
+    | < WHERE: "WHERE" >
+    | < WIDTH_BUCKET: "WIDTH_BUCKET" >
+    | < WINDOW: "WINDOW" >
+    | < WITH: "WITH" >
+    | < WITHIN: "WITHIN" >
+    | < WITHOUT: "WITHOUT" >
+    | < WORK: "WORK" >
+    | < WRAPPER: "WRAPPER" >
+    | < WRITE: "WRITE" >
+    | < XML: "XML" >
+    | < YEAR: "YEAR" >
+    | < ZONE: "ZONE" >
+    | < APPLY: "APPLY" >
+    | < LOCATION: "LOCATION" >
+    | < INPUTFORMAT: "INPUTFORMAT" >
+    | < OUTPUTFORMAT: "OUTPUTFORMAT" >
+    | < PARALLELISM: "PARALLELISM" >
+    | < STORED: "STORED" >
+    | < TBLPROPERTIES: "TBLPROPERTIES" >
+    | < JAR: "JAR" >
+}
+
+/*
+ * Abstract production:
+ *
+ *   String NonReservedKeyWord()
+ *
+ * Parses non-reserved keywords (e.g. keywords that may be used as
+ * identifiers).  Should use CommonNonReservedKeyWord as a base, but
+ * may add other key words.
+ */
+
+/**
+ * Parses a non-reserved keyword for use as an identifier.  Specializations
+ * of this parser can use this as a base for implementing the
+ * NonReservedKeyWord() production.
+ *
+ * <p>When adding keywords to this list, be sure that they are not reserved
+ * by the SQL:2003 standard (see productions for "non-reserved word"
+ * and "reserved word" in reference below).
+ *
+ * @sql.2003 Part 2 Section 5.2
+ */
+String CommonNonReservedKeyWord() :
+{
+}
+{
+    (
+        <A>
+        | <ABSOLUTE>
+        | <ACTION>
+        | <ADA>
+        | <ADD>
+        | <ADMIN>
+        | <AFTER>
+        | <ALWAYS>
+        | <APPLY>
+        | <ASC>
+        | <ASSERTION>
+        | <ASSIGNMENT>
+        | <ATTRIBUTE>
+        | <ATTRIBUTES>
+        | <BEFORE>
+        | <BERNOULLI>
+        | <BREADTH>
+        | <C>
+        | <CASCADE>
+        | <CATALOG>
+        | <CATALOG_NAME>
+        | <CENTURY>
+        | <CHAIN>
+        | <CHARACTER_SET_CATALOG>
+        | <CHARACTER_SET_NAME>
+        | <CHARACTER_SET_SCHEMA>
+        | <CHARACTERISTICS>
+        | <CHARACTERS>
+        | <CLASS_ORIGIN>
+        | <COBOL>
+        | <COLLATION>
+        | <COLLATION_CATALOG>
+        | <COLLATION_NAME>
+        | <COLLATION_SCHEMA>
+        | <COLUMN_NAME>
+        | <COMMAND_FUNCTION>
+        | <COMMAND_FUNCTION_CODE>
+        | <COMMITTED>
+        | <CONDITION_NUMBER>
+        | <CONNECTION>
+        | <CONNECTION_NAME>
+        | <CONSTRAINT_CATALOG>
+        | <CONSTRAINT_NAME>
+        | <CONSTRAINT_SCHEMA>
+        | <CONSTRAINTS>
+        | <CONSTRUCTOR>
+        | <CONTAINS>
+        | <CONTINUE>
+        | <CURSOR_NAME>
+        | <DATA>
+        | <DATABASE>
+        | <DATETIME_INTERVAL_CODE>
+        | <DATETIME_INTERVAL_PRECISION>
+        | <DECADE>
+        | <DEFAULTS>
+        | <DEFERRABLE>
+        | <DEFERRED>
+        | <DEFINED>
+        | <DEFINER>
+        | <DEGREE>
+        | <DEPTH>
+        | <DERIVED>
+        | <DESC>
+        | <DESCRIPTION>
+        | <DESCRIPTOR>
+        | <DIAGNOSTICS>
+        | <DISPATCH>
+        | <DOMAIN>
+        | <DOW>
+        | <DOY>
+        | <DYNAMIC_FUNCTION>
+        | <DYNAMIC_FUNCTION_CODE>
+        | <EPOCH>
+        | <EQUALS>
+        | <EXCEPTION>
+        | <EXCLUDE>
+        | <EXCLUDING>
+        | <FINAL>
+        | <FIRST>
+        | <FOLLOWING>
+        | <FORTRAN>
+        | <FOUND>
+        | <FRAC_SECOND>
+        | <G>
+        | <GENERAL>
+        | <GENERATED>
+        | <GO>
+        | <GOTO>
+        | <GRANTED>
+        | <HIERARCHY>
+        | <IMMEDIATE>
+        | <IMPLEMENTATION>
+        | <INCLUDING>
+        | <INCREMENT>
+        | <INITIALLY>
+        | <INPUT>
+        | <INSTANCE>
+        | <INSTANTIABLE>
+        | <INVOKER>
+        | <ISOLATION>
+        | <JAVA>
+        | <JSON>
+        | <K>
+        | <KEY>
+        | <KEY_MEMBER>
+        | <KEY_TYPE>
+        | <LABEL>
+        | <LAST>
+        | <LENGTH>
+        | <LEVEL>
+        | <LIBRARY>
+        | <LOCATOR>
+        | <M>
+        | <MAP>
+        | <MATCHED>
+        | <MAXVALUE>
+        | <MICROSECOND>
+        | <MESSAGE_LENGTH>
+        | <MESSAGE_OCTET_LENGTH>
+        | <MESSAGE_TEXT>
+        | <MILLENNIUM>
+        | <MINVALUE>
+        | <MORE_KW>
+        | <MUMPS>
+        | <NAME>
+        | <NAMES>
+        | <NESTING>
+        | <NORMALIZED>
+        | <NULLABLE>
+        | <NULLS>
+        | <NUMBER>
+        | <OBJECT>
+        | <OCTETS>
+        | <OPTION>
+        | <OPTIONS>
+        | <ORDERING>
+        | <ORDINALITY>
+        | <OTHERS>
+        | <OUTPUT>
+        | <OVERRIDING>
+        | <PAD>
+        | <PARAMETER_MODE>
+        | <PARAMETER_NAME>
+        | <PARAMETER_ORDINAL_POSITION>
+        | <PARAMETER_SPECIFIC_CATALOG>
+        | <PARAMETER_SPECIFIC_NAME>
+        | <PARAMETER_SPECIFIC_SCHEMA>
+        | <PARTIAL>
+        | <PASCAL>
+        | <PASSTHROUGH>
+        | <PATH>
+        | <PLACING>
+        | <PLAN>
+        | <PLI>
+        | <PRECEDING>
+        | <PRESERVE>
+        | <PRIOR>
+        | <PRIVILEGES>
+        | <PUBLIC>
+        | <QUARTER>
+        | <READ>
+        | <RELATIVE>
+        | <REPEATABLE>
+        | <REPLACE>
+        | <RESTART>
+        | <RESTRICT>
+        | <RETURNED_CARDINALITY>
+        | <RETURNED_LENGTH>
+        | <RETURNED_OCTET_LENGTH>
+        | <RETURNED_SQLSTATE>
+        | <ROLE>
+        | <ROUTINE>
+        | <ROUTINE_CATALOG>
+        | <ROUTINE_NAME>
+        | <ROUTINE_SCHEMA>
+        | <ROW_COUNT>
+        | <SCALE>
+        | <SCHEMA>
+        | <SCHEMA_NAME>
+        | <SCOPE_CATALOGS>
+        | <SCOPE_NAME>
+        | <SCOPE_SCHEMA>
+        | <SECTION>
+        | <SECURITY>
+        | <SELF>
+        | <SEQUENCE>
+        | <SERIALIZABLE>
+        | <SERVER>
+        | <SERVER_NAME>
+        | <SESSION>
+        | <SETS>
+        | <SIMPLE>
+        | <SIZE>
+        | <SOURCE>
+        | <SPACE>
+        | <SPECIFIC_NAME>
+        | <SQL_BIGINT>
+        | <SQL_BINARY>
+        | <SQL_BIT>
+        | <SQL_BLOB>
+        | <SQL_BOOLEAN>
+        | <SQL_CHAR>
+        | <SQL_CLOB>
+        | <SQL_DATE>
+        | <SQL_DECIMAL>
+        | <SQL_DOUBLE>
+        | <SQL_FLOAT>
+        | <SQL_INTEGER>
+        | <SQL_INTERVAL_DAY>
+        | <SQL_INTERVAL_DAY_TO_HOUR>
+        | <SQL_INTERVAL_DAY_TO_MINUTE>
+        | <SQL_INTERVAL_DAY_TO_SECOND>
+        | <SQL_INTERVAL_HOUR>
+        | <SQL_INTERVAL_HOUR_TO_MINUTE>
+        | <SQL_INTERVAL_HOUR_TO_SECOND>
+        | <SQL_INTERVAL_MINUTE>
+        | <SQL_INTERVAL_MINUTE_TO_SECOND>
+        | <SQL_INTERVAL_MONTH>
+        | <SQL_INTERVAL_SECOND>
+        | <SQL_INTERVAL_YEAR>
+        | <SQL_INTERVAL_YEAR_TO_MONTH>
+        | <SQL_LONGVARBINARY>
+        | <SQL_LONGVARNCHAR>
+        | <SQL_LONGVARCHAR>
+        | <SQL_NCHAR>
+        | <SQL_NCLOB>
+        | <SQL_NUMERIC>
+        | <SQL_NVARCHAR>
+        | <SQL_REAL>
+        | <SQL_SMALLINT>
+        | <SQL_TIME>
+        | <SQL_TIMESTAMP>
+        | <SQL_TINYINT>
+        | <SQL_TSI_DAY>
+        | <SQL_TSI_FRAC_SECOND>
+        | <SQL_TSI_HOUR>
+        | <SQL_TSI_MICROSECOND>
+        | <SQL_TSI_MINUTE>
+        | <SQL_TSI_MONTH>
+        | <SQL_TSI_QUARTER>
+        | <SQL_TSI_SECOND>
+        | <SQL_TSI_WEEK>
+        | <SQL_TSI_YEAR>
+        | <SQL_VARBINARY>
+        | <SQL_VARCHAR>
+        | <STATE>
+        | <STATEMENT>
+        | <STRUCTURE>
+        | <STYLE>
+        | <SUBCLASS_ORIGIN>
+        | <SUBSTITUTE>
+        | <TABLE_NAME>
+        | <TEMPORARY>
+        | <TIES>
+        | <TIMESTAMPADD>
+        | <TIMESTAMPDIFF>
+        | <TOP_LEVEL_COUNT>
+        | <TRANSACTION>
+        | <TRANSACTIONS_ACTIVE>
+        | <TRANSACTIONS_COMMITTED>
+        | <TRANSACTIONS_ROLLED_BACK>
+        | <TRANSFORM>
+        | <TRANSFORMS>
+        | <TRIGGER_CATALOG>
+        | <TRIGGER_NAME>
+        | <TRIGGER_SCHEMA>
+        | <TYPE>
+        | <UNBOUNDED>
+        | <UNCOMMITTED>
+        | <UNDER>
+        | <UNNAMED>
+        | <USAGE>
+        | <USER_DEFINED_TYPE_CATALOG>
+        | <USER_DEFINED_TYPE_CODE>
+        | <USER_DEFINED_TYPE_NAME>
+        | <USER_DEFINED_TYPE_SCHEMA>
+        | <VERSION>
+        | <VIEW>
+        | <WEEK>
+        | <WRAPPER>
+        | <WORK>
+        | <WRITE>
+        | <XML>
+        | <ZONE>
+    )
+    {
+        return unquotedIdentifier();
+    }
+}
+
+/* LITERALS */
+
+<DEFAULT, DQID, BTID> TOKEN :
+{
+    < UNSIGNED_INTEGER_LITERAL: (["0"-"9"])+ >
+    |
+    < APPROX_NUMERIC_LITERAL:
+    (<UNSIGNED_INTEGER_LITERAL> | <DECIMAL_NUMERIC_LITERAL>) <EXPONENT> >
+    |
+    < DECIMAL_NUMERIC_LITERAL:
+    (["0"-"9"])+(".")?(["0"-"9"])*
+    | "."(["0"-"9"])+
+    >
+    |
+    < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
+    |
+    < #HEXDIGIT: ["0"-"9","a"-"f","A"-"F"] >
+    |
+    < #WHITESPACE:
+    [ " ","\t","\n","\r","\f" ]
+    >
+    |
+    /* To improve error reporting, we allow all kinds of characters,
+     * not just hexits, in a binary string literal. */
+    < BINARY_STRING_LITERAL: ["x","X"] <QUOTE> ( (~["'"]) | ("''"))* <QUOTE> >
+    |
+    < QUOTED_STRING: <QUOTE> ( (~["'"]) | ("''"))* <QUOTE> >
+    |
+    < PREFIXED_STRING_LITERAL: ("_" <CHARSETNAME> | "N") <QUOTED_STRING> >
+    |
+    < UNICODE_STRING_LITERAL: "U" "&" <QUOTED_STRING> >
+    |
+    < #CHARSETNAME: (["a"-"z","A"-"Z","0"-"9"])
+    (["a"-"z","A"-"Z","0"-"9",":",".","-","_"])*
+    >
+}
+
+<DEFAULT, DQID, BTID> TOKEN :
+{
+    < UNICODE_QUOTED_ESCAPE_CHAR:
+    <QUOTE>
+    (~["0"-"9","a"-"f","A"-"F","+","\""," ","\t","\n","\r","\f"])
+    <QUOTE>
+    >
+}
+
+/* SEPARATORS */
+
+<DEFAULT, DQID, BTID> TOKEN :
+{
+    < LPAREN: "(">
+    | < RPAREN: ")">
+    | < LBRACE_D: "{" (" ")* ["d","D"] >
+    | < LBRACE_T: "{" (" ")* ["t","T"] >
+    | < LBRACE_TS: "{" (" ")* ["t","T"] ["s","S"] >
+    | < LBRACE_FN: "{" (" ")* ["f","F"] ["n","N"] >
+    | < LBRACE: "{" >
+    | < RBRACE: "}" >
+    | < LBRACKET: "[" >
+    | < RBRACKET: "]" >
+    | < SEMICOLON: ";" >
+    | < DOT: "." >
+    | < COMMA: "," >
+}
+
+/* OPERATORS */
+
+<DEFAULT, DQID, BTID> TOKEN :
+{
+    < EQ: "=" >
+    | < GT: ">" >
+    | < LT: "<" >
+    | < HOOK: "?" >
+    | < COLON: ":" >
+    | < LE: "<=" >
+    | < GE: ">=" >
+    | < NE: "<>" >
+    | < NE2: "!=" >
+    | < PLUS: "+" >
+    | < MINUS: "-" >
+    | < STAR: "*" >
+    | < SLASH: "/" >
+    | < CONCAT: "||" >
+    | < NAMED_ARGUMENT_ASSIGNMENT: "=>" >
+    | < DOUBLE_PERIOD: ".." >
+    | < QUOTE: "'" >
+    | < DOUBLE_QUOTE: "\"" >
+}
+
+
+/*****************************************
+ * Lexical Descriptions                  *
+ *****************************************/
+
+TOKEN_MGR_DECLS : {
+    List<Integer> lexicalStateStack = new ArrayList<Integer>();
+
+    void pushState() {
+      lexicalStateStack.add(curLexState);
+    }
+
+    void popState() {
+      SwitchTo(lexicalStateStack.remove(lexicalStateStack.size() - 1));
+    }
+
+}
+
+/*
+Lexical states:
+
+DEFAULT: Identifiers are quoted in brackets, e.g. [My Identifier]
+DQID:    Identifiers are double-quoted, e.g. "My Identifier"
+BTID:    Identifiers are enclosed in back-ticks, e.g. `My Identifier`
+IN_SINGLE_LINE_COMMENT:
+IN_FORMAL_COMMENT:
+IN_MULTI_LINE_COMMENT:
+
+DEFAULT, DQID, BTID are the 3 'normal states'. Behavior is identical except
+for how quoted identifiers are recognized.
+
+After a comment has completed, the lexer returns to the previous state, one
+of the 'normal states'.
+*/
+
+/* WHITE SPACE */
+
+<DEFAULT, DQID, BTID> SKIP :
+{
+    " "
+    | "\t"
+    | "\n"
+    | "\r"
+    | "\f"
+}
+
+/* COMMENTS */
+
+<DEFAULT, DQID, BTID> MORE :
+{
+    <"/**" ~["/"]> { pushState(); } : IN_FORMAL_COMMENT
+}
+
+<DEFAULT, DQID, BTID> MORE :
+{
+    "/*" { pushState(); } : IN_MULTI_LINE_COMMENT
+}
+
+<DEFAULT, DQID, BTID> SKIP :
+{
+    <SINGLE_LINE_COMMENT: ("//"|"--")(~["\n","\r"])* ("\n"|"\r"|"\r\n")? >
+}
+
+<IN_FORMAL_COMMENT>
+SPECIAL_TOKEN :
+{
+    <FORMAL_COMMENT: "*/" > { popState(); }
+}
+
+<IN_MULTI_LINE_COMMENT>
+SPECIAL_TOKEN :
+{
+    <MULTI_LINE_COMMENT: "*/" > { popState(); }
+}
+
+<IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
+MORE :
+{
+    < ~[] >
+}
+
+
+/* IDENTIFIERS */
+
+<DEFAULT> TOKEN :
+{
+    < BRACKET_QUOTED_IDENTIFIER:
+    "["
+    (   (~["]","\n","\r"])
+        | ("]]")
+        )+
+    "]"
+    >
+}
+
+<DQID> TOKEN :
+{
+    < QUOTED_IDENTIFIER:
+    "\""
+    (   (~["\"","\n","\r"])
+        | ("\"\"")
+        )+
+    "\""
+    >
+}
+
+<BTID>  TOKEN :
+{
+    < BACK_QUOTED_IDENTIFIER:
+    "`"
+    (   (~["`","\n","\r"])
+        | ("``")
+        )+
+    "`"
+    >
+}
+
+<DEFAULT, DQID, BTID> TOKEN :
+{
+    < COLLATION_ID:
+    (<LETTER>|<DIGIT>)+ (<LETTER>|<DIGIT>|":"|"."|"-"|"_")*
+    "$"
+    (<LETTER>|"_")+
+    ("$" (<LETTER>|<DIGIT>|"_")+)?
+    >
+    |
+    < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
+    |
+    < UNICODE_QUOTED_IDENTIFIER: "U" "&" <QUOTED_IDENTIFIER> >
+    |
+    < #LETTER:
+    [
+        "\u0024",
+        "\u0041"-"\u005a",
+        "\u005f",
+        "\u0061"-"\u007a",
+        "\u00c0"-"\u00d6",
+        "\u00d8"-"\u00f6",
+        "\u00f8"-"\u00ff",
+        "\u0100"-"\u1fff",
+        "\u3040"-"\u318f",
+        "\u3300"-"\u337f",
+        "\u3400"-"\u3d2d",
+        "\u4e00"-"\u9fff",
+        "\uf900"-"\ufaff"
+    ]
+    >
+    |
+    < #DIGIT:
+    [
+        "\u0030"-"\u0039",
+        "\u0660"-"\u0669",
+        "\u06f0"-"\u06f9",
+        "\u0966"-"\u096f",
+        "\u09e6"-"\u09ef",
+        "\u0a66"-"\u0a6f",
+        "\u0ae6"-"\u0aef",
+        "\u0b66"-"\u0b6f",
+        "\u0be7"-"\u0bef",
+        "\u0c66"-"\u0c6f",
+        "\u0ce6"-"\u0cef",
+        "\u0d66"-"\u0d6f",
+        "\u0e50"-"\u0e59",
+        "\u0ed0"-"\u0ed9",
+        "\u1040"-"\u1049"
+    ]
+    >
+}
+
+/* Special token to throw a wrench in the works. It is never valid in SQL,
+   and so when it occurs, it causes the parser to print which tokens would
+   have been valid at that point. Used by SqlAdvisor. */
+<DEFAULT, DQID, BTID> TOKEN :
+{
+    < BEL:
+    [
+        "\u0007"
+    ]
+    >
+}
+
+/**
+ * Defines a production which can never be accepted by the parser.
+ * In effect, it tells the parser, "If you got here, you've gone too far."
+ * It is used as the default production for parser extension points;
+ * derived parsers replace it with a real production when they want to
+ * implement a particular extension point.
+ */
+void UnusedExtension() :
+{
+}
+{
+    (
+        LOOKAHEAD({false}) <ZONE>
+    )
+}
diff --git a/third_party/java/BUILD b/third_party/java/BUILD
index ba59451..1471844 100644
--- a/third_party/java/BUILD
+++ b/third_party/java/BUILD
@@ -2,6 +2,11 @@
 
 package(default_visibility = ["//visibility:public"])
 
+filegroup(
+    name = "javacc",
+    srcs = ["@javacc//:javacc"],
+)
+
 java_library(
     name = "aws-java-sdk",
     srcs = [ "Empty.java" ],
diff --git a/third_party/java/javacc/javacc.BUILD b/third_party/java/javacc/javacc.BUILD
new file mode 100644
index 0000000..036aad7
--- /dev/null
+++ b/third_party/java/javacc/javacc.BUILD
@@ -0,0 +1,10 @@
+licenses(["notice"])
+  
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+    name = "javacc",
+    srcs = [
+        "bin/javacc",
+    ],
+)