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 <[email protected]> Authored: Wed Oct 7 20:23:24 2015 +0200 Committer: Kasper Sørensen <[email protected]> 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();
