/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
<@pp.dropOutputFile />

<@pp.changeOutputFile name="javacc/Parser.jj" />

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


PARSER_BEGIN(${parser.class})

package ${parser.package};

<#list (parser.imports) as importStr>
import ${importStr};
</#list>

import org.apache.calcite.avatica.util.Casing;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.runtime.CalciteContextException;
import org.apache.calcite.sql.JoinConditionType;
import org.apache.calcite.sql.JoinType;
import org.apache.calcite.sql.SqlAlter;
import org.apache.calcite.sql.SqlBasicTypeNameSpec;
import org.apache.calcite.sql.SqlBinaryOperator;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlCharStringLiteral;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlCollectionTypeNameSpec;
import org.apache.calcite.sql.SqlDataTypeSpec;
import org.apache.calcite.sql.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.SqlFunctionCategory;
import org.apache.calcite.sql.SqlHint;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlInsert;
import org.apache.calcite.sql.SqlInsertKeyword;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.SqlJdbcDataTypeName;
import org.apache.calcite.sql.SqlJdbcFunctionCall;
import org.apache.calcite.sql.SqlJoin;
import org.apache.calcite.sql.SqlJsonConstructorNullClause;
import org.apache.calcite.sql.SqlJsonEncoding;
import org.apache.calcite.sql.SqlJsonExistsErrorBehavior;
import org.apache.calcite.sql.SqlJsonEmptyOrError;
import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior;
import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior;
import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
import org.apache.calcite.sql.SqlJsonValueReturning;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlMatchRecognize;
import org.apache.calcite.sql.SqlMerge;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlNumericLiteral;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlOrderBy;
import org.apache.calcite.sql.SqlPivot;
import org.apache.calcite.sql.SqlPostfixOperator;
import org.apache.calcite.sql.SqlPrefixOperator;
import org.apache.calcite.sql.SqlRowTypeNameSpec;
import org.apache.calcite.sql.SqlSampleSpec;
import org.apache.calcite.sql.SqlSelectKeyword;
import org.apache.calcite.sql.SqlSetOption;
import org.apache.calcite.sql.SqlSnapshot;
import org.apache.calcite.sql.SqlTableRef;
import org.apache.calcite.sql.SqlTypeNameSpec;
import org.apache.calcite.sql.SqlUnnestOperator;
import org.apache.calcite.sql.SqlUnpivot;
import org.apache.calcite.sql.SqlUpdate;
import org.apache.calcite.sql.SqlUserDefinedTypeNameSpec;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.SqlWindow;
import org.apache.calcite.sql.SqlWith;
import org.apache.calcite.sql.SqlWithItem;
import org.apache.calcite.sql.fun.SqlCase;
import org.apache.calcite.sql.fun.SqlInternalOperators;
import org.apache.calcite.sql.fun.SqlLibraryOperators;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.fun.SqlTrimFunction;
import org.apache.calcite.sql.parser.Span;
import org.apache.calcite.sql.parser.SqlAbstractParserImpl;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.parser.SqlParserImplFactory;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.parser.SqlParserUtil;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlConformance;
import org.apache.calcite.util.Glossary;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.SourceStringReader;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;

import java.io.Reader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static org.apache.calcite.util.Static.RESOURCE;

/**
 * SQL parser, generated from Parser.jj by JavaCC.
 *
 * <p>The public wrapper for this parser is {@link SqlParser}.
 */
public class ${parser.class} extends SqlAbstractParserImpl
{
    private static final Logger LOGGER = CalciteTrace.getParserTracer();

    // Can't use quoted literal because of a bug in how JavaCC translates
    // backslash-backslash.
    private static final char BACKSLASH = 0x5c;
    private static final char DOUBLE_QUOTE = 0x22;
    private static final String DQ = DOUBLE_QUOTE + "";
    private static final String DQDQ = DQ + DQ;
    private static final SqlLiteral LITERAL_ZERO =
        SqlLiteral.createExactNumeric("0", SqlParserPos.ZERO);
    private static final SqlLiteral LITERAL_ONE =
        SqlLiteral.createExactNumeric("1", SqlParserPos.ZERO);
    private static final SqlLiteral LITERAL_MINUS_ONE =
        SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO);

    private static Metadata metadata;

    private Casing unquotedCasing;
    private Casing quotedCasing;
    private int identifierMaxLength;
    private SqlConformance conformance;

    /**
     * {@link SqlParserImplFactory} implementation for creating parser.
     */
    public static final SqlParserImplFactory FACTORY = new SqlParserImplFactory() {
        public SqlAbstractParserImpl getParser(Reader reader) {
            final ${parser.class} parser = new ${parser.class}(reader);
            if (reader instanceof SourceStringReader) {
                final String sql =
                    ((SourceStringReader) reader).getSourceString();
                parser.setOriginalSql(sql);
            }
          return parser;
        }
    };

    public SqlParseException normalizeException(Throwable ex) {
        try {
            if (ex instanceof ParseException) {
                ex = cleanupParseException((ParseException) ex);
            }
            return convertException(ex);
        } catch (ParseException e) {
            throw new AssertionError(e);
        }
    }

    public Metadata getMetadata() {
        synchronized (${parser.class}.class) {
            if (metadata == null) {
                metadata = new MetadataImpl(
                    new ${parser.class}(new java.io.StringReader("")));
            }
            return metadata;
        }
    }

    public void setTabSize(int tabSize) {
        jj_input_stream.setTabSize(tabSize);
    }

    public void switchTo(SqlAbstractParserImpl.LexicalState state) {
        final int stateOrdinal =
            Arrays.asList(${parser.class}TokenManager.lexStateNames)
                .indexOf(state.name());
        token_source.SwitchTo(stateOrdinal);
    }

    public void setQuotedCasing(Casing quotedCasing) {
        this.quotedCasing = quotedCasing;
    }

    public void setUnquotedCasing(Casing unquotedCasing) {
        this.unquotedCasing = unquotedCasing;
    }

    public void setIdentifierMaxLength(int identifierMaxLength) {
        this.identifierMaxLength = identifierMaxLength;
    }

    public void setConformance(SqlConformance conformance) {
        this.conformance = conformance;
    }

    public SqlNode parseSqlExpressionEof() throws Exception {
        return SqlExpressionEof();
    }

    public SqlNode parseSqlStmtEof() throws Exception {
        return SqlStmtEof();
    }

    public SqlNodeList parseSqlStmtList() throws Exception {
        return SqlStmtList();
    }

    public SqlNode parseArray() throws SqlParseException {
        switchTo(LexicalState.BQID);
        try {
          return ArrayLiteral();
        } catch (ParseException ex) {
          throw normalizeException(ex);
        } catch (TokenMgrError ex) {
          throw normalizeException(ex);
        }
    }

    private SqlNode extend(SqlNode table, SqlNodeList extendList) {
        return SqlStdOperatorTable.EXTEND.createCall(
            Span.of(table, extendList).pos(), table, extendList);
    }

    /** Adds a warning that a token such as "HOURS" was used,
    * whereas the SQL standard only allows "HOUR".
    *
    * <p>Currently, we silently add an exception to a list of warnings. In
    * future, we may have better compliance checking, for example a strict
    * compliance mode that throws if any non-standard features are used. */
    private TimeUnit warn(TimeUnit timeUnit) throws ParseException {
        final String token = getToken(0).image.toUpperCase(Locale.ROOT);
        warnings.add(
            SqlUtil.newContextException(getPos(),
                RESOURCE.nonStandardFeatureUsed(token)));
        return timeUnit;
    }
}

PARSER_END(${parser.class})


/***************************************
 * Utility Codes for Semantic Analysis *
 ***************************************/

/* For Debug */
JAVACODE
void debug_message1() {
    LOGGER.info("{} , {}", getToken(0).image, getToken(1).image);
}

JAVACODE String unquotedIdentifier() {
    return SqlParserUtil.toCase(getToken(0).image, unquotedCasing);
}

/**
 * Allows parser to be extended with new types of table references.  The
 * default implementation of this production is empty.
 */
SqlNode ExtendedTableRef() :
{
}
{
    UnusedExtension()
    {
        return null;
    }
}

/**
 * Allows an OVER clause following a table expression as an extension to
 * standard SQL syntax. The default implementation of this production is empty.
 */
SqlNode TableOverOpt() :
{
}
{
    {
        return null;
    }
}

/*
 * Parses dialect-specific keywords immediately following the SELECT keyword.
 */
void SqlSelectKeywords(List<SqlLiteral> keywords) :
{}
{
    E()
}

/*
 * Parses dialect-specific keywords immediately following the INSERT keyword.
 */
void SqlInsertKeywords(List<SqlLiteral> keywords) :
{}
{
    E()
}

/*
* Parse Floor/Ceil function parameters
*/
SqlNode FloorCeilOptions(Span s, boolean floorFlag) :
{
    SqlNode node;
}
{
    node = StandardFloorCeilOptions(s, floorFlag) {
        return node;
    }
}

/*
// This file contains the heart of a parser for SQL SELECT statements.
// code can be shared between various parsers (for example, a DDL parser and a
// DML parser) but is not a standalone JavaCC file. You need to prepend a
// parser declaration (such as that in Parser.jj).
*/

/* Epsilon */
JAVACODE
void E() {}

/** @Deprecated */
JAVACODE List startList(Object o)
{
    List list = new ArrayList();
    list.add(o);
    return list;
}

/*
 * NOTE jvs 6-Feb-2004: The straightforward way to implement the SQL grammar is
 * to keep query expressions (SELECT, UNION, etc) separate from row expressions
 * (+, LIKE, etc).  However, this is not possible with an LL(k) parser, because
 * both kinds of expressions allow parenthesization, so no fixed amount of left
 * context is ever good enough.  A sub-query can be a leaf in a row expression,
 * and can include operators like UNION, so it's not even possible to use a
 * syntactic lookahead rule like "look past an indefinite number of parentheses
 * until you see SELECT, VALUES, or TABLE" (since at that point we still
 * don't know whether we're parsing a sub-query like ((select ...) + x)
 * vs. (select ... union select ...).
 *
 * The somewhat messy solution is to unify the two kinds of expression,
 * and to enforce syntax rules using parameterized context.  This
 * is the purpose of the ExprContext parameter.  It is passed to
 * most expression productions, which check the expressions encountered
 * against the context for correctness.  When a query
 * element like SELECT is encountered, the production calls
 * checkQueryExpression, which will throw an exception if
 * a row expression was expected instead.  When a row expression like
 * IN is encountered, the production calls checkNonQueryExpression
 * instead.  It is very important to understand how this works
 * when modifying the grammar.
 *
 * The commingling of expressions results in some bogus ambiguities which are
 * resolved with LOOKAHEAD hints.  The worst example is comma.  SQL allows both
 * (WHERE x IN (1,2)) and (WHERE x IN (select ...)).  This means when we parse
 * the right-hand-side of an IN, we have to allow any kind of expression inside
 * the parentheses.  Now consider the expression "WHERE x IN(SELECT a FROM b
 * GROUP BY c,d)".  When the parser gets to "c,d" it doesn't know whether the
 * comma indicates the end of the GROUP BY or the end of one item in an IN
 * list.  Luckily, we know that select and comma-list are mutually exclusive
 * within IN, so we use maximal munch for the GROUP BY comma.  However, this
 * usage of hints could easily mask unintended ambiguities resulting from
 * future changes to the grammar, making it very brittle.
 */

JAVACODE protected SqlParserPos getPos()
{
    return new SqlParserPos(
        token.beginLine,
        token.beginColumn,
        token.endLine,
        token.endColumn);
}

/** Starts a span at the current position. */
JAVACODE Span span()
{
    return Span.of(getPos());
}

JAVACODE void checkQueryExpression(ExprContext exprContext)
{
    switch (exprContext) {
    case ACCEPT_NON_QUERY:
    case ACCEPT_SUB_QUERY:
    case ACCEPT_CURSOR:
        throw SqlUtil.newContextException(getPos(),
            RESOURCE.illegalQueryExpression());
    }
}

JAVACODE void checkNonQueryExpression(ExprContext exprContext)
{
    switch (exprContext) {
    case ACCEPT_QUERY:
        throw SqlUtil.newContextException(getPos(),
            RESOURCE.illegalNonQueryExpression());
    }
}

JAVACODE SqlNode checkNotJoin(SqlNode e)
{
    if (e instanceof SqlJoin) {
        throw SqlUtil.newContextException(e.getParserPosition(),
            RESOURCE.illegalJoinExpression());
    }
    return e;
}

/**
 * Converts a ParseException (local to this particular instantiation
 * of the parser) into a SqlParseException (common to all parsers).
 */
JAVACODE SqlParseException convertException(Throwable ex)
{
    if (ex instanceof SqlParseException) {
        return (SqlParseException) ex;
    }
    SqlParserPos pos = null;
    int[][] expectedTokenSequences = null;
    String[] tokenImage = null;
    if (ex instanceof ParseException) {
        ParseException pex = (ParseException) ex;
        expectedTokenSequences = pex.expectedTokenSequences;
        tokenImage = pex.tokenImage;
        if (pex.currentToken != null) {
            final Token token = pex.currentToken.next;
            // Checks token.image.equals("1") to avoid recursive call.
            // The SqlAbstractParserImpl#MetadataImpl constructor uses constant "1" to
            // throw intentionally to collect the expected tokens.
            if (!token.image.equals("1")
                && getMetadata().isKeyword(token.image)
                && SqlParserUtil.allowsIdentifier(tokenImage, expectedTokenSequences)) {
                // If the next token is a keyword, reformat the error message as:

                // Incorrect syntax near the keyword '{keyword}' at line {line_number},
                // column {column_number}.
                final String expecting = ex.getMessage()
                    .substring(ex.getMessage().indexOf("Was expecting"));
                final String errorMsg = String.format("Incorrect syntax near the keyword '%s' "
                        + "at line %d, column %d.\n%s",
                    token.image,
                    token.beginLine,
                    token.beginColumn,
                    expecting);
                // Replace the ParseException with explicit error message.
                ex = new ParseException(errorMsg);
            }
            pos = new SqlParserPos(
                token.beginLine,
                token.beginColumn,
                token.endLine,
                token.endColumn);
        }
    } else if (ex instanceof TokenMgrError) {
        expectedTokenSequences = null;
        tokenImage = null;
        // Example:
        //    Lexical error at line 3, column 24.  Encountered "#" after "a".
        final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
            "(?s)Lexical error at line ([0-9]+), column ([0-9]+).*");
        java.util.regex.Matcher matcher = pattern.matcher(ex.getMessage());
        if (matcher.matches()) {
            int line = Integer.parseInt(matcher.group(1));
            int column = Integer.parseInt(matcher.group(2));
            pos = new SqlParserPos(line, column, line, column);
        }
    } else if (ex instanceof CalciteContextException) {
        // CalciteContextException is the standard wrapper for exceptions
        // produced by the validator, but in the parser, the standard is
        // SqlParseException; so, strip it away. In case you were wondering,
        // the CalciteContextException appears because the parser
        // occasionally calls into validator-style code such as
        // SqlSpecialOperator.reduceExpr.
        CalciteContextException ece =
            (CalciteContextException) ex;
        pos = new SqlParserPos(
            ece.getPosLine(),
            ece.getPosColumn(),
            ece.getEndPosLine(),
            ece.getEndPosColumn());
        ex = ece.getCause();
    }

    return new SqlParseException(
        ex.getMessage(), pos, expectedTokenSequences, tokenImage, ex);
}

/**
 * Removes or transforms misleading information from a parse exception.
 *
 * @param e dirty excn
 *
 * @return clean excn
 */
JAVACODE ParseException cleanupParseException(ParseException ex)
{
    if (ex.expectedTokenSequences == null) {
        return ex;
    }
    int iIdentifier = Arrays.asList(ex.tokenImage).indexOf("<IDENTIFIER>");

    // Find all sequences in the error which contain identifier. For
    // example,
    //       {<IDENTIFIER>}
    //       {A}
    //       {B, C}
    //       {D, <IDENTIFIER>}
    //       {D, A}
    //       {D, B}
    //
    // would yield
    //       {}
    //       {D}
    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              *
 *****************************************/

SqlNode ExprOrJoinOrOrderedQuery(ExprContext exprContext) :
{
    SqlNode e;
    final List<Object> list = new ArrayList<Object>();
}
{
    // Lookhead to distinguish between "TABLE emp" (which will be
    // matched by ExplicitTable() via Query())
    // and "TABLE fun(args)" (which will be matched by TableRef())
    (
        LOOKAHEAD(2)
        e = Query(exprContext)
        e = OrderByLimitOpt(e)
        { return e; }
    |
        e = TableRef1(ExprContext.ACCEPT_QUERY_OR_JOIN)
        ( e = JoinTable(e) )*
        { list.add(e); }
        ( AddSetOpQuery(list, exprContext) )*
        { return SqlParserUtil.toTree(list); }
    )
}

/**
 * Parses either a row expression or a query expression with an optional
 * ORDER BY.
 *
 * <p>Postgres syntax for limit:
 *
 * <blockquote><pre>
 *    [ LIMIT { count | ALL } ]
 *    [ OFFSET start ]</pre>
 * </blockquote>
 *
 * <p>Trino syntax for limit:
 *
 * <blockquote><pre>
 *    [ OFFSET start ]
 *    [ LIMIT { count | ALL } ]</pre>
 * </blockquote>
 *
 * <p>MySQL syntax for limit:
 *
 * <blockquote><pre>
 *    [ LIMIT { count | start, count } ]</pre>
 * </blockquote>
 *
 * <p>SQL:2008 syntax for limit:
 *
 * <blockquote><pre>
 *    [ OFFSET start { ROW | ROWS } ]
 *    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]</pre>
 * </blockquote>
 */
SqlNode OrderedQueryOrExpr(ExprContext exprContext) :
{
    SqlNode e;
}
{
    e = QueryOrExpr(exprContext)
    e = OrderByLimitOpt(e)
    { return e; }
}

/** Reads optional "ORDER BY", "LIMIT", "OFFSET", "FETCH" following a query,
 * {@code e}. If any of them are present, adds them to the query;
 * otherwise returns the query unchanged.
 * Throws if they are present and {@code e} is not a query. */
SqlNode OrderByLimitOpt(SqlNode e) :
{
    final SqlNodeList orderBy;
    final Span s = Span.of();
    SqlNode[] offsetFetch = {null, null};
    ExportOptions exportOptions = null;
    boolean forUpdate=false;
}
{
    (
        // use the syntactic type of the expression we just parsed
        // to decide whether ORDER BY makes sense
        orderBy = OrderBy(e.isA(SqlKind.QUERY))
    |   { orderBy = null; }
    )
    [
        LimitClause(s, offsetFetch)
        [ OffsetClause(s, offsetFetch) ]
    |
        OffsetClause(s, offsetFetch)
        [
            LimitClause(s, offsetFetch) {
                if (!this.conformance.isOffsetLimitAllowed()) {
                    throw SqlUtil.newContextException(s.end(this),
                        RESOURCE.offsetLimitNotAllowed());
                }
            }
        |
            FetchClause(offsetFetch)
        ]
    |
        FetchClause(offsetFetch)
    ]
    [ exportOptions = exportOptions() ]
    [ <FOR><UPDATE> { forUpdate = true;} ]
    {
        if (e instanceof SqlSelect && exportOptions != null) {
           SqlSelect sqlSelect = (SqlSelect)e;
           sqlSelect.setExportOptions(exportOptions);
        }
        if (orderBy != null || offsetFetch[0] != null || offsetFetch[1] != null) {
            return new SqlOrderBy(getPos(), e,
                Util.first(orderBy, SqlNodeList.EMPTY),
                offsetFetch[0], offsetFetch[1]);
        }
        return e;
    }
}

/** Reads optional "ORDER BY", "LIMIT", "OFFSET", "FETCH" following a query,
 * {@code e}. If any of them are present, adds them to the query;
 * otherwise returns the query unchanged.
 * Throws if they are present and {@code e} is not a query. */
SqlNode OrderByLimitTraceOpt(SqlNode e) :
{
    final SqlNodeList orderBy;
    final Span s = Span.of();
    SqlNode[] offsetFetch = {null, null};
    ExportOptions exportOptions = null;
    boolean forUpdate=false;
}
{
    (
        // use the syntactic type of the expression we just parsed
        // to decide whether ORDER BY makes sense
        orderBy = OrderBy(e.isA(SqlKind.QUERY))
    |   { orderBy = null; }
    )
    [
        LimitClause(s, offsetFetch)
        [ OffsetClause(s, offsetFetch) ]
    |
        OffsetClause(s, offsetFetch)
        [
            LimitClause(s, offsetFetch) {
                if (!this.conformance.isOffsetLimitAllowed()) {
                    throw SqlUtil.newContextException(s.end(this),
                        RESOURCE.offsetLimitNotAllowed());
                }
            }
        |
            FetchClause(offsetFetch)
        ]
    |
        FetchClause(offsetFetch)
    ]
    [ exportOptions = exportOptions() ]
    [ <FOR><UPDATE> { forUpdate = true;} ]
    {
        if (orderBy != null || offsetFetch[0] != null || offsetFetch[1] != null) {
            return new io.dingodb.calcite.grammar.dql.SqlOrderBy(getPos(), e,
                Util.first(orderBy, SqlNodeList.EMPTY),
                offsetFetch[0], offsetFetch[1], true);
        }
        return e;
    }
}

/**
 * Parses an OFFSET clause in an ORDER BY expression.
 */
void OffsetClause(Span s, SqlNode[] offsetFetch) :
{
}
{
    // ROW or ROWS is required in SQL:2008 but we make it optional
    // because it is not present in Postgres-style syntax.
    <OFFSET> { s.add(this); }
    offsetFetch[0] = UnsignedNumericLiteralOrParam()
    [ <ROW> | <ROWS> ]
}

/**
 * Parses a FETCH clause in an ORDER BY expression.
 */
void FetchClause(SqlNode[] offsetFetch) :
{
}
{
    // SQL:2008-style syntax. "OFFSET ... FETCH ...".
    // If you specify both LIMIT and FETCH, FETCH wins.
    <FETCH> ( <FIRST> | <NEXT> ) offsetFetch[1] = UnsignedNumericLiteralOrParam()
    ( <ROW> | <ROWS> ) <ONLY>
}

/**
 * Parses a LIMIT clause in an ORDER BY expression.
 */
void LimitClause(Span s, SqlNode[] offsetFetch) :
{
}
{
    // Postgres-style syntax. "LIMIT ... OFFSET ..."
    <LIMIT> { s.add(this); }
    (
        // MySQL-style syntax. "LIMIT start, count"
        LOOKAHEAD(2)
        offsetFetch[0] = UnsignedNumericLiteralOrParam()
        <COMMA> offsetFetch[1] = UnsignedNumericLiteralOrParam() {
            if (!this.conformance.isLimitStartCountAllowed()) {
                throw SqlUtil.newContextException(s.end(this),
                    RESOURCE.limitStartCountNotAllowed());
            }
        }
    |
        offsetFetch[1] = UnsignedNumericLiteralOrParam()
    |
        <ALL>
    )
}

/**
 * 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.
 * Depending on {@code exprContext}, may also accept a join.
 */
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 = ExprOrJoinOrOrderedQuery(exprContext)
    <RPAREN>
    {
        exprContext.throwIfNotCompatible(e);
        return e;
    }
}

/**
 * Parses a parenthesized query or comma-list of row expressions.
 *
 * <p>REVIEW jvs 8-Feb-2004: There's a small hole in this production.  It can be
 * used to construct something like
 *
 * <blockquote><pre>
 * WHERE x IN (select count(*) from t where c=d,5)</pre>
 * </blockquote>
 *
 * <p>which should be illegal.  The above is interpreted as equivalent to
 *
 * <blockquote><pre>
 * WHERE x IN ((select count(*) from t where c=d),5)</pre>
 * </blockquote>
 *
 * <p>which is a legal use of a sub-query.  The only way to fix the hole is to
 * be able to remember whether a subexpression was parenthesized or not, which
 * means preserving parentheses in the SqlNode tree.  This is probably
 * desirable anyway for use in purely syntactic parsing applications (e.g. SQL
 * pretty-printer).  However, if this is done, it's important to also make
 * isA() on the paren node call down to its operand so that we can
 * always correctly discriminate a query from a row expression.
 */
SqlNodeList ParenthesizedQueryOrCommaList(
    ExprContext exprContext) :
{
    SqlNode e;
    final List<SqlNode> list = new ArrayList<SqlNode>();
    ExprContext firstExprContext = exprContext;
    final Span s;
}
{
    <LPAREN>
    {
        // we've now seen left paren, so a query by itself should
        // be interpreted as a sub-query
        s = span();
        switch (exprContext) {
        case ACCEPT_SUB_QUERY:
            firstExprContext = ExprContext.ACCEPT_NONCURSOR;
            break;
        case ACCEPT_CURSOR:
            firstExprContext = ExprContext.ACCEPT_ALL;
            break;
        }
    }
    e = OrderedQueryOrExpr(firstExprContext) { list.add(e); }
    (
        <COMMA>
        {
            // a comma-list can't appear where only a query is expected
            checkNonQueryExpression(exprContext);
        }
        AddExpression(list, exprContext)
    )*
    <RPAREN>
    {
        return new SqlNodeList(list, s.end(this));
    }
}

/** As ParenthesizedQueryOrCommaList, but allows DEFAULT
 * in place of any of the expressions. For example,
 * {@code (x, DEFAULT, null, DEFAULT)}. */
SqlNodeList ParenthesizedQueryOrCommaListWithDefault(
    ExprContext exprContext) :
{
    SqlNode e;
    final List<SqlNode> list = new ArrayList<SqlNode>();
    ExprContext firstExprContext = exprContext;
    final Span s;
}
{
    <LPAREN>
    {
        // we've now seen left paren, so a query by itself should
        // be interpreted as a sub-query
        s = span();
        switch (exprContext) {
        case ACCEPT_SUB_QUERY:
            firstExprContext = ExprContext.ACCEPT_NONCURSOR;
            break;
        case ACCEPT_CURSOR:
            firstExprContext = ExprContext.ACCEPT_ALL;
            break;
        }
    }
    (
        e = OrderedQueryOrExpr(firstExprContext) { list.add(e); }
    |
        e = Default() { list.add(e); }
    )
    (
        <COMMA>
        {
            // a comma-list can't appear where only a query is expected
            checkNonQueryExpression(exprContext);
        }
        (
            e = Expression(exprContext) { list.add(e); }
        |
            e = Default() { list.add(e); }
        )
    )*
    <RPAREN>
    {
        return new SqlNodeList(list, s.end(this));
    }
}

/**
 * Parses function parameter lists.
 * If the list starts with DISTINCT or ALL, it is discarded.
 */
List<SqlNode> UnquantifiedFunctionParameterList(ExprContext exprContext) :
{
    final List<SqlNode> args;
}
{
    args = FunctionParameterList(exprContext) {
        args.remove(0); // remove DISTINCT or ALL, if present
        return args;
    }
}

/**
 * Parses function parameter lists including DISTINCT keyword recognition,
 * DEFAULT, and named argument assignment.
 */
List<SqlNode> FunctionParameterList(ExprContext exprContext) :
{
    final SqlLiteral qualifier;
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    <LPAREN>
    (
        qualifier = AllOrDistinct() { list.add(qualifier); }
    |
        { list.add(null); }
    )
    AddArg0(list, exprContext)
    (
        <COMMA> {
            // a comma-list can't appear where only a query is expected
            checkNonQueryExpression(exprContext);
        }
        AddArg(list, exprContext)
    )*
    <RPAREN>
    {
        return list;
    }
}

SqlLiteral AllOrDistinct() :
{
}
{
    <DISTINCT> { return SqlSelectKeyword.DISTINCT.symbol(getPos()); }
|
    <ALL> { return SqlSelectKeyword.ALL.symbol(getPos()); }
}

void AddArg0(List<SqlNode> list, ExprContext exprContext) :
{
    final SqlIdentifier name;
    SqlNode e;
    final ExprContext firstExprContext;
    {
        // we've now seen left paren, so queries inside should
        // be allowed as sub-queries
        switch (exprContext) {
        case ACCEPT_SUB_QUERY:
            firstExprContext = ExprContext.ACCEPT_NONCURSOR;
            break;
        case ACCEPT_CURSOR:
            firstExprContext = ExprContext.ACCEPT_ALL;
            break;
        default:
            firstExprContext = exprContext;
            break;
        }
    }
}
{
    (
        LOOKAHEAD(2) name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT>
    |   { name = null; }
    )
    (
        e = Default()
    |
        LOOKAHEAD(3)
        e = TableParam()
    |
        e = PartitionedQueryOrQueryOrExpr(firstExprContext)
    |
        e = TableFunctionCall()
    )
    {
        if (name != null) {
            e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall(
                Span.of(name, e).pos(), e, name);
        }
        list.add(e);
    }
}

void AddArg(List<SqlNode> list, ExprContext exprContext) :
{
    final SqlIdentifier name;
    SqlNode e;
}
{
    (
        LOOKAHEAD(2) name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT>
    |   { name = null; }
    )
    (
        e = Default()
    |
        e = Expression(exprContext)
    |
        e = TableParam()
    |
        e = TableFunctionCall()
    )
    {
        if (name != null) {
            e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall(
                Span.of(name, e).pos(), e, name);
        }
        list.add(e);
    }
}

SqlNode Default() : {}
{
    <DEFAULT_> {
        return SqlStdOperatorTable.DEFAULT.createCall(getPos());
    }
}

/**
 * Parses a query (SELECT, UNION, INTERSECT, EXCEPT, VALUES, TABLE) followed by
 * the end-of-file symbol.
 */
SqlNode SqlQueryEof() :
{
    SqlNode query;
}
{
    query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
    <EOF>
    { return query; }
}

/**
 * Parses a list of SQL statements separated by semicolon.
 * The semicolon is required between statements, but is
 * optional at the end.
 */
SqlNodeList SqlStmtList() :
{
    final List<SqlNode> stmtList = new ArrayList<SqlNode>();
    SqlNode stmt;
}
{
    stmt = SqlStmt() {
        stmtList.add(stmt);
    }
    (
        <SEMICOLON>
        [
            stmt = SqlStmt() {
                stmtList.add(stmt);
            }
        ]
    )*
    <EOF>
    {
        return new SqlNodeList(stmtList, Span.of(stmtList).pos());
    }
}

/**
 * Parses an SQL statement.
 */
SqlNode SqlStmt() :
{
    SqlNode stmt;
}
{
    (
<#-- Add methods to parse additional statements here -->
<#list (parser.statementParserMethods!) as method>
        LOOKAHEAD(2) stmt = ${method}()
    |
</#list>
        stmt = SqlSetOption(Span.of(), null)
    |
        stmt = SqlAlter()
    |
<#if (parser.createStatementParserMethods!)?size != 0>
        stmt = SqlCreate()
    |
</#if>
<#if (parser.dropStatementParserMethods!)?size != 0>
        stmt = SqlDrop()
    |
</#if>
        stmt = SqlDescTable()
    |
        stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
    |
        stmt = SqlTruncate()
    |
        stmt = SqlExplain()
    |
        stmt = SqlDescribe()
    |
        stmt = SqlInsert()
    |
        stmt = SqlDelete()
    |
        stmt = SqlUpdate()
    |
        stmt = SqlMerge()
    |
        stmt = SqlProcedureCall()
    )
    {
        return stmt;
    }
}

/**
 * Parses an SQL statement followed by the end-of-file symbol.
 */
SqlNode SqlStmtEof() :
{
    SqlNode stmt;
}
{
    stmt = SqlStmt() <EOF>
    {
        return stmt;
    }
}

<#-- Add implementations of additional parser statement calls here -->
<#list (parser.implementationFiles!) as file>
    <#include "/@includes/"+file />
</#list>

SqlNodeList ParenthesizedKeyValueOptionCommaList() :
{
    final Span s;
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    { s = span(); }
    <LPAREN>
    AddKeyValueOption(list)
    (
        <COMMA>
        AddKeyValueOption(list)
    )*
    <RPAREN> {
        return new SqlNodeList(list, s.end(this));
    }
}

/**
* Parses an option with format key=val whose key is a simple identifier or string literal
* and value is a string literal.
*/
void AddKeyValueOption(List<SqlNode> list) :
{
    final SqlNode key;
    final SqlNode value;
}
{
    (
        key = SimpleIdentifier()
    |
        key = StringLiteral()
    )
    <EQ>
    value = StringLiteral() {
        list.add(key);
        list.add(value);
    }
}

/** Parses an option value (either a string or a numeric) and adds to a list. */
void AddOptionValue(List<SqlNode> list) :
{
    final SqlNode value;
}
{
    (
        value = NumericLiteral() { list.add(value); }
    |
        value = StringLiteral() { list.add(value); }
    )
}

/**
 * Parses a literal list separated by comma. The literal is either a string or a numeric.
 */
SqlNodeList ParenthesizedLiteralOptionCommaList() :
{
    final Span s;
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    { s = span(); }
    <LPAREN>
    AddOptionValue(list) ( <COMMA> AddOptionValue(list) )*
    <RPAREN> {
        return new SqlNodeList(list, s.end(this));
    }
}

void AddHint(List<SqlNode> hints) :
{
    final SqlIdentifier hintName;
    final SqlNodeList hintOptions;
    final SqlHint.HintOptionFormat optionFormat;
}
{
    hintName = SimpleIdentifier()
    (
       LOOKAHEAD(5)
        hintOptions = ParenthesizedKeyValueOptionCommaList() {
            optionFormat = SqlHint.HintOptionFormat.KV_LIST;
        }
    |
        LOOKAHEAD(3)
        hintOptions = ParenthesizedSimpleIdentifierList() {
            optionFormat = SqlHint.HintOptionFormat.ID_LIST;
        }
    |
        LOOKAHEAD(3)
        hintOptions = ParenthesizedLiteralOptionCommaList() {
            optionFormat = SqlHint.HintOptionFormat.LITERAL_LIST;
        }
    |
        LOOKAHEAD(2)
        [<LPAREN> <RPAREN>]
        {
            hintOptions = SqlNodeList.EMPTY;
            optionFormat = SqlHint.HintOptionFormat.EMPTY;
        }
    )
    {
        hints.add(
           new SqlHint(Span.of(hintOptions).end(this), hintName, hintOptions,
               optionFormat));
    }
}

/** Parses hints following a table reference,
 * and returns the wrapped table reference. */
SqlNode TableHints(SqlIdentifier tableName) :
{
    final List<SqlNode> hints = new ArrayList<SqlNode>();
}
{
    <HINT_BEG> AddHint(hints) ( <COMMA> AddHint(hints) )* <COMMENT_END> {
        final SqlParserPos pos = Span.of(tableName).addAll(hints).end(this);
        final SqlNodeList hintList = new SqlNodeList(hints, pos);
        return new SqlTableRef(pos, tableName, hintList);
    }
}

/**
 * Parses a leaf SELECT expression without ORDER BY.
 */
SqlNode SqlSelect() :
{
    final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
    final SqlLiteral keyword;
    final SqlNodeList keywordList;
    final List<SqlNode> selectList = new ArrayList<SqlNode>();
    final SqlNode fromClause;
    final SqlNode where;
    final SqlNodeList groupBy;
    final SqlNode having;
    final SqlNodeList windowDecls;
    final List<SqlNode> hints = new ArrayList<SqlNode>();
    final Span s;
    boolean forUpdate = false;
    boolean flashbackQuery = false;
    String flashBackStr = null;
    String flashBackTsoStr = null;
    ExportOptions exportOptions = null;
}
{
    <SELECT> { s = span(); }
    [
        <NEXT_AUTO_INCREMENT> <FROM>
        { return new SqlNextAutoIncrement(s.end(this), CompoundTableIdentifier()); }
    ]

    [ <HINT_BEG> AddHint(hints) ( <COMMA> AddHint(hints) )* <COMMENT_END> ]
    SqlSelectKeywords(keywords)
    (
        <STREAM> {
            keywords.add(SqlSelectKeyword.STREAM.symbol(getPos()));
        }
    )?
    (
        keyword = AllOrDistinct() { keywords.add(keyword); }
    )?
    {
        keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos());
    }
    AddSelectItem(selectList)
    ( <COMMA> AddSelectItem(selectList) )*
    [ exportOptions = exportOptions() ]
    (
        <FROM> fromClause = FromClause()
        ( where = Where() | { where = null; } )
        ( groupBy = GroupBy() | { groupBy = null; } )
        ( having = Having() | { having = null; } )
        ( windowDecls = Window() | { windowDecls = null; } )
        [ exportOptions = exportOptions() ]
        [ <FOR> <UPDATE> { forUpdate=true; }]
        [ <AS> <OF> { flashbackQuery = true; }
         (
           <TIMESTAMP> <QUOTED_STRING> { flashBackStr = token.image.replace("'", ""); }
           |
           <TSO> <UNSIGNED_INTEGER_LITERAL> { flashBackTsoStr = token.image; }
         ) ]
    |
        E() {
            fromClause = null;
            where = null;
            groupBy = null;
            having = null;
            windowDecls = null;
        }
    )
    {
        return new SqlSelect(s.end(this), keywordList,
            new SqlNodeList(selectList, Span.of(selectList).pos()),
            fromClause, where, groupBy, having, windowDecls, null, null, null,
            new SqlNodeList(hints, getPos()),
            exportOptions, flashbackQuery, flashBackStr, flashBackTsoStr);
    }
}

/**
 * Parses a leaf SELECT expression without ORDER BY.
 */
SqlNode SqlTraceSelect() :
{
    final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
    final SqlLiteral keyword;
    final SqlNodeList keywordList;
    final List<SqlNode> selectList = new ArrayList<SqlNode>();
    final SqlNode fromClause;
    final SqlNode where;
    final SqlNodeList groupBy;
    final SqlNode having;
    final SqlNodeList windowDecls;
    final List<SqlNode> hints = new ArrayList<SqlNode>();
    final SqlNodeList orderBy;
    final Span s;
    boolean forUpdate = false;
    SqlNode[] offsetFetch = {null, null};
    ExportOptions exportOptions = null;
}
{
    <TRACE> <SELECT> { s = span(); }
    [
        <NEXT_AUTO_INCREMENT> <FROM>
        { return new SqlNextAutoIncrement(s.end(this), CompoundTableIdentifier()); }
    ]

    [ <HINT_BEG> AddHint(hints) ( <COMMA> AddHint(hints) )* <COMMENT_END> ]
    SqlSelectKeywords(keywords)
    (
        <STREAM> {
            keywords.add(SqlSelectKeyword.STREAM.symbol(getPos()));
        }
    )?
    (
        keyword = AllOrDistinct() { keywords.add(keyword); }
    )?
    {
        keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos());
    }
    AddSelectItem(selectList)
    ( <COMMA> AddSelectItem(selectList) )*
    [ exportOptions = exportOptions() ]
    (
        <FROM> fromClause = FromClause()
        ( where = Where() | { where = null; } )
        ( groupBy = GroupBy() | { groupBy = null; } )
        ( having = Having() | { having = null; } )
        ( windowDecls = Window() | { windowDecls = null; } )
        ( orderBy = OrderBy(true) | { orderBy = null; } )
        [ LimitClause(s, offsetFetch) ]
        [ exportOptions = exportOptions() ]
        [ <FOR> <UPDATE> { forUpdate=true; }]
    |
        E() {
            fromClause = null;
            where = null;
            groupBy = null;
            having = null;
            orderBy = null;
            windowDecls = null;
        }
    )
    {
        return new SqlSelect(s.end(this), keywordList,
            new SqlNodeList(selectList, Span.of(selectList).pos()),
            fromClause, where, groupBy, having, windowDecls, orderBy, null, null,
            new SqlNodeList(hints, getPos()),
            exportOptions, true);
    }
}

ExportOptions exportOptions(): {
    boolean export = false;
    String outfile = null;
    byte[] terminated = new byte[]{0x09};
    String enclosed = null;
    byte[] lineTerminated = new byte[]{0x0d, 0x0a};
    byte[] escaped = "\\".getBytes();
    byte[] lineStarting = null;
    String exportCharset = "utf8";
    long startTs = 0;
} {
    <INTO> <OUTFILE> <QUOTED_STRING>
    {
      export = true;
      outfile = token.image.replace("'", "");
    }
    [
       <CHARACTER> <SET> <QUOTED_STRING> { exportCharset = token.image.replace("'", ""); }
    ]
    [
       <FIELDS>
             [<TERMINATED> <BY> [<QUOTED_STRING> { terminated = getSpecialBytes(token.image); }]
                                [<BINARY_STRING_LITERAL> { terminated = getSpecialHexBytes(token.image);}]
             ]
             [<ENCLOSED> <BY> <QUOTED_STRING> { enclosed = getEnclosed(token.image); } ]
             [<ESCAPED> <BY> [<QUOTED_STRING> { escaped = getSpecialBytes(token.image); }]
                             [<BINARY_STRING_LITERAL> { escaped = getSpecialHexBytes(token.image);}]
             ]
     ]
     [
         <LINES>
                 [ <STARTING> <BY> [<QUOTED_STRING> { lineStarting = getSpecialBytes(token.image); }]
                                   [<BINARY_STRING_LITERAL> { lineStarting = getSpecialHexBytes(token.image); }]
                 ]
                 [ <TERMINATED> <BY> [<QUOTED_STRING> { lineTerminated = getSpecialBytes(token.image); } ]
                                     [<BINARY_STRING_LITERAL> { lineTerminated = getSpecialHexBytes(token.image); }]
                 ]
                 [ <STARTING> <BY> [<QUOTED_STRING> { lineStarting = getSpecialBytes(token.image); }]
                                   [<BINARY_STRING_LITERAL> { lineStarting = getSpecialHexBytes(token.image); }]
                 ]
     ]
     [ <STARTTS> (<UNSIGNED_INTEGER_LITERAL> | <DECIMAL_NUMERIC_LITERAL>) { startTs = Long.parseLong(token.image); }]
     {
       return new ExportOptions(export, outfile, terminated, enclosed, lineTerminated, escaped, lineStarting, exportCharset, startTs);
     }
}

/*
 * Abstract production:
 *
 *    void SqlSelectKeywords(List keywords)
 *
 * Parses dialect-specific keywords immediately following the SELECT keyword.
 */

/**
 * Parses an EXPLAIN PLAN statement.
 */
SqlNode SqlExplain() :
{
    SqlNode stmt;
    SqlExplainLevel detailLevel = SqlExplainLevel.EXPPLAN_ATTRIBUTES;
    SqlExplain.Depth depth;
    final SqlExplainFormat format;
}
{
    <EXPLAIN> <PLAN>
    [ detailLevel = ExplainDetailLevel() ]
    depth = ExplainDepth()
    (
        LOOKAHEAD(2)
        <AS> <XML> { format = SqlExplainFormat.XML; }
    |
        LOOKAHEAD(2)
        <AS> <JSON> { format = SqlExplainFormat.JSON; }
    |
        <AS> <DOT_FORMAT> { format = SqlExplainFormat.DOT; }
    |
        { format = SqlExplainFormat.TEXT; }
    )
    <FOR> stmt = SqlQueryOrDml() {
        return new SqlExplain(getPos(),
            stmt,
            detailLevel.symbol(SqlParserPos.ZERO),
            depth.symbol(SqlParserPos.ZERO),
            format.symbol(SqlParserPos.ZERO),
            nDynamicParams);
    }
}

/** Parses a query (SELECT or VALUES)
 * or DML statement (INSERT, UPDATE, DELETE, MERGE). */
SqlNode SqlQueryOrDml() :
{
    SqlNode stmt;
}
{
    (
        stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
    |
        stmt = SqlInsert()
    |
        stmt = SqlDelete()
    |
        stmt = SqlUpdate()
    |
        stmt = SqlMerge()
    ) { return stmt; }
}

/**
 * Parses WITH TYPE | WITH IMPLEMENTATION | WITHOUT IMPLEMENTATION modifier for
 * EXPLAIN PLAN.
 */
SqlExplain.Depth ExplainDepth() :
{
}
{
    (
        LOOKAHEAD(2)
        <WITH> <TYPE>
        {
            return SqlExplain.Depth.TYPE;
        }
        |
        <WITH> <IMPLEMENTATION>
        {
            return SqlExplain.Depth.PHYSICAL;
        }
        |
        <WITHOUT> <IMPLEMENTATION>
        {
            return SqlExplain.Depth.LOGICAL;
        }
        |
        {
            return SqlExplain.Depth.PHYSICAL;
        }

    )
}

/**
 * Parses INCLUDING ALL ATTRIBUTES modifier for EXPLAIN PLAN.
 */
SqlExplainLevel ExplainDetailLevel() :
{
    SqlExplainLevel level = SqlExplainLevel.EXPPLAN_ATTRIBUTES;
}
{
    (
        <EXCLUDING> <ATTRIBUTES>
        {
            level = SqlExplainLevel.NO_ATTRIBUTES;
        }
        |
        <INCLUDING>
        [ <ALL> { level = SqlExplainLevel.ALL_ATTRIBUTES; } ]
        <ATTRIBUTES>
        {
        }
    )
    {
        return level;
    }
}

/**
 * Parses a DESCRIBE statement.
 */
SqlNode SqlDescribe() :
{
   final Span s;
   final SqlIdentifier table;
   final SqlIdentifier column;
   final SqlIdentifier id;
   final SqlNode stmt;
}
{
    <DESCRIBE> { s = span(); }
    (
        LOOKAHEAD(2) (<DATABASE> | <CATALOG> | <SCHEMA>)
        id = CompoundIdentifier() {
            // DESCRIBE DATABASE and DESCRIBE CATALOG currently do the same as
            // DESCRIBE SCHEMA but should be different. See
            //   [CALCITE-1221] Implement DESCRIBE DATABASE, CATALOG, STATEMENT
            return new SqlDescribeSchema(s.end(id), id);
        }
    |
        // Use syntactic lookahead to determine whether a table name is coming.
        // We do not allow SimpleIdentifier() because that includes <STATEMENT>.
        LOOKAHEAD( <TABLE>
           | <IDENTIFIER>
           | <HYPHENATED_IDENTIFIER>
           | <QUOTED_IDENTIFIER>
           | <BACK_QUOTED_IDENTIFIER>
           | <BIG_QUERY_BACK_QUOTED_IDENTIFIER>
           | <BRACKET_QUOTED_IDENTIFIER> )
        (<TABLE>)?
        table = CompoundIdentifier()
        ( column = SimpleIdentifier() | { column = null; } )
        {
            return new SqlDescribeTable(s.add(table).addIf(column).pos(),
                table, column);
        }
    |
        (LOOKAHEAD(1) <STATEMENT>)?
        stmt = SqlQueryOrDml() {
            // DESCRIBE STATEMENT currently does the same as EXPLAIN. See
            //   [CALCITE-1221] Implement DESCRIBE DATABASE, CATALOG, STATEMENT
            final SqlExplainLevel detailLevel = SqlExplainLevel.NO_ATTRIBUTES;
            final SqlExplain.Depth depth = SqlExplain.Depth.PHYSICAL;
            final SqlExplainFormat format = SqlExplainFormat.TEXT;
            return new SqlExplain(s.end(stmt),
                stmt,
                detailLevel.symbol(SqlParserPos.ZERO),
                depth.symbol(SqlParserPos.ZERO),
                format.symbol(SqlParserPos.ZERO),
                nDynamicParams);
        }
    )
}

/**
 * Parses a CALL statement.
 */
SqlNode SqlProcedureCall() :
{
    final Span s;
    SqlNode routineCall;
}
{
    <CALL> {
        s = span();
    }
    routineCall = NamedRoutineCall(
        SqlFunctionCategory.USER_DEFINED_PROCEDURE,
        ExprContext.ACCEPT_SUB_QUERY)
    {
        return SqlStdOperatorTable.PROCEDURE_CALL.createCall(
            s.end(routineCall), routineCall);
    }
}

SqlNode NamedRoutineCall(
    SqlFunctionCategory routineType,
    ExprContext exprContext) :
{
    final SqlIdentifier name;
    final List<SqlNode> list = new ArrayList<SqlNode>();
    final Span s;
}
{
    name = CompoundIdentifier() {
        s = span();
    }
    <LPAREN>
    [
        AddArg0(list, exprContext)
        (
            <COMMA> {
                // a comma-list can't appear where only a query is expected
                checkNonQueryExpression(exprContext);
            }
            AddArg(list, exprContext)
        )*
    ]
    <RPAREN>
    {
        return createCall(name, s.end(this), routineType, null, list);
    }
}

/**
 * Table parameter of a table function.
 * The input table with set semantics may be partitioned/ordered on one or more columns.
 */
SqlNode TableParam() :
{
    final Span s;
    final SqlNodeList partitionList;
    final SqlNodeList orderList;
    SqlNode tableRef;
}
{
    { s = span(); }
    tableRef = ExplicitTable(getPos())
    (
        <PARTITION> <BY>
        partitionList = SimpleIdentifierOrList()
    |   { partitionList = SqlNodeList.EMPTY; }
    )
    (
        orderList = OrderByOfSetSemanticsTable()
     |  { orderList = SqlNodeList.EMPTY; }
    )
    { return CreateSetSemanticsTableIfNeeded(s, tableRef, partitionList, orderList); }
}

SqlNode PartitionedQueryOrQueryOrExpr(ExprContext exprContext) :
{
    SqlNode e;
}
{
    e = OrderedQueryOrExpr(exprContext)
    e = PartitionedByAndOrderBy(e)

    { return e; }
}

SqlNode PartitionedByAndOrderBy(SqlNode e) :
{
    final Span s;
    final SqlNodeList partitionList;
    final SqlNodeList orderList;
}
{
    { s = span(); }
    (
        <PARTITION> <BY>
        partitionList = SimpleIdentifierOrList()
    |   { partitionList = SqlNodeList.EMPTY; }
    )
    (
        orderList = OrderByOfSetSemanticsTable()
     |  { orderList = SqlNodeList.EMPTY; }
    )
    { return CreateSetSemanticsTableIfNeeded(s, e, partitionList, orderList); }
}

SqlNodeList OrderByOfSetSemanticsTable() :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
    final Span s;
}
{
  <ORDER>
  { s = span(); }
  <BY>
  (
      LOOKAHEAD(2)
      <LPAREN> AddOrderItem(list)
      (
        // NOTE jvs 6-Feb-2004:  See comments at top of file for why
        // hint is necessary here.
        LOOKAHEAD(2) <COMMA> AddOrderItem(list)
      )*
      <RPAREN> {
          return new SqlNodeList(list, s.addAll(list).pos());
      }
  |
      AddOrderItem(list)
      {
          return new SqlNodeList(list, s.addAll(list).pos());
      }
  )
}

SqlNode CreateSetSemanticsTableIfNeeded(
    final Span s,
    final SqlNode e,
    final SqlNodeList partitionList,
    final SqlNodeList orderList) :
{

}
{

    {
        if (partitionList.isEmpty() && orderList.isEmpty()) {
            return e;
        } else {
            return SqlStdOperatorTable.SET_SEMANTICS_TABLE.createCall(
                s.pos(), e, partitionList, orderList);
        }
    }
}

/**
 * Parses an INSERT statement.
 */
SqlNode SqlInsert() :
{
    final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
    final SqlNodeList keywordList;
    final SqlIdentifier tableName;
    SqlNode tableRef;
    SqlNode source;
    final SqlNodeList columnList;
    final Span s;
    final Pair<SqlNodeList, SqlNodeList> p;
    SqlNodeList sourceExpressionList;
    SqlNodeList targetColumnList;
    SqlIdentifier id;
}
{
    (
        <INSERT>
    |
        <UPSERT> { keywords.add(SqlInsertKeyword.UPSERT.symbol(getPos())); }
    )
    {
        s = span();
        targetColumnList = new SqlNodeList(s.pos());
        sourceExpressionList = new SqlNodeList(s.pos());
    }
    SqlInsertKeywords(keywords) {
        keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos());
    }
    <INTO> tableName = CompoundTableIdentifier()
    ( tableRef = TableHints(tableName) | { tableRef = tableName; } )
    [ LOOKAHEAD(5) tableRef = ExtendTable(tableRef) ]
    (
        LOOKAHEAD(2)
        p = ParenthesizedCompoundIdentifierList() {
            if (p.right.size() > 0) {
                tableRef = extend(tableRef, p.right);
            }
            if (p.left.size() > 0) {
                columnList = p.left;
            } else {
                columnList = null;
            }
        }
    |   { columnList = null; }
    )
    source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
    [
        <ON> <DUPLICATE> <KEY> <UPDATE>
            id = SimpleIdentifier() {
                targetColumnList.add(id);
            }
            <EQ> AddExpression(sourceExpressionList, ExprContext.ACCEPT_SUB_QUERY)
            (
                <COMMA>
                id = SimpleIdentifier() { targetColumnList.add(id); }
                <EQ> AddExpression(sourceExpressionList, ExprContext.ACCEPT_SUB_QUERY)
            )*
    ]
    {
        return new io.dingodb.calcite.grammar.dml.SqlInsert(s.end(source), keywordList, tableRef, source,
            columnList, sourceExpressionList, targetColumnList);
    }
}

/**
 * Parses a leaf insert into expression.
 */
SqlNode SqlTraceInsert() :
{
    final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
    final SqlNodeList keywordList;
    final SqlIdentifier tableName;
    SqlNode tableRef;
    SqlNode source;
    final SqlNodeList columnList;
    final Span s;
    final Pair<SqlNodeList, SqlNodeList> p;
}
{

    <TRACE><INSERT> { s = span(); }
    SqlInsertKeywords(keywords) {
        keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos());
    }
    <INTO> tableName = CompoundTableIdentifier()
    ( tableRef = TableHints(tableName) | { tableRef = tableName; } )
    [ LOOKAHEAD(5) tableRef = ExtendTable(tableRef) ]
    (
        LOOKAHEAD(2)
        p = ParenthesizedCompoundIdentifierList() {
            if (p.right.size() > 0) {
                tableRef = extend(tableRef, p.right);
            }
            if (p.left.size() > 0) {
                columnList = p.left;
            } else {
                columnList = null;
            }
        }
    |   { columnList = null; }
    )
    source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) {
        return new io.dingodb.calcite.grammar.dml.SqlInsert(s.end(source), keywordList, tableRef, source,
            columnList, true);
    }
}

/*
 * Abstract production:
 *
 *    void SqlInsertKeywords(List keywords)
 *
 * Parses dialect-specific keywords immediately following the INSERT keyword.
 */

/**
 * Parses a DELETE statement.
 */
SqlNode SqlDelete() :
{
    final SqlIdentifier tableName;
    SqlNode tableRef;
    final SqlIdentifier alias;
    final SqlNode where;
    final Span s;
    SqlNode[] offsetFetch = {null, null};
}
{
    <DELETE> {
        s = span();
    }
    <FROM> tableName = CompoundTableIdentifier()
    ( tableRef = TableHints(tableName) | { tableRef = tableName; } )
    [ tableRef = ExtendTable(tableRef) ]
    ( [ <AS> ] alias = SimpleIdentifier() | { alias = null; } )
    ( where = Where() | { where = null; } )
    [ LimitClause(s, offsetFetch) ]
    {
        return new io.dingodb.calcite.grammar.dml.SqlDelete(s.add(tableRef).addIf(alias).addIf(where).pos(),
            tableRef, where, null, alias, offsetFetch);
    }
}

/**
 * Parses an UPDATE statement.
 */
SqlNode SqlUpdate() :
{
    final SqlIdentifier tableName;
    SqlNode tableRef;
    final SqlIdentifier alias;
    final SqlNode where;
    final SqlNodeList sourceExpressionList;
    final SqlNodeList targetColumnList;
    SqlIdentifier id;
    final Span s;
}
{
    <UPDATE> {
        s = span();
        targetColumnList = new SqlNodeList(s.pos());
        sourceExpressionList = new SqlNodeList(s.pos());
    }
    tableName = CompoundTableIdentifier()
    ( tableRef = TableHints(tableName) | { tableRef = tableName; } )
    [ tableRef = ExtendTable(tableRef) ]
    ( [ <AS> ] alias = SimpleIdentifier() | { alias = null; } )
    <SET> id = SimpleIdentifier() {
        targetColumnList.add(id);
    }
    // TODO:  support DEFAULT also
    <EQ> AddExpression(sourceExpressionList, ExprContext.ACCEPT_SUB_QUERY)
    (
        <COMMA>
        id = SimpleIdentifier() { targetColumnList.add(id); }
        <EQ> AddExpression(sourceExpressionList, ExprContext.ACCEPT_SUB_QUERY)
    )*
    ( where = Where() | { where = null; } )
    {
        final SqlParserPos pos = s.addAll(targetColumnList)
            .addAll(sourceExpressionList).addIf(where).pos();
        return new SqlUpdate(pos, tableRef, targetColumnList,
            sourceExpressionList, where, null, alias);
    }
}

/**
 * Parses a MERGE statement.
 */
SqlNode SqlMerge() :
{
    final SqlIdentifier tableName;
    SqlNode tableRef;
    final SqlIdentifier alias;
    final SqlNode sourceTableRef;
    final SqlNode condition;
    final SqlUpdate updateCall;
    final SqlInsert insertCall;
    final Span s;
}
{
    <MERGE> { s = span(); } <INTO> tableName = CompoundTableIdentifier()
    ( tableRef = TableHints(tableName) | { tableRef = tableName; } )
    [ tableRef = ExtendTable(tableRef) ]
    ( [ <AS> ] alias = SimpleIdentifier() | { alias = null; } )
    <USING> sourceTableRef = TableRef()
    <ON> condition = Expression(ExprContext.ACCEPT_SUB_QUERY)
    (
        LOOKAHEAD(2)
        updateCall = WhenMatchedClause(tableRef, alias)
        ( insertCall = WhenNotMatchedClause(tableRef) | { insertCall = null; } )
    |
        { updateCall = null; }
        insertCall = WhenNotMatchedClause(tableRef)
    )
    {
        final SqlParserPos pos = s.addIf(updateCall).addIf(insertCall).pos();
        return new SqlMerge(pos, tableRef, condition, sourceTableRef,
            updateCall, insertCall, null, alias);
    }
}

SqlUpdate WhenMatchedClause(SqlNode table, SqlIdentifier alias) :
{
    SqlIdentifier id;
    final Span s;
    final SqlNodeList updateColumnList = new SqlNodeList(SqlParserPos.ZERO);
    final SqlNodeList updateExprList = new SqlNodeList(SqlParserPos.ZERO);
}
{
    <WHEN> { s = span(); } <MATCHED> <THEN>
    <UPDATE> <SET> id = CompoundIdentifier() {
        updateColumnList.add(id);
    }
    <EQ> AddExpression(updateExprList, ExprContext.ACCEPT_SUB_QUERY)
    (
        <COMMA>
        id = CompoundIdentifier() {
            updateColumnList.add(id);
        }
        <EQ> AddExpression(updateExprList, ExprContext.ACCEPT_SUB_QUERY)
    )*
    {
        return new SqlUpdate(s.addAll(updateExprList).pos(), table,
            updateColumnList, updateExprList, null, null, alias);
    }
}

SqlInsert WhenNotMatchedClause(SqlNode table) :
{
    final Span insertSpan, valuesSpan;
    final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>();
    final SqlNodeList keywordList;
    final SqlNodeList insertColumnList;
    SqlNode rowConstructor;
    SqlNode insertValues;
}
{
    <WHEN> <NOT> <MATCHED> <THEN> <INSERT> {
        insertSpan = span();
    }
    SqlInsertKeywords(keywords) {
        keywordList = new SqlNodeList(keywords, insertSpan.end(this));
    }
    (
        LOOKAHEAD(2)
        insertColumnList = ParenthesizedSimpleIdentifierList()
    |   { insertColumnList = null; }
    )
    (
        <LPAREN>
        <VALUES> { valuesSpan = span(); } rowConstructor = RowConstructor()
        <RPAREN>
    |
        <VALUES> { valuesSpan = span(); } rowConstructor = RowConstructor()
    )
    {
        // TODO zfong 5/26/06: note that extra parentheses are accepted above
        // around the VALUES clause as a hack for unparse, but this is
        // actually invalid SQL; should fix unparse
        insertValues = SqlStdOperatorTable.VALUES.createCall(
            valuesSpan.end(this), rowConstructor);
        return new SqlInsert(insertSpan.end(this), keywordList,
            table, insertValues, insertColumnList);
    }
}

/**
 * Parses one item in a select list.
 */
void AddSelectItem(List<SqlNode> list) :
{
    final SqlNode e;
    final SqlIdentifier id;
}
{
    e = SelectExpression()
    (
        [ <AS> ]
        (
            id = SimpleIdentifier()
        |
            // Mute the warning about ambiguity between alias and continued
            // string literal.
            LOOKAHEAD(1)
            id = SimpleIdentifierFromStringLiteral()
        )
        { list.add(SqlStdOperatorTable.AS.createCall(span().end(e), e, id)); }
    |   { list.add(e); }
    )
}

/**
 * Parses one unaliased expression in a select list.
 */
SqlNode SelectExpression() :
{
    SqlNode e;
}
{
    <STAR> {
        return SqlIdentifier.star(getPos());
    }
|
    e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
        return e;
    }
}

SqlLiteral Natural() :
{
}
{
    <NATURAL> { return SqlLiteral.createBoolean(true, getPos()); }
|
    { return SqlLiteral.createBoolean(false, getPos()); }
}

SqlLiteral JoinType() :
{
    JoinType joinType;
}
{
    (
    LOOKAHEAD(3) // required for "LEFT SEMI JOIN" in Babel
<#list (parser.joinTypes!) as method>
        joinType = ${method}()
    |
</#list>
        <JOIN> { joinType = JoinType.INNER; }
    |
        <INNER> <JOIN> { joinType = JoinType.INNER; }
    |
        <LEFT> [ <OUTER> ] <JOIN> { joinType = JoinType.LEFT; }
    |
        <RIGHT> [ <OUTER> ] <JOIN> { joinType = JoinType.RIGHT; }
    |
        <FULL> [ <OUTER> ] <JOIN> { joinType = JoinType.FULL; }
    |
        <CROSS> <JOIN> { joinType = JoinType.CROSS; }
    )
    {
        return joinType.symbol(getPos());
    }
}

/**
 * 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;
    SqlLiteral joinType;
}
{
    e = Join()
    (
        // Comma joins should only occur at top-level in the FROM clause.
        // Valid:
        //  * FROM a, b
        //  * FROM (a CROSS JOIN b), c
        // Not valid:
        //  * FROM a CROSS JOIN (b, c)
        LOOKAHEAD(1)
        <COMMA> { joinType = JoinType.COMMA.symbol(getPos()); }
        e2 = Join() {
            e = new SqlJoin(joinType.getParserPosition(),
                e,
                SqlLiteral.createBoolean(false, joinType.getParserPosition()),
                joinType,
                e2,
                JoinConditionType.NONE.symbol(SqlParserPos.ZERO),
                null);
        }
    )*
    { return e; }
}

SqlNode Join() :
{
    SqlNode e;
}
{
    e = TableRef1(ExprContext.ACCEPT_QUERY_OR_JOIN)
    (
        LOOKAHEAD(2)
        e = JoinTable(e)
    )*
    {
        return e;
    }
}

/** Matches "LEFT JOIN t ON ...", "RIGHT JOIN t USING ...", "JOIN t". */
SqlNode JoinTable(SqlNode e) :
{
    SqlNode e2, condition;
    final SqlLiteral natural, joinType, on, using;
    SqlNodeList list;
}
{
    // LOOKAHEAD(3) is needed here rather than a LOOKAHEAD(2) because JavaCC
    // calculates minimum lookahead count incorrectly for choice that contains
    // zero size child. For instance, with the generated code,
    // "LOOKAHEAD(2, Natural(), JoinType())"
    // returns true immediately if it sees a single "<CROSS>" token. Where we
    // expect the lookahead succeeds after "<CROSS> <APPLY>".
    //
    // For more information about the issue,
    // see https://github.com/javacc/javacc/issues/86
    //
    // We allow CROSS JOIN (joinType = CROSS_JOIN) to have a join condition,
    // even though that is not valid SQL; the validator will catch it.
    LOOKAHEAD(3)
    natural = Natural()
    joinType = JoinType()
    e2 = TableRef1(ExprContext.ACCEPT_QUERY_OR_JOIN)
    (
        <ON> { on = JoinConditionType.ON.symbol(getPos()); }
        condition = Expression(ExprContext.ACCEPT_SUB_QUERY) {
            return new SqlJoin(joinType.getParserPosition(),
                e,
                natural,
                joinType,
                e2,
                on,
                condition);
        }
    |
        <USING> { using = JoinConditionType.USING.symbol(getPos()); }
        list = ParenthesizedSimpleIdentifierList() {
            return new SqlJoin(joinType.getParserPosition(),
                e,
                natural,
                joinType,
                e2,
                using,
                new SqlNodeList(list, Span.of(using).end(this)));
        }
    |
        {
            return new SqlJoin(joinType.getParserPosition(),
                e,
                natural,
                joinType,
                e2,
                JoinConditionType.NONE.symbol(joinType.getParserPosition()),
                null);
        }
    )
|
    <CROSS> { joinType = JoinType.CROSS.symbol(getPos()); } <APPLY>
    e2 = TableRef2(true) {
        if (!this.conformance.isApplyAllowed()) {
            throw SqlUtil.newContextException(getPos(), RESOURCE.applyNotAllowed());
        }
        return new SqlJoin(joinType.getParserPosition(),
            e,
            SqlLiteral.createBoolean(false, joinType.getParserPosition()),
            joinType,
            e2,
            JoinConditionType.NONE.symbol(SqlParserPos.ZERO),
            null);
    }
|
    <OUTER> { joinType = JoinType.LEFT.symbol(getPos()); } <APPLY>
    e2 = TableRef2(true) {
        if (!this.conformance.isApplyAllowed()) {
            throw SqlUtil.newContextException(getPos(), RESOURCE.applyNotAllowed());
        }
        return new SqlJoin(joinType.getParserPosition(),
            e,
            SqlLiteral.createBoolean(false, joinType.getParserPosition()),
            joinType,
            e2,
            JoinConditionType.ON.symbol(SqlParserPos.ZERO),
            SqlLiteral.createBoolean(true, joinType.getParserPosition()));
    }
}

/**
 * Parses a table reference in a FROM clause, not lateral unless LATERAL
 * is explicitly specified.
 */
SqlNode TableRef() :
{
    final SqlNode e;
}
{
    e = TableRef3(ExprContext.ACCEPT_QUERY, false) { return e; }
}

SqlNode TableRef1(ExprContext exprContext) :
{
    final SqlNode e;
}
{
    e = TableRef3(exprContext, false) { return e; }
}

/**
 * Parses a table reference in a FROM clause.
 */
SqlNode TableRef2(boolean lateral) :
{
    final SqlNode e;
}
{
    e = TableRef3(ExprContext.ACCEPT_QUERY, lateral) { return e; }
}

SqlNode TableRef3(ExprContext exprContext, boolean lateral) :
{
    final SqlIdentifier tableName;
    SqlNode tableRef;
    final SqlIdentifier alias;
    final Span s;
    SqlNodeList args;
    SqlNodeList columnAliasList = null;
    SqlUnnestOperator unnestOp = SqlStdOperatorTable.UNNEST;
    String flashBackStr = null;
    String flashBackTsoStr = null;
    boolean flashBack = false;
}
{
    (
        LOOKAHEAD(2)
        tableName = CompoundTableIdentifier()
        ( tableRef = TableHints(tableName) | { tableRef = tableName; } )
        [ tableRef = ExtendTable(tableRef) ]
        tableRef = Over(tableRef)
        [ tableRef = Snapshot(tableRef) ]
        [ tableRef = MatchRecognize(tableRef) ]
    |
        LOOKAHEAD(2)
        [ <LATERAL> { lateral = true; } ]
        tableRef = ParenthesizedExpression(exprContext)
        tableRef = Over(tableRef)
        tableRef = addLateral(tableRef, lateral)
        [ tableRef = MatchRecognize(tableRef) ]
    |
        <UNNEST> { s = span(); }
        args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY)
        [
            <WITH> <ORDINALITY> {
                unnestOp = SqlStdOperatorTable.UNNEST_WITH_ORDINALITY;
            }
        ]
        {
            tableRef = unnestOp.createCall(s.end(this), (List<SqlNode>) args);
        }
    |
        [ <LATERAL> { lateral = true; } ]
        <#list (parser.functionCallMethods!) as method>
            tableRef = ${method}()
        </#list>
        tableRef = addLateral(tableRef, lateral)
    |
        tableRef = ExtendedTableRef()
    )
    [
        LOOKAHEAD(2)
        tableRef = Pivot(tableRef)
    ]
    [
        LOOKAHEAD(2)
        tableRef = Unpivot(tableRef)
    ]
    [
        [ <AS> ]
        (
          alias = SimpleIdentifier()
         |
          <OF> (
                <TIMESTAMP> <QUOTED_STRING> { flashBackStr = token.image.replace("'", ""); }
                |
                <TSO> <UNSIGNED_INTEGER_LITERAL> { flashBackTsoStr = token.image; }
               )
          { alias = null; flashBack=true; }
        )
        (
            columnAliasList = ParenthesizedSimpleIdentifierList()
         |
           { columnAliasList = null; }
        )
        {
            if (!flashBack) {
            // Standard SQL (and Postgres) allow applying "AS alias" to a JOIN,
            // e.g. "FROM (a CROSS JOIN b) AS c". The new alias obscures the
            // internal aliases, and columns cannot be referenced if they are
            // not unique. TODO: Support this behavior; see
            // [CALCITE-5168] Allow AS after parenthesized JOIN
            checkNotJoin(tableRef);
            if (columnAliasList == null) {
                tableRef = SqlStdOperatorTable.AS.createCall(
                    Span.of(tableRef).end(this), tableRef, alias);
            } else {
                List<SqlNode> idList = new ArrayList<SqlNode>();
                idList.add(tableRef);
                idList.add(alias);
                idList.addAll(columnAliasList.getList());
                tableRef = SqlStdOperatorTable.AS.createCall(
                    Span.of(tableRef).end(this), idList);
            }
            } else if (tableRef instanceof SqlIdentifier) {
              SqlIdentifier ident = (SqlIdentifier)tableRef;
              tableRef = new FlashBackSqlIdentifier(ident.names, ident.getParserPosition(), flashBackStr, flashBackTsoStr);
            }
        }
    ]
    [ tableRef = Tablesample(tableRef) ]
    { return tableRef; }
}

SqlNode Tablesample(SqlNode tableRef) :
{
    final Span s;
    final SqlNode sample;
    final boolean isBernoulli;
    final SqlNumericLiteral samplePercentage;
    boolean isRepeatable = false;
    int repeatableSeed = 0;
}
{
    <TABLESAMPLE> { s = span(); checkNotJoin(tableRef); }
    (
        <SUBSTITUTE> <LPAREN> sample = StringLiteral() <RPAREN>
        {
            String sampleName =
                SqlLiteral.unchain(sample).getValueAs(String.class);
            SqlSampleSpec sampleSpec = SqlSampleSpec.createNamed(sampleName);
            final SqlLiteral sampleLiteral =
                SqlLiteral.createSample(sampleSpec, s.end(this));
            tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall(
                s.add(tableRef).end(this), tableRef, sampleLiteral);
        }
    |
        (
            <BERNOULLI> { isBernoulli = true; }
        |
            <SYSTEM> { isBernoulli = false; }
        )
        <LPAREN> samplePercentage = UnsignedNumericLiteral() <RPAREN>
        [
            <REPEATABLE> <LPAREN> repeatableSeed = IntLiteral() <RPAREN>
            {
                isRepeatable = true;
            }
        ]
        {
            final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100L);
            BigDecimal rate = samplePercentage.bigDecimalValue();
            if (rate.compareTo(BigDecimal.ZERO) < 0
                || rate.compareTo(ONE_HUNDRED) > 0)
            {
                throw SqlUtil.newContextException(getPos(), RESOURCE.invalidSampleSize());
            }

            // Treat TABLESAMPLE(0) and TABLESAMPLE(100) as no table
            // sampling at all.  Not strictly correct: TABLESAMPLE(0)
            // should produce no output, but it simplifies implementation
            // to know that some amount of sampling will occur.
            // In practice values less than ~1E-43% are treated as 0.0 and
            // values greater than ~99.999997% are treated as 1.0
            float fRate = rate.divide(ONE_HUNDRED).floatValue();
            if (fRate > 0.0f && fRate < 1.0f) {
                SqlSampleSpec tableSampleSpec =
                    isRepeatable
                        ? SqlSampleSpec.createTableSample(
                            isBernoulli, fRate, repeatableSeed)
                        : SqlSampleSpec.createTableSample(isBernoulli, fRate);

                SqlLiteral tableSampleLiteral =
                    SqlLiteral.createSample(tableSampleSpec, s.end(this));
                tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall(
                    s.end(this), tableRef, tableSampleLiteral);
            }
        }
    )
    { return tableRef; }
}

/** Wraps a table reference in a call to EXTEND if an optional "EXTEND" clause
 * is present. */
SqlNode ExtendTable(SqlNode tableRef) :
{
    final SqlNodeList extendList;
}
{
    [ <EXTEND> ]
    extendList = ExtendList() {
        return extend(tableRef, extendList);
    }
}

SqlNodeList ExtendList() :
{
    final Span s;
    List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    <LPAREN> { s = span(); }
    AddColumnType(list)
    (
        <COMMA> AddColumnType(list)
    )*
    <RPAREN> {
        return new SqlNodeList(list, s.end(this));
    }
}

void AddColumnType(List<SqlNode> list) :
{
    final SqlIdentifier name;
    final SqlDataTypeSpec type;
    final boolean nullable;
}
{
    name = CompoundIdentifier()
    type = DataType()
    nullable = NotNullOpt()
    {
        list.add(name);
        list.add(type.withNullable(nullable, getPos()));
    }
}

/**
 * Parses a compound identifier with optional type.
 */
void AddCompoundIdentifierType(List<SqlNode> list, List<SqlNode> extendList) :
{
    final SqlIdentifier name;
    final SqlDataTypeSpec type;
    final boolean nullable;
}
{
    name = CompoundIdentifier()
    (
        type = DataType()
        nullable = NotNullOpt()
    |
        { type = null; nullable = true; }
    )
    {
        if (type != null) {
            if (!this.conformance.allowExtend()) {
                throw SqlUtil.newContextException(type.getParserPosition(),
                    RESOURCE.extendNotAllowed());
            }
            extendList.add(name);
            extendList.add(type.withNullable(nullable, getPos()));
        }
        list.add(name);
    }
}

/**
 * Abstract production:
 *    SqlNode ExtendedTableRef()
 *
 * <p>Allows parser to be extended with new types of table references.  The
 * default implementation of this production is empty.
 */

/*
 * Abstract production:
 *
 *    SqlNode TableOverOpt()
 *
 * Allows an OVER clause following a table expression as an extension to
 * standard SQL syntax. The default implementation of this production is empty.
 */

/**
 * Parses an explicit TABLE t reference.
 */
SqlNode ExplicitTable(SqlParserPos pos) :
{
    SqlNode tableRef;
}
{
    <TABLE> tableRef = CompoundIdentifier()
    {
        return SqlStdOperatorTable.EXPLICIT_TABLE.createCall(pos, tableRef);
    }
}

/**
 * Parses a VALUES leaf query expression.
 */
SqlNode TableConstructor() :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
    final Span s;
}
{
    (
        <VALUES> { s = span(); }
    |
        <VALUE>
        {
            s = span();
            if (!this.conformance.isValueAllowed()) {
                throw SqlUtil.newContextException(getPos(), RESOURCE.valueNotAllowed());
            }
        }
    )
    AddRowConstructor(list)
    (
        LOOKAHEAD(2)
        <COMMA> AddRowConstructor(list)
    )*
    {
        return SqlStdOperatorTable.VALUES.createCall(s.end(this), list);
    }
}

/** Parses a row constructor and adds it to a list. */
void AddRowConstructor(List<SqlNode> list) :
{
    SqlNode e;
}
{
    e = RowConstructor() { list.add(e); }
}

/**
 * Parses a row constructor in the context of a VALUES expression.
 */
SqlNode RowConstructor() :
{
    final SqlNodeList valueList;
    final SqlNode value;
    final Span s;
}
{
    // hints are necessary here due to common LPAREN prefixes
    (
        // TODO jvs 8-Feb-2004: extra parentheses are accepted here as a hack
        // for unparse, but this is actually invalid SQL; should
        // fix unparse
        LOOKAHEAD(3)
        <LPAREN> { s = span(); }
        <ROW>
        valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR)
        <RPAREN> { s.add(this); }
    |
        LOOKAHEAD(3)
        (
            <ROW> { s = span(); }
        |
            { s = Span.of(); }
        )
        valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR)
    |
        value = Expression(ExprContext.ACCEPT_NONCURSOR)
        {
            // NOTE: A bare value here is standard SQL syntax, believe it or
            // not.  Taken together with multi-row table constructors, it leads
            // to very easy mistakes if you forget the parentheses on a
            // single-row constructor.  This is also the reason for the
            // LOOKAHEAD in TableConstructor().  It would be so much more
            // reasonable to require parentheses.  Sigh.
            s = Span.of(value);
            valueList = new SqlNodeList(ImmutableList.of(value),
                value.getParserPosition());
        }
    )
    {
        // REVIEW jvs 8-Feb-2004: Should we discriminate between scalar
        // sub-queries inside of ROW and row sub-queries?  The standard does,
        // but the distinction seems to be purely syntactic.
        return SqlStdOperatorTable.ROW.createCall(s.end(valueList),
            (List<SqlNode>) valueList);
    }
}

/** Parses a WHERE clause for SELECT, DELETE, and UPDATE. */
SqlNode Where() :
{
    SqlNode condition;
}
{
    <WHERE> condition = Expression(ExprContext.ACCEPT_SUB_QUERY) {
        return condition;
    }
}

/** Parses a GROUP BY clause for SELECT. */
SqlNodeList GroupBy() :
{
    final List<SqlNode> list;
    final boolean distinct;
    final Span s;
}
{
    <GROUP> { s = span(); }
    <BY>
    (
        <DISTINCT> { distinct = true; }
    |   <ALL> { distinct = false; }
    |   { distinct = false; }
    )
    list = GroupingElementList() {
        final SqlParserPos pos = s.end(this);
        final List<SqlNode> list2 = distinct
            ? ImmutableList.of(
                SqlInternalOperators.GROUP_BY_DISTINCT.createCall(pos, list))
            : list;
        return new SqlNodeList(list2, pos);
    }
}

List<SqlNode> GroupingElementList() :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    AddGroupingElement(list)
    ( LOOKAHEAD(2) <COMMA> AddGroupingElement(list) )*
    { return list; }
}

void AddGroupingElement(List<SqlNode> list) :
{
    final List<SqlNode> subList;
    final SqlNodeList nodes;
    final Span s;
}
{
    LOOKAHEAD(2)
    <GROUPING> { s = span(); }
    <SETS> <LPAREN> subList = GroupingElementList() <RPAREN> {
        list.add(
            SqlStdOperatorTable.GROUPING_SETS.createCall(s.end(this), subList));
    }
|   <ROLLUP> { s = span(); }
    <LPAREN> nodes = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY)
    <RPAREN> {
        list.add(
            SqlStdOperatorTable.ROLLUP.createCall(s.end(this), nodes.getList()));
    }
|   <CUBE> { s = span(); }
    <LPAREN> nodes = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY)
    <RPAREN> {
        list.add(
            SqlStdOperatorTable.CUBE.createCall(s.end(this), nodes.getList()));
    }
|   LOOKAHEAD(3)
    <LPAREN> { s = span(); } <RPAREN> {
        list.add(new SqlNodeList(s.end(this)));
    }
|   AddExpression(list, ExprContext.ACCEPT_SUB_QUERY)
}

/**
 * Parses a list of expressions separated by commas.
 */
SqlNodeList ExpressionCommaList(
    final Span s,
    ExprContext exprContext) :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    AddExpressions(list, exprContext) {
        return new SqlNodeList(list, s.addAll(list).pos());
    }
}

/**
 * Parses a list of expressions separated by commas,
 * appending expressions to a given list.
 */
void AddExpressions(List<SqlNode> list, ExprContext exprContext) :
{
}
{
    AddExpression(list, exprContext)
    (
        // NOTE jvs 6-Feb-2004:  See comments at top of file for why
        // hint is necessary here.
        LOOKAHEAD(2)
        <COMMA> AddExpression(list, ExprContext.ACCEPT_SUB_QUERY)
    )*
}

/** Parses a HAVING clause for SELECT. */
SqlNode Having() :
{
    SqlNode e;
}
{
    <HAVING> e = Expression(ExprContext.ACCEPT_SUB_QUERY) { return e; }
}

/** Parses a WINDOW clause for SELECT. */
SqlNodeList Window() :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
    final Span s;
}
{
    <WINDOW> { s = span(); }
    AddWindowSpec(list)
    (
        LOOKAHEAD(2)
        <COMMA> AddWindowSpec(list)
    )*
    {
        return new SqlNodeList(list, s.addAll(list).pos());
    }
}

void AddWindowSpec(List<SqlNode> list) :
{
    final SqlIdentifier id;
    final SqlWindow e;
}
{
    id = SimpleIdentifier() <AS> e = WindowSpecification() {
        e.setDeclName(id);
        list.add(e);
    }
}

/**
 * Parses a window specification.
 */
SqlWindow WindowSpecification() :
{
    final SqlIdentifier id;
    final SqlNodeList partitionList;
    final SqlNodeList orderList;
    final SqlLiteral isRows;
    final SqlNode lowerBound, upperBound;
    final Span s, s1, s2;
    final SqlLiteral allowPartial;
}
{
    <LPAREN> { s = span(); }
    (
        id = SimpleIdentifier()
    |   { id = null; }
    )
    (
        <PARTITION> { s1 = span(); }
        <BY>
        partitionList = ExpressionCommaList(s1, ExprContext.ACCEPT_NON_QUERY)
    |   { partitionList = SqlNodeList.EMPTY; }
    )
    (
        orderList = OrderBy(true)
    |   { orderList = SqlNodeList.EMPTY; }
    )
    (
        (
            <ROWS> { isRows = SqlLiteral.createBoolean(true, getPos()); }
        |
            <RANGE> { isRows = SqlLiteral.createBoolean(false, getPos()); }
        )
        (
            <BETWEEN> lowerBound = WindowRange()
            <AND> upperBound = WindowRange()
        |
            lowerBound = WindowRange()
            { upperBound = null; }
        )
    |
        {
            isRows = SqlLiteral.createBoolean(false, SqlParserPos.ZERO);
            lowerBound = upperBound = null;
        }
    )
    (
        <ALLOW> { s2 = span(); } <PARTIAL> {
            allowPartial = SqlLiteral.createBoolean(true, s2.end(this));
        }
    |
        <DISALLOW> { s2 = span(); } <PARTIAL> {
            allowPartial = SqlLiteral.createBoolean(false, s2.end(this));
        }
    |   { allowPartial = null; }
    )
    <RPAREN>
    {
        return SqlWindow.create(null, id, partitionList, orderList,
            isRows, lowerBound, upperBound, allowPartial, s.end(this));
    }
}

SqlNode WindowRange() :
{
    final SqlNode e;
    final Span s;
}
{
    LOOKAHEAD(2)
    <CURRENT> { s = span(); } <ROW> {
        return SqlWindow.createCurrentRow(s.end(this));
    }
|
    LOOKAHEAD(2)
    <UNBOUNDED> { s = span(); }
    (
        <PRECEDING> {
            return SqlWindow.createUnboundedPreceding(s.end(this));
        }
    |
        <FOLLOWING> {
            return SqlWindow.createUnboundedFollowing(s.end(this));
        }
    )
|
    e = Expression(ExprContext.ACCEPT_NON_QUERY)
    (
        <PRECEDING> {
            return SqlWindow.createPreceding(e, getPos());
        }
    |
        <FOLLOWING> {
            return SqlWindow.createFollowing(e, getPos());
        }
    )
}

/**
 * Parses an ORDER BY clause.
 */
SqlNodeList OrderBy(boolean accept) :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
    final Span s;
}
{
    <ORDER> {
        s = span();
        if (!accept) {
            // Someone told us ORDER BY wasn't allowed here.  So why
            // did they bother calling us?  To get the correct
            // parser position for error reporting.
            throw SqlUtil.newContextException(s.pos(), RESOURCE.illegalOrderBy());
        }
    }
    <BY> AddOrderItem(list)
    (
        // NOTE jvs 6-Feb-2004:  See comments at top of file for why
        // hint is necessary here.
        LOOKAHEAD(2) <COMMA> AddOrderItem(list)
    )*
    {
        return new SqlNodeList(list, s.addAll(list).pos());
    }
}

/**
 * Parses one item in an ORDER BY clause, and adds it to a list.
 */
void AddOrderItem(List<SqlNode> list) :
{
    SqlNode e;
}
{
    e = Expression(ExprContext.ACCEPT_SUB_QUERY)
    (
        <ASC>
    |   <DESC> {
            e = SqlStdOperatorTable.DESC.createCall(getPos(), e);
        }
    )?
    (
        LOOKAHEAD(2)
        <NULLS> <FIRST> {
            e = SqlStdOperatorTable.NULLS_FIRST.createCall(getPos(), e);
        }
    |
        <NULLS> <LAST> {
            e = SqlStdOperatorTable.NULLS_LAST.createCall(getPos(), e);
        }
    )?
    {
        list.add(e);
    }
}

/** Wraps a table reference in a call to OVER if an optional "OVER" clause
 * is present (if the dialect supports OVER for table expressions). */
SqlNode Over(SqlNode tableRef) :
{
    final SqlNode over;
}
{
    over = TableOverOpt() {
        if (over != null) {
            return SqlStdOperatorTable.OVER.createCall(
                getPos(), checkNotJoin(tableRef), over);
        } else {
            return tableRef;
        }
    }
}

/** Wraps a table reference in a call to LATERAL if {@code lateral} is true. */
JAVACODE SqlNode addLateral(SqlNode tableRef, boolean lateral) {
    return lateral
        ? SqlStdOperatorTable.LATERAL.createCall(getPos(),
            checkNotJoin(tableRef))
        : tableRef;
}

/**
 * Parses a FOR SYSTEM_TIME clause following a table expression.
 */
SqlSnapshot Snapshot(SqlNode tableRef) :
{
    final Span s;
    final SqlNode e;
}
{
    { s = span(); } <FOR> <SYSTEM_TIME> <AS> <OF>
    // Syntax for temporal table in
    // standard SQL 2011 IWD 9075-2:201?(E) 7.6 <table reference>
    // supports grammar as following:
    // 1. datetime literal
    // 2. datetime value function, i.e. CURRENT_TIMESTAMP
    // 3. datetime term in 1 or 2 +(or -) interval term

    // We extend to support column reference, use Expression
    // to simplify the parsing code.
    e = Expression(ExprContext.ACCEPT_NON_QUERY) {
        return new SqlSnapshot(s.end(this), tableRef, e);
    }
}

/** Parses a PIVOT clause following a table expression. */
SqlNode Pivot(SqlNode tableRef) :
{
    final Span s;
    final Span s2;
    final List<SqlNode> aggList = new ArrayList<SqlNode>();
    final List<SqlNode> valueList = new ArrayList<SqlNode>();
    final SqlNodeList axisList;
    final SqlNodeList inList;
}
{
    <PIVOT> { s = span(); checkNotJoin(tableRef); }
    <LPAREN>
    AddPivotAgg(aggList) ( <COMMA> AddPivotAgg(aggList) )*
    <FOR> axisList = SimpleIdentifierOrList()
    <IN> <LPAREN> { s2 = span(); }
    [ AddPivotValue(valueList) ( <COMMA> AddPivotValue(valueList) )* ]
    <RPAREN> {
        inList = new SqlNodeList(valueList, s2.end(this));
    }
    <RPAREN>
    {
        return new SqlPivot(s.end(this), tableRef,
            new SqlNodeList(aggList, SqlParserPos.sum(aggList)),
            axisList, inList);
    }
}

void AddPivotAgg(List<SqlNode> list) :
{
    final SqlNode e;
    final SqlIdentifier alias;
}
{
    e = NamedFunctionCall()
    (
        // Because babel put FOR into non-reserved keyword set.
        LOOKAHEAD({getToken(1).kind != COMMA && getToken(1).kind != FOR})
        [ <AS> ] alias = SimpleIdentifier() {
            list.add(
                SqlStdOperatorTable.AS.createCall(Span.of(e).end(this), e,
                    alias));
        }
    |
        { list.add(e); }
    )
}

void AddPivotValue(List<SqlNode> list) :
{
    final SqlNode e;
    final SqlNodeList tuple;
    final SqlIdentifier alias;
}
{
    e = RowConstructor() { tuple = SqlParserUtil.stripRow(e); }
    (
        [ <AS> ] alias = SimpleIdentifier() {
            list.add(
                SqlStdOperatorTable.AS.createCall(Span.of(tuple).end(this),
                    tuple, alias));
        }
    |
        { list.add(tuple); }
    )
}

/** Parses an UNPIVOT clause following a table expression. */
SqlNode Unpivot(SqlNode tableRef) :
{
    final Span s;
    final boolean includeNulls;
    final SqlNodeList measureList;
    final SqlNodeList axisList;
    final Span s2;
    final List<SqlNode> values = new ArrayList<SqlNode>();
    final SqlNodeList inList;
}
{
    <UNPIVOT> { s = span(); checkNotJoin(tableRef); }
    (
        <INCLUDE> <NULLS> { includeNulls = true; }
    |   <EXCLUDE> <NULLS> { includeNulls = false; }
    |   { includeNulls = false; }
    )
    <LPAREN>
    measureList = SimpleIdentifierOrList()
    <FOR> axisList = SimpleIdentifierOrList()
    <IN>
    <LPAREN> { s2 = span(); }
    AddUnpivotValue(values) ( <COMMA> AddUnpivotValue(values) )*
    <RPAREN>
    { inList = new SqlNodeList(values, s2.end(this)); }
    <RPAREN> {
        return new SqlUnpivot(s.end(this), tableRef, includeNulls, measureList,
            axisList, inList);
    }
}

void AddUnpivotValue(List<SqlNode> list) :
{
    final SqlNodeList columnList;
    final SqlNode values;
}
{
    columnList = SimpleIdentifierOrList()
    (
        <AS> values = RowConstructor() {
            final SqlNodeList valueList = SqlParserUtil.stripRow(values);
            list.add(
                SqlStdOperatorTable.AS.createCall(Span.of(columnList).end(this),
                    columnList, valueList));
        }
    |
        { list.add(columnList); }
    )
}

/**
 * Parses a MATCH_RECOGNIZE clause following a table expression.
 */
SqlMatchRecognize MatchRecognize(SqlNode tableRef) :
{
    final Span s, s0, s1, s2;
    final SqlNodeList measureList;
    final SqlNodeList partitionList;
    final SqlNodeList orderList;
    final SqlNode pattern;
    final SqlLiteral interval;
    final SqlNodeList patternDefList;
    final SqlNode after;
    final SqlNode var;
    final SqlLiteral rowsPerMatch;
    final SqlNodeList subsetList;
    final SqlLiteral isStrictStarts;
    final SqlLiteral isStrictEnds;
}
{
    <MATCH_RECOGNIZE> { s = span(); checkNotJoin(tableRef); } <LPAREN>
    (
        <PARTITION> { s2 = span(); } <BY>
        partitionList = ExpressionCommaList(s2, ExprContext.ACCEPT_NON_QUERY)
    |
        { partitionList = SqlNodeList.EMPTY; }
    )
    (
        orderList = OrderBy(true)
    |
        { orderList = SqlNodeList.EMPTY; }
    )
    (
        <MEASURES>
        measureList = MeasureColumnCommaList(span())
    |
        { measureList = SqlNodeList.EMPTY; }
    )
    (
        <ONE> { s0 = span(); } <ROW> <PER> <MATCH> {
            rowsPerMatch = SqlMatchRecognize.RowsPerMatchOption.ONE_ROW.symbol(s0.end(this));
        }
    |
        <ALL> { s0 = span(); } <ROWS> <PER> <MATCH> {
            rowsPerMatch = SqlMatchRecognize.RowsPerMatchOption.ALL_ROWS.symbol(s0.end(this));
        }
    |   { rowsPerMatch = null; }
    )
    (
        <AFTER> { s1 = span(); } <MATCH> <SKIP_>
        (
            <TO>
            (
                LOOKAHEAD(2)
                <NEXT> <ROW> {
                    after = SqlMatchRecognize.AfterOption.SKIP_TO_NEXT_ROW
                        .symbol(s1.end(this));
                }
            |
                LOOKAHEAD(2)
                <FIRST> var = SimpleIdentifier() {
                    after = SqlMatchRecognize.SKIP_TO_FIRST.createCall(
                        s1.end(var), var);
                }
            |
                // This "LOOKAHEAD({true})" is a workaround for Babel.
                // Because of babel parser uses option "LOOKAHEAD=2" globally,
                // JavaCC generates something like "LOOKAHEAD(2, [<LAST>] SimpleIdentifier())"
                // here. But the correct LOOKAHEAD should be
                // "LOOKAHEAD(2, [ LOOKAHEAD(2, <LAST> SimpleIdentifier()) <LAST> ]
                // SimpleIdentifier())" which have the syntactic lookahead for <LAST> considered.
                //
                // Overall LOOKAHEAD({true}) is even better as this is the last branch in the
                // choice.
                LOOKAHEAD({true})
                [ LOOKAHEAD(2, <LAST> SimpleIdentifier()) <LAST> ] var = SimpleIdentifier() {
                    after = SqlMatchRecognize.SKIP_TO_LAST.createCall(
                        s1.end(var), var);
                }
            )
        |
            <PAST> <LAST> <ROW> {
                 after = SqlMatchRecognize.AfterOption.SKIP_PAST_LAST_ROW
                     .symbol(s1.end(this));
            }
        )
    |   { after = null; }
    )
    <PATTERN>
    <LPAREN>
    (
        <CARET> { isStrictStarts = SqlLiteral.createBoolean(true, getPos()); }
    |   { isStrictStarts = SqlLiteral.createBoolean(false, getPos()); }
    )
    pattern = PatternExpression()
    (
        <DOLLAR> { isStrictEnds = SqlLiteral.createBoolean(true, getPos()); }
    |   { isStrictEnds = SqlLiteral.createBoolean(false, getPos()); }
    )
    <RPAREN>
    (
        <WITHIN> interval = IntervalLiteral()
    |   { interval = null; }
    )
    (
        <SUBSET> subsetList = SubsetDefinitionCommaList(span())
    |   { subsetList = SqlNodeList.EMPTY; }
    )
    <DEFINE>
    patternDefList = PatternDefinitionCommaList(span())
    <RPAREN> {
        return new SqlMatchRecognize(s.end(this), tableRef,
            pattern, isStrictStarts, isStrictEnds, patternDefList, measureList,
            after, subsetList, rowsPerMatch, partitionList, orderList, interval);
    }
}

SqlNodeList MeasureColumnCommaList(Span s) :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    AddMeasureColumn(list)
    ( <COMMA> AddMeasureColumn(list) )*
    { return new SqlNodeList(list, s.addAll(list).pos()); }
}

void AddMeasureColumn(List<SqlNode> list) :
{
    final SqlNode e;
    final SqlIdentifier alias;
}
{
    e = Expression(ExprContext.ACCEPT_NON_QUERY)
    <AS>
    alias = SimpleIdentifier() {
        list.add(SqlStdOperatorTable.AS.createCall(Span.of(e).end(this), e, alias));
    }
}

SqlNode PatternExpression() :
{
    SqlNode left;
    SqlNode right;
}
{
    left = PatternTerm()
    (
        <VERTICAL_BAR>
        right = PatternTerm() {
            left = SqlStdOperatorTable.PATTERN_ALTER.createCall(
                Span.of(left).end(right), left, right);
        }
    )*
    {
        return left;
    }
}

SqlNode PatternTerm() :
{
    SqlNode left;
    SqlNode right;
}
{
    left = PatternFactor()
    (
        right = PatternFactor() {
            left = SqlStdOperatorTable.PATTERN_CONCAT.createCall(
                Span.of(left).end(right), left, right);
        }
    )*
    {
        return left;
    }
}

SqlNode PatternFactor() :
{
    SqlNode e = null;
    SqlNode extra = null;
    SqlLiteral startNum = null;
    SqlLiteral endNum = null;
    SqlLiteral reluctant = null;
}
{
    e = PatternPrimary()
    (
        LOOKAHEAD(1)
        (
            <STAR> {
                startNum = LITERAL_ZERO;
                endNum = LITERAL_MINUS_ONE;
            }
        |
            <PLUS> {
                startNum = LITERAL_ONE;
                endNum = LITERAL_MINUS_ONE;
            }
        |
            <HOOK> {
                startNum = LITERAL_ZERO;
                endNum = LITERAL_ONE;
            }
        |
            <LBRACE>
            (
                startNum = UnsignedNumericLiteral()
                (
                    <COMMA>
                    (
                        endNum = UnsignedNumericLiteral()
                    |
                        { endNum = LITERAL_MINUS_ONE; }
                    )
                |
                     { endNum = startNum; }
                )
                <RBRACE>
            |
                <COMMA>
                endNum = UnsignedNumericLiteral()
                <RBRACE>
                { startNum = LITERAL_MINUS_ONE; }
            |
                <MINUS> extra = PatternExpression() <MINUS> <RBRACE> {
                    return SqlStdOperatorTable.PATTERN_CONCAT.createCall(
                        Span.of(e).end(this), e,
                        SqlStdOperatorTable.PATTERN_EXCLUDE.createCall(
                            Span.of(extra).end(this), extra));
                }
            )
        )
        (
            <HOOK> {
                reluctant = SqlLiteral.createBoolean(
                    startNum.intValue(true) != endNum.intValue(true),
                    SqlParserPos.ZERO);
            }
        |
            { reluctant = SqlLiteral.createBoolean(false, SqlParserPos.ZERO); }
        )
    |
        { return e; }
    )
    {
        return SqlStdOperatorTable.PATTERN_QUANTIFIER.createCall(
            span().end(e), e, startNum, endNum, reluctant);
    }
}

SqlNode PatternPrimary() :
{
    final Span s;
    SqlNode e;
    final List<SqlNode> list;
}
{
    e = SimpleIdentifier() { return e; }
|
    <LPAREN> e = PatternExpression() <RPAREN> { return e; }
|
    <LBRACE> { s = span(); }
    <MINUS> e = PatternExpression()
    <MINUS> <RBRACE> {
        return SqlStdOperatorTable.PATTERN_EXCLUDE.createCall(s.end(this), e);
    }
|
    (
        <PERMUTE> { s = span(); list = new ArrayList<SqlNode>(); }
        <LPAREN>
        e = PatternExpression() { list.add(e); }
        ( <COMMA> e = PatternExpression() { list.add(e); } )*
        <RPAREN> {
            return SqlStdOperatorTable.PATTERN_PERMUTE.createCall(
                s.end(this), list);
        }
    )
}

SqlNodeList SubsetDefinitionCommaList(Span s) :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    AddSubsetDefinition(list)
    ( <COMMA> AddSubsetDefinition(list) )*
    { return new SqlNodeList(list, s.addAll(list).pos()); }
}

void AddSubsetDefinition(List<SqlNode> list) :
{
    final SqlNode var;
    final SqlNodeList varList;
}
{
    var = SimpleIdentifier()
    <EQ>
    <LPAREN>
    varList = ExpressionCommaList(span(), ExprContext.ACCEPT_NON_QUERY)
    <RPAREN> {
        list.add(
            SqlStdOperatorTable.EQUALS.createCall(span().end(var), var,
                varList));
    }
}

SqlNodeList PatternDefinitionCommaList(Span s) :
{
    SqlNode e;
    final List<SqlNode> eList = new ArrayList<SqlNode>();
}
{
    e = PatternDefinition() {
        eList.add(e);
    }
    (
        <COMMA>
        e = PatternDefinition() {
            eList.add(e);
        }
    )*
    {
        return new SqlNodeList(eList, s.addAll(eList).pos());
    }
}

SqlNode PatternDefinition() :
{
    final SqlNode var;
    final SqlNode e;
}
{
    var = SimpleIdentifier()
    <AS>
    e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
        return SqlStdOperatorTable.AS.createCall(Span.of(var, e).pos(), e, var);
    }
}

// ----------------------------------------------------------------------------
// Expressions

/**
 * Parses a SQL expression (such as might occur in a WHERE clause) followed by
 * the end-of-file symbol.
 */
SqlNode SqlExpressionEof() :
{
    SqlNode e;
}
{
    e = Expression(ExprContext.ACCEPT_SUB_QUERY) (<EOF>)
    {
        return e;
    }
}

/**
 * Parses either a row expression or a query expression without ORDER BY.
 *
 * <p>Examples of valid queries:
 * <ul>
 * <li>{@code SELECT c FROM t}
 * <li>{@code SELECT c} (valid in some dialects)
 * <li>{@code SELECT c FROM t UNION SELECT c2 FROM t2}
 * <li>{@code WITH q AS (SELECT 1) SELECT * FROM q}
 * <li>{@code VALUES (1, 2)}
 * <li>{@code TABLE t}
 * </ul>
 *
 * <p>Non-examples:
 * <ul>
 * <li>{@code emp CROSS JOIN dept}
 * <li>{@code SELECT c FROM t ORDER BY c}
 * <li>{@code (SELECT c FROM t)}
 * </ul>
 */
SqlNode QueryOrExpr(ExprContext exprContext) :
{
    SqlNodeList withList = null;
    final SqlNode e;
    final List<Object> list = new ArrayList<Object>();
}
{
    [ withList = WithList() ]
    e = LeafQueryOrExpr(exprContext) { list.add(e); }
    ( AddSetOpQuery(list, exprContext) )*
    { return addWith(withList, SqlParserUtil.toTree(list)); }
}

SqlNode Query(ExprContext exprContext) :
{
    SqlNodeList withList = null;
    final SqlNode e;
    final List<Object> list = new ArrayList<Object>();
}
{
    [ withList = WithList() ]
    e = LeafQuery(exprContext) { list.add(e); }
    ( AddSetOpQuery(list, exprContext) )*
    { return addWith(withList, SqlParserUtil.toTree(list)); }
}

JAVACODE SqlNode addWith(SqlNodeList withList, SqlNode e) {
    return withList == null
        ? e
        : new SqlWith(withList.getParserPosition(), withList, e);
}

/** Parses a set operator (e.g. UNION or INTERSECT)
 * followed by a query or expression,
 * and adds both to {@code list}. */
void AddSetOpQueryOrExpr(List<Object> list, ExprContext exprContext) :
{
    final SqlOperator op;
    final SqlParserPos pos;
    final SqlNode e;
}
{
    {
        if (list.size() == 1 && !((SqlNode) list.get(0)).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);
    }
}

/** Parses a set operator (e.g. UNION or INTERSECT)
 * followed by a query,
 * and adds both to {@code list}. */
void AddSetOpQuery(List<Object> list, ExprContext exprContext) :
{
    final SqlOperator op;
    final SqlParserPos pos;
    final SqlNode e;
}
{
    {
        if (list.size() == 1 && !((SqlNode) list.get(0)).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);
    }
}

SqlNodeList WithList() :
{
    final Span s;
    final List<SqlWithItem> list = new ArrayList<SqlWithItem>();
}
{
    <WITH> { s = span(); }
    AddWithItem(list) ( <COMMA> AddWithItem(list) )*
    { return new SqlNodeList(list, s.end(this)); }
}

void AddWithItem(List<SqlWithItem> list) :
{
    final SqlIdentifier id;
    final SqlNodeList columnList;
    final SqlNode definition;
}
{
    id = SimpleIdentifier()
    ( columnList = ParenthesizedSimpleIdentifierList() | { columnList = null; } )
    <AS>
    definition = ParenthesizedExpression(ExprContext.ACCEPT_QUERY)
    { list.add(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 = LeafQuery(exprContext) { return e; }
|
    e = Expression(exprContext) { return e; }
}

/** As {@link #Expression} but appends to a list. */
void AddExpression(List<SqlNode> list, ExprContext exprContext) :
{
    final SqlNode e;
}
{
    e = Expression(exprContext) { list.add(e); }
}

/**
 * Parses a row expression or a parenthesized expression of any kind.
 */
SqlNode Expression(ExprContext exprContext) :
{
    final List<Object> list;
}
{
    list = Expression2(exprContext) { return SqlParserUtil.toTree(list); }
}

void AddExpression2b(List<Object> list, ExprContext exprContext) :
{
    SqlNode e;
    SqlOperator op;
    SqlNode ext;
}
{
    (
        LOOKAHEAD(1)
        op = PrefixRowOperator() {
            checkNonQueryExpression(exprContext);
            list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
        }
    )*
    e = Expression3(exprContext) {
        list.add(e);
    }
    (
        LOOKAHEAD(2) <DOT>
        ext = RowExpressionExtension() {
            list.add(
                new SqlParserUtil.ToTreeListItem(
                    SqlStdOperatorTable.DOT, getPos()));
            list.add(ext);
        }
    )*
}

/**
 * Parses a binary row expression, or a parenthesized expression of any
 * kind.
 *
 * <p>The result is as a flat list of operators and operands. The top-level
 * call to get an expression should call {@link #Expression}, but lower-level
 * calls should call this, to give the parser the opportunity to associate
 * operator calls.
 *
 * <p>For example 'a = b like c = d' should come out '((a = b) like c) = d'
 * because LIKE and '=' have the same precedence, but tends to come out as '(a
 * = b) like (c = d)' because (a = b) and (c = d) are parsed as separate
 * expressions.
 */
List<Object> Expression2(ExprContext exprContext) :
{
    final List<Object> list = new ArrayList();
    List<Object> list2;
    final List<Object> list3 = new ArrayList();
    SqlNodeList nodeList;
    SqlNode e;
    SqlOperator op;
    SqlIdentifier p;
    final Span s = span();
}
{
    AddExpression2b(list, exprContext)
    (
        LOOKAHEAD(2)
        (
            LOOKAHEAD(2)
            (
                // Special case for "IN", because RHS of "IN" is the only place
                // that an expression-list is allowed ("exp IN (exp1, exp2)").
                LOOKAHEAD(2) {
                    checkNonQueryExpression(exprContext);
                }
                (
                    <NOT> <IN> { op = SqlStdOperatorTable.NOT_IN; }
                |
                    <IN> { op = SqlStdOperatorTable.IN; }
                |
                    { final SqlKind k; }
                    k = comp()
                    (
                        <SOME> { op = SqlStdOperatorTable.some(k); }
                    |
                        <ANY> { op = SqlStdOperatorTable.some(k); }
                    |
                        <ALL> { op = SqlStdOperatorTable.all(k); }
                    )
                )
                { s.clear().add(this); }
                nodeList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR)
                {
                    list.add(new SqlParserUtil.ToTreeListItem(op, s.pos()));
                    s.add(nodeList);
                    // special case for stuff like IN (s1 UNION s2)
                    if (nodeList.size() == 1) {
                        SqlNode item = nodeList.get(0);
                        if (item.isA(SqlKind.QUERY)) {
                            list.add(item);
                        } else {
                            list.add(nodeList);
                        }
                    } else {
                        list.add(nodeList);
                    }
                }
            |
                LOOKAHEAD(2) {
                    checkNonQueryExpression(exprContext);
                }
                (
                    <NOT> <BETWEEN> {
                        op = SqlStdOperatorTable.NOT_BETWEEN;
                        s.clear().add(this);
                    }
                    [
                        <SYMMETRIC> { op = SqlStdOperatorTable.SYMMETRIC_NOT_BETWEEN; }
                    |
                        <ASYMMETRIC>
                    ]
                |
                    <BETWEEN>
                    {
                        op = SqlStdOperatorTable.BETWEEN;
                        s.clear().add(this);
                    }
                    [
                        <SYMMETRIC> { op = SqlStdOperatorTable.SYMMETRIC_BETWEEN; }
                    |
                        <ASYMMETRIC>
                    ]
                )
                AddExpression2b(list3, ExprContext.ACCEPT_SUB_QUERY) {
                    list.add(new SqlParserUtil.ToTreeListItem(op, s.pos()));
                    list.addAll(list3);
                    list3.clear();
                }
            |
                LOOKAHEAD(2) {
                    checkNonQueryExpression(exprContext);
                    s.clear().add(this);
                }
                (
                    (
                        <NOT>
                        (
                            <LIKE> { op = SqlStdOperatorTable.NOT_LIKE; }
                        |
                            <ILIKE> { op = SqlLibraryOperators.NOT_ILIKE; }
                        |
                            <RLIKE> { op = SqlLibraryOperators.NOT_RLIKE; }
                        |
                            <LIKE_BINARY> { op = SqlUserDefinedOperators.NOT_LIKE_BINARY; }
                        |
                            <SIMILAR> <TO> { op = SqlStdOperatorTable.NOT_SIMILAR_TO; }
                        )
                    |
                        <LIKE> { op = SqlStdOperatorTable.LIKE; }
                    |
                        <ILIKE> { op = SqlLibraryOperators.ILIKE; }
                    |
                        <RLIKE> { op = SqlLibraryOperators.RLIKE; }
                    |
                        <LIKE_BINARY> { op = SqlUserDefinedOperators.LIKE_BINARY; }
                    |
                        <SIMILAR> <TO> { op = SqlStdOperatorTable.SIMILAR_TO; }
                    )
                <#if (parser.includePosixOperators!)>
                |
                    <NEGATE> <TILDE> { op = SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_SENSITIVE; }
                    [ <STAR> { op = SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_INSENSITIVE; } ]
                |
                    <TILDE> { op = SqlStdOperatorTable.POSIX_REGEX_CASE_SENSITIVE; }
                    [ <STAR> { op = SqlStdOperatorTable.POSIX_REGEX_CASE_INSENSITIVE; } ]
                </#if>
                )
                list2 = Expression2(ExprContext.ACCEPT_SUB_QUERY) {
                    list.add(new SqlParserUtil.ToTreeListItem(op, s.pos()));
                    list.addAll(list2);
                }
                [
                    LOOKAHEAD(2)
                    <ESCAPE> e = Expression3(ExprContext.ACCEPT_SUB_QUERY) {
                        s.clear().add(this);
                        list.add(
                            new SqlParserUtil.ToTreeListItem(
                                SqlStdOperatorTable.ESCAPE, s.pos()));
                        list.add(e);
                    }
                ]
            |
            <#list (parser.extraBinaryExpressions!) as extra >
                ${extra}(list, exprContext, s)
            |
            </#list>
                LOOKAHEAD(3) op = BinaryRowOperator() {
                    checkNonQueryExpression(exprContext);
                    list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
                }
                AddExpression2b(list, ExprContext.ACCEPT_SUB_QUERY)
                {
                   if ((op == SqlUserDefinedOperators.L2_DISTANCE
                        || op == SqlUserDefinedOperators.IP_DISTANCE
                        || op == SqlUserDefinedOperators.COSINE_SIMILARITY)
                        && list.size() == 3) {
                       SqlNode call = op.createCall(s.end(this), (SqlNode)list.get(0), (SqlNode)list.get(2));
                       list.clear();
                       list.add(call);
                   }
                }
            |
                <LBRACKET>
                e = Expression(ExprContext.ACCEPT_SUB_QUERY)
                <RBRACKET> {
                    list.add(
                        new SqlParserUtil.ToTreeListItem(
                            SqlStdOperatorTable.ITEM, getPos()));
                    list.add(e);
                }
                (
                    LOOKAHEAD(2) <DOT>
                    p = SimpleIdentifier() {
                        list.add(
                            new SqlParserUtil.ToTreeListItem(
                                SqlStdOperatorTable.DOT, getPos()));
                        list.add(p);
                    }
                )*
            |
                {
                    checkNonQueryExpression(exprContext);
                }
                op = PostfixRowOperator() {
                    list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
                }
            )
        )+
        {
            return list;
        }
    |
        {
            return list;
        }
    )
}

/** Parses a comparison operator inside a SOME / ALL predicate. */
SqlKind comp() :
{
}
{
    <LT> { return SqlKind.LESS_THAN; }
|
    <LE> { return SqlKind.LESS_THAN_OR_EQUAL; }
|
    <GT> { return SqlKind.GREATER_THAN; }
|
    <GE> { return SqlKind.GREATER_THAN_OR_EQUAL; }
|
    <EQ> { return SqlKind.EQUALS; }
|
    <NE> { return SqlKind.NOT_EQUALS; }
|
    <NE2> {
        if (!this.conformance.isBangEqualAllowed()) {
            throw SqlUtil.newContextException(getPos(), RESOURCE.bangEqualNotAllowed());
        }
        return SqlKind.NOT_EQUALS;
    }
}

/**
 * Parses a unary row expression, or a parenthesized expression of any
 * kind.
 */
SqlNode Expression3(ExprContext exprContext) :
{
    final SqlNode e;
    final SqlNodeList list;
    final SqlNodeList list1;
    final Span s;
    final Span rowSpan;
}
{
    LOOKAHEAD(2)
    e = AtomicRowExpression()
    {
        checkNonQueryExpression(exprContext);
        return e;
    }
|
    e = CursorExpression(exprContext) { return e; }
|
    LOOKAHEAD(3)
    <ROW> {
        s = span();
    }
    list = ParenthesizedQueryOrCommaList(exprContext) {
        if (exprContext != ExprContext.ACCEPT_ALL
            && exprContext != ExprContext.ACCEPT_CURSOR
            && !this.conformance.allowExplicitRowValueConstructor())
        {
            throw SqlUtil.newContextException(s.end(list),
                RESOURCE.illegalRowExpression());
        }
        return SqlStdOperatorTable.ROW.createCall(list);
    }
|
    (
        <ROW> { rowSpan = span(); }
    |   { rowSpan = null; }
    )
    list1 = ParenthesizedQueryOrCommaList(exprContext) {
        if (rowSpan != null) {
            // interpret as row constructor
            return SqlStdOperatorTable.ROW.createCall(rowSpan.end(list1),
                (List<SqlNode>) list1);
        }
    }
    [
        LOOKAHEAD(2)
        /* TODO:
        (
            op = periodOperator()
            list2 = ParenthesizedQueryOrCommaList(exprContext)
            {
                if (list1.size() != 2 || list2.size() != 2) {
                    throw SqlUtil.newContextException(
                        list1.getParserPosition().plus(
                            list2.getParserPosition()),
                        RESOURCE.illegalOverlaps());
                }
                for (SqlNode node : list2) {
                    list1.add(node);
                }
                return op.createCall(
                    list1.getParserPosition().plus(list2.getParserPosition()),
                    list1.toArray());
            }
        )
    |
        */
        (
            e = IntervalQualifier()
            {
                if ((list1.size() == 1)
                    && list1.get(0) instanceof SqlCall)
                {
                    final SqlCall call = (SqlCall) list1.get(0);
                    if (call.getKind() == SqlKind.MINUS
                            && call.operandCount() == 2) {
                        return SqlStdOperatorTable.MINUS_DATE.createCall(
                            Span.of(list1).end(this), call.operand(0),
                            call.operand(1), e);
                     }
                }
                throw SqlUtil.newContextException(span().end(list1),
                    RESOURCE.illegalMinusDate());
            }
        )
    ]
    {
        if (list1.size() == 1) {
            // interpret as single value or query
            return list1.get(0);
        } else {
            // interpret as row constructor
            return SqlStdOperatorTable.ROW.createCall(span().end(list1),
                (List<SqlNode>) list1);
        }
    }
}

SqlOperator periodOperator() :
{
}
{
     <OVERLAPS> { return SqlStdOperatorTable.OVERLAPS; }
|
     LOOKAHEAD(2)
     <IMMEDIATELY> <PRECEDES> { return SqlStdOperatorTable.IMMEDIATELY_PRECEDES; }
|
     <PRECEDES> { return SqlStdOperatorTable.PRECEDES; }
|
     <IMMEDIATELY> <SUCCEEDS> { return SqlStdOperatorTable.IMMEDIATELY_SUCCEEDS; }
|
     <SUCCEEDS> { return SqlStdOperatorTable.SUCCEEDS; }
|
     <EQUALS> { return SqlStdOperatorTable.PERIOD_EQUALS; }
}

/**
 * Parses a COLLATE clause
 */
SqlCollation CollateClause() :
{
}
{
    <COLLATE> <COLLATION_ID>
    {
        return new SqlCollation(
            getToken(0).image, SqlCollation.Coercibility.EXPLICIT);
    }
}

/**
 * Numeric literal or parameter; used in LIMIT, OFFSET and FETCH clauses.
 */
SqlNode UnsignedNumericLiteralOrParam() :
{
    final SqlNode e;
}
{
    (
        e = UnsignedNumericLiteral()
    |
        e = DynamicParam()
    )
    { return e; }
}

/**
 * Parses a row expression extension, it can be either an identifier,
 * or a call to a named function.
 */
SqlNode RowExpressionExtension() :
{
    final SqlFunctionCategory funcType = SqlFunctionCategory.USER_DEFINED_FUNCTION;
    final SqlIdentifier p;
    final Span s;
    final List<SqlNode> args;
    final SqlLiteral quantifier;
}
{
    p = SimpleIdentifier()
    (
        LOOKAHEAD( <LPAREN> ) { s = span(); }
        (
            LOOKAHEAD(2) <LPAREN> <STAR> {
                quantifier = null;
                args = ImmutableList.of(SqlIdentifier.star(getPos()));
            }
            <RPAREN>
        |
            LOOKAHEAD(2) <LPAREN> <RPAREN> {
                quantifier = null;
                args = ImmutableList.of();
            }
        |
            args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
                quantifier = (SqlLiteral) args.get(0);
                args.remove(0);
            }
        )
        { return createCall(p, s.end(this), funcType, quantifier, args); }
    |
        { return p; }
    )
}

/**
 * Parses a call to the STRING_AGG aggregate function (or to an aggregate
 * function with similar syntax: ARRAY_AGG, ARRAY_CONCAT_AGG, GROUP_CONCAT).
 */
SqlCall StringAggFunctionCall() :
{
    final Span s, s2;
    final SqlOperator op;
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final SqlLiteral qualifier;
    final SqlNodeList orderBy;
    final Pair<SqlParserPos, SqlOperator> nullTreatment;
    final SqlNode separator;
}
{
    (
        <ARRAY_AGG> { s = span(); op = SqlLibraryOperators.ARRAY_AGG; }
    |   <ARRAY_CONCAT_AGG> { s = span(); op = SqlLibraryOperators.ARRAY_CONCAT_AGG; }
    |   <GROUP_CONCAT> { s = span(); op = SqlLibraryOperators.GROUP_CONCAT; }
    |   <STRING_AGG> { s = span(); op = SqlLibraryOperators.STRING_AGG; }
    )
    <LPAREN>
    (
        qualifier = AllOrDistinct()
    |   { qualifier = null; }
    )
    AddArg(args, ExprContext.ACCEPT_SUB_QUERY)
    (
        <COMMA> {
            // a comma-list can't appear where only a query is expected
            // TODO: the following line is a no-op; remove it?
            checkNonQueryExpression(ExprContext.ACCEPT_SUB_QUERY);
        }
        AddArg(args, ExprContext.ACCEPT_SUB_QUERY)
    )*
    (
        nullTreatment = NullTreatment()
    |   { nullTreatment = null; }
    )
    [
        orderBy = OrderBy(true) {
            args.add(orderBy);
        }
    ]
    [
        <SEPARATOR> { s2 = span(); } separator = StringLiteral() {
            args.add(SqlInternalOperators.SEPARATOR.createCall(s2.end(this), separator));
        }
    ]
    <RPAREN>
    {
        SqlCall call = op.createCall(qualifier, s.end(this), args);
        if (nullTreatment != null) {
            // Wrap in RESPECT_NULLS or IGNORE_NULLS.
            call = nullTreatment.right.createCall(nullTreatment.left, call);
        }
        return call;
    }
}

/**
 * Parses an atomic row expression.
 */
SqlNode AtomicRowExpression() :
{
    final SqlNode e;
}
{
    (
        e = LiteralOrIntervalExpression()
    |
        e = DynamicParam()
    |
        LOOKAHEAD(2)
        e = BuiltinFunctionCall()
    |
        e = JdbcFunctionCall()
    |
        e = MultisetConstructor()
    |
        e = ArrayConstructor()
    |
        LOOKAHEAD(3)
        e = MapConstructor()
    |
        e = PeriodConstructor()
    |
        // NOTE jvs 18-Jan-2005:  use syntactic lookahead to discriminate
        // compound identifiers from function calls in which the function
        // name is a compound identifier
        LOOKAHEAD( [<SPECIFIC>] FunctionName() <LPAREN>)
        e = NamedFunctionCall()
    |
        e = ContextVariable()
    |
        e = CompoundIdentifier()
    |
        e = NewSpecification()
    |
        e = CaseExpression()
    |
        e = SequenceExpression()
    |
        e = ScopeVariable()
    )
    { return e; }
}

SqlNode CaseExpression() :
{
    final Span whenSpan = Span.of();
    final Span thenSpan = Span.of();
    final Span s;
    SqlNode e;
    final SqlNode caseIdentifier;
    final SqlNode elseClause;
    final List<SqlNode> whenList = new ArrayList<SqlNode>();
    final List<SqlNode> thenList = new ArrayList<SqlNode>();
}
{
    <CASE> { s = span(); }
    (
        caseIdentifier = Expression(ExprContext.ACCEPT_SUB_QUERY)
    |   { caseIdentifier = null; }
    )
    (
        <WHEN> { whenSpan.add(this); }
        e = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) {
            if (((SqlNodeList) e).size() == 1) {
                e = ((SqlNodeList) e).get(0);
            }
            whenList.add(e);
        }
        <THEN> { thenSpan.add(this); }
        e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
            thenList.add(e);
        }
    )+
    (
        <ELSE> elseClause = Expression(ExprContext.ACCEPT_SUB_QUERY)
    |   { elseClause = null; }
    )
    <END> {
        return SqlCase.createSwitched(s.end(this), caseIdentifier,
            new SqlNodeList(whenList, whenSpan.addAll(whenList).pos()),
            new SqlNodeList(thenList, thenSpan.addAll(thenList).pos()),
            elseClause);
    }
}

SqlCall SequenceExpression() :
{
    final Span s;
    final SqlOperator f;
    final SqlNode sequenceRef;
}
{
    (
        <NEXT> { f = SqlStdOperatorTable.NEXT_VALUE; s = span(); }
    |
        LOOKAHEAD(3)
        <CURRENT> { f = SqlStdOperatorTable.CURRENT_VALUE; s = span(); }
    )
    <VALUE> <FOR> sequenceRef = CompoundIdentifier() {
        return f.createCall(s.end(sequenceRef), sequenceRef);
    }
}

/**
 * Parses "SET &lt;NAME&gt; = VALUE" or "RESET &lt;NAME&gt;", without a leading
 * "ALTER &lt;SCOPE&gt;".
 */
SqlSetOption SqlSetOption(Span s, String scope) :
{
    SqlIdentifier name = null;
    SqlIdentifier identifier = null;
    SqlNode val = null;
    String user = null;
    String host = "%";
    String password = null;
}
{
    (
        <SET> {
            s.add(this);
        }
        [
             <PASSWORD> <FOR>
             ( <QUOTED_STRING> | <IDENTIFIER> )
              { user = token.image; }
              [<AT_SPLIT> (<QUOTED_STRING> | <IDENTIFIER>) {host = token.image; }  ]
              <EQ>
              <PASSWORD>
              <LPAREN>
              <QUOTED_STRING> { password = token.image; }
              <RPAREN>
              {
                return new SqlSetPassword(s.end(this), user, host, password);
              }
        ]
        (
        [ <COMMA> ]
        [ <GLOBAL> { scope = "SYSTEM"; } ]
        [ <SESSION> { scope = "SESSION"; } ]
        [ <EXECUTOR> { scope = "EXECUTOR"; } ]
        [ <AT_SPLIT> { scope = "USER"; } ]
        [ <AT_SPLIT_2> { scope = "GLOBAL"; } ]
        name = CompoundIdentifier()
        (
         <UTF8> {
            val = new SqlIdentifier("utf8", s.end(this));
            return new SqlSetOption(s.end(this), scope, name, val);
         }
        |
         <IDENTIFIER>  {
            val = new SqlIdentifier(token.image, s.end(this));
            return new SqlSetOption(s.end(this), scope, name, val);
         }
        |
         (<QUOTED_STRING> <COLLATE> <QUOTED_STRING>
         |
         <ISOLATION> <LEVEL>
         [<READ> [<MINUS>] [ <COMMITTED> { val = new SqlIdentifier("read-committed", s.end(this)); } ] [<UNCOMMITTED> { val = new SqlIdentifier("read-uncommitted", s.end(this)); }]  ]
         [<REPEATABLE> [<MINUS>] <READ> { val = new SqlIdentifier("repeatable-read", s.end(this)); } ]
         [<SERIALIZABLE> { val = new SqlIdentifier(token.image, s.end(this)); }]
         ){
            if (name.names.size() != 1 || !name.names.get(0).equalsIgnoreCase("transaction")) {
               throw new RuntimeException("Syntax error");
            }
            if (val == null) {
               val = new SqlIdentifier(token.image, s.end(this));
            }
            name = new SqlIdentifier("transaction_isolation", name.getParserPosition());
            return new SqlSetOption(s.end(this), scope, name, val);
         }
        |
          <READ> [<WRITE> { val = new SqlIdentifier("off", s.end(this)); }]{
             name = new SqlIdentifier("transaction_read_only", name.getParserPosition());
             if (val == null) {
               val = new SqlIdentifier("on", s.end(this));
             }
           }
        |
          <WRITE> {
             name = new SqlIdentifier("transaction_read_only", name.getParserPosition());
             val = new SqlIdentifier("off", s.end(this));
           }
        |
        <EQ>
        (
            val = Literal()
        |
            val = SimpleIdentifier()
        |
            <ON> {
                // OFF is handled by SimpleIdentifier, ON handled here.
                val = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT),
                    getPos());
            }
        |
           <DEFAULT_> {
             val = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT), getPos());
           }
        )
        )
        )*
        {
            if (scope == null) {
               scope = "session";
            }
            return new SqlSetOption(s.end(val), scope, name, val);
        }
    |
        <RESET> {
            s.add(this);
        }
        (
            name = CompoundIdentifier()
        |
            <ALL> {
                name = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT),
                    getPos());
            }
        )
        {
            return new SqlSetOption(s.end(name), scope, name, null);
        }
    )
}

/**
 * Parses an expression for setting or resetting an option in SQL, such as QUOTED_IDENTIFIERS,
 * or explain plan level (physical/logical).
 */
SqlAlter SqlAlter() :
{
    final Span s;
    String scope = null;
    SqlAlter alterNode = null;
}
{
    <ALTER> { s = span(); }
    (
<#-- additional literal parser methods are included here -->
<#list (parser.alterStatementParserMethods!) as method>
        alterNode = ${method}(s, scope)
    |
</#list>

        alterNode = SqlSetOption(s, scope)
    )
    {
        return alterNode;
    }
}

String Scope() :
{
}
{
    ( <SYSTEM> | <SESSION> | <TABLE> ) { return token.image.toUpperCase(Locale.ROOT); }
}

<#if (parser.createStatementParserMethods!)?size != 0>
/**
 * Parses a CREATE statement.
 */
SqlCreate SqlCreate() :
{
    final Span s;
    boolean replace = false;
    final SqlCreate create;
}
{
    <CREATE> { s = span(); }
    [
        <OR> <REPLACE> {
            replace = true;
        }
    ]
    (
<#-- additional literal parser methods are included here -->
<#list (parser.createStatementParserMethods!) as method>
        create = ${method}(s, replace)
        <#sep>| LOOKAHEAD(2) </#sep>
</#list>
        |
         create = SqlCreateUser(s, replace)
    )
    {
        return create;
    }
}
</#if>

<#if (parser.dropStatementParserMethods!)?size != 0>
/**
 * Parses a DROP statement.
 */
SqlDrop SqlDrop() :
{
    final Span s;
    boolean replace = false;
    final SqlDrop drop;
}
{
    <DROP> { s = span(); }
    (
<#-- additional literal parser methods are included here -->
<#list (parser.dropStatementParserMethods!) as method>
        drop = ${method}(s, replace)
        <#sep>|</#sep>
</#list>
    )
    {
        return drop;
    }
}
</#if>

/**
 * Parses a literal expression, allowing continued string literals.
 * Usually returns an SqlLiteral, but a continued string literal
 * is an SqlCall expression, which concatenates 2 or more string
 * literals; the validator reduces this.
 *
 * <p>If the context allows both literals and expressions,
 * use {@link #LiteralOrIntervalExpression}, which requires less
 * lookahead.
 */
SqlNode Literal() :
{
    SqlNode e;
}
{
    (
        e = NonIntervalLiteral()
    |
        e = IntervalLiteral()
    )
    { return e; }
}

/** Parses a literal that is not an interval literal. */
SqlNode NonIntervalLiteral() :
{
    final SqlNode e;
}
{
    (
        e = NumericLiteral()
    |
        e = StringLiteral()
    |
        e = SpecialLiteral()
    |
        e = DateTimeLiteral()
<#-- additional literal parser methods are included here -->
<#list (parser.literalParserMethods!) as method>
    |
        e = ${method}
</#list>
    )
    {
        return e;
    }
}

/** Parses a literal or an interval expression.
 *
 * <p>We include them in the same production because it is difficult to
 * distinguish interval literals from interval expression (both of which
 * start with the {@code INTERVAL} keyword); this way, we can use less
 * LOOKAHEAD. */
SqlNode LiteralOrIntervalExpression() :
{
    final SqlNode e;
}
{
    (
        e = IntervalLiteralOrExpression()
    |
        e = NonIntervalLiteral()
    )
    { return e; }
}

/** Parses a unsigned numeric literal */
SqlNumericLiteral UnsignedNumericLiteral() :
{
}
{
    <UNSIGNED_INTEGER_LITERAL> {
        return SqlLiteral.createExactNumeric(token.image, getPos());
    }
|
    <DECIMAL_NUMERIC_LITERAL> {
        return SqlLiteral.createExactNumeric(token.image, getPos());
    }
|
    <APPROX_NUMERIC_LITERAL> {
        return SqlLiteral.createApproxNumeric(token.image, getPos());
    }
}

/** Parses a numeric literal (can be signed) */
SqlLiteral NumericLiteral() :
{
    final SqlNumericLiteral num;
    final Span s;
}
{
    <PLUS> num = UnsignedNumericLiteral() {
        return num;
    }
|
    <MINUS> { s = span(); } num = UnsignedNumericLiteral() {
        return SqlLiteral.createNegative(num, s.end(this));
    }
|
    num = UnsignedNumericLiteral() {
        return num;
    }
}

/** Parse a special literal keyword */
SqlLiteral SpecialLiteral() :
{
}
{
    <TRUE> { return SqlLiteral.createBoolean(true, getPos()); }
|
    <FALSE> { return SqlLiteral.createBoolean(false, getPos()); }
|
    <UNKNOWN> { return SqlLiteral.createUnknown(getPos()); }
|
    <NULL> { return SqlLiteral.createNull(getPos()); }
}

/**
 * Parses a string literal. The literal may be continued onto several
 * lines.  For a simple literal, the result is an SqlLiteral.  For a continued
 * literal, the result is an SqlCall expression, which concatenates 2 or more
 * string literals; the validator reduces this.
 *
 * @see SqlLiteral#unchain(SqlNode)
 * @see SqlLiteral#stringValue(SqlNode)
 *
 * @return a literal expression
 */
SqlNode StringLiteral() :
{
    String p;
    final List<SqlLiteral> frags;
    char unicodeEscapeChar = 0;
    String charSet = null;
    SqlCharStringLiteral literal;
}
{
    // A continued string literal consists of a head fragment and one or more
    // tail fragments. Since comments may occur between the fragments, and
    // comments are special tokens, each fragment is a token. But since spaces
    // or comments may not occur between the prefix and the first quote, the
    // head fragment, with any prefix, is one token.

    <BINARY_STRING_LITERAL>
    {
        frags = new ArrayList<SqlLiteral>();
        try {
            p = SqlParserUtil.trim(token.image, "xX'");
            frags.add(SqlLiteral.createBinaryString(p, getPos()));
        } catch (NumberFormatException ex) {
            throw SqlUtil.newContextException(getPos(),
                RESOURCE.illegalBinaryString(token.image));
        }
    }
    (
        // The grammar is ambiguous when a continued literals and a character
        // string alias are both possible. For example, in
        //   SELECT x'01'\n'ab'
        // we prefer that 'ab' continues the literal, and is not an alias.
        // The following LOOKAHEAD mutes the warning about ambiguity.
        LOOKAHEAD(1)
        <QUOTED_STRING>
        {
            try {
                p = SqlParserUtil.trim(token.image, "'"); // no embedded quotes
                frags.add(SqlLiteral.createBinaryString(p, getPos()));
            } catch (NumberFormatException ex) {
                throw SqlUtil.newContextException(getPos(),
                    RESOURCE.illegalBinaryString(token.image));
            }
        }
    )*
    {
        assert !frags.isEmpty();
        if (frags.size() == 1) {
            return frags.get(0); // just the head fragment
        } else {
            SqlParserPos pos2 = SqlParserPos.sum(frags);
            return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, frags);
        }
    }
|
    (
        <PREFIXED_STRING_LITERAL>
        { charSet = SqlParserUtil.getCharacterSet(token.image); }
    |   <QUOTED_STRING>
    |   <UNICODE_STRING_LITERAL> {
            // TODO jvs 2-Feb-2009:  support the explicit specification of
            // a character set for Unicode string literals, per SQL:2003
            unicodeEscapeChar = BACKSLASH;
            charSet = "UTF16";
        }
    )
    {
        frags = new ArrayList<SqlLiteral>();
        p = SqlParserUtil.parseString(token.image);
        try {
            literal = SqlLiteral.createCharString(p, charSet, getPos());
            frags.add(literal);
        } catch (java.nio.charset.UnsupportedCharsetException e) {
            throw SqlUtil.newContextException(getPos(),
                RESOURCE.unknownCharacterSet(charSet));
        }
    }
    (
        // The grammar is ambiguous when a continued literals and a character
        // string alias are both possible. For example, in
        //   SELECT 'taxi'\n'cab'
        // we prefer that 'cab' continues the literal, and is not an alias.
        // The following LOOKAHEAD mutes the warning about ambiguity.
        LOOKAHEAD(1)
        <QUOTED_STRING>
        {
            p = SqlParserUtil.parseString(token.image);
            try {
                literal = SqlLiteral.createCharString(p, charSet, getPos());
                frags.add(literal);
            } catch (java.nio.charset.UnsupportedCharsetException e) {
                throw SqlUtil.newContextException(getPos(),
                    RESOURCE.unknownCharacterSet(charSet));
            }
        }
    )*
    [
        <UESCAPE> <QUOTED_STRING>
        {
            if (unicodeEscapeChar == 0) {
                throw SqlUtil.newContextException(getPos(),
                    RESOURCE.unicodeEscapeUnexpected());
            }
            String s = SqlParserUtil.parseString(token.image);
            unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s);
        }
    ]
    {
        assert !frags.isEmpty();
        if (frags.size() == 1) {
            // just the head fragment
            SqlLiteral lit = (SqlLiteral) frags.get(0);
            return lit.unescapeUnicode(unicodeEscapeChar);
        } else {
            SqlNode[] rands = (SqlNode[]) frags.toArray(new SqlNode[0]);
            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);
        }
    }
|
    <C_STYLE_ESCAPED_STRING_LITERAL>
    {
        try {
            p = SqlParserUtil.parseCString(getToken(0).image);
        } catch (SqlParserUtil.MalformedUnicodeEscape e) {
            throw SqlUtil.newContextException(getPos(),
                RESOURCE.unicodeEscapeMalformed(e.i));
       }
       return SqlLiteral.createCharString(p, "UTF16", getPos());
    }
|
    <BIG_QUERY_DOUBLE_QUOTED_STRING>
    {
        p = SqlParserUtil.stripQuotes(getToken(0).image, DQ, DQ, "\\\"",
            Casing.UNCHANGED);
        try {
            return SqlLiteral.createCharString(p, charSet, getPos());
        } catch (java.nio.charset.UnsupportedCharsetException e) {
            throw SqlUtil.newContextException(getPos(),
                RESOURCE.unknownCharacterSet(charSet));
        }
    }
|
    <BIG_QUERY_QUOTED_STRING>
    {
        p = SqlParserUtil.stripQuotes(getToken(0).image, "'", "'", "\\'",
            Casing.UNCHANGED);
        try {
            return SqlLiteral.createCharString(p, charSet, getPos());
        } catch (java.nio.charset.UnsupportedCharsetException e) {
            throw SqlUtil.newContextException(getPos(),
                RESOURCE.unknownCharacterSet(charSet));
        }
    }
}

/** Parses a character literal.
 * Matches a single-quoted string, such as 'foo';
 * on BigQuery also matches a double-quoted string, such as "foo".
 * Returns the value of the string with quotes removed. */
String SimpleStringLiteral() :
{
}
{
    <QUOTED_STRING> {
        return SqlParserUtil.parseString(token.image);
    }
|
    <BIG_QUERY_QUOTED_STRING> {
        return SqlParserUtil.stripQuotes(token.image, "'", "'", "\\'", Casing.UNCHANGED);
    }
|
    <BIG_QUERY_DOUBLE_QUOTED_STRING> {
        return SqlParserUtil.stripQuotes(token.image, DQ, DQ, "\\\"", Casing.UNCHANGED);
    }
}

/**
 * Parses a date/time literal.
 */
SqlLiteral DateTimeLiteral() :
{
    final String p;
    final Span s;
}
{
    <LBRACE_D> <QUOTED_STRING> {
        p = SqlParserUtil.parseString(token.image);
    }
    <RBRACE> {
        return SqlParserUtil.parseDateLiteral(p, getPos());
    }
|
    <LBRACE_T> <QUOTED_STRING> {
        p = SqlParserUtil.parseString(token.image);
    }
    <RBRACE> {
        return SqlParserUtil.parseTimeLiteral(p, getPos());
    }
|
    <LBRACE_TS> { s = span(); } <QUOTED_STRING> {
        p = SqlParserUtil.parseString(token.image);
    }
    <RBRACE> {
        return SqlParserUtil.parseTimestampLiteral(p, s.end(this));
    }
|
    <DATE> { s = span(); } p = SimpleStringLiteral() {
      return SqlLiteral.createUnknown("DATE", p, s.end(this));
    }
|
    <DATETIME> { s = span(); } p = SimpleStringLiteral() {
        return SqlLiteral.createUnknown("DATETIME", p, s.end(this));
    }
|
    <TIME> { s = span(); } p = SimpleStringLiteral() {
      return SqlLiteral.createUnknown("TIME", p, s.end(this));
    }
|
    LOOKAHEAD(2)
    <TIMESTAMP> { s = span(); } p = SimpleStringLiteral() {
        return SqlLiteral.createUnknown("TIMESTAMP", p, s.end(this));
    }
|
    <TIMESTAMP> { s = span(); } <WITH> <LOCAL> <TIME> <ZONE> p = SimpleStringLiteral() {
        return SqlLiteral.createUnknown("TIMESTAMP WITH LOCAL TIME ZONE", p, s.end(this));
    }
}

/** Parses a MULTISET constructor */
SqlNode MultisetConstructor() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    SqlNode e;
    final Span s;
}
{
    <MULTISET> { s = span(); }
    (
        LOOKAHEAD(2)
        <LPAREN>
        // by sub query "MULTISET(SELECT * FROM T)"
        e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
        <RPAREN> {
            return SqlStdOperatorTable.MULTISET_QUERY.createCall(
                s.end(this), e);
        }
    |
        // by enumeration "MULTISET[e0, e1, ..., eN]"
        <LBRACKET> // TODO: do trigraph as well ??( ??)
        AddExpression(args, ExprContext.ACCEPT_NON_QUERY)
        ( <COMMA> AddExpression(args, ExprContext.ACCEPT_NON_QUERY) )*
        <RBRACKET>
        {
            return SqlStdOperatorTable.MULTISET_VALUE.createCall(
                s.end(this), args);
        }
    )
}

/** Parses an ARRAY constructor */
SqlNode ArrayConstructor() :
{
    SqlNodeList args;
    SqlNode e;
    final Span s;
    final String p;
}
{
    <ARRAY> { s = span(); }
    (
        LOOKAHEAD(1)
        <LPAREN>
        // by sub query "MULTISET(SELECT * FROM T)"
        e = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
        <RPAREN>
        {
            return SqlStdOperatorTable.ARRAY_QUERY.createCall(
                s.end(this), e);
        }
    |
        // by enumeration "ARRAY[e0, e1, ..., eN]"
        <LBRACKET> // TODO: do trigraph as well ??( ??)
        (
            args = ExpressionCommaList(s, ExprContext.ACCEPT_NON_QUERY)
        |
            { args = SqlNodeList.EMPTY; }
        )
        <RBRACKET>
        {
            return SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(
                s.end(this), args.getList());
        }
<#if (parser.includeParsingStringLiteralAsArrayLiteral!) >
    |
        p = SimpleStringLiteral() {
            try {
                return SqlParserUtil.parseArrayLiteral(p);
            } catch (SqlParseException ex) {
                throw SqlUtil.newContextException(getPos(),
                    RESOURCE.illegalArrayExpression(p));
            }
        }
</#if>
    )
}

SqlCall ArrayLiteral() :
{
    final List<SqlNode> list;
    SqlNode e;
    final Span s;
}
{
    <LBRACE> { s = span(); }
    (
        e = Literal() { list = startList(e); }
        ( <COMMA> e = Literal() { list.add(e); } )*
    |
        e = ArrayLiteral() { list = startList(e); }
        ( <COMMA> e = ArrayLiteral() { list.add(e); } )*
    |
        { list = Collections.emptyList(); }
    )
    <RBRACE> {
       return SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(s.end(this), list);
    }
}

/** Parses a MAP constructor */
SqlNode MapConstructor() :
{
    SqlNodeList args;
    SqlNode e;
    final Span s;
}
{
    <MAP> { s = span(); }
    (
        LOOKAHEAD(1)
        <LPAREN>
        // by sub query "MAP (SELECT empno, deptno FROM emp)"
        e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
        <RPAREN>
        {
            return SqlStdOperatorTable.MAP_QUERY.createCall(
                s.end(this), e);
        }
    |
        // by enumeration "MAP[k0, v0, ..., kN, vN]"
        <LBRACKET> // TODO: do trigraph as well ??( ??)
        (
            args = ExpressionCommaList(s, ExprContext.ACCEPT_NON_QUERY)
        |
            { args = SqlNodeList.EMPTY; }
        )
        <RBRACKET>
        {
            return SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR.createCall(
                s.end(this), args.getList());
        }
    )
}

/** Parses a PERIOD constructor */
SqlNode PeriodConstructor() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final Span s;
}
{
    <PERIOD> { s = span(); }
    <LPAREN>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <RPAREN> {
        return SqlStdOperatorTable.ROW.createCall(s.end(this), args);
    }
}

/**
 * Parses an interval literal.
 */
SqlLiteral IntervalLiteral() :
{
    final String p;
    final SqlIntervalQualifier intervalQualifier;
    int sign = 1;
    final Span s;
}
{
    <INTERVAL> { s = span(); }
    [
        <MINUS> { sign = -1; }
    |
        <PLUS> { sign = 1; }
    ]
    p = SimpleStringLiteral()
    intervalQualifier = IntervalQualifier() {
        return SqlParserUtil.parseIntervalLiteral(s.end(intervalQualifier),
            sign, p, intervalQualifier);
    }
}

/** Parses an interval literal (e.g. {@code INTERVAL '2:3' HOUR TO MINUTE})
 * or an interval expression (e.g. {@code INTERVAL emp.empno MINUTE}
 * or {@code INTERVAL 3 MONTHS}). */
SqlNode IntervalLiteralOrExpression() :
{
    final String p;
    final SqlIntervalQualifier intervalQualifier;
    int sign = 1;
    final Span s;
    SqlNode e;
}
{
    <INTERVAL> { s = span(); }
    [
        <MINUS> { sign = -1; }
    |
        <PLUS> { sign = 1; }
    ]
    (
        // literal (with quoted string)
        p = SimpleStringLiteral()
        intervalQualifier = IntervalQualifier() {
            return SqlParserUtil.parseIntervalLiteral(s.end(intervalQualifier),
                sign, p, intervalQualifier);
        }
    |
        // To keep parsing simple, any expressions besides numeric literal and
        // identifiers must be enclosed in parentheses.
        (
            <LPAREN>
            e = Expression(ExprContext.ACCEPT_SUB_QUERY)
            <RPAREN>
        |
            e = UnsignedNumericLiteral()
        |
            e = CompoundIdentifier()
        )
        intervalQualifier = IntervalQualifierStart() {
            if (sign == -1) {
                e = SqlStdOperatorTable.UNARY_MINUS.createCall(e.getParserPosition(), e);
            }
            return SqlStdOperatorTable.INTERVAL.createCall(s.end(this), e,
                intervalQualifier);
        }
    )
}

TimeUnit Year() :
{
}
{
    <YEAR> { return TimeUnit.YEAR; }
|
    <YEARS> { return warn(TimeUnit.YEAR); }
}

TimeUnit Quarter() :
{
}
{
    <QUARTER> { return TimeUnit.QUARTER; }
|
    <QUARTERS> { return warn(TimeUnit.QUARTER); }
}

TimeUnit Month() :
{
}
{
    <MONTH> { return TimeUnit.MONTH; }
|
    <MONTHS> { return warn(TimeUnit.MONTH); }
}

TimeUnit Week() :
{
}
{
    <WEEK> { return TimeUnit.WEEK; }
|
    <WEEKS> { return warn(TimeUnit.WEEK); }
}

TimeUnit Day() :
{
}
{
    <DAY> { return TimeUnit.DAY; }
|
    <DAYS> { return warn(TimeUnit.DAY); }
}

TimeUnit Hour() :
{
}
{
    <HOUR> { return TimeUnit.HOUR; }
|
    <HOURS> { return warn(TimeUnit.HOUR); }
}

TimeUnit Minute() :
{
}
{
    <MINUTE> { return TimeUnit.MINUTE; }
|
    <MINUTES> { return warn(TimeUnit.MINUTE); }
}

TimeUnit Second() :
{
}
{
    <SECOND> { return TimeUnit.SECOND; }
|
    <SECONDS> { return warn(TimeUnit.SECOND); }
}

SqlIntervalQualifier IntervalQualifier() :
{
    final Span s;
    final TimeUnit start;
    final TimeUnit end;
    final int startPrec;
    int secondFracPrec = RelDataType.PRECISION_NOT_SPECIFIED;
}
{
    (
        start = Year() { s = span(); } startPrec = PrecisionOpt()
        (
            LOOKAHEAD(2) <TO> end = Month()
        |   { end = null; }
        )
    |
        start = Quarter() { s = span(); } startPrec = PrecisionOpt()
        { end = null; }
    |
        start = Month() { s = span(); } startPrec = PrecisionOpt()
        { end = null; }
    |
        start = Week() { s = span(); } startPrec = PrecisionOpt()
        { end = null; }
    |
        start = Day() { s = span(); } startPrec = PrecisionOpt()
        (
            LOOKAHEAD(2) <TO>
            (
                end = Hour()
            |
                end = Minute()
            |
                end = Second() secondFracPrec = PrecisionOpt()
            )
        |   { end = null; }
        )
    |
        start = Hour() { s = span(); } startPrec = PrecisionOpt()
        (
            LOOKAHEAD(2) <TO>
            (
                end = Minute()
            |
                end = Second()
                [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
            )
        |   { end = null; }
        )
    |
        start = Minute() { s = span(); } startPrec = PrecisionOpt()
        (
            LOOKAHEAD(2) <TO>
            end = Second()
            [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
        |   { end = null; }
        )
    |
        start = Second() { s = span(); }
        (
            <LPAREN> startPrec = UnsignedIntLiteral()
            [ <COMMA> secondFracPrec = UnsignedIntLiteral() ]
            <RPAREN>
        |   { startPrec = -1; }
        )
        { end = null; }
    )
    {
        return new SqlIntervalQualifier(start, startPrec, end, secondFracPrec,
            s.end(this));
    }
}

/** Interval qualifier without 'TO unit'. */
SqlIntervalQualifier IntervalQualifierStart() :
{
    final Span s;
    final TimeUnit start;
    int startPrec = RelDataType.PRECISION_NOT_SPECIFIED;
    int secondFracPrec = RelDataType.PRECISION_NOT_SPECIFIED;
}
{
    (
        (
            start = Year()
        |   start = Quarter()
        |   start = Month()
        |   start = Week()
        |   start = Day()
        |   start = Hour()
        |   start = Minute()
        )
        { s = span(); }
        startPrec = PrecisionOpt()
    |
        start = Second() { s = span(); }
        [   <LPAREN> startPrec = UnsignedIntLiteral()
            [ <COMMA> secondFracPrec = UnsignedIntLiteral() ]
            <RPAREN>
        ]
    )
    {
        return new SqlIntervalQualifier(start, startPrec, null, secondFracPrec,
            s.end(this));
    }
}

/** Parses a built-in time unit (e.g. "YEAR")
 * or user-defined time frame (e.g. "MINUTE15")
 * and in each case returns a {@link SqlIntervalQualifier}.
 *
 * <p>The units are used in several functions, incuding CEIL, FLOOR, EXTRACT.
 * Includes NANOSECOND, MILLISECOND, which were previously allowed in EXTRACT
 * but not CEIL, FLOOR.
 *
 * <p>Includes {@code WEEK} and {@code WEEK(SUNDAY)} through
  {@code WEEK(SATURDAY)}.
 *
 * <p>Does not include SQL_TSI_DAY, SQL_TSI_FRAC_SECOND etc. These will be
 * parsed as identifiers and can be resolved in the validator if they are
 * registered as abbreviations in your time frame set.
 */
SqlIntervalQualifier TimeUnitOrName() : {
    final Span span;
    final String w;
    final TimeUnit unit;
    final SqlIdentifier unitName;
}
{
    LOOKAHEAD(2)
    <NANOSECOND> { return new SqlIntervalQualifier(TimeUnit.NANOSECOND, null, getPos()); }
|   <MICROSECOND> { return new SqlIntervalQualifier(TimeUnit.MICROSECOND, null, getPos()); }
|   <MILLISECOND> { return new SqlIntervalQualifier(TimeUnit.MILLISECOND, null, getPos()); }
|   <SECOND> { return new SqlIntervalQualifier(TimeUnit.SECOND, null, getPos()); }
|   <MINUTE> { return new SqlIntervalQualifier(TimeUnit.MINUTE, null, getPos()); }
|   <HOUR> { return new SqlIntervalQualifier(TimeUnit.HOUR, null, getPos()); }
|   <DAY> { return new SqlIntervalQualifier(TimeUnit.DAY, null, getPos()); }
|   <DOW> { return new SqlIntervalQualifier(TimeUnit.DOW, null, getPos()); }
|   <DOY> { return new SqlIntervalQualifier(TimeUnit.DOY, null, getPos()); }
|   <ISODOW> { return new SqlIntervalQualifier(TimeUnit.ISODOW, null, getPos()); }
|   <ISOYEAR> { return new SqlIntervalQualifier(TimeUnit.ISOYEAR, null, getPos()); }
|   <WEEK> { span = span(); }
    (
        LOOKAHEAD(2)
        <LPAREN> w = weekdayName() <RPAREN> {
            return new SqlIntervalQualifier(w, span.end(this));
        }
    |
        { return new SqlIntervalQualifier(TimeUnit.WEEK, null, getPos()); }
    )
|   <MONTH> { return new SqlIntervalQualifier(TimeUnit.MONTH, null, getPos()); }
|   <QUARTER> { return new SqlIntervalQualifier(TimeUnit.QUARTER, null, getPos()); }
|   <YEAR> { return new SqlIntervalQualifier(TimeUnit.YEAR, null, getPos()); }
|   <EPOCH> { return new SqlIntervalQualifier(TimeUnit.EPOCH, null, getPos()); }
|   <DECADE> { return new SqlIntervalQualifier(TimeUnit.DECADE, null, getPos()); }
|   <CENTURY> { return new SqlIntervalQualifier(TimeUnit.CENTURY, null, getPos()); }
|   <MILLENNIUM> { return new SqlIntervalQualifier(TimeUnit.MILLENNIUM, null, getPos()); }
|   unitName = SimpleIdentifier() {
        return new SqlIntervalQualifier(unitName.getSimple(),
            unitName.getParserPosition());
    }
}

String weekdayName() :
{
}
{
    <SUNDAY> { return "WEEK_SUNDAY"; }
|   <MONDAY> { return "WEEK_MONDAY"; }
|   <TUESDAY> { return "WEEK_TUESDAY"; }
|   <WEDNESDAY> { return "WEEK_WEDNESDAY"; }
|   <THURSDAY> { return "WEEK_THURSDAY"; }
|   <FRIDAY> { return "WEEK_FRIDAY"; }
|   <SATURDAY> { return "WEEK_SATURDAY"; }
}

/**
 * Parses a dynamic parameter marker.
 */
SqlDynamicParam DynamicParam() :
{
}
{
    <HOOK> {
        return new SqlDynamicParam(nDynamicParams++, getPos());
    }
}

/**
 * Parses one segment of an identifier that may be composite.
 *
 * <p>Each time it reads an identifier it writes one element to each list;
 * the entry in {@code positions} records its position and whether the
 * segment was quoted.
 */
void AddIdentifierSegment(List<String> names, List<SqlParserPos> positions) :
{
    final String id;
    char unicodeEscapeChar = BACKSLASH;
    final SqlParserPos pos;
    final Span span;
}
{
    (
        <IDENTIFIER> {
            id = unquotedIdentifier();
            pos = getPos();
        }
    |
        <HYPHENATED_IDENTIFIER> {
            id = unquotedIdentifier();
            pos = getPos();
        }
    |
        <QUOTED_IDENTIFIER> {
            id = SqlParserUtil.stripQuotes(getToken(0).image, DQ, DQ, DQDQ,
                quotedCasing);
            pos = getPos().withQuoting(true);
        }
    |
        <BACK_QUOTED_IDENTIFIER> {
            id = SqlParserUtil.stripQuotes(getToken(0).image, "`", "`", "``",
                quotedCasing);
            pos = getPos().withQuoting(true);
        }
    |
        <BIG_QUERY_BACK_QUOTED_IDENTIFIER> {
            id = SqlParserUtil.stripQuotes(getToken(0).image, "`", "`", "\\`",
                quotedCasing);
            pos = getPos().withQuoting(true);
        }
    |
        <BRACKET_QUOTED_IDENTIFIER> {
            id = SqlParserUtil.stripQuotes(getToken(0).image, "[", "]", "]]",
                quotedCasing);
            pos = getPos().withQuoting(true);
        }
    |
        <UNICODE_QUOTED_IDENTIFIER> {
            span = span();
            String image = getToken(0).image;
            image = image.substring(image.indexOf('"'));
            image = SqlParserUtil.stripQuotes(image, DQ, DQ, DQDQ, quotedCasing);
        }
        [
            <UESCAPE> <QUOTED_STRING> {
                String s = SqlParserUtil.parseString(token.image);
                unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s);
            }
        ]
        {
            pos = span.end(this).withQuoting(true);
            SqlLiteral lit = SqlLiteral.createCharString(image, "UTF16", pos);
            lit = lit.unescapeUnicode(unicodeEscapeChar);
            id = lit.toValue();
        }
    |
        id = NonReservedKeyWord() {
            pos = getPos();
        }
    )
    {
        if (id.length() > this.identifierMaxLength) {
            throw SqlUtil.newContextException(pos,
                RESOURCE.identifierTooLong(id, this.identifierMaxLength));
        }
        names.add(id);
        if (positions != null) {
            positions.add(pos);
        }
    }
}

/** As {@link #AddIdentifierSegment} but part of a table name (for example,
 * following {@code FROM}, {@code INSERT} or {@code UPDATE}).
 *
 * <p>In some dialects the lexical rules for table names are different from
 * for other identifiers. For example, in BigQuery, table names may contain
 * hyphens. */
void AddTableIdentifierSegment(List<String> names, List<SqlParserPos> positions) :
{
}
{
    AddIdentifierSegment(names, positions) {
        final int n = names.size();
        if (n > 0
                && positions.size() == n
                && names.get(n - 1).contains(".")
                && positions.get(n - 1).isQuoted()
                && this.conformance.splitQuotedTableName()) {
            final String name = names.remove(n - 1);
            final SqlParserPos pos = positions.remove(n - 1);
            final String[] splitNames = name.split("\\.");
            for (String splitName : splitNames) {
                names.add(splitName);
                positions.add(pos);
            }
        }
    }
}

/**
 * Parses a simple identifier as a String.
 */
String Identifier() :
{
    final List<String> names = new ArrayList<String>();
}
{
    AddIdentifierSegment(names, null) {
        return names.get(0);
    }
}

/**
 * Parses a simple identifier as an SqlIdentifier.
 */
SqlIdentifier SimpleIdentifier() :
{
    final List<String> names = new ArrayList<String>();
    final List<SqlParserPos> positions = new ArrayList<SqlParserPos>();
}
{
    AddIdentifierSegment(names, positions) {
        return new SqlIdentifier(names.get(0), positions.get(0));
    }
}

/**
 * Parses a character literal as an SqlIdentifier.
 * Only valid for column aliases in certain dialects.
 */
SqlIdentifier SimpleIdentifierFromStringLiteral() :
{
}
{
    <QUOTED_STRING> {
        if (!this.conformance.allowCharLiteralAlias()) {
            throw SqlUtil.newContextException(getPos(), RESOURCE.charLiteralAliasNotValid());
        }
        final String s = SqlParserUtil.parseString(token.image);
        return new SqlIdentifier(s, getPos());
    }
}

/**
 * Parses a comma-separated list of simple identifiers.
 */
void AddSimpleIdentifiers(List<SqlNode> list) :
{
    SqlIdentifier id;
}
{
    id = SimpleIdentifier() {list.add(id);} [<ASC>] [<DESC>]
    (
        <COMMA> id = SimpleIdentifier() {
            list.add(id);
        } [<ASC>] [<DESC>]
    )*
}

/**
  * List of simple identifiers in parentheses. The position extends from the
  * open parenthesis to the close parenthesis.
  */
SqlNodeList ParenthesizedSimpleIdentifierList() :
{
    final Span s;
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    <LPAREN> { s = span(); }
    AddSimpleIdentifiers(list)
    <RPAREN> {
        return new SqlNodeList(list, s.end(this));
    }
}

/** List of simple identifiers in parentheses or one simple identifier.
 *
 * <ul>Examples:
 * <li>{@code DEPTNO}
 * <li>{@code (EMPNO, DEPTNO)}
 * </ul>
 */
SqlNodeList SimpleIdentifierOrList() :
{
    SqlIdentifier id;
    SqlNodeList list;
}
{
    id = SimpleIdentifier() {
        return new SqlNodeList(Collections.singletonList(id), id.getParserPosition());
    }
|
    list = ParenthesizedSimpleIdentifierList() {
        return list;
    }
}

<#if (parser.includeCompoundIdentifier!) >
/**
 * Parses a compound identifier.
 */
SqlIdentifier CompoundIdentifier() :
{
    final List<String> nameList = new ArrayList<String>();
    final List<SqlParserPos> posList = new ArrayList<SqlParserPos>();
    boolean star = false;
}
{
    AddIdentifierSegment(nameList, posList)
    (
        LOOKAHEAD(2)
        <DOT>
        AddIdentifierSegment(nameList, posList)
    )*
    (
        LOOKAHEAD(2)
        <DOT>
        <STAR> {
            star = true;
            nameList.add("");
            posList.add(getPos());
        }
    )?
    {
        SqlParserPos pos = SqlParserPos.sum(posList);
        if (star) {
            return SqlIdentifier.star(nameList, pos, posList);
        }
        return new SqlIdentifier(nameList, null, pos, posList);
    }
}

/**
 * Parses a compound identifier in the FROM clause.
 */
SqlIdentifier CompoundTableIdentifier() :
{
    final List<String> nameList = new ArrayList<String>();
    final List<SqlParserPos> posList = new ArrayList<SqlParserPos>();
}
{
    AddTableIdentifierSegment(nameList, posList)
    (
        LOOKAHEAD(2)
        <DOT>
        AddTableIdentifierSegment(nameList, posList)
    )*
    {
        SqlParserPos pos = SqlParserPos.sum(posList);
        return new SqlIdentifier(nameList, null, pos, posList);
    }
}

/**
 * Parses a comma-separated list of compound identifiers.
 */
void AddCompoundIdentifierTypes(List<SqlNode> list, List<SqlNode> extendList) :
{
}
{
    AddCompoundIdentifierType(list, extendList)
    (<COMMA> AddCompoundIdentifierType(list, extendList))*
}

/**
 * List of compound identifiers in parentheses. The position extends from the
 * open parenthesis to the close parenthesis.
 */
Pair<SqlNodeList, SqlNodeList> ParenthesizedCompoundIdentifierList() :
{
    final Span s;
    final List<SqlNode> list = new ArrayList<SqlNode>();
    final List<SqlNode> extendList = new ArrayList<SqlNode>();
}
{
    <LPAREN> { s = span(); }
    AddCompoundIdentifierTypes(list, extendList)
    <RPAREN> {
        return Pair.of(new SqlNodeList(list, s.end(this)), new SqlNodeList(extendList, s.end(this)));
    }
}
<#else>
  <#include "/@includes/compoundIdentifier.ftl" />
</#if>

/**
 * Parses a NEW UDT(...) expression.
 */
SqlNode NewSpecification() :
{
    final Span s;
    final SqlNode routineCall;
}
{
    <NEW> { s = span(); }
    routineCall =
        NamedRoutineCall(SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR,
            ExprContext.ACCEPT_SUB_QUERY) {
        return SqlStdOperatorTable.NEW.createCall(s.end(routineCall), routineCall);
    }
}

//TODO: real parse errors.
int UnsignedIntLiteral() :
{
    Token t;
}
{
    t = <UNSIGNED_INTEGER_LITERAL>
    {
        try {
            return Integer.parseInt(t.image);
        } catch (NumberFormatException ex) {
            throw SqlUtil.newContextException(getPos(),
                RESOURCE.invalidLiteral(t.image, Integer.class.getCanonicalName()));
        }
    }
}

int IntLiteral() :
{
    Token t;
}
{
    (
        t = <UNSIGNED_INTEGER_LITERAL>
    |
        <PLUS> t = <UNSIGNED_INTEGER_LITERAL>
    )
    {
        try {
            return Integer.parseInt(t.image);
        } catch (NumberFormatException ex) {
            throw SqlUtil.newContextException(getPos(),
                RESOURCE.invalidLiteral(t.image, Integer.class.getCanonicalName()));
        }
    }
|
    <MINUS> t = <UNSIGNED_INTEGER_LITERAL> {
        try {
            return -Integer.parseInt(t.image);
        } catch (NumberFormatException ex) {
            throw SqlUtil.newContextException(getPos(),
                RESOURCE.invalidLiteral(t.image, Integer.class.getCanonicalName()));
        }
    }
}

// Type name with optional scale and precision.
SqlDataTypeSpec DataType() :
{
    SqlTypeNameSpec typeName;
    final Span s;
}
{
    typeName = TypeName() {
        s = Span.of(typeName.getParserPos());
    }
    (
        typeName = CollectionsTypeName(typeName)
    )*
    {
        return new SqlDataTypeSpec(typeName, s.add(typeName.getParserPos()).pos());
    }
}

// Some SQL type names need special handling due to the fact that they have
// spaces in them but are not quoted.
SqlTypeNameSpec TypeName() :
{
    final SqlTypeNameSpec typeNameSpec;
    final SqlIdentifier typeName;
    final Span s = Span.of();
}
{
    (
<#-- additional types are included here -->
<#-- put custom data types in front of Calcite core data types -->
<#list (parser.dataTypeParserMethods!) as method>
        LOOKAHEAD(2)
        typeNameSpec = ${method}
    |
</#list>
        LOOKAHEAD(2)
        typeNameSpec = SqlTypeName(s)
    |
        typeNameSpec = RowTypeName()
    |
        typeName = CompoundIdentifier() {
            typeNameSpec = new SqlUserDefinedTypeNameSpec(typeName, s.end(this));
        }
    )
    {
        return typeNameSpec;
    }
}

// Types used for JDBC and ODBC scalar conversion function
SqlTypeNameSpec SqlTypeName(Span s) :
{
    final SqlTypeNameSpec sqlTypeNameSpec;
}
{
    (
    <#list (parser.sqlTypeNameMethods!) as method>
        sqlTypeNameSpec = ${method}(s)
    |
    </#list>
        sqlTypeNameSpec = SqlTypeName1(s)
    |
        sqlTypeNameSpec = SqlTypeName2(s)
    |
        sqlTypeNameSpec = SqlTypeName3(s)
    |
        sqlTypeNameSpec = CharacterTypeName(s)
    |
        sqlTypeNameSpec = DateTimeTypeName()
    )
    {
        return sqlTypeNameSpec;
    }
}

// Parse sql type name that don't allow any extra specifications except the type name.
// For extra specification, we mean precision, scale, charSet, etc.
SqlTypeNameSpec SqlTypeName1(Span s) :
{
    final SqlTypeName sqlTypeName;
    int precision = -1;
    int scale = -1;
}
{
    (
        <GEOMETRY> {
            if (!this.conformance.allowGeometry()) {
                throw SqlUtil.newContextException(getPos(), RESOURCE.geometryDisabled());
            }
            s.add(this);
            sqlTypeName = SqlTypeName.GEOMETRY;
        }
    |
        <BOOLEAN> { s.add(this); sqlTypeName = SqlTypeName.BOOLEAN; }
    |
        <INTEGER> { s.add(this); sqlTypeName = SqlTypeName.INTEGER; } [
                         <LPAREN>
                         precision = UnsignedIntLiteral()
                         [
                             <COMMA>
                             scale = UnsignedIntLiteral()
                         ]
                         <RPAREN>
                     ] [<UNSIGNED>]
    |
        <INT>  [
                   <LPAREN>
                         precision = UnsignedIntLiteral()
                         [
                             <COMMA>
                             scale = UnsignedIntLiteral()
                         ]
                         <RPAREN>
                     ] [<UNSIGNED>] { s.add(this); sqlTypeName = SqlTypeName.INTEGER; }
    |
        <TINYINT> { s.add(this); sqlTypeName = SqlTypeName.TINYINT; }
    |
        <SMALLINT> { s.add(this); sqlTypeName = SqlTypeName.SMALLINT; }
    |
        <BIGINT>    [
                         <LPAREN>
                         precision = UnsignedIntLiteral()
                         [
                             <COMMA>
                             scale = UnsignedIntLiteral()
                         ]
                         <RPAREN>
                     ] { s.add(this); sqlTypeName = SqlTypeName.BIGINT; } [<UNSIGNED>]
    |
        <REAL> { s.add(this); sqlTypeName = SqlTypeName.REAL; }
    |
        <DOUBLE> { s.add(this); }
        [ <PRECISION> ] { sqlTypeName = SqlTypeName.DOUBLE; }
    |
        <FLOAT> { s.add(this); sqlTypeName = SqlTypeName.FLOAT; }
    )
    {
        return new SqlBasicTypeNameSpec(sqlTypeName, s.end(this));
    }
}

// Parse sql type name that allows precision specification.
SqlTypeNameSpec SqlTypeName2(Span s) :
{
    final SqlTypeName sqlTypeName;
    int precision = -1;
}
{
    (
        <BINARY> { s.add(this); }
        (
            <VARYING> { sqlTypeName = SqlTypeName.VARBINARY; }
        |
            { sqlTypeName = SqlTypeName.BINARY; }
        )
    |
        <VARBINARY> { s.add(this); sqlTypeName = SqlTypeName.VARBINARY; }
    )
    precision = PrecisionOpt()
    {
        return new SqlBasicTypeNameSpec(sqlTypeName, precision, s.end(this));
    }
}

// Parse sql type name that allows precision and scale specifications.
SqlTypeNameSpec SqlTypeName3(Span s) :
{
    final SqlTypeName sqlTypeName;
    int precision = -1;
    int scale = -1;
}
{
    (
        (<DECIMAL> | <DEC> | <NUMERIC>) { s.add(this); sqlTypeName = SqlTypeName.DECIMAL; }
    |
        <ANY> { s.add(this); sqlTypeName = SqlTypeName.ANY; }
    )
    [
        <LPAREN>
        precision = UnsignedIntLiteral()
        [
            <COMMA>
            scale = UnsignedIntLiteral()
        ]
        <RPAREN>
    ]
    {
        return new SqlBasicTypeNameSpec(sqlTypeName, precision, scale, s.end(this));
    }
}

// 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());
    }
}

/**
* Parse a collection type name, the input element type name may
* also be a collection type.
*/
SqlTypeNameSpec CollectionsTypeName(SqlTypeNameSpec elementTypeName) :
{
    final SqlTypeName collectionTypeName;
}
{
    (
        <MULTISET> { collectionTypeName = SqlTypeName.MULTISET; }
    |
        <ARRAY> { collectionTypeName = SqlTypeName.ARRAY; }
    )
    {
        return new SqlCollectionTypeNameSpec(elementTypeName,
                collectionTypeName, getPos());
    }
}

/**
* Parse a nullable option, default is true.
*/
boolean NullableOptDefaultTrue() :
{
}
{
    <NULL> { return true; }
|
    <NOT> <NULL> { return false; }
|
    { return true; }
}

/**
* Parse a nullable option, default is false.
*/
boolean NullableOptDefaultFalse() :
{
}
{
    <NULL> { return true; }
|
    <NOT> <NULL> { return false; }
|
    { return false; }
}

/** Parses NOT NULL and returns false, or parses nothing and returns true. */
boolean NotNullOpt() :
{
}
{
    <NOT> <NULL> { return false; }
|
    { return true; }
}

/**
* Parse a "name1 type1 [NULL | NOT NULL], name2 type2 [NULL | NOT NULL] ..." list,
* the field type default is not nullable.
*/
void AddFieldNameTypes(List<SqlIdentifier> fieldNames,
    List<SqlDataTypeSpec> fieldTypes) :
{
}
{
    AddFieldNameType(fieldNames, fieldTypes)
    ( <COMMA> AddFieldNameType(fieldNames, fieldTypes) )*
}

void AddFieldNameType(List<SqlIdentifier> fieldNames,
    List<SqlDataTypeSpec> fieldTypes) :
{
    final SqlIdentifier fName;
    final SqlDataTypeSpec fType;
    final boolean nullable;
}
{
    fName = SimpleIdentifier()
    fType = DataType()
    nullable = NullableOptDefaultFalse()
    {
        fieldNames.add(fName);
        fieldTypes.add(fType.withNullable(nullable, getPos()));
    }
}

/**
* Parse Row type with format: Row(name1 type1, name2 type2).
* Every field type can have suffix of `NULL` or `NOT NULL` to indicate if this type is nullable.
* i.e. Row(f0 int not null, f1 varchar null).
*/
SqlTypeNameSpec RowTypeName() :
{
    List<SqlIdentifier> fieldNames = new ArrayList<SqlIdentifier>();
    List<SqlDataTypeSpec> fieldTypes = new ArrayList<SqlDataTypeSpec>();
}
{
    <ROW>
    <LPAREN> AddFieldNameTypes(fieldNames, fieldTypes) <RPAREN>
    {
        return new SqlRowTypeNameSpec(getPos(), fieldNames, fieldTypes);
    }
}

/**
* Parse character types: char, varchar.
*/
SqlTypeNameSpec CharacterTypeName(Span s) :
{
    int precision = -1;
    final SqlTypeName sqlTypeName;
    String charSetName = null;
}
{
    (
        (<CHARACTER> | <CHAR>) { s.add(this); }
        (
            <VARYING> { sqlTypeName = SqlTypeName.VARCHAR; }
        |
            { sqlTypeName = SqlTypeName.CHAR; }
        )
    |
        <VARCHAR> { s.add(this); sqlTypeName = SqlTypeName.VARCHAR; }
    )
    precision = PrecisionOpt()
    [
        <CHARACTER> <SET>
        charSetName = Identifier()
    ]
    {
        if (charSetName != null) {
            charSetName = charSetName.toUpperCase();
        }
        return new SqlBasicTypeNameSpec(sqlTypeName, precision, charSetName, s.end(this));
    }
}

/**
* Parse datetime types: date, time, timestamp.
*/
SqlTypeNameSpec DateTimeTypeName() :
{
    int precision = -1;
    SqlTypeName typeName;
    boolean withLocalTimeZone = false;
    final Span s;
}
{
    <DATE> {
        typeName = SqlTypeName.DATE;
        return new SqlBasicTypeNameSpec(typeName, getPos());
    }
|
    LOOKAHEAD(2)
    <TIME> { s = span(); }
    precision = PrecisionOpt()
    withLocalTimeZone = TimeZoneOpt()
    {
        if (withLocalTimeZone) {
            typeName = SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE;
        } else {
            typeName = SqlTypeName.TIME;
        }
        return new SqlBasicTypeNameSpec(typeName, precision, s.end(this));
    }
|
    <TIMESTAMP>  { s = span(); }
    precision = PrecisionOpt()
    withLocalTimeZone = TimeZoneOpt()
    {
        if (withLocalTimeZone) {
            typeName = SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE;
        } else {
            typeName = SqlTypeName.TIMESTAMP;
        }
        return new SqlBasicTypeNameSpec(typeName, precision, s.end(this));
    }
}

// Parse an optional data type precision, default is -1.
int PrecisionOpt() :
{
    int precision = -1;
}
{
    <LPAREN>
    precision = UnsignedIntLiteral()
    <RPAREN>
    { return precision; }
|
    { return -1; }
}

/**
* Parse a time zone suffix for DateTime types. According to SQL-2011,
* "with time zone" and "without time zone" belong to standard SQL but we
* only implement the "without time zone".
*
* <p>We also support "with local time zone".
*
* @return true if this is "with local time zone".
*/
boolean TimeZoneOpt() :
{
}
{
    LOOKAHEAD(3)
    <WITHOUT> <TIME> <ZONE> { return false; }
|
    <WITH> <LOCAL> <TIME> <ZONE> { return true; }
|
    { return false; }
}

/**
 * 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) :
{
    final SqlNode e;
    final Span s;
}
{
    <CURSOR> {
        s = span();
        if (exprContext != ExprContext.ACCEPT_ALL
                && exprContext != ExprContext.ACCEPT_CURSOR) {
            throw SqlUtil.newContextException(s.end(this),
                RESOURCE.illegalCursorExpression());
        }
    }
    e = Expression(ExprContext.ACCEPT_QUERY) {
        return SqlStdOperatorTable.CURSOR.createCall(s.end(e), e);
    }
}

/**
 * Parses a call to a builtin function with special syntax.
 */
SqlNode BuiltinFunctionCall() :
{
    final SqlIdentifier name;
    final List<SqlNode> args = new ArrayList<SqlNode>();
    SqlNode e;
    final Span s;
    SqlDataTypeSpec dt;
    final SqlIntervalQualifier unit;
    final SqlNode node;
}
{
    //~ FUNCTIONS WITH SPECIAL SYNTAX ---------------------------------------
    (
        <CAST> { s = span(); }
        <LPAREN> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        <AS>
        (
            dt = DataType() { args.add(dt); }
        |
            <INTERVAL> e = IntervalQualifier() { args.add(e); }
        )
        <RPAREN> {
            return SqlStdOperatorTable.CAST.createCall(s.end(this), args);
        }
    |
        <EXTRACT> { s = span(); }
        <LPAREN> unit = TimeUnitOrName() {
            args.add(unit);
        }
        <FROM>
        AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        <RPAREN> {
            return SqlStdOperatorTable.EXTRACT.createCall(s.end(this), args);
        }
    |
        <POSITION> { s = span(); }
        <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.add(e); }
        <IN>
        AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        [ <FROM> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) ]
        <RPAREN> {
            return SqlStdOperatorTable.POSITION.createCall(s.end(this), args);
        }
    |
        <CONVERT> { s = span(); }
        <LPAREN>
        AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        <USING> name = SimpleIdentifier() { args.add(name); }
        <RPAREN> {
            return SqlStdOperatorTable.CONVERT.createCall(s.end(this), args);
        }
    |
        <TRANSLATE> { s = span(); }
        <LPAREN>
        AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        (
            <USING> name = SimpleIdentifier() { args.add(name); }
            <RPAREN> {
                return SqlStdOperatorTable.TRANSLATE.createCall(s.end(this),
                    args);
            }
        |
            ( <COMMA> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) )*
            <RPAREN> {
                return SqlLibraryOperators.TRANSLATE3.createCall(s.end(this),
                    args);
            }
        )
    |
        <OVERLAY> { s = span(); }
        <LPAREN> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        <PLACING> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        <FROM> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        [ <FOR> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) ]
        <RPAREN> {
            return SqlStdOperatorTable.OVERLAY.createCall(s.end(this), args);
        }
    |
        <FLOOR> { s = span(); }
        e = FloorCeilOptions(s, true) {
            return e;
        }
    |
        ( <CEIL> | <CEILING>) { s = span(); }
        e = FloorCeilOptions(s, false) {
            return e;
        }
    |
        <SUBSTRING> { s = span(); }
        <LPAREN>
        AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        ( <FROM> | <COMMA>)
        AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        [
            (<FOR> | <COMMA>)
            AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
        ]
        <RPAREN> {
            return SqlStdOperatorTable.SUBSTRING.createCall(
                s.end(this), args);
        }
    |
        <TRIM> {
            SqlLiteral flag = null;
            SqlNode trimChars = null;
            s = span();
        }
        <LPAREN>
        [
            LOOKAHEAD(2)
            [
                <BOTH> {
                    s.add(this);
                    flag = SqlTrimFunction.Flag.BOTH.symbol(getPos());
                }
            |
                <TRAILING> {
                    s.add(this);
                    flag = SqlTrimFunction.Flag.TRAILING.symbol(getPos());
                }
            |
                <LEADING> {
                    s.add(this);
                    flag = SqlTrimFunction.Flag.LEADING.symbol(getPos());
                }
            ]
            [ 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.add(flag);
                    args.add(null); // no trim chars
                    args.add(trimChars); // reinterpret trimChars as source
                    return SqlStdOperatorTable.TRIM.createCall(s.end(this),
                        args);
                }
            )
        ]
        e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
            if (flag == null) {
                flag = SqlTrimFunction.Flag.BOTH.symbol(SqlParserPos.ZERO);
            }
            args.add(flag);
            args.add(trimChars);
            args.add(e);
        }
        <RPAREN> {
            return SqlStdOperatorTable.TRIM.createCall(s.end(this), args);
        }
    |
        node = DateTruncFunctionCall() { return node; }
    |
        node = TimestampAddFunctionCall() { return node; }
    |
        node = TimestampDiffFunctionCall() { return node; }
    |
        node = TimestampDiff3FunctionCall() { return node; }
    |
        node = TimestampTruncFunctionCall() { return node; }
    |
        node = TimeDiffFunctionCall() { return node; }
    |
        node = TimeTruncFunctionCall() { return node; }
    |
<#list (parser.builtinFunctionCallMethods!) as method>
        node = ${method} { return node; }
    |
</#list>
        node = MatchRecognizeFunctionCall() { return node; }
    |
        node = JsonExistsFunctionCall() { return node; }
    |
        node = JsonValueFunctionCall() { return node; }
    |
        node = JsonQueryFunctionCall() { return node; }
    |
        node = JsonObjectFunctionCall() { return node; }
    |
        node = JsonObjectAggFunctionCall() { return node; }
    |
        node = JsonArrayFunctionCall() { return node; }
    |
        node = JsonArrayAggFunctionCall() { return node; }
    |
        node = GroupByWindowingCall() { return node; }
    )
}

SqlJsonEncoding JsonRepresentation() :
{
}
{
    <JSON>
    [
        // Encoding is currently ignored.
        LOOKAHEAD(2) <ENCODING>
        (
            <UTF8> { return SqlJsonEncoding.UTF8; }
        |
            <UTF16> { return SqlJsonEncoding.UTF16; }
        |
            <UTF32> { return SqlJsonEncoding.UTF32; }
        )
    ]
    {
        return SqlJsonEncoding.UTF8;
    }
}

void JsonInputClause() :
{
}
{
    <FORMAT> JsonRepresentation()
}

SqlDataTypeSpec JsonReturningClause() :
{
    SqlDataTypeSpec dt;
}
{
    <RETURNING> dt = DataType() { return dt; }
}

SqlDataTypeSpec JsonOutputClause() :
{
    SqlDataTypeSpec dataType;
}
{
    dataType = JsonReturningClause()
    [
        <FORMAT> JsonRepresentation()
    ]
    {
        return dataType;
    }
}

SqlNode JsonPathSpec() :
{
    SqlNode e;
}
{
    e = StringLiteral() {
        return e;
    }
}

List<SqlNode> JsonApiCommonSyntax() :
{
    SqlNode e;
    final List<SqlNode> args = new ArrayList<SqlNode>();
}
{
    AddExpression(args, ExprContext.ACCEPT_NON_QUERY)
    <COMMA>
    AddExpression(args, ExprContext.ACCEPT_NON_QUERY)
    [
        // We currently don't support JSON passing clause, leave the java code blocks no-op
        <PASSING> e = Expression(ExprContext.ACCEPT_NON_QUERY) {
            // no-op
        }
        <AS> e = SimpleIdentifier() {
            // no-op
        }
        (
            <COMMA>
            e = Expression(ExprContext.ACCEPT_NON_QUERY) {
                // no-op
            }
            <AS> e = SimpleIdentifier() {
                // no-op
            }
        )*
    ]
    {
        return args;
    }
}

SqlJsonExistsErrorBehavior JsonExistsErrorBehavior() :
{

}
{
    <TRUE> { return SqlJsonExistsErrorBehavior.TRUE; }
    |
    <FALSE> { return SqlJsonExistsErrorBehavior.FALSE; }
    |
    <UNKNOWN> { return SqlJsonExistsErrorBehavior.UNKNOWN; }
    |
    <ERROR> { return SqlJsonExistsErrorBehavior.ERROR; }
}

SqlCall JsonExistsFunctionCall() :
{
    List<SqlNode> args = new ArrayList<SqlNode>();
    List<SqlNode> commonSyntax;
    final Span span;
    SqlJsonExistsErrorBehavior errorBehavior;
}
{
    <JSON_EXISTS> { span = span(); }
    <LPAREN> commonSyntax = JsonApiCommonSyntax() {
        args.addAll(commonSyntax);
    }
    [
        errorBehavior = JsonExistsErrorBehavior() { args.add(errorBehavior.symbol(getPos())); }
        <ON> <ERROR>
    ]
    <RPAREN> {
        return SqlStdOperatorTable.JSON_EXISTS.createCall(span.end(this), args);
    }
}

List<SqlNode> JsonValueEmptyOrErrorBehavior() :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    (
        <ERROR> {
            list.add(SqlJsonValueEmptyOrErrorBehavior.ERROR.symbol(getPos()));
        }
    |
        <NULL> {
            list.add(SqlJsonValueEmptyOrErrorBehavior.NULL.symbol(getPos()));
        }
    |
        <DEFAULT_> {
            list.add(SqlJsonValueEmptyOrErrorBehavior.DEFAULT.symbol(getPos()));
        }
        AddExpression(list, ExprContext.ACCEPT_NON_QUERY)
    )
    <ON>
    (
        <EMPTY> {
            list.add(SqlJsonEmptyOrError.EMPTY.symbol(getPos()));
        }
    |
        <ERROR> {
            list.add(SqlJsonEmptyOrError.ERROR.symbol(getPos()));
        }
    )
    { return list; }
}

SqlCall JsonValueFunctionCall() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>(7);
    SqlNode e;
    List<SqlNode> commonSyntax;
    final Span span;
    List<SqlNode> behavior;
}
{
    <JSON_VALUE> { span = span(); }
    <LPAREN> commonSyntax = JsonApiCommonSyntax() {
        args.addAll(commonSyntax);
    }
    [
        e = JsonReturningClause() {
            args.add(SqlJsonValueReturning.RETURNING.symbol(getPos()));
            args.add(e);
        }
    ]
    (
        behavior = JsonValueEmptyOrErrorBehavior() {
            args.addAll(behavior);
        }
    )*
    <RPAREN> {
        return SqlStdOperatorTable.JSON_VALUE.createCall(span.end(this), args);
    }
}

List<SqlNode> JsonQueryEmptyOrErrorBehavior() :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
}
{
    (
        <ERROR> {
            list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.ERROR, getPos()));
        }
    |
        <NULL> {
            list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.NULL, getPos()));
        }
    |
        LOOKAHEAD(2)
        <EMPTY> <ARRAY> {
            list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.EMPTY_ARRAY, getPos()));
        }
    |
        <EMPTY> <OBJECT> {
            list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.EMPTY_OBJECT, getPos()));
        }
    )
    <ON>
    (
        <EMPTY> {
            list.add(SqlLiteral.createSymbol(SqlJsonEmptyOrError.EMPTY, getPos()));
        }
    |
        <ERROR> {
            list.add(SqlLiteral.createSymbol(SqlJsonEmptyOrError.ERROR, getPos()));
        }
    )
    { return list; }
}

SqlNode JsonQueryWrapperBehavior() :
{
}
{
    <WITHOUT> [<ARRAY>] {
        return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITHOUT_ARRAY, getPos());
    }
|
    LOOKAHEAD(2)
    <WITH> <CONDITIONAL> [<ARRAY>] {
        return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITH_CONDITIONAL_ARRAY, getPos());
    }
|
    <WITH> [<UNCONDITIONAL>] [<ARRAY>] {
        return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITH_UNCONDITIONAL_ARRAY, getPos());
    }
}

SqlCall JsonQueryFunctionCall() :
{
    final SqlNode[] args = new SqlNode[5];
    SqlNode e;
    List<SqlNode> commonSyntax;
    final Span span;
    List<SqlNode> behavior;
}
{
    <JSON_QUERY> { span = span(); }
    <LPAREN> commonSyntax = JsonApiCommonSyntax() {
        args[0] = commonSyntax.get(0);
        args[1] = commonSyntax.get(1);
    }
    [
        e = JsonQueryWrapperBehavior() <WRAPPER> {
            args[2] = e;
        }
    ]
    (
        behavior = JsonQueryEmptyOrErrorBehavior() {
            final SqlJsonEmptyOrError symbol =
                ((SqlLiteral) behavior.get(1)).getValueAs(SqlJsonEmptyOrError.class);
            switch (symbol) {
            case EMPTY:
                args[3] = behavior.get(0);
                break;
            case ERROR:
                args[4] = behavior.get(0);
                break;
            }
        }
    )*
    <RPAREN> {
        return SqlStdOperatorTable.JSON_QUERY.createCall(span.end(this), args);
    }
}

SqlNode JsonName() :
{
    final SqlNode e;
}
{
     e = Expression(ExprContext.ACCEPT_NON_QUERY) {
        return e;
     }
}

List<SqlNode> JsonNameAndValue() :
{
    final List<SqlNode> list = new ArrayList<SqlNode>();
    final SqlNode e;
    boolean kvMode = false;
}
{
    [
        LOOKAHEAD(2, <KEY> JsonName())
        <KEY> { kvMode = true; }
    ]
    e = JsonName() {
        list.add(e);
    }
    (
        <VALUE>
    |
        <COLON> {
            if (kvMode) {
                throw SqlUtil.newContextException(getPos(), RESOURCE.illegalColon());
            }
        }
    )
    AddExpression(list, ExprContext.ACCEPT_NON_QUERY)
    {
        return list;
    }
}

SqlNode JsonConstructorNullClause() :
{
}
{
    <NULL> <ON> <NULL> {
        return SqlLiteral.createSymbol(SqlJsonConstructorNullClause.NULL_ON_NULL, getPos());
    }
|
    <ABSENT> <ON> <NULL> {
        return SqlLiteral.createSymbol(SqlJsonConstructorNullClause.ABSENT_ON_NULL, getPos());
    }
}

SqlCall JsonObjectFunctionCall() :
{
    final List<SqlNode> nvArgs = new ArrayList<SqlNode>();
    final SqlNode[] otherArgs = new SqlNode[1];
    SqlNode e;
    List<SqlNode> list;
    final Span span;
}
{
    <JSON_OBJECT> { span = span(); }
    <LPAREN> [
        LOOKAHEAD(2)
        list = JsonNameAndValue() {
            nvArgs.addAll(list);
        }
        (
            <COMMA>
            list = JsonNameAndValue() {
                nvArgs.addAll(list);
            }
        )*
    ]
    [
        e = JsonConstructorNullClause() {
            otherArgs[0] = e;
        }
    ]
    <RPAREN> {
        final List<SqlNode> args = new ArrayList();
        args.addAll(Arrays.asList(otherArgs));
        args.addAll(nvArgs);
        return SqlStdOperatorTable.JSON_OBJECT.createCall(span.end(this), args);
    }
}

SqlCall JsonObjectAggFunctionCall() :
{
    final SqlNode[] args = new SqlNode[2];
    List<SqlNode> list;
    final Span span;
    SqlJsonConstructorNullClause nullClause =
        SqlJsonConstructorNullClause.NULL_ON_NULL;
    final SqlNode e;
}
{
    <JSON_OBJECTAGG> { span = span(); }
    <LPAREN> list = JsonNameAndValue() {
        args[0] = list.get(0);
        args[1] = list.get(1);
    }
    [
        e = JsonConstructorNullClause() {
            nullClause = (SqlJsonConstructorNullClause) ((SqlLiteral) e).getValue();
        }
    ]
    <RPAREN> {
        return SqlStdOperatorTable.JSON_OBJECTAGG.with(nullClause)
            .createCall(span.end(this), args);
    }
}

SqlCall JsonArrayFunctionCall() :
{
    final List<SqlNode> elements = new ArrayList<SqlNode>();
    final SqlNode[] otherArgs = new SqlNode[1];
    SqlNode e;
    final Span span;
}
{
    <JSON_ARRAY> { span = span(); }
    <LPAREN> [
        LOOKAHEAD(2)
        AddExpression(elements, ExprContext.ACCEPT_NON_QUERY)
        ( <COMMA> AddExpression(elements, ExprContext.ACCEPT_NON_QUERY) )*
    ]
    [
        e = JsonConstructorNullClause() {
            otherArgs[0] = e;
        }
    ]
    <RPAREN> {
        final List<SqlNode> args = new ArrayList();
        args.addAll(Arrays.asList(otherArgs));
        args.addAll(elements);
        return SqlStdOperatorTable.JSON_ARRAY.createCall(span.end(this), args);
    }
}

SqlNodeList JsonArrayAggOrderByClause() :
{
    final SqlNodeList orderList;
}
{
    orderList = OrderBy(true)
    { return orderList; }
}

SqlCall JsonArrayAggFunctionCall() :
{
    final SqlNode valueExpr;
    final SqlNodeList orderList;
    final Span span;
    final SqlJsonConstructorNullClause nullClause;
    SqlNode e;
    final SqlNode aggCall;
}
{
    <JSON_ARRAYAGG> { span = span(); }
    <LPAREN> e = Expression(ExprContext.ACCEPT_NON_QUERY) {
        valueExpr = e;
    }
    ( orderList = JsonArrayAggOrderByClause() | { orderList = null; } )
    (
        e = JsonConstructorNullClause() {
            nullClause = (SqlJsonConstructorNullClause) ((SqlLiteral) e).getValue();
        }
    |   { nullClause = SqlJsonConstructorNullClause.ABSENT_ON_NULL; }
    )
    <RPAREN>
    {
        aggCall = SqlStdOperatorTable.JSON_ARRAYAGG.with(nullClause)
            .createCall(span.end(this), valueExpr, orderList);
    }
    [
        e = withinGroup(aggCall) {
            if (orderList != null) {
                throw SqlUtil.newContextException(span.pos().plus(e.getParserPosition()),
                    RESOURCE.ambiguousSortOrderInJsonArrayAggFunc());
            }
            return (SqlCall) e;
        }
    ]
    {
        if (orderList == null) {
            return SqlStdOperatorTable.JSON_ARRAYAGG.with(nullClause)
                .createCall(span.end(this), valueExpr);
        }
        return SqlStdOperatorTable.JSON_ARRAYAGG.with(nullClause)
            .createCall(span.end(this), valueExpr, orderList);
    }
}

/**
 * Parses a call to TIMESTAMPADD.
 */
SqlCall TimestampAddFunctionCall() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final Span s;
    final SqlIntervalQualifier unit;
}
{
    <TIMESTAMPADD> { s = span(); }
    <LPAREN>
    unit = TimeUnitOrName() { args.add(unit); }
    <COMMA>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <RPAREN> {
        return SqlStdOperatorTable.TIMESTAMP_ADD.createCall(
            s.end(this), args);
    }
}

/**
 * Parses a call to TIMESTAMPDIFF.
 */
SqlCall TimestampDiffFunctionCall() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final Span s;
    final SqlIntervalQualifier unit;
}
{
    <TIMESTAMPDIFF> { s = span(); }
    <LPAREN>
    unit = TimeUnitOrName() { args.add(unit); }
    <COMMA>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <RPAREN> {
        return SqlStdOperatorTable.TIMESTAMP_DIFF.createCall(
            s.end(this), args);
    }
}

/**
 * Parses a call to BigQuery's TIMESTAMP_DIFF.
 *
 * <p>The difference between TIMESTAMPDIFF and TIMESTAMP_DIFF is the ordering of
 * the parameters and the arrangement of the subtraction.
 * TIMESTAMPDIFF uses (unit, timestamp1, timestamp2) with (t2 - t1), while
 * TIMESTAMP_DIFF uses (timestamp1, timestamp2, unit) with (t1 - t2).
 */
SqlCall TimestampDiff3FunctionCall() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final Span s;
    final SqlIntervalQualifier unit;
}
{
    <TIMESTAMP_DIFF> { s = span(); }
    <LPAREN>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    unit = TimeUnitOrName() { args.add(unit); }
    <RPAREN> {
        return SqlLibraryOperators.TIMESTAMP_DIFF3.createCall(s.end(this), args);
    }
}

/**
 * Parses a call to DATE_TRUNC.
 */
SqlCall DateTruncFunctionCall() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final Span s;
    final SqlIntervalQualifier unit;
}
{
    <DATE_TRUNC> { s = span(); }
    <LPAREN>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    // A choice of arguments allows us to support both
    // the BigQuery variant, e.g. "DATE_TRUNC(d, YEAR)",
    // and the Redshift variant, e.g. "DATE_TRUNC('year', DATE '2008-09-08')".
    (
        unit = TimeUnitOrName() { args.add(unit); }
    |
        AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    )
    <RPAREN> {
        return SqlLibraryOperators.DATE_TRUNC.createCall(s.end(this), args);
    }
}

/**
 * Parses a call to TIMESTAMP_TRUNC.
 */
SqlCall TimestampTruncFunctionCall() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final Span s;
    final SqlIntervalQualifier unit;
}
{
    <TIMESTAMP_TRUNC> { s = span(); }
    <LPAREN>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    unit = TimeUnitOrName() { args.add(unit); }
    <RPAREN> {
        return SqlLibraryOperators.TIMESTAMP_TRUNC.createCall(s.end(this), args);
    }
}

/**
 * Parses a call to BigQuery's TIME_DIFF.
 */
SqlCall TimeDiffFunctionCall() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final Span s;
    final SqlIntervalQualifier unit;
}
{
    <TIME_DIFF> { s = span(); }
    <LPAREN>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    unit = TimeUnitOrName() { args.add(unit); }
    <RPAREN> {
        return SqlLibraryOperators.TIME_DIFF.createCall(s.end(this), args);
    }
}

/**
 * Parses a call to TIME_TRUNC.
 */
SqlCall TimeTruncFunctionCall() :
{
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final Span s;
    final SqlIntervalQualifier unit;
}
{
    <TIME_TRUNC> { s = span(); }
    <LPAREN>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    <COMMA>
    unit = TimeUnitOrName() { args.add(unit); }
    <RPAREN> {
        return SqlLibraryOperators.TIME_TRUNC.createCall(s.end(this), args);
    }
}

/**
 * Parses a call to a grouping function inside the GROUP BY clause,
 * for example {@code TUMBLE(rowtime, INTERVAL '1' MINUTE)}.
 */
SqlCall GroupByWindowingCall() :
{
    final Span s;
    final List<SqlNode> args;
    final SqlOperator op;
}
{
    (
        <TUMBLE> { op = SqlStdOperatorTable.TUMBLE_OLD; }
    |
        <HOP> { op = SqlStdOperatorTable.HOP_OLD; }
    |
        <SESSION> { op = SqlStdOperatorTable.SESSION_OLD; }
    )
    { s = span(); }
    args = UnquantifiedFunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
        return op.createCall(s.end(this), args);
    }
}

SqlCall MatchRecognizeFunctionCall() :
{
    final SqlCall func;
    final Span s;
}
{
    (
        <CLASSIFIER> { s = span(); } <LPAREN> <RPAREN> {
            func = SqlStdOperatorTable.CLASSIFIER.createCall(s.end(this));
        }
    |
        <MATCH_NUMBER> { s = span(); } <LPAREN> <RPAREN> {
            func = SqlStdOperatorTable.MATCH_NUMBER.createCall(s.end(this));
        }
    |
        LOOKAHEAD(3)
        func = MatchRecognizeNavigationLogical()
    |
        LOOKAHEAD(2)
        func = MatchRecognizeNavigationPhysical()
    |
        func = MatchRecognizeCallWithModifier()
    )
    { return func; }
}

SqlCall MatchRecognizeCallWithModifier() :
{
    final Span s;
    final SqlOperator runningOp;
    final SqlNode func;
}
{
    (
        <RUNNING> { runningOp = SqlStdOperatorTable.RUNNING; }
    |
        <FINAL> { runningOp = SqlStdOperatorTable.FINAL; }
    )
    { s = span(); }
    func = NamedFunctionCall() {
        return runningOp.createCall(s.end(func), func);
    }
}

SqlCall MatchRecognizeNavigationLogical() :
{
    final Span s = Span.of();
    SqlCall func;
    final SqlOperator funcOp;
    final SqlOperator runningOp;
    final List<SqlNode> args = new ArrayList<SqlNode>();
    SqlNode e;
}
{
    (
        <RUNNING> { runningOp = SqlStdOperatorTable.RUNNING; s.add(this); }
    |
        <FINAL> { runningOp = SqlStdOperatorTable.FINAL; s.add(this); }
    |   { runningOp = null; }
    )
    (
        <FIRST> { funcOp = SqlStdOperatorTable.FIRST; }
    |
        <LAST> { funcOp = SqlStdOperatorTable.LAST; }
    )
    { s.add(this); }
    <LPAREN>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    (
        <COMMA> e = NumericLiteral() { args.add(e); }
    |
        { args.add(LITERAL_ZERO); }
    )
    <RPAREN> {
        func = funcOp.createCall(s.end(this), args);
        if (runningOp != null) {
            return runningOp.createCall(s.end(this), func);
        } else {
            return func;
        }
    }
}

SqlCall MatchRecognizeNavigationPhysical() :
{
    final Span s;
    final SqlOperator funcOp;
    final List<SqlNode> args = new ArrayList<SqlNode>();
    SqlNode e;
}
{
    (
        <PREV> { funcOp = SqlStdOperatorTable.PREV; }
    |
        <NEXT> { funcOp = SqlStdOperatorTable.NEXT; }
    )
    { s = span(); }
    <LPAREN>
    AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    (
        <COMMA> e = NumericLiteral() { args.add(e); }
    |
        { args.add(LITERAL_ONE); }
    )
    <RPAREN> {
        return funcOp.createCall(s.end(this), args);
    }
}

SqlCall withinDistinct(SqlNode arg) :
{
    final Span s;
    final SqlNodeList distinctList;
}
{
    <WITHIN> { s = span(); }
    <DISTINCT>
    <LPAREN>
    distinctList = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY)
    <RPAREN> {
        return SqlStdOperatorTable.WITHIN_DISTINCT.createCall(
            s.end(this), arg, distinctList);
    }
}

SqlCall withinGroup(SqlNode arg) :
{
    final Span s;
    final SqlNodeList orderList;
}
{
    <WITHIN> { s = span(); }
    <GROUP>
    <LPAREN>
    orderList = OrderBy(true)
    <RPAREN> {
        return SqlStdOperatorTable.WITHIN_GROUP.createCall(
            s.end(this), arg, orderList);
    }
}

Pair<SqlParserPos, SqlOperator> NullTreatment() :
{
    final Span span;
}
{
    <IGNORE> { span = span(); } <NULLS> {
        return Pair.of(span.end(this), SqlStdOperatorTable.IGNORE_NULLS);
    }
|
    <RESPECT> { span = span(); } <NULLS> {
        return Pair.of(span.end(this), SqlStdOperatorTable.RESPECT_NULLS);
    }
}

SqlCall nullTreatment(SqlCall arg) :
{
    final Pair<SqlParserPos, SqlOperator> pair;
}
{
    pair = NullTreatment() { return pair.right.createCall(pair.left, arg); }
}

/**
 * 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() :
{
    SqlCall call;
    final Span filterSpan;
    final SqlNode filter;
    final Span overSpan;
    final SqlNode over;
}
{
    (
        LOOKAHEAD(2)
        call = StringAggFunctionCall()
    |
        call = NamedCall()
    )
    [
        LOOKAHEAD(2) call = nullTreatment(call)
    ]
    [
        LOOKAHEAD(2) // decide between WITHIN DISTINCT and WITHIN GROUP
        call = withinDistinct(call)
    ]
    [
        call = withinGroup(call)
    ]
    [
        <FILTER> { filterSpan = span(); }
        <LPAREN>
        <WHERE>
        filter = Expression(ExprContext.ACCEPT_SUB_QUERY)
        <RPAREN> {
            call = SqlStdOperatorTable.FILTER.createCall(
                filterSpan.end(this), call, filter);
        }
    ]
    [
        <OVER> { overSpan = span(); }
        (
            over = SimpleIdentifier()
        |
            over = WindowSpecification()
        )
        {
            call = SqlStdOperatorTable.OVER.createCall(overSpan.end(over), call, over);
        }
    ]
    {
        return call;
    }
}

SqlCall NamedCall() :
{
    final SqlFunctionCategory funcType;
    final SqlIdentifier qualifiedName;
    final Span s;
    final List<SqlNode> args;
    SqlLiteral quantifier = null;
}
{
    (
        <SPECIFIC> {
            funcType = SqlFunctionCategory.USER_DEFINED_SPECIFIC_FUNCTION;
        }
    |
        { funcType = SqlFunctionCategory.USER_DEFINED_FUNCTION; }
    )
    qualifiedName = FunctionName() {
        s = span();
    }
    (
        LOOKAHEAD(2) <LPAREN> <STAR> {
            args = ImmutableList.of(SqlIdentifier.star(getPos()));
        }
        <RPAREN>
    |
        LOOKAHEAD(2) <LPAREN> <RPAREN> {
            args = ImmutableList.of();
        }
    |
        args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
            quantifier = (SqlLiteral) args.get(0);
            args.remove(0);
        }
    )
    {
        return createCall(qualifiedName, s.end(this), funcType, quantifier, args);
    }
}

/*
* Parse Floor/Ceil function parameters
*/
SqlNode StandardFloorCeilOptions(Span s, boolean floorFlag) :
{
    SqlNode e;
    final List<SqlNode> args = new ArrayList<SqlNode>();
    final SqlIntervalQualifier unit;
    SqlCall function;
    final Span s1;
}
{
    <LPAREN> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
    (
        <TO> unit = TimeUnitOrName() {
            args.add(unit);
        }
    )?
    <RPAREN> {
        SqlOperator op = floorFlag
            ? SqlStdOperatorTable.FLOOR
            : SqlStdOperatorTable.CEIL;
        function =  op.createCall(s.end(this), args);
    }
    (
        <OVER> { s1 = span(); }
        (
            e = SimpleIdentifier()
        |
            e = WindowSpecification()
        )
        {
            return SqlStdOperatorTable.OVER.createCall(s1.end(this), function, e);
        }
    |
        { 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>
    |   <CEILING>
    |   <CHAR>
    |   <CHAR_LENGTH>
    |   <CHARACTER_LENGTH>
    |   <COALESCE>
    |   <COLLECT>
    |   <COVAR_POP>
    |   <COVAR_SAMP>
    |   <CUME_DIST>
    |   <COUNT>
    |   <CURRENT_DATE>
    |   <CURRENT_TIME>
    |   <CURRENT_TIMESTAMP>
    |   <DENSE_RANK>
    |   <DUPLICATE>
    |   <ELEMENT>
    |   <EVERY>
    |   <EXP>
    |   <FIRST_VALUE>
    |   <FLOOR>
    |   <FUSION>
    |   <INTERSECTION>
    |   <IFNULL>
    |   <GROUPING>
    |   <HOUR>
    |   <LAG>
    |   <LEAD>
    |   <LEFT>
    |   <LAST_VALUE>
    |   <LN>
    |   <LOCALTIME>
    |   <LOCALTIMESTAMP>
    |   <LOWER>
    |   <MAX>
    |   <MIN>
    |   <MINUTE>
    |   <MOD>
    |   <MONTH>
    |   <NTH_VALUE>
    |   <NTILE>
    |   <NULLIF>
    |   <OCTET_LENGTH>
    |   <ON>
    |   <PERCENT_RANK>
    |   <PERCENTILE_CONT>
    |   <PERCENTILE_DISC>
    |   <POWER>
    |   <RANK>
    |   <REGR_COUNT>
    |   <REGR_SXX>
    |   <REGR_SYY>
    |   <RIGHT>
    |   <ROW_NUMBER>
    |   <SECOND>
    |   <SOME>
    |   <SQRT>
    |   <STDDEV_POP>
    |   <STDDEV_SAMP>
    |   <SUBSTRING_INDEX>
    |   <SUM>
    |   <UPPER>
    |   <USER>
    |   <VAR_POP>
    |   <VAR_SAMP>
    |   <YEAR>
    )
    {
        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;
    final Span s, s1;
}
{
    <LBRACE_FN> {
        s = span();
    }
    (
        LOOKAHEAD(1)
        call = TimestampAddFunctionCall() {
            name = call.getOperator().getName();
            args = new SqlNodeList(call.getOperandList(), getPos());
        }
    |
        LOOKAHEAD(1)
        call = DateTruncFunctionCall() {
            name = call.getOperator().getName();
            args = new SqlNodeList(call.getOperandList(), getPos());
        }
    |
        LOOKAHEAD(1)
        call = TimestampTruncFunctionCall() {
            name = call.getOperator().getName();
            args = new SqlNodeList(call.getOperandList(), getPos());
        }
    |
        LOOKAHEAD(1)
        call = TimeTruncFunctionCall() {
            name = call.getOperator().getName();
            args = new SqlNodeList(call.getOperandList(), getPos());
        }
    |
        LOOKAHEAD(1)
        call = TimestampDiff3FunctionCall() {
            name = call.getOperator().getName();
            args = new SqlNodeList(call.getOperandList(), getPos());
        }
    |
        LOOKAHEAD(1)
        call = TimeDiffFunctionCall() {
            name = call.getOperator().getName();
            args = new SqlNodeList(call.getOperandList(), getPos());
        }
    |
        LOOKAHEAD(3)
        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}
            // Similarly LEFT, RIGHT, TRUNCATE
            LOOKAHEAD(1)
            ( <INSERT> | <LEFT> | <RIGHT> | <TRUNCATE> ) { 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> { s1 = span(); } <RPAREN>
            {
                args = new SqlNodeList(s1.pos());
                args.add(SqlIdentifier.star(s1.pos()));
            }
        |
            LOOKAHEAD(2) <LPAREN> <RPAREN> { args = SqlNodeList.EMPTY; }
        |
            args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY)
        )
    )
    <RBRACE> {
        return new SqlJdbcFunctionCall(name).createCall(s.end(this),
            args.getList());
    }
}

/**
 * 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 SqlUtil.newContextException(getPos(), RESOURCE.minusNotAllowed());
                }
            }
        )
        (
            <ALL> { return SqlStdOperatorTable.EXCEPT_ALL; }
        |   <DISTINCT> { return SqlStdOperatorTable.EXCEPT; }
        |   { return SqlStdOperatorTable.EXCEPT; }
        )
    )
}

/**
 * Parses a binary multiset operator.
 */
SqlBinaryOperator BinaryMultisetOperator() :
{
}
{
    // If both the ALL or DISTINCT keywords are missing, DISTINCT is implicit
    <MULTISET>
    (
        <UNION>
        [
            <ALL>
        |   <DISTINCT> { return SqlStdOperatorTable.MULTISET_UNION_DISTINCT; }
        ]
        { return SqlStdOperatorTable.MULTISET_UNION; }
    |
        <INTERSECT>
        [
            <ALL>
        |   <DISTINCT> { return SqlStdOperatorTable.MULTISET_INTERSECT_DISTINCT; }
        ]
        { return SqlStdOperatorTable.MULTISET_INTERSECT; }
    |
        <EXCEPT>
        [
            <ALL>
        |   <DISTINCT> { return SqlStdOperatorTable.MULTISET_EXCEPT_DISTINCT; }
        ]
        { return SqlStdOperatorTable.MULTISET_EXCEPT; }
    )
}

/**
 * Parses a binary row operator like AND.
 */
SqlBinaryOperator BinaryRowOperator() :
{
    SqlBinaryOperator op;
}
{
    // <IN> is handled as a special case
    <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 SqlUtil.newContextException(getPos(), RESOURCE.bangEqualNotAllowed());
        }
        return SqlStdOperatorTable.NOT_EQUALS;
    }
|   <PLUS> { return SqlStdOperatorTable.PLUS; }
|   <MINUS> { return SqlStdOperatorTable.MINUS; }
|   <STAR> { return SqlStdOperatorTable.MULTIPLY; }
|   <SLASH> { return SqlStdOperatorTable.DIVIDE; }
|   <PERCENT_REMAINDER> {
        if (!this.conformance.isPercentRemainderAllowed()) {
            throw SqlUtil.newContextException(getPos(), RESOURCE.percentRemainderNotAllowed());
        }
        return SqlStdOperatorTable.PERCENT_REMAINDER;
    }
|   <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; }
|   <MEMBER> <OF> { return SqlStdOperatorTable.MEMBER_OF; }
|   LOOKAHEAD(2) <SUBMULTISET> <OF> { return SqlStdOperatorTable.SUBMULTISET_OF; }
|   <NOT> <SUBMULTISET> <OF> { return SqlStdOperatorTable.NOT_SUBMULTISET_OF; }
|   <CONTAINS> { return SqlStdOperatorTable.CONTAINS; }
|   <OVERLAPS> { return SqlStdOperatorTable.OVERLAPS; }
|   <EQUALS> { return SqlStdOperatorTable.PERIOD_EQUALS; }
|   <PRECEDES> { return SqlStdOperatorTable.PRECEDES; }
|   <SUCCEEDS> { return SqlStdOperatorTable.SUCCEEDS; }
|   LOOKAHEAD(2) <IMMEDIATELY> <PRECEDES> { return SqlStdOperatorTable.IMMEDIATELY_PRECEDES; }
|   <IMMEDIATELY> <SUCCEEDS> { return SqlStdOperatorTable.IMMEDIATELY_SUCCEEDS; }
|   op = BinaryMultisetOperator() { return op; }
|   <L2DISTANCE> { return SqlUserDefinedOperators.L2_DISTANCE; }
|   <COSINESIMILARITY> { return SqlUserDefinedOperators.COSINE_SIMILARITY; }
|   <IPDISTANCE> { return SqlUserDefinedOperators.IP_DISTANCE; }
}

/**
 * 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; }
|   <UNIQUE> { return SqlStdOperatorTable.UNIQUE; }
}

/**
 * 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; }
        |   <A> <SET> { return SqlStdOperatorTable.IS_NOT_A_SET; }
        |   <EMPTY> { return SqlStdOperatorTable.IS_NOT_EMPTY; }
        |   LOOKAHEAD(2) <JSON> <VALUE> { return SqlStdOperatorTable.IS_NOT_JSON_VALUE; }
        |   LOOKAHEAD(2) <JSON> <OBJECT> { return SqlStdOperatorTable.IS_NOT_JSON_OBJECT; }
        |   LOOKAHEAD(2) <JSON> <ARRAY> { return SqlStdOperatorTable.IS_NOT_JSON_ARRAY; }
        |   LOOKAHEAD(2) <JSON> <SCALAR> { return SqlStdOperatorTable.IS_NOT_JSON_SCALAR; }
        |   <JSON> { return SqlStdOperatorTable.IS_NOT_JSON_VALUE; }
        )
    |
        (
            <NULL> { return SqlStdOperatorTable.IS_NULL; }
        |   <TRUE> { return SqlStdOperatorTable.IS_TRUE; }
        |   <FALSE> { return SqlStdOperatorTable.IS_FALSE; }
        |   <UNKNOWN> { return SqlStdOperatorTable.IS_UNKNOWN; }
        |   <EMPTY> { return SqlStdOperatorTable.IS_EMPTY; }
        |   LOOKAHEAD(2) <JSON> <VALUE> { return SqlStdOperatorTable.IS_JSON_VALUE; }
        |   LOOKAHEAD(2) <JSON> <OBJECT> { return SqlStdOperatorTable.IS_JSON_OBJECT; }
        |   LOOKAHEAD(2) <JSON> <ARRAY> { return SqlStdOperatorTable.IS_JSON_ARRAY; }
        |   LOOKAHEAD(2) <JSON> <SCALAR> { return SqlStdOperatorTable.IS_JSON_SCALAR; }
        |   <JSON> { return SqlStdOperatorTable.IS_JSON_VALUE; }
        )
    )
|
    <FORMAT>
    (
        JsonRepresentation() {
            return SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
        }
    )
}


/* KEYWORDS:  anything in this list is a reserved word unless it appears
   in the NonReservedKeyWord() production. */

<DEFAULT, DQID, BTID, BQID, BQHID> TOKEN :
{
    < A: "A" >
|   < ABS: "ABS" >
|   < ABSENT: "ABSENT" >
|   < ABSOLUTE: "ABSOLUTE" >
|   < ACTION: "ACTION" >
|   < ADA: "ADA" >
|   < ADD: "ADD" >
|   < ADMIN: "ADMIN" >
|   < AFTER: "AFTER" >
|   < ALL: "ALL" >
|   < ALLOCATE: "ALLOCATE" >
|   < ALLOW: "ALLOW" >
|   < ALTER: "ALTER" >
|   < ALWAYS: "ALWAYS" >
|   < AND: "AND" >
|   < ANY: "ANY" >
|   < APPLY: "APPLY" >
|   < ARE: "ARE" >
|   < ARRAY: "ARRAY" >
|   < ARRAY_AGG: "ARRAY_AGG" >
|   < ARRAY_CONCAT_AGG: "ARRAY_CONCAT_AGG" >
|   < ARRAY_MAX_CARDINALITY: "ARRAY_MAX_CARDINALITY" >
|   < AS: "AS" >
|   < ASC: "ASC" >
|   < ASENSITIVE: "ASENSITIVE" >
|   < ASSERTION: "ASSERTION" >
|   < ASSIGNMENT: "ASSIGNMENT" >
|   < ASYMMETRIC: "ASYMMETRIC" >
|   < AT: "AT" >
|   < ATOMIC: "ATOMIC" >
|   < AT_SPLIT: "@" >
|   < AT_SPLIT_2: "@@" >
|   < ATTRIBUTE: "ATTRIBUTE" >
|   < ATTRIBUTES: "ATTRIBUTES" >
|   < AUTHORIZATION: "AUTHORIZATION" >
|   < AVG: "AVG" >
|   < BEFORE: "BEFORE" >
|   < BEGIN: "BEGIN" >
|   < BEGIN_FRAME: "BEGIN_FRAME" >
|   < BEGIN_PARTITION: "BEGIN_PARTITION" >
|   < 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" >
|   < CLASSIFIER: "CLASSIFIER" >
|   < 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" >
|   < CONDITIONAL: "CONDITIONAL" >
|   < 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" >
|   < CONTINUE: "CONTINUE" >
|   < CONVERT: "CONVERT" >
|   < 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_ROW: "CURRENT_ROW" >
|   < 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" >
|   < DATE_TRUNC: "DATE_TRUNC" >
|   < DATETIME: "DATETIME" >
|   < DATETIME_INTERVAL_CODE: "DATETIME_INTERVAL_CODE" >
|   < DATETIME_INTERVAL_PRECISION: "DATETIME_INTERVAL_PRECISION" >
|   < DAY: "DAY" >
|   < DAYS: "DAYS" >
|   < DEALLOCATE: "DEALLOCATE" >
|   < DEC: "DEC" >
|   < DECADE: "DECADE" >
|   < DECIMAL: "DECIMAL" >
|   < DECLARE: "DECLARE" >
|   < DEFAULT_: "DEFAULT" >
|   < DEFAULTS: "DEFAULTS" >
|   < DEFERRABLE: "DEFERRABLE" >
|   < DEFERRED: "DEFERRED" >
|   < DEFINE: "DEFINE" >
|   < DEFINED: "DEFINED" >
|   < DEFINER: "DEFINER" >
|   < DEGREE: "DEGREE" >
|   < DELETE: "DELETE" > { beforeTableName(); }
|   < DENSE_RANK: "DENSE_RANK" >
|   < DEPTH: "DEPTH" >
|   < DEREF: "DEREF" >
|   < DERIVED: "DERIVED" >
|   < DESC: "DESC" >
|   < DESCRIBE: "DESCRIBE" > { beforeTableName(); }
|   < DESCRIPTION: "DESCRIPTION" >
|   < DESCRIPTOR: "DESCRIPTOR" >
|   < DETERMINISTIC: "DETERMINISTIC" >
|   < DIAGNOSTICS: "DIAGNOSTICS" >
|   < DISALLOW: "DISALLOW" >
|   < DISCONNECT: "DISCONNECT" >
|   < DISPATCH: "DISPATCH" >
|   < DISTINCT: "DISTINCT" >
|   < DOMAIN: "DOMAIN" >
|   < DOT_FORMAT: "DOT" >
|   < DOUBLE: "DOUBLE" >
|   < DOW: "DOW" >
|   < DOY: "DOY" >
|   < DUPLICATE: "DUPLICATE">
|   < DROP: "DROP" >
|   < DYNAMIC: "DYNAMIC" >
|   < DYNAMIC_FUNCTION: "DYNAMIC_FUNCTION" >
|   < DYNAMIC_FUNCTION_CODE: "DYNAMIC_FUNCTION_CODE" >
|   < EACH: "EACH" >
|   < ELEMENT: "ELEMENT" >
|   < ELSE: "ELSE" >
|   < EMPTY: "EMPTY" >
|   < ENCODING: "ENCODING">
|   < END: "END" >
|   < END_EXEC: "END-EXEC" >
|   < END_FRAME: "END_FRAME" >
|   < END_PARTITION: "END_PARTITION" >
|   < EPOCH: "EPOCH" >
|   < EQUALS: "EQUALS" >
|   < ERROR: "ERROR" >
|   < ESCAPE: "ESCAPE" >
|   < EVERY: "EVERY" >
|   < EXCEPT: "EXCEPT" >
|   < EXCEPTION: "EXCEPTION" >
|   < EXCLUDE: "EXCLUDE" >
|   < EXCLUDING: "EXCLUDING" >
|   < EXEC: "EXEC" >
|   < EXECUTE: "EXECUTE" >
|   < EXISTS: "EXISTS" >
|   < EXP: "EXP" >
|   < EXPLAIN: "EXPLAIN" >
|   < EXTEND: "EXTEND" >
|   < EXTERNAL: "EXTERNAL" >
|   < EXTRACT: "EXTRACT" >
|   < FALSE: "FALSE" >
|   < FETCH: "FETCH" >
|   < FILTER: "FILTER" >
|   < FINAL: "FINAL" >
|   < FIRST: "FIRST" >
|   < FIRST_VALUE: "FIRST_VALUE">
|   < FLOAT: "FLOAT" >
|   < FLOOR: "FLOOR" >
|   < FOLLOWING: "FOLLOWING" >
|   < FOR: "FOR" >
|   < FORMAT: "FORMAT" >
|   < FOREIGN: "FOREIGN" >
|   < FORTRAN: "FORTRAN" >
|   < FOUND: "FOUND" >
|   < FRAC_SECOND: "FRAC_SECOND" >
|   < FRAME_ROW: "FRAME_ROW" >
|   < FREE: "FREE" >
|   < FRIDAY: "FRIDAY" >
|   < FROM: "FROM" > { beforeTableName(); }
|   < FULL: "FULL" >
|   < FUNCTION: "FUNCTION" >
|   < FUSION: "FUSION" >
|   < G: "G" >
|   < GENERAL: "GENERAL" >
|   < GENERATED: "GENERATED" >
|   < GEOMETRY: "GEOMETRY" >
|   < GET: "GET" >
|   < GLOBAL: "GLOBAL" >
|   < GO: "GO" >
|   < GOTO: "GOTO" >
|   < GRANT: "GRANT" >
|   < GRANTED: "GRANTED" >
|   < GROUP: "GROUP" >
|   < GROUP_CONCAT: "GROUP_CONCAT" >
|   < GROUPING: "GROUPING" >
|   < GROUPS: "GROUPS" >
|   < HAVING: "HAVING" >
|   < HIERARCHY: "HIERARCHY" >
|   < HOLD: "HOLD" >
|   < HOP: "HOP" >
|   < HOUR: "HOUR" >
|   < HOURS: "HOURS" >
|   < IDENTITY: "IDENTITY" >
|   < IFNULL: "IFNULL">
|   < IGNORE: "IGNORE" >
|   < ILIKE: "ILIKE" >
|   < IMMEDIATE: "IMMEDIATE" >
|   < IMMEDIATELY: "IMMEDIATELY" >
|   < IMPLEMENTATION: "IMPLEMENTATION" >
|   < IMPORT: "IMPORT" >
|   < IN: "IN" >
|   < INCLUDE: "INCLUDE" >
|   < INCLUDING: "INCLUDING" >
|   < INCREMENT: "INCREMENT" >
|   < INDICATOR: "INDICATOR" >
|   < INITIAL: "INITIAL" >
|   < INITIALLY: "INITIALLY" >
|   < INNER: "INNER" >
|   < INOUT: "INOUT" >
|   < INPUT: "INPUT" >
|   < INSENSITIVE: "INSENSITIVE" >
|   < INSERT: "INSERT" > { beforeTableName(); }
|   < INSTANCE: "INSTANCE" >
|   < INSTANTIABLE: "INSTANTIABLE" >
|   < INT: "INT" >
|   < INTEGER: "INTEGER" >
|   < INTERSECT: "INTERSECT" >
|   < INTERSECTION: "INTERSECTION" >
|   < INTERVAL: "INTERVAL" >
|   < INTO: "INTO" >
|   < INVOKER: "INVOKER" >
|   < IS: "IS" >
|   < ISODOW: "ISODOW" >
|   < ISOYEAR: "ISOYEAR" >
|   < ISOLATION: "ISOLATION" >
|   < JAVA: "JAVA" >
|   < JOIN: "JOIN" > { beforeTableName(); }
|   < JSON: "JSON" >
|   < JSON_ARRAY: "JSON_ARRAY">
|   < JSON_ARRAYAGG: "JSON_ARRAYAGG">
|   < JSON_EXISTS: "JSON_EXISTS" >
|   < JSON_OBJECT: "JSON_OBJECT">
|   < JSON_OBJECTAGG: "JSON_OBJECTAGG">
|   < JSON_QUERY: "JSON_QUERY" >
|   < JSON_VALUE: "JSON_VALUE" >
|   < K: "K" >
|   < KEY: "KEY" >
|   < KEY_MEMBER: "KEY_MEMBER" >
|   < KEY_TYPE: "KEY_TYPE" >
|   < LABEL: "LABEL" >
|   < LAG: "LAG" >
|   < LANGUAGE: "LANGUAGE" >
|   < LARGE: "LARGE" >
|   < LAST: "LAST" >
|   < LAST_VALUE: "LAST_VALUE" >
|   < LATERAL: "LATERAL" >
|   < LEAD: "LEAD" >
|   < LEADING: "LEADING" >
|   < LEFT: "LEFT" >
|   < LENGTH: "LENGTH" >
|   < LEVEL: "LEVEL" >
|   < LIBRARY: "LIBRARY" >
|   < LIKE: "LIKE" >
|   < LIKE_BINARY: "LIKE BINARY" >
|   < LIKE_REGEX: "LIKE_REGEX" >
|   < LIMIT: "LIMIT" >
|   < LN: "LN" >
|   < LOCAL: "LOCAL" >
|   < LOCALTIME: "LOCALTIME" >
|   < LOCALTIMESTAMP: "LOCALTIMESTAMP" >
|   < LOCATOR: "LOCATOR" >
|   < LOWER: "LOWER" >
|   < M: "M" >
|   < MAP: "MAP" >
|   < MATCH: "MATCH" >
|   < MATCHED: "MATCHED" >
|   < MATCHES: "MATCHES" >
|   < MATCH_NUMBER: "MATCH_NUMBER">
|   < MATCH_RECOGNIZE: "MATCH_RECOGNIZE">
|   < MAX: "MAX" >
|   < MAXVALUE: "MAXVALUE" >
|   < MEASURES: "MEASURES" >
|   < MEMBER: "MEMBER" >
|   < MERGE: "MERGE" > { beforeTableName(); }
|   < MESSAGE_LENGTH: "MESSAGE_LENGTH" >
|   < MESSAGE_OCTET_LENGTH: "MESSAGE_OCTET_LENGTH" >
|   < MESSAGE_TEXT: "MESSAGE_TEXT" >
|   < METHOD: "METHOD" >
|   < MICROSECOND: "MICROSECOND" >
|   < MILLISECOND: "MILLISECOND" >
|   < MILLENNIUM: "MILLENNIUM" >
|   < MIN: "MIN" >
|   < MINUTE: "MINUTE" >
|   < MINUTES: "MINUTES" >
|   < MINVALUE: "MINVALUE" >
|   < MOD: "MOD" >
|   < MODIFIES: "MODIFIES" >
|   < MODULE: "MODULE" >
|   < MONDAY: "MONDAY" >
|   < MONTH: "MONTH" >
|   < MONTHS: "MONTHS" >
|   < MORE_: "MORE" >
|   < MULTISET: "MULTISET" >
|   < MUMPS: "MUMPS" >
|   < NAME: "NAME" >
|   < NAMES: "NAMES" >
|   < NANOSECOND: "NANOSECOND" >
|   < NATIONAL: "NATIONAL" >
|   < NATURAL: "NATURAL" >
|   < NCHAR: "NCHAR" >
|   < NCLOB: "NCLOB" >
|   < NESTING: "NESTING" >
|   < NEW: "NEW" >
|   < NEXT: "NEXT" >
|   < NO: "NO" >
|   < NONE: "NONE" >
|   < NORMALIZE: "NORMALIZE" >
|   < NORMALIZED: "NORMALIZED" >
|   < NOT: "NOT" >
|   < NTH_VALUE: "NTH_VALUE" >
|   < NTILE: "NTILE" >
|   < NULL: "NULL" >
|   < NULLABLE: "NULLABLE" >
|   < NULLIF: "NULLIF" >
|   < NULLS: "NULLS" >
|   < NUMBER: "NUMBER" >
|   < NUMERIC: "NUMERIC" >
|   < OBJECT: "OBJECT" >
|   < OCCURRENCES_REGEX: "OCCURRENCES_REGEX" >
|   < OCTET_LENGTH: "OCTET_LENGTH" >
|   < OCTETS: "OCTETS" >
|   < OF: "OF" >
|   < OFFSET: "OFFSET" >
|   < OLD: "OLD" >
|   < OMIT: "OMIT" >
|   < ON: "ON" >
|   < ONE: "ONE" >
|   < 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" >
|   < PASSING: "PASSING" >
|   < PASSTHROUGH: "PASSTHROUGH" >
|   < PAST: "PAST" >
|   < PATH: "PATH" >
|   < PATTERN: "PATTERN" >
|   < PER: "PER" >
|   < PERCENT: "PERCENT" >
|   < PERCENTILE_CONT: "PERCENTILE_CONT" >
|   < PERCENTILE_DISC: "PERCENTILE_DISC" >
|   < PERCENT_RANK: "PERCENT_RANK" >
|   < PERIOD: "PERIOD" >
|   < PERMUTE: "PERMUTE" >
|   < PIVOT: "PIVOT" >
|   < PLACING: "PLACING" >
|   < PLAN: "PLAN" >
|   < PLI: "PLI" >
|   < PORTION: "PORTION" >
|   < POSITION: "POSITION" >
|   < POSITION_REGEX: "POSITION_REGEX" >
|   < POWER: "POWER" >
|   < PRECEDES: "PRECEDES" >
|   < PRECEDING: "PRECEDING" >
|   < PRECISION: "PRECISION" >
|   < PRESERVE: "PRESERVE" >
|   < PREV: "PREV" >
|   < PRIMARY: "PRIMARY" >
|   < PRIOR: "PRIOR" >
|   < PRIVILEGES: "PRIVILEGES" >
|   < PROCEDURE: "PROCEDURE" >
|   < PUBLIC: "PUBLIC" >
|   < PURGE_RESOURCES: "PURGE_RESOURCES" >
|   < QUARTER: "QUARTER" >
|   < QUARTERS: "QUARTERS" >
|   < 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" >
|   < RESPECT: "RESPECT" >
|   < 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" >
|   < RETURNING: "RETURNING" >
|   < RETURNS: "RETURNS" >
|   < REVOKE: "REVOKE" >
|   < RIGHT: "RIGHT" >
|   < RLIKE: "RLIKE" >
|   < 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" >
|   < RUNNING: "RUNNING" >
|   < SATURDAY: "SATURDAY" >
|   < SAVEPOINT: "SAVEPOINT" >
|   < SCALAR: "SCALAR" >
|   < 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" >
|   < SECONDS: "SECONDS" >
|   < SECTION: "SECTION" >
|   < SECURITY: "SECURITY" >
|   < SEEK: "SEEK" >
|   < SELECT: "SELECT" > { afterTableName(); }
|   < SELF: "SELF" >
|   < SENSITIVE: "SENSITIVE" >
|   < SEPARATOR: "SEPARATOR" >
|   < SEQUENCE: "SEQUENCE" >
|   < SERIALIZABLE: "SERIALIZABLE" >
|   < SERVER: "SERVER" >
|   < SERVER_NAME: "SERVER_NAME" >
|   < SESSION: "SESSION" >
|   < SESSION_USER: "SESSION_USER" >
|   < SET: "SET" > { afterTableName(); }
|   < SETS: "SETS" >
|   < SET_MINUS: "MINUS">
|   < SHOW: "SHOW" >
|   < SIMILAR: "SIMILAR" >
|   < SIMPLE: "SIMPLE" >
|   < SIZE: "SIZE" >
|   < SKIP_: "SKIP" >
|   < 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" >
|   < STRING_AGG: "STRING_AGG" >
|   < STRUCTURE: "STRUCTURE" >
|   < STYLE: "STYLE" >
|   < SUBCLASS_ORIGIN: "SUBCLASS_ORIGIN" >
|   < SUBMULTISET: "SUBMULTISET" >
|   < SUBSET: "SUBSET" >
|   < SUBSTITUTE: "SUBSTITUTE" >
|   < SUBSTRING: "SUBSTRING" >
|   < SUBSTRING_REGEX: "SUBSTRING_REGEX" >
|   < SUBSTRING_INDEX: "SUBSTRING_INDEX" >
|   < SUCCEEDS: "SUCCEEDS" >
|   < SUM: "SUM" >
|   < SUNDAY: "SUNDAY" >
|   < SYMMETRIC: "SYMMETRIC" >
|   < SYSTEM: "SYSTEM" >
|   < SYSTEM_TIME: "SYSTEM_TIME" >
|   < SYSTEM_USER: "SYSTEM_USER" >
|   < TABLE: "TABLE" > { beforeTableName(); }
|   < TABLE_NAME: "TABLE_NAME" >
|   < TABLESAMPLE: "TABLESAMPLE" >
|   < TEMPORARY: "TEMPORARY" >
|   < THEN: "THEN" >
|   < THURSDAY: "THURSDAY" >
|   < TIES: "TIES" >
|   < TIME: "TIME" >
|   < TIME_DIFF: "TIME_DIFF" >
|   < TIME_TRUNC: "TIME_TRUNC" >
|   < TIMESTAMP: "TIMESTAMP" >
|   < TIMESTAMPADD: "TIMESTAMPADD" >
|   < TIMESTAMPDIFF: "TIMESTAMPDIFF" >
|   < TIMESTAMP_DIFF: "TIMESTAMP_DIFF" >
|   < TIMESTAMP_TRUNC: "TIMESTAMP_TRUNC" >
|   < 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" >
|   < TRANSLATE_REGEX: "TRANSLATE_REGEX" >
|   < TRANSLATION: "TRANSLATION" >
|   < TREAT: "TREAT" >
|   < TRIGGER: "TRIGGER" >
|   < TRIGGER_CATALOG: "TRIGGER_CATALOG" >
|   < TRIGGER_NAME: "TRIGGER_NAME" >
|   < TRIGGER_SCHEMA: "TRIGGER_SCHEMA" >
|   < TRIM: "TRIM" >
|   < TRIM_ARRAY: "TRIM_ARRAY" >
|   < TRUE: "TRUE" >
|   < TRUNCATE: "TRUNCATE" >
|   < TUESDAY: "TUESDAY" >
|   < TUMBLE: "TUMBLE" >
|   < TYPE: "TYPE" >
|   < UESCAPE: "UESCAPE" >
|   < UNBOUNDED: "UNBOUNDED" >
|   < UNCOMMITTED: "UNCOMMITTED" >
|   < UNCONDITIONAL: "UNCONDITIONAL" >
|   < UNDER: "UNDER" >
|   < UNION: "UNION" >
|   < UNIQUE: "UNIQUE" >
|   < UNKNOWN: "UNKNOWN" >
|   < UNPIVOT: "UNPIVOT" >
|   < UNNAMED: "UNNAMED" >
|   < UNNEST: "UNNEST" >
|   < UPDATE: "UPDATE" > { beforeTableName(); }
|   < 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" >
|   < UTF8: "UTF8" >
|   < UTF16: "UTF16" >
|   < UTF32: "UTF32" >
|   < VALUE: "VALUE" >
|   < VALUES: "VALUES" > { afterTableName(); }
|   < VALUE_OF: "VALUE_OF" >
|   < VAR_POP: "VAR_POP" >
|   < VAR_SAMP: "VAR_SAMP" >
|   < VARBINARY: "VARBINARY" >
|   < VARCHAR: "VARCHAR" >
|   < VARYING: "VARYING" >
|   < VERSION: "VERSION" >
|   < VERSIONING: "VERSIONING" >
|   < VIEW: "VIEW" >
|   < WEDNESDAY: "WEDNESDAY" >
|   < WEEK: "WEEK" >
|   < WEEKS: "WEEKS" >
|   < 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" >
|   < YEARS: "YEARS" >
|   < ZONE: "ZONE" >
<#-- additional parser keywords are included here -->
<#list (parser.keywords!) as keyword>
|   < ${keyword}: "${keyword}" >
</#list>
}

/**
 * Parses a non-reserved keyword for use as an identifier.
 *
 * <p>The method is broken up into several sub-methods; without this
 * decomposition, parsers such as Babel with more than ~1,000 non-reserved
 * keywords would generate such deeply nested 'if' statements that javac would
 * fail with a {@link StackOverflowError}.
 *
 * <p>The list is generated from the FMPP config data. To add or remove
 * keywords, modify config.fmpp. For parsers except Babel, make sure that
 * keywords are not reserved by the SQL standard.
 *
 * @see Glossary#SQL2003 SQL:2003 Part 2 Section 5.2
 */
String NonReservedKeyWord() :
{
}
{
    (
        NonReservedKeyWord0of3()
    |   NonReservedKeyWord1of3()
    |   NonReservedKeyWord2of3()
    )
    {
        return unquotedIdentifier();
    }
}

/** @see #NonReservedKeyWord */
void NonReservedKeyWord0of3() :
{
}
{
    (
<#list (parser.nonReservedKeywords!) + (parser.nonReservedKeywordsToAdd!) as keyword>
<#if keyword?index == 0>
        <${keyword}>
<#elseif keyword?index % 3 == 0>
    |   <${keyword}>
</#if>
</#list>
    )
}

/** @see #NonReservedKeyWord */
void NonReservedKeyWord1of3() :
{
}
{
    (
<#list (parser.nonReservedKeywords!) + (parser.nonReservedKeywordsToAdd!) as keyword>
<#if keyword?index == 1>
        <${keyword}>
<#elseif keyword?index % 3 == 1>
    |   <${keyword}>
</#if>
</#list>
    )
}

/** @see #NonReservedKeyWord */
void NonReservedKeyWord2of3() :
{
}
{
    (
<#list (parser.nonReservedKeywords!) + (parser.nonReservedKeywordsToAdd!) as keyword>
<#if keyword?index == 2>
        <${keyword}>
<#elseif keyword?index % 3 == 2>
    |   <${keyword}>
</#if>
</#list>
    )
}

/* LITERALS */

<DEFAULT, DQID, BTID, BQID, BQHID> 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"])+
    >
|
    < DATE_LITERAL:
        (["0"-"9"])+("-")?(["0"-"9"]["0"-"9"])("-")?(["0"-"9"]["0"-"9"])
        >
|
    < TIME_LITERAL:
                (["0"-"9"])+(":")?(["0"-"9"]["0"-"9"])(":")?(["0"-"9"]["0"-"9"])
                >
|
    < DATE_TIME:
       (["0"-"9"])+("-")?(["0"-"9"]["0"-"9"])("-")?(["0"-"9"]["0"-"9"])(" ")
       (["0"-"9"])+(":")?(["0"-"9"]["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> >
}

// All databases except BigQuery support standard single-quoted literals,
// which use single-quote as the escape character.
<DEFAULT, DQID, BTID> TOKEN :
{
    < QUOTED_STRING: <QUOTE> ( (~["'"]) | ("''"))* <QUOTE> >
|
    < PREFIXED_STRING_LITERAL: ("_" <CHARSETNAME> | "N") <QUOTED_STRING> >
|
    < UNICODE_STRING_LITERAL: "U" "&" <QUOTED_STRING> >
|
    < C_STYLE_ESCAPED_STRING_LITERAL: "E" <QUOTE> ( (~["'", "\\"]) | ("\\" ~[]) | "''")* <QUOTE> >
|
    < #CHARSETNAME: (["a"-"z","A"-"Z","0"-"9"])
    (["a"-"z","A"-"Z","0"-"9",":",".","-","_"])*
    >
}

// BigQuery supports single- and double-quoted literals with back-slash
// as the escape character.
<BQID, BQHID> TOKEN :
{
    // BigQuery-style double-quoted string, escaped using backslash
    < BIG_QUERY_DOUBLE_QUOTED_STRING:
      <DOUBLE_QUOTE> ( (~["\\", "\""]) | ("\\" ~[]) )* <DOUBLE_QUOTE>
    >
|
    // BigQuery-style single-quoted string, escaped using backslash
    < BIG_QUERY_QUOTED_STRING:
      <QUOTE> ( (~["\\", "'"]) | ("\\" ~[]) )* <QUOTE>
    >
}

<DEFAULT, DQID, BTID, BQID, BQHID> TOKEN :
{
    < UNICODE_QUOTED_ESCAPE_CHAR:
    <QUOTE>
    (~["0"-"9","a"-"f","A"-"F","+","\""," ","\t","\n","\r","\f"])
    <QUOTE>
    >
}

/* SEPARATORS */

<DEFAULT, DQID, BTID, BQID, BQHID> TOKEN :
{
    < LPAREN: "(">
|   < RPAREN: ")">
<#if (parser.includeBraces!) >
|   < LBRACE_D: "{" (" ")* ["d","D"] >
|   < LBRACE_T: "{" (" ")* ["t","T"] >
|   < LBRACE_TS: "{" (" ")* ["t","T"] ["s","S"] >
|   < LBRACE_FN: "{" (" ")* ["f","F"] ["n","N"] >
|   < LBRACE: "{" >
|   < RBRACE: "}" >
<#else>
<#include "/@includes/braces.ftl" />
</#if>
|   < LBRACKET: "[" >
|   < RBRACKET: "]" >
|   < SEMICOLON: ";" >
|   < DOT: "." >
|   < COMMA: "," >
}

/* OPERATORS */

<DEFAULT, DQID, BTID, BQID, BQHID> TOKEN :
{
    < EQ: "=" >
|   < GT: ">" >
|   < LT: "<" >
|   < HOOK: "?" >
|   < COLON: ":" >
|   < LE: "<=" >
|   < GE: ">=" >
|   < NE: "<>" >
|   < NE2: "!=" >
|   < PLUS: "+" >
|   < MINUS: "-" >
|   < STAR: "*" >
|   < SLASH: "/" >
|   < PERCENT_REMAINDER: "%" >
|   < CONCAT: "||" >
|   < NAMED_ARGUMENT_ASSIGNMENT: "=>" >
|   < L2DISTANCE: "<->" >
|   < COSINESIMILARITY: "<=>" >
|   < IPDISTANCE: "<*>" >
|   < DOUBLE_PERIOD: ".." >
|   < QUOTE: "'" >
|   < DOUBLE_QUOTE: "\"" >
|   < VERTICAL_BAR: "|" >
|   < CARET: "^" >
|   < DOLLAR: "$" >
<#list (parser.binaryOperatorsTokens!) as operator>
|   ${operator}
</#list>
}


/*****************************************
 * Lexical Descriptions                  *
 *****************************************/

TOKEN_MGR_DECLS : {
    final List<Integer> lexicalStateStack = new ArrayList<Integer>();

    void pushState() {
      lexicalStateStack.add(curLexState);
    }

    void popState() {
      SwitchTo(lexicalStateStack.remove(lexicalStateStack.size() - 1));
    }

    void beforeTableName() {
      if (curLexState == BQID) {
        pushState();
        SwitchTo(BQHID);
      }
    }

    void afterTableName() {
      if (curLexState == BQHID) {
        popState();
      }
    }

<#if (parser.includeAdditionalDeclarations!)>
  <#include "/@includes/tokenManagerDeclarations.ftl" />
</#if>
}

/*
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, escaped using back-ticks,
         e.g. `My ``Quoted`` Identifier`
BQID:    Identifiers are enclosed in back-ticks, escaped using backslash,
         e.g. `My \`Quoted\` Identifier`,
         and with the potential to shift into BQHID in contexts where table
         names are expected, and thus allow hyphen-separated identifiers as
         part of table names
BQHID:   Identifiers are enclosed in back-ticks, escaped using backslash,
         e.g. `My \`Quoted\` Identifier`
         and unquoted identifiers may contain hyphens, e.g. foo-bar
IN_SINGLE_LINE_COMMENT:
IN_FORMAL_COMMENT:
IN_MULTI_LINE_COMMENT:

DEFAULT, DQID, BTID, BQID are the 4 'normal states'. Behavior is identical
except for how quoted identifiers are recognized.

The BQHID state exists only at the start of a table name (e.g. immediately after
FROM or INSERT INTO). As soon as an identifier is seen, the state shifts back
to BTID.

After a comment has completed, the lexer returns to the previous state, one
of the 'normal states'.
*/

/* WHITE SPACE */

<DEFAULT, DQID, BTID, BQID, BQHID> SKIP :
{
    " "
|   "\t"
|   "\n"
|   "\r"
|   "\f"
}

/* COMMENTS */

<DEFAULT, DQID, BTID, BQID, BQHID> TOKEN :
{
    < HINT_BEG: "/*+">
|   < COMMENT_END: "*/" >
}

<DEFAULT, DQID, BTID, BQID, BQHID> MORE :
{
    <"/**" ~["/"]> { pushState(); } : IN_FORMAL_COMMENT
}

<DEFAULT, DQID, BTID, BQID, BQHID> MORE :
{
    "/*" { pushState(); } : IN_MULTI_LINE_COMMENT
}

<DEFAULT, DQID, BTID, BQID, BQHID> SKIP :
{
    <SINGLE_LINE_COMMENT: ("//"|"--")(~["\n","\r"])* ("\n"|"\r"|"\r\n")? >
}

<IN_FORMAL_COMMENT>
SPECIAL_TOKEN :
{
    <FORMAL_COMMENT: <COMMENT_END> > { popState(); }
}

<IN_MULTI_LINE_COMMENT>
SPECIAL_TOKEN :
{
    <MULTI_LINE_COMMENT: <COMMENT_END> > { 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"])
    |
        ("``")
    )+
    "`"
    >
}

<BQID, BQHID> TOKEN :
{
    // BigQuery-style backtick-quoted identifier, escaped using backslash
    < BIG_QUERY_BACK_QUOTED_IDENTIFIER:
    "`"
    (
        (~["\\", "`"])
    |
        ("\\" ~[])
    )*
    "`"
    >
}

<BQHID> TOKEN :
{
    // Per BigQuery: "Project IDs must contain 6-63 lowercase letters, digits,
    // or dashes. IDs must start with a letter and may not end with a dash."
    // We do not restrict length, or prevent identifiers from ending in a dash.
    < HYPHENATED_IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>|"-")* > { popState(); }
}

<DEFAULT, DQID, BTID, BQID> TOKEN :
{
    < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
}

<DEFAULT, DQID, BTID, BQID, BQHID> TOKEN :
{
    < COLLATION_ID:
    (<LETTER>|<DIGIT>)+ (<LETTER>|<DIGIT>|":"|"."|"-"|"_")*
    "$"
    (<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, BQID, BQHID> 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>
    )
}
