/* * 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.salesforce.execution.visitors; import static org.junit.Assert.*; import java.io.FileReader; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.TimeZone; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.teiid.adminapi.impl.ModelMetaData; import org.teiid.cdk.api.TranslationUtility; import org.teiid.core.types.DataTypeManager; import org.teiid.core.util.TimestampWithTimezone; import org.teiid.core.util.UnitTestUtil; import org.teiid.language.Command; import org.teiid.language.Select; import org.teiid.language.visitor.SQLStringVisitor; import org.teiid.metadata.Column; import org.teiid.metadata.FunctionMethod; import org.teiid.metadata.MetadataFactory; import org.teiid.metadata.Procedure; import org.teiid.metadata.ProcedureParameter; import org.teiid.metadata.RuntimeMetadata; import org.teiid.metadata.Table; import org.teiid.query.metadata.MetadataValidator; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.metadata.SystemMetadata; import org.teiid.query.metadata.TransformationMetadata; import org.teiid.query.parser.QueryParser; import org.teiid.query.sql.lang.SPParameter; import org.teiid.query.unittest.RealMetadataFactory; import org.teiid.query.validator.ValidatorReport; import org.teiid.translator.Execution; import org.teiid.translator.ExecutionContext; import org.teiid.translator.TypeFacility; import org.teiid.translator.salesforce.SalesForceExecutionFactory; import org.teiid.translator.salesforce.SalesForceMetadataProcessor; import org.teiid.translator.salesforce.SalesforceConnection; import org.teiid.translator.salesforce.Util; import org.teiid.translator.salesforce.execution.QueryExecutionImpl; import com.sforce.soap.partner.QueryResult; import com.sforce.soap.partner.sobject.SObject; @SuppressWarnings("nls") public class TestVisitors { public static QueryMetadataInterface exampleSalesforce() { try { ModelMetaData mmd = new ModelMetaData(); mmd.setName("SalesforceModel"); MetadataFactory mf = new MetadataFactory("sf", 1, SystemMetadata.getInstance().getRuntimeTypeMap(), mmd); mf.setParser(new QueryParser()); //load the metadata as captured from 8.9 on 9/3/2014 mf.parse(new FileReader(UnitTestUtil.getTestDataFile("sf.ddl"))); SalesForceExecutionFactory factory = new SalesForceExecutionFactory(); factory.start(); for (FunctionMethod func : factory.getPushDownFunctions()) { mf.addFunction(func); } SalesForceMetadataProcessor.addProcedrues(mf); // Create Contacts group - which has different name in sources Table contactTable = RealMetadataFactory.createPhysicalGroup("Contacts", mf.getSchema()); //$NON-NLS-1$ contactTable.setNameInSource("Contact"); //$NON-NLS-1$ contactTable.setProperty("Supports Query", Boolean.TRUE.toString()); //$NON-NLS-1$ // Create Contact Columns String[] elemNames = new String[] { "ContactID", "Name", "AccountId", "InitialContact", "LastTime" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ }; String[] elemTypes = new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.TIMESTAMP, DataTypeManager.DefaultDataTypes.TIME }; List<Column> contactCols = RealMetadataFactory.createElements(contactTable, elemNames, elemTypes); // Set name in source on each column String[] contactNameInSource = new String[] { "id", "ContactName", "accountid", "InitialContact", "LastTime" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ }; for(int i=0; i<2; i++) { Column obj = contactCols.get(i); obj.setNameInSource(contactNameInSource[i]); } //add a procedure with a native query property List<ProcedureParameter> params = new LinkedList<ProcedureParameter>(); params.add(RealMetadataFactory.createParameter("x", SPParameter.IN, TypeFacility.RUNTIME_NAMES.STRING)); Procedure nativeProc = RealMetadataFactory.createStoredProcedure("foo", mf.getSchema(), params); nativeProc.setProperty(SQLStringVisitor.TEIID_NATIVE_QUERY, "search;select accountname from account where accountid = $1"); nativeProc.setResultSet(RealMetadataFactory.createResultSet("rs", new String[] {"accountname"}, new String[] {TypeFacility.RUNTIME_NAMES.STRING})); TransformationMetadata tm = RealMetadataFactory.createTransformationMetadata(mf.asMetadataStore(), "x"); ValidatorReport report = new MetadataValidator().validate(tm.getVdbMetaData(), tm.getMetadataStore()); if (report.hasItems()) { throw new RuntimeException(report.getFailureMessage()); } return tm; } catch (Exception e) { throw new RuntimeException(e); } } private static TranslationUtility translationUtility = new TranslationUtility(exampleSalesforce()); @Test public void testOr() throws Exception { String sql = "select id from Account where Name = 'foo' or BillingStreet = 'bar'"; helpTest(sql, "SELECT Account.Id FROM Account WHERE (Account.Name = 'foo') OR (Account.BillingStreet = 'bar')"); } @Test public void testNot() throws Exception { String sql = "select Account.id, Account.Name, Account.Industry from Account where not (Name = 'foo' and BillingStreet = 'bar')"; //$NON-NLS-1$ helpTest(sql, "SELECT Account.Id, Account.Name, Account.Industry FROM Account WHERE (Account.Name != 'foo') OR (Account.BillingStreet != 'bar')"); } @Test public void testCountStar() throws Exception { String sql = "select count(*) from Account"; helpTest(sql,"SELECT COUNT(Id) FROM Account"); } @Test public void testNotLike() throws Exception { Select command = (Select)translationUtility.parseCommand("select id from Account where Name not like '%foo' or BillingCity = 'bar'"); //$NON-NLS-1$ SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Account.Id FROM Account WHERE (NOT (Account.Name LIKE '%foo')) OR (Account.BillingCity = 'bar')", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testIN() throws Exception { Select command = (Select)translationUtility.parseCommand("select id from Account where Industry IN (1,2,3)"); //$NON-NLS-1$ SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertFalse(visitor.hasOnlyIDCriteria()); assertEquals("SELECT Account.Id FROM Account WHERE Account.Industry IN('1','2','3')", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testOnlyIDsIN() throws Exception { // this can resolve to a better performing retrieve call Select command = (Select)translationUtility.parseCommand("select id, name from Account where ID IN (1,2,3)"); //$NON-NLS-1$ SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertTrue(visitor.hasOnlyIdInCriteria()); assertEquals("Account", visitor.getTableName()); assertEquals("Account.Id, Account.Name", visitor.getRetrieveFieldList()); assertEquals(Arrays.asList(new String[]{"1", "2", "3"}), visitor.getIdInCriteria()); } @Test public void testJoin() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Account.Name, Contact.Name FROM Contact LEFT OUTER JOIN Account ON Account.Id = Contact.AccountId"); //$NON-NLS-1$ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Account.Name, Contact.Name FROM Contact", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testJoin2() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Account.Name, Contact.Name FROM Account LEFT OUTER JOIN Contact ON Account.Id = Contact.AccountId"); //$NON-NLS-1$ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Account.Name, (SELECT Contact.Name FROM Contacts) FROM Account", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testJoin3() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Contact.Name FROM Account LEFT OUTER JOIN Contact ON Account.Id = Contact.AccountId"); //$NON-NLS-1$ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT (SELECT Contact.Name FROM Contacts) FROM Account", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testJoin4() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Contact.Name FROM Account INNER JOIN Contact ON Account.Id = Contact.AccountId WHERE Contact.Name='foo' AND Account.Id=5"); //$NON-NLS-1$ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Contact.Name FROM Contact WHERE ((Contact.Name = 'foo') AND (Account.Id = '5')) AND (Contact.AccountId != NULL)", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testInnerJoin() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Account.Phone, Account.Name, Account.Type, Contact.LastName FROM Account inner join Contact on Account.Id = Contact.AccountId"); //$NON-NLS-1$ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Account.Phone, Account.Name, Account.Type, Contact.LastName FROM Contact WHERE Contact.AccountId != NULL", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testInnerJoin1() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Account.Phone, Account.Name, Account.Type, Contact.LastName FROM Contact inner join Account on Account.Id = Contact.AccountId"); //$NON-NLS-1$ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Account.Phone, Account.Name, Account.Type, Contact.LastName FROM Contact WHERE Contact.AccountId != NULL", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testInnerJoin2() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Account.Phone, Account.Name, Account.Type, Contact.LastName FROM Contact inner join Account on Contact.AccountId = Account.Id"); //$NON-NLS-1$ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Account.Phone, Account.Name, Account.Type, Contact.LastName FROM Contact WHERE Contact.AccountId != NULL", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testInWithNameInSourceDifferent() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Contacts.Name FROM Contacts WHERE Contacts.Name in ('x', 'y')"); //$NON-NLS-1$ SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Contact.ContactName FROM Contact WHERE Contact.ContactName IN('x','y')", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testEqualsElement() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Contact.Name FROM Contact WHERE Contact.Name = Contact.AccountId"); //$NON-NLS-1$ SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Contact.Name FROM Contact WHERE Contact.Name = Contact.AccountId", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testIsNull() throws Exception { Select command = (Select)translationUtility.parseCommand("SELECT Contact.Name FROM Contact WHERE Contact.Name is not null"); //$NON-NLS-1$ SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata()); visitor.visit(command); assertEquals("SELECT Contact.Name FROM Contact WHERE Contact.Name != NULL", visitor.getQuery().toString().trim()); //$NON-NLS-1$ } @Test public void testIDCriteria() throws Exception { Select command = (Select)translationUtility.parseCommand("select id, name from Account where id = 'bar'"); //$NON-NLS-1$ SalesforceConnection sfc = Mockito.mock(SalesforceConnection.class); Mockito.stub(sfc.retrieve("Account.Id, Account.Name", "Account", Arrays.asList("bar"))).toReturn(new SObject[] {null}); QueryExecutionImpl qei = new QueryExecutionImpl(command, sfc, translationUtility.createRuntimeMetadata(), Mockito.mock(ExecutionContext.class), new SalesForceExecutionFactory()); qei.execute(); Mockito.verify(sfc).retrieve("Account.Id, Account.Name", "Account", Arrays.asList("bar")); } @BeforeClass static public void oneTimeSetup() { Util.resetTimeZone(); TimestampWithTimezone.resetCalendar(TimeZone.getTimeZone("GMT-06:00")); } @AfterClass static public void oneTimeTearDown() { Util.resetTimeZone(); TimestampWithTimezone.resetCalendar(null); } @Test public void testDateTimeFormating() throws Exception { String sql = "select id from clientbrowser where LastUpdate = {ts'2003-03-11 11:42:10.5'}"; String source = "SELECT ClientBrowser.Id FROM ClientBrowser WHERE ClientBrowser.LastUpdate = 2003-03-11T11:42:10.500-06:00"; helpTest(sql, source); } @Test public void testDateTimeFormating1() throws Exception { String sql = "select id from clientbrowser where LastUpdate in ({ts'2003-03-11 11:42:10.506'}, {ts'2003-03-11 11:42:10.8088'})"; String source = "SELECT ClientBrowser.Id FROM ClientBrowser WHERE ClientBrowser.LastUpdate IN(2003-03-11T11:42:10.506-06:00,2003-03-11T11:42:10.80-06:00)"; helpTest(sql, source); } @Test public void testTimeFormatting() throws Exception { String sql = "select name from BusinessHours where SundayStartTime = {t'11:42:10'}"; String source = "SELECT BusinessHours.Name FROM BusinessHours WHERE BusinessHours.SundayStartTime = 11:42:10.000-06:00"; helpTest(sql, source); } @Test public void testAggregateSelect() throws Exception { String sql = "select max(name), count(1) from contact"; String source = "SELECT MAX(Contact.Name), COUNT(Id) FROM Contact"; helpTest(sql, source); } @Test public void testAggregateGroupByHaving() throws Exception { String sql = "select max(name), EmailBouncedDate from contact group by EmailBouncedDate having min(LastCUUpdateDate) in ({ts'2003-03-11 11:42:10.506'}, {ts'2003-03-11 11:42:10.8088'})"; String source = "SELECT MAX(Contact.Name), Contact.EmailBouncedDate FROM Contact GROUP BY Contact.EmailBouncedDate HAVING MIN(Contact.LastCUUpdateDate) IN(2003-03-11T11:42:10.506-06:00,2003-03-11T11:42:10.80-06:00)"; helpTest(sql, source); } @Test public void testPluralChild() throws Exception { String sql = "select Opportunity.CloseDate as Opportunity_CloseDate1 from Campaign Campaign LEFT OUTER JOIN Opportunity Opportunity ON Campaign.Id = Opportunity.CampaignId"; String source = "SELECT (SELECT Opportunity.CloseDate FROM Opportunities) FROM Campaign"; helpTest(sql, source); } @Test public void testParentName() throws Exception { String sql = "select Product2Feed.ParentId as Product2Feed_ParentId, Product2.Description as Product2_Description, Product2Feed.Title as Product2Feed_Title from SalesForceModel.Product2Feed Product2Feed LEFT OUTER JOIN SalesForceModel.Product2 Product2 ON Product2Feed.ParentId = Product2.Id"; String source = "SELECT Product2Feed.ParentId, Parent.Description, Product2Feed.Title FROM Product2Feed"; helpTest(sql, source); } private void helpTest(String sql, String expected) throws Exception { Command command = translationUtility.parseCommand(sql); SalesForceExecutionFactory factory = new SalesForceExecutionFactory(); ExecutionContext ec = Mockito.mock(ExecutionContext.class); RuntimeMetadata rm = Mockito.mock(RuntimeMetadata.class); SalesforceConnection connection = Mockito.mock(SalesforceConnection.class); ArgumentCaptor<String> queryArgument = ArgumentCaptor.forClass(String.class); QueryResult qr = Mockito.mock(QueryResult.class); Mockito.stub(connection.query(queryArgument.capture(), Mockito.anyInt(), Mockito.anyBoolean())).toReturn(qr); Execution execution = factory.createExecution(command, ec, rm, connection); execution.execute(); Mockito.verify(connection, Mockito.times(1)).query(queryArgument.capture(), Mockito.anyInt(), Mockito.anyBoolean()); assertEquals(expected, queryArgument.getValue().trim()); } @Test public void testNativeProc() throws Exception { String sql = "exec foo('1')"; String source = "select accountname from account where accountid = '1'"; helpTest(sql, source); } @Test public void testPluralNameFromKey() throws Exception { String sql = "SELECT CaseSolution.SolutionId, Case_.Origin FROM Case_ LEFT OUTER JOIN CaseSolution ON Case_.Id = CaseSolution.CaseId"; String source = "SELECT Case.Origin, (SELECT CaseSolution.SolutionId FROM CaseSolutions) FROM Case"; helpTest(sql, source); } @Test public void testInMulti() throws Exception { String sql = "select id from idea where categories in ('a', 'b')"; String source = "SELECT Idea.Id FROM Idea WHERE Categories includes('a','b')"; helpTest(sql, source); sql = "select id from idea where categories not in ('a', 'b')"; source = "SELECT Idea.Id FROM Idea WHERE Categories EXCLUDES('a','b')"; helpTest(sql, source); } @Test public void testIncludExclude() throws Exception { String sql = "select id from idea where includes(categories, 'a,b')"; String source = "SELECT Idea.Id FROM Idea WHERE Categories includes('a','b')"; helpTest(sql, source); sql = "select id from idea where excludes(categories, 'a')"; source = "SELECT Idea.Id FROM Idea WHERE Categories EXCLUDES('a')"; helpTest(sql, source); } @Test public void testFloatingLiteral() throws Exception { String sql = "SELECT COUNT(*) FROM Opportunity where amount > 100000000"; String source = "SELECT COUNT(Id) FROM Opportunity WHERE Opportunity.Amount > 100000000"; helpTest(sql, source); } }