Package org.modeshape.sequencer.ddl.dialect.teiid

Source Code of org.modeshape.sequencer.ddl.dialect.teiid.CreateProcedureParser

/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.sequencer.ddl.dialect.teiid;

import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DATATYPE_LENGTH;
import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DATATYPE_NAME;
import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DATATYPE_PRECISION;
import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DATATYPE_SCALE;
import org.modeshape.common.text.ParsingException;
import org.modeshape.common.text.Position;
import org.modeshape.common.util.StringUtil;
import org.modeshape.sequencer.ddl.DdlTokenStream;
import org.modeshape.sequencer.ddl.StandardDdlLexicon;
import org.modeshape.sequencer.ddl.datatype.DataType;
import org.modeshape.sequencer.ddl.dialect.teiid.TeiidDdlConstants.DdlStatement;
import org.modeshape.sequencer.ddl.dialect.teiid.TeiidDdlConstants.SchemaElementType;
import org.modeshape.sequencer.ddl.dialect.teiid.TeiidDdlConstants.TeiidNonReservedWord;
import org.modeshape.sequencer.ddl.dialect.teiid.TeiidDdlConstants.TeiidReservedWord;
import org.modeshape.sequencer.ddl.node.AstNode;

/**
* A parser for the Teiid <create procedure> DDL statement.
* <p>
* <code>
* CREATE ( VIRTUAL | FOREIGN )? ( PROCEDURE | FUNCTION ) ( <identifier> <lparen> ( <procedure parameter> ( <comma> <procedure parameter> )* )? <rparen> ( RETURNS ( ( ( TABLE )? <lparen> <procedure result column> ( <comma> <procedure result column> )* <rparen> ) | <data type> ) )? ( <options clause> )? ( AS <statement> )? )
* </code>
*/
final class CreateProcedureParser extends StatementParser {

    CreateProcedureParser( final TeiidDdlParser teiidDdlParser ) {
        super(teiidDdlParser);
    }

    /**
     * {@inheritDoc}
     *
     * @see org.modeshape.sequencer.ddl.dialect.teiid.StatementParser#matches(org.modeshape.sequencer.ddl.DdlTokenStream)
     */
    @Override
    boolean matches( final DdlTokenStream tokens ) {
        return tokens.matches(DdlStatement.CREATE_VIRTUAL_FUNCTION.tokens())
               || tokens.matches(DdlStatement.CREATE_VIRTUAL_PROCEDURE.tokens())
               || tokens.matches(DdlStatement.CREATE_FOREIGN_FUNCTION.tokens())
               || tokens.matches(DdlStatement.CREATE_FOREIGN_PROCEDURE.tokens())
               || tokens.matches(DdlStatement.CREATE_FUNCTION.tokens()) || tokens.matches(DdlStatement.CREATE_PROCEDURE.tokens());
    }

    /**
     * {@inheritDoc}
     *
     * @see org.modeshape.sequencer.ddl.dialect.teiid.StatementParser#parse(org.modeshape.sequencer.ddl.DdlTokenStream,
     *      org.modeshape.sequencer.ddl.node.AstNode)
     */
    @Override
    AstNode parse( final DdlTokenStream tokens,
                   final AstNode parentNode ) throws ParsingException {
        boolean procedure = true;
        DdlStatement stmt = null;
        SchemaElementType schemaElementType = null;

        if (tokens.canConsume(DdlStatement.CREATE_VIRTUAL_FUNCTION.tokens())) {
            stmt = DdlStatement.CREATE_VIRTUAL_FUNCTION;
            schemaElementType = SchemaElementType.VIRTUAL;
            procedure = false;
        } else if (tokens.canConsume(DdlStatement.CREATE_VIRTUAL_PROCEDURE.tokens())) {
            stmt = DdlStatement.CREATE_VIRTUAL_PROCEDURE;
            schemaElementType = SchemaElementType.VIRTUAL;
        } else if (tokens.canConsume(DdlStatement.CREATE_FOREIGN_FUNCTION.tokens())) {
            stmt = DdlStatement.CREATE_FOREIGN_FUNCTION;
            schemaElementType = SchemaElementType.FOREIGN;
            procedure = false;
        } else if (tokens.canConsume(DdlStatement.CREATE_FOREIGN_PROCEDURE.tokens())) {
            stmt = DdlStatement.CREATE_FOREIGN_PROCEDURE;
            schemaElementType = SchemaElementType.FOREIGN;
        } else if (tokens.canConsume(DdlStatement.CREATE_FUNCTION.tokens())) {
            stmt = DdlStatement.CREATE_FUNCTION;
            schemaElementType = SchemaElementType.FOREIGN;
            procedure = false;
        } else if (tokens.canConsume(DdlStatement.CREATE_PROCEDURE.tokens())) {
            stmt = DdlStatement.CREATE_PROCEDURE;
            schemaElementType = SchemaElementType.FOREIGN;
        } else {
            throw new TeiidDdlParsingException(tokens, "Unparsable create procedure statement");
        }

        assert (stmt != null) : "Create procedure statement is null";
        assert (schemaElementType != null) : "Create procedure schema element type is null";

        // parse identifier
        final String id = parseIdentifier(tokens);
        final AstNode procedureNode = getNodeFactory().node(id,
                                                            parentNode,
                                                            (procedure ? TeiidDdlLexicon.CreateProcedure.PROCEDURE_STATEMENT : TeiidDdlLexicon.CreateProcedure.FUNCTION_STATEMENT));
        procedureNode.setProperty(TeiidDdlLexicon.SchemaElement.TYPE, schemaElementType.toDdl());

        // must have parens after identifier and may have one or more parameters
        parseProcedureParameters(tokens, procedureNode);

        // may have a returns clause
        parseReturnsClause(tokens, procedureNode);

        // may have an option clause
        parseOptionsClause(tokens, procedureNode);

        // may have AS clause
        parseAsClause(tokens, procedureNode);

        return procedureNode;
    }

    boolean parseAsClause( final DdlTokenStream tokens,
                           final AstNode procedureNode ) {
        if (tokens.canConsume(TeiidReservedWord.AS.toDdl())) {
            final String statement = parseStatement(tokens, 0, "", tokens.nextPosition(), "");

            if (StringUtil.isBlank(statement)) {
                throw new TeiidDdlParsingException(tokens, "Unparsable AS clause (no statement found)");
            }

            procedureNode.setProperty(TeiidDdlLexicon.CreateProcedure.STATEMENT, statement);
            return true;
        }

        return false;
    }

    /**
     * <procedure parameter> <code>
     * ( IN | OUT | INOUT | VARIADIC )? <identifier> <data type> ( NOT NULL )? ( RESULT )? ( DEFAULT <string> )? ( <options clause> )?
     * <code>
     *
     * @param tokens the tokens being processed (cannot be <code>null</code> or empty)
     * @param procedureNode the create procedure node owning this parameter (cannot be <code>null</code>)
     */
    void parseProcedureParameter( final DdlTokenStream tokens,
                                  final AstNode procedureNode ) {
        String paramType = TeiidReservedWord.IN.toDdl();

        if (tokens.matches(TeiidReservedWord.IN.toDdl()) || tokens.matches(TeiidReservedWord.OUT.toDdl())
            || tokens.matches(TeiidReservedWord.INOUT.toDdl()) || tokens.matches(TeiidNonReservedWord.VARIADIC.toDdl())) {
            paramType = tokens.consume();
        }

        final String id = parseIdentifier(tokens);
        final AstNode parameterNode = getNodeFactory().node(id, procedureNode, TeiidDdlLexicon.CreateProcedure.PARAMETER);
        parameterNode.setProperty(TeiidDdlLexicon.CreateProcedure.PARAMETER_TYPE, paramType);

        // parse data type
        final DataType dataType = getDataTypeParser().parse(tokens);
        getDataTypeParser().setPropertiesOnNode(parameterNode, dataType);

        // parse any optional clauses
        boolean foundNotNull = false;
        boolean foundResult = false;
        boolean foundDefault = false;
        boolean foundOptions = false;
        boolean keepParsing = true;

        while (keepParsing && (!foundNotNull || !foundResult || !foundDefault || foundOptions)) {
            if (tokens.canConsume(NOT_NULL)) {
                foundNotNull = true;
            } else if (tokens.canConsume(TeiidNonReservedWord.RESULT.toDdl())) {
                foundResult = true;
            } else if (parseDefaultClause(tokens, parameterNode)) {
                foundDefault = true;
            } else if (parseOptionsClause(tokens, parameterNode)) {
                foundOptions = true;
            } else {
                keepParsing = false;
            }
        }

        parameterNode.setProperty(StandardDdlLexicon.NULLABLE, (foundNotNull ? "NOT NULL" : "NULL"));
        parameterNode.setProperty(TeiidDdlLexicon.CreateProcedure.PARAMETER_RESULT_FLAG, foundResult);
    }

    /**
     * <procedure parameters> <code>
     * <lparen> ( <procedure parameter> ( <comma> <procedure parameter> )* )? <rparen>
     * <code>
     *
     * @param tokens the tokens being processed (cannot be <code>null</code> or empty)
     * @param procedureNode the create procedure node owning these parameters (cannot be <code>null</code>)
     */
    void parseProcedureParameters( final DdlTokenStream tokens,
                                   final AstNode procedureNode ) {
        if (tokens.canConsume(L_PAREN)) {
            // parse parameters if any exist
            if (!tokens.matches(R_PAREN)) {
                parseProcedureParameter(tokens, procedureNode);

                while (tokens.canConsume(COMMA)) {
                    parseProcedureParameter(tokens, procedureNode);
                }
            }

            // must have ending paren
            if (!tokens.canConsume(R_PAREN)) {
                throw new TeiidDdlParsingException(tokens, "Unparsable procedure parameters (right paren not found)");
            }
        } else {
            throw new TeiidDdlParsingException(tokens, "Unparsable procedure parameters (left paren not found)");
        }
    }

    /**
     * <procedure result column>
     * <p>
     * <code>
     * <identifier> <data type> ( NOT NULL )? ( <options clause> )?
     * <code>
     *
     * @param tokens the tokens being processed (cannot be <code>null</code> or empty)
     * @param resultSetNode the result set node owning this result column (cannot be <code>null</code>)
     */
    void parseProcedureResultColumn( final DdlTokenStream tokens,
                                     final AstNode resultSetNode ) {
        final String id = parseIdentifier(tokens);
        final DataType dataType = getDataTypeParser().parse(tokens);
        final boolean notNull = tokens.canConsume(NOT_NULL);

        final AstNode resultColumnNode = getNodeFactory().node(id, resultSetNode, TeiidDdlLexicon.CreateProcedure.RESULT_COLUMN);
        resultColumnNode.setProperty(StandardDdlLexicon.NULLABLE, (notNull ? "NOT NULL" : "NULL"));
        getDataTypeParser().setPropertiesOnNode(resultColumnNode, dataType);

        // may have an options clause
        parseOptionsClause(tokens, resultColumnNode);
    }

    /**
     * <procedure result columns> <code>
     * ( TABLE )? <lparen> <procedure result column> ( <comma> <procedure result column> )* <rparen> )
     * <code>
     *
     * @param tokens the tokens being processed (cannot be <code>null</code> or empty)
     * @param procedureNode the create procedure node owning these result columns (cannot be <code>null</code>)
     * @return <code>true</code> if procedure results columns were successfully parsed
     * @throws ParsingException if there is a problem parsing the procedure result columns
     */
    boolean parseProcedureResultColumns( final DdlTokenStream tokens,
                                         final AstNode procedureNode ) throws ParsingException {
        if (tokens.matches(TABLE, L_PAREN) || tokens.matches(L_PAREN)) {
            boolean table = tokens.canConsume(TABLE);

            if (tokens.canConsume(L_PAREN)) {
                // create result columns node
                final AstNode resultSetNode = getNodeFactory().node(TeiidDdlLexicon.CreateProcedure.RESULT_SET,
                                                                    procedureNode,
                                                                    TeiidDdlLexicon.CreateProcedure.RESULT_COLUMNS);
                resultSetNode.setProperty(TeiidDdlLexicon.CreateProcedure.TABLE_FLAG, table);

                parseProcedureResultColumn(tokens, resultSetNode); // must have at least one

                while (tokens.canConsume(COMMA)) {
                    parseProcedureResultColumn(tokens, resultSetNode);
                }

                // must have ending paren
                if (!tokens.canConsume(R_PAREN)) {
                    throw new TeiidDdlParsingException(tokens, "Unparsable procedure result columns (right paren not found)");
                }

                return true;
            }

            throw new TeiidDdlParsingException(tokens, "Unparsable procedure result columns (left paren not found)");
        }

        return false;
    }

    /**
     * And optional clause of the create procedure statement.
     * <p>
     * <code>
     * ( RETURNS ( ( ( TABLE )? <lparen> <procedure result column> ( <comma> <procedure result column> )* <rparen> ) | <data type> ) )?
     * </code>
     *
     * @param tokens the tokens being processed (cannot be <code>null</code>)
     * @param procedureNode the procedure node of of the returns clause (cannot be <code>null</code>)
     * @return <code>true</code> if the returns clause was successfully parsed
     */
    boolean parseReturnsClause( final DdlTokenStream tokens,
                                final AstNode procedureNode ) {
        if (tokens.canConsume(TeiidReservedWord.RETURNS.toDdl())) {
            // must have either one or more result columns or a data type
            if (!parseProcedureResultColumns(tokens, procedureNode)) {
                final DataType dataType = getDataTypeParser().parse(tokens);

                // create result node
                final AstNode resultNode = getNodeFactory().node(TeiidDdlLexicon.CreateProcedure.RESULT_SET,
                                                                 procedureNode,
                                                                 TeiidDdlLexicon.CreateProcedure.RESULT_DATA_TYPE);
                resultNode.setProperty(DATATYPE_NAME, dataType.getName());

                if (dataType.getLength() != DataType.DEFAULT_LENGTH) {
                    resultNode.setProperty(DATATYPE_LENGTH, dataType.getLength());
                }

                if (dataType.getPrecision() != DataType.DEFAULT_PRECISION) {
                    resultNode.setProperty(DATATYPE_PRECISION, dataType.getPrecision());
                }

                if (dataType.getScale() != DataType.DEFAULT_SCALE) {
                    resultNode.setProperty(DATATYPE_SCALE, dataType.getScale());
                }
            }

            return true;
        }

        return false;
    }

    private String parseStatement( final DdlTokenStream tokens,
                                   int numBegins,
                                   final String statement,
                                   Position prevPosition,
                                   String prevValue ) throws ParsingException {
        final StringBuilder text = new StringBuilder(statement);

        while (tokens.hasNext()) {
            final Position currPosition = tokens.nextPosition();
            final String value = tokens.consume();

            if (TeiidReservedWord.BEGIN.toDdl().equals(value)) {
                text.append(getWhitespace(currPosition, prevPosition, prevValue));
                text.append(TeiidReservedWord.BEGIN.toDdl());
                return parseStatement(tokens, ++numBegins, text.toString(), currPosition, value);
            }

            if (TeiidReservedWord.END.toDdl().equals(value)) {
                text.append(getWhitespace(currPosition, prevPosition, prevValue));
                text.append(TeiidReservedWord.END.toDdl());
                return parseStatement(tokens, --numBegins, text.toString(), currPosition, value);
            }

            if (SEMICOLON.equals(value)) {
                if (numBegins > 0) {
                    text.append(getWhitespace(currPosition, prevPosition, prevValue));
                    text.append(SEMICOLON);
                    return parseStatement(tokens, numBegins, text.toString(), currPosition, value);
                }

                text.append(SEMICOLON);
                break;
            }

            text.append(getWhitespace(currPosition, prevPosition, prevValue));
            text.append(value);
            prevValue = value;
            prevPosition = currPosition;
        }

        return text.toString();
    }

    @Override
    protected void postProcess( AstNode rootNode ) {

    }
}
TOP

Related Classes of org.modeshape.sequencer.ddl.dialect.teiid.CreateProcedureParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.