You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2015/10/07 20:29:19 UTC
[1/2] metamodel git commit: METAMODEL-192: Fixed scalar
datatype-conversion functions Fixes #55
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());
[2/2] metamodel git commit: METAMODEL-192: Fixed scalar
datatype-conversion functions Fixes #55
Posted by ka...@apache.org.
METAMODEL-192: Fixed scalar datatype-conversion functions
Fixes #55
Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/d17a10dd
Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/d17a10dd
Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/d17a10dd
Branch: refs/heads/master
Commit: d17a10dd01a5e22b8d9c3f5511c553d97e0c5098
Parents: 04c0baa
Author: Kasper Sørensen <i....@gmail.com>
Authored: Wed Oct 7 20:23:24 2015 +0200
Committer: Kasper Sørensen <i....@gmail.com>
Committed: Wed Oct 7 20:23:24 2015 +0200
----------------------------------------------------------------------
.../org/apache/metamodel/MetaModelHelper.java | 127 +++++++++++++++----
.../metamodel/QueryPostprocessDataContext.java | 68 +++++++---
.../convert/ConvertedDataSetInterceptor.java | 2 +-
.../apache/metamodel/data/FilteredDataSet.java | 7 +-
.../apache/metamodel/data/FirstRowDataSet.java | 7 +-
.../apache/metamodel/data/MaxRowsDataSet.java | 7 +-
.../metamodel/data/ScalarFunctionDataSet.java | 64 ++++++++++
.../metamodel/data/ScalarFunctionRow.java | 72 +++++++++++
.../metamodel/data/SimpleDataSetHeader.java | 11 ++
.../metamodel/data/SubSelectionDataSet.java | 10 +-
.../apache/metamodel/data/WrappingDataSet.java | 34 +++++
.../query/AverageAggregateFunction.java | 4 +-
.../metamodel/query/CountAggregateFunction.java | 1 +
.../query/DefaultAggregateFunction.java | 12 +-
.../metamodel/query/DefaultScalarFunction.java | 27 ++++
.../apache/metamodel/query/FunctionType.java | 6 +-
.../metamodel/query/FunctionTypeFactory.java | 42 ++++--
.../metamodel/query/MaxAggregateFunction.java | 4 +-
.../metamodel/query/MinAggregateFunction.java | 4 +-
.../apache/metamodel/query/ScalarFunction.java | 18 ++-
.../org/apache/metamodel/query/SelectItem.java | 24 +++-
.../metamodel/query/SumAggregateFunction.java | 13 +-
.../metamodel/query/ToBooleanFunction.java | 49 +++++++
.../apache/metamodel/query/ToDateFunction.java | 51 ++++++++
.../metamodel/query/ToNumberFunction.java | 49 +++++++
.../metamodel/query/ToStringFunction.java | 48 +++++++
.../builder/GroupedQueryBuilderCallback.java | 24 +++-
.../query/builder/GroupedQueryBuilderImpl.java | 24 +++-
.../query/builder/SatisfiedFromBuilder.java | 4 +-
.../query/builder/SatisfiedQueryBuilder.java | 9 +-
.../query/builder/WhereBuilderImpl.java | 6 +-
.../apache/metamodel/util/CollectionUtils.java | 7 +-
.../QueryPostprocessDataContextTest.java | 56 +++++++-
.../metamodel/query/parser/QueryParserTest.java | 17 ++-
.../metamodel/jdbc/FetchSizeCalculator.java | 2 +-
.../apache/metamodel/jdbc/JdbcDataContext.java | 49 ++++++-
.../org/apache/metamodel/jdbc/JdbcDataSet.java | 2 +-
.../metamodel/jdbc/SplitQueriesDataSet.java | 8 +-
.../jdbc/dialects/AbstractQueryRewriter.java | 13 +-
.../jdbc/dialects/DefaultQueryRewriter.java | 6 +
.../metamodel/jdbc/dialects/IQueryRewriter.java | 13 ++
.../metamodel/jdbc/JdbcDataContextTest.java | 106 +++++++++++-----
42 files changed, 961 insertions(+), 146 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/MetaModelHelper.java b/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
index e0b9b01..7ffa870 100644
--- a/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
+++ b/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
@@ -40,6 +40,7 @@ import org.apache.metamodel.data.IRowFilter;
import org.apache.metamodel.data.InMemoryDataSet;
import org.apache.metamodel.data.MaxRowsDataSet;
import org.apache.metamodel.data.Row;
+import org.apache.metamodel.data.ScalarFunctionDataSet;
import org.apache.metamodel.data.SimpleDataSetHeader;
import org.apache.metamodel.data.SubSelectionDataSet;
import org.apache.metamodel.query.FilterItem;
@@ -47,6 +48,7 @@ import org.apache.metamodel.query.FromItem;
import org.apache.metamodel.query.GroupByItem;
import org.apache.metamodel.query.OrderByItem;
import org.apache.metamodel.query.Query;
+import org.apache.metamodel.query.ScalarFunction;
import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.query.parser.QueryParser;
import org.apache.metamodel.schema.Column;
@@ -56,7 +58,6 @@ import org.apache.metamodel.schema.SuperColumnType;
import org.apache.metamodel.schema.Table;
import org.apache.metamodel.util.AggregateBuilder;
import org.apache.metamodel.util.CollectionUtils;
-import org.apache.metamodel.util.EqualsBuilder;
import org.apache.metamodel.util.Func;
import org.apache.metamodel.util.ObjectComparator;
import org.apache.metamodel.util.Predicate;
@@ -265,35 +266,40 @@ public final class MetaModelHelper {
}
public static DataSet getSelection(final List<SelectItem> selectItems, final DataSet dataSet) {
- final SelectItem[] dataSetSelectItems = dataSet.getSelectItems();
+ final List<SelectItem> dataSetSelectItems = Arrays.asList(dataSet.getSelectItems());
// check if the selection is already the same
- if (selectItems.size() == dataSetSelectItems.length) {
- boolean same = true;
- int i = 0;
- for (SelectItem selectItem : selectItems) {
- if (!EqualsBuilder.equals(selectItem, dataSetSelectItems[i])) {
- same = false;
- break;
+ if (selectItems.equals(dataSetSelectItems)) {
+ // return the DataSet unmodified
+ return dataSet;
+ }
+
+ final List<SelectItem> scalarFunctionSelectItemsToEvaluate = new ArrayList<>();
+
+ for (SelectItem selectItem : selectItems) {
+ if (selectItem.getScalarFunction() != null) {
+ if (!dataSetSelectItems.contains(selectItem)
+ && dataSetSelectItems.contains(selectItem.replaceFunction(null))) {
+ scalarFunctionSelectItemsToEvaluate.add(selectItem);
}
- i++;
}
+ }
- if (same) {
- // return the dataSet unmodified
- return dataSet;
- }
+ if (scalarFunctionSelectItemsToEvaluate.isEmpty()) {
+ return new SubSelectionDataSet(selectItems, dataSet);
}
- SelectItem[] selectItemsArray = selectItems.toArray(new SelectItem[selectItems.size()]);
- return new SubSelectionDataSet(selectItemsArray, dataSet);
+ final ScalarFunctionDataSet scalaFunctionDataSet = new ScalarFunctionDataSet(
+ scalarFunctionSelectItemsToEvaluate, dataSet);
+ return new SubSelectionDataSet(selectItems, scalaFunctionDataSet);
}
public static DataSet getSelection(SelectItem[] selectItems, DataSet dataSet) {
return getSelection(Arrays.asList(selectItems), dataSet);
}
- public static DataSet getGrouped(List<SelectItem> selectItems, DataSet dataSet, Collection<GroupByItem> groupByItems) {
+ public static DataSet getGrouped(List<SelectItem> selectItems, DataSet dataSet,
+ Collection<GroupByItem> groupByItems) {
return getGrouped(selectItems, dataSet, groupByItems.toArray(new GroupByItem[groupByItems.size()]));
}
@@ -377,7 +383,7 @@ public final class MetaModelHelper {
Object functionResult = item.getAggregateFunction().evaluate(objects.toArray());
resultRow[i] = functionResult;
} else {
- if (item.getFunction() != null) {
+ if (item.getAggregateFunction() != null) {
logger.error("No function input found for SelectItem: {}", item);
}
}
@@ -409,7 +415,7 @@ public final class MetaModelHelper {
* @return
*/
public static DataSet getAggregated(List<SelectItem> workSelectItems, DataSet dataSet) {
- final List<SelectItem> functionItems = getFunctionSelectItems(workSelectItems);
+ final List<SelectItem> functionItems = getAggregateFunctionSelectItems(workSelectItems);
if (functionItems.isEmpty()) {
return dataSet;
}
@@ -502,6 +508,15 @@ public final class MetaModelHelper {
return new InMemoryDataSet(header, resultRows);
}
+ /**
+ *
+ * @param selectItems
+ * @return
+ *
+ * @deprecated use {@link #getAggregateFunctionSelectItems(Iterable)} or
+ * {@link #getScalarFunctionSelectItems(Iterable)} instead
+ */
+ @Deprecated
public static List<SelectItem> getFunctionSelectItems(Iterable<SelectItem> selectItems) {
return CollectionUtils.filter(selectItems, new Predicate<SelectItem>() {
@Override
@@ -511,6 +526,24 @@ public final class MetaModelHelper {
});
}
+ public static List<SelectItem> getAggregateFunctionSelectItems(Iterable<SelectItem> selectItems) {
+ return CollectionUtils.filter(selectItems, new Predicate<SelectItem>() {
+ @Override
+ public Boolean eval(SelectItem arg) {
+ return arg.getAggregateFunction() != null;
+ }
+ });
+ }
+
+ public static List<SelectItem> getScalarFunctionSelectItems(Iterable<SelectItem> selectItems) {
+ return CollectionUtils.filter(selectItems, new Predicate<SelectItem>() {
+ @Override
+ public Boolean eval(SelectItem arg) {
+ return arg.getScalarFunction() != null;
+ }
+ });
+ }
+
public static DataSet getOrdered(DataSet dataSet, List<OrderByItem> orderByItems) {
return getOrdered(dataSet, orderByItems.toArray(new OrderByItem[orderByItems.size()]));
}
@@ -683,9 +716,10 @@ public final class MetaModelHelper {
List<Row> ds1rows = new ArrayList<Row>();
ds1rows.add(ds1row);
- DataSet carthesianProduct = getCarthesianProduct(new DataSet[] {
- new InMemoryDataSet(new CachingDataSetHeader(si1), ds1rows),
- new InMemoryDataSet(new CachingDataSetHeader(si2), ds2data) }, onConditions);
+ DataSet carthesianProduct = getCarthesianProduct(
+ new DataSet[] { new InMemoryDataSet(new CachingDataSetHeader(si1), ds1rows),
+ new InMemoryDataSet(new CachingDataSetHeader(si2), ds2data) },
+ onConditions);
List<Row> carthesianRows = readDataSetFull(carthesianProduct);
if (carthesianRows.size() > 0) {
resultRows.addAll(carthesianRows);
@@ -825,4 +859,53 @@ public final class MetaModelHelper {
}
return null;
}
+
+ /**
+ * Determines if a query contains {@link ScalarFunction}s in any clause of
+ * the query EXCEPT for the SELECT clause. This is a handy thing to
+ * determine because decorating with {@link ScalarFunctionDataSet} only
+ * gives you select-item evaluation so if the rest of the query is pushed to
+ * an underlying datastore, then it may create issues.
+ *
+ * @param query
+ * @return
+ */
+ public static boolean containsNonSelectScalaFunctions(Query query) {
+ // check FROM clause
+ final List<FromItem> fromItems = query.getFromClause().getItems();
+ for (FromItem fromItem : fromItems) {
+ // check sub-queries
+ final Query subQuery = fromItem.getSubQuery();
+ if (subQuery != null) {
+ if (containsNonSelectScalaFunctions(subQuery)) {
+ return true;
+ }
+ if (!getScalarFunctionSelectItems(subQuery.getSelectClause().getItems()).isEmpty()) {
+ return true;
+ }
+ }
+ }
+
+ // check WHERE clause
+ if (!getScalarFunctionSelectItems(query.getWhereClause().getEvaluatedSelectItems()).isEmpty()) {
+ return true;
+ }
+
+ // check GROUP BY clause
+ if (!getScalarFunctionSelectItems(query.getGroupByClause().getEvaluatedSelectItems()).isEmpty()) {
+ return true;
+ }
+
+ // check HAVING clause
+ if (!getScalarFunctionSelectItems(query.getHavingClause().getEvaluatedSelectItems()).isEmpty()) {
+ return true;
+ }
+
+ // check ORDER BY clause
+ if (!getScalarFunctionSelectItems(query.getOrderByClause().getEvaluatedSelectItems()).isEmpty()) {
+ return true;
+ }
+
+ return false;
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java b/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
index 0282a26..c7dcfb7 100644
--- a/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
+++ b/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
@@ -44,6 +44,7 @@ import org.apache.metamodel.query.JoinType;
import org.apache.metamodel.query.OperatorType;
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.Column;
@@ -123,7 +124,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
whereItems.size());
final Number count = executeCountQuery(table, whereItems, functionApproximationAllowed);
if (count == null) {
- logger.debug("DataContext did not return any count query results. Proceeding with manual counting.");
+ logger.debug(
+ "DataContext did not return any count query results. Proceeding with manual counting.");
} else {
List<Row> data = new ArrayList<Row>(1);
final DataSetHeader header = new SimpleDataSetHeader(new SelectItem[] { selectItem });
@@ -143,14 +145,16 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
if (!whereItem.isCompoundFilter() && selectItem != null && selectItem.getColumn() != null) {
final Column column = selectItem.getColumn();
if (column.isPrimaryKey() && OperatorType.EQUALS_TO.equals(whereItem.getOperator())) {
- logger.debug("Query is a primary key lookup query. Trying executePrimaryKeyLookupQuery(...)");
+ logger.debug(
+ "Query is a primary key lookup query. Trying executePrimaryKeyLookupQuery(...)");
if (table != null) {
if (isMainSchemaTable(table)) {
final Object operand = whereItem.getOperand();
final Row row = executePrimaryKeyLookupQuery(table, selectItems, column,
operand);
if (row == null) {
- logger.debug("DataContext did not return any GET query results. Proceeding with manual lookup.");
+ logger.debug(
+ "DataContext did not return any GET query results. Proceeding with manual lookup.");
} else {
final DataSetHeader header = new SimpleDataSetHeader(selectItems);
return new InMemoryDataSet(header, row);
@@ -228,7 +232,7 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
return false;
}
for (SelectItem item : clause.getItems()) {
- if (item.getFunction() != null || item.getExpression() != null) {
+ if (item.getAggregateFunction() != null || item.getExpression() != null) {
return false;
}
}
@@ -404,8 +408,34 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
}
private List<SelectItem> buildWorkingSelectItems(List<SelectItem> selectItems, List<FilterItem> whereItems) {
+ final List<SelectItem> primarySelectItems = new ArrayList<>(selectItems.size());
+ for (SelectItem selectItem : selectItems) {
+ final ScalarFunction scalarFunction = selectItem.getScalarFunction();
+ if (scalarFunction == null || isScalarFunctionMaterialized(scalarFunction)) {
+ primarySelectItems.add(selectItem);
+ } else {
+ final SelectItem copySelectItem = selectItem.replaceFunction(null);
+ primarySelectItems.add(copySelectItem);
+ }
+ }
final List<SelectItem> evaluatedSelectItems = MetaModelHelper.getEvaluatedSelectItems(whereItems);
- return CollectionUtils.concat(true, selectItems, evaluatedSelectItems);
+ return CollectionUtils.concat(true, primarySelectItems, evaluatedSelectItems);
+ }
+
+ /**
+ * Determines if the subclass of this class can materialize
+ * {@link SelectItem}s with the given {@link ScalarFunction}. Usually scalar
+ * functions are applied by MetaModel on the client side, but when possible
+ * they can also be handled by e.g.
+ * {@link #materializeMainSchemaTable(Table, List, int, int)} and
+ * {@link #materializeMainSchemaTable(Table, List, List, int, int)} in which
+ * case MetaModel will not evaluate it client-side.
+ *
+ * @param function
+ * @return
+ */
+ protected boolean isScalarFunctionMaterialized(ScalarFunction function) {
+ return false;
}
@Deprecated
@@ -484,14 +514,14 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
// Create "relationships" table: primary_table, primary_column,
// foreign_table, foreign_column
- relationshipsTable.addColumn(new MutableColumn("primary_table", ColumnType.VARCHAR, relationshipsTable, 0,
- false));
- relationshipsTable.addColumn(new MutableColumn("primary_column", ColumnType.VARCHAR, relationshipsTable, 1,
- false));
- relationshipsTable.addColumn(new MutableColumn("foreign_table", ColumnType.VARCHAR, relationshipsTable, 2,
- false));
- relationshipsTable.addColumn(new MutableColumn("foreign_column", ColumnType.VARCHAR, relationshipsTable, 3,
- false));
+ relationshipsTable
+ .addColumn(new MutableColumn("primary_table", ColumnType.VARCHAR, relationshipsTable, 0, false));
+ relationshipsTable
+ .addColumn(new MutableColumn("primary_column", ColumnType.VARCHAR, relationshipsTable, 1, false));
+ relationshipsTable
+ .addColumn(new MutableColumn("foreign_table", ColumnType.VARCHAR, relationshipsTable, 2, false));
+ relationshipsTable
+ .addColumn(new MutableColumn("foreign_column", ColumnType.VARCHAR, relationshipsTable, 3, false));
MutableRelationship.createRelationship(tablesTable.getColumnByName("name"),
columnsTable.getColumnByName("table"));
@@ -520,8 +550,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
if (t.getType() != null) {
typeString = t.getType().toString();
}
- data.add(new DefaultRow(header, new Object[] { t.getName(), typeString, t.getColumnCount(),
- t.getRemarks() }));
+ data.add(new DefaultRow(header,
+ new Object[] { t.getName(), typeString, t.getColumnCount(), t.getRemarks() }));
}
} else if ("columns".equals(tableName)) {
// "columns" columns: name, type, native_type, size, nullable,
@@ -547,8 +577,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
for (int i = 0; i < primaryColumns.length; i++) {
Column pColumn = primaryColumns[i];
Column fColumn = foreignColumns[i];
- data.add(new DefaultRow(header, new Object[] { pTable.getName(), pColumn.getName(),
- fTable.getName(), fColumn.getName() }));
+ data.add(new DefaultRow(header,
+ new Object[] { pTable.getName(), pColumn.getName(), fTable.getName(), fColumn.getName() }));
}
}
} else {
@@ -615,8 +645,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
* @param maxRows
* @return
*/
- protected DataSet materializeMainSchemaTable(Table table, List<SelectItem> selectItems,
- List<FilterItem> whereItems, int firstRow, int maxRows) {
+ protected DataSet materializeMainSchemaTable(Table table, List<SelectItem> selectItems, List<FilterItem> whereItems,
+ int firstRow, int maxRows) {
final List<SelectItem> workingSelectItems = buildWorkingSelectItems(selectItems, whereItems);
DataSet dataSet;
if (whereItems.isEmpty()) {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java b/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java
index d5746de..082d121 100644
--- a/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java
+++ b/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java
@@ -72,7 +72,7 @@ public class ConvertedDataSetInterceptor implements DataSetInterceptor, HasReadT
for (int i = 0; i < selectItems.length; i++) {
SelectItem selectItem = selectItems[i];
Column column = selectItem.getColumn();
- if (column != null && selectItem.getFunction() == null) {
+ if (column != null && selectItem.getAggregateFunction() == null) {
TypeConverter<?, ?> converter = converters.get(column);
if (converter != null) {
hasConverter = true;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/data/FilteredDataSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/data/FilteredDataSet.java b/core/src/main/java/org/apache/metamodel/data/FilteredDataSet.java
index b45d162..c322401 100644
--- a/core/src/main/java/org/apache/metamodel/data/FilteredDataSet.java
+++ b/core/src/main/java/org/apache/metamodel/data/FilteredDataSet.java
@@ -22,7 +22,7 @@ package org.apache.metamodel.data;
/**
* Wraps another DataSet and transparently applies a set of filters to it.
*/
-public final class FilteredDataSet extends AbstractDataSet {
+public final class FilteredDataSet extends AbstractDataSet implements WrappingDataSet {
private final DataSet _dataSet;
private final IRowFilter[] _filters;
@@ -39,6 +39,11 @@ public final class FilteredDataSet extends AbstractDataSet {
super.close();
_dataSet.close();
}
+
+ @Override
+ public DataSet getWrappedDataSet() {
+ return _dataSet;
+ }
@Override
public boolean next() {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/data/FirstRowDataSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/data/FirstRowDataSet.java b/core/src/main/java/org/apache/metamodel/data/FirstRowDataSet.java
index e3fb172..7e811d9 100644
--- a/core/src/main/java/org/apache/metamodel/data/FirstRowDataSet.java
+++ b/core/src/main/java/org/apache/metamodel/data/FirstRowDataSet.java
@@ -21,7 +21,7 @@ package org.apache.metamodel.data;
/**
* Wraps another DataSet and enforces a first row offset.
*/
-public final class FirstRowDataSet extends AbstractDataSet {
+public final class FirstRowDataSet extends AbstractDataSet implements WrappingDataSet {
private final DataSet _dataSet;
private volatile int _rowsLeftToSkip;
@@ -52,6 +52,11 @@ public final class FirstRowDataSet extends AbstractDataSet {
public Row getRow() {
return _dataSet.getRow();
}
+
+ @Override
+ public DataSet getWrappedDataSet() {
+ return _dataSet;
+ }
@Override
public boolean next() {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/data/MaxRowsDataSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/data/MaxRowsDataSet.java b/core/src/main/java/org/apache/metamodel/data/MaxRowsDataSet.java
index 6baf023..dd33495 100644
--- a/core/src/main/java/org/apache/metamodel/data/MaxRowsDataSet.java
+++ b/core/src/main/java/org/apache/metamodel/data/MaxRowsDataSet.java
@@ -21,7 +21,7 @@ package org.apache.metamodel.data;
/**
* Wraps another DataSet and enforces a maximum number of rows constraint
*/
-public final class MaxRowsDataSet extends AbstractDataSet {
+public final class MaxRowsDataSet extends AbstractDataSet implements WrappingDataSet {
private final DataSet _dataSet;
private volatile int _rowsLeft;
@@ -31,6 +31,11 @@ public final class MaxRowsDataSet extends AbstractDataSet {
_dataSet = dataSet;
_rowsLeft = maxRows;
}
+
+ @Override
+ public DataSet getWrappedDataSet() {
+ return _dataSet;
+ }
@Override
public void close() {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/data/ScalarFunctionDataSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/data/ScalarFunctionDataSet.java b/core/src/main/java/org/apache/metamodel/data/ScalarFunctionDataSet.java
new file mode 100644
index 0000000..64a7866
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/data/ScalarFunctionDataSet.java
@@ -0,0 +1,64 @@
+/**
+ * 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.metamodel.data;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.metamodel.query.ScalarFunction;
+import org.apache.metamodel.query.SelectItem;
+import org.apache.metamodel.util.CollectionUtils;
+
+/**
+ * A {@link DataSet} that enhances another {@link DataSet} with
+ * {@link ScalarFunction}s.
+ */
+public class ScalarFunctionDataSet extends AbstractDataSet implements WrappingDataSet {
+
+ private final DataSet _dataSet;
+ private final List<SelectItem> _scalarFunctionSelectItemsToEvaluate;
+
+ public ScalarFunctionDataSet(List<SelectItem> scalarFunctionSelectItemsToEvaluate, DataSet dataSet) {
+ super(CollectionUtils.concat(false, scalarFunctionSelectItemsToEvaluate,
+ Arrays.<SelectItem> asList(dataSet.getSelectItems())));
+ _scalarFunctionSelectItemsToEvaluate = scalarFunctionSelectItemsToEvaluate;
+ _dataSet = dataSet;
+ }
+
+ @Override
+ public boolean next() {
+ return _dataSet.next();
+ }
+
+ @Override
+ public Row getRow() {
+ final Row row = _dataSet.getRow();
+ return new ScalarFunctionRow(this, row);
+ }
+
+ public List<SelectItem> getScalarFunctionSelectItemsToEvaluate() {
+ return _scalarFunctionSelectItemsToEvaluate;
+ }
+
+ @Override
+ public DataSet getWrappedDataSet() {
+ return _dataSet;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/data/ScalarFunctionRow.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/data/ScalarFunctionRow.java b/core/src/main/java/org/apache/metamodel/data/ScalarFunctionRow.java
new file mode 100644
index 0000000..7ba8c7c
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/data/ScalarFunctionRow.java
@@ -0,0 +1,72 @@
+/**
+ * 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.metamodel.data;
+
+import java.util.List;
+
+import org.apache.metamodel.query.ScalarFunction;
+import org.apache.metamodel.query.SelectItem;
+
+/**
+ * A {@link Row} implementation that applies {@link ScalarFunction}s when
+ * requested. This class closely interacts with the
+ * {@link ScalarFunctionDataSet}.
+ */
+final class ScalarFunctionRow extends AbstractRow {
+
+ private static final long serialVersionUID = 1L;
+
+ private final ScalarFunctionDataSet _scalarFunctionDataSet;
+ private final Row _row;
+
+ public ScalarFunctionRow(ScalarFunctionDataSet scalarFunctionDataSet, Row row) {
+ _scalarFunctionDataSet = scalarFunctionDataSet;
+ _row = row;
+ }
+
+ @Override
+ public Object getValue(int index) throws IndexOutOfBoundsException {
+ final List<SelectItem> scalarFunctionSelectItems = _scalarFunctionDataSet
+ .getScalarFunctionSelectItemsToEvaluate();
+ final int scalarFunctionCount = scalarFunctionSelectItems.size();
+ if (index >= scalarFunctionCount) {
+ return _row.getValue(index - scalarFunctionCount);
+ }
+ final SelectItem selectItem = scalarFunctionSelectItems.get(index);
+ final SelectItem selectItemWithoutFunction = selectItem.replaceFunction(null);
+ return selectItem.getScalarFunction().evaluate(_row, selectItemWithoutFunction);
+ }
+
+ @Override
+ public Style getStyle(int index) throws IndexOutOfBoundsException {
+ final List<SelectItem> scalarFunctionSelectItems = _scalarFunctionDataSet
+ .getScalarFunctionSelectItemsToEvaluate();
+ final int scalarFunctionCount = scalarFunctionSelectItems.size();
+ if (index >= scalarFunctionCount) {
+ _row.getStyle(index - scalarFunctionCount);
+ }
+ return Style.NO_STYLE;
+ }
+
+ @Override
+ protected DataSetHeader getHeader() {
+ return _scalarFunctionDataSet.getHeader();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/data/SimpleDataSetHeader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/data/SimpleDataSetHeader.java b/core/src/main/java/org/apache/metamodel/data/SimpleDataSetHeader.java
index a3c9e40..8f8a8a8 100644
--- a/core/src/main/java/org/apache/metamodel/data/SimpleDataSetHeader.java
+++ b/core/src/main/java/org/apache/metamodel/data/SimpleDataSetHeader.java
@@ -101,6 +101,12 @@ public class SimpleDataSetHeader implements DataSetHeader {
}
i++;
}
+
+ final boolean scalarFunctionQueried = item.getScalarFunction() != null;
+ if (scalarFunctionQueried) {
+ final SelectItem itemWithoutFunction = item.replaceFunction(null);
+ return indexOf(itemWithoutFunction);
+ }
return -1;
}
@@ -129,4 +135,9 @@ public class SimpleDataSetHeader implements DataSetHeader {
return false;
return true;
}
+
+ @Override
+ public String toString() {
+ return "DataSetHeader" + _items.toString();
+ }
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/data/SubSelectionDataSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/data/SubSelectionDataSet.java b/core/src/main/java/org/apache/metamodel/data/SubSelectionDataSet.java
index 5d36bbd..7ca25ba 100644
--- a/core/src/main/java/org/apache/metamodel/data/SubSelectionDataSet.java
+++ b/core/src/main/java/org/apache/metamodel/data/SubSelectionDataSet.java
@@ -18,12 +18,14 @@
*/
package org.apache.metamodel.data;
+import java.util.List;
+
import org.apache.metamodel.query.SelectItem;
/**
* {@link DataSet} wrapper for doing subselection.
*/
-public final class SubSelectionDataSet extends AbstractDataSet {
+public final class SubSelectionDataSet extends AbstractDataSet implements WrappingDataSet {
private final DataSet _dataSet;
@@ -32,6 +34,12 @@ public final class SubSelectionDataSet extends AbstractDataSet {
_dataSet = dataSet;
}
+ public SubSelectionDataSet(List<SelectItem> selectItems, DataSet dataSet) {
+ super(selectItems);
+ _dataSet = dataSet;
+ }
+
+ @Override
public DataSet getWrappedDataSet() {
return _dataSet;
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/data/WrappingDataSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/data/WrappingDataSet.java b/core/src/main/java/org/apache/metamodel/data/WrappingDataSet.java
new file mode 100644
index 0000000..47735e5
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/data/WrappingDataSet.java
@@ -0,0 +1,34 @@
+/**
+ * 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.metamodel.data;
+
+/**
+ * Sub-interface for {@link DataSet}s that wrap other {@link DataSet}s,
+ * typically to apply some client-side filtering or enhancement logic on raw
+ * data.
+ */
+public interface WrappingDataSet extends DataSet {
+
+ /**
+ * Gets the {@link DataSet} that is wrapped by this {@link WrappingDataSet}.
+ *
+ * @return
+ */
+ public DataSet getWrappedDataSet();
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/AverageAggregateFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/AverageAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/AverageAggregateFunction.java
index a1ea23f..d80de09 100644
--- a/core/src/main/java/org/apache/metamodel/query/AverageAggregateFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/AverageAggregateFunction.java
@@ -20,12 +20,14 @@ package org.apache.metamodel.query;
import org.apache.metamodel.util.AggregateBuilder;
-public class AverageAggregateFunction extends DefaultAggregateFunction<Double> implements AggregateFunction {
+public class AverageAggregateFunction extends DefaultAggregateFunction<Double> {
+ @Override
public String getFunctionName() {
return "AVG";
}
+ @Override
public AggregateBuilder<Double> createAggregateBuilder() {
return new AverageAggregateBuilder();
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/CountAggregateFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/CountAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/CountAggregateFunction.java
index dc86f90..c88b47e 100644
--- a/core/src/main/java/org/apache/metamodel/query/CountAggregateFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/CountAggregateFunction.java
@@ -27,6 +27,7 @@ public class CountAggregateFunction extends DefaultAggregateFunction<Long> {
return "COUNT";
}
+ @Override
public AggregateBuilder<Long> createAggregateBuilder() {
return new CountAggregateBuilder();
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/DefaultAggregateFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/DefaultAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/DefaultAggregateFunction.java
index e38d403..d904bb0 100644
--- a/core/src/main/java/org/apache/metamodel/query/DefaultAggregateFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/DefaultAggregateFunction.java
@@ -26,20 +26,11 @@ import org.apache.metamodel.util.AggregateBuilder;
*/
public abstract class DefaultAggregateFunction<T> implements AggregateFunction {
- public abstract AggregateBuilder<T> createAggregateBuilder();
-
+ @Override
public ColumnType getExpectedColumnType(ColumnType type) {
return type;
}
- public Object evaluate(Iterable<?> values) {
- AggregateBuilder<?> builder = createAggregateBuilder();
- for (Object object : values) {
- builder.add(object);
- }
- return builder.getAggregate();
- }
-
/**
* Executes the function
*
@@ -51,6 +42,7 @@ public abstract class DefaultAggregateFunction<T> implements AggregateFunction {
* yields a Long, AVG and SUM yields Double values and MAX and MIN
* yields the type of the provided values.
*/
+ @Override
public Object evaluate(Object... values) {
AggregateBuilder<?> builder = createAggregateBuilder();
for (Object object : values) {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/DefaultScalarFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/DefaultScalarFunction.java b/core/src/main/java/org/apache/metamodel/query/DefaultScalarFunction.java
new file mode 100644
index 0000000..e647b02
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/DefaultScalarFunction.java
@@ -0,0 +1,27 @@
+/**
+ * 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.metamodel.query;
+
+public abstract class DefaultScalarFunction implements ScalarFunction {
+
+ @Override
+ public String toString() {
+ return getFunctionName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/FunctionType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FunctionType.java b/core/src/main/java/org/apache/metamodel/query/FunctionType.java
index 695e7f4..0ef259a 100644
--- a/core/src/main/java/org/apache/metamodel/query/FunctionType.java
+++ b/core/src/main/java/org/apache/metamodel/query/FunctionType.java
@@ -24,7 +24,7 @@ import org.apache.metamodel.schema.ColumnType;
* Represents a generic function to use in a SelectItem.
*
* @see SelectItem
-*/
+ */
public interface FunctionType {
public static final AggregateFunction COUNT = new CountAggregateFunction();
@@ -32,6 +32,10 @@ public interface FunctionType {
public static final AggregateFunction SUM = new SumAggregateFunction();
public static final AggregateFunction MAX = new MaxAggregateFunction();
public static final AggregateFunction MIN = new MinAggregateFunction();
+ public static final ScalarFunction TO_STRING = new ToStringFunction();
+ public static final ScalarFunction TO_NUMBER = new ToNumberFunction();
+ public static final ScalarFunction TO_DATE = new ToDateFunction();
+ public static final ScalarFunction TO_BOOLEAN = new ToBooleanFunction();
public ColumnType getExpectedColumnType(ColumnType type);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java b/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java
index 9345f42..c021ae3 100644
--- a/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java
+++ b/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java
@@ -19,24 +19,48 @@
package org.apache.metamodel.query;
/**
- * Factory to create AggregateFunctions through
- * its function name.
+ * Factory to create AggregateFunctions through its function name.
*
*/
public class FunctionTypeFactory {
- public static AggregateFunction get(String functionName) {
- if (functionName.equals("COUNT")) {
+ public static FunctionType get(String functionName) {
+ if (functionName == null || functionName.isEmpty()) {
+ return null;
+ }
+
+ functionName = functionName.toUpperCase();
+
+ switch (functionName) {
+ case "COUNT":
return FunctionType.COUNT;
- } else if (functionName.equals("AVG")) {
+ case "AVG":
return FunctionType.AVG;
- } else if (functionName.equals("SUM")) {
+ case "SUM":
return FunctionType.SUM;
- } else if (functionName.equals("MAX")) {
+ case "MAX":
return FunctionType.MAX;
- } else if (functionName.equals("MIN")) {
+ case "MIN":
return FunctionType.MIN;
- } else {
+ case "TO_NUMBER":
+ case "NUMBER":
+ case "TO_NUM":
+ case "NUM":
+ return FunctionType.TO_NUMBER;
+ case "TO_STRING":
+ case "STRING":
+ case "TO_STR":
+ case "STR":
+ return FunctionType.TO_STRING;
+ case "TO_BOOLEAN":
+ case "BOOLEAN":
+ case "TO_BOOL":
+ case "BOOL":
+ return FunctionType.TO_BOOLEAN;
+ case "TO_DATE":
+ case "DATE":
+ return FunctionType.TO_DATE;
+ default:
return null;
}
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/MaxAggregateFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/MaxAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/MaxAggregateFunction.java
index aeadba0..32ecc2a 100644
--- a/core/src/main/java/org/apache/metamodel/query/MaxAggregateFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/MaxAggregateFunction.java
@@ -20,12 +20,14 @@ package org.apache.metamodel.query;
import org.apache.metamodel.util.AggregateBuilder;
-public class MaxAggregateFunction extends DefaultAggregateFunction<Object> implements AggregateFunction {
+public class MaxAggregateFunction extends DefaultAggregateFunction<Object> {
+ @Override
public String getFunctionName() {
return "MAX";
}
+ @Override
public AggregateBuilder<Object> createAggregateBuilder() {
return new MaxAggregateBuilder();
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/MinAggregateFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/MinAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/MinAggregateFunction.java
index 8c7599b..0181376 100644
--- a/core/src/main/java/org/apache/metamodel/query/MinAggregateFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/MinAggregateFunction.java
@@ -20,12 +20,14 @@ package org.apache.metamodel.query;
import org.apache.metamodel.util.AggregateBuilder;
-public class MinAggregateFunction extends DefaultAggregateFunction<Object> implements AggregateFunction {
+public class MinAggregateFunction extends DefaultAggregateFunction<Object> {
+ @Override
public String getFunctionName() {
return "MIN";
}
+ @Override
public AggregateBuilder<Object> createAggregateBuilder() {
return new MinAggregateBuilder();
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/ScalarFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/ScalarFunction.java b/core/src/main/java/org/apache/metamodel/query/ScalarFunction.java
index c5131ec..8262c2b 100644
--- a/core/src/main/java/org/apache/metamodel/query/ScalarFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/ScalarFunction.java
@@ -18,14 +18,26 @@
*/
package org.apache.metamodel.query;
+import org.apache.metamodel.data.Row;
+
/**
* Interface that contains scalar specific methods.
*
- * Scalar functions returns only a single value, based
- * on the input value.
+ * Scalar functions returns only a single value, based on the input value.
*
*/
public interface ScalarFunction extends FunctionType {
- //TODO: Add scalar function methods
+ /**
+ * Applies and evaluates the function on a particular row of data.
+ *
+ * @param row
+ * the row containing data
+ * @param operandItem
+ * the select item which is the argument to this function. If a
+ * function takes multiple select items, this will be the primary
+ * one.
+ * @return
+ */
+ public Object evaluate(Row row, SelectItem operandItem);
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/SelectItem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/SelectItem.java b/core/src/main/java/org/apache/metamodel/query/SelectItem.java
index bfe1298..b9883f8 100644
--- a/core/src/main/java/org/apache/metamodel/query/SelectItem.java
+++ b/core/src/main/java/org/apache/metamodel/query/SelectItem.java
@@ -39,7 +39,8 @@ import org.slf4j.LoggerFactory;
* <li>expression function SELECTs (retrieves databased on a function and an
* expression, only COUNT(*) is supported for non-JDBC datastores))</li>
* <li>SELECTs from subqueries (works just like column selects, but in stead of
- * pointing to a column, it retrieves data from the select item of a subquery)</li>
+ * pointing to a column, it retrieves data from the select item of a subquery)
+ * </li>
* </ul>
*
* @see SelectClause
@@ -92,7 +93,8 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
}
public static boolean isCountAllItem(SelectItem item) {
- if (item != null && item.getFunction()!= null && item.getFunction().toString().equals("COUNT") && item.getExpression() == "*") {
+ if (item != null && item.getFunction() != null && item.getFunction().toString().equals("COUNT")
+ && item.getExpression() == "*") {
return true;
}
return false;
@@ -207,6 +209,13 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
return this;
}
+ /**
+ *
+ * @return
+ * @deprecated use {@link #getAggregateFunction()} or
+ * {@link #getScalarFunction()} instead
+ */
+ @Deprecated
public FunctionType getFunction() {
return _function;
}
@@ -215,9 +224,14 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
if (_function instanceof AggregateFunction) {
return (AggregateFunction) _function;
}
- else {
- return null;
+ return null;
+ }
+
+ public ScalarFunction getScalarFunction() {
+ if (_function instanceof ScalarFunction) {
+ return (ScalarFunction) _function;
}
+ return null;
}
/**
@@ -398,7 +412,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
sb.append(_subQuerySelectItem.getSuperQueryAlias());
}
if (_function != null) {
- sb.insert(0, _function + "(");
+ sb.insert(0, _function.getFunctionName() + "(");
sb.append(")");
}
return sb;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/SumAggregateFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/SumAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/SumAggregateFunction.java
index 193195e..655f130 100644
--- a/core/src/main/java/org/apache/metamodel/query/SumAggregateFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/SumAggregateFunction.java
@@ -21,13 +21,20 @@ package org.apache.metamodel.query;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.util.AggregateBuilder;
-public class SumAggregateFunction extends DefaultAggregateFunction<Double> implements AggregateFunction {
+public class SumAggregateFunction extends DefaultAggregateFunction<Double> {
- public String getFunctionName() { return "SUM"; }
+ @Override
+ public String getFunctionName() {
+ return "SUM";
+ }
+ @Override
public AggregateBuilder<Double> createAggregateBuilder() {
return new SumAggregateBuilder();
}
- public ColumnType getExpectedColumnType(ColumnType type) { return ColumnType.DOUBLE; }
+ @Override
+ public ColumnType getExpectedColumnType(ColumnType type) {
+ return ColumnType.DOUBLE;
+ }
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/ToBooleanFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/ToBooleanFunction.java b/core/src/main/java/org/apache/metamodel/query/ToBooleanFunction.java
new file mode 100644
index 0000000..829f251
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/ToBooleanFunction.java
@@ -0,0 +1,49 @@
+/**
+ * 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.metamodel.query;
+
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.schema.ColumnType;
+import org.apache.metamodel.util.BooleanComparator;
+
+public class ToBooleanFunction extends DefaultScalarFunction {
+
+ @Override
+ public ColumnType getExpectedColumnType(ColumnType type) {
+ if (type.isBoolean()) {
+ return type;
+ }
+ return ColumnType.BOOLEAN;
+ }
+
+ @Override
+ public String getFunctionName() {
+ return "TO_BOOLEAN";
+ }
+
+ @Override
+ public Object evaluate(Row row, SelectItem item) {
+ final Object value = row.getValue(item);
+ if (value == null || value instanceof Boolean) {
+ return value;
+ }
+ return BooleanComparator.toBoolean(value);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/ToDateFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/ToDateFunction.java b/core/src/main/java/org/apache/metamodel/query/ToDateFunction.java
new file mode 100644
index 0000000..5d66787
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/ToDateFunction.java
@@ -0,0 +1,51 @@
+/**
+ * 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.metamodel.query;
+
+import java.util.Date;
+
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.schema.ColumnType;
+import org.apache.metamodel.util.TimeComparator;
+
+public class ToDateFunction extends DefaultScalarFunction {
+
+ @Override
+ public ColumnType getExpectedColumnType(ColumnType type) {
+ if (type.isTimeBased()) {
+ return type;
+ }
+ return ColumnType.TIMESTAMP;
+ }
+
+ @Override
+ public String getFunctionName() {
+ return "TO_DATE";
+ }
+
+ @Override
+ public Object evaluate(Row row, SelectItem item) {
+ final Object value = row.getValue(item);
+ if (value == null || value instanceof Date) {
+ return value;
+ }
+ return TimeComparator.toDate(value);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/ToNumberFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/ToNumberFunction.java b/core/src/main/java/org/apache/metamodel/query/ToNumberFunction.java
new file mode 100644
index 0000000..0d30e13
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/ToNumberFunction.java
@@ -0,0 +1,49 @@
+/**
+ * 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.metamodel.query;
+
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.schema.ColumnType;
+import org.apache.metamodel.util.NumberComparator;
+
+public class ToNumberFunction extends DefaultScalarFunction {
+
+ @Override
+ public ColumnType getExpectedColumnType(ColumnType type) {
+ if (type.isNumber()) {
+ return type;
+ }
+ return ColumnType.NUMBER;
+ }
+
+ @Override
+ public String getFunctionName() {
+ return "TO_NUMBER";
+ }
+
+ @Override
+ public Object evaluate(Row row, SelectItem item) {
+ final Object value = row.getValue(item);
+ if (value == null || value instanceof Number) {
+ return value;
+ }
+ return NumberComparator.toNumber(value);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/ToStringFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/ToStringFunction.java b/core/src/main/java/org/apache/metamodel/query/ToStringFunction.java
new file mode 100644
index 0000000..6eaec39
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/ToStringFunction.java
@@ -0,0 +1,48 @@
+/**
+ * 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.metamodel.query;
+
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.schema.ColumnType;
+
+public class ToStringFunction extends DefaultScalarFunction {
+
+ @Override
+ public ColumnType getExpectedColumnType(ColumnType type) {
+ if (type.isLiteral()) {
+ return type;
+ }
+ return ColumnType.STRING;
+ }
+
+ @Override
+ public String getFunctionName() {
+ return "TO_STRING";
+ }
+
+ @Override
+ public Object evaluate(Row row, SelectItem item) {
+ final Object value = row.getValue(item);
+ if (value == null || value instanceof String) {
+ return value;
+ }
+ return String.valueOf(value);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderCallback.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderCallback.java b/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderCallback.java
index 5026696..b5367ca 100644
--- a/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderCallback.java
+++ b/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderCallback.java
@@ -25,6 +25,7 @@ import org.apache.metamodel.query.CompiledQuery;
import org.apache.metamodel.query.FilterItem;
import org.apache.metamodel.query.FunctionType;
import org.apache.metamodel.query.Query;
+import org.apache.metamodel.query.ScalarFunction;
import org.apache.metamodel.schema.Column;
import org.apache.metamodel.util.BaseObject;
@@ -39,22 +40,22 @@ abstract class GroupedQueryBuilderCallback extends BaseObject implements Grouped
protected GroupedQueryBuilder getQueryBuilder() {
return queryBuilder;
}
-
+
@Override
public SatisfiedQueryBuilder<GroupedQueryBuilder> firstRow(int firstRow) {
return getQueryBuilder().firstRow(firstRow);
}
-
+
@Override
public SatisfiedQueryBuilder<GroupedQueryBuilder> limit(int maxRows) {
return getQueryBuilder().limit(maxRows);
}
-
+
@Override
public SatisfiedQueryBuilder<GroupedQueryBuilder> offset(int offset) {
return getQueryBuilder().offset(offset);
}
-
+
@Override
public SatisfiedQueryBuilder<GroupedQueryBuilder> maxRows(int maxRows) {
return getQueryBuilder().maxRows(maxRows);
@@ -76,6 +77,11 @@ abstract class GroupedQueryBuilderCallback extends BaseObject implements Grouped
}
@Override
+ public SatisfiedQueryBuilder<?> select(FunctionType function, String columnName) {
+ return getQueryBuilder().select(function, columnName);
+ }
+
+ @Override
public FunctionSelectBuilder<GroupedQueryBuilder> select(FunctionType functionType, Column column) {
return getQueryBuilder().select(functionType, column);
}
@@ -94,6 +100,16 @@ abstract class GroupedQueryBuilderCallback extends BaseObject implements Grouped
public WhereBuilder<GroupedQueryBuilder> where(Column column) {
return getQueryBuilder().where(column);
}
+
+ @Override
+ public WhereBuilder<GroupedQueryBuilder> where(ScalarFunction function, Column column) {
+ return getQueryBuilder().where(function, column);
+ }
+
+ @Override
+ public WhereBuilder<GroupedQueryBuilder> where(ScalarFunction function, String columnName) {
+ return getQueryBuilder().where(function, columnName);
+ }
@Override
public SatisfiedOrderByBuilder<GroupedQueryBuilder> orderBy(Column column) {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderImpl.java b/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderImpl.java
index 3c02090..4c05bfe 100644
--- a/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderImpl.java
+++ b/core/src/main/java/org/apache/metamodel/query/builder/GroupedQueryBuilderImpl.java
@@ -28,6 +28,7 @@ import org.apache.metamodel.query.FilterItem;
import org.apache.metamodel.query.FromItem;
import org.apache.metamodel.query.FunctionType;
import org.apache.metamodel.query.Query;
+import org.apache.metamodel.query.ScalarFunction;
import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.schema.Column;
import org.apache.metamodel.schema.Table;
@@ -62,6 +63,15 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
}
@Override
+ public SatisfiedQueryBuilder<?> select(FunctionType function, String columnName) {
+ if (function == null) {
+ throw new IllegalArgumentException("function cannot be null");
+ }
+ final Column column = findColumn(columnName);
+ return new FunctionSelectBuilderImpl(function, column, _query, this);
+ }
+
+ @Override
public FunctionSelectBuilder<GroupedQueryBuilder> select(FunctionType function, Column column) {
if (function == null) {
throw new IllegalArgumentException("function cannot be null");
@@ -114,9 +124,21 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
@Override
public WhereBuilder<GroupedQueryBuilder> where(String columnName) {
- Column column = findColumn(columnName);
+ final Column column = findColumn(columnName);
return where(column);
}
+
+ @Override
+ public WhereBuilder<GroupedQueryBuilder> where(ScalarFunction function, Column column) {
+ final SelectItem selectItem = new SelectItem(function, column);
+ return new WhereBuilderImpl(selectItem, _query, this);
+ }
+
+ @Override
+ public WhereBuilder<GroupedQueryBuilder> where(ScalarFunction function, String columnName) {
+ final Column column = findColumn(columnName);
+ return where(function, column);
+ }
@Override
public Column findColumn(final String columnName) throws IllegalArgumentException {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilder.java b/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilder.java
index 92aa278..c01bef3 100644
--- a/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilder.java
+++ b/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilder.java
@@ -36,9 +36,9 @@ public interface SatisfiedFromBuilder {
public ColumnSelectBuilder<?> select(Column column);
- public SatisfiedQueryBuilder<?> select(FunctionType functionType, String columnName);
+ public SatisfiedQueryBuilder<?> select(FunctionType function, String columnName);
- public FunctionSelectBuilder<?> select(FunctionType functionType, Column column);
+ public FunctionSelectBuilder<?> select(FunctionType function, Column column);
public CountSelectBuilder<?> selectCount();
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedQueryBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedQueryBuilder.java b/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedQueryBuilder.java
index 2ca4efc..1b62de9 100644
--- a/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedQueryBuilder.java
+++ b/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedQueryBuilder.java
@@ -24,6 +24,7 @@ import org.apache.metamodel.query.CompiledQuery;
import org.apache.metamodel.query.FilterItem;
import org.apache.metamodel.query.FunctionType;
import org.apache.metamodel.query.Query;
+import org.apache.metamodel.query.ScalarFunction;
import org.apache.metamodel.schema.Column;
/**
@@ -77,7 +78,9 @@ public interface SatisfiedQueryBuilder<B extends SatisfiedQueryBuilder<?>> {
*/
public SatisfiedQueryBuilder<B> maxRows(int maxRows);
- public FunctionSelectBuilder<B> select(FunctionType functionType, Column column);
+ public FunctionSelectBuilder<B> select(FunctionType function, Column column);
+
+ public SatisfiedQueryBuilder<?> select(FunctionType function, String columnName);
public CountSelectBuilder<B> selectCount();
@@ -86,6 +89,10 @@ public interface SatisfiedQueryBuilder<B extends SatisfiedQueryBuilder<?>> {
public WhereBuilder<B> where(Column column);
public WhereBuilder<B> where(String columnName);
+
+ public WhereBuilder<B> where(ScalarFunction function, Column column);
+
+ public WhereBuilder<B> where(ScalarFunction function, String columnName);
public SatisfiedQueryBuilder<B> where(FilterItem... filters);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/query/builder/WhereBuilderImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/builder/WhereBuilderImpl.java b/core/src/main/java/org/apache/metamodel/query/builder/WhereBuilderImpl.java
index 65f7a7e..2ce21d7 100644
--- a/core/src/main/java/org/apache/metamodel/query/builder/WhereBuilderImpl.java
+++ b/core/src/main/java/org/apache/metamodel/query/builder/WhereBuilderImpl.java
@@ -36,7 +36,11 @@ final class WhereBuilderImpl extends AbstractQueryFilterBuilder<SatisfiedWhereBu
private FilterItem _parentOrFilter;
public WhereBuilderImpl(Column column, Query query, GroupedQueryBuilder queryBuilder) {
- super(new SelectItem(column), queryBuilder);
+ this(new SelectItem(column), query, queryBuilder);
+ }
+
+ public WhereBuilderImpl(SelectItem selectItem, Query query, GroupedQueryBuilder queryBuilder) {
+ super(selectItem, queryBuilder);
_query = query;
_orFilters = new ArrayList<FilterItem>();
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java b/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java
index 4fac86a..2cd4e6a 100644
--- a/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java
+++ b/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java
@@ -173,8 +173,8 @@ public final class CollectionUtils {
if (existingArray == null) {
return elements;
}
- Object result = Array.newInstance(existingArray.getClass().getComponentType(), existingArray.length
- + elements.length);
+ Object result = Array.newInstance(existingArray.getClass().getComponentType(),
+ existingArray.length + elements.length);
System.arraycopy(existingArray, 0, result, 0, existingArray.length);
System.arraycopy(elements, 0, result, existingArray.length, elements.length);
return (E[]) result;
@@ -197,7 +197,8 @@ public final class CollectionUtils {
return result;
}
- private static <E> void addElements(boolean removeDuplicates, final List<E> result, Collection<? extends E> elements) {
+ private static <E> void addElements(boolean removeDuplicates, final List<E> result,
+ Collection<? extends E> elements) {
for (E item : elements) {
if (removeDuplicates) {
if (!result.contains(item)) {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java b/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java
index a1abea8..618ff55 100644
--- a/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java
+++ b/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java
@@ -107,8 +107,8 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase {
public void testAggregateQueryRegularWhereClause() throws Exception {
MockDataContext dc = new MockDataContext("sch", "tab", "1");
Table table = dc.getDefaultSchema().getTables()[0];
- assertSingleRowResult("Row[values=[3]]", dc.query().from(table).selectCount().where("baz").eq("world")
- .execute());
+ assertSingleRowResult("Row[values=[3]]",
+ dc.query().from(table).selectCount().where("baz").eq("world").execute());
}
public void testApplyFunctionToNullValues() throws Exception {
@@ -216,6 +216,54 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase {
assertFalse(ds.next());
}
+ public void testScalarFunctionSelect() throws Exception {
+ MockDataContext dc = new MockDataContext("sch", "tab", "1");
+ Table table = dc.getDefaultSchema().getTables()[0];
+
+ Query query = dc.query().from(table).select("foo").select(FunctionType.TO_NUMBER, "foo").select("bar")
+ .select(FunctionType.TO_STRING, "bar").select(FunctionType.TO_NUMBER, "bar").toQuery();
+ assertEquals("SELECT tab.foo, TO_NUMBER(tab.foo), tab.bar, TO_STRING(tab.bar), TO_NUMBER(tab.bar) FROM sch.tab", query.toSql());
+
+ DataSet ds = dc.executeQuery(query);
+ assertTrue(ds.next());
+ Row row;
+
+ row = ds.getRow();
+ assertEquals("Row[values=[1, 1, hello, hello, null]]", row.toString());
+ Object value1 = row.getValue(1);
+ assertEquals(Integer.class, value1.getClass());
+
+ assertTrue(ds.next());
+
+ row = ds.getRow();
+ assertEquals("Row[values=[2, 2, 1, 1, 1]]", row.toString());
+ Object value2 = row.getValue(1);
+ assertEquals(Integer.class, value2.getClass());
+ Object value3 = row.getValue(4);
+ assertEquals(Integer.class, value3.getClass());
+
+ assertTrue(ds.next());
+ ds.close();
+ }
+
+ public void testScalarFunctionWhere() throws Exception {
+ MockDataContext dc = new MockDataContext("sch", "tab", "1");
+ Table table = dc.getDefaultSchema().getTables()[0];
+
+ Query query = dc.query().from(table).select("foo").where(FunctionType.TO_NUMBER, "bar").eq(1).toQuery();
+ assertEquals("SELECT tab.foo FROM sch.tab WHERE TO_NUMBER(tab.bar) = 1", query.toSql());
+
+ DataSet ds = dc.executeQuery(query);
+ assertTrue(ds.next());
+ Row row;
+
+ row = ds.getRow();
+ assertEquals("Row[values=[2]]", row.toString());
+
+ assertFalse(ds.next());
+ ds.close();
+ }
+
public void testSelectItemReferencesToFromItems() throws Exception {
MockDataContext dc = new MockDataContext("sch", "tab", "1");
@@ -702,8 +750,8 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase {
Query q = new Query();
q.from(table1);
q.select(table1.getColumns());
- SelectItem countrySelectItem = q.getSelectClause().getSelectItem(
- table1.getColumnByName(COLUMN_CONTRIBUTOR_COUNTRY));
+ SelectItem countrySelectItem = q.getSelectClause()
+ .getSelectItem(table1.getColumnByName(COLUMN_CONTRIBUTOR_COUNTRY));
q.where(new FilterItem(countrySelectItem, OperatorType.EQUALS_TO, "denmark"));
DataSet data = dc.executeQuery(q);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/core/src/test/java/org/apache/metamodel/query/parser/QueryParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/query/parser/QueryParserTest.java b/core/src/test/java/org/apache/metamodel/query/parser/QueryParserTest.java
index 401dcf7..f83d4ff 100644
--- a/core/src/test/java/org/apache/metamodel/query/parser/QueryParserTest.java
+++ b/core/src/test/java/org/apache/metamodel/query/parser/QueryParserTest.java
@@ -18,12 +18,9 @@
*/
package org.apache.metamodel.query.parser;
-import java.lang.String;
import java.util.Arrays;
import java.util.List;
-import junit.framework.TestCase;
-
import org.apache.metamodel.MetaModelException;
import org.apache.metamodel.MetaModelHelper;
import org.apache.metamodel.MockDataContext;
@@ -38,6 +35,8 @@ import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.schema.MutableColumn;
+import junit.framework.TestCase;
+
public class QueryParserTest extends TestCase {
private MockDataContext dc;
@@ -58,6 +57,12 @@ public class QueryParserTest extends TestCase {
assertEquals("SELECT a.foo AS f FROM sch.tbl a INNER JOIN sch.tbl b ON a.foo = b.foo ORDER BY a.foo ASC",
q.toSql());
}
+
+ public void testParseScalarFunctions() throws Exception {
+ Query q = MetaModelHelper.parseQuery(dc,
+ "select TO_NUM(a.foo) from sch.tbl a WHERE BOOLEAN(a.bar) = false");
+ assertEquals("SELECT TO_NUMBER(a.foo) FROM sch.tbl a WHERE TO_BOOLEAN(a.bar) = FALSE", q.toSql());
+ }
public void testSelectEverythingFromTable() throws Exception {
Query q = MetaModelHelper.parseQuery(dc, "SELECT * FROM sch.tbl");
@@ -361,11 +366,11 @@ public class QueryParserTest extends TestCase {
assertNotNull("SelectItem 1 should be a column", q.getSelectClause().getItem(0).getColumn());
// COUNT(*)
- assertNotNull("SelectItem 2 should be a Function", q.getSelectClause().getItem(1).getFunction());
+ assertNotNull("SelectItem 2 should be a Function", q.getSelectClause().getItem(1).getAggregateFunction());
assertNotNull("SelectItem 2 should be a Function of '*'", q.getSelectClause().getItem(1).getExpression());
// MAX(tbl.baz)
- assertNotNull("SelectItem 3 should be a Function", q.getSelectClause().getItem(2).getFunction());
+ assertNotNull("SelectItem 3 should be a Function", q.getSelectClause().getItem(2).getAggregateFunction());
assertNotNull("SelectItem 4 should be a Function of a column", q.getSelectClause().getItem(2).getColumn());
// FROM tbl.foo
@@ -377,7 +382,7 @@ public class QueryParserTest extends TestCase {
// HAVING COUNT(*) > 2
FilterItem havingItem = q.getHavingClause().getItem(0);
assertNull(havingItem.getExpression());
- assertNotNull(havingItem.getSelectItem().getFunction());
+ assertNotNull(havingItem.getSelectItem().getAggregateFunction());
assertEquals("*", havingItem.getSelectItem().getExpression());
// ORDER BY tbl.foo ASC
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/jdbc/src/main/java/org/apache/metamodel/jdbc/FetchSizeCalculator.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/FetchSizeCalculator.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/FetchSizeCalculator.java
index 468aa9b..14acdce 100644
--- a/jdbc/src/main/java/org/apache/metamodel/jdbc/FetchSizeCalculator.java
+++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/FetchSizeCalculator.java
@@ -97,7 +97,7 @@ final class FetchSizeCalculator {
List<SelectItem> items = query.getSelectClause().getItems();
for (SelectItem item : items) {
- if (item.getFunction() == null) {
+ if (item.getAggregateFunction() == null) {
return false;
}
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java
index 59c6283..7c8abfe 100644
--- a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java
+++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java
@@ -27,6 +27,7 @@ import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
@@ -36,11 +37,13 @@ import javax.sql.DataSource;
import org.apache.metamodel.AbstractDataContext;
import org.apache.metamodel.BatchUpdateScript;
import org.apache.metamodel.MetaModelException;
+import org.apache.metamodel.MetaModelHelper;
import org.apache.metamodel.UpdateScript;
import org.apache.metamodel.UpdateableDataContext;
import org.apache.metamodel.data.DataSet;
import org.apache.metamodel.data.EmptyDataSet;
import org.apache.metamodel.data.MaxRowsDataSet;
+import org.apache.metamodel.data.ScalarFunctionDataSet;
import org.apache.metamodel.jdbc.dialects.DB2QueryRewriter;
import org.apache.metamodel.jdbc.dialects.DefaultQueryRewriter;
import org.apache.metamodel.jdbc.dialects.H2QueryRewriter;
@@ -53,6 +56,7 @@ import org.apache.metamodel.jdbc.dialects.PostgresqlQueryRewriter;
import org.apache.metamodel.jdbc.dialects.SQLServerQueryRewriter;
import org.apache.metamodel.query.CompiledQuery;
import org.apache.metamodel.query.Query;
+import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.schema.ColumnTypeImpl;
import org.apache.metamodel.schema.Schema;
@@ -84,8 +88,8 @@ public class JdbcDataContext extends AbstractDataContext implements UpdateableDa
public static final String DATABASE_PRODUCT_ORACLE = "Oracle";
public static final String DATABASE_PRODUCT_HIVE = "Apache Hive";
- public static final ColumnType COLUMN_TYPE_CLOB_AS_STRING = new ColumnTypeImpl("CLOB",
- SuperColumnType.LITERAL_TYPE, String.class, true);
+ public static final ColumnType COLUMN_TYPE_CLOB_AS_STRING = new ColumnTypeImpl("CLOB", SuperColumnType.LITERAL_TYPE,
+ String.class, true);
public static final ColumnType COLUMN_TYPE_BLOB_AS_BYTES = new ColumnTypeImpl("BLOB", SuperColumnType.BINARY_TYPE,
byte[].class, true);
@@ -320,15 +324,25 @@ public class JdbcDataContext extends AbstractDataContext implements UpdateableDa
// otherwise
jdbcCompiledQuery.returnLease(lease);
throw JdbcUtils.wrapException(e, "execute compiled query");
+ } catch (RuntimeException e) {
+ // only close in case of an error - the JdbcDataSet will close
+ // otherwise
+ jdbcCompiledQuery.returnLease(lease);
+ throw e;
}
return dataSet;
}
private DataSet execute(Connection connection, Query query, Statement statement, JdbcCompiledQuery compiledQuery,
- JdbcCompiledQueryLease lease, Object[] values) throws SQLException {
- if (_databaseProductName.equals(DATABASE_PRODUCT_POSTGRESQL)) {
+ JdbcCompiledQueryLease lease, Object[] values) throws SQLException, MetaModelException {
+ if (MetaModelHelper.containsNonSelectScalaFunctions(query)) {
+ throw new MetaModelException(
+ "Scalar functions outside of SELECT clause is not supported for JDBC databases. Query rejected: "
+ + query);
+ }
+ if (_databaseProductName.equals(DATABASE_PRODUCT_POSTGRESQL)) {
try {
// this has to be done in order to make a result set not load
// all data in memory only for Postgres.
@@ -340,6 +354,17 @@ public class JdbcDataContext extends AbstractDataContext implements UpdateableDa
ResultSet resultSet = null;
+ // build a list of select items whose scalar functions has to be
+ // evaluated client-side
+ final List<SelectItem> scalarFunctionSelectItems = MetaModelHelper
+ .getScalarFunctionSelectItems(query.getSelectClause().getItems());
+ for (Iterator<SelectItem> it = scalarFunctionSelectItems.iterator(); it.hasNext();) {
+ final SelectItem selectItem = (SelectItem) it.next();
+ if (_queryRewriter.isScalarFunctionSupported(selectItem.getScalarFunction())) {
+ it.remove();
+ }
+ }
+
boolean postProcessFirstRow = false;
final Integer firstRow = query.getFirstRow();
if (firstRow != null) {
@@ -433,6 +458,12 @@ public class JdbcDataContext extends AbstractDataContext implements UpdateableDa
if (postProcessMaxRows) {
dataSet = new MaxRowsDataSet(dataSet, maxRows);
}
+
+ if (!scalarFunctionSelectItems.isEmpty()) {
+ dataSet = new ScalarFunctionDataSet(scalarFunctionSelectItems, dataSet);
+ dataSet = MetaModelHelper.getSelection(query.getSelectClause().getItems(), dataSet);
+ }
+
} catch (SQLException exception) {
if (resultSet != null) {
resultSet.close();
@@ -451,16 +482,20 @@ public class JdbcDataContext extends AbstractDataContext implements UpdateableDa
} catch (SQLException e) {
throw JdbcUtils.wrapException(e, "create statement for query");
}
- DataSet dataSet = null;
- try {
+ final DataSet dataSet;
+ try {
dataSet = execute(connection, query, statement, null, null, null);
-
} catch (SQLException e) {
// only close in case of an error - the JdbcDataSet will close
// otherwise
close(connection, null, statement);
throw JdbcUtils.wrapException(e, "execute query");
+ } catch (RuntimeException e) {
+ // only close in case of an error - the JdbcDataSet will close
+ // otherwise
+ close(connection, null, statement);
+ throw e;
}
return dataSet;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/d17a10dd/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataSet.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataSet.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataSet.java
index aebe4d4..a1b76f7 100644
--- a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataSet.java
+++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataSet.java
@@ -148,7 +148,7 @@ final class JdbcDataSet extends AbstractDataSet {
private Object getValue(ResultSet resultSet, int i) throws SQLException {
final SelectItem selectItem = getHeader().getSelectItem(i);
final int columnIndex = i + 1;
- if (selectItem.getFunction() == null) {
+ if (selectItem.getAggregateFunction() == null) {
Column column = selectItem.getColumn();
if (column != null) {
ColumnType type = column.getType();