/* * 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.translator.odata; import static org.junit.Assert.*; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Properties; import org.junit.Before; import org.junit.Test; import org.odata4j.edm.EdmDataServices; import org.odata4j.format.xml.EdmxFormatParser; import org.odata4j.stax2.util.StaxUtil; import org.teiid.cdk.api.TranslationUtility; import org.teiid.core.util.ObjectConverterUtil; import org.teiid.core.util.UnitTestUtil; import org.teiid.language.Call; import org.teiid.language.Command; import org.teiid.language.Select; import org.teiid.metadata.MetadataFactory; import org.teiid.query.function.FunctionTree; import org.teiid.query.function.UDFSource; import org.teiid.query.metadata.MetadataValidator; import org.teiid.query.metadata.SystemMetadata; import org.teiid.query.metadata.TransformationMetadata; import org.teiid.query.unittest.RealMetadataFactory; import org.teiid.query.validator.ValidatorReport; import org.teiid.translator.TranslatorException; @SuppressWarnings("nls") public class TestODataSQLVistor { private static final boolean printPayload = false; private ODataExecutionFactory translator; private TranslationUtility utility; @Before public void setUp() throws Exception { translator = new ODataExecutionFactory(); translator.start(); String csdl = ObjectConverterUtil.convertFileToString(UnitTestUtil.getTestDataFile("northwind.xml")); EdmDataServices eds = new EdmxFormatParser().parseMetadata(StaxUtil.newXMLEventReader(new InputStreamReader(new ByteArrayInputStream(csdl.getBytes())))); ODataMetadataProcessor processor = new ODataMetadataProcessor(); Properties props = new Properties(); props.setProperty("schemaNamespace", "ODataWeb.Northwind.Model"); props.setProperty("entityContainer", "NorthwindEntities"); MetadataFactory mf = new MetadataFactory("vdb", 1, "nw", SystemMetadata.getInstance().getRuntimeTypeMap(), props, null); processor.getMetadata(mf, eds); TransformationMetadata metadata = RealMetadataFactory.createTransformationMetadata(mf.asMetadataStore(), "northwind", new FunctionTree("foo", new UDFSource(translator.getPushDownFunctions()))); ValidatorReport report = new MetadataValidator().validate(metadata.getVdbMetaData(), metadata.getMetadataStore()); if (report.hasItems()) { throw new RuntimeException(report.getFailureMessage()); } //TransformationMetadata metadata = RealMetadataFactory.fromDDL(ObjectConverterUtil.convertFileToString(UnitTestUtil.getTestDataFile("northwind.ddl")), "northwind", "nw"); utility = new TranslationUtility(metadata); } private ArrayList<TranslatorException> helpExecute(String query, String expected) throws Exception { Select cmd = (Select)this.utility.parseCommand(query); ODataSQLVisitor visitor = new ODataSQLVisitor(this.translator, utility.createRuntimeMetadata()); visitor.visitNode(cmd); String actual = URLDecoder.decode(visitor.buildURL(), "UTF-8"); assertEquals(expected, actual); return visitor.exceptions; } @Test public void testSelectStar() throws Exception { helpExecute("select * from customers", "Customers?$select=CustomerID,CompanyName,ContactName,ContactTitle,Mailing,Shipping"); } @Test public void testSelectSpecificColumns() throws Exception { helpExecute("select CustomerID from customers", "Customers?$select=CustomerID"); } @Test public void testPKBasedFilter() throws Exception { helpExecute("select CompanyName from Customers where CustomerID = 'ALSK'", "Customers('ALSK')?$select=CompanyName"); } @Test public void testMultiKeyKeyBasedFilter() throws Exception { helpExecute("select UnitPrice from Order_Details where OrderID = 1 and ProductID = 12 and Quantity = 2", "Order_Details(OrderID=1,ProductID=12)?$filter=Quantity eq 2&$select=UnitPrice"); } @Test public void testAddFilter() throws Exception { helpExecute("select UnitPrice from Order_Details where OrderID = 1 and (Quantity+2) > OrderID", "Order_Details?$filter=OrderID eq 1 and (cast(Quantity,'integer') add 2) gt OrderID&$select=UnitPrice"); } @Test public void testEndsWithFilter() throws Exception { helpExecute("select CompanyName from Customers where endswith('k', CustomerID)", "Customers?$filter=endswith(CustomerID,'k') eq true&$select=CompanyName"); } @Test public void testMultiKeyKeyBasedFilterOr() throws Exception { helpExecute("select UnitPrice from Order_Details where (OrderID = 1 and ProductID = 12) or Quantity = 2", "Order_Details?$filter=(OrderID eq 1 and ProductID eq 12) or Quantity eq 2&$select=UnitPrice"); } @Test public void testPartialPK() throws Exception { helpExecute("select UnitPrice from Order_Details where Quantity >= 2 and ProductID = 12", "Order_Details?$filter=Quantity ge 2 and ProductID eq 12&$select=UnitPrice"); } @Test public void testSimpleJoin() throws Exception { helpExecute("SELECT od.UnitPrice FROM Orders o JOIN Order_Details od ON o.OrderID=od.OrderID and o.OrderID=12", "Orders(12)/Order_Details?$select=UnitPrice"); } @Test public void testJoinBasedTwoPK() throws Exception { helpExecute("SELECT od.UnitPrice FROM Orders o JOIN Order_Details od ON o.OrderID=od.OrderID and o.OrderID=12 WHERE od.ProductID=1", "Orders(12)/Order_Details(OrderID=12,ProductID=1)?$select=UnitPrice"); } @Test public void testJoinBasedTwoPKOnKey() throws Exception { helpExecute("SELECT od.UnitPrice FROM Orders o JOIN Order_Details od ON o.OrderID=od.OrderID and o.OrderID=12 and od.ProductID=1", "Orders(12)/Order_Details(OrderID=12,ProductID=1)?$select=UnitPrice"); } @Test public void testEmbeddedJoin() throws Exception { testSelectStar(); // customer table is embedded } @Test public void testFunction() throws Exception { helpExecute("SELECT ContactName FROM Customers WHERE odata.startswith(CompanyName, 'CN')", "Customers?$filter=startswith(CompanyName,'CN') eq true&$select=ContactName"); } @Test public void testBooleanFunction() throws Exception { this.translator.setSupportsOdataBooleanFunctionsWithComparison(false); try { helpExecute("SELECT ContactName FROM Customers WHERE odata.startswith(CompanyName, 'CN')", "Customers?$filter=startswith(CompanyName,'CN')&$select=ContactName"); helpExecute("SELECT ContactName FROM Customers WHERE odata.startswith(CompanyName, 'CN') = 0", "Customers?$filter=NOT (startswith(CompanyName,'CN'))&$select=ContactName"); helpExecute("SELECT ContactName FROM Customers WHERE odata.startswith(CompanyName, 'CN') <> 1", "Customers?$filter=NOT (startswith(CompanyName,'CN'))&$select=ContactName"); helpExecute("SELECT ContactName FROM Customers WHERE odata.startswith(CompanyName, 'CN') <> 0", "Customers?$filter=startswith(CompanyName,'CN')&$select=ContactName"); helpExecute("SELECT ContactName FROM Customers WHERE endswith('CN', CompanyName)", "Customers?$filter=endswith(CompanyName,'CN')&$select=ContactName"); helpExecute("SELECT ContactName FROM Customers WHERE odata.substringof(CompanyName, 'CN')", "Customers?$filter=substringof(CompanyName,'CN')&$select=ContactName"); ArrayList<TranslatorException> exceptions = helpExecute("SELECT ContactName FROM Customers WHERE odata.startswith(CompanyName, 'CN') < 1", "Customers?$filter=startswith(CompanyName,'CN')&$select=ContactName"); assertTrue(!exceptions.isEmpty()); assertTrue(exceptions.get(0).getMessage().contains(ODataPlugin.Event.TEIID17018.name())); } finally { this.translator.setSupportsOdataBooleanFunctionsWithComparison(true); } } @Test public void testLimit() throws Exception { helpExecute("SELECT ContactName FROM Customers limit 10", "Customers?$select=ContactName&$top=10"); } @Test public void testLimitOffset() throws Exception { helpExecute("SELECT ContactName FROM Customers limit 10, 19", "Customers?$select=ContactName&$skip=10&$top=19"); } @Test public void testUseAirthmaticFunction() throws Exception { helpExecute("SELECT LastName FROM Employees WHERE EmployeeID/10 > EmployeeID", "Employees?$filter=(EmployeeID div 10) gt EmployeeID&$select=LastName"); } @Test public void testOrderBy() throws Exception { helpExecute("SELECT LastName FROM Employees Order By LastName", "Employees?$orderby=LastName&$select=LastName"); } @Test public void testOrderByDESC() throws Exception { helpExecute("SELECT LastName FROM Employees Order By LastName DESC", "Employees?$orderby=LastName desc&$select=LastName"); } @Test public void testOrderByMultiple() throws Exception { helpExecute("SELECT LastName FROM Employees Order By LastName DESC, EmployeeId", "Employees?$orderby=LastName desc,EmployeeID&$select=LastName"); } @Test public void testisNotNull() throws Exception { helpExecute("SELECT LastName FROM Employees WHERE LastName is NOT NULL", "Employees?$filter=not(LastName eq null)&$select=LastName"); } @Test public void testisNull() throws Exception { helpExecute("SELECT LastName FROM Employees WHERE LastName is NULL", "Employees?$filter=LastName eq null&$select=LastName"); } @Test public void testCountStar() throws Exception { helpExecute("SELECT count(*) FROM Employees", "Employees/$count"); } private void helpFunctionExecute(String query, String expected) throws Exception { Call cmd = (Call)this.utility.parseCommand(query); ODataProcedureVisitor visitor = new ODataProcedureVisitor(translator, utility.createRuntimeMetadata()); visitor.visitNode(cmd); String odataCmd = visitor.buildURL(); assertEquals(expected, odataCmd); assertEquals("GET", visitor.getMethod()); } @Test public void testProcedureExec() throws Exception { helpFunctionExecute("Exec TopCustomers('newyork')", "TopCustomers?city=%27newyork%27"); } @Test public void testProcedureExecEncoded() throws Exception { helpFunctionExecute("Exec CommonCustomers('new york', 'los angeles')", "CommonCustomers?city1=%27new%20york%27&city2=%27los%20angeles%27"); } private void helpUpdateExecute(String query, String expected, String expectedMethod, boolean checkPayload) throws Exception { Command cmd = this.utility.parseCommand(query); ODataUpdateVisitor visitor = new ODataUpdateVisitor(translator, utility.createRuntimeMetadata()); visitor.visitNode(cmd); if (!visitor.exceptions.isEmpty()) { throw visitor.exceptions.get(0); } String odataCmd = visitor.buildURL(); if (checkPayload) { assertNotNull(visitor.getPayload()); } if (printPayload) { System.out.println(visitor.getPayload()); } assertEquals(expected, odataCmd); assertEquals(expectedMethod, visitor.getMethod()); } @Test public void testInsert() throws Exception { helpUpdateExecute("INSERT INTO Regions (RegionID,RegionDescription) VALUES (10,'Asian')", "Regions", "POST", true); } @Test(expected=TranslatorException.class) public void testDeletewithoutPK() throws Exception { helpUpdateExecute("Delete From Regions", "Regions", "DELETE", false); } @Test public void testDelete() throws Exception { helpUpdateExecute("Delete From Regions where RegionID=10", "Regions(10)", "DELETE", false); } @Test(expected=TranslatorException.class) public void testDeleteOtherClause() throws Exception { helpUpdateExecute("Delete From Regions where RegionDescription='foo'", "Regions", "DELETE", false); } @Test public void testUpdate() throws Exception { helpUpdateExecute("UPDATE Regions SET RegionDescription='foo' WHERE RegionID=10", "Regions(10)", "PUT", true); } @Test(expected=TranslatorException.class) public void testUpdatewithoutPK() throws Exception { helpUpdateExecute("UPDATE Regions SET RegionDescription='foo'", "Regions(10)", "PATCH", true); } @Test(expected=TranslatorException.class) public void testUpdateOtherClause() throws Exception { helpUpdateExecute("UPDATE Regions SET RegionID=10 WHERE RegionDescription='foo'", "Regions(10)", "PATCH", true); } }