All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.teiid.modeshape.sequencer.ddl.CreateProcedureParser Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source.
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */
package org.teiid.modeshape.sequencer.ddl;

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

/**
 * A parser for the Teiid  DDL statement.
 * 

* * CREATE ( VIRTUAL | FOREIGN )? ( PROCEDURE | FUNCTION ) ( ( ( )* )? ( RETURNS ( ( ( TABLE )? ( )* ) | ) )? ( )? ( AS )? ) * */ final class CreateProcedureParser extends StatementParser { CreateProcedureParser( final TeiidDdlParser teiidDdlParser ) { super(teiidDdlParser); } /** * {@inheritDoc} * * @see org.teiid.modeshape.sequencer.ddl.StatementParser#matches(org.teiid.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.teiid.modeshape.sequencer.ddl.StatementParser#parse(org.teiid.modeshape.sequencer.ddl.DdlTokenStream, * org.teiid.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; } /** * * ( IN | OUT | INOUT | VARIADIC )? ( NOT NULL )? ( RESULT )? ( DEFAULT )? ( )? * * * @param tokens the tokens being processed (cannot be null or empty) * @param procedureNode the create procedure node owning this parameter (cannot be null) */ 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); } /** * * ( ( )* )? * * * @param tokens the tokens being processed (cannot be null or empty) * @param procedureNode the create procedure node owning these parameters (cannot be null) */ 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)"); } } /** * *

* * ( NOT NULL )? ( )? * * * @param tokens the tokens being processed (cannot be null or empty) * @param resultSetNode the result set node owning this result column (cannot be null) */ 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); } /** * * ( TABLE )? ( )* ) * * * @param tokens the tokens being processed (cannot be null or empty) * @param procedureNode the create procedure node owning these result columns (cannot be null) * @return the node of the tabular result set or null if tabular result set was not found * @throws ParsingException if there is a problem parsing the procedure result columns */ AstNode 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 resultSetNode; } throw new TeiidDdlParsingException(tokens, "Unparsable procedure result columns (left paren not found)"); } return null; // no tabular result set found } /** * The RETURNS clause is optional for the create procedure statement. *

* * ( RETURNS ( <options clause> )? ( ( ( TABLE )? <lparen> <procedure result column> ( <comma> <procedure result column> )* <rparen> ) | <data type> ) )? * * * @param tokens the tokens being processed (cannot be null) * @param procedureNode the procedure node of of the returns clause (cannot be null) * @return true if the returns clause was successfully parsed */ boolean parseReturnsClause( final DdlTokenStream tokens, final AstNode procedureNode ) { if (tokens.canConsume(TeiidReservedWord.RETURNS.toDdl())) { // may have options associated with the result set final Map options = new HashMap<>(); final boolean optionsExist = parseOptionsClause(tokens, options); // result set must have either one or more result columns or be a data type AstNode resultNode = parseProcedureResultColumns(tokens, procedureNode); // if null must be a data type result set if (resultNode == null) { final DataType dataType = getDataTypeParser().parse(tokens); // create data type result set node 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()); } if (dataType.getArrayDimensions() != DataType.DEFAULT_ARRAY_DIMENSIONS) { resultNode.setProperty(DATATYPE_ARRAY_DIMENSIONS, dataType.getArrayDimensions()); } } assert (resultNode != null); // now that we have a result set node we can add in the options as children if (optionsExist) { createOptionNodes(options, resultNode); } 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 ) { } }