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();