/* * 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.query.parser; import java.io.Reader; import java.io.StringReader; import java.util.HashSet; import org.teiid.designer.annotation.Removed; import org.teiid.designer.query.IQueryParser; import org.teiid.designer.runtime.version.spi.ITeiidServerVersion; import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version; import org.teiid.metadata.FunctionMethod; import org.teiid.metadata.MetadataFactory; import org.teiid.query.parser.v9.Teiid9Parser; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.lang.Criteria; import org.teiid.query.sql.symbol.Expression; import org.teiid.runtime.client.Messages; import org.teiid.runtime.client.TeiidClientException; /** * <p>Converts a SQL-string to an object version of a query. This * QueryParser can be reused but is NOT thread-safe as the parser uses an * input stream. Putting multiple queries into the same stream will result * in unpredictable and most likely incorrect behavior.</p> */ public class QueryParser implements IQueryParser { private TeiidParser teiidParser; private final ITeiidServerVersion teiidVersion; /** * Construct a QueryParser - this may be reused. * * @param teiidVersion */ public QueryParser(ITeiidServerVersion teiidVersion) { this.teiidVersion = teiidVersion; this.teiidParser = createTeiidParser(new StringReader("")); //$NON-NLS-1$ } private TeiidParser createTeiidParser(Reader sql) { if (sql == null) throw new IllegalArgumentException(Messages.gs(Messages.TEIID.TEIID30377)); int major = Integer.parseInt(teiidVersion.getMajor()); TeiidParser teiidParser = null; switch (major) { case 9: teiidParser = new Teiid9Parser(sql); teiidParser.setVersion(teiidVersion); break; default: throw new IllegalStateException(Messages.getString(Messages.TeiidParser.noParserForVersion, major)); } return teiidParser; } /** * @return the teiid version */ public ITeiidServerVersion getTeiidVersion() { return teiidVersion; } /** * @return the teiidParser */ public TeiidParser getTeiidParser() { return this.teiidParser; } /** * @param sql * @return the {@link TeiidParser} initialised with the given sql */ public TeiidParser getTeiidParser(String sql) { return getSqlParser(new StringReader(sql)); } private TeiidParser getSqlParser(Reader sql) { if(teiidParser == null) { teiidParser = createTeiidParser(sql); } else teiidParser.reset(sql); return teiidParser; } /** * Parses the given procedure sql string, returning the object * representation * * @param sql * @param update * @return command of sql * @throws Exception */ public Command parseProcedure(String sql, boolean update) throws Exception { try{ if (update) { return getTeiidParser(sql).forEachRowTriggerAction(new ParseInfo()); } Command result = getTeiidParser(sql).procedureBodyCommand(new ParseInfo()); return result; } catch(Exception pe) { throw convertParserException(pe); } } /** * Takes a SQL string representing a Command and returns the object * representation. * * @param sql SQL string * instead of string litral * @return SQL object representation * @throws Exception if parsing fails * @throws IllegalArgumentException if sql is null */ @Override public Command parseCommand(String sql) throws Exception { return parseCommand(sql, new ParseInfo(), false); } /** * Takes a SQL string representing a Command and returns the object * representation. * * @param sql SQL string instead of string litral * @param parseInfo * @return SQL object representation * @throws Exception if parsing fails * @throws IllegalArgumentException if sql is null */ public Command parseCommand(String sql, ParseInfo parseInfo) throws Exception { return parseCommand(sql, parseInfo, false); } /** * Takes a SQL string representing a Command and returns the object * representation. * * @param sql * @return SQL object representation * @throws Exception if parsing fails * @throws IllegalArgumentException if sql is null */ @Override public Command parseDesignerCommand(String sql) throws Exception { return parseCommand(sql, new ParseInfo(), true); } private Command parseCommand(String sql, ParseInfo parseInfo, boolean designerCommands) throws Exception { if(sql == null || sql.length() == 0) { throw new TeiidClientException(Messages.gs(Messages.TEIID.TEIID30377)); } Command result = null; try{ TeiidParser teiidParser = getTeiidParser(sql); if (designerCommands) { result = teiidParser.designerCommand(parseInfo); } else { result = teiidParser.command(parseInfo); } result.setCacheHint(teiidParser.getQueryCacheOption(sql)); } catch(Exception e) { throw convertParserException(e); } return result; } /** * @param e * @return */ private TeiidClientException convertParserException(Exception e) { TeiidClientException qpe = new TeiidClientException(e, Messages.getString(Messages.TeiidParser.lexicalError, e.getMessage())); return qpe; } /** * Takes a SQL string representing an SQL criteria (i.e. just the WHERE * clause) and returns the object representation. * @param sql SQL criteria (WHERE clause) string * @return Criteria SQL object representation * @throws Exception if parsing fails * @throws TeiidClientException if sql is null */ @Override public Criteria parseCriteria(String sql) throws Exception { if(sql == null) { throw new TeiidClientException(Messages.gs(Messages.TEIID.TEIID30377)); } ParseInfo dummyInfo = new ParseInfo(); Criteria result = null; try{ result = getTeiidParser(sql).criteria(dummyInfo); } catch(Exception e) { throw convertParserException(e); } return result; } /** * Takes a SQL string representing an SQL expression * and returns the object representation. * @param sql SQL expression string * @return SQL expression object * @throws Exception if parsing fails * @throws IllegalArgumentException if sql is null */ @Override public Expression parseExpression(String sql) throws Exception { if(sql == null) { throw new TeiidClientException(Messages.gs(Messages.TEIID.TEIID30377)); } ParseInfo dummyInfo = new ParseInfo(); Expression result = null; try{ result = getTeiidParser(sql).expression(dummyInfo); } catch (Exception e) { throw convertParserException(e); } return result; } /** * @param sql * @return Expression representing sql * @throws Exception */ public Expression parseSelectExpression(String sql) throws Exception { if (sql == null) { throw new TeiidClientException(Messages.gs(Messages.TEIID.TEIID30377)); } ParseInfo dummyInfo = new ParseInfo(); Expression result = null; try { result = getTeiidParser(sql).selectExpression(dummyInfo); } catch (Exception e) { throw convertParserException(e); } return result; } // TODO consider whether we need to explicitly parse update procedure / procedure // public Command parseProcedure(String sql, boolean update) throws Exception { // try{ // if (update) { // return getTeiidParser(sql).forEachRowTriggerAction(new ParseInfo()); // } // Command result = getSqlParser(sql).procedureBodyCommand(new ParseInfo()); // result.setCacheHint(SQLParserUtil.getQueryCacheOption(sql)); // return result; // } catch(ParseException pe) { // throw convertParserException(pe); // } finally { // tm.reinit(); // } // } // // public Command parseUpdateProcedure(String sql) throws Exception { // try{ // Command result = getTeiidParser(sql).updateProcedure(new ParseInfo()); // result.setCacheHint(SQLParserUtil.getQueryCacheOption(sql)); // return result; // } catch(ParseException pe) { // throw convertParserException(pe); // } catch(TokenMgrError tme) { // throw handleTokenMgrError(tme); // } // } public void parseDDL(MetadataFactory factory, String ddl) { parseDDL(factory, new StringReader(ddl)); } public void parseDDL(MetadataFactory factory, Reader ddl) { try { getSqlParser(ddl).parseMetadata(factory); } catch (Exception e) { throw new RuntimeException(e); } HashSet<FunctionMethod> functions = new HashSet<FunctionMethod>(); for (FunctionMethod functionMethod : factory.getSchema().getFunctions().values()) { if (!functions.add(functionMethod)) { throw new RuntimeException(Messages.gs(Messages.TEIID.TEIID60015, functionMethod.getName())); } } } }