/*
// $Id: //open/dev/farrago/src/org/eigenbase/sql/parser/CommonParser.jj#73 $
// Package org.eigenbase is a class library of data management components.
// Copyright (C) 2005-2009 The Eigenbase Project
// Copyright (C) 2002-2009 SQLstream, Inc.
// Copyright (C) 2005-2009 LucidEra, Inc.
// Portions Copyright (C) 2002-2009 John V. Sichi
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later Eigenbase-approved version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

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

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

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

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

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

JAVACODE void checkQueryExpression(ExprContext exprContext)
{
    switch (exprContext) {
    case ACCEPT_NONQUERY:
    case ACCEPT_SUBQUERY:
    case ACCEPT_CURSOR:
        throw SqlUtil.newContextException(
            getPos(),
            EigenbaseResource.instance().IllegalQueryExpression.ex());
    }
}

JAVACODE void checkNonQueryExpression(ExprContext exprContext)
{
    if (exprContext == ExprContext.ACCEPT_QUERY) {
        throw SqlUtil.newContextException(
            getPos(),
            EigenbaseResource.instance().IllegalNonQueryExpression.ex());
    }
}

// The date/time parse utilities have to live here, instead of in the
// SqlParserUtil class because ParseException is ambiguous, and
// CommonParser has to live in multiple packages.

JAVACODE SqlDateLiteral parseDateLiteral(
    String s, SqlParserPos pos)
{
    String dateStr = SqlParserUtil.strip(s, "'");
    Calendar cal = DateTimeUtil.parseDateFormat(
        dateStr, DateTimeUtil.DateFormatStr, DateTimeUtil.gmtZone);
    if (null == cal) {
        EigenbaseResource res = EigenbaseResource.instance();
        throw SqlUtil.newContextException(
            pos,
            res.IllegalLiteral.ex(
                "DATE",
                s,
                res.BadFormat.str(DateTimeUtil.DateFormatStr)));
    }
    return SqlLiteral.createDate(cal, pos);
}

JAVACODE SqlTimeLiteral parseTimeLiteral(
    String s, SqlParserPos pos)
{
    String dateStr = SqlParserUtil.strip(s, "'");
    DateTimeUtil.PrecisionTime pt =
    DateTimeUtil.parsePrecisionDateTimeLiteral(
        dateStr, DateTimeUtil.TimeFormatStr, DateTimeUtil.gmtZone);
    if (null == pt) {
        EigenbaseResource res = EigenbaseResource.instance();
        throw SqlUtil.newContextException(
            pos,
            res.IllegalLiteral.ex(
                "TIME",
                s,
                res.BadFormat.str(DateTimeUtil.TimeFormatStr)));
    }
    return SqlLiteral.createTime(pt.getCalendar(), pt.getPrecision(), pos);
}

JAVACODE SqlTimestampLiteral parseTimestampLiteral(
    String s, SqlParserPos pos)
{
    String dateStr = SqlParserUtil.strip(s, "'");
    DateTimeUtil.PrecisionTime pt =
    DateTimeUtil.parsePrecisionDateTimeLiteral(
        dateStr, DateTimeUtil.TimestampFormatStr, DateTimeUtil.gmtZone);
    if (null == pt) {
        EigenbaseResource res = EigenbaseResource.instance();
        throw SqlUtil.newContextException(
            pos,
            res.IllegalLiteral.ex(
                "TIMESTAMP",
                s,
                res.BadFormat.str(DateTimeUtil.TimestampFormatStr)));
    }
    return SqlLiteral.createTimestamp(pt.getCalendar(), pt.getPrecision(), pos);
}

JAVACODE SqlIntervalLiteral parseIntervalLiteral(
    SqlParserPos pos,
    int sign,
    String s,
    SqlIntervalQualifier intervalQualifier) throws ParseException
{
    String intervalStr = SqlParserUtil.strip(s, "'");

    if ("".equals(intervalStr)) {
        EigenbaseResource res = EigenbaseResource.instance();
        String msg = res.IllegalIntervalLiteral.str(
            s + " " + intervalQualifier.toString(), pos.toString());
        throw new ParseException(msg);
    }
    return SqlLiteral.createInterval(sign, intervalStr, intervalQualifier, pos);
}

/**
 * Converts a ParseException (local to this particular instantiation
 * of the parser) into a SqlParseException (common to all parsers).
 */
JAVACODE SqlParseException convertException(Throwable ex)
{
    if (ex instanceof SqlParseException) {
        return (SqlParseException) ex;
    }
    SqlParserPos pos = null;
    int[][] expectedTokenSequences = null;
    String[] tokenImage = null;
    if (ex instanceof ParseException) {
        ParseException pex = (ParseException) ex;
        expectedTokenSequences = pex.expectedTokenSequences;
        tokenImage = pex.tokenImage;
        if (pex.currentToken != null) {
            final Token token = pex.currentToken.next;
            pos = new SqlParserPos(
                token.beginLine,
                token.beginColumn,
                token.endLine,
                token.endColumn);
        }
    } else if (ex instanceof TokenMgrError) {
        TokenMgrError tme = (TokenMgrError) ex;
        expectedTokenSequences = null;
        tokenImage = null;
        // Example:
        //    Lexical error at line 3, column 24.  Encountered "#" after "a".
        final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
            "(?s)Lexical error at line ([0-9]+), column ([0-9]+).*");
        java.util.regex.Matcher matcher = pattern.matcher(ex.getMessage());
        if (matcher.matches()) {
            int line = Integer.parseInt(matcher.group(1));
            int column = Integer.parseInt(matcher.group(2));
            pos = new SqlParserPos(line, column, line, column);
        }
    } else if (ex instanceof org.eigenbase.util.EigenbaseContextException) {
        // EigenbaseContextException 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 EigenbaseContextException appears because the parser
        // occasionally calls into validator-style code such as
        // SqlSpecialOperator.reduceExpr.
        org.eigenbase.util.EigenbaseContextException ece =
            (org.eigenbase.util.EigenbaseContextException) ex;
        pos = new SqlParserPos(
            ece.getPosLine(),
            ece.getPosColumn(),
            ece.getEndPosLine(),
            ece.getEndPosColumn());
        ex = ece.getCause();
    }

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

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

    // Find all sequences in the error which contain identifier. For
    // example,
    //       {<IDENTIFIER>}
    //       {A}
    //       {B, C}
    //       {D, <IDENTIFIER>}
    //       {D, A}
    //       {D, B}
    //
    // would yield
    //       {}
    //       {D}
    boolean id = false;
    ArrayList prefixList = new ArrayList();
    for (int i = 0; i < ex.expectedTokenSequences.length; ++i) {
        int[] seq = ex.expectedTokenSequences[i];
        int j = seq.length - 1;
        int i1 = seq[j];
        if (i1 == iIdentifier) {
            int[] prefix = new int[j];
            System.arraycopy(seq, 0, prefix, 0, j);
            prefixList.add(prefix);
        }
    }

    if (prefixList.isEmpty()) {
        return ex;
    }

    int[][] prefixes = (int[][])
        prefixList.toArray(new int[prefixList.size()][]);

    // Since <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.

    List list = new ArrayList();
    Metadata metadata = getMetadata();
    for (int i = 0; i < ex.expectedTokenSequences.length; ++i) {
        int [] seq = ex.expectedTokenSequences[i];
        String tokenImage = ex.tokenImage[seq[seq.length - 1]];
        String token = SqlParserUtil.getTokenVal(tokenImage);
        if (token == null  || !metadata.isNonReservedKeyword(token)) {
            list.add(seq);
            continue;
        }
        boolean match = matchesPrefix(seq, prefixes);
        if (!match) {
            list.add(seq);
        }
    }

    ex.expectedTokenSequences =
        (int [][]) list.toArray(new int [list.size()][]);
    return ex;
}

JAVACODE boolean matchesPrefix(int[] seq, int[][] prefixes)
{
    nextPrefix:
    for (int j = 0; j < prefixes.length; j++) {
        int[] prefix = prefixes[j];
        if (seq.length == prefix.length + 1) {
            for (int k = 0; k < prefix.length; k++) {
                if (prefix[k] != seq[k]) {
                    continue nextPrefix;
                }
            }
            return true;
        }
    }
    return false;
}

/*****************************************
 * Syntactical Descriptions              *
 *****************************************/

/**
 * Parses either a row expression or a query expression with an optional
 * ORDER BY.
 */
SqlNode OrderedQueryOrExpr(ExprContext exprContext) :
{
    SqlNode e;
    SqlNodeList orderBy;
    SqlParserPos pos;
}
{
    (
        e = QueryOrExpr(exprContext)
    )
    [
        // use the syntactic type of the expression we just parsed
        // to decide whether ORDER BY makes sense
        orderBy = OrderBy(e.isA(SqlKind.Query))
        {
            pos = getPos();
            e = SqlStdOperatorTable.orderByOperator.createCall(pos, e, orderBy);
        }
    ]
    {
        return e;
    }
}

/**
 * Parses a leaf in a query expression (SELECT, VALUES or TABLE).
 */
SqlNode LeafQuery(ExprContext exprContext) :
{
    SqlNode e;
}
{
    {
        // ensure a query is legal in this context
        checkQueryExpression(exprContext);
    }
    e = SqlSelect()
    {
        return e;
    }
    | e = TableConstructor()
    {
        return e;
    }
    | e = ExplicitTable(getPos())
    {
        return e;
    }
}

/**
 * Parses a parenthesized query or single row expression.
 */
SqlNode ParenthesizedExpression(ExprContext exprContext) :
{
    SqlNode e;
}
{
    <LPAREN>
    {
        // we've now seen left paren, so queries inside should
        // be allowed as subqueries
        switch (exprContext) {
        case ACCEPT_SUBQUERY:
            exprContext = ExprContext.ACCEPT_NONCURSOR;
            break;
        case ACCEPT_CURSOR:
            exprContext = ExprContext.ACCEPT_ALL;
            break;
        }
    }
    e = OrderedQueryOrExpr(exprContext)
    <RPAREN>
    {
        return e;
    }
}

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

/**
 * Parses function parameter lists including DISTINCT keyword recognition
 *
 *<p>
 *
 * This is pretty much the same as ParenthesizedQueryOrCommaList but allows the
 * DISTINCT keyword to follow the left paren and not be followed by a comma.
 */
List FunctionParameterList(
    ExprContext exprContext) :
{
    SqlNode e = null;
    List list = new ArrayList();
    ExprContext firstExprContext = exprContext;
}
{
    <LPAREN>
    {
        // we've now seen left paren, so queries inside should
        // be allowed as subqueries
        switch (exprContext) {
        case ACCEPT_SUBQUERY:
            firstExprContext = ExprContext.ACCEPT_NONCURSOR;
            break;
        case ACCEPT_CURSOR:
            firstExprContext = ExprContext.ACCEPT_ALL;
            break;
        }
    }
    [ <DISTINCT>
        {
            e = SqlLiteral.createSymbol(SqlSelectKeyword.Distinct, getPos());
        }
        |
      <ALL>
        {
            e = SqlLiteral.createSymbol(SqlSelectKeyword.All, getPos());
        }
    ]
    {
       list.add(e);
    }
    e = OrderedQueryOrExpr(firstExprContext)
    {
        if (e != null) {
            list.add(e);
        }
    }
    (
        <COMMA>
        {
            // a comma-list can't appear where only a query is expected
            checkNonQueryExpression(exprContext);
        }
        e = Expression(exprContext)
        {
            list.add(e);
        }
    ) *
    <RPAREN>
    {
        return list;
    }
}

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

/**
 * Parses an SQL statement followed by the end-of-file symbol.
 */
SqlNode SqlStmtEof() :
{
    SqlNode stmt;
}
{
    (
        stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
        |
        stmt = SqlExplain()
        |
        stmt = SqlInsert()
        |
        stmt = SqlDelete()
        |
        stmt = SqlUpdate()
        |
        stmt = SqlMerge()
        |
        stmt = SqlProcedureCall()
    )
    (<EOF>)
    {
        return stmt;
    }
}

/**
 * Parses a leaf SELECT expression without ORDER BY.
 */
SqlSelect SqlSelect() :
{
    List keywords = new ArrayList();
    List selectList;
    SqlNode fromClause;
    SqlNode where;
    SqlNode groupBy;
    SqlNode having;
    SqlNodeList windowDecls;
    SqlParserPos pos;
    SqlParserPos selectListPos;
    SqlNode selectItem;
}
{
    <SELECT>
    {
        pos = getPos();
    }
    SqlSelectKeywords(keywords)
    (
        <DISTINCT> {
            keywords.add(
                SqlLiteral.createSymbol(SqlSelectKeyword.Distinct, getPos()));
        }
        |   <ALL> {
            keywords.add(
                SqlLiteral.createSymbol(SqlSelectKeyword.All, getPos()));
        }
        |   E()
    )
    selectList = SelectList()
    <FROM>
    fromClause = FromClause()
    where = WhereOpt()
    groupBy = GroupByOpt()
    having = HavingOpt()
    windowDecls = WindowOpt()
    {
        selectItem = (SqlNode)selectList.get(0);
        selectListPos = selectItem.getParserPosition();
        return SqlStdOperatorTable.selectOperator.createCall(
            new SqlNodeList(keywords, pos),
            new SqlNodeList(selectList, selectListPos.plusAll(selectList)),
            fromClause, where, groupBy, having, windowDecls, null,
            pos.plus(getPos()));
    }
}

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

/**
 * Parses an EXPLAIN PLAN statement.
 */
SqlNode SqlExplain() :
{
    SqlNode stmt;
    SqlExplainLevel detailLevel = SqlExplainLevel.EXPPLAN_ATTRIBUTES;
    SqlExplain.Depth depth;
    SqlParserPos pos;
    boolean asXml;
}
{
    <EXPLAIN> <PLAN>
    [ detailLevel = ExplainDetailLevel() ]
    depth = ExplainDepth()
    (
        <AS> <XML> { asXml = true; }
        |
        {
            asXml = false;
        }
    )
    <FOR> (
        stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
        | stmt = SqlInsert()
        | stmt = SqlDelete()
        | stmt = SqlUpdate()
        | stmt = SqlMerge()
    )
    {
        pos = getPos();
        return new SqlExplain(
            SqlStdOperatorTable.explainOperator,
            stmt,
            SqlLiteral.createSymbol(detailLevel, SqlParserPos.ZERO),
            SqlLiteral.createSymbol(depth, SqlParserPos.ZERO),
            SqlLiteral.createBoolean(asXml, SqlParserPos.ZERO),
            nDynamicParams,
            pos);
    }
}

/**
 * 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 CALL statement.
 */
SqlNode SqlProcedureCall() :
{
    SqlParserPos callPos;
    SqlNode routineCall;
}
{
    <CALL>
    {
        callPos = getPos();
    }
    routineCall = NamedRoutineCall(
        SqlFunctionCategory.UserDefinedProcedure,
        ExprContext.ACCEPT_SUBQUERY)
    {
        return SqlStdOperatorTable.procedureCallOperator.createCall(
            callPos, routineCall);
    }
}

SqlNode NamedRoutineCall(
    SqlFunctionCategory routineType,
    ExprContext exprContext) :
{
    SqlIdentifier name;
    SqlNodeList args;
    SqlParserPos pos;
}
{
    name = CompoundIdentifier()
    {
        pos = getPos();
    }
    (
        LOOKAHEAD(2)
        <LPAREN>
        { pos = getPos(); }
        <RPAREN>
        { pos = pos.plus(getPos()); args = new SqlNodeList(pos); }
        | args = ParenthesizedQueryOrCommaList(exprContext)
        { pos = pos.plus(getPos()); }
    )
    {
        SqlNode function = createCall(
            name, pos, routineType, null, SqlParserUtil.toNodeArray(args));
        return function;
    }
}

/**
 * Parses an INSERT statement.
 */
SqlNode SqlInsert() :
{
    List keywords = new ArrayList();
    SqlIdentifier table;
    SqlNode source;
    List columnList = null;
    SqlParserPos pos;
    SqlParserPos columnListPos = null;
}
{
    <INSERT>
    SqlInsertKeywords(keywords)
    <INTO> table = CompoundIdentifier()
    {
        pos = getPos();
    }
    [
        LOOKAHEAD(2)
        <LPAREN>
        columnList = SimpleIdentifierCommaList()
        {
            columnListPos = getPos();
        }
        <RPAREN>
    ]
    source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
    {
        return new SqlInsert(
            SqlStdOperatorTable.insertOperator,
            new SqlNodeList(keywords, pos),
            table,
            source,
            (columnList != null)
            ? new SqlNodeList(columnList, columnListPos) : null,
            pos);
    }
}

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

/**
 * Parses a DELETE statement.
 */
SqlNode SqlDelete() :
{
    SqlIdentifier table;
    SqlIdentifier alias = null;
    SqlNode condition;
    SqlParserPos pos;
}
{
    <DELETE>
    {
        pos = getPos();
    }
    <FROM> table = CompoundIdentifier()
    {

    }
    [ [ <AS> ] alias = SimpleIdentifier() ]
    condition = WhereOpt()
    {
        return new SqlDelete(
            SqlStdOperatorTable.deleteOperator,
            table,
            condition,
            alias,
            pos);
    }
}

/**
 * Parses an UPDATE statement.
 */
SqlNode SqlUpdate() :
{
    SqlIdentifier table;
    SqlIdentifier alias = null;
    SqlNode condition;
    SqlNodeList sourceExpressionList;
    SqlNodeList targetColumnList;
    SqlIdentifier id;
    SqlNode exp;
    SqlParserPos pos;
}
{
    <UPDATE> table = CompoundIdentifier()
    {
        pos = getPos();
        targetColumnList = new SqlNodeList(pos);
        sourceExpressionList = new SqlNodeList(pos);
    }
    [ [ <AS> ] alias = SimpleIdentifier() ]
    <SET> id = SimpleIdentifier()
    {
        targetColumnList.add(id);
    }
    <EQ> exp = Expression(ExprContext.ACCEPT_SUBQUERY)
    {
        // TODO:  support DEFAULT also
        sourceExpressionList.add(exp);
    }
    (
        <COMMA>
        id = SimpleIdentifier()
        {
            targetColumnList.add(id);
        }
        <EQ> exp = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            sourceExpressionList.add(exp);
        }
    ) *
    condition = WhereOpt()
    {
        return new SqlUpdate(
            SqlStdOperatorTable.updateOperator,
            table,
            targetColumnList,
            sourceExpressionList,
            condition,
            alias,
            pos);
    }
}

/**
 * Parses a MERGE statement.
 */
SqlNode SqlMerge() :
{
    SqlIdentifier table;
    SqlIdentifier alias = null;
    SqlNode sourceTableRef;
    SqlNode condition;
    SqlNode updateCall = null;
    SqlNode insertCall = null;
    SqlParserPos mergePos;
}
{
    <MERGE> <INTO> table = CompoundIdentifier()
    {
        mergePos = getPos();
    }
    [ [ <AS> ] alias = SimpleIdentifier() ]

    <USING> sourceTableRef = TableRef()

    <ON> condition = Expression(ExprContext.ACCEPT_SUBQUERY)

    (
    LOOKAHEAD(2)
    updateCall = WhenMatchedClause(table, alias)
    [ insertCall = WhenNotMatchedClause(table) ]
    |
    insertCall = WhenNotMatchedClause(table)
    )
    {
        return new SqlMerge(
            SqlStdOperatorTable.mergeOperator,
            table,
            condition,
            sourceTableRef,
            updateCall,
            insertCall,
            alias,
            mergePos);
    }
}

SqlNode WhenMatchedClause(SqlIdentifier table, SqlIdentifier alias) :
{
    SqlIdentifier id;
    SqlParserPos pos;
    SqlNodeList updateColumnList;
    SqlNode exp;
    SqlNodeList updateExprList;
}
{
    <WHEN> <MATCHED> <THEN>
    <UPDATE> <SET> id = SimpleIdentifier()
    {
        pos = getPos();
        updateColumnList = new SqlNodeList(pos);
        updateExprList = new SqlNodeList(pos);
        updateColumnList.add(id);
    }
    <EQ> exp = Expression(ExprContext.ACCEPT_SUBQUERY)
    {
        updateExprList.add(exp);
    }
    (
        <COMMA>
        id = SimpleIdentifier()
        {
            updateColumnList.add(id);
        }
        <EQ> exp = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            updateExprList.add(exp);
        }
    ) *
    {
        return new SqlUpdate(
            SqlStdOperatorTable.updateOperator,
            table,
            updateColumnList,
            updateExprList,
            null,
            alias,
            pos);
    }
}

SqlNode WhenNotMatchedClause(SqlIdentifier table) :
{
    SqlParserPos pos, insertPos, columnListPos = null;
    List keywords = new ArrayList();
    List insertColumnList = null;
    SqlNode rowConstructor;
    SqlNode insertValues;
}
{
    <WHEN> <NOT> <MATCHED> <THEN>
    <INSERT>
    {
        insertPos = getPos();
    }
    SqlInsertKeywords(keywords)
    [
        LOOKAHEAD(2)
        <LPAREN>
        insertColumnList = SimpleIdentifierCommaList()
        {
            columnListPos = getPos();
        }
        <RPAREN>
    ]
    [ <LPAREN> ]
    <VALUES> { pos = getPos(); }
    rowConstructor = RowConstructor()
    [ <RPAREN> ]
    {
        // TODO zfong 5/26/06: note that extra parentheses are accepted above
        // around the VALUES clause as a hack for unparse, but this is
        // actually invalid SQL; should fix unparse
        insertValues = SqlStdOperatorTable.valuesOperator.createCall(
            pos.plus(rowConstructor.getParserPosition()),
            rowConstructor);
        return new SqlInsert(
            SqlStdOperatorTable.insertOperator,
            new SqlNodeList(keywords, insertPos),
            table,
            insertValues,
            (insertColumnList != null)
                ? new SqlNodeList(insertColumnList, columnListPos) : null,
            insertPos);
    }
}

/**
 * Parses the select list of a SELECT statement.
 */
List SelectList() :
{
    List list = new ArrayList();
    SqlNode item;
}
{
    item = SelectItem() {list.add(item);}
    ( <COMMA> item = SelectItem() {list.add(item);} ) *
    {
        return list;
    }
}

/**
 * Parses one item in a select list.
 */
SqlNode SelectItem() :
{
    SqlNode e;
    SqlIdentifier id;
    SqlParserPos pos;
}
{
    e = SelectExpression()
    [
        [ <AS> ]
        id = SimpleIdentifier()
        {
            pos = e.getParserPosition().plus(getPos());
            e = SqlStdOperatorTable.asOperator.createCall(pos, e, id);
        }
    ]
    {
        return e;
    }
}

/**
 * Parses one unaliased expression in a select list.
 */
SqlNode SelectExpression() :
{
    SqlNode e;
    String id;
    SqlParserPos pos, starPos;
}
{
    LOOKAHEAD(3)
    id = Identifier() { pos = getPos(); } <DOT> <STAR>
    {
        starPos = getPos();
        return new SqlIdentifier(
            new String[] {id, "*"},
            null,
            pos.plus(starPos),
            new SqlParserPos[] {pos, starPos});
    }
    |
    <STAR>
    {
        pos = getPos();
        return new SqlIdentifier("*", pos);
    }
    |
    e = Expression(ExprContext.ACCEPT_SUBQUERY)
    {
        return e;
    }
}

// TODO jvs 15-Nov-2003:  SQL standard allows parentheses in the FROM list for
// building up non-linear join trees (e.g. OUTER JOIN two tables, and then INNER
// JOIN the result).  Also note that aliases on parenthesized FROM expressions
// "hide" all table names inside the parentheses (without aliases, they're
// visible).
//
// We allow CROSS JOIN to have a join condition, even though that is not valid
// SQL; the validator will catch it.
/**
 * Parses the FROM clause a SELECT.
 */
SqlNode FromClause() :
{
    SqlNode e, e2, condition;
    boolean isNatural;
    SqlJoinOperator.JoinType joinType;
    List list;
    SqlParserPos pos;
    SqlParserPos joinTypePos;
    SqlParserPos naturalPos;
}
{
    e = TableRef()
    (
        // Decide whether to read a JOIN clause or a comma, or to quit having
        // seen a single entry FROM clause like 'FROM emps'. See comments
        // elsewhere regarding <COMMA> lookahead.
        LOOKAHEAD(2)
        (
            <NATURAL> {isNatural = true;}
            |
            {isNatural = false;}
        )
        {
            naturalPos = getPos();
        }
        (
            <JOIN>
            {joinType = SqlJoinOperator.JoinType.Inner;}
            |
            <INNER> <JOIN>
            {joinType = SqlJoinOperator.JoinType.Inner;}
            |
            <LEFT> [ <OUTER> ] <JOIN>
            {joinType = SqlJoinOperator.JoinType.Left;}
            |
            <RIGHT> [ <OUTER> ] <JOIN>
            {joinType = SqlJoinOperator.JoinType.Right;}
            |
            <FULL> [ <OUTER> ] <JOIN>
            {joinType = SqlJoinOperator.JoinType.Full;}
            |
            <CROSS> <JOIN>
            {joinType = SqlJoinOperator.JoinType.Cross;}
        )
        {
            joinTypePos = getPos();
        }
        e2 = TableRef()
        (
            <ON> { pos = getPos(); }
            condition = Expression(ExprContext.ACCEPT_SUBQUERY) {
                SqlParserPos onPos = pos.plus(getPos());
                e = SqlStdOperatorTable.joinOperator.createCall(
                    e,
                    SqlLiteral.createBoolean(isNatural, naturalPos),
                    SqlLiteral.createSymbol(joinType, joinTypePos),
                    e2,
                    SqlLiteral.createSymbol(
                        SqlJoinOperator.ConditionType.On, onPos),
                    condition,
                    joinTypePos);
            }
            |
            <USING> { pos = getPos(); }
            <LPAREN> list = SimpleIdentifierCommaList() <RPAREN> {
                SqlParserPos usingPos = pos.plus(getPos());
                e = SqlStdOperatorTable.joinOperator.createCall(
                    e,
                    SqlLiteral.createBoolean(isNatural, naturalPos),
                    SqlLiteral.createSymbol(joinType, joinTypePos),
                    e2,
                    SqlLiteral.createSymbol(
                        SqlJoinOperator.ConditionType.Using, usingPos),
                    new SqlNodeList(list, usingPos),
                    joinTypePos);
            }
            |
            {
                e = SqlStdOperatorTable.joinOperator.createCall(
                    e,
                    SqlLiteral.createBoolean(isNatural, naturalPos),
                    SqlLiteral.createSymbol(joinType, joinTypePos),
                    e2,
                    SqlLiteral.createSymbol(
                        SqlJoinOperator.ConditionType.None, joinTypePos),
                    null,
                    joinTypePos);
            }
        )
        |
        // NOTE jvs 6-Feb-2004:  See comments at top of file for why
        // hint is necessary here.  I had to use this special semantic
        // lookahead form to get JavaCC to shut up, which makes
        // me even more uneasy.
        LOOKAHEAD({true})
        <COMMA> { pos = getPos(); }
        e2 = TableRef() {
            e = SqlStdOperatorTable.joinOperator.createCall(
                e,
                SqlLiteral.createBoolean(false, pos),
                SqlLiteral.createSymbol(SqlJoinOperator.JoinType.Comma,
                    SqlParserPos.ZERO),
                e2,
                SqlLiteral.createSymbol(
                    SqlJoinOperator.ConditionType.None, SqlParserPos.ZERO),
                null,
                pos);
        }
    ) *
    {
        return e;
    }
}

// TODO jvs 15-Nov-2003: SQL standard allows column aliases on table
// references, e.g. DEPTS AS D1(DEPTNO1,DNAME1); I guess this is syntactic
// sugar to make it easier for query writers to conform to the column name
// uniqueness rules without requiring them to write a nested SELECT, but it
// seems pretty useless for non-trivial tables, since you have to supply names
// for ALL columns at once.
/**
 * Parses a table reference in a FROM clause.
 */
SqlNode TableRef() :
{
    SqlNode tableRef;
    SqlNode over;
    String alias;
    SqlParserPos pos;
    SqlNodeList args;
    SqlNode sample;
    boolean isBernoulli;
    SqlNumericLiteral samplePercentage;
    boolean isRepeatable = false;
    int repeatableSeed = 0;
    List<SqlNode> columnAliasList = null;
}
{
    (
        LOOKAHEAD(2)
        tableRef = CompoundIdentifier()
        over = TableOverOpt()
        {
            if (over != null) {
                pos = getPos();
                tableRef = SqlStdOperatorTable.overOperator.createCall(
                    pos, tableRef, over);
            }
        }
    |
        { boolean isLateral = false; }
        [<LATERAL> { isLateral = true;} ]
        tableRef = ParenthesizedExpression(ExprContext.ACCEPT_QUERY)
        over = TableOverOpt()
        {
            if (over != null) {
                pos = getPos();
                tableRef = SqlStdOperatorTable.overOperator.createCall(
                    pos, tableRef, over);
            }
            if (isLateral) {
                tableRef = SqlStdOperatorTable.lateralOperator.createCall(
                    getPos(), tableRef);
            }
        }
    |
        <UNNEST> { pos = getPos(); }
        args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUBQUERY)
        {
            tableRef =
                SqlStdOperatorTable.unnestOperator.createCall(
                    pos.plus(getPos()), args.toArray());
        }
    |
        <TABLE> { pos = getPos(); } <LPAREN>
        tableRef = TableFunctionCall(pos)
        <RPAREN>
        {
        }
    |
        tableRef = ExtendedTableRef()
    )
    [
        [ <AS> ] alias = Identifier()
        [ <LPAREN> columnAliasList = SimpleIdentifierCommaList() <RPAREN> ]
        {
            pos = getPos();
            if (columnAliasList == null) {
                tableRef = SqlStdOperatorTable.asOperator.createCall(
                    pos, tableRef, new SqlIdentifier(alias, pos));
            } else {
                List<SqlNode> idList = new ArrayList<SqlNode>();
                idList.add(tableRef);
                idList.add(new SqlIdentifier(alias, pos));
                idList.addAll(columnAliasList);
                tableRef = SqlStdOperatorTable.asOperator.createCall(
                    pos, idList);
            }
        }
    ]
    [
        <TABLESAMPLE> { pos = getPos(); }
        (
            <SUBSTITUTE> <LPAREN> sample = StringLiteral(true) <RPAREN>
            {
                String sampleName = SqlLiteral.stringValue(sample);
                SqlSampleSpec sampleSpec = SqlSampleSpec.createNamed(sampleName);
                SqlLiteral sampleLiteral = SqlLiteral.createSample(sampleSpec, pos);
                tableRef = SqlStdOperatorTable.sampleFunction.createCall(
                    pos.plus(getPos()), tableRef, sampleLiteral);
             }
        |
            (
                <BERNOULLI>
                {
                    isBernoulli = true;
                }
            |
                <SYSTEM>
                {
                    isBernoulli = false;
                }
            )
            <LPAREN> samplePercentage = UnsignedNumericLiteral() <RPAREN>
            [ 
                <REPEATABLE> <LPAREN> repeatableSeed = IntLiteral() <RPAREN>
                {
                    isRepeatable = true;
                }
            ]
            {
                final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100L);
                BigDecimal rate = samplePercentage.bigDecimalValue();
                if (rate.compareTo(BigDecimal.ZERO) < 0 ||
                    rate.compareTo(ONE_HUNDRED) > 0)
                {
                    throw new ParseException(
                        EigenbaseResource.instance().InvalidSampleSize.str());
                }

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

SqlNode TableFunctionCall(SqlParserPos pos) :
{
    SqlNode call;
    SqlFunctionCategory funcType =
        SqlFunctionCategory.UserDefinedFunction;
}
{
    [
        <SPECIFIC>
        {
            funcType = SqlFunctionCategory.UserDefinedSpecificFunction;
        }
    ]
    {
    }
    call = NamedRoutineCall(funcType, ExprContext.ACCEPT_CURSOR)
    {
        return SqlStdOperatorTable.collectionTableOperator.createCall(
            pos, call);

        return call;
    }
}

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

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

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

/**
 * Parses a VALUES leaf query expression.
 */
SqlNode TableConstructor() :
{
    SqlNodeList rowConstructorList;
    SqlParserPos pos;
}
{
    <VALUES>
    {
        pos = getPos();
    }
    rowConstructorList = RowConstructorList(pos)
    {
        return SqlStdOperatorTable.valuesOperator.createCall(
            pos.plus(getPos()), rowConstructorList.toArray());
    }
}

/**
 * Parses one or more rows in a VALUES expression.
 */
SqlNodeList RowConstructorList(SqlParserPos pos) :
{
    List list = new ArrayList();
    SqlNode rowConstructor;
}
{
    rowConstructor = RowConstructor() { list.add(rowConstructor); }
    (
        LOOKAHEAD(2)
        <COMMA> rowConstructor = RowConstructor() { list.add(rowConstructor); }
    ) *
    {
        return new SqlNodeList(list, pos.plus(getPos()));
    }
}

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

/**
 * Parses the optional WHERE clause for SELECT, DELETE, and UPDATE.
 */
SqlNode WhereOpt() :
{
    SqlNode condition;
}
{
    <WHERE> condition = Expression(ExprContext.ACCEPT_SUBQUERY)
    {
        return condition;
    }
    |
    {
        return null;
    }
}

/**
 * Parses the optional GROUP BY clause for SELECT.
 */
SqlNode GroupByOpt() :
{
    SqlNodeList list;
    SqlNode e;
    SqlParserPos pos;
}
{
    LOOKAHEAD(4)
    <GROUP> { pos = getPos(); }
    <BY> list = ExpressionCommaList(pos, ExprContext.ACCEPT_SUBQUERY)
    {
        return list;
    }
    |
    <GROUP> <BY> <LPAREN> <RPAREN>
    {
        return new SqlNodeList(getPos());
    }
    |
    {
        return null;
    }
}

/**
 * Parses a list of expressions separated by commas.
 */
SqlNodeList ExpressionCommaList(
    SqlParserPos pos,
    ExprContext exprContext) :
{
    List list;
    SqlNode e;
}
{
    e = Expression(exprContext)
    {
        if (pos == null) {
            pos = getPos();
        }
        pos = pos.plus(getPos());
        list = startList(e);
    }
    (
        // NOTE jvs 6-Feb-2004:  See comments at top of file for why
        // hint is necessary here.
        LOOKAHEAD(2)
        <COMMA> e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            list.add(e);
            pos = pos.plus(getPos());
        }
    ) *
    {
        return new SqlNodeList(list, pos);
    }
}

/**
 * Parses the optional HAVING clause for SELECT.
 */
SqlNode HavingOpt() :
{
    SqlNode e;
}
{
    <HAVING> e = Expression(ExprContext.ACCEPT_SUBQUERY)
    {
        return e;
    }
    |
    {
        return null;
    }
}

/**
 * Parses the optional WINDOW clause for SELECT
 */
SqlNodeList WindowOpt() :
{
    SqlIdentifier id;
    SqlWindow e;
    List list;
    SqlParserPos pos;
}
{
    <WINDOW> id = SimpleIdentifier() <AS> e = WindowSpecification()
    {
        pos = getPos();
        e.setDeclName(id);
        list = startList(e);
    }
    (
        // NOTE jhyde 22-Oct-2004:  See comments at top of file for why
        // hint is necessary here.
        LOOKAHEAD(2)
        <COMMA> id = SimpleIdentifier() <AS> e = WindowSpecification()
        {
            e.setDeclName(id);
            list.add(e);
        }
    ) *
    {
        return new SqlNodeList(list, pos);
    }
    |
    {
        return null;
    }
}

/**
 * Parses a window specification.
 */
SqlWindow WindowSpecification() :
{
    SqlIdentifier id;
    List list;
    SqlNodeList partitionList;
    SqlNodeList orderList;
    SqlLiteral isRows = SqlLiteral.createBoolean(false, SqlParserPos.ZERO);
    SqlNode lowerBound = null, upperBound = null;
    SqlParserPos startPos;
    SqlParserPos endPos;
    SqlParserPos pos;
    SqlLiteral allowPartial = null;
}
{
    <LPAREN> { startPos = pos = getPos(); }
    (
        id = SimpleIdentifier()
        |
        { id = null; }
    )
    (
        <PARTITION>
        { pos = getPos(); }
        <BY>
        partitionList = ExpressionCommaList(pos, ExprContext.ACCEPT_NONQUERY)
        |
        { partitionList = SqlNodeList.Empty; }
    )
    (
        orderList = OrderBy(true)
        |
        { orderList = SqlNodeList.Empty; }
    )
    [
        (
            <ROWS> { isRows = SqlLiteral.createBoolean(true, getPos()); }
            |
            <RANGE> { isRows = SqlLiteral.createBoolean(false, getPos()); }
        )
        (
            <BETWEEN> lowerBound = WindowRange()
            <AND> upperBound = WindowRange()
            |
            lowerBound = WindowRange()
        )
    ]
    [
        <ALLOW> { pos = getPos(); } <PARTIAL> {
            allowPartial = SqlLiteral.createBoolean(true, pos.plus(getPos()));
        }
    |
        <DISALLOW> { pos = getPos(); } <PARTIAL> {
            allowPartial = SqlLiteral.createBoolean(false, pos.plus(getPos()));
        }
    ]
    <RPAREN>
    {
        endPos = getPos();
        return SqlStdOperatorTable.windowOperator.createCall(
            null, id, partitionList, orderList,
            isRows, lowerBound, upperBound, allowPartial,
            startPos.plus(endPos));
    }
}

SqlNode WindowRange() :
{
    SqlLiteral literal;
    SqlParserPos pos=null;
    SqlParserPos endPos;
}
{
    <CURRENT> {pos = getPos();} <ROW>
    {
        endPos = getPos();
        return SqlStdOperatorTable.windowOperator.createCurrentRow(
            pos.plus(endPos));
    }
    |
    <UNBOUNDED>
        { pos = getPos();}
    (
        <PRECEDING>
        {
            endPos = getPos();
            return SqlStdOperatorTable.windowOperator.createUnboundedPreceding(
                pos.plus(endPos));
        }
        |
        <FOLLOWING>
        {
            endPos = getPos();
            return SqlStdOperatorTable.windowOperator.createUnboundedFollowing(
                pos.plus(endPos));
        }
    )
    |
    literal = StrictLiteral()
    (
        <PRECEDING>
        {
            return SqlStdOperatorTable.windowOperator.createPreceding(
                literal, getPos());
        }
        |
        <FOLLOWING>
        {
            return SqlStdOperatorTable.windowOperator.createFollowing(
                literal, getPos());
        }
    )
}

/**
 * Parses an ORDER BY clause.
 */
SqlNodeList OrderBy(boolean accept) :
{
    List list;
    SqlNode e;
    SqlParserPos pos;
}
{
    <ORDER>
    {
        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(
                getPos(),
                EigenbaseResource.instance().IllegalOrderBy.ex());
        }
    }
    <BY> e = OrderItem()
    {
        pos = getPos();
        list = startList(e);
    }
    (
        // NOTE jvs 6-Feb-2004:  See comments at top of file for why
        // hint is necessary here.
        LOOKAHEAD(2) <COMMA> e = OrderItem() { list.add(e); }
    ) *
    {
        return new SqlNodeList(list, pos);
    }
}

/**
 * Parses one list item in an ORDER BY clause.
 */
SqlNode OrderItem() :
{
    SqlNode e;
    SqlParserPos pos;
}
{
    e = Expression(ExprContext.ACCEPT_SUBQUERY)
    (
        <ASC>
        {
            return e;
        }
        | <DESC>
        {
            pos = getPos();
            return SqlStdOperatorTable.descendingOperator.createCall(pos, e);
        }
        |
        {
            return e;
        }
    )
}

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

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

/**
 * Parses either a row expression or a query expression without ORDER BY.
 */
SqlNode QueryOrExpr(ExprContext exprContext) :
{
    SqlNode e;
    SqlOperator op;
    SqlParserPos pos;
    List list;
}
{
    e = LeafQueryOrExpr(exprContext)
    {
        list = startList(e);
    }
    (
        {
            if (!e.isA(SqlKind.Query)) {
                // whoops, expression we just parsed wasn't a query,
                // but we're about to see something like UNION, so
                // force an exception retroactively
                checkNonQueryExpression(ExprContext.ACCEPT_QUERY);
            }
        }
        op = BinaryQueryOperator()
        {
            // ensure a query is legal in this context
            pos = getPos();
            checkQueryExpression(exprContext);

        }
        e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
        {
            list.add(new SqlParserUtil.ToTreeListItem(op, pos));
            list.add(e);
        }
    ) *
    {
        e = SqlParserUtil.toTree(list);
        return e;
    }
}

/**
 * Parses either a row expression, a leaf query expression, or
 * a parenthesized expression of any kind.
 */
SqlNode LeafQueryOrExpr(ExprContext exprContext) :
{
    SqlNode e;
}
{
    e = Expression(exprContext)
    {
        return e;
    }
    | e = LeafQuery(exprContext)
    {
        return e;
    }
}

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

// TODO jvs 15-Nov-2003:  ANY/ALL

/**
 * Parses a binary row expression, or a parenthesized expression of any
 * kind.
 *
 * <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 opos 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 Expression2(ExprContext exprContext) :
{
    List list, list2;
    SqlNodeList nodeList;
    SqlNode e;
    SqlOperator op;
    SqlParserPos pos;
}
{
    e = Expression3(exprContext)
    {
        list = startList(e);
    }
    (
        (
            LOOKAHEAD(2)
            (
                // Special case for "IN", because RHS of "IN" is the only place
                // that an expression-list is allowed ("exp IN (exp1, exp2)").
                LOOKAHEAD(2)
                {
                    checkNonQueryExpression(exprContext);
                }
                (
                    <NOT> <IN>
                    {
                        op = SqlStdOperatorTable.notInOperator;
                        pos = getPos();
                    }
                    |
                    <IN>
                    {
                        op = SqlStdOperatorTable.inOperator;
                        pos = getPos();
                    }
                )
                nodeList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR)
                {
                    list.add(new SqlParserUtil.ToTreeListItem(op, pos));
                    pos = pos.plus(getPos());
                    // special case for stuff like IN (s1 UNION s2)
                    if (nodeList.size() == 1) {
                        SqlNode item = nodeList.get(0);
                        if (item.isA(SqlKind.Query)) {
                            list.add(item);
                        } else {
                            list.add(nodeList);
                        }
                    } else {
                        list.add(nodeList);
                    }
                }
                |
                LOOKAHEAD(2)
                {
                    checkNonQueryExpression(exprContext);
                }
                (
                    <NOT> <BETWEEN>
                    {
                        op = SqlStdOperatorTable.notBetweenOperator;
                        pos = getPos();
                    }
                    [
                        <SYMMETRIC> { op = SqlStdOperatorTable.symmetricNotBetweenOperator; }
                        |
                        <ASYMMETRIC>
                    ]
                    |
                    <BETWEEN>
                    {
                        op = SqlStdOperatorTable.betweenOperator;
                        pos = getPos();
                    }
                    [
                        <SYMMETRIC> { op = SqlStdOperatorTable.symmetricBetweenOperator; }
                        |
                        <ASYMMETRIC>
                    ]
                )
                e = Expression3(ExprContext.ACCEPT_SUBQUERY)
                {
                    list.add(new SqlParserUtil.ToTreeListItem(op, pos));
                    list.add(e);
                }
                |
                {
                    checkNonQueryExpression(exprContext);
                    pos = getPos();
                }
                (
                    <NOT>
                    (
                        <LIKE> { op = SqlStdOperatorTable.notLikeOperator; }
                        |
                        <SIMILAR> <TO> { op = SqlStdOperatorTable.notSimilarOperator; }
                    )
                    |
                    <LIKE> { op = SqlStdOperatorTable.likeOperator; }
                    |
                    <SIMILAR> <TO> { op = SqlStdOperatorTable.similarOperator; }
                )
                list2 = Expression2(ExprContext.ACCEPT_SUBQUERY)
                {
                    list.add(new SqlParserUtil.ToTreeListItem(op, pos));
                    list.addAll(list2);
                }
                [
                    LOOKAHEAD(2)
                    <ESCAPE> e = Expression3(ExprContext.ACCEPT_SUBQUERY)
                    {
                        pos = getPos();
                        list.add(
                            new SqlParserUtil.ToTreeListItem(
                                SqlStdOperatorTable.escapeOperator, pos));
                        list.add(e);
                    }
                ]
                |
                LOOKAHEAD(3) op = BinaryRowOperator()
                {
                    checkNonQueryExpression(exprContext);
                }
                e = Expression3(ExprContext.ACCEPT_SUBQUERY)
                {
                    list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
                    list.add(e);
                }
                |
                {
                    checkNonQueryExpression(exprContext);
                }
                op = PostfixRowOperator()
                {
                    list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
                }
            )
        ) +
        {
            return list;
        }
        |
        {
            return list;
        }
    )
}

/**
 * Parses a unary row expression, or a parenthesized expression of any
 * kind.
 */
SqlNode Expression3(ExprContext exprContext) :
{
    SqlNode e;
    List list;
    SqlNodeList list1;
    SqlNodeList list2;
    SqlPrefixOperator op;
    boolean rowSeen = false;
    SqlParserPos pos;
    SqlParserPos prefixRowOpPos;
}
{
    LOOKAHEAD(2)
    e = AtomicRowExpression()
    {
        checkNonQueryExpression(exprContext);
        return e;
    }
    |
    e = CursorExpression(exprContext) { return e; }
    |
    LOOKAHEAD(3)
    <ROW> <LPAREN> list = SimpleIdentifierCommaList() <RPAREN>
    {
        pos = getPos();
        if (exprContext != ExprContext.ACCEPT_ALL &&
            exprContext != ExprContext.ACCEPT_CURSOR)
        {
            throw SqlUtil.newContextException(
                pos,
                EigenbaseResource.instance().IllegalRowExpression.ex());
        }
        return SqlStdOperatorTable.rowConstructor.createCall(
            pos, SqlParserUtil.toNodeArray(list));
    }
    |
    op = PrefixRowOperator()
    {
        prefixRowOpPos = getPos();
        checkNonQueryExpression(exprContext);
    }
    e = Expression3(ExprContext.ACCEPT_SUBQUERY)
    {
        SqlParserPos callPos = prefixRowOpPos.plus(e.getParserPosition());
        return op.createCall(callPos, e);
    }
    |
    {
        pos = getPos();
    }
    [
        <ROW>
        {
            pos = getPos(); rowSeen = true;
        }
    ]
    list1 = ParenthesizedQueryOrCommaList(exprContext) {
        if (rowSeen) {
            // interpret as row constructor
            return SqlStdOperatorTable.rowConstructor.createCall(
                pos, list1.toArray());

        }
    }
    [
        (
            <OVERLAPS>
            list2 = ParenthesizedQueryOrCommaList(exprContext)
            {
                if (list1.size() != 2 || list2.size() != 2) {
                    throw SqlUtil.newContextException(
                        list1.getParserPosition().plus(
                            list2.getParserPosition()),
                        EigenbaseResource.instance().IllegalOverlaps.ex());
                }
                for (SqlNode node : list2) {
                    list1.add(node);
                }
                return SqlStdOperatorTable.overlapsOperator.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.isA(SqlKind.Minus) &&
                        (call.operands.length == 2))
                    {
                        List list3 = startList(call.operands[0]);
                        list3.add(call.operands[1]);
                        list3.add(e);
                        return SqlStdOperatorTable.minusDateOperator.createCall(
                            list1.getParserPosition().plus(getPos()),
                            SqlParserUtil.toNodeArray(list3));
                     }
                }
                throw SqlUtil.newContextException(
                    list1.getParserPosition().plus(getPos()),
                    EigenbaseResource.instance().IllegalMinusDate.ex());
            }
        )
    ]
    {
        if (list1.size() == 1) {
            // interpret as single value or query
            return list1.get(0);
        } else {
            // interpret as row constructor
            return SqlStdOperatorTable.rowConstructor.createCall(
                pos, list1.toArray());
        }
    }
}

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

/**
 * Parses an atomic row expression.
 */
SqlNode AtomicRowExpression() :
{
    SqlNode e;
    SqlParserPos whenPos;
    SqlParserPos thenPos;
    SqlParserPos pos;
}
{
    e = Literal() { return e; }
    |
    e = DynamicParam() { return e; }
    |
    e = BuiltinFunctionCall() { return e; }
    |
    e = JdbcFunctionCall() { return e; }
    |
    e = MultisetConstructor() { return e; }
    |
    // NOTE jvs 18-Jan-2005:  use syntactic lookahead to discriminate
    // compound identifiers from function calls in which the function
    // name is a compound identifier
    LOOKAHEAD( [<SPECIFIC>] FunctionName() <LPAREN>)
    e = NamedFunctionCall() { return e; }
    |
    e = ContextVariable() { return e; }
    |
    e = CompoundIdentifier() { return e; }
    |
    e = NewSpecification() { return e; }
    |
    {
        SqlNode caseIdentifier=null;
        SqlNode elseClause = null;
        List whenList = new ArrayList();
        List thenList = new ArrayList();
        //sorry there is no wishlist
    }
    <CASE>
    {
        pos = getPos();
    }
    [
        caseIdentifier = Expression(ExprContext.ACCEPT_SUBQUERY)
    ]
    (
        <WHEN> e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {  whenPos = getPos(); whenList.add(e); }
        <THEN> e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {  thenPos = getPos(); thenList.add(e); }
    ) +
    [
        <ELSE> elseClause = Expression(ExprContext.ACCEPT_SUBQUERY)
    ]
    <END>
    {
        pos = pos.plus(getPos());
        return SqlStdOperatorTable.caseOperator.createSwitchedCall(
            pos,
            caseIdentifier,
            new SqlNodeList(whenList, whenPos),
            new SqlNodeList(thenList, thenPos),
            elseClause);
    }
}

/**
 * Parses a literal expression, allowing continued string literals.
 * Usually returns an SqlLiteral, but a continued string literal
 * is an SqlCall expression, which concatenates 2 or more string
 * literals; the validator reduces this.
 */
SqlNode Literal() :
{
    SqlNode e;
}
{
    e = NumericLiteral()
    {
        return e;
    }
    | e = StringLiteral(false)
    {
        return e;
    }
    | e = SpecialLiteral()
    {
        return e;
    }
    | e = DateTimeLiteral()
    {
        return e;
    }
    | e = IntervalLiteral()
    {
        return e;
    }
}


/**
 * Parses a literal expression, not allowing a continued string literal.
 * Always returns an SqlLiteral. (Some places in the DDL parser expect a simple
 * SqlLiteral.)
 */
SqlLiteral StrictLiteral() :
{
    SqlLiteral lit;
    SqlNode e;
}
{
    lit = NumericLiteral()
    {
        return lit;
    }
    | e = StringLiteral(true)
    {
        return (SqlLiteral) e;
    }
    | lit = SpecialLiteral()
    {
        return lit;
    }
    | lit = DateTimeLiteral()
    {
        return lit;
    }
    | lit = IntervalLiteral()
    {
        return lit;
    }
}


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

/** Parses a numeric literal (can be signed) */
SqlLiteral NumericLiteral() :
{
    SqlNumericLiteral num;
    SqlParserPos pos;
}
{
    <PLUS> num = UnsignedNumericLiteral()
    {
        return num;
    }
    |
    <MINUS> { pos = getPos(); } num = UnsignedNumericLiteral()
    {
        return SqlLiteral.createNegative(num, pos.plus(getPos()));
    }
    |
    num = UnsignedNumericLiteral()
    {
        return num;
    }
}

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

/**
 * Parses a string literal. Optionally parses a literal 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.
 *
 * @param strict when false, allow continued literals; when true, forbid them.
 * @return a literal expression. When strict, returns an SqlLiteral.
 */
SqlNode StringLiteral(boolean strict) :
{
    String p;
    int nfrags = 0;
    List frags = null;
    char unicodeEscapeChar = 0;
}
{
    // A continued string literal consists of a head fragment and one or more
    // tail fragments. Since comments may occur between the fragments, and
    // comments are special tokens, each fragment is a token. But since spaces
    // or comments may not occur between the prefix and the first quote, the
    // head fragment, with any prefix, is one token.

    <BINARY_STRING_LITERAL>
    {
        try {
            p = SqlParserUtil.trim(token.image, "xX'");
            frags = startList(SqlLiteral.createBinaryString(p, getPos()));
            nfrags++;
        } catch (NumberFormatException ex) {
            throw SqlUtil.newContextException(
                getPos(),
                EigenbaseResource.instance().IllegalBinaryString.ex(
                    token.image));
        }
    }
    (
        <QUOTED_STRING>
        {
            try {
                p = SqlParserUtil.trim(token.image, "'"); // no embedded quotes
                frags.add(SqlLiteral.createBinaryString(p, getPos()));
                nfrags++;
            } catch (NumberFormatException ex) {
                throw SqlUtil.newContextException(
                    getPos(),
                    EigenbaseResource.instance().IllegalBinaryString.ex(
                        token.image));
            }
        }
    ) *
    {
        assert (nfrags > 0);
        if (nfrags == 1) {
            return (SqlLiteral) frags.get(0); // just the head fragment
        } else {
            SqlParserPos pos2 = SqlParserPos.sum(frags);
            return SqlStdOperatorTable.literalChainOperator.createCall(
                pos2, frags);
        }
    }
    |
    {
        String charSet = null;
    }
    (
        <PREFIXED_STRING_LITERAL>
        { charSet = SqlParserUtil.getCharacterSet(token.image); }
        | <QUOTED_STRING>
        | <UNICODE_STRING_LITERAL>
        {
            // TODO jvs 2-Feb-2009:  support the explicit specification of
            // a character set for Unicode string literals, per SQL:2003
            unicodeEscapeChar = '\\';
            charSet = "UTF16";
        }
    )
    {
        p = SqlParserUtil.parseString(token.image);
        SqlCharStringLiteral literal;
        try {
            literal = SqlLiteral.createCharString(p, charSet, getPos());
        } catch (java.nio.charset.UnsupportedCharsetException e) {
            throw SqlUtil.newContextException(
                getPos(),
                EigenbaseResource.instance().UnknownCharacterSet.ex(charSet));
        }
        frags = startList(literal);
        nfrags++;
    }
    (
        <QUOTED_STRING>
        {
            p = SqlParserUtil.parseString(token.image);
            try {
                literal = SqlLiteral.createCharString(p, charSet, getPos());
            } catch (java.nio.charset.UnsupportedCharsetException e) {
                throw SqlUtil.newContextException(
                    getPos(),
                    EigenbaseResource.instance().UnknownCharacterSet.ex(charSet));
            }
            frags.add(literal);
            nfrags++;
        }
    ) *
    {
    }
    [
        <UESCAPE> <QUOTED_STRING>
        {
            if (unicodeEscapeChar == 0) {
                throw SqlUtil.newContextException(
                    getPos(),
                    EigenbaseResource.instance().UnicodeEscapeUnexpected.ex());
            }
            String s = SqlParserUtil.parseString(token.image);
            unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s);
        }
    ]
    {
    }
    {
        assert (nfrags > 0);
        if (nfrags == 1) {
            // just the head fragment
            SqlLiteral lit = (SqlLiteral) frags.get(0);
            return lit.unescapeUnicode(unicodeEscapeChar);
        } else {
            SqlNode[] rands = (SqlNode[]) frags.toArray(new SqlNode[nfrags]);
            for (int i = 0; i < rands.length; ++i) {
                rands[i] = ((SqlLiteral) rands[i]).unescapeUnicode(
                    unicodeEscapeChar);
            }
            SqlParserPos pos2 = SqlParserPos.sum(rands);
            return SqlStdOperatorTable.literalChainOperator.createCall(
                pos2, rands);
        }
    }
}


/**
 * Parses a date/time literal.
 */
SqlLiteral DateTimeLiteral() :
{
    String  p;
    SqlParserPos pos;
}
{
    <LBRACE_D> <QUOTED_STRING>
    {
        p = token.image;
    }
    <RBRACE>
    {
        return parseDateLiteral(p, getPos());
    }
    |
    <LBRACE_T> <QUOTED_STRING>
    {
        p = token.image;
    }
    <RBRACE>
    {
        return parseTimeLiteral(p, getPos());
    }
    |
    <LBRACE_TS> <QUOTED_STRING>
    {
        p = token.image;
    }
    <RBRACE>
    {
        return parseTimestampLiteral(p, getPos());
    }
    |
    <DATE> { pos = getPos(); } <QUOTED_STRING>
    {
        return parseDateLiteral(token.image, pos.plus(getPos()));
    }
    |
    <TIME> { pos = getPos(); } <QUOTED_STRING>
    {
        return parseTimeLiteral(token.image, pos.plus(getPos()));
    }
    |
    <TIMESTAMP> { pos = getPos(); } <QUOTED_STRING>
    {
        return parseTimestampLiteral(token.image, pos.plus(getPos()));
    }
}

/** Parses a MULTISET constructor */
SqlNode MultisetConstructor() :
{
    List args;
    SqlNode e;
    SqlParserPos pos;
}
{
    LOOKAHEAD(2)
    <MULTISET> { pos = getPos(); }
    <LPAREN>
    //by sub query "MULTISET(SELECT * FROM T)"
    e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY)
    <RPAREN>
    {
        return SqlStdOperatorTable.multisetQueryConstructor.createCall(
            pos.plus(getPos()), e);
    }
    |
    <MULTISET> { pos = getPos(); }
    //by enumeration "MULTISET[e0, e1, ..., eN]"
    <LBRACKET> //todo do trigraph aswell ??( ??)
    e = Expression(ExprContext.ACCEPT_NONQUERY) { args = startList(e); }
    (
        <COMMA> e = Expression(ExprContext.ACCEPT_NONQUERY) { args.add(e); }
    ) *
    <RBRACKET>
    {
        return SqlStdOperatorTable.multisetValueConstructor.createCall(
            pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
    }
}

/**
 * Parses an interval literal.
 */
SqlLiteral IntervalLiteral() :
{
    String p;
    SqlIntervalQualifier intervalQualifier;
    int sign = 1;
    SqlParserPos pos;
}
{
    <INTERVAL> { pos = getPos(); }
    [
        <MINUS> { sign = -1; }
    |
        <PLUS> { sign = 1; }
    ]
    <QUOTED_STRING> { p = token.image; }
    intervalQualifier = IntervalQualifier()
    {
        return parseIntervalLiteral(pos.plus(getPos()), sign, p, intervalQualifier);
    }
}

SqlIntervalQualifier IntervalQualifier() :
{
    SqlIntervalQualifier.TimeUnit start;
    SqlIntervalQualifier.TimeUnit end = null;
    int startPrec = SqlIntervalQualifier.getDefaultPrecisionId();
    int secondFracPrec = SqlIntervalQualifier.getDefaultPrecisionId();
}
{
    (
        <YEAR> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
        [
            LOOKAHEAD(2) <TO> <MONTH>
            {
                end = SqlIntervalQualifier.TimeUnit.Month;
            }
        ]
        { start = SqlIntervalQualifier.TimeUnit.Year; }
        |
        <MONTH> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
        { start = SqlIntervalQualifier.TimeUnit.Month; }
        |
        <DAY> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
        [ LOOKAHEAD(2) <TO>
            (
                <HOUR> { end = SqlIntervalQualifier.TimeUnit.Hour; }
                | <MINUTE> { end = SqlIntervalQualifier.TimeUnit.Minute; }
                | <SECOND> { end = SqlIntervalQualifier.TimeUnit.Second; }
                [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
            )
        ]
        { start = SqlIntervalQualifier.TimeUnit.Day; }
        |
        <HOUR> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
        [ LOOKAHEAD(2) <TO>
            (
                <MINUTE> { end = SqlIntervalQualifier.TimeUnit.Minute; }
                | <SECOND> { end = SqlIntervalQualifier.TimeUnit.Second; }
                [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
            )
        ]
        { start = SqlIntervalQualifier.TimeUnit.Hour; }
        |
        <MINUTE> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
        [ LOOKAHEAD(2) <TO>
            (
                <SECOND> { end = SqlIntervalQualifier.TimeUnit.Second; }
                [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
            )
        ]
        { start = SqlIntervalQualifier.TimeUnit.Minute; }
        |
        <SECOND>
        [   <LPAREN> startPrec = UnsignedIntLiteral()
            [ <COMMA> secondFracPrec = UnsignedIntLiteral() ]
            <RPAREN>
        ]
        { start = SqlIntervalQualifier.TimeUnit.Second; }
    )
    {
        return new SqlIntervalQualifier(start,
            startPrec,
            end,
            secondFracPrec,
            getPos());
    }
}

SqlIntervalQualifier.TimeUnit TimeUnit() :
{}
{
    <YEAR>
    {
        return SqlIntervalQualifier.TimeUnit.Year;
    }
    | <MONTH>
    {
        return SqlIntervalQualifier.TimeUnit.Month;
    }
    | <DAY>
    {
        return SqlIntervalQualifier.TimeUnit.Day;
    }
    | <HOUR>
    {
        return SqlIntervalQualifier.TimeUnit.Hour;
    }
    | <MINUTE>
    {
        return SqlIntervalQualifier.TimeUnit.Minute;
    }
    |   <SECOND>
    {
        return SqlIntervalQualifier.TimeUnit.Second;
    }
}

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


/**
 * Parses a simple identifier as a string.
 */
String Identifier() :
{
    String id;
    char unicodeEscapeChar = '\\';
}
{
    (
        <IDENTIFIER>
        {
            id = getToken(0).image.toUpperCase();
        }
        | <QUOTED_IDENTIFIER>
        {
            id = SqlParserUtil.strip(getToken(0).image, "\"");
        }
        | <UNICODE_QUOTED_IDENTIFIER>
        {
            id = getToken(0).image;
            id = id.substring(id.indexOf('"'));
            id = SqlParserUtil.strip(id, "\"");
        }
        [ <UESCAPE> <QUOTED_STRING>
            {
                String s = SqlParserUtil.parseString(token.image);
                unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s);
            }
        ]
        {
        }
        {
            SqlLiteral lit = SqlLiteral.createCharString(id, "UTF16", getPos());
            lit = lit.unescapeUnicode(unicodeEscapeChar);
            return lit.toValue();
        }
        | id = NonReservedKeyWord()
    )
    {
        if (id.length() > 128) {
            throw SqlUtil.newContextException(
                getPos(),
                EigenbaseResource.instance().IdentifierTooLong.ex(id, 128));
        }
        return id;
    }
}

/**
 * Parses a simple identifier as an SqlIdentifier.
 */
SqlIdentifier SimpleIdentifier() :
{
    String p;
    SqlParserPos pos;
}
{
    p = Identifier(){pos = getPos();}
    {
        return new SqlIdentifier(p,pos);
    }
}

/**
 * Parses a comma-separated list of simple identifiers.
 */
List SimpleIdentifierCommaList() :
{
    List list = new ArrayList();
    SqlIdentifier id;
}
{
    id = SimpleIdentifier() {list.add(id);}
    (<COMMA> id = SimpleIdentifier() {list.add(id);}) *
    {
        return list;
    }
}

/**
 * Parses a compound identifier.
 */
SqlIdentifier CompoundIdentifier() :
{
    List list = new ArrayList();
    List posList = new ArrayList();
    String p;
}
{
    p = Identifier()
    {
        posList.add(getPos());
        list.add(p);
    }
    (
        <DOT> p = Identifier()
        {
            list.add(p);
            posList.add(getPos());
        }
    ) *
    {
        SqlParserPos[] componentPositions =
            (SqlParserPos []) posList.toArray(
                new SqlParserPos[posList.size()]);
        SqlParserPos pos = SqlParserPos.sum(componentPositions);
        return new SqlIdentifier(
            SqlParserUtil.toStringArray(list), null, pos, componentPositions);
    }
}

/**
 * Parses a NEW UDT(...) expression.
 */
SqlNode NewSpecification() :
{
    SqlParserPos callPos;
    SqlNode routineCall;
}
{
    <NEW>
    {
        callPos = getPos();
    }
    routineCall =
    NamedRoutineCall(
        SqlFunctionCategory.UserDefinedConstructor,
        ExprContext.ACCEPT_SUBQUERY)
    {
        return SqlStdOperatorTable.newOperator.createCall(
            callPos, routineCall);
    }
}

//TODO: real parse errors.
int UnsignedIntLiteral() :
{
    Token t;
}
{
    t = <UNSIGNED_INTEGER_LITERAL>
    {
        try {
            return Integer.parseInt(t.image);
        } catch (NumberFormatException ex) {
            throw generateParseException();
        }
    }
}

int IntLiteral() :
{
    Token t;
}
{
    ( t = <UNSIGNED_INTEGER_LITERAL> | <PLUS> t = <UNSIGNED_INTEGER_LITERAL> )
    {
        try {
            return Integer.parseInt(t.image);
        } catch (NumberFormatException ex) {
            throw generateParseException();
        }
    }
    |
    <MINUS> t = <UNSIGNED_INTEGER_LITERAL>
    {
        try {
            return -Integer.parseInt(t.image);
        } catch (NumberFormatException ex) {
            throw generateParseException();
        }
    }
}

// Type name with optional scale and precision
SqlDataTypeSpec DataType() :
{
    SqlIdentifier typeName;
    SqlIdentifier collectionTypeName = null;
    int scale = -1;
    int precision = -1;
    String charSetName = null;
    SqlParserPos pos;
}
{
    (
        typeName = TypeName()
        {
            pos = getPos();
        }
        [
            <LPAREN>
            precision = UnsignedIntLiteral()
            [
                <COMMA>
                scale = UnsignedIntLiteral()
            ]
            <RPAREN>
        ]
        [
            <CHARACTER> <SET>
            charSetName = Identifier()
        ]
        [
            collectionTypeName = CollectionsTypeName()
        ]
    )
    {
        if (null != collectionTypeName) {
            return new SqlDataTypeSpec(
                collectionTypeName,
                typeName,
                precision,
                scale,
                charSetName,
                pos);
        }
        return new SqlDataTypeSpec(
            typeName,
            precision,
            scale,
            charSetName,
            null,
            pos);
    }
}

// Some SQL type names need special handling due to the fact that they have
// spaces in them but are not quoted.
SqlIdentifier TypeName() :
{
    SqlIdentifier typeName = null;
    SqlParserPos pos = getPos();
}
{
    (
        (<CHARACTER> | <CHAR>)
        {
            typeName = new SqlIdentifier(
                SqlTypeName.CHAR.name(), pos);
        }
        [
            <VARYING>
            {
                typeName = new SqlIdentifier(
                    SqlTypeName.VARCHAR.name(), pos);
            }
        ]
        | <VARCHAR>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.VARCHAR.name(), pos);
        }
        | <DATE>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.DATE.name(),pos);
        }
        | <TIME>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.TIME.name(),pos);
        }
        | <TIMESTAMP>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.TIMESTAMP.name(),pos);
        }
        | (<DECIMAL> | <DEC> | <NUMERIC>)
        {
            typeName = new SqlIdentifier(
                SqlTypeName.DECIMAL.name(),pos);
        }
        | <BOOLEAN>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.BOOLEAN.name(),pos);
        }
        | <INTEGER>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.INTEGER.name(),pos);
        }
        | <INT>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.INTEGER.name(),pos);
        }
        | (
            <BINARY>
            {
                typeName = new SqlIdentifier(
                    SqlTypeName.BINARY.name(),pos);
            }
        )
        [
            <VARYING>
            {
                typeName = new SqlIdentifier(
                    SqlTypeName.VARBINARY.name(), pos);
            }
        ]
        | <VARBINARY>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.VARBINARY.name(),pos);
        }
        | <TINYINT>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.TINYINT.name(),pos);
        }
        | <SMALLINT>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.SMALLINT.name(),pos);
        }
        | <BIGINT>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.BIGINT.name(),pos);
        }
        | <REAL>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.REAL.name(),pos);
        }
        | <DOUBLE> [ <PRECISION> ]
        {
            typeName = new SqlIdentifier(
                SqlTypeName.DOUBLE.name(),pos);
        }
        | <FLOAT>
        {
            typeName = new SqlIdentifier(
                SqlTypeName.FLOAT.name(),pos);
        }
        |   typeName = CollectionsTypeName()
        |   typeName = CompoundIdentifier()
    )
    {
        return typeName;
    }
}

SqlIdentifier CollectionsTypeName() :
{
    SqlIdentifier typeName = null;
    SqlParserPos pos = getPos();
}
{
    <MULTISET>
    {
        typeName = new SqlIdentifier(
            SqlTypeName.MULTISET.name(),pos);
        return typeName;
    }
}

/**
 * Parses a CURSOR(query) expression.  The parser allows these
 * anywhere, but the validator restricts them to appear only as
 * arguments to table functions.
 */
SqlNode CursorExpression(ExprContext exprContext) :
{
    SqlNode e;
    SqlParserPos pos;
}
{
    <CURSOR>
    {
        pos = getPos();
        if (exprContext != ExprContext.ACCEPT_ALL &&
            exprContext != ExprContext.ACCEPT_CURSOR)
        {
            throw SqlUtil.newContextException(
                pos,
                EigenbaseResource.instance().IllegalCursorExpression.ex());
        }
    }
    e = Expression(ExprContext.ACCEPT_QUERY)
    {
        return SqlStdOperatorTable.cursorConstructor.createCall(
            pos, e);
        return e;
    }
}

/**
 * Parses a call to a builtin function with special syntax.
 */
SqlNode BuiltinFunctionCall() :
{
    String name;
    List args = null;
    SqlNode e = null;
    SqlParserPos pos;
    SqlParserPos starPos;
    SqlParserPos namePos;
    SqlDataTypeSpec dt;
}
{
    //~ FUNCTIONS WITH SPECIAL SYNTAX ---------------------------------------
    (
        <CAST>
        {
            pos = getPos();
        }
        <LPAREN> e = Expression(ExprContext.ACCEPT_SUBQUERY) { args = startList(e); }
        <AS>
        (
            dt = DataType() { args.add(dt); }
            |  <INTERVAL> e = IntervalQualifier() { args.add(e); }
        )
        <RPAREN>
        {
            return SqlStdOperatorTable.castFunc.createCall(
                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
        }
    )
    |
    (
        <EXTRACT>
        {
            pos = getPos();
            SqlIntervalQualifier.TimeUnit unit;
        }
        <LPAREN>
        unit = TimeUnit()
        { args = startList(new SqlIntervalQualifier(unit, null,getPos())); }
        <FROM>
        e = Expression(ExprContext.ACCEPT_SUBQUERY) { args.add(e); }
        <RPAREN>
        {
            return SqlStdOperatorTable.extractFunc.createCall(
                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
        }
    )
    |
    (
        <POSITION>
        {
            pos = getPos();
        }
        <LPAREN>
        // FIXME jvs 31-Aug-2006:  FRG-192:  This should be
        // Expression(ExprContext.ACCEPT_SUBQUERY), but that doesn't work
        // because it matches the other kind of IN.
        e = AtomicRowExpression() { args = startList(e); }
        <IN>
        e = Expression(ExprContext.ACCEPT_SUBQUERY) { args.add(e);}
        <RPAREN>
        {
            return SqlStdOperatorTable.positionFunc.createCall(
                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
        }
    )
    |
    (
        <CONVERT>
        {
            pos = getPos();
        }
        <LPAREN>
        e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            args = startList(e);
        }
        <USING> name = Identifier()
        {
            namePos = getPos();
            args.add(new SqlIdentifier(name, namePos));
        }
        <RPAREN>
        {
            return SqlStdOperatorTable.convertFunc.createCall(
                pos, SqlParserUtil.toNodeArray(args));
        }
    )
    |
    (
        <TRANSLATE>
        {
            pos = getPos();
        }
        <LPAREN>
        e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            args = startList(e);
        }
        <USING> name = Identifier()
        {
            namePos = getPos();
            args.add(new SqlIdentifier(name, namePos));
        }
        <RPAREN>
        {
            return SqlStdOperatorTable.translateFunc.createCall(
                pos, SqlParserUtil.toNodeArray(args));
        }
    )
    |
    (
        <OVERLAY>
        {
            pos = getPos();
        }
        <LPAREN>
        e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            args = startList(e);
        }
        <PLACING>
        e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            args.add(e);
        }
        <FROM>
        e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            args.add(e);
        }
        [
            <FOR>
            e = Expression(ExprContext.ACCEPT_SUBQUERY)
            {
                args.add(e);
            }
        ]
        <RPAREN>
        {
            return SqlStdOperatorTable.overlayFunc.createCall(
                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
        }
    )
    |
    (
        <FLOOR>
        e = FloorCeilOptions(getPos(), true)
        { return e; }
    )
    |
    (
        ( <CEIL> | <CEILING>)
        e = FloorCeilOptions(getPos(), false)
        { return e; }
    )
    |
    (
        <SUBSTRING>
        {
            pos = getPos();
        }
        <LPAREN>
        e = Expression(ExprContext.ACCEPT_SUBQUERY)
        { args = startList(e); }
        ( <FROM> | <COMMA>)
        e = Expression(ExprContext.ACCEPT_SUBQUERY)
        { args.add(e); }
        [
            (<FOR> | <COMMA>)
            e = Expression(ExprContext.ACCEPT_SUBQUERY)
            { args.add(e); }
        ]
        <RPAREN>
        {
            return SqlStdOperatorTable.substringFunc.createCall(
                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
        }
    )
    |
    (
        <TRIM>
        {
            SqlLiteral flag = null;
            SqlNode trimChars = null;
            pos = getPos();
        }
        <LPAREN>
        [
            LOOKAHEAD(2)
            [
                <BOTH>
                {
                    pos = getPos();
                    flag = SqlLiteral.createSymbol(
                        SqlTrimFunction.Flag.BOTH, pos);
                }
                |
                <TRAILING>
                {
                    pos = getPos();
                    flag = SqlLiteral.createSymbol(
                        SqlTrimFunction.Flag.TRAILING, pos);
                }
                |
                <LEADING>
                {
                    pos = getPos();
                    flag = SqlLiteral.createSymbol(
                        SqlTrimFunction.Flag.LEADING, pos);
                }
            ]
            [ trimChars = Expression(ExprContext.ACCEPT_SUBQUERY) ]
            (
                <FROM>
                {
                    if (null == flag && null == trimChars) {
                        throw SqlUtil.newContextException(
                            getPos(),
                            EigenbaseResource.instance().IllegalFromEmpty.ex());
                    }
                }
                | <RPAREN>
                {
                    // This is to handle the case of TRIM(x)
                    // (FRG-191).
                    args = startList(flag);
                    args.add(null); // no trim chars
                    args.add(trimChars); // reinterpret trimChars as source
                    return SqlStdOperatorTable.trimFunc.createCall(
                        pos, SqlParserUtil.toNodeArray(args));
                }
            )
        ]
        e = Expression(ExprContext.ACCEPT_SUBQUERY)
        {
            args = startList(flag);
            args.add(trimChars);
            args.add(e);
        }
        <RPAREN>
        {
            return SqlStdOperatorTable.trimFunc.createCall(
                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
        }
    )
    |
    {
        SqlNode node;
    }
    node = ExtendedBuiltinFunctionCall()
    {
        return node;
    }
}

/**
 * Parses a call to a named function (could be a builtin with regular
 * syntax, or else a UDF).
 *
 *<p>
 *
 * NOTE: every UDF has two names: an <em>invocation name</em> and a
 * <em>specific name</em>.  Normally, function calls are resolved via overload
 * resolution and invocation names.  The SPECIFIC prefix allows overload
 * resolution to be bypassed.  Note that usage of the SPECIFIC prefix in
 * queries is non-standard; it is used internally by Farrago, e.g. in stored
 * view definitions to permanently bind references to a particular function
 * after the overload resolution performed by view creation.
 *
 *<p>
 *
 * TODO jvs 25-Mar-2005:  Once we have SQL-Flagger support, flag SPECIFIC
 * as non-standard.
 */
SqlNode NamedFunctionCall() :
{
    SqlIdentifier qualifiedName;
    SqlIdentifier id = null;
    List args;
    SqlParserPos pos;
    SqlParserPos starPos;
    SqlParserPos overPos = null;
    boolean over = false;
    SqlCall function = null;
    SqlFunctionCategory funcType =
        SqlFunctionCategory.UserDefinedFunction;
    SqlNode e = null;
    SqlLiteral quantifier = null;
}
{
    [
        <SPECIFIC>
        {
            funcType = SqlFunctionCategory.UserDefinedSpecificFunction;
        }
    ]
    (
        qualifiedName = FunctionName()
        {
            pos = getPos();
        }
        (
            LOOKAHEAD(2) <LPAREN> <STAR> { starPos = getPos(); } <RPAREN>
            {
                args = startList(new SqlIdentifier("*", starPos));
                pos = pos.plus(getPos());
            }
            | LOOKAHEAD(2) <LPAREN> <RPAREN>
            {
                args = SqlParserUtil.emptyList;
                pos = pos.plus(getPos());
            }
            | args = FunctionParameterList(ExprContext.ACCEPT_SUBQUERY)
            {
                pos = pos.plus(getPos());
                quantifier = (SqlLiteral) args.get(0);
                args.remove(0);
            }
        )
        [ <OVER>
            {
                overPos = getPos();
                over = true;
                pos = pos.plus(overPos);
            }
            (
                id = SimpleIdentifier()
                | e = WindowSpecification()
            )
        ]
        {
            function = createCall(
                qualifiedName, pos, funcType, quantifier,
                SqlParserUtil.toNodeArray(args));

            if (over) {
                if (id != null) {
                    return SqlStdOperatorTable.overOperator.createCall(
                        overPos, new SqlNode[] {function, id});
                } else {
                    return SqlStdOperatorTable.overOperator.createCall(
                        overPos, new SqlNode[] { function, e });
                }
            } else {
                return function;
            }
        }
    )
}


/*
* Parse Floor/Ceil function parameters
*/
SqlNode StandardFloorCeilOptions(SqlParserPos pos, boolean floorFlag) :
{
    SqlIdentifier name;
    SqlParserPos overPos = null;
    SqlIdentifier id = null;
    SqlNode e = null;
    SqlCall function = null;
    SqlNodeList args;
    boolean over = false;
}
{
    args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUBQUERY)
    [
        <OVER>
        {
            overPos = getPos();
            over = true;
        }
        (
            id = SimpleIdentifier()
            | e = WindowSpecification()
        )
    ]
    {
        SqlOperator op = floorFlag ?
            SqlStdOperatorTable.floorFunc :
            SqlStdOperatorTable.ceilFunc;
        function =  op.createCall(
            pos, args.toArray());
        if (over) {
            if (id != null) {
                return SqlStdOperatorTable.overOperator.createCall(
                    overPos, new SqlNode[] {function, id});
            } else {
                return SqlStdOperatorTable.overOperator.createCall(
                    overPos, new SqlNode[] { function, e });
            }
        } else {
            return function;
        }
    }
}

/**
 * Parses the name of a JDBC function that is a token but is not reserved.
 */
String NonReservedJdbcFunctionName() :
{
}
{
    (
        <SUBSTRING>
    )
    {
        return getToken(0).image.toUpperCase();
    }
}

/**
 * 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() :
{
    SqlIdentifier name;
}
{
    (
        <ABS>
        | <AVG>
        | <CARDINALITY>
        | <CHAR_LENGTH>
        | <CHARACTER_LENGTH>
        | <COALESCE>
        | <COLLECT>
        | <CUME_DIST>
        | <COUNT>
        | <CURRENT_DATE>
        | <CURRENT_TIME>
        | <CURRENT_TIMESTAMP>
        | <DENSE_RANK>
        | <ELEMENT>
        | <EXP>
        | <FIRST_VALUE>
        | <FUSION>
        | <LAST_VALUE>
        | <LN>
        | <LOCALTIME>
        | <LOCALTIMESTAMP>
        | <LOWER>
        | <MAX>
        | <MIN>
        | <MOD>
        | <NULLIF>
        | <OCTET_LENGTH>
        | <PERCENT_RANK>
        | <POWER>
        | <RANK>
        | <ROW_NUMBER>
        | <SQRT>
        | <SUM>
        | <UPPER>
    )
    {
        name = new SqlIdentifier(
            getToken(0).image.toUpperCase(), getPos());
        return name;
    }
}

SqlIdentifier ContextVariable() :
{
    SqlIdentifier name;
}
{
    (
        <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>
    )
    {
        name = new SqlIdentifier(
            getToken(0).image.toUpperCase(), getPos());
        return name;
    }
}

/**
 * Parses a function call expression with JDBC syntax.
 */
SqlNode JdbcFunctionCall() :
{
    String name;
    SqlIdentifier id;
    SqlNodeList args;
    SqlParserPos pos;
    SqlParserPos starPos;
}
{
    (
        <LBRACE_FN>
        {
            pos = getPos();
        }
        (
            // INSERT is a reserved word, but we need to handle {fn insert}
            <INSERT> { name = getToken(0).image.toUpperCase(); }
            |
            // For cases like {fn power(1,2)} and {fn lower('a')}
            id = ReservedFunctionName() { name = id.getSimple(); }
            |
            // For cases like {fn substring('foo', 1,2)}
            name = NonReservedJdbcFunctionName()
            |
            name = Identifier()
        )
        (
            LOOKAHEAD(2) <LPAREN> <STAR> {starPos = getPos();} <RPAREN>
            {
                args = new SqlNodeList(starPos);
                args.add(new SqlIdentifier("*", starPos));
            }
            | LOOKAHEAD(2) <LPAREN> <RPAREN>
            { args = new SqlNodeList(pos); }
            | args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUBQUERY)
        )
        <RBRACE>
        {
            return new SqlJdbcFunctionCall(name).createCall(
                pos.plus(getPos()), SqlParserUtil.toNodeArray(args));
        }
    )
}

/**
 * Parses a binary query operator like UNION.
 */
SqlBinaryOperator BinaryQueryOperator() :
{
    SqlBinaryOperator op;
}
{
    /* If both the ALL or DISTINCT keywords are missing, DISTINCT is implicit */
    (
        <UNION> { op = SqlStdOperatorTable.unionOperator; }
        [
            <ALL> { op = SqlStdOperatorTable.unionAllOperator; }
            | <DISTINCT> { op = SqlStdOperatorTable.unionOperator; }
        ]
        |
        <INTERSECT> { op = SqlStdOperatorTable.intersectOperator; }
        [
            <ALL> { op = SqlStdOperatorTable.intersectAllOperator; }
            | <DISTINCT> { op = SqlStdOperatorTable.intersectOperator; }
        ]
        |
        <EXCEPT> { op = SqlStdOperatorTable.exceptOperator; }
        [
            <ALL> { op = SqlStdOperatorTable.exceptAllOperator; }
            | <DISTINCT> { op = SqlStdOperatorTable.exceptOperator; }
        ]
    )
    { return op; }
}

/**
 * Parses a binary multiset operator.
 */
SqlBinaryOperator BinaryMultisetOperator() :
{
    SqlBinaryOperator op;
}
{
    /* If both the ALL or DISTINCT keywords are missing, DISTINCT is implicit */
    (
        <MULTISET>
        (
            <UNION> { op = SqlStdOperatorTable.multisetUnionOperator; }
            [
                <ALL> { op = SqlStdOperatorTable.multisetUnionAllOperator; }
                | <DISTINCT> { op = SqlStdOperatorTable.multisetUnionOperator; }
            ]
            |
            <INTERSECT> { op = SqlStdOperatorTable.multisetIntersectOperator; }
            [
                <ALL> { op = SqlStdOperatorTable.multisetIntersectAllOperator; }
                | <DISTINCT> { op = SqlStdOperatorTable.multisetIntersectOperator; }
            ]
            |
            <EXCEPT> { op = SqlStdOperatorTable.multisetExceptOperator; }
            [
                <ALL> { op = SqlStdOperatorTable.multisetExceptAllOperator; }
                | <DISTINCT> { op = SqlStdOperatorTable.multisetExceptOperator; }
            ]
        )
    )
    { return op; }
}

/**
 * Parses a binary row operator like AND.
 */
SqlBinaryOperator BinaryRowOperator() :
{
}
{
    <EQ>
    { return SqlStdOperatorTable.equalsOperator; }
    | <GT>
    { return SqlStdOperatorTable.greaterThanOperator; }
    | <LT>
    { return SqlStdOperatorTable.lessThanOperator; }
    | <LE>
    { return SqlStdOperatorTable.lessThanOrEqualOperator; }
    | <GE>
    { return SqlStdOperatorTable.greaterThanOrEqualOperator; }
    | <NE>
    { return SqlStdOperatorTable.notEqualsOperator; }
    | <PLUS>
    { return SqlStdOperatorTable.plusOperator; }
    | <MINUS>
    { return SqlStdOperatorTable.minusOperator; }
    | <STAR>
    { return SqlStdOperatorTable.multiplyOperator; }
    | <SLASH>
    { return SqlStdOperatorTable.divideOperator; }
    | <CONCAT>
    { return SqlStdOperatorTable.concatOperator; }
    | <AND>
    { return SqlStdOperatorTable.andOperator; }
    | <OR>
    { return SqlStdOperatorTable.orOperator; }
    | LOOKAHEAD(2) <IS> <DISTINCT> <FROM>
    { return SqlStdOperatorTable.isDistinctFromOperator; }
    | <IS> <NOT> <DISTINCT> <FROM>
    { return SqlStdOperatorTable.isNotDistinctFromOperator; }
    // <IN> is handled as a special case
    | <MEMBER> <OF>
    { return SqlStdOperatorTable.memberOfOperator; }
    | <SUBMULTISET> <OF>
    { return SqlStdOperatorTable.submultisetOfOperator; }
    |
    { SqlBinaryOperator op;}
    op = BinaryMultisetOperator()
    { return op; }
}

/**
 * Parses a prefix row operator like NOT.
 */
SqlPrefixOperator PrefixRowOperator() :
{}
{
    <PLUS> { return SqlStdOperatorTable.prefixPlusOperator; }
    | <MINUS> { return SqlStdOperatorTable.prefixMinusOperator; }
    | <NOT> { return SqlStdOperatorTable.notOperator; }
    | <EXISTS> { return SqlStdOperatorTable.existsOperator; }
}

/**
 * Parses a postfix row operator like IS NOT NULL.
 */
SqlPostfixOperator PostfixRowOperator() :
{}
{
    <IS>
    (
        <A> <SET> { return SqlStdOperatorTable.isASetOperator; }
        |
        <NOT>
        (
            <NULL> { return SqlStdOperatorTable.isNotNullOperator; }
            | <TRUE> { return SqlStdOperatorTable.isNotTrueOperator; }
            | <FALSE> { return SqlStdOperatorTable.isNotFalseOperator; }
            | <UNKNOWN> { return SqlStdOperatorTable.isNotUnknownOperator; }
        )
        |
        (
            <NULL> { return SqlStdOperatorTable.isNullOperator; }
            | <TRUE> { return SqlStdOperatorTable.isTrueOperator; }
            | <FALSE> { return SqlStdOperatorTable.isFalseOperator; }
            | <UNKNOWN> { return SqlStdOperatorTable.isUnknownOperator; }
        )
    )
}


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

TOKEN :
{
    < A: "A" >
    | < ABS: "ABS" >
    | < ABSOLUTE: "ABSOLUTE" >
    | < ACTION: "ACTION" >
    | < ADA: "ADA" >
    | < ADD: "ADD" >
    | < ADMIN: "ADMIN" >
    | < AFTER: "AFTER" >
    | < ALL: "ALL" >
    | < ALLOCATE: "ALLOCATE" >
    | < ALLOW: "ALLOW" >
    | < ALWAYS: "ALWAYS" >
    | < ALTER: "ALTER" >
    | < AND: "AND" >
    | < ANY: "ANY" >
    | < ARE: "ARE" >
    | < ARRAY: "ARRAY" >
    | < AS: "AS" >
    | < ASC: "ASC" >
    | < ASENSITIVE: "ASENSITIVE" >
    | < ASSERTION: "ASSERTION" >
    | < ASSIGNMENT: "ASSIGNMENT" >
    | < ASYMMETRIC: "ASYMMETRIC" >
    | < AT: "AT" >
    | < ATOMIC: "ATOMIC" >
    | < ATTRIBUTE: "ATTRIBUTE" >
    | < ATTRIBUTES: "ATTRIBUTES" >
    | < AUTHORIZATION: "AUTHORIZATION" >
    | < AVG: "AVG" >
    | < BEFORE: "BEFORE" >
    | < BEGIN: "BEGIN" >
    | < BERNOULLI: "BERNOULLI" >
    | < BETWEEN: "BETWEEN" >
    | < BIGINT: "BIGINT" >
    | < BINARY: "BINARY" >
    | < BIT: "BIT" >
    | < BLOB: "BLOB" >
    | < BOOLEAN: "BOOLEAN" >
    | < BOTH: "BOTH" >
    | < BREADTH: "BREADTH" >
    | < BY: "BY" >
    | < C: "C" >
    | < CALL: "CALL" >
    | < CALLED: "CALLED" >
    | < CARDINALITY: "CARDINALITY" >
    | < CASCADE: "CASCADE" >
    | < CASCADED: "CASCADED" >
    | < CASE: "CASE" >
    | < CAST: "CAST" >
    | < CATALOG: "CATALOG" >
    | < CATALOG_NAME: "CATALOG_NAME" >
    | < CEIL: "CEIL" >
    | < CEILING: "CEILING" >
    | < 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: "CHARACTERISTICTS" >
    | < CHARACTERS: "CHARACTERS" >
    | < CHECK: "CHECK" >
    | < CLASS_ORIGIN: "CLASS_ORIGIN" >
    | < CLOB: "CLOB" >
    | < CLOSE: "CLOSE" >
    | < COALESCE: "COALESCE" >
    | < COBOL: "COBOL" >
    | < COLLATE: "COLLATE" >
    | < COLLATION: "COLLATION" >
    | < COLLATION_CATALOG: "COLLATION_CATALOG" >
    | < COLLATION_NAME: "COLLATION_NAME" >
    | < COLLATION_SCHEMA: "COLLATION_SCHEMA" >
    | < COLLECT: "COLLECT" >
    | < COLUMN: "COLUMN" >
    | < COLUMN_NAME: "COLUMN_NAME" >
    | < COMMAND_FUNCTION: "COMMAND_FUNCTION" >
    | < COMMAND_FUNCTION_CODE: "COMMAND_FUNCTION_CODE" >
    | < COMMIT: "COMMIT" >
    | < COMMITTED: "COMMITTED" >
    | < CONDITION: "CONDITION" >
    | < CONDITION_NUMBER: "CONDITION_NUMBER" >
    | < CONNECT: "CONNECT" >
    | < CONNECTION: "CONNECTION" >
    | < CONNECTION_NAME: "CONNECTION_NAME" >
    | < CONSTRAINT: "CONSTRAINT" >
    | < CONSTRAINT_CATALOG: "CONSTRAINT_CATALOG" >
    | < CONSTRAINT_NAME: "CONSTRAINT_NAME" >
    | < CONSTRAINT_SCHEMA: "CONSTRAINT_SCHEMA" >
    | < CONSTRAINTS: "CONSTRAINTS" >
    | < CONSTRUCTOR: "CONSTRUCTOR" >
    | < CONTAINS: "CONTAINS" >
    | < CONVERT: "CONVERT" >
    | < CONTINUE: "CONTINUE" >
    | < CORR: "CORR" >
    | < CORRESPONDING: "CORRESPONDING" >
    | < COUNT: "COUNT" >
    | < COVAR_POP: "COVAR_POP" >
    | < COVAR_SAMP: "COVAR_SAMP" >
    | < CREATE: "CREATE" >
    | < CROSS: "CROSS" >
    | < CUBE: "CUBE" >
    | < CUME_DIST: "CUME_DIST" >
    | < CURRENT: "CURRENT" >
    | < CURRENT_CATALOG: "CURRENT_CATALOG" >
    | < CURRENT_DATE: "CURRENT_DATE" >
    | < CURRENT_DEFAULT_TRANSFORM_GROUP: "CURRENT_DEFAULT_TRANSFORM_GROUP" >
    | < CURRENT_PATH: "CURRENT_PATH" >
    | < CURRENT_ROLE: "CURRENT_ROLE" >
    | < CURRENT_SCHEMA: "CURRENT_SCHEMA" >
    | < CURRENT_TIME: "CURRENT_TIME" >
    | < CURRENT_TIMESTAMP: "CURRENT_TIMESTAMP" >
    | < CURRENT_TRANSFORM_GROUP_FOR_TYPE: "CURRENT_TRANSFORM_GROUP_FOR_TYPE" >
    | < CURRENT_USER: "CURRENT_USER" >
    | < CURSOR: "CURSOR" >
    | < CURSOR_NAME: "CURSOR_NAME" >
    | < CYCLE: "CYCLE" >
    | < DATA: "DATA" >
    | < DATE: "DATE" >
    | < DATETIME_INTERVAL_CODE: "DATETIME_INTERVAL_CODE" >
    | < DATETIME_INTERVAL_PRECISION: "DATETIME_INTERVAL_PRECISION" >
    | < DAY: "DAY" >
    | < DEALLOCATE: "DEALLOCATE" >
    | < DEC: "DEC" >
    | < DECIMAL: "DECIMAL" >
    | < DECLARE: "DECLARE" >
    | < DEFAULT_KW: "DEFAULT" >
    | < DEFAULTS: "DEFAULTS" >
    | < DEFERRABLE: "DEFERRABLE" >
    | < DEFERRED: "DEFERRED" >
    | < DEFINED: "DEFINED" >
    | < DEFINER: "DEFINER" >
    | < DEGREE: "DEGREE" >
    | < DELETE: "DELETE" >
    | < DENSE_RANK: "DENSE_RANK" >
    | < DEPTH: "DEPTH" >
    | < DEREF: "DEREF" >
    | < DERIVED: "DERIVED" >
    | < DESCRIBE: "DESCRIBE" >
    | < DESC: "DESC" >
    | < DESCRIPTION: "DESCRIPTION" >
    | < DESCRIPTOR: "DESCRIPTOR" >
    | < DETERMINISTIC: "DETERMINISTIC" >
    | < DIAGNOSTICS: "DIAGNOSTICS" >
    | < DISALLOW: "DISALLOW" >
    | < DISCONNECT: "DISCONNECT" >
    | < DISPATCH: "DISPATCH" >
    | < DISTINCT: "DISTINCT" >
    | < DOMAIN: "DOMAIN" >
    | < DOUBLE: "DOUBLE" >
    | < DROP: "DROP" >
    | < DYNAMIC: "DYNAMIC" >
    | < DYNAMIC_FUNCTION: "DYNAMIC_FUNCTION" >
    | < DYNAMIC_FUNCTION_CODE: "DYNAMIC_FUNCTION_CODE" >
    | < EACH: "EACH" >
    | < ELEMENT: "ELEMENT" >
    | < ELSE: "ELSE" >
    | < END: "END" >
    | < END_EXEC: "END-EXEC" >
    | < EQUALS: "EQUALS" >
    | < ESCAPE: "ESCAPE" >
    | < EVERY: "EVERY" >
    | < EXCEPT: "EXCEPT" >
    | < EXCEPTION: "EXCEPTION" >
    | < EXCLUDE: "EXCLUDE" >
    | < EXCLUDING: "EXCLUDING" >
    | < EXEC: "EXEC" >
    | < EXECUTE: "EXECUTE" >
    | < EXISTS: "EXISTS" >
    | < EXP: "EXP" >
    | < EXPLAIN: "EXPLAIN" >
    | < EXTERNAL: "EXTERNAL" >
    | < EXTRACT: "EXTRACT" >
    | < FALSE: "FALSE" >
    | < FETCH: "FETCH" >
    | < FILTER: "FILTER" >
    | < FINAL: "FINAL" >
    | < FIRST: "FIRST" >
    | < FIRST_VALUE: "FIRST_VALUE">
    | < FLOAT: "FLOAT" >
    | < FLOOR: "FLOOR" >
    | < FOLLOWING: "FOLLOWING" >
    | < FOR: "FOR" >
    | < FOREIGN: "FOREIGN" >
    | < FORTRAN: "FORTRAN" >
    | < FOUND: "FOUND" >
    | < FREE: "FREE" >
    | < FROM: "FROM" >
    | < FULL: "FULL" >
    | < FUNCTION: "FUNCTION" >
    | < FUSION: "FUSION" >
    | < G: "G" >
    | < GENERAL: "GENERAL" >
    | < GENERATED: "GENERATED" >
    | < GET: "GET" >
    | < GLOBAL: "GLOBAL" >
    | < GO: "GO" >
    | < GOTO: "GOTO" >
    | < GRANT: "GRANT" >
    | < GRANTED: "GRANTED" >
    | < GROUP: "GROUP" >
    | < GROUPING: "GROUPING" >
    | < HAVING: "HAVING" >
    | < HIERARCHY: "HIERARCHY" >
    | < HOLD: "HOLD" >
    | < HOUR: "HOUR" >
    | < IDENTITY: "IDENTITY" >
    | < IMMEDIATE: "IMMEDIATE" >
    | < IMPLEMENTATION: "IMPLEMENTATION" >
    | < IMPORT: "IMPORT" >
    | < IN: "IN" >
    | < INDICATOR: "INDICATOR" >
    | < INCLUDING: "INCLUDING" >
    | < INCREMENT: "INCREMENT" >
    | < INITIALLY: "INITIALLY" >
    | < INNER: "INNER" >
    | < INOUT: "INOUT" >
    | < INPUT: "INPUT" >
    | < INSENSITIVE: "INSENSITIVE" >
    | < INSERT: "INSERT" >
    | < INSTANCE: "INSTANCE" >
    | < INSTANTIABLE: "INSTANTIABLE" >
    | < INT: "INT" >
    | < INTEGER: "INTEGER" >
    | < INTERSECT: "INTERSECT" >
    | < INTERSECTION: "INTERSECTION" >
    | < INTERVAL: "INTERVAL" >
    | < INTO: "INTO" >
    | < INVOKER: "INVOKER" >
    | < IS: "IS" >
    | < ISOLATION: "ISOLATION" >
    | < JAVA: "JAVA" >
    | < JOIN: "JOIN" >
    | < K: "K" >
    | < KEY: "KEY" >
    | < KEY_MEMBER: "KEY_MEMBER" >
    | < KEY_TYPE: "KEY_TYPE" >
    | < LABEL: "LABEL" >
    | < LANGUAGE: "LANGUAGE" >
    | < LARGE: "LARGE" >
    | < LAST: "LAST" >
    | < LAST_VALUE: "LAST_VALUE" >
    | < LATERAL: "LATERAL" >
    | < LEADING: "LEADING" >
    | < LEFT: "LEFT" >
    | < LENGTH: "LENGTH" >
    | < LEVEL: "LEVEL" >
    | < LIBRARY: "LIBRARY" >
    | < LIKE: "LIKE" >
    | < LIMIT: "LIMIT" >
    | < LN: "LN" >
    | < LOCAL: "LOCAL" >
    | < LOCALTIME: "LOCALTIME" >
    | < LOCALTIMESTAMP: "LOCALTIMESTAMP" >
    | < LOCATOR: "LOCATOR" >
    | < LOWER: "LOWER" >
    | < M: "M" >
    | < MAP: "MAP" >
    | < MATCH: "MATCH" >
    | < MATCHED: "MATCHED" >
    | < MAX: "MAX" >
    | < MAXVALUE: "MAXVALUE" >
    | < MEMBER: "MEMBER" >
    | < MERGE: "MERGE" >
    | < MESSAGE_LENGTH: "MESSAGE_LENGTH" >
    | < MESSAGE_OCTET_LENGTH: "MESSAGE_OCTET_LENGTH" >
    | < MESSAGE_TEXT: "MESSAGE_TEXT" >
    | < METHOD: "METHOD" >
    | < MIN: "MIN" >
    | < MINUTE: "MINUTE" >
    | < MINVALUE: "MINVALUE" >
    | < MOD: "MOD" >
    | < MODIFIES: "MODIFIES" >
    | < MODULE: "MODULE" >
    | < MONTH: "MONTH" >
    | < MORE_KW: "MORE" >
    | < MULTISET: "MULTISET" >
    | < MUMPS: "MUMPS" >
    | < NAME: "NAME" >
    | < NAMES: "NAMES" >
    | < NATIONAL: "NATIONAL" >
    | < NATURAL: "NATURAL" >
    | < NCHAR: "NCHAR" >
    | < NCLOB: "NCLOB" >
    | < NESTING: "NESTING" >
    | < NEW: "NEW" >
    | < NEXT: "NEXT" >
    | < NO: "NO" >
    | < NONE: "NONE" >
    | < NORMALIZE: "NORMALIZE" >
    | < NORMALIZED: "NORMALIZED" >
    | < NOT: "NOT" >
    | < NULL: "NULL" >
    | < NULLABLE: "NULLABLE" >
    | < NULLIF: "NULLIF" >
    | < NULLS: "NULLS" >
    | < NUMBER: "NUMBER" >
    | < NUMERIC: "NUMERIC" >
    | < OBJECT: "OBJECT" >
    | < OCTET_LENGTH: "OCTET_LENGTH" >
    | < OCTETS: "OCTETS" >
    | < OF: "OF" >
    | < OLD: "OLD" >
    | < ON: "ON" >
    | < ONLY: "ONLY" >
    | < OPEN: "OPEN" >
    | < OPTION: "OPTION" >
    | < OPTIONS: "OPTIONS" >
    | < OR: "OR" >
    | < ORDER: "ORDER" >
    | < ORDERING: "ORDERING" >
    | < ORDINALITY: "ORDINALITY" >
    | < OTHERS: "OTHERS" >
    | < OUT: "OUT" >
    | < OUTER: "OUTER" >
    | < OUTPUT: "OUTPUT" >
    | < OVER: "OVER" >
    | < OVERLAPS: "OVERLAPS" >
    | < OVERLAY: "OVERLAY" >
    | < OVERRIDING: "OVERRIDING" >
    | < PAD: "PAD" >
    | < PARAMETER: "PARAMETER" >
    | < PARAMETER_MODE: "PARAMETER_MODE" >
    | < PARAMETER_NAME: "PARAMETER_NAME" >
    | < PARAMETER_ORDINAL_POSITION: "PARAMETER_ORDINAL_POSITION" >
    | < PARAMETER_SPECIFIC_CATALOG: "PARAMETER_SPECIFIC_CATALOG" >
    | < PARAMETER_SPECIFIC_NAME: "PARAMETER_SPECIFIC_NAME" >
    | < PARAMETER_SPECIFIC_SCHEMA: "PARAMETER_SPECIFIC_SCHEMA" >
    | < PARTIAL: "PARTIAL" >
    | < PARTITION: "PARTITION" >
    | < PASCAL: "PASCAL" >
    | < PATH: "PATH" >
    | < PERCENT_RANK: "PERCENT_RANK" >
    | < PERCENTILE_CONT: "PERCENTILE_CONT" >
    | < PERCENTILE_DISC: "PERCENTILE_DISC" >
    | < PLACING: "PLACING" >
    | < PLAN: "PLAN" >
    | < PLI: "PLI" >
    | < POSITION: "POSITION" >
    | < POWER: "POWER" >
    | < PRECEDING: "PRECEDING" >
    | < PRECISION: "PRECISION" >
    | < PREPARE: "PREPARE" >
    | < PRESERVE: "PRESERVE" >
    | < PRIMARY: "PRIMARY" >
    | < PRIOR: "PRIOR" >
    | < PRIVILEGES: "PRIVILEGES" >
    | < PROCEDURE: "PROCEDURE" >
    | < PUBLIC: "PUBLIC" >
    | < 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" >
    | < RELATIVE: "RELATIVE" >
    | < RELEASE: "RELEASE" >
    | < REPEATABLE: "REPEATABLE" >
    | < RESTART: "RESTART" >
    | < RESTRICT: "RESTRICT" >
    | < RESULT: "RESULT" >
    | < RETURN: "RETURN" >
    | < RETURNED_CARDINALITY: "RETURNED_CARDINALITY" >
    | < RETURNED_LENGTH: "RETURNED_LENGTH" >
    | < RETURNED_OCTET_LENGTH: "RETURNED_OCTET_LENGTH" >
    | < RETURNED_SQLSTATE: "RETURNED_SQLSTATE" >
    | < RETURNS: "RETURNS" >
    | < REVOKE: "REVOKE" >
    | < RIGHT: "RIGHT" >
    | < ROLE: "ROLE" >
    | < ROLLBACK: "ROLLBACK" >
    | < ROLLUP: "ROLLUP" >
    | < ROUTINE: "ROUTINE" >
    | < ROUTINE_CATALOG: "ROUTINE_CATALOG" >
    | < ROUTINE_NAME: "ROUTINE_NAME" >
    | < ROUTINE_SCHEMA: "ROUTINE_SCHEMA" >
    | < ROW: "ROW" >
    | < ROW_COUNT: "ROW_COUNT" >
    | < ROW_NUMBER: "ROW_NUMBER" >
    | < ROWS: "ROWS" >
    | < SAVEPOINT: "SAVEPOINT" >
    | < SCALE: "SCALE" >
    | < SCHEMA: "SCHEMA" >
    | < SCHEMA_NAME: "SCHEMA_NAME" >
    | < SCOPE: "SCOPE" >
    | < SCOPE_CATALOGS: "SCOPE_CATALOGS" >
    | < SCOPE_NAME: "SCOPE_NAME" >
    | < SCOPE_SCHEMA: "SCOPE_SCHEMA" >
    | < SCROLL: "SCROLL" >
    | < SEARCH: "SEARCH" >
    | < SECOND: "SECOND" >
    | < SECTION: "SECTION" >
    | < SECURITY: "SECURITY" >
    | < SELECT: "SELECT" >
    | < SELF: "SELF" >
    | < SENSITIVE: "SENSITIVE" >
    | < SEQUENCE: "SEQUENCE" >
    | < SERIALIZABLE: "SERIALIZABLE" >
    | < SERVER: "SERVER" >
    | < SERVER_NAME: "SERVER_NAME" >
    | < SESSION: "SESSION" >
    | < SESSION_USER: "SESSION_USER" >
    | < SET: "SET" >
    | < SETS: "SETS" >
    | < SIMILAR: "SIMILAR" >
    | < SIMPLE: "SIMPLE" >
    | < SIZE: "SIZE" >
    | < SMALLINT: "SMALLINT" >
    | < SOME: "SOME" >
    | < SOURCE: "SOURCE" >
    | < SPACE: "SPACE" >
    | < SPECIFIC: "SPECIFIC" >
    | < SPECIFIC_NAME: "SPECIFIC_NAME" >
    | < SPECIFICTYPE: "SPECIFICTYPE" >
    | < SQL: "SQL" >
    | < SQLEXCEPTION: "SQLEXCEPTION" >
    | < SQLSTATE: "SQLSTATE" >
    | < SQLWARNING: "SQLWARNING" >
    | < SQRT: "SQRT" >
    | < START: "START" >
    | < STATE: "STATE" >
    | < STATEMENT: "STATEMENT" >
    | < STATIC: "STATIC" >
    | < STDDEV_POP: "STDDEV_POP" >
    | < STDDEV_SAMP: "STDDEV_SAMP" >
    | < STRUCTURE: "STRUCTURE" >
    | < STYLE: "STYLE" >
    | < SUBCLASS_ORIGIN: "SUBCLASS_ORIGIN" >
    | < SUBMULTISET: "SUBMULTISET" >
    | < SUBSTRING: "SUBSTRING" >
    | < SUBSTITUTE: "SUBSTITUTE" >
    | < SUM: "SUM" >
    | < SYMMETRIC: "SYMMETRIC" >
    | < SYSTEM: "SYSTEM" >
    | < SYSTEM_USER: "SYSTEM_USER" >
    | < TABLE: "TABLE" >
    | < TABLE_NAME: "TABLE_NAME" >
    | < TABLESAMPLE: "TABLESAMPLE" >
    | < TEMPORARY: "TEMPORARY" >
    | < THEN: "THEN" >
    | < TIES: "TIES" >
    | < TIME: "TIME" >
    | < TIMESTAMP: "TIMESTAMP" >
    | < TIMEZONE_HOUR: "TIMEZONE_HOUR" >
    | < TIMEZONE_MINUTE: "TIMEZONE_MINUTE" >
    | < TINYINT: "TINYINT" >
    | < TO: "TO" >
    | < TOP_LEVEL_COUNT: "TOP_LEVEL_COUNT" >
    | < TRAILING: "TRAILING" >
    | < TRANSACTION: "TRANSACTION" >
    | < TRANSACTIONS_ACTIVE: "TRANSACTIONS_ACTIVE" >
    | < TRANSACTIONS_COMMITTED: "TRANSACTIONS_COMMITTED" >
    | < TRANSACTIONS_ROLLED_BACK: "TRANSACTIONS_ROLLED_BACK" >
    | < TRANSFORM: "TRANSFORM" >
    | < TRANSFORMS: "TRANSFORMS" >
    | < TRANSLATE: "TRANSLATE" >
    | < TRANSLATION: "TRANSLATION" >
    | < TREAT: "TREAT" >
    | < TRIGGER: "TRIGGER" >
    | < TRIGGER_CATALOG: "TRIGGER_CATALOG" >
    | < TRIGGER_NAME: "TRIGGER_NAME" >
    | < TRIGGER_SCHEMA: "TRIGGER_SCHEMA" >
    | < TRIM: "TRIM" >
    | < TRUE: "TRUE" >
    | < TYPE: "TYPE" >
    | < UESCAPE: "UESCAPE" >
    | < UNBOUNDED: "UNBOUNDED" >
    | < UNCOMMITTED: "UNCOMMITTED" >
    | < UNDER: "UNDER" >
    | < UNION: "UNION" >
    | < UNIQUE: "UNIQUE" >
    | < UNKNOWN: "UNKNOWN" >
    | < UNNAMED: "UNNAMED" >
    | < UNNEST: "UNNEST" >
    | < UPDATE: "UPDATE" >
    | < UPPER: "UPPER" >
    | < USAGE: "USAGE" >
    | < USER: "USER" >
    | < USER_DEFINED_TYPE_CATALOG: "USER_DEFINED_TYPE_CATALOG" >
    | < USER_DEFINED_TYPE_CODE: "USER_DEFINED_TYPE_CODE" >
    | < USER_DEFINED_TYPE_NAME: "USER_DEFINED_TYPE_NAME" >
    | < USER_DEFINED_TYPE_SCHEMA: "USER_DEFINED_TYPE_SCHEMA" >
    | < USING: "USING" >
    | < VALUE: "VALUE" >
    | < VALUES: "VALUES" >
    | < VAR_POP: "VAR_POP" >
    | < VAR_SAMP: "VAR_SAMP" >
    | < VARCHAR: "VARCHAR" >
    | < VARBINARY: "VARBINARY" >
    | < VARYING: "VARYING" >
    | < VERSION: "VERSION" >
    | < VIEW: "VIEW" >
    | < WHEN: "WHEN" >
    | < WHENEVER: "WHENEVER" >
    | < WHERE: "WHERE" >
    | < WIDTH_BUCKET: "WIDTH_BUCKET" >
    | < WINDOW: "WINDOW" >
    | < WITH: "WITH" >
    | < WITHIN: "WITHIN" >
    | < WITHOUT: "WITHOUT" >
    | < WORK: "WORK" >
    | < WRAPPER: "WRAPPER" >
    | < WRITE: "WRITE" >
    | < XML: "XML" >
    | < YEAR: "YEAR" >
    | < ZONE: "ZONE" >
}

/*
 * Abstract production:
 *
 *   String NonReservedKeyWord()
 *
 * Parses non-reserved keywords (e.g. keywords that may be used as
 * identifiers).  Should use CommonNonReservedKeyWord as a base, but
 * may add other key words.
 */

/**
 * Parses a non-reserved keyword for use as an identifier.  Specializations
 * of this parser can use this as a base for implementing the
 * NonReservedKeyWord() production.
 *
 *<p>
 *
 * When adding keywords to this list, be sure that they are not reserved
 * by the SQL:2003 standard (see productions for "non-reserved word"
 * and "reserved word" in reference below).
 *
 * @sql.2003 Part 2 Section 5.2
 */
String CommonNonReservedKeyWord() :
{
}
{
    (
        <A>
        | <ABSOLUTE>
        | <ACTION>
        | <ADA>
        | <ADD>
        | <ADMIN>
        | <AFTER>
        | <ALWAYS>
        | <ASC>
        | <ASSERTION>
        | <ASSIGNMENT>
        | <ATTRIBUTE>
        | <ATTRIBUTES>
        | <BEFORE>
        | <BERNOULLI>
        | <BREADTH>
        | <C>
        | <CASCADE>
        | <CATALOG>
        | <CATALOG_NAME>
        | <CHAIN>
        | <CHARACTER_SET_CATALOG>
        | <CHARACTER_SET_NAME>
        | <CHARACTER_SET_SCHEMA>
        | <CHARACTERISTICS>
        | <CHARACTERS>
        | <CLASS_ORIGIN>
        | <COBOL>
        | <COLLATION>
        | <COLLATION_CATALOG>
        | <COLLATION_NAME>
        | <COLLATION_SCHEMA>
        | <COLUMN_NAME>
        | <COMMAND_FUNCTION>
        | <COMMAND_FUNCTION_CODE>
        | <COMMITTED>
        | <CONDITION_NUMBER>
        | <CONNECTION>
        | <CONNECTION_NAME>
        | <CONSTRAINT_CATALOG>
        | <CONSTRAINT_NAME>
        | <CONSTRAINT_SCHEMA>
        | <CONSTRAINTS>
        | <CONSTRUCTOR>
        | <CONTAINS>
        | <CONTINUE>
        | <CURSOR_NAME>
        | <DATA>
        | <DATETIME_INTERVAL_CODE>
        | <DATETIME_INTERVAL_PRECISION>
        | <DEFAULTS>
        | <DEFERRABLE>
        | <DEFERRED>
        | <DEFINED>
        | <DEFINER>
        | <DEGREE>
        | <DEPTH>
        | <DERIVED>
        | <DESC>
        | <DESCRIPTION>
        | <DESCRIPTOR>
        | <DIAGNOSTICS>
        | <DISPATCH>
        | <DOMAIN>
        | <DYNAMIC_FUNCTION>
        | <DYNAMIC_FUNCTION_CODE>
        | <EQUALS>
        | <EXCEPTION>
        | <EXCLUDE>
        | <EXCLUDING>
        | <FINAL>
        | <FIRST>
        | <FOLLOWING>
        | <FORTRAN>
        | <FOUND>
        | <G>
        | <GENERAL>
        | <GENERATED>
        | <GO>
        | <GOTO>
        | <GRANTED>
        | <HIERARCHY>
        | <IMMEDIATE>
        | <IMPLEMENTATION>
        | <INCLUDING>
        | <INCREMENT>
        | <INITIALLY>
        | <INPUT>
        | <INSTANCE>
        | <INSTANTIABLE>
        | <INVOKER>
        | <ISOLATION>
        | <JAVA>
        | <K>
        | <KEY>
        | <KEY_MEMBER>
        | <KEY_TYPE>
        | <LABEL>
        | <LAST>
        | <LENGTH>
        | <LEVEL>
        | <LIBRARY>
        | <LOCATOR>
        | <M>
        | <MAP>
        | <MATCHED>
        | <MAXVALUE>
        | <MESSAGE_LENGTH>
        | <MESSAGE_OCTET_LENGTH>
        | <MESSAGE_TEXT>
        | <MINVALUE>
        | <MORE_KW>
        | <MUMPS>
        | <NAME>
        | <NAMES>
        | <NESTING>
        | <NEXT>
        | <NORMALIZED>
        | <NULLABLE>
        | <NULLS>
        | <NUMBER>
        | <OBJECT>
        | <OCTETS>
        | <OPTION>
        | <OPTIONS>
        | <ORDERING>
        | <ORDINALITY>
        | <OTHERS>
        | <OUTPUT>
        | <OVERRIDING>
        | <PAD>
        | <PARAMETER_MODE>
        | <PARAMETER_NAME>
        | <PARAMETER_ORDINAL_POSITION>
        | <PARAMETER_SPECIFIC_CATALOG>
        | <PARAMETER_SPECIFIC_NAME>
        | <PARAMETER_SPECIFIC_SCHEMA>
        | <PARTIAL>
        | <PASCAL>
        | <PATH>
        | <PLACING>
        | <PLAN>
        | <PLI>
        | <PRECEDING>
        | <PRESERVE>
        | <PRIOR>
        | <PRIVILEGES>
        | <PUBLIC>
        | <READ>
        | <RELATIVE>
        | <REPEATABLE>
        | <RESTART>
        | <RESTRICT>
        | <RETURNED_CARDINALITY>
        | <RETURNED_LENGTH>
        | <RETURNED_OCTET_LENGTH>
        | <RETURNED_SQLSTATE>
        | <ROLE>
        | <ROUTINE>
        | <ROUTINE_CATALOG>
        | <ROUTINE_NAME>
        | <ROUTINE_SCHEMA>
        | <ROW_COUNT>
        | <SCALE>
        | <SCHEMA>
        | <SCHEMA_NAME>
        | <SCOPE_CATALOGS>
        | <SCOPE_NAME>
        | <SCOPE_SCHEMA>
        | <SECTION>
        | <SECURITY>
        | <SELF>
        | <SEQUENCE>
        | <SERIALIZABLE>
        | <SERVER>
        | <SERVER_NAME>
        | <SESSION>
        | <SETS>
        | <SIMPLE>
        | <SIZE>
        | <SOURCE>
        | <SPACE>
        | <SPECIFIC_NAME>
        | <STATE>
        | <STATEMENT>
        | <STRUCTURE>
        | <STYLE>
        | <SUBCLASS_ORIGIN>
        | <SUBSTITUTE>
        | <TABLE_NAME>
        | <TEMPORARY>
        | <TIES>
        | <TOP_LEVEL_COUNT>
        | <TRANSACTION>
        | <TRANSACTIONS_ACTIVE>
        | <TRANSACTIONS_COMMITTED>
        | <TRANSACTIONS_ROLLED_BACK>
        | <TRANSFORM>
        | <TRANSFORMS>
        | <TRIGGER_CATALOG>
        | <TRIGGER_NAME>
        | <TRIGGER_SCHEMA>
        | <TYPE>
        | <UNBOUNDED>
        | <UNCOMMITTED>
        | <UNDER>
        | <UNNAMED>
        | <USAGE>
        | <USER_DEFINED_TYPE_CATALOG>
        | <USER_DEFINED_TYPE_CODE>
        | <USER_DEFINED_TYPE_NAME>
        | <USER_DEFINED_TYPE_SCHEMA>
        | <VERSION>
        | <VIEW>
        | <WRAPPER>
        | <WORK>
        | <WRITE>
        | <XML>
        | <ZONE>
    )
    {
        return getToken(0).image.toUpperCase();
    }
}

/* LITERALS */

TOKEN :
{
    < UNSIGNED_INTEGER_LITERAL: (["0"-"9"])+ >
    |
    < APPROX_NUMERIC_LITERAL:
    (<UNSIGNED_INTEGER_LITERAL> | <DECIMAL_NUMERIC_LITERAL>) <EXPONENT> >
    |
    < DECIMAL_NUMERIC_LITERAL:
    (["0"-"9"])+(".")?(["0"-"9"])*
    | "."(["0"-"9"])+
    >
    |
    < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
    |
    < #HEXDIGIT: ["0"-"9","a"-"f","A"-"F"] >
    |
    < #WHITESPACE:
    [ " ","\t","\n","\r","\f" ]
    >
    |
    /* To improve error reporting, we allow all kinds of characters,
     * not just hexits, in a binary string literal. */
    < BINARY_STRING_LITERAL: ["x","X"] <QUOTE> ( (~["'"]) | ("''"))* <QUOTE> >
    |
    < QUOTED_STRING: <QUOTE> ( (~["'"]) | ("''"))* <QUOTE> >
    |
    < PREFIXED_STRING_LITERAL: ("_" <CHARSETNAME> | "N") <QUOTED_STRING> >
    |
    < UNICODE_STRING_LITERAL: "U" "&" <QUOTED_STRING> >
    |
    < #CHARSETNAME: (["a"-"z","A"-"Z","0"-"9"])
    (["a"-"z","A"-"Z","0"-"9",":",".","-","_"])*
    >
}

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

/* SEPARATORS */

TOKEN :
{
    < LPAREN: "(">
    | < RPAREN: ")">
    | < LBRACE_D: "{" (" ")* ["d","D"] >
    | < LBRACE_T: "{" (" ")* ["t","T"] >
    | < LBRACE_TS: "{" (" ")* ["t","T"] ["s","S"] >
    | < LBRACE_FN: "{" (" ")* ["f","F"] ["n","N"] >
    | < LBRACE: "{" >
    | < RBRACE: "}" >
    | < LBRACKET: "[" >
    | < RBRACKET: "]" >
    | < SEMICOLON: ";" >
    | < DOT: "." >
    | < COMMA: "," >
}

/* OPERATORS */

TOKEN :
{
    < EQ: "=" >
    | < GT: ">" >
    | < LT: "<" >
    | < HOOK: "?" >
    | < COLON: ":" >
    | < LE: "<=" >
    | < GE: ">=" >
    | < NE: "<>" >
    | < PLUS: "+" >
    | < MINUS: "-" >
    | < STAR: "*" >
    | < SLASH: "/" >
    | < CONCAT: "||" >
    | < DOUBLE_PERIOD: ".." >
    | < QUOTE: "'" >
}


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

/* WHITE SPACE */

SKIP :
{
    " "
    | "\t"
    | "\n"
    | "\r"
    | "\f"
}

/* COMMENTS */

MORE :
{
    <"/**" ~["/"]> : IN_FORMAL_COMMENT
}

MORE :
{
    "//" : IN_SINGLE_LINE_COMMENT
    |
    "--" : IN_SINGLE_LINE_COMMENT
    |
    "/*" : IN_MULTI_LINE_COMMENT
}

<IN_SINGLE_LINE_COMMENT>
SPECIAL_TOKEN :
{
    <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > : DEFAULT
}

<IN_FORMAL_COMMENT>
SPECIAL_TOKEN :
{
    <FORMAL_COMMENT: "*/" > : DEFAULT
}

<IN_MULTI_LINE_COMMENT>
SPECIAL_TOKEN :
{
    <MULTI_LINE_COMMENT: "*/" > : DEFAULT
}

<IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
MORE :
{
    < ~[] >
}


/* IDENTIFIERS */

TOKEN :
{
    < COLLATION_ID:
    (<LETTER>|<DIGIT>)+ (<LETTER>|<DIGIT>|":"|"."|"-"|"_")*
    "$"
    (<LETTER>|"_")+
    ("$" (<LETTER>|<DIGIT>|"_")+)?
    >
    |
    < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
    |
    < QUOTED_IDENTIFIER:
    "\""
    (   (~["\"","\n","\r"])
        | ("\"\"")
        )*
    "\""
    >
    |
    < 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. */
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>
    )
}
