/*
// $Id: //open/dev/farrago/src/net/sf/farrago/parser/CommonDdlParser.jj#74 $
// Farrago is an extensible data management system.
// Copyright (C) 2005-2009 The Eigenbase Project
// Copyright (C) 2004-2009 SQLstream, Inc.
// Copyright (C) 2005-2009 LucidEra, Inc.
// Portions Copyright (C) 2003-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
*/

// NOTE jvs 1-Feb-2005:  convenience functions belong in
// FarragoAbstractParserImpl, not here, unless they depend on
// information which is only available in the generated parser class
// (e.g. token).

/*****************************************
 * Convenience Functions                 *
 *****************************************/

JAVACODE SqlParserPos getEndPos()
{
    return new SqlParserPos(
        token.beginLine,
        token.beginColumn + token.image.length());
}

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


SqlIdentifier CompoundIdentifier2() :
{
    List list = new ArrayList();
    String p;
    SqlParserPos pos;
}
{
    p = Identifier()
    {
        list.add(p);
        pos = getPos();
    }
    [
        <DOT> p = Identifier()
        {
            list.add(p);
            pos = pos.plus(getPos());
        }
    ]
    {
        return new SqlIdentifier(SqlParserUtil.toStringArray(list), pos);
    }
}

SqlIdentifier CompoundIdentifier3() :
{
    List list = new ArrayList();
    String p;
    SqlParserPos pos;
}
{
    p = Identifier()
    {
        list.add(p);
        pos = getPos();
    }
    [
        <DOT> p = Identifier()
        {
            list.add(p);
            pos = pos.plus(getPos());
        }
        [
            <DOT> p = Identifier()
            {
                list.add(p);
                pos = pos.plus(getPos());
            }
        ]
    ]
    {
        return new SqlIdentifier(SqlParserUtil.toStringArray(list), pos);
    }
}

String QuotedString() :
{
    Token t;
}
{
    t = <QUOTED_STRING>
    {
        return SqlParserUtil.parseString(t.image);
    }
}

/**
 * Parses an SQL statement followed by the end-of-file symbol.
 *
 * @return top-level CWM object affected for DDL statement, or top-level SqlNode
 * for DML or query statement
 */
Object FarragoSqlStmtEof() :
{
    Object obj;
}
{
    (
        obj = DdlStmtEof()
        |
        (
            /*
             * Insure that a read-only repository transaction is started.
             * Note that view expansion can cause a second call into the
             * parser, which requires us to avoid attempting to start
             * the read txn twice.
             */
            {
                FarragoReposTxnContext reposTxnContext =
                    farragoParser.getStmtValidator().getReposTxnContext();

                if (!reposTxnContext.isReadTxnInProgress()) {
                    reposTxnContext.beginLockedTxn(true);
                }
            }
            obj = SqlStmtEof()
            |
            obj = PsmBody()
        )
    )
    <EOF>
    {
        return obj;
    }
}

DdlStmt DdlStmtEof() :
{
    CwmModelElement modelElement;
    DdlStmt ddlStmt;
    SqlNode expr;
    DdlReplaceOptions replaceOptions = new DdlReplaceOptions();
    SqlIdentifier newName;
    FarragoReposTxnContext reposTxnContext =
        farragoParser.getStmtValidator().getReposTxnContext();
}
{
    (
        (
            <CREATE>
            {
                reposTxnContext.beginLockedTxn(false);
            }
            [
                ( <OR> <REPLACE> )
                {
                    replaceOptions.setIsReplace(true);
                }
                [ ( <RENAME> <TO> newName = SimpleIdentifier() )
                    {
                        replaceOptions.setNewName(newName);
                    }
                ]
            ]
            (
                modelElement = SchemaDefinition()
                |
                LOOKAHEAD(2)
                modelElement = DataWrapperDefinition()
                |
                modelElement = SchemaObjectDefinition()
                |
                modelElement = IndexDefinition(null)
                |
                modelElement = DataServerDefinition()
                |
                modelElement = UserDefinition()
                |
                modelElement = RoleDefinition()
                |
                modelElement = ExtensionModelDefinition()
                |
                modelElement = LabelDefinition()
            )
            {
                ddlStmt = newDdlCreateStmt(modelElement, replaceOptions);
            }
        )
        |
        (
            <DROP>
            {
                reposTxnContext.beginLockedTxn(false);

                // assume RESTRICT; CascadeOption() may override
                dropRestrict = true;
            }
            (
                (
                    modelElement = SchemaDrop()
                    |
                    modelElement = TableDrop()
                    |
                    modelElement = IndexDrop()
                    |
                    modelElement = ViewDrop()
                    |
                    modelElement = RoutineDrop()
                    |
                    modelElement = UserDefinedTypeDrop()
                    |
                    modelElement = UserDefinedOrderingDrop()
                    |
                    modelElement = JarDrop()
                    |
                    modelElement = DataServerDrop()
                    |
                    LOOKAHEAD(2)
                    modelElement = DataWrapperDrop()
                    |
                    LOOKAHEAD(2)
                    modelElement = ForeignTableDrop()
                    |
                    modelElement = UserDrop()
                    |
                    modelElement = RoleDrop()
                    |
                    modelElement = ExtensionModelDrop()
                )
                {
                    ddlStmt = newDdlDropStmt(modelElement,dropRestrict);
                }
                |
                modelElement = LabelDrop()
                {
                    ddlStmt = newDdlDropLabelStmt(modelElement,dropRestrict);
                }
            )
        )
        |
        (
            <TRUNCATE>
            {
                reposTxnContext.beginLockedTxn(true);
            }
            (
                <TABLE> modelElement = TableIdentifier()
            )
            {
                ddlStmt = new DdlTruncateStmt(modelElement);
            }
        )
        |
        (
            <SET>
            {
                reposTxnContext.beginLockedTxn(false);
            }
            (
                <CATALOG> expr = Expression(ExprContext.ACCEPT_NONQUERY)
                {
                    ddlStmt = new DdlSetCatalogStmt(expr);
                }
                | <SCHEMA> expr = Expression(ExprContext.ACCEPT_NONQUERY)
                {
                    ddlStmt = new DdlSetSchemaStmt(expr);
                }
                | <PATH> expr = Expression(ExprContext.ACCEPT_NONQUERY)
                {
                    ddlStmt = new DdlSetPathStmt(expr);
                }
            )
        )
        |
        <ALTER>
        (
            <SYSTEM>
            {
                reposTxnContext.beginLockedTxn(false);
            }
            (
                <SET> ddlStmt = SystemParamAssignment()
                |
                <ADD> ddlStmt = CatalogExtension()
                |
                <REPLACE> ddlStmt = CatalogReplace()
                |
                <DEALLOCATE> ddlStmt = DeallocateOld()
            )
            |
            <SESSION>
            {
                reposTxnContext.beginLockedTxn(false);
            }
            (
                <IMPLEMENTATION> ddlStmt = SessionImplementation()
                |
                <SET> ddlStmt = SessionParamAssignment()
            )
            |
            <TABLE>
            {
                reposTxnContext.beginLockedTxn(false);
                ddlStmt = AlterTableStmt();
            }
            |
            {
                reposTxnContext.beginLockedTxn(false);
                ddlStmt = ExtensionModelAlter();
            }
        )
        |
        <GRANT>
        {
            reposTxnContext.beginLockedTxn(false);
        }
        (
            ddlStmt = GrantRoleStmt()
            |
            ddlStmt = GrantPrivStmt()
        )
        |
        <CHECKPOINT>
        {
            reposTxnContext.beginLockedTxn(false);

            ddlStmt = new DdlCheckpointStmt();
        }
        |
        <COMMIT>
        {
            reposTxnContext.beginLockedTxn(false);

            ddlStmt = new DdlCommitStmt();
        }
        |
        <ROLLBACK>
        {
            reposTxnContext.beginLockedTxn(false);
        }
        ddlStmt = Rollback()
        |
        <SAVEPOINT>
        {
            reposTxnContext.beginLockedTxn(false);
        }
        ddlStmt = Savepoint()
        |
        <RELEASE> <SAVEPOINT>
        {
            reposTxnContext.beginLockedTxn(false);
        }
        ddlStmt = ReleaseSavepoint()
        |
        <IMPORT>
        {
            reposTxnContext.beginLockedTxn(false);
        }
        ddlStmt = ImportForeignSchemaStmt()
        |
        <ANALYZE>
        {
            // During the initial prepare of an analyze statement, we only
            // need to acquire shared locks since we're only reading from the
            // catalogs.  During catalog update, we'll start a new write xact
            reposTxnContext.beginLockedTxn(true);
        }
        ddlStmt = AnalyzeStmt()
    )
    <EOF>
    {
        return ddlStmt;
    }
}

// TODO:  support non-topological order of object definition, e.g. a
// referencing table before a referenced table

FemLocalSchema SchemaDefinition() :
{
    FemLocalSchema schema;
    SqlIdentifier schemaName, userName;
    boolean pathDefined = false;
    SqlNode description;
}
{
    <SCHEMA> schemaName = CompoundIdentifier2()
    {
        schema = getRepos().newFemLocalSchema();
        if (schemaName.names.length == 2) {
            CwmCatalog catalog =
            farragoParser.getStmtValidator().findCatalog(
                schemaName.names[0]);
            schema.setNamespace(catalog);
            schema.setName(schemaName.names[1]);
        } else {
            schema.setNamespace(
                farragoParser.getStmtValidator().getDefaultCatalog());
            schema.setName(schemaName.getSimple());
        }
    }
    [ <AUTHORIZATION> userName = SimpleIdentifier() ]
    {
        // TODO:  store userName
    }
    [ SchemaPath(schema) { pathDefined = true; } ]
    {
        if (!pathDefined) {
            // implicit default path is just this schema itself
            FemSqlpathElement element = getRepos().newFemSqlpathElement();
            element.setSearchedSchemaCatalogName(
                schema.getNamespace().getName());
            element.setSearchedSchemaName(
                schema.getName());
            schema.getPathElement().add(element);
        }

        // set up context which will prevail for any object
        // definitions included in this schema definition
        farragoParser.getDdlValidator().setCreatedSchemaContext(schema);
    }
    OptionalDescription(schema)
    (
        <CREATE>
        SchemaObjectDefinition()
    ) *
    {
        // TODO: character set, all that jazz
        return schema;
    }
}

CwmModelElement SchemaObjectDefinition() :
{
    CwmModelElement modelElement;
}
{
    (
        modelElement = TableDefinition()
        |
        modelElement = ViewDefinition()
        |
        LOOKAHEAD(2)
        modelElement = ForeignTableDefinition()
        |
        modelElement = FunctionDefinition()
        |
        modelElement = ProcedureDefinition()
        |
        modelElement = ConstructorDefinition()
        |
        modelElement = JarDefinition()
        |
        modelElement = UserDefinedTypeDefinition()
        |
        modelElement = UserDefinedOrderingDefinition()
        |
        modelElement = ExtensionModelSchemaObjDefinition()
    )
    {
        return modelElement;
    }
}

void SchemaPath(FemLocalSchema schema) :
{
    List searchPath;
}
{
    searchPath = SearchPath()
    {
        Iterator iter = searchPath.iterator();
        while (iter.hasNext()) {
            CwmSchema searchedSchema = (CwmSchema) iter.next();
            if (!searchedSchema.getNamespace().equals(schema.getNamespace())) {
                throw FarragoResource.instance().ValidatorPathBadCatalog.ex(
                    getRepos().getLocalizedObjectName(
                        searchedSchema.getNamespace()),
                    getRepos().getLocalizedObjectName(
                        schema.getNamespace()));
            }
            FemSqlpathElement element = getRepos().newFemSqlpathElement();
            element.setSearchedSchemaCatalogName(
                searchedSchema.getNamespace().getName());
            element.setSearchedSchemaName(
                searchedSchema.getName());
            schema.getPathElement().add(element);
        }
    }
}

List SearchPath() :
{
    List list = new ArrayList();
}
{
    <PATH> SearchPathElement(list)
    (
        <COMMA> SearchPathElement(list)
    ) *
    {
        return list;
    }
}

void SearchPathElement(List list) :
{
    SqlIdentifier id;
}
{
    id = CompoundIdentifier2()
    {
        FemLocalSchema searchedSchema =
        farragoParser.getStmtValidator().findSchema(id);
        list.add(searchedSchema);
    }
}

// TODO:  support non-topological order of table element definition, e.g. a
// referencing constraint definition before a referenced column definition

CwmTable TableDefinition() :
{
    FemStoredTable table;
    FemDataServer server = null;
}
{
    (
        table = UnscopedTableDefinition()
        |
        table = ScopedTableDefinition()
    )
    [ server = DataServerReference() ]
    OptionalStorageOptions(table)
    ( LOOKAHEAD(2) <CREATE> IndexDefinition(table) ) *
    {
        if (server == null) {
            server =
            farragoParser.getStmtValidator().getDefaultLocalDataServer();
        }
        table.setServer(server);
        return table;
    }
}

FemLocalTable UnscopedTableDefinition() :
{
    FemLocalTable table;
    SqlIdentifier qualifiedTableName;
}
{
    <TABLE>
    {
        table = getRepos().newFemLocalTable();
    }
    qualifiedTableName = CompoundIdentifier3()
    {
        farragoParser.getDdlValidator().setSchemaObjectName(
            table,qualifiedTableName);
        table.setModality(ModalityTypeEnum.MODALITYTYPE_RELATIONAL);
    }
    <LPAREN> TableElementList(table) <RPAREN>
    OptionalSampleList(table, false)
    OptionalDescription(table)
    {
        return table;
    }
}

FemLocalTable ScopedTableDefinition() :
{
    FemLocalTable table;
    String scope;
    String commitOption;
}
{
    <GLOBAL> <TEMPORARY>
    table = UnscopedTableDefinition()
    {
        table.setTemporary(true);

        // TODO:  support LOCAL (as "MODULE")
        scope = "SESSION";
        commitOption = "DELETE";
    }
    [
        <ON> <COMMIT>
        (
            <PRESERVE>
            {
                commitOption = "PRESERVE";
            }
            |
            <DELETE>
            {
                commitOption = "DELETE";
            }
        )
        <ROWS>
    ]
    {
        // TODO:  add commitOption to FemLocalTable, since CWM doesn't
        // have a place for it?
        table.setTemporaryScope(scope + ":COMMIT=" + commitOption);
        return table;
    }
}

FemLocalView ViewDefinition() :
{
    FemLocalView view;
    SqlIdentifier qualifiedViewName;
    SqlNode query, description;
    List columnNameList;
    SqlParserPos defStart, defEnd;
}
{
    <VIEW>
    {
        view = getRepos().newFemLocalView();
    }
    qualifiedViewName = CompoundIdentifier3()
    {
        farragoParser.getDdlValidator().setSchemaObjectName(
            view,qualifiedViewName);
        view.setModality(ModalityTypeEnum.MODALITYTYPE_RELATIONAL);
    }
    [
        <LPAREN> columnNameList = SimpleIdentifierCommaList()
        {
            Iterator columnNameIter = columnNameList.iterator();
            while (columnNameIter.hasNext()) {
                String columnName =
                ((SqlIdentifier) columnNameIter.next()).getSimple();
                CwmColumn column = getRepos().newFemViewColumn();
                column.setName(columnName);
                view.getFeature().add(column);
            }
        }
        <RPAREN>
    ]
    OptionalDescription(view)
    <AS>
    {
        defStart = getEndPos();
    }
    query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
    {
        defEnd = getEndPos();

        // NOTE:  we accept ORDER BY on the view definition, even though
        // that's not standard SQL, and leave it up to the validator to
        // decide what to do with it

        String bodyText = farragoParser.getSubstring(defStart, defEnd);
        CwmQueryExpression queryExpr = getRepos().newCwmQueryExpression();
        queryExpr.setLanguage("SQL");
        bodyText =
            farragoParser.getDdlValidator().setParserOffset(
                view, defStart, bodyText);
        queryExpr.setBody(bodyText);
        view.setQueryExpression(queryExpr);
        return view;
    }
}


FemLocalIndex IndexDefinition(CwmTable table) :
{
    FemLocalIndex index;
    SqlIdentifier indexName;
    CwmTable specifiedTable;
    List columnNameList;
    boolean clustered = false;
}
{
    [ <CLUSTERED> { clustered = true; } ]
    <INDEX>
    {
        index = getRepos().newFemLocalIndex();
    }
    indexName = SimpleIdentifier()
    {
        index.setName(indexName.getSimple());
    }
    <ON> specifiedTable = TableIdentifier()
    {
        if (table == null) {
            table = specifiedTable;
        } else {
            // TODO:  move rule down into validator
            if (!table.equals(specifiedTable)) {
                throw farragoParser.getDdlValidator().newPositionalError(
                    index,
                    FarragoResource.instance().ValidatorIndexBadTable.ex());
            }
        }
        index.setSpannedClass(table);
        if (clustered) {
            index.setClustered(true);
        }
        index.setSorted(true);
        index.setUnique(false);
        index.setNamespace(table.getNamespace());
    }
    <LPAREN> columnNameList = SimpleIdentifierCommaList() <RPAREN>
    {
        int iOrdinal = 0;
        Iterator columnNameIter = columnNameList.iterator();
        while (columnNameIter.hasNext()) {
            String columnName =
            ((SqlIdentifier) columnNameIter.next()).getSimple();
            CwmColumn column = farragoParser.getStmtValidator().findColumn(
                table,columnName);
            FemLocalIndexColumn indexColumn =
            getRepos().newFemLocalIndexColumn();
            indexColumn.setName(columnName);
            indexColumn.setAscending(Boolean.TRUE);
            indexColumn.setFeature(column);
            indexColumn.setIndex(index);
            indexColumn.setOrdinal(iOrdinal++);
        }
        return index;
    }
}

FemUser UserDefinition() :
{
    FemUser user;
    SqlIdentifier userName;
    SqlNode expr;
    CwmNamespace defaultNamespace;
}
{
    <USER>
    {
        user = getRepos().newFemUser();
    }
    userName = SimpleIdentifier()
    {
        // set the repos name attribute
        user.setName(userName.getSimple());
    }
    <AUTHORIZATION> expr = Expression(ExprContext.ACCEPT_NONQUERY)
    {
        // TODO: Finalize the syntax for JAAS integration
        // user.setAuthenticationMethod =
        // TODO: validate and save the authentication method
    }
    [<DEFAULT_KW>
        (
            defaultNamespace = CatalogReference()
            |
            defaultNamespace = SchemaReference()
        )
        {
            // Set the default namespace for the user. The session default will
            // be this name space when the user logs on
            user.setDefaultNamespace(defaultNamespace);
        }

    ]
    {
        return user;
    }
}

FemRole RoleDefinition() :
{
    FemRole role;
    SqlIdentifier roleName;
    SqlIdentifier grantor;
}
{
    <ROLE>
    {
        role = getRepos().newFemRole();
    }
    roleName = SimpleIdentifier()
    {
        // set the repos name attribute
        role.setName(roleName.getSimple());
    }
    [ <WITH> <ADMIN> grantor = SimpleIdentifier() {
            // Create a grant to represent an inheritant of this role and make the
            // grantor specified in this statement as the grantee of that grant.
            FemGrant grant = FarragoCatalogUtil.newElementGrant(
                getRepos(),
                farragoParser.getStmtValidator().getSession()
                .getSessionVariables().currentUserName,
                grantor.getSimple(),
                role);
            // set grant properties to reflect inheritant with admin capability
            grant.setAction(PrivilegedActionEnum.INHERIT_ROLE.toString());
            grant.setWithGrantOption(true);
        }
    ]
    {
        return role;
    }
}

FemLabel LabelDefinition() :
{
    FemLabel label;
    SqlIdentifier labelName;
    SqlIdentifier parentLabel;
}
{
    <LABEL> labelName = SimpleIdentifier()
    {
        label = getRepos().newFemLabel();
        label.setName(labelName.getSimple());
        label.setNamespace(
            getRepos().getCatalog(
                FarragoCatalogInit.SYSBOOT_CATALOG_NAME));
    }
    [
        <FROM> <LABEL> parentLabel = SimpleIdentifier()
        {
            label.setParentLabel(
                farragoParser.getStmtValidator().findUnqualifiedObject(
                    parentLabel, FemLabel.class));
        }
    ]
    OptionalDescription(label)
    {
        return label;
    }
}

CwmTable TableIdentifier() :
{
    SqlIdentifier qualifiedTableName;
    CwmTable table;
}
{
    qualifiedTableName = CompoundIdentifier3()
    {
        table = farragoParser.getStmtValidator().findSchemaObject(
            qualifiedTableName, CwmTable.class);
        return table;
    }
}

CwmColumn ColumnIdentifier(CwmNamedColumnSet columnSet) :
{
    SqlIdentifier columnName;
    CwmColumn column;
}
{
    columnName = SimpleIdentifier()
    {
        column = (CwmColumn)
        farragoParser.getStmtValidator().findColumn(
            columnSet,
            columnName.toString());
        return column;
    }
}

CwmNamedColumnSet ColumnSetIdentifier() :
{
    SqlIdentifier qualifiedTableName;
    CwmNamedColumnSet table;
}
{
    qualifiedTableName = CompoundIdentifier3()
    {
        table = farragoParser.getStmtValidator().findSchemaObject(
            qualifiedTableName, CwmNamedColumnSet.class);
        return table;
    }
}

FemLocalView ViewReference() :
{
    SqlIdentifier qualifiedViewName;
    FemLocalView view;
}
{
    <VIEW> qualifiedViewName = CompoundIdentifier3()
    {
        view = farragoParser.getStmtValidator().findSchemaObject(
            qualifiedViewName, FemLocalView.class);
        return view;
    }
}

FemRoutine RoutineReference() :
{
    boolean specific = false;
    FemRoutine routine;
}
{
    [ <SPECIFIC> { specific = true; } ]
    routine = RoutineReferenceImpl(specific)
    {
        return routine;
    }
}

FemRoutine SpecificRoutineReference() :
{
    FemRoutine routine;
}
{
    <SPECIFIC> routine = RoutineReferenceImpl(true)
    {
        return routine;
    }
}

FemRoutine RoutineReferenceImpl(boolean specific) :
{
    SqlIdentifier qualifiedRoutineName;
    FemRoutine routine;
    ProcedureType routineType;
}
{
    (
        <ROUTINE>
        {
            routineType = null;
        }
        | <FUNCTION>
        {
            routineType = ProcedureTypeEnum.FUNCTION;
        }
        | <PROCEDURE>
        {
            routineType = ProcedureTypeEnum.PROCEDURE;
        }
    )
    qualifiedRoutineName = CompoundIdentifier3()
    {
        // TODO jvs 24-Dec-2004: implement lookup by invocation name
        // and signature when !specific
        routine = farragoParser.getStmtValidator().findSchemaObject(
            qualifiedRoutineName, FemRoutine.class);

        // REVIEW jvs 29-Dec-2004:  localization in message below
        // is iffy

        // if they specifically asked for a FUNCTION or PROCEDURE,
        // and the object found does not match, pretend we
        // found nothing
        if ((routineType != null) && (routineType != routine.getType())) {
            throw farragoParser.newPositionalError(
                FarragoResource.instance().ValidatorUnknownObject.ex(
                    routineType.toString() + " " +
                    getRepos().getLocalizedObjectName(
                        routine.getNamespace().getName(),
                        routine.getName(),
                        null)));
        }
        return routine;
    }
}

void TableElementList(CwmTable table) :
{
}
{
    TableElement(table)
    (
        <COMMA> TableElement(table)
    ) *
}



CwmModelElement TableElement(CwmTable table) :
{
    CwmModelElement modelElement;
}
{
    (
        modelElement = ColumnDefinition(table)
        |
        modelElement = TableConstraint(table)
    )
    {
        return modelElement;
    }
}

void BasicColumnDefinition(CwmColumnSet table,FemAbstractColumn column) :
{
    SqlIdentifier columnName;
}
{
    columnName = SimpleIdentifier()
    {
        table.getFeature().add(column);
        column.setName(columnName.getSimple());
    }
    TypedElement(column)
}

void TypedElement(FemAbstractTypedElement element) :
{
    SqlDataTypeSpec dataType;
}
{
    dataType = DataType()
    {
        if (dataType.getCollectionsTypeName() != null) {
            FemSqlmultisetType colType = getRepos().newFemSqlmultisetType();
            colType.setName("SYS$MULTISET_" + colType.refMofId());
            FemSqltypeAttribute componentType =
            getRepos().newFemSqltypeAttribute();
            componentType.setName("COMPONENT");
            colType.getFeature().add(componentType);
            farragoParser.getDdlValidator().setSqlDefinition(
                componentType,
                dataType.getComponentTypeSpec());
            FemSqltypedElement typedElement =
            FarragoCatalogUtil.toFemSqltypedElement(element);
            typedElement.setType(colType);
            // REVIEW jvs 19-July-2005:  This won't work once we
            // have DROP COLUMN.  Need to either make the column
            // a Namespace of its own, or create a trigger to
            // make the lifetime of the constructed type match
            // that of the column.
            CwmNamespace namespace = (CwmNamespace)
                element.refImmediateComposite();
            namespace.getOwnedElement().add(colType);
            return;
        }

        farragoParser.getDdlValidator().setSqlDefinition(
            element,
            dataType);
    }
}

CwmColumn ColumnDefinition(CwmTable table) :
{
    FemStoredColumn column;
    SqlNode defaultClause;
    FarragoSequenceOptions opts;
    FemSequenceGenerator sequence;
    SqlCollation sqlCollation;
}
{
    {
        column = getRepos().newFemStoredColumn();
    }
    BasicColumnDefinition(table,column)
    [
        defaultClause = DefaultClause()
        {
            setDefaultExpression(column, defaultClause);
        }
        |
        opts = SequenceOptions(column.getName())
        {
            SqlDataTypeSpec dataType = (SqlDataTypeSpec)
                farragoParser.getDdlValidator().getSqlDefinition(column);
            RelDataType relType = dataType.deriveType(
                farragoParser.getDdlValidator().getTypeFactory());

            sequence = getRepos().newFemSequenceGenerator();
            sequence.setName("");
            opts.init(sequence, relType);
            column.setGeneratedAlways(opts.getGeneratedAlways());
            column.setSequence(sequence);
        }
    ]
    ( ColumnConstraint(table,column) ) *
    [ sqlCollation = CollateClause()
        {
            column.setCollationName(sqlCollation.getCollationName());
        }
    ]
    OptionalStorageOptions(column)
    {
        return column;
    }
}

SqlNode DefaultClause() :
{
    SqlNode expression;
}
{
    <DEFAULT_KW>
    (
        // TODO:  allow USER, CURRENT_USER, CURRENT_ROLE, SESSION_USER,
        // SYSTEM_USER, CURRENT_PATH, CURRENT_CATALOG, CURRENT_SCHEMA,
        // <datetime value function>,
        // <implicitly typed value specification>
        expression = Literal()
    )
    {
        return expression;
    }
}

FarragoSequenceOptions SequenceOptions(String name) :
{
    FarragoSequenceOptions opts;
}
{
    <GENERATED>
    {
        opts = new FarragoSequenceOptions(name);
    }
    (
        <ALWAYS>
        {
            opts.setGeneratedAlways(true);
        }
        |
        <BY> <DEFAULT_KW>
        {
            opts.setGeneratedAlways(false);
        }
    )
    <AS> <IDENTITY>
    [ <LPAREN> ( CommonSequenceOption(opts) )* <RPAREN> ]
    {
        return opts;
    }
}

void CommonSequenceOption(FarragoSequenceOptions opts) :
{
    SqlLiteral value;
}
{
    <START> <WITH> value = NumericLiteral()
    {
        opts.setStart(value.longValue(true));
    }
    |
    BasicSequenceOption(opts)
}

void BasicSequenceOption(FarragoSequenceOptions opts) :
{
    SqlLiteral value;
}
{
    <INCREMENT> <BY> value = NumericLiteral()
    {
        opts.setIncrement(value.longValue(true));
    }
    |
    <MINVALUE> value = NumericLiteral()
    {
        opts.setMin(value.longValue(true));
    }
    |
    LOOKAHEAD(2)
    <NO> <MINVALUE>
    {
        opts.setMin(null);
    }
    |
    <MAXVALUE> value = NumericLiteral()
    {
        opts.setMax(value.longValue(true));
    }
    |
    LOOKAHEAD(2)
    <NO> <MAXVALUE>
    {
        opts.setMax(null);
    }
    |
    <CYCLE>
    {
        opts.setCycle(true);
    }
    |
    LOOKAHEAD(2)
    <NO> <CYCLE>
    {
        opts.setCycle(false);
    }
}

CwmModelElement TableConstraint(CwmTable table) :
{
    String constraintName = null;
    FemAbstractUniqueConstraint constraint;
}
{
    [ <CONSTRAINT> constraintName = Identifier() ]
    constraint = TableUniqueConstraint(table)
    {
        // NOTE jvs 6-Nov-2006:  We store contraints as owned
        // by tables because CWM doesn't give us a good way
        // to associate them directly.
        table.getOwnedElement().add(constraint);
        if (constraintName != null) {
            constraint.setName(constraintName);
        } else {
            FarragoCatalogUtil.generateConstraintName(
                getRepos(), constraint);
        }
        return constraint;
    }
}

FemAbstractUniqueConstraint TableUniqueConstraint(CwmTable table) :
{
    FemAbstractUniqueConstraint constraint;
    List columnNameList;
}
{
    constraint = UniqueConstraint()
    {
    }
    <LPAREN> columnNameList = SimpleIdentifierCommaList() <RPAREN>
    {
        Iterator columnNameIter = columnNameList.iterator();
        while (columnNameIter.hasNext()) {
            String columnName =
            ((SqlIdentifier) columnNameIter.next()).getSimple();
            CwmColumn column = farragoParser.getStmtValidator().findColumn(
                table,columnName);
            constraint.getFeature().add(column);
            // TODO:  validate no duplicates
        }
        // TODO:  deferrability, etc.
        return constraint;
    }
}

FemAbstractUniqueConstraint UniqueConstraint() :
{
    FemAbstractUniqueConstraint constraint;
}
{
    (
        <UNIQUE>
        {
            constraint = getRepos().newFemUniqueKeyConstraint();
        }
        |
        <PRIMARY> <KEY>
        {
            constraint = getRepos().newFemPrimaryKeyConstraint();
        }
    )
    {
        constraint.setDeferrability(DeferrabilityTypeEnum.NOT_DEFERRABLE);
        return constraint;
    }
}



void ColumnConstraint(CwmNamedColumnSet table,CwmColumn column) :
{
    FemAbstractUniqueConstraint constraint;
    String constraintName = null;
}
{
    [ <CONSTRAINT> constraintName = Identifier() ]
    (
        <NOT> <NULL>
        {
            // TODO jvs 7-Nov-2006:  Model explicit named constraint
            // object for NOT NULL constraints.
            column.setIsNullable(NullableTypeEnum.COLUMN_NO_NULLS);
        }
        | constraint = UniqueConstraint()
        {
            constraint.getFeature().add(column);
            table.getOwnedElement().add(constraint);
            if (constraintName != null) {
                constraint.setName(constraintName);
            } else {
                FarragoCatalogUtil.generateConstraintName(
                    getRepos(), constraint);
            }
        }
    )
}

CwmModelElement SchemaDrop() :
{
    SqlIdentifier schemaName;
    FemLocalSchema schema;
}
{
    schema = SchemaReference()
    CascadeOption()
    {
        return schema;
    }
}

CwmCatalog CatalogReference() :
{
    SqlIdentifier catalogName;
}
{
    <CATALOG> catalogName = SimpleIdentifier()
    {
        return farragoParser.getStmtValidator().findCatalog(
            catalogName.getSimple());
    }
}

FemLocalSchema SchemaReference() :
{
    SqlIdentifier schemaName;
}
{
    <SCHEMA> schemaName = CompoundIdentifier2()
    {
        return farragoParser.getStmtValidator().findSchema(schemaName);
    }
}

CwmModelElement TableDrop() :
{
    CwmModelElement table;
}
{
    <TABLE> table = TableIdentifier()
    CascadeOption()
    {
        return table;
    }
}

CwmModelElement ViewDrop() :
{
    CwmModelElement view;
}
{
    view = ViewReference()
    CascadeOption()
    {
        return view;
    }
}

CwmModelElement RoutineDrop() :
{
    CwmModelElement routine;
}
{
    routine = RoutineReference()
    CascadeOption()
    {
        return routine;
    }
}

CwmModelElement UserDefinedTypeDrop() :
{
    SqlIdentifier typeName;
    CwmModelElement typeDef;
}
{
    <TYPE> typeName = CompoundIdentifier3()
    {
        typeDef = farragoParser.getStmtValidator().findSchemaObject(
            typeName, FemUserDefinedType.class);
    }
    CascadeOption()
    {
        return typeDef;
    }
}

CwmModelElement UserDefinedOrderingDrop() :
{
    SqlIdentifier typeName;
    CwmModelElement orderingDef;
}
{
    <ORDERING> <FOR> typeName = CompoundIdentifier3()
    {
        FemUserDefinedType typeDef =
            farragoParser.getStmtValidator().findSchemaObject(
                typeName, FemUserDefinedType.class);
        if (typeDef.getOrdering().isEmpty()) {
            throw FarragoResource.instance().ValidatorNoOrdering.ex(
                getRepos().getLocalizedObjectName(
                    typeDef));
        }
        orderingDef = (CwmModelElement)
        typeDef.getOrdering().iterator().next();
    }
    CascadeOption()
    {
        return orderingDef;
    }
}

CwmModelElement JarDrop() :
{
    SqlIdentifier qualifiedJarName;
    SqlLiteral deploymentState;
}
{
    <JAR> qualifiedJarName = CompoundIdentifier3()
    <OPTIONS>
    <LPAREN> deploymentState = NumericLiteral() <RPAREN>
    CascadeOption()
    {
        FemJar jar = farragoParser.getStmtValidator().findSchemaObject(
            qualifiedJarName, FemJar.class);
        jar.setDeploymentState(deploymentState.intValue(true));
        return jar;
    }
}

CwmModelElement IndexDrop() :
{
    SqlIdentifier qualifiedIndexName;
}
{
    <INDEX> qualifiedIndexName = CompoundIdentifier3()
    {
        return farragoParser.getStmtValidator().findSchemaObject(
            qualifiedIndexName, FemLocalIndex.class);
    }
}

void CascadeOption() :
{
}
{
    [
        (
            <RESTRICT>
            {
                dropRestrict = true;
            }
            | <CASCADE>
            {
                dropRestrict = false;
            }
        )
    ]
}

DdlStmt SystemParamAssignment() :
{
    SqlIdentifier paramName;
    SqlLiteral paramValue;
}
{
    paramName = SimpleIdentifier()
    <EQ>
    paramValue = SystemParamValue()
    {
        return new DdlSetSystemParamStmt(
            paramName.getSimple(),paramValue);
    }
}

DdlStmt SessionParamAssignment() :
{
    SqlIdentifier paramName;
    SqlLiteral paramValue;
}
{
    paramName = SimpleIdentifier()
    <EQ>
    paramValue = SystemParamValue()
    {
        return new DdlSetSessionParamStmt(
            paramName.getSimple(),paramValue);
    }
}

DdlStmt CatalogExtension() :
{
    SqlIdentifier jarName;
}
{
    <CATALOG> <JAR> jarName = CompoundIdentifier3()
    {
        return new DdlExtendCatalogStmt(jarName);
    }
}

DdlStmt CatalogReplace() :
{
}
{
    <CATALOG>
    {
        return new DdlReplaceCatalogStmt();
    }
}

DdlStmt DeallocateOld() :
{
}
{
    <OLD>
    {
        return new DdlDeallocateOldStmt();
    }
}

DdlStmt SessionImplementation() :
{
    SqlIdentifier jarName = null;
}
{
    <SET>
    (
        <JAR> jarName = CompoundIdentifier3()
        | <DEFAULT_KW>
    )
    {
        return new DdlSetSessionImplementationStmt(jarName, false);
    }
    |
    <ADD> <JAR> jarName = CompoundIdentifier3()
    {
        return new DdlSetSessionImplementationStmt(jarName, true);
    }
}

DdlStmt AlterTableStmt() :
{
    DdlStmt ddlStmt;
    CwmTable table;
}
{
    table = TableIdentifier()
    (
        <ADD> [ <COLUMN> ] ddlStmt = AddColumnStmt(table)
        {
            return ddlStmt;
        }
        |
        <ALTER> <COLUMN> ddlStmt = AlterColumnStmt(table)
        {
            return ddlStmt;
        }
        |
        <REBUILD>
        {
            return new DdlRebuildTableStmt(table);
        }
    )
}

DdlStmt AddColumnStmt(CwmTable table) :
{
    DdlStmt ddlStmt;
    CwmColumn column;
}
{
    // NOTE jvs 4-Dec-2008:  It's important that we construct
    // ddlStmt BEFORE parsing the actual column definition.
    // The reason is that the constructor for
    // DdlAlterTableStructureStmt needs to make a copy
    // of the unmodified table structure.
    {
        FarragoSession session =
            farragoParser.getDdlValidator().getInvokingSession();
        ddlStmt = new DdlAlterTableStructureStmt(
            getRepos(), table);
    }
    column = ColumnDefinition(table)
    {
        boolean constraints = false;
        UniqueFeature uf =
            getRepos().getKeysIndexesPackage().getUniqueFeature();
        if (!uf.getUniqueKey(column).isEmpty()) {
            constraints = true;
        }
        if (constraints) {
            // TODO jvs 4-Dec-2008:  This is supposed to work; we just
            // haven't added the necessary support yet.
            throw Util.needToImplement(
                "Adding keys via ALTER TABLE is not yet supported");
        }
        return ddlStmt;
    }
}

DdlStmt AlterColumnStmt(CwmTable table) :
{
    DdlStmt ddlStmt;
    CwmColumn column;
}
{
    column = ColumnIdentifier(table)
    ddlStmt = AlterIdentityColumnStmt(column)
    {
        return ddlStmt;
    }
}

DdlStmt AlterIdentityColumnStmt(CwmColumn column) :
{
    DdlStmt ddlStmt;
    FarragoSequenceOptions opts;
    SqlLiteral value;
}
{
    {
        opts = new FarragoSequenceOptions(column.getName());
    }
    (
        <RESTART> <WITH> value = NumericLiteral()
        {
            opts.setStart(value.longValue(true));
        }
        |
        <SET> BasicSequenceOption(opts)
    )+
    {
        ddlStmt = new DdlAlterIdentityColumnStmt(column, opts);
        return ddlStmt;
    }
}

SqlLiteral SystemParamValue() :
{
    SqlLiteral paramValue;
    SqlParserPos pos = null;
}
{
    (
        paramValue = StrictLiteral()
        | <MAX>
        {
            // TODO jvs 22-May-2004:  for MIN and MAX here, use metadata
            // to determine the correct min and max values
            pos = getPos();
            paramValue = SqlLiteral.createExactNumeric("-1", pos);
        }
        | <MIN>
        {
            pos = getPos();
            paramValue = SqlLiteral.createExactNumeric("0", pos);
        }
    )
    {
        return paramValue;
    }
}

DdlStmt Rollback() :
{
    SqlIdentifier savepointName = null;
}
{
    [
        <TO> <SAVEPOINT> savepointName = SimpleIdentifier()
    ]
    {
        if (savepointName != null) {
            return new DdlRollbackStmt(savepointName.getSimple());
        } else {
            return new DdlRollbackStmt(null);
        }
    }
}

DdlStmt Savepoint() :
{
    SqlIdentifier savepointName = null;
}
{
    savepointName = SimpleIdentifier()
    {
        return new DdlSavepointStmt(savepointName.getSimple());
    }
}

DdlStmt ReleaseSavepoint() :
{
    SqlIdentifier savepointName = null;
}
{
    savepointName = SimpleIdentifier()
    {
        return new DdlReleaseSavepointStmt(savepointName.getSimple());
    }
}

FemDataWrapper DataWrapperDefinition() :
{
    FemDataWrapper wrapper;
    SqlIdentifier wrapperName;
    String libraryFile;
    boolean isForeign;
}
{
    isForeign = ForeignOpt()
    <DATA> <WRAPPER> wrapperName = SimpleIdentifier()
    {
        wrapper = getRepos().newFemDataWrapper();
        wrapper.setName(wrapperName.getSimple());
        wrapper.setNamespace(
            getRepos().getCatalog(
                FarragoCatalogInit.SYSBOOT_CATALOG_NAME));
        wrapper.setForeign(isForeign);
    }
    <LIBRARY> libraryFile = QuotedString()
    {
        wrapper.setLibraryFile(libraryFile);
    }
    <LANGUAGE> <JAVA>
    {
        wrapper.setLanguage("JAVA");
    }
    OptionalStorageOptions(wrapper)
    OptionalDescription(wrapper)
    {
        return wrapper;
    }
}

boolean ForeignOpt() :
{
}
{
    <LOCAL>
    {
        return false;
    }
    | <FOREIGN>
    {
        return true;
    }
}

FemDataWrapper DataWrapperReference() :
{
    SqlIdentifier name;
    boolean isForeign;
}
{
    isForeign = ForeignOpt()
    <DATA> <WRAPPER> name = SimpleIdentifier()
    {
        return farragoParser.getStmtValidator().findDataWrapper(
            name,isForeign);
    }
}

FemDataServer DataServerDefinition() :
{
    FemDataServer server;
    FemDataWrapper wrapper;
    SqlIdentifier serverName;
    String serverType;
    String serverVersion;
}
{
    <SERVER> serverName = SimpleIdentifier()
    {
        server = getRepos().newFemDataServer();
        server.setName(serverName.getSimple());
        server.setNamespace(
            getRepos().getCatalog(
                FarragoCatalogInit.SYSBOOT_CATALOG_NAME));
    }
    [
        <TYPE> serverType = QuotedString()
        {
            server.setType(serverType);
        }
    ]
    [
        <VERSION> serverVersion = QuotedString()
        {
            server.setVersion(serverVersion);
        }
    ]
    wrapper = DataWrapperReference()
    {
        server.setWrapper(wrapper);
    }
    OptionalStorageOptions(server)
    OptionalDescription(server)
    {
        return server;
    }
}

FemRoutine FunctionDefinition() :
{
    FemRoutine function;
    SqlIdentifier qualifiedName;
}
{
    <FUNCTION>
    {
        function = getRepos().newFemRoutine();
        function.setType(ProcedureTypeEnum.FUNCTION);
        function.setCalledOnNullInput(true);
    }
    qualifiedName = CompoundIdentifier3()
    {
        // REVIEW jvs 27-Dec-2004:  SQL standard says that
        // specific name is implementation-defined when unspecified.
        // The behavior here is to use the invocation name, which
        // means if there's a conflict with an existing specific name,
        // the CREATE statement will fail.  This forces users to
        // provide specific names when overloads are defined.  An
        // alternative is to generate unique specific names automatically
        // in case of conflict.
        farragoParser.getDdlValidator().setSchemaObjectName(
            function,qualifiedName);
        function.setInvocationName(function.getName());
    }
    <LPAREN> [ RoutineParamList(function) ] <RPAREN>
    ReturnsClause(function)
    RoutineCharacteristics(function)
    [ <STATIC> <DISPATCH> ]
    {
        function.setStaticDispatch(true);
    }
    (
        SqlFunctionBody(function)
        | ExternalRoutineBody(function)
    )
    {
        FarragoCatalogUtil.setRoutineSpecification(
            getRepos(),
            function,
            null);
        return function;
    }
}

FemRoutine ConstructorDefinition() :
{
    FemRoutine method;
    SqlIdentifier constructorName;
    SqlIdentifier typeName;
    // TODO jvs 26-Feb-2005:  support silly RETURNS clause and
    // method reference by invocation name and parameters
    SqlParserPos defStart, defEnd;
}
{
    <SPECIFIC> <METHOD> constructorName = CompoundIdentifier3()
    {
        method = farragoParser.getStmtValidator().findSchemaObject(
            constructorName, FemRoutine.class);
    }
    <FOR> typeName = CompoundIdentifier3()
    {
        // TODO jvs 26-Feb-2005:  cross-check
    }
    RightsClause(method)
    {
        defStart = getEndPos();
    }
    PsmBody()
    {
        defEnd = getEndPos();
        String bodyText = farragoParser.getSubstring(defStart, defEnd);
        CwmProcedureExpression procedureExpr = method.getBody();
        procedureExpr.setLanguage("SQL");
        bodyText =
            farragoParser.getDdlValidator().setParserOffset(
                method, defStart, bodyText);
        procedureExpr.setBody(bodyText);
        // NOTE: this is necessary to force a revalidation of the method
        method.setBody(procedureExpr);
    }
    {
        return method;
    }
}

SqlNode PsmBody() :
{
    // TODO jvs 26-Feb-2005:  once we implement SQL/PSM, this should
    // accept any statement list; right now this is all special-cased
    // for UDT constructors
    SqlNodeList list = new SqlNodeList(getPos());
    SqlNode stmt;
}
{
    <BEGIN> (
        stmt = ConstructorAssignment()
        {
            list.add(stmt);
        }
    ) *
    <RETURN> <SELF> <SEMICOLON> <END>
    {
        return list;
    }
}

SqlNode ConstructorAssignment() :
{
    SqlIdentifier attributeName;
    SqlNode expr;
}
{
    <SET> <SELF> <DOT> attributeName = SimpleIdentifier()
    <EQ> expr = Expression(ExprContext.ACCEPT_NONQUERY) <SEMICOLON>
    {
        return opTab.equalsOperator.createCall(
            getPos(),
            attributeName,
            expr);
    }
}

void ReturnsClause(FemRoutine function) :
{
    FemRoutineParameter returnParam = getRepos().newFemRoutineParameter();
    returnParam.setName("RETURN");
    returnParam.setKind(ParameterDirectionKindEnum.PDK_RETURN);
    function.getParameter().add(returnParam);
}
{
    <RETURNS>
    (
        <TABLE>
        <LPAREN>
        TableFunctionColumnListElement(function)
        (<COMMA> TableFunctionColumnListElement(function))*
        <RPAREN>
        {
            // TODO jvs 8-Jan-2006:  should be MULTISET of ROW(x, y, z)
            returnParam.setType(
                farragoParser.getStmtValidator().findSqldataType(
                    new SqlIdentifier(
                        SqlTypeName.INTEGER.name(),
                        SqlParserPos.ZERO)));
        }
        |
        TypedElement(returnParam)
        {
        }
    )
    {
    }
}

void TableFunctionColumnListElement(FemRoutine function) :
{
    FemViewColumn column = getRepos().newFemViewColumn();
    SqlIdentifier inputCursorName;
}
{
    LOOKAHEAD(2) BasicColumnDefinition(function, column)
    {
    }
    |
    inputCursorName = SimpleIdentifier() <DOT> <STAR>
    {
        farragoParser.getDdlValidator().setSqlDefinition(
            column,
            new SqlIdentifier("CURSOR", getPos()));
        function.getFeature().add(column);
        column.setName(inputCursorName.getSimple());
    }
}

FemRoutine ProcedureDefinition() :
{
    FemRoutine procedure;
    SqlIdentifier qualifiedName;
}
{
    <PROCEDURE>
    {
        procedure = getRepos().newFemRoutine();
        procedure.setCalledOnNullInput(true);
    }
    qualifiedName = CompoundIdentifier3()
    {
        // REVIEW jvs 18-Jan-2005:  see corresponding comment
        // in FunctionDefinition().
        farragoParser.getDdlValidator().setSchemaObjectName(
            procedure,qualifiedName);
        procedure.setInvocationName(procedure.getName());
    }
    <LPAREN> [ RoutineParamList(procedure) ] <RPAREN>
    RoutineCharacteristics(procedure)
    {
        // TODO jvs 18-Jan-2005:  disallow characteristics which
        // don't apply to procedures
    }
    (
        ExternalRoutineBody(procedure)
    )
    {
        FarragoCatalogUtil.setRoutineSpecification(
            getRepos(),
            procedure,
            null);
        procedure.setType(ProcedureTypeEnum.PROCEDURE);
        return procedure;
    }
}

void SqlFunctionBody(FemRoutine routine) :
{
    SqlNode expr;
    SqlParserPos defStart, defEnd;
}
{
    RightsClause(routine)
    <RETURN>
    {
        defStart = getEndPos();

        // TODO jvs 24-Dec-2004: eventually allow subqueries and
        // non-functional routine bodies.
    }
    expr = Expression(ExprContext.ACCEPT_NONQUERY)
    {
        defEnd = getEndPos();

        String bodyText = farragoParser.getSubstring(defStart, defEnd);
        CwmProcedureExpression procedureExpr =
        getRepos().newCwmProcedureExpression();
        procedureExpr.setLanguage("SQL");
        bodyText =
            farragoParser.getDdlValidator().setParserOffset(
                routine, defStart, bodyText);
        procedureExpr.setBody(
            FarragoUserDefinedRoutine.addReturnPrefix(bodyText));
        routine.setBody(procedureExpr);
    }
}

void RightsClause(FemRoutine routine) :
{
}
{
    [
        <SQL> <SECURITY>
        (
            <INVOKER>
            {
                routine.setImpersonateDefiner(false);
            }
            | <DEFINER>
            {
                routine.setImpersonateDefiner(true);
            }
        )
    ]
}

void ExternalRoutineBody(FemRoutine routine) :
{
    String name;
}
{
    <EXTERNAL> <NAME>
    (
        name = Identifier()
        | name = QuotedString()
    )
    {
        routine.setExternalName(name);
    }
    [ RoutineParameterStyle(routine) ]
    [
        <EXTERNAL> <SECURITY> (
            <DEFINER>
            {
                routine.setImpersonateDefiner(true);
            }
            | <INVOKER>
            {
                routine.setImpersonateDefiner(false);
            }
            | <IMPLEMENTATION> <DEFINED>
            {
                routine.setImpersonateDefiner(true);
            }
        )
    ]
}

void RoutineParameterStyle(FemRoutine routine) :
{
}
{
    <PARAMETER> <STYLE>
    (
        <SQL>
        {
            routine.setParameterStyle(
                RoutineParameterStyleEnum.RPS_SQL.toString());
        }
        | <GENERAL>
        {
            routine.setParameterStyle(
                RoutineParameterStyleEnum.RPS_GENERAL.toString());
        }
        | <JAVA>
        {
            routine.setParameterStyle(
                RoutineParameterStyleEnum.RPS_JAVA.toString());
        }
        | <SYSTEM> <DEFINED> <JAVA>
        {
            routine.setParameterStyle(
                RoutineParameterStyleEnum.RPS_JAVA_FARRAGO.toString());
        }
    )
}

void RoutineCharacteristics(FemRoutine routine) :
{
    SqlIdentifier specificName;
}
{
// TODO jvs 24-Dec-2004:  prevent conflicting declarations
    (
        (
            <LANGUAGE>
            (
                <JAVA>
                {
                    routine.setLanguage("JAVA");
                }
                | <SQL>
                {
                    routine.setLanguage("SQL");
                }
            )
        )
        | RoutineParameterStyle(routine)
        | (<SPECIFIC> specificName = CompoundIdentifier2())
        {
            if (specificName.names.length > 1) {
                assert(specificName.names.length == 2);
                if (!specificName.names[0].equals(
                        routine.getNamespace().getName()))
                {
                    throw farragoParser.getDdlValidator().newPositionalError(
                        routine,
                        FarragoResource.instance().
                            ValidatorRoutineSchemaMismatch.ex(
                                getRepos().getLocalizedObjectName(
                                    specificName.names[0]),
                                getRepos().getLocalizedObjectName(
                                    routine.getNamespace())));
                }
                routine.setName(specificName.names[1]);
            } else {
                routine.setName(specificName.getSimple());
            }
        }
        | <DETERMINISTIC>
        {
            routine.setDeterministic(true);
        }
        | <DYNAMIC_FUNCTION>
        {
            routine.setDynamicFunction(true);
        }
        | <NOT>
        (
            <DETERMINISTIC>
            {
                routine.setDeterministic(false);
            }
            | <DYNAMIC_FUNCTION>
            {
                routine.setDynamicFunction(false);
            }
        )
        | <NO> <SQL>
        {
            routine.setDataAccess(RoutineDataAccessEnum.RDA_NO_SQL);
        }
        | <CONTAINS> <SQL>
        {
            routine.setDataAccess(RoutineDataAccessEnum.RDA_CONTAINS_SQL);
        }
        | <READS> <SQL> <DATA>
        {
            routine.setDataAccess(RoutineDataAccessEnum.RDA_READS_SQL_DATA);
        }
        | <MODIFIES> <SQL> <DATA>
        {
            routine.setDataAccess(RoutineDataAccessEnum.RDA_MODIFIES_SQL_DATA);
        }
        | <RETURNS> <NULL> <ON> <NULL> <INPUT>
        {
            routine.setCalledOnNullInput(false);
        }
        | <CALLED> <ON> <NULL> <INPUT>
        {
            routine.setCalledOnNullInput(true);
        }
        // TODO jvs 24-Dec-2004:  add characteristics for dynamic result sets
        // and savepoint level
    ) *
}

void RoutineParamList(FemRoutine routine) :
{
}
{
    RoutineParam(routine)
    (
        <COMMA>
        RoutineParam(routine)
    ) *
}

FemRoutineParameter RoutineParam(FemRoutine routine) :
{
    FemRoutineParameter param;
    SqlIdentifier name;
    SqlIdentifier cursorName;
    FemColumnListRoutineParameter columnListParam;
    ParameterDirectionKind kind = null;
}
{
    [
        (
            <IN>
            {
                kind = ParameterDirectionKindEnum.PDK_IN;
            }
            | <OUT>
            {
                kind = ParameterDirectionKindEnum.PDK_OUT;
            }
            | <INOUT>
            {
                kind = ParameterDirectionKindEnum.PDK_INOUT;
            }
        )
    ]
    name = SimpleIdentifier()
    (
        param = NonColumnListRoutineParam(kind, name)
        |
        <SELECT> <FROM> cursorName = SimpleIdentifier()
        {
            columnListParam = getRepos().newFemColumnListRoutineParameter();
            columnListParam.setKind(kind);
            columnListParam.setName(name.getSimple());
            columnListParam.setSourceCursorName(cursorName.getSimple());
            farragoParser.getDdlValidator().setSqlDefinition(
                columnListParam,
                new SqlIdentifier("COLUMN_LIST", getPos()));
            param = columnListParam;
        }
    )
    {
        routine.getParameter().add(param);
        return param;
    }
}

FemRoutineParameter NonColumnListRoutineParam(
    ParameterDirectionKind kind,
    SqlIdentifier name) :
{
    FemRoutineParameter param;

    param = getRepos().newFemRoutineParameter();
    param.setKind(kind);
    param.setName(name.getSimple());
}
{
    (
        TypedElement(param)
        |
        <CURSOR>
        {
            farragoParser.getDdlValidator().setSqlDefinition(
                param,
                new SqlIdentifier("CURSOR", getPos()));
        }
    )
    {
        return param;
    }
}

FemJar JarDefinition() :
{
    FemJar jar;
    SqlIdentifier qualifiedName;
    String url;
    SqlLiteral deploymentState;
}
{
    <JAR>
    {
        jar = getRepos().newFemJar();
    }
    qualifiedName = CompoundIdentifier3()
    {
        farragoParser.getDdlValidator().setSchemaObjectName(
            jar,qualifiedName);
    }
    <LIBRARY> url = QuotedString()
    {
        jar.setUrl(url);
    }
    <OPTIONS> <LPAREN>
    deploymentState = NumericLiteral() <RPAREN>
    {
        jar.setDeploymentState(deploymentState.intValue(true));
        return jar;
    }
}

FemUserDefinedType UserDefinedTypeDefinition() :
{
    SqlIdentifier qualifiedName;
    FemUserDefinedType typeDef;
}
{
    // TODO jvs 1-Feb-2005:  UNDER, AS-less, methods,
    // references, cast defs
    <TYPE> qualifiedName = CompoundIdentifier3()
    <AS>
    (
        typeDef = ObjectTypeDefinition()
        | typeDef = DistinctTypeDefinition()
    )
    {
        farragoParser.getDdlValidator().setSchemaObjectName(
            typeDef, qualifiedName);
    }
    UserDefinedTypeOptions(typeDef)
    OptionalDescription(typeDef)
    MethodSpecificationList(typeDef)
    {
        return typeDef;
    }
}

FemUserDefinedOrdering UserDefinedOrderingDefinition() :
{
    FemUserDefinedOrdering orderingDef;
    FemUserDefinedType typeDef;
    SqlIdentifier typeName;
}
{
    <ORDERING> <FOR> typeName = CompoundIdentifier3()
    {
        typeDef = farragoParser.getStmtValidator().findSchemaObject(
            typeName, FemUserDefinedType.class);
        orderingDef = getRepos().newFemUserDefinedOrdering();
        typeDef.getOrdering().add(orderingDef);
    }
    (
        <EQUALS> <ONLY> <BY> OrderingCategory(orderingDef)
        {
            orderingDef.setFull(false);
        }
        | <ORDER> <FULL> <BY> OrderingCategory(orderingDef)
        {
            orderingDef.setFull(true);
        }
    )
    {
        return orderingDef;
    }
}

void OrderingCategory(FemUserDefinedOrdering orderingDef) :
{
    FemRoutine routine = null;
    SqlIdentifier specificName = null;
}
{
    (
        <RELATIVE> <WITH> routine = SpecificRoutineReference()
        {
            orderingDef.setCategory(
                UserDefinedOrderingCategoryEnum.UDOC_RELATIVE);
        }
        | <MAP> <WITH> routine = SpecificRoutineReference()
        {
            orderingDef.setCategory(
                UserDefinedOrderingCategoryEnum.UDOC_MAP);
        }
        | <STATE> [ specificName = CompoundIdentifier3() ]
        {
            // REVIEW jvs 17-Mar-2005:  do we care about the specific name?
            // The standard in this area seems very pointless.  For now,
            // ignore it and generate a name during validation.
            orderingDef.setCategory(
                UserDefinedOrderingCategoryEnum.UDOC_STATE);
        }
    )
    {
        if (routine != null) {
            // NOTE jvs 22-Mar-2005:  This dependency is actually the only
            // link between the ordering and the routine.  There is
            // no explicit association.
            farragoParser.getDdlValidator().createDependency(
                orderingDef,
                Collections.singleton(routine));
        }
    }
}

void MethodSpecificationList(FemUserDefinedType typeDef) :
{
}
{
    [
        MethodSpecification(typeDef)
        (
            <COMMA> MethodSpecification(typeDef)
        ) *
    ]
}

void MethodSpecification(FemUserDefinedType typeDef) :
{
    SqlIdentifier name, returnTypeName;
    SqlIdentifier qualifiedName;
    FemRoutine method;
}
{
    // TODO jvs 25-Feb-2005:  non-constructor methods
    <CONSTRUCTOR> <METHOD>
    name = SimpleIdentifier()
    {
        method = getRepos().newFemRoutine();
        method.setType(ProcedureTypeEnum.FUNCTION);
        qualifiedName = new SqlIdentifier(
            new String [] {
                typeDef.getNamespace().getName(),
                name.getSimple()
            },
            name.getParserPosition());
        farragoParser.getDdlValidator().setSchemaObjectName(
            method, qualifiedName);
        method.setInvocationName(method.getName());
        method.setCalledOnNullInput(true);
        // TODO jvs 25-Feb-2005:  mark as constructor
        FarragoCatalogUtil.setRoutineSpecification(
            getRepos(),
            method,
            typeDef);
    }
    <LPAREN> [ RoutineParamList(method) ] <RPAREN>
    {
        // TODO jvs 25-Feb-2005:  copy parameters to operation
    }
    ReturnsClause(method)
    <SELF> <AS> <RESULT>
    {
        // REVIEW jvs 25-Feb-2005:  according to SQL:2003, SPECIFIC
        // NAME has to come before SELF AS RESULT
    }
    RoutineCharacteristics(method)
}

void UserDefinedTypeOptions(FemUserDefinedType typeDef) :
{
}
{
// TODO jvs 13-Feb-2005:  prevent conflicting declarations.  Also, technically,
// structured types REQUIRE a finality declaration, and distinct types
// PROHIBIT an instantiability declaration.
    (
        <FINAL>
        {
            typeDef.setFinal(true);
        }
        | <INSTANTIABLE>
        {
            typeDef.setAbstract(false);
        }
        | <NOT>
        (
            <FINAL>
            {
                typeDef.setFinal(false);
            }
            | <INSTANTIABLE>
            {
                typeDef.setAbstract(true);
            }
        )
    ) *
}

FemSqlobjectType ObjectTypeDefinition() :
{
    FemSqlobjectType typeDef = getRepos().newFemSqlobjectType();
}
{
    <LPAREN>
    AttributeDefinition(typeDef)
    (
        <COMMA> AttributeDefinition(typeDef)
    ) *
    <RPAREN>
    {
        // NOTE jvs 13-Feb-2005:  for now, this is to trigger an error
        // in case they forget to specify FINAL
        typeDef.setFinal(false);
        typeDef.setAbstract(false);
        return typeDef;
    }
}

FemSqldistinguishedType DistinctTypeDefinition() :
{
    FemSqldistinguishedType typeDef = getRepos().newFemSqldistinguishedType();
    SqlDataTypeSpec dataType;
}
{
    dataType = DataType()
    {
        farragoParser.getDdlValidator().setSqlDefinition(
            typeDef,
            dataType);
        typeDef.setFinal(true);
        typeDef.setAbstract(false);
        return typeDef;
    }
}

void AttributeDefinition(FemSqlobjectType typeDef) :
{
    SqlIdentifier name;
    SqlDataTypeSpec dataType;
    FemSqltypeAttribute attrDef = getRepos().newFemSqltypeAttribute();
    SqlNode defaultClause;
    SqlCollation sqlCollation;
}
{
    name = SimpleIdentifier()
    {
        attrDef.setName(name.getSimple());
    }
    dataType = DataType()
    {
        farragoParser.getDdlValidator().setSqlDefinition(
            attrDef,
            dataType);
    }
    [ defaultClause = DefaultClause()
        {
            setDefaultExpression(attrDef, defaultClause);
        }
    ]
    [ sqlCollation = CollateClause()
        {
            attrDef.setCollationName(sqlCollation.getCollationName());
        }
    ]
    {
        typeDef.getFeature().add(attrDef);
    }
}

FemDataServer DataServerReference() :
{
    SqlIdentifier name;
}
{
    <SERVER> name = SimpleIdentifier()
    {
        return farragoParser.getStmtValidator().findDataServer(name);
    }
}

FemForeignTable ForeignTableDefinition() :
{
    FemForeignTable table;
    FemDataServer server;
    SqlIdentifier qualifiedTableName;
}
{
    <FOREIGN> <TABLE>
    {
        table = getRepos().newFemForeignTable();
    }
    qualifiedTableName = CompoundIdentifier3()
    {
        farragoParser.getDdlValidator().setSchemaObjectName(
            table,qualifiedTableName);
        table.setModality(ModalityTypeEnum.MODALITYTYPE_RELATIONAL);
    }
    [ <LPAREN> ForeignTableElementList(table) <RPAREN> ]
    server = DataServerReference()
    {
        server.getColumnSet().add(table);
    }
    OptionalStorageOptions(table)
    OptionalDescription(table)
    {
        return table;
    }
}

FemForeignTable ForeignTableReference() :
{
    SqlIdentifier qualifiedTableName;
    FemForeignTable table;
}
{
    <FOREIGN> <TABLE> qualifiedTableName = CompoundIdentifier3()
    {
        table = farragoParser.getStmtValidator().findSchemaObject(
            qualifiedTableName, FemForeignTable.class);
        return table;
    }
}

void ForeignTableElementList(FemForeignTable table) :
{
}
{
    ColumnDefinition(table)
    (
        <COMMA> ColumnDefinition(table)
    ) *
}

void OptionalDescription(FemAnnotatedElement element) :
{
    SqlNode description;
}
{
    [
        <DESCRIPTION> description = StringLiteral(false)
        {
            element.setDescription(SqlLiteral.stringValue(description));
        }
    ]
}

void OptionalSampleList(FemBaseColumnSet element, boolean allowBaseline) :
{}
{
    [
        <SAMPLE> <LPAREN>
        Sample(element, allowBaseline)
        (
            <COMMA> Sample(element, allowBaseline)
        )*
        <RPAREN>
    ]
}

void Sample(FemBaseColumnSet element, boolean allowBaseline) :
{
    SqlIdentifier qualifiedTableName;
    String sampleName;
    SqlTimestampLiteral baseline = null;
}
{
    qualifiedTableName = CompoundIdentifier()
    (
        LOOKAHEAD( {allowBaseline} )
        baseline = Baseline()
    |
        { baseline = null; }
    )
    <AS> sampleName = Identifier()
    {
        CwmNamedColumnSet columnSet =
            farragoParser.getStmtValidator().findSchemaObject(
                qualifiedTableName,
                CwmNamedColumnSet.class);
        FemSampleDataset dataset = getRepos().newFemSampleDataset();
        dataset.setName(sampleName);
        dataset.setOwningColumnSet(element);
        dataset.setUsedColumnSet(columnSet);
        if (baseline != null) {
            dataset.setBaselineTimestamp(baseline.toString());
        }
    }
}

SqlTimestampLiteral Baseline() :
{
}
{
    <BASELINE> <TIMESTAMP> <QUOTED_STRING>
    {
        return parseTimestampLiteral(token.image, getPos());
    }
    |
    {
        return null;
    }
}

void OptionalStorageOptions(FemElementWithStorageOptions element) :
{
}
{
    [
        <OPTIONS>
        <LPAREN>
        StorageOption(element)
        (
            <COMMA>
            StorageOption(element)
        ) *
        <RPAREN>
    ]
}

void StorageOption(FemElementWithStorageOptions element) :
{
    SqlIdentifier name;
    String value;
}
{
    name = SimpleIdentifier()
    value = QuotedString()
    {
        FemStorageOption option = getRepos().newFemStorageOption();
        option.setName(name.getSimple());
        option.setValue(value);
        element.getStorageOptions().add(option);
    }
}

DdlImportForeignSchemaStmt ImportForeignSchemaStmt() :
{
    SqlIdentifier foreignSchemaName;
    SqlIdentifier localSchemaName;
    boolean exclude = false;
    List roster = null;
    String pattern = null;
    FemDataServer server;
}
{
    // REVIEW jvs 5-Aug-2005:  is a catalog-qualified name legal here?
    <FOREIGN> <SCHEMA> foreignSchemaName = SimpleIdentifier()
    [
        (<LIMIT> <TO> | <EXCEPT> { exclude = true; })
        // TODO jvs 5-Aug-2005:  this is supposed to take compound
        // identifiers, but it is useless because all the schema names
        // have to match foreignSchemaName
        (
            <LPAREN> roster = SimpleIdentifierCommaList() <RPAREN>
            |
            <TABLE_NAME> <LIKE> pattern = QuotedString()
        )
    ]
    <FROM> server = DataServerReference()
    <INTO> localSchemaName = CompoundIdentifier2()
    {
        return new DdlImportForeignSchemaStmt(
            farragoParser.getStmtValidator().findSchema(localSchemaName),
            server,
            foreignSchemaName,
            exclude,
            roster,
            pattern);
    }
}

CwmModelElement DataWrapperDrop() :
{
    CwmModelElement wrapper;
}
{
    wrapper = DataWrapperReference()
    CascadeOption()
    {
        return wrapper;
    }
}

CwmModelElement DataServerDrop() :
{
    CwmModelElement server;
}
{
    server = DataServerReference()
    CascadeOption()
    {
        return server;
    }
}

CwmModelElement ForeignTableDrop() :
{
    CwmModelElement table;
}
{
    table = ForeignTableReference()
    CascadeOption()
    {
        return table;
    }
}

CwmModelElement UserDrop() :
{
    SqlIdentifier name;
}
{
    <USER> name = SimpleIdentifier()
    {
        return farragoParser.getStmtValidator().findUnqualifiedObject(
            name, FemUser.class);
    }
}

CwmModelElement RoleDrop() :
{
    SqlIdentifier name;
}
{
    <ROLE> name = SimpleIdentifier()
    {
        return farragoParser.getStmtValidator().findUnqualifiedObject(
            name, FemRole.class);
    }
}

CwmModelElement LabelDrop() :
{
    SqlIdentifier name;
    FemLabel label;
}
{
    <LABEL> name = SimpleIdentifier()
    {
        label = farragoParser.getStmtValidator().findUnqualifiedObject(
            name, FemLabel.class);
    }
    CascadeOption()
    {
        return label;
    }
}

SqlIdentifier PrivilegedAction() :
{
}
{
    (<INSERT> | <UPDATE> | <SELECT> | <DELETE> | <EXECUTE> | <USAGE>)
    {
        SqlIdentifier privId = new SqlIdentifier(
            token.image.toUpperCase(),  getPos());
        return privId;
    }
}


List PrivilegedActionList() :
{
    List privList = new ArrayList();
    SqlIdentifier privId;
}
{
    privId = PrivilegedAction()
    {
        privList.add(privId);
    }
    (
        <COMMA> privId = PrivilegedAction()
        {
            privList.add(privId);
        }
    ) *
    {
        return privList;
    }
}

//
// This method is common between the two grant statements
// (i.e. GRANT privilege and GRANT ROLE).
//
void GrantOptions(DdlGrantStmt stmt) :
{
    SqlIdentifier grantor;
}
{

    [
        <WITH> <GRANT> <OPTION>
        {
            stmt.setGrantOption(true);
        }
    ]
    [
        <GRANTED> <BY>
        (
            <CURRENT_ROLE> {stmt.setCurrentRoleOption(true);}
            |
            <CURRENT_USER> {stmt.setCurrentUserOption(true);}
        )
    ]
}

DdlStmt GrantPrivStmt() :
{
    DdlGrantPrivStmt stmt = new DdlGrantPrivStmt();
    CwmModelElement grantedObject;
    List granteeList;
    List privList;
    SqlParserPos pos1, pos2;
}
{
    (
        <ALL> pos1 = getPos() <PRIVILEGES> pos2 = getPos()
        {
            // TODO: externalize "ALL"
            SqlIdentifier allPrivId = new SqlIdentifier("ALL", pos1.plus(pos2));
            privList = startList(allPrivId);
        }
        |
        privList = PrivilegedActionList()
    )
    {
        stmt.setPrivList(privList);
    }
    <ON> (
        [ <TABLE> ] grantedObject = ColumnSetIdentifier()
        |
        grantedObject = SpecificRoutineReference()
        // TODO: (qtt) <SEQUENCE> grantedObject = SequenceIdentifier()
    )
    {
        stmt.setGrantedObject(grantedObject);
    }
    <TO> granteeList = SimpleIdentifierCommaList()
    {
        stmt.setGranteeList(granteeList);
    }
    [
        LOOKAHEAD (2) <WITH> <HIERARCHY> <OPTION>
        {
            stmt.setHierarchyOption(true);
        }
    ]
    GrantOptions(stmt)
    {
        return stmt;
    }
}


DdlStmt GrantRoleStmt() :
{
    DdlGrantRoleStmt stmt = new DdlGrantRoleStmt();
    List roleList;
    List granteeList;
}
{
    <ROLE> roleList = SimpleIdentifierCommaList()
    {
        stmt.setRoleList(roleList);
    }
    <TO> granteeList = SimpleIdentifierCommaList()
    {
        stmt.setGranteeList(granteeList);
    }
    GrantOptions(stmt)
    {
        return stmt;
    }
}


/**
 * Parses an analyze statement. Currently analyze table is supported.
 */
DdlStmt AnalyzeStmt() :
{
    DdlAnalyzeStmt stmt;
    CwmTable table;
    List columnNameList;
    SqlNumericLiteral percent;
}
{
    <TABLE> table = TableIdentifier()
    {
        stmt = new DdlAnalyzeStmt(table);
    }
    (
        <COMPUTE>
        |
        <ESTIMATE>
        {
            stmt.setEstimateOption(true);
        }
    )
    <STATISTICS> <FOR>
    (
        ( <ALL> <COLUMNS> )
        {
            stmt.setColumns((List) table.getFeature());
        }
        |
        ( <COLUMNS>
            <LPAREN> columnNameList = SimpleIdentifierCommaList() <RPAREN> )
        {
            List columnList = new LinkedList();
            for (Object columnName : columnNameList) {
                String simpleName = ((SqlIdentifier) columnName).getSimple();
                CwmColumn column =
                    farragoParser.getStmtValidator().findColumn(
                        table, simpleName);
                columnList.add(column);
            }
            stmt.setColumns(columnList);
        }
    )
    [
        <SAMPLE> percent = UnsignedNumericLiteral() <PERCENT>
        {
            BigDecimal rate = percent.bigDecimalValue();
            if (rate.compareTo(BigDecimal.ZERO) <= 0 ||
                rate.compareTo(BigDecimal.valueOf(100L)) > 0)
            {
                throw new ParseException(
                    EigenbaseResource.instance().InvalidSampleSize.str());
            }
            stmt.setSamplePercent(percent);
        }
    ]
    {
        return stmt;
    }
}


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

TOKEN :
{
    < ANALYZE: "ANALYZE">
    | < BASELINE: "BASELINE" >
    | < CHECKPOINT: "CHECKPOINT" >
    | < CLUSTERED: "CLUSTERED" >
    | < COLUMNS: "COLUMNS" >
    | < COMPUTE: "COMPUTE" >
    | < ESTIMATE: "ESTIMATE" >
    | < INDEX: "INDEX" >
    | < JAR: "JAR" >
    | < PERCENT: "PERCENT" >
    | < REBUILD: "REBUILD" >
    | < RENAME: "RENAME" >
    | < REPLACE: "REPLACE" >
    | < SAMPLE: "SAMPLE" >
    | < STATISTICS: "STATISTICS">
    | < TRUNCATE: "TRUNCATE" >
}

/**
 * Parses a non-reserved DDL keyword for use as an identifier.  Parser
 * implementations based on CommonDdlParser.jj should include this as
 * well as CommonParser.jj's CommonNonReservedKeyWord() in their
 * implementation of 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 DdlNonReservedKeyWord() :
{
}
{
    (
        // NOTE: ANALYZE a reserved word to facilitate LL(1) parsing
        <BASELINE>
        | <COLUMNS>
        | <COMPUTE>
        | <ESTIMATE>
        | <INDEX>
        | <JAR>
        | <PERCENT>
        | <REBUILD>
        | <RENAME>
        | <REPLACE>
        | <SAMPLE>
        | <STATISTICS>
    )
    {
        return getToken(0).image.toUpperCase();
    }
}



// End CommonDdlParser.jj

