/***************************************************************** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.cayenne.access.translator.ejbql; import org.apache.cayenne.configuration.server.ServerRuntime; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.di.Inject; import org.apache.cayenne.ejbql.EJBQLCompiledExpression; import org.apache.cayenne.ejbql.EJBQLParser; import org.apache.cayenne.ejbql.EJBQLParserFactory; import org.apache.cayenne.query.EJBQLQuery; import org.apache.cayenne.query.SQLTemplate; import org.apache.cayenne.unit.di.server.CayenneProjects; import org.apache.cayenne.unit.di.server.ServerCase; import org.apache.cayenne.unit.di.server.UseServerRuntime; import org.junit.Test; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) public class EJBQLSelectTranslatorIT extends ServerCase { @Inject private ServerRuntime runtime; @Inject private DbAdapter adapter; private SQLTemplate translateSelect(String ejbql) { return translateSelect(ejbql, Collections.EMPTY_MAP); } private SQLTemplate translateSelect(String ejbql, final Map<Integer, Object> queryParameters) { EJBQLParser parser = EJBQLParserFactory.getParser(); EJBQLCompiledExpression select = parser.compile(ejbql, runtime.getDataDomain().getEntityResolver()); EJBQLQuery query = new EJBQLQuery(ejbql) { @Override public Map<Integer, Object> getPositionalParameters() { return queryParameters; } }; EJBQLTranslationContext tr = new EJBQLTranslationContext(runtime.getDataDomain().getEntityResolver(), query, select, new JdbcEJBQLTranslatorFactory(), adapter.getQuotingStrategy()); select.getExpression().visit(new EJBQLSelectTranslator(tr)); return tr.getQuery(); } @Test public void testSelectFrom() { SQLTemplate query = translateSelect("select a from Artist a"); String sql = query.getDefaultTemplate(); // column order is unpredictable, just need to ensure that they are all // there assertTrue(sql, sql.startsWith("SELECT")); assertTrue(sql, sql.indexOf("t0.ARTIST_ID") > 0); assertTrue(sql, sql.indexOf("t0.ARTIST_NAME") > 0); assertTrue(sql, sql.indexOf("t0.DATE_OF_BIRTH") > 0); assertTrue(sql, sql.endsWith(" FROM ARTIST t0")); } @Test public void testSelectMultipleJoinsToTheSameTable() throws Exception { SQLTemplate query = translateSelect("SELECT a " + "FROM Artist a JOIN a.paintingArray b JOIN a.paintingArray c " + "WHERE b.paintingTitle = 'P1' AND c.paintingTitle = 'P2'"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT")); assertTrue(sql, sql.indexOf("INNER JOIN PAINTING t1 ON (t0.ARTIST_ID = t1.ARTIST_ID)") > 0); assertTrue(sql, sql.indexOf("INNER JOIN PAINTING t2 ON (t0.ARTIST_ID = t2.ARTIST_ID)") > 0); } @Test public void testSelectImplicitColumnJoins() throws Exception { SQLTemplate query = translateSelect("SELECT a.paintingArray.toGallery.galleryName " + "FROM Artist a JOIN a.paintingArray b"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT")); // check that overlapping implicit and explicit joins did not result in // duplicates assertTrue(sql, sql.contains("INNER JOIN GALLERY")); assertTrue(sql, sql.contains("INNER JOIN PAINTING")); int i1 = sql.indexOf("INNER JOIN PAINTING"); assertTrue(sql, i1 >= 0); // TODO: andrus 1/6/2008 - this fails // int i2 = sql.indexOf("INNER JOIN PAINTING", i1 + 1); // assertTrue(sql, i2 < 0); } @Test public void testSelectDistinct() { SQLTemplate query = translateSelect("select distinct a from Artist a"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT DISTINCT ")); } @Test public void testSelectFromWhereEqual() { SQLTemplate query = translateSelect("select a from Artist a where a.artistName = 'Dali'"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT")); assertTrue(sql, sql.endsWith(" FROM ARTIST t0 WHERE t0.ARTIST_NAME =" + " #bind('Dali' 'VARCHAR')")); } @Test public void testSelectFromWhereOrEqual() { SQLTemplate query = translateSelect("select a from Artist a where a.artistName = 'Dali' " + "or a.artistName = 'Malevich'"); String sql = query.getDefaultTemplate(); SQLTemplate query1 = translateSelect("select a from Artist a where a.artistName = 'Picasso' " + "or a.artistName = 'Malevich' " + "or a.artistName = 'Dali'"); String sql1 = query1.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT")); assertTrue(sql, sql.indexOf(" FROM ARTIST t0 WHERE ") > 0); assertEquals(1, countDelimiters(sql, " OR ", sql.indexOf("WHERE "))); assertTrue(sql1, sql1.startsWith("SELECT")); assertTrue(sql1, sql.indexOf(" FROM ARTIST t0 WHERE ") > 0); assertEquals(2, countDelimiters(sql1, " OR ", sql.indexOf("WHERE "))); } @Test public void testSelectFromWhereAndEqual() { SQLTemplate query = translateSelect("select a from Artist a where a.artistName = 'Dali' " + "and a.artistName = 'Malevich'"); String sql = query.getDefaultTemplate(); SQLTemplate query1 = translateSelect("select a from Artist a where a.artistName = 'Picasso' " + "and a.artistName = 'Malevich' " + "and a.artistName = 'Dali'"); String sql1 = query1.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT")); assertTrue(sql, sql.indexOf("WHERE ") > 0); assertEquals(1, countDelimiters(sql, " AND ", sql.indexOf("WHERE "))); assertTrue(sql1, sql1.startsWith("SELECT")); assertTrue(sql1, sql1.indexOf("WHERE ") > 0); assertEquals(2, countDelimiters(sql1, " AND ", sql1.indexOf("WHERE "))); } @Test public void testSelectFromWhereNot() { SQLTemplate query = translateSelect("select a from Artist a where not (a.artistName = 'Dali')"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT")); assertTrue(sql, sql.endsWith("WHERE NOT " + "t0.ARTIST_NAME = #bind('Dali' 'VARCHAR')")); } @Test public void testSelectFromWhereGreater() { SQLTemplate query = translateSelect("select p from Painting p where p.estimatedPrice > 1.0"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT")); assertTrue(sql, sql.endsWith("WHERE t0.ESTIMATED_PRICE > #bind($id0 'DECIMAL')")); } @Test public void testSelectFromWhereGreaterOrEqual() { SQLTemplate query = translateSelect("select p from Painting p where p.estimatedPrice >= 2"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("WHERE t0.ESTIMATED_PRICE >= #bind($id0 'INTEGER')")); } @Test public void testSelectFromWhereLess() { SQLTemplate query = translateSelect("select p from Painting p where p.estimatedPrice < 1.0"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("WHERE t0.ESTIMATED_PRICE < #bind($id0 'DECIMAL')")); } @Test public void testSelectFromWhereLessOrEqual() { SQLTemplate query = translateSelect("select p from Painting p where p.estimatedPrice <= 1.0"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("WHERE t0.ESTIMATED_PRICE <= #bind($id0 'DECIMAL')")); } @Test public void testSelectFromWhereNotEqual() { SQLTemplate query = translateSelect("select a from Artist a where a.artistName <> 'Dali'"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("WHERE t0.ARTIST_NAME <> #bind('Dali' 'VARCHAR')")); } @Test public void testSelectFromWhereBetween() { SQLTemplate query = translateSelect("select p from Painting p where p.estimatedPrice between 3 and 5"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("WHERE t0.ESTIMATED_PRICE " + "BETWEEN #bind($id0 'INTEGER') AND #bind($id1 'INTEGER')")); } @Test public void testSelectFromWhereNotBetween() { SQLTemplate query = translateSelect("select p from Painting p where p.estimatedPrice not between 3 and 5"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("WHERE t0.ESTIMATED_PRICE " + "NOT BETWEEN #bind($id0 'INTEGER') AND #bind($id1 'INTEGER')")); } @Test public void testSelectFromWhereLike() { SQLTemplate query = translateSelect("select p from Painting p where p.paintingTitle like 'Stuff'"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("WHERE t0.PAINTING_TITLE " + "LIKE #bind('Stuff' 'VARCHAR')")); } @Test public void testSelectFromWhereNotLike() { SQLTemplate query = translateSelect("select p from Painting p where p.paintingTitle NOT like 'Stuff'"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("WHERE t0.PAINTING_TITLE " + "NOT LIKE #bind('Stuff' 'VARCHAR')")); } @Test public void testSelectPositionalParameters() { Map<Integer, Object> params = new HashMap<Integer, Object>(); params.put(new Integer(1), "X"); params.put(new Integer(2), "Y"); SQLTemplate query = translateSelect("select a from Artist a where a.artistName = ?1 or a.artistName = ?2", params); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.endsWith("t0.ARTIST_NAME = #bind($id0) OR t0.ARTIST_NAME = #bind($id1)")); } @Test public void testMax() { SQLTemplate query = translateSelect("select max(p.estimatedPrice) from Painting p"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT " + "#result('MAX(t0.ESTIMATED_PRICE)' 'java.math.BigDecimal' 'sc0') " + "FROM PAINTING t0")); } @Test public void testDistinctSum() { SQLTemplate query = translateSelect("select sum( distinct p.estimatedPrice) from Painting p"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT #result('SUM(DISTINCT t0.ESTIMATED_PRICE)' 'java.math.BigDecimal' 'sc0') " + "FROM PAINTING t0")); } @Test public void testColumnPaths() { SQLTemplate query = translateSelect("select p.estimatedPrice, p.toArtist.artistName from Painting p"); String sql = query.getDefaultTemplate(); assertTrue(sql, sql.startsWith("SELECT " + "#result('t0.ESTIMATED_PRICE' 'java.math.BigDecimal' 'sc0' 'sc0' 3), " + "#result('t1.ARTIST_NAME' 'java.lang.String' 'sc1' 'sc1' 1) FROM")); } private int countDelimiters(String string, String delim, int fromIndex) { int i = 0; while ((fromIndex = string.indexOf(delim, fromIndex)) >= 0) { fromIndex += delim.length(); i++; } return i; } // if parameter value is null (in this test x := null) we will generate // "IS NULL" @Test public void testEqualsNullParameter() { String ejbql = "select p from Painting p WHERE p.toArtist=:x"; EJBQLParser parser = EJBQLParserFactory.getParser(); EJBQLCompiledExpression select = parser.compile(ejbql, runtime.getDataDomain().getEntityResolver()); EJBQLQuery query = new EJBQLQuery(ejbql); query.setParameter("x", null); EJBQLTranslationContext tr = new EJBQLTranslationContext(runtime.getDataDomain().getEntityResolver(), query, select, new JdbcEJBQLTranslatorFactory(), adapter.getQuotingStrategy()); select.getExpression().visit(new EJBQLSelectTranslator(tr)); String sql = tr.getQuery().getDefaultTemplate(); assertTrue(sql, sql.endsWith("t0.ARTIST_ID IS NULL")); } // if parameter value is null and more than one parameter in query @Test public void testEqualsNullAndNotNullParameter() { String ejbql = "select p from Painting p WHERE p.toArtist=:x OR p.toArtist.artistName=:b"; EJBQLParser parser = EJBQLParserFactory.getParser(); EJBQLCompiledExpression select = parser.compile(ejbql, runtime.getDataDomain().getEntityResolver()); EJBQLQuery query = new EJBQLQuery(ejbql); query.setParameter("x", null); query.setParameter("b", "Y"); EJBQLTranslationContext tr = new EJBQLTranslationContext(runtime.getDataDomain().getEntityResolver(), query, select, new JdbcEJBQLTranslatorFactory(), adapter.getQuotingStrategy()); select.getExpression().visit(new EJBQLSelectTranslator(tr)); String sql = tr.getQuery().getDefaultTemplate(); assertTrue(sql, sql.endsWith("t0.ARTIST_ID IS NULL OR t1.ARTIST_NAME = #bind($id0)")); } }