Repository: metamodel Updated Branches: refs/heads/master 04c0baa47 -> d17a10dd0
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/jdbc/src/main/java/org/apache/metamodel/jdbc/SplitQueriesDataSet.java ---------------------------------------------------------------------- diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/SplitQueriesDataSet.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/SplitQueriesDataSet.java index f9c3697..cb2d7fa 100644 --- a/jdbc/src/main/java/org/apache/metamodel/jdbc/SplitQueriesDataSet.java +++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/SplitQueriesDataSet.java @@ -30,6 +30,7 @@ import org.apache.metamodel.MetaModelException; import org.apache.metamodel.data.AbstractDataSet; import org.apache.metamodel.data.DataSet; import org.apache.metamodel.data.Row; +import org.apache.metamodel.data.WrappingDataSet; import org.apache.metamodel.query.Query; import org.apache.metamodel.query.SelectItem; @@ -38,7 +39,7 @@ import org.apache.metamodel.query.SelectItem; * * @see org.apache.metamodel.jdbc.QuerySplitter */ -final class SplitQueriesDataSet extends AbstractDataSet { +final class SplitQueriesDataSet extends AbstractDataSet implements WrappingDataSet { private static final Logger logger = LoggerFactory.getLogger(SplitQueriesDataSet.class); private final DataContext _dataContext; @@ -63,6 +64,11 @@ final class SplitQueriesDataSet extends AbstractDataSet { } @Override + public DataSet getWrappedDataSet() { + return _currentDataSet; + } + + @Override public void close() { if (_currentDataSet != null) { logger.debug("currentDataSet.close()"); http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/AbstractQueryRewriter.java ---------------------------------------------------------------------- diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/AbstractQueryRewriter.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/AbstractQueryRewriter.java index 05453df..cf8cfd8 100644 --- a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/AbstractQueryRewriter.java +++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/AbstractQueryRewriter.java @@ -32,6 +32,7 @@ import org.apache.metamodel.query.OperatorType; import org.apache.metamodel.query.OrderByClause; import org.apache.metamodel.query.OrderByItem; import org.apache.metamodel.query.Query; +import org.apache.metamodel.query.ScalarFunction; import org.apache.metamodel.query.SelectClause; import org.apache.metamodel.query.SelectItem; import org.apache.metamodel.schema.ColumnType; @@ -58,7 +59,7 @@ public abstract class AbstractQueryRewriter implements IQueryRewriter { public JdbcDataContext getDataContext() { return _dataContext; } - + @Override public boolean isTransactional() { return true; @@ -104,7 +105,7 @@ public abstract class AbstractQueryRewriter implements IQueryRewriter { public String rewriteColumnType(ColumnType columnType, Integer columnSize) { return rewriteColumnTypeInternal(columnType.toString(), columnSize); } - + protected String rewriteColumnTypeInternal(String columnType, Object columnParameter) { final StringBuilder sb = new StringBuilder(); sb.append(columnType); @@ -264,6 +265,14 @@ public abstract class AbstractQueryRewriter implements IQueryRewriter { if (i != 0) { sb.append(AbstractQueryClause.DELIM_COMMA); } + + final ScalarFunction scalarFunction = item.getScalarFunction(); + if (scalarFunction != null && !isScalarFunctionSupported(scalarFunction)) { + // replace with a SelectItem without the function - the + // function will be applied in post-processing. + item = item.replaceFunction(null); + } + sb.append(rewriteSelectItem(query, item)); } } http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java ---------------------------------------------------------------------- diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java index 288bf78..56e10c3 100644 --- a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java +++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java @@ -26,6 +26,7 @@ import org.apache.metamodel.query.FilterItem; import org.apache.metamodel.query.FromItem; import org.apache.metamodel.query.OperatorType; import org.apache.metamodel.query.Query; +import org.apache.metamodel.query.ScalarFunction; import org.apache.metamodel.query.SelectItem; import org.apache.metamodel.schema.ColumnType; import org.apache.metamodel.util.CollectionUtils; @@ -144,6 +145,11 @@ public class DefaultQueryRewriter extends AbstractQueryRewriter { } return super.rewriteFilterItem(item); } + + @Override + public boolean isScalarFunctionSupported(ScalarFunction function) { + return false; + } @Override public boolean isFirstRowSupported() { http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java ---------------------------------------------------------------------- diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java index 061ee8b..9562b67 100644 --- a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java +++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java @@ -24,6 +24,7 @@ import org.apache.metamodel.jdbc.JdbcDataContext; import org.apache.metamodel.query.FilterItem; import org.apache.metamodel.query.FromItem; import org.apache.metamodel.query.Query; +import org.apache.metamodel.query.ScalarFunction; import org.apache.metamodel.schema.ColumnType; /** @@ -62,6 +63,18 @@ public interface IQueryRewriter { public boolean isFirstRowSupported(); /** + * Determines whether a specific scalar function is supported by the + * database or not. + * + * If the function is not supported then MetaModel will handle the function + * on the client side. + * + * @param function + * @return + */ + public boolean isScalarFunctionSupported(ScalarFunction function); + + /** * Escapes the quotes within a String literal of a query item. * * @return String item with quotes escaped. http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java ---------------------------------------------------------------------- diff --git a/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java b/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java index 05ab057..fe93a40 100644 --- a/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java +++ b/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java @@ -24,6 +24,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -113,8 +114,8 @@ public class JdbcDataContextTest extends JdbcTestCase { EasyMock.verify(ds); - String assertionFailMsg = "Expected 5x true: " + con1.isClosed() + "," + con2.isClosed() + "," - + con3.isClosed() + "," + con4.isClosed() + "," + con5.isClosed(); + String assertionFailMsg = "Expected 5x true: " + con1.isClosed() + "," + con2.isClosed() + "," + con3.isClosed() + + "," + con4.isClosed() + "," + con5.isClosed(); assertTrue(assertionFailMsg, con1.isClosed()); assertTrue(assertionFailMsg, con2.isClosed()); @@ -136,20 +137,22 @@ public class JdbcDataContextTest extends JdbcTestCase { assertEquals( "SELECT a._CUSTOMERNUMBER_, a._CUSTOMERNAME_, a._CONTACTLASTNAME_, a._CONTACTFIRSTNAME_, a._PHONE_, " + "a._ADDRESSLINE1_, a._ADDRESSLINE2_, a._CITY_, a._STATE_, a._POSTALCODE_, a._COUNTRY_, " - + "a._SALESREPEMPLOYEENUMBER_, a._CREDITLIMIT_ FROM PUBLIC._CUSTOMERS_ a", q.toString() - .replace('\"', '_')); + + "a._SALESREPEMPLOYEENUMBER_, a._CREDITLIMIT_ FROM PUBLIC._CUSTOMERS_ a", + q.toString().replace('\"', '_')); DataSet result = strategy.executeQuery(q); assertTrue(result.next()); assertEquals( "Row[values=[103, Atelier graphique, Schmitt, Carine, 40.32.2555, 54, rue Royale, null, Nantes, null, " - + "44000, France, 1370, 21000.0]]", result.getRow().toString()); + + "44000, France, 1370, 21000.0]]", + result.getRow().toString()); assertTrue(result.next()); assertTrue(result.next()); assertTrue(result.next()); assertTrue(result.next()); assertEquals( "Row[values=[121, Baane Mini Imports, Bergulfsen, Jonas, 07-98 9555, Erling Skakkes gate 78, null, " - + "Stavern, null, 4110, Norway, 1504, 81700.0]]", result.getRow().toString()); + + "Stavern, null, 4110, Norway, 1504, 81700.0]]", + result.getRow().toString()); result.close(); } @@ -181,12 +184,14 @@ public class JdbcDataContextTest extends JdbcTestCase { + "a._ADDRESSLINE1_, a._ADDRESSLINE2_, a._CITY_, " + "a._STATE_, a._POSTALCODE_, a._COUNTRY_, a._SALESREPEMPLOYEENUMBER_, " + "a._CREDITLIMIT_ FROM PUBLIC._CUSTOMERS_ a WHERE a._CUSTOMERNUMBER_ = ? " - + "AND a._CUSTOMERNAME_ = ?", compliedQueryString.replace('\"', '_')); + + "AND a._CUSTOMERNAME_ = ?", + compliedQueryString.replace('\"', '_')); DataSet result1 = dataContext.executeQuery(compiledQuery, new Object[] { 103, "Atelier graphique" }); assertTrue(result1.next()); assertEquals( "Row[values=[103, Atelier graphique, Schmitt, Carine, 40.32.2555, 54, rue Royale, null, Nantes, null, " - + "44000, France, 1370, 21000.0]]", result1.getRow().toString()); + + "44000, France, 1370, 21000.0]]", + result1.getRow().toString()); assertFalse(result1.next()); assertEquals(1, jdbcCompiledQuery.getActiveLeases()); @@ -213,6 +218,42 @@ public class JdbcDataContextTest extends JdbcTestCase { assertEquals(0, jdbcCompiledQuery.getIdleLeases()); } + public void testSelectScalarFunction() throws Exception { + final Connection connection = getTestDbConnection(); + final JdbcDataContext dataContext = new JdbcDataContext(connection); + final DataSet dataSet = dataContext.query().from("customers").select("creditlimit") + .select(FunctionType.TO_DATE, "creditlimit").select("creditlimit").limit(2).execute(); + try { + assertEquals("[_CUSTOMERS_._CREDITLIMIT_, TO_DATE(_CUSTOMERS_._CREDITLIMIT_), _CUSTOMERS_._CREDITLIMIT_]", + Arrays.toString(dataSet.getSelectItems()).replaceAll("\"", "_")); + + assertTrue(dataSet.next()); + final Object value0 = dataSet.getRow().getValue(0); + assertTrue("Expected a number but got: " + value0, value0 instanceof Number); + final Object value1 = dataSet.getRow().getValue(1); + assertTrue("Expected a date but got: " + value1, value1 instanceof Date); + final Object value2 = dataSet.getRow().getValue(2); + assertTrue("Expected a number but got: " + value2, value2 instanceof Number); + } finally { + dataSet.close(); + } + } + + public void testWhereScalarFunction() throws Exception { + final Connection connection = getTestDbConnection(); + final JdbcDataContext dataContext = new JdbcDataContext(connection); + try { + dataContext.query().from("customers").select("customernumber").where(FunctionType.TO_BOOLEAN, "creditlimit") + .eq(true).limit(2).execute(); + fail("Exception expected"); + } catch (MetaModelException e) { + assertEquals( + "Scalar functions outside of SELECT clause is not supported for JDBC databases. Query rejected: " + + "SELECT \"CUSTOMERS\".\"CUSTOMERNUMBER\" FROM PUBLIC.\"CUSTOMERS\" WHERE TO_BOOLEAN(\"CUSTOMERS\".\"CREDITLIMIT\") = TRUE", + e.getMessage()); + } + } + public void testExecuteQueryWithComparisonGreaterThanOrEquals() throws Exception { Connection connection = getTestDbConnection(); JdbcDataContext dataContext = new JdbcDataContext(connection, @@ -341,8 +382,8 @@ public class JdbcDataContextTest extends JdbcTestCase { public void testParseColumns() throws Exception { Connection connection = getTestDbConnection(); - JdbcDataContext dc = new JdbcDataContext(connection, new TableType[] { TableType.OTHER, - TableType.GLOBAL_TEMPORARY }, null); + JdbcDataContext dc = new JdbcDataContext(connection, + new TableType[] { TableType.OTHER, TableType.GLOBAL_TEMPORARY }, null); Schema schema = dc.getDefaultSchema(); Table customersTable = schema.getTableByName("CUSTOMERS"); Column[] columns = customersTable.getColumns(); @@ -406,24 +447,24 @@ public class JdbcDataContextTest extends JdbcTestCase { EasyMock.expect(mockCon.getAutoCommit()).andReturn(true); - EasyMock.expect(mockCon.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)).andReturn( - mockStatement); + EasyMock.expect(mockCon.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) + .andReturn(mockStatement); EasyMock.expect(mockStatement.getFetchSize()).andReturn(10); mockStatement.setFetchSize(EasyMock.anyInt()); mockStatement.setMaxRows(3); EasyMock.expectLastCall().andThrow(new SQLException("I wont allow max rows")); - EasyMock.expect( - mockStatement - .executeQuery("SELECT a.\"CUSTOMERNUMBER\", a.\"CUSTOMERNAME\", a.\"CONTACTLASTNAME\", a.\"CONTACTFIRSTNAME\", " - + "a.\"PHONE\", a.\"ADDRESSLINE1\", a.\"ADDRESSLINE2\", a.\"CITY\", a.\"STATE\", " - + "a.\"POSTALCODE\", a.\"COUNTRY\", a.\"SALESREPEMPLOYEENUMBER\", " - + "a.\"CREDITLIMIT\" FROM PUBLIC.\"CUSTOMERS\" a")).andReturn( - realStatement.executeQuery("SELECT a.\"CUSTOMERNUMBER\", a.\"CUSTOMERNAME\", a.\"CONTACTLASTNAME\", " - + "a.\"CONTACTFIRSTNAME\", a.\"PHONE\", a.\"ADDRESSLINE1\", a.\"ADDRESSLINE2\", a.\"CITY\", " - + "a.\"STATE\", a.\"POSTALCODE\", a.\"COUNTRY\", a.\"SALESREPEMPLOYEENUMBER\", " - + "a.\"CREDITLIMIT\" FROM PUBLIC.\"CUSTOMERS\" a")); + EasyMock.expect(mockStatement.executeQuery( + "SELECT a.\"CUSTOMERNUMBER\", a.\"CUSTOMERNAME\", a.\"CONTACTLASTNAME\", a.\"CONTACTFIRSTNAME\", " + + "a.\"PHONE\", a.\"ADDRESSLINE1\", a.\"ADDRESSLINE2\", a.\"CITY\", a.\"STATE\", " + + "a.\"POSTALCODE\", a.\"COUNTRY\", a.\"SALESREPEMPLOYEENUMBER\", " + + "a.\"CREDITLIMIT\" FROM PUBLIC.\"CUSTOMERS\" a")) + .andReturn(realStatement + .executeQuery("SELECT a.\"CUSTOMERNUMBER\", a.\"CUSTOMERNAME\", a.\"CONTACTLASTNAME\", " + + "a.\"CONTACTFIRSTNAME\", a.\"PHONE\", a.\"ADDRESSLINE1\", a.\"ADDRESSLINE2\", a.\"CITY\", " + + "a.\"STATE\", a.\"POSTALCODE\", a.\"COUNTRY\", a.\"SALESREPEMPLOYEENUMBER\", " + + "a.\"CREDITLIMIT\" FROM PUBLIC.\"CUSTOMERS\" a")); mockStatement.close(); @@ -437,15 +478,17 @@ public class JdbcDataContextTest extends JdbcTestCase { Table table = schema.getTables()[0]; q.from(table, "a"); q.select(table.getColumns()); - assertEquals("SELECT a.\"CUSTOMERNUMBER\", a.\"CUSTOMERNAME\", a.\"CONTACTLASTNAME\", a.\"CONTACTFIRSTNAME\", " - + "a.\"PHONE\", a.\"ADDRESSLINE1\", a.\"ADDRESSLINE2\", a.\"CITY\", a.\"STATE\", a.\"POSTALCODE\", " - + "a.\"COUNTRY\", a.\"SALESREPEMPLOYEENUMBER\", a.\"CREDITLIMIT\" FROM PUBLIC.\"CUSTOMERS\" a", + assertEquals( + "SELECT a.\"CUSTOMERNUMBER\", a.\"CUSTOMERNAME\", a.\"CONTACTLASTNAME\", a.\"CONTACTFIRSTNAME\", " + + "a.\"PHONE\", a.\"ADDRESSLINE1\", a.\"ADDRESSLINE2\", a.\"CITY\", a.\"STATE\", a.\"POSTALCODE\", " + + "a.\"COUNTRY\", a.\"SALESREPEMPLOYEENUMBER\", a.\"CREDITLIMIT\" FROM PUBLIC.\"CUSTOMERS\" a", q.toString()); DataSet result = dc.executeQuery(q); assertTrue(result.next()); assertEquals( "Row[values=[103, Atelier graphique, Schmitt, Carine, 40.32.2555, 54, rue Royale, null, Nantes, null, " - + "44000, France, 1370, 21000.0]]", result.getRow().toString()); + + "44000, France, 1370, 21000.0]]", + result.getRow().toString()); assertTrue(result.next()); assertTrue(result.next()); assertFalse(result.next()); @@ -542,8 +585,8 @@ public class JdbcDataContextTest extends JdbcTestCase { q.select(countrySelect, new SelectItem(FunctionType.SUM, creditLimitColumn)); q.groupBy(countryColumn); q.orderBy(new OrderByItem(countrySelect)); - q.where(new FilterItem(new SelectItem(employeeNumberColumn1), OperatorType.EQUALS_TO, new SelectItem( - employeeNumberColumn2))); + q.where(new FilterItem(new SelectItem(employeeNumberColumn1), OperatorType.EQUALS_TO, + new SelectItem(employeeNumberColumn2))); assertEquals("SELECT c.\"COUNTRY\", SUM(c.\"CREDITLIMIT\") FROM PUBLIC.\"CUSTOMERS\" c, " + "PUBLIC.\"EMPLOYEES\" o WHERE c.\"SALESREPEMPLOYEENUMBER\" = o.\"EMPLOYEENUMBER\" GROUP BY c" @@ -576,8 +619,8 @@ public class JdbcDataContextTest extends JdbcTestCase { ds.setTimeBetweenEvictionRunsMillis(-1); ds.setDefaultTransactionIsolation(java.sql.Connection.TRANSACTION_READ_COMMITTED); - final JdbcDataContext dataContext = new JdbcDataContext(ds, - new TableType[] { TableType.TABLE, TableType.VIEW }, null); + final JdbcDataContext dataContext = new JdbcDataContext(ds, new TableType[] { TableType.TABLE, TableType.VIEW }, + null); final JdbcCompiledQuery compiledQuery = (JdbcCompiledQuery) dataContext.query().from("CUSTOMERS") .select("CUSTOMERNAME").where("CUSTOMERNUMBER").eq(new QueryParameter()).compile(); @@ -587,8 +630,7 @@ public class JdbcDataContextTest extends JdbcTestCase { final String compliedQueryString = compiledQuery.toSql(); - assertEquals( - "SELECT _CUSTOMERS_._CUSTOMERNAME_ FROM PUBLIC._CUSTOMERS_ WHERE _CUSTOMERS_._CUSTOMERNUMBER_ = ?", + assertEquals("SELECT _CUSTOMERS_._CUSTOMERNAME_ FROM PUBLIC._CUSTOMERS_ WHERE _CUSTOMERS_._CUSTOMERNUMBER_ = ?", compliedQueryString.replace('\"', '_')); assertEquals(0, compiledQuery.getActiveLeases());
