You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by lo...@apache.org on 2017/12/08 22:07:00 UTC
[2/2] metamodel git commit: METAMODEL-1173: Fixed issue with
evaluating ScalarFunctions in WHERE closes apache/metamodel#171
METAMODEL-1173: Fixed issue with evaluating ScalarFunctions in WHERE closes apache/metamodel#171
Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/39947f55
Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/39947f55
Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/39947f55
Branch: refs/heads/master
Commit: 39947f559078f3a633cd6b0df6e52744a9929737
Parents: 6fc258f
Author: Kasper Sørensen <i....@gmail.com>
Authored: Fri Dec 8 22:58:03 2017 +0100
Committer: Dennis Du Krøger <lo...@apache.org>
Committed: Fri Dec 8 23:00:33 2017 +0100
----------------------------------------------------------------------
.travis.yml | 2 +
CHANGES.md | 3 +-
.../org/apache/metamodel/MetaModelHelper.java | 56 ++++-
.../metamodel/QueryPostprocessDataContext.java | 24 +-
.../apache/metamodel/data/FilteredDataSet.java | 85 +++----
.../metamodel/data/SimpleDataSetHeader.java | 6 -
.../org/apache/metamodel/query/FilterItem.java | 12 +-
.../apache/metamodel/query/FunctionType.java | 2 +
.../metamodel/query/FunctionTypeFactory.java | 7 +
.../metamodel/query/MapValueFunction.java | 4 +-
.../org/apache/metamodel/query/SelectItem.java | 93 ++++----
.../metamodel/query/SubstringFunction.java | 116 +++++++++
.../metamodel/query/ToStringFunction.java | 16 ++
.../query/parser/SelectItemParser.java | 27 ++-
.../apache/metamodel/util/EqualsBuilder.java | 31 ++-
.../apache/metamodel/MetaModelHelperTest.java | 90 +++++--
.../QueryPostprocessDataContextTest.java | 86 ++++++-
.../apache/metamodel/query/SelectItemTest.java | 15 ++
.../metamodel/query/SubstringFunctionTest.java | 77 ++++++
.../metamodel/query/parser/QueryParserTest.java | 21 +-
.../mongodb/mongo2/MongoDbDataContextTest.java | 2 +-
.../mongodb/mongo3/MongoDbDataContext.java | 236 +++++++++++--------
.../mongodb/mongo3/MongoDbDeleteBuilder.java | 6 +-
.../mongodb/mongo3/MongoDbDataContextTest.java | 190 ++++++++++-----
24 files changed, 870 insertions(+), 337 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index 33b571c..1b1e342 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,8 @@ jdk:
- oraclejdk8
before_install:
+ # copy integration test config
+ - cp travis-metamodel-integrationtest-configuration.properties /home/travis/metamodel-integrationtest-configuration.properties
# install Neo4j locally:
- wget dist.neo4j.org/neo4j-community-2.2.3-unix.tar.gz
- tar -xzf neo4j-community-2.2.3-unix.tar.gz
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 3fd8879..f442440 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,4 +1,5 @@
- * [METAMODEL-1169] - Fixed issue with SQL Server milliseconds precision in WHERE
+ * [METAMODEL-1169] - Fixed issue with SQL Server milliseconds precision in WHERE.
+ * [METAMODEL-1173] - Fixed parsing and handling of scalar functions in WHERE clause.
* [METAMODEL-1171] - Fixed parsing of query operators with DATE, TIME, TIMESTAMP prefix to operand date/time values.
### Apache MetaModel 5.0
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 2b547da..59900f9 100644
--- a/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
+++ b/core/src/main/java/org/apache/metamodel/MetaModelHelper.java
@@ -32,6 +32,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import org.apache.metamodel.data.CachingDataSetHeader;
import org.apache.metamodel.data.DataSet;
@@ -40,7 +41,6 @@ import org.apache.metamodel.data.DefaultRow;
import org.apache.metamodel.data.EmptyDataSet;
import org.apache.metamodel.data.FilteredDataSet;
import org.apache.metamodel.data.FirstRowDataSet;
-import org.apache.metamodel.data.IRowFilter;
import org.apache.metamodel.data.InMemoryDataSet;
import org.apache.metamodel.data.MaxRowsDataSet;
import org.apache.metamodel.data.Row;
@@ -270,20 +270,40 @@ public final class MetaModelHelper {
}
public static DataSet getFiltered(DataSet dataSet, Iterable<FilterItem> filterItems) {
- List<IRowFilter> filters = CollectionUtils.map(filterItems, filterItem -> {
- return filterItem;
- });
- if (filters.isEmpty()) {
- return dataSet;
- }
-
- return new FilteredDataSet(dataSet, filters.toArray(new IRowFilter[filters.size()]));
+ final FilterItem[] filterItemsArray =
+ StreamSupport.stream(filterItems.spliterator(), false).toArray(FilterItem[]::new);
+ return getFiltered(dataSet, filterItemsArray);
}
public static DataSet getFiltered(DataSet dataSet, FilterItem... filterItems) {
+ if (filterItems == null || filterItems.length == 0) {
+ return dataSet;
+ }
return getFiltered(dataSet, Arrays.asList(filterItems));
}
+ public static DataSet getFiltered(DataSet dataSet, Collection<FilterItem> filterItems) {
+ if (filterItems == null || filterItems.isEmpty()) {
+ return dataSet;
+ }
+ final List<SelectItem> selectItemsOnOutput = dataSet.getSelectItems();
+ final Iterable<SelectItem> selectItems =
+ filterItems.stream().map(f -> f.getSelectItem()).filter(s -> s != null)::iterator;
+ final List<SelectItem> scalarFunctionSelectItems =
+ getUnmaterializedScalarFunctionSelectItems(selectItems, dataSet);
+ final boolean calculateScalarFunctions = !scalarFunctionSelectItems.isEmpty();
+ if (calculateScalarFunctions) {
+ // scalar functions are needed in evaluation of the filters
+ dataSet = new ScalarFunctionDataSet(scalarFunctionSelectItems, dataSet);
+ }
+ final FilteredDataSet filteredDataSet = new FilteredDataSet(dataSet, filterItems);
+ if (calculateScalarFunctions) {
+ return getSelection(selectItemsOnOutput, filteredDataSet);
+ } else {
+ return filteredDataSet;
+ }
+ }
+
public static DataSet getSelection(final List<SelectItem> selectItems, final DataSet dataSet) {
final List<SelectItem> dataSetSelectItems = dataSet.getSelectItems();
@@ -317,7 +337,8 @@ public final class MetaModelHelper {
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) {
DataSet result = dataSet;
if (groupByItems != null && groupByItems.size() > 0) {
Map<Row, Map<SelectItem, List<Object>>> uniqueRows = new HashMap<Row, Map<SelectItem, List<Object>>>();
@@ -524,8 +545,21 @@ public final class MetaModelHelper {
}
public static List<SelectItem> getScalarFunctionSelectItems(Iterable<SelectItem> selectItems) {
+ return getUnmaterializedScalarFunctionSelectItems(selectItems, null);
+ }
+
+ /**
+ * Gets select items with scalar functions that haven't already been materialized in a data set.
+ *
+ * @param selectItems
+ * @param dataSetWithMaterializedSelectItems a {@link DataSet} containing the already materialized select items
+ * @return
+ */
+ public static List<SelectItem> getUnmaterializedScalarFunctionSelectItems(Iterable<SelectItem> selectItems,
+ DataSet dataSetWithMaterializedSelectItems) {
return CollectionUtils.filter(selectItems, arg -> {
- return arg.getScalarFunction() != null;
+ return arg.getScalarFunction() != null && (dataSetWithMaterializedSelectItems == null
+ || dataSetWithMaterializedSelectItems.indexOf(arg) == -1);
});
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 c77479b..d6e287d 100644
--- a/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
+++ b/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -157,7 +158,8 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
if (whereItems.size() == 1) {
final FilterItem whereItem = whereItems.get(0);
final SelectItem selectItem = whereItem.getSelectItem();
- if (!whereItem.isCompoundFilter() && selectItem != null && selectItem.getColumn() != null) {
+ if (!whereItem.isCompoundFilter() && selectItem != null && !selectItem.hasFunction()
+ && selectItem.getColumn() != null) {
final Column column = selectItem.getColumn();
if (column.isPrimaryKey() && OperatorType.EQUALS_TO.equals(whereItem.getOperator())) {
logger.debug(
@@ -284,7 +286,7 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
// We need to materialize a single table
final Table table = MetaModelHelper.resolveTable(fromItem);
final List<SelectItem> selectItemsToMaterialize = new ArrayList<SelectItem>();
-
+
for (final SelectItem selectItem : selectItems) {
final FromItem selectedFromItem = selectItem.getFromItem();
if (selectedFromItem != null) {
@@ -358,6 +360,7 @@ public abstract class QueryPostprocessDataContext extends AbstractDataContext im
if (dataSet == null) {
throw new IllegalStateException("FromItem was not succesfully materialized: " + fromItem);
}
+
return dataSet;
}
@@ -406,18 +409,15 @@ 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);
- }
+ if (whereItems == null || whereItems.isEmpty()) {
+ return selectItems;
}
final List<SelectItem> evaluatedSelectItems = MetaModelHelper.getEvaluatedSelectItems(whereItems);
- return CollectionUtils.concat(true, primarySelectItems, evaluatedSelectItems);
+
+ final LinkedHashSet<SelectItem> workingSelectItems = new LinkedHashSet<>();
+ workingSelectItems.addAll(selectItems);
+ workingSelectItems.addAll(evaluatedSelectItems);
+ return new ArrayList<>(workingSelectItems);
}
/**
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 c322401..dc7ec05 100644
--- a/core/src/main/java/org/apache/metamodel/data/FilteredDataSet.java
+++ b/core/src/main/java/org/apache/metamodel/data/FilteredDataSet.java
@@ -18,54 +18,59 @@
*/
package org.apache.metamodel.data;
+import java.util.Collection;
/**
* Wraps another DataSet and transparently applies a set of filters to it.
*/
public final class FilteredDataSet extends AbstractDataSet implements WrappingDataSet {
- private final DataSet _dataSet;
- private final IRowFilter[] _filters;
- private Row _row;
+ private final DataSet _dataSet;
+ private final IRowFilter[] _filters;
+ private Row _row;
- public FilteredDataSet(DataSet dataSet, IRowFilter... filters) {
- super(dataSet);
- _dataSet = dataSet;
- _filters = filters;
- }
+ public FilteredDataSet(DataSet dataSet, Collection<? extends IRowFilter> filterItems) {
+ this(dataSet, filterItems.stream().toArray(IRowFilter[]::new));
+ }
- @Override
- public void close() {
- super.close();
- _dataSet.close();
- }
-
- @Override
- public DataSet getWrappedDataSet() {
- return _dataSet;
- }
+ public FilteredDataSet(DataSet dataSet, IRowFilter... filters) {
+ super(dataSet);
+ _dataSet = dataSet;
+ _filters = filters;
+ }
- @Override
- public boolean next() {
- boolean next = false;
- while (_dataSet.next()) {
- Row row = _dataSet.getRow();
- for (IRowFilter filter : _filters) {
- next = filter.accept(row);
- if (!next) {
- break;
- }
- }
- if (next) {
- _row = row;
- break;
- }
- }
- return next;
- }
+ @Override
+ public void close() {
+ super.close();
+ _dataSet.close();
+ }
- @Override
- public Row getRow() {
- return _row;
- }
+ @Override
+ public DataSet getWrappedDataSet() {
+ return _dataSet;
+ }
+
+ @Override
+ public boolean next() {
+ boolean next = false;
+ while (_dataSet.next()) {
+ Row row = _dataSet.getRow();
+ for (IRowFilter filter : _filters) {
+ next = filter.accept(row);
+ if (!next) {
+ break;
+ }
+ }
+ if (next) {
+ _row = row;
+ break;
+ }
+ }
+ return next;
+ }
+
+ @Override
+ public Row getRow() {
+ return _row;
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 5dc2ca9..3232bad 100644
--- a/core/src/main/java/org/apache/metamodel/data/SimpleDataSetHeader.java
+++ b/core/src/main/java/org/apache/metamodel/data/SimpleDataSetHeader.java
@@ -104,12 +104,6 @@ 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;
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/core/src/main/java/org/apache/metamodel/query/FilterItem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FilterItem.java b/core/src/main/java/org/apache/metamodel/query/FilterItem.java
index 427844b..fe13250 100644
--- a/core/src/main/java/org/apache/metamodel/query/FilterItem.java
+++ b/core/src/main/java/org/apache/metamodel/query/FilterItem.java
@@ -252,7 +252,7 @@ public class FilterItem extends BaseObject implements QueryItem, Cloneable, IRow
return _expression;
}
- StringBuilder sb = new StringBuilder();
+ final StringBuilder sb = new StringBuilder();
if (_childItems == null) {
sb.append(_selectItem.getSameQueryAlias(includeSchemaInColumnPaths));
@@ -268,7 +268,7 @@ public class FilterItem extends BaseObject implements QueryItem, Cloneable, IRow
.getSameQueryAlias(includeSchemaInColumnPaths);
sb.append(selectItemString);
} else {
- ColumnType columnType = _selectItem.getExpectedColumnType();
+ final ColumnType columnType = _selectItem.getExpectedColumnType();
final String sqlValue = FormatHelper.formatSqlValue(columnType, operand);
sb.append(sqlValue);
}
@@ -310,10 +310,10 @@ public class FilterItem extends BaseObject implements QueryItem, Cloneable, IRow
if (_childItems == null) {
// Evaluate a single constraint
- Object selectItemValue = row.getValue(_selectItem);
+ final Object selectItemValue = row.getValue(_selectItem);
Object operandValue = _operand;
if (_operand instanceof SelectItem) {
- SelectItem selectItem = (SelectItem) _operand;
+ final SelectItem selectItem = (SelectItem) _operand;
operandValue = row.getValue(selectItem);
}
if (operandValue == null) {
@@ -339,7 +339,7 @@ public class FilterItem extends BaseObject implements QueryItem, Cloneable, IRow
if (_logicalOperator == LogicalOperator.AND) {
// require all results to be true
for (FilterItem item : _childItems) {
- boolean result = item.evaluate(row);
+ final boolean result = item.evaluate(row);
if (!result) {
return false;
}
@@ -348,7 +348,7 @@ public class FilterItem extends BaseObject implements QueryItem, Cloneable, IRow
} else {
// require at least one result to be true
for (FilterItem item : _childItems) {
- boolean result = item.evaluate(row);
+ final boolean result = item.evaluate(row);
if (result) {
return true;
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 ecedc97..2dad077 100644
--- a/core/src/main/java/org/apache/metamodel/query/FunctionType.java
+++ b/core/src/main/java/org/apache/metamodel/query/FunctionType.java
@@ -41,6 +41,8 @@ public interface FunctionType extends Serializable {
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 static final ScalarFunction SUBSTRING = SubstringFunction.createSqlStyle();
+ public static final ScalarFunction JAVA_SUBSTRING = SubstringFunction.createJavaStyle();
public static final ScalarFunction MAP_VALUE = new MapValueFunction();
public ColumnType getExpectedColumnType(ColumnType type);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 95e25ce..1de7f86 100644
--- a/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java
+++ b/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java
@@ -67,6 +67,13 @@ public class FunctionTypeFactory {
case "TO_DATE":
case "DATE":
return FunctionType.TO_DATE;
+ case "SUBSTRING":
+ case "SUBSTR":
+ case "SUB_STRING":
+ case "SUB_STR":
+ return FunctionType.SUBSTRING;
+ case "JAVA_SUBSTRING":
+ return FunctionType.JAVA_SUBSTRING;
case "MAP_VALUE":
return FunctionType.MAP_VALUE;
default:
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java b/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java
index 099bcab..e017747 100644
--- a/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java
@@ -37,9 +37,9 @@ public final class MapValueFunction extends DefaultScalarFunction {
if (parameters.length == 0) {
throw new IllegalArgumentException("Expecting path parameter to MAP_VALUE function");
}
- Object value = row.getValue(operandItem);
+ final Object value = row.getValue(operandItem);
if (value instanceof Map) {
- Map<?, ?> map = (Map<?, ?>) value;
+ final Map<?, ?> map = (Map<?, ?>) value;
return CollectionUtils.find(map, (String) parameters[0]);
}
return null;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 fec900d..40ac3fc 100644
--- a/core/src/main/java/org/apache/metamodel/query/SelectItem.java
+++ b/core/src/main/java/org/apache/metamodel/query/SelectItem.java
@@ -83,7 +83,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
_column = column;
_fromItem = fromItem;
_function = function;
- _functionParameters = functionParameters;
+ _functionParameters = (functionParameters != null && functionParameters.length == 0) ? null : functionParameters;
_expression = expression;
_subQuerySelectItem = subQuerySelectItem;
_alias = alias;
@@ -351,21 +351,12 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
return _alias;
} else if (_column != null) {
final StringBuilder sb = new StringBuilder();
- if (_function != null) {
- if (_functionApproximationAllowed) {
- sb.append(FUNCTION_APPROXIMATION_PREFIX);
- }
- sb.append(_function.getFunctionName());
- sb.append('(');
- }
if (includeQuotes) {
sb.append(_column.getQuotedName());
} else {
sb.append(_column.getName());
}
- if (_function != null) {
- sb.append(')');
- }
+ appendFunctionSql(sb);
return sb.toString();
} else {
logger.debug("Could not resolve a reasonable super-query alias for SelectItem: {}", toSql());
@@ -382,24 +373,18 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
*/
public String getSameQueryAlias(boolean includeSchemaInColumnPath) {
if (_column != null) {
- StringBuilder sb = new StringBuilder();
- String columnPrefix = getToStringColumnPrefix(includeSchemaInColumnPath);
+ final StringBuilder sb = new StringBuilder();
+ final String columnPrefix = getToStringColumnPrefix(includeSchemaInColumnPath);
sb.append(columnPrefix);
sb.append(_column.getQuotedName());
- if (_function != null) {
- if (_functionApproximationAllowed) {
- sb.insert(0, FUNCTION_APPROXIMATION_PREFIX + _function.getFunctionName() + "(");
- } else {
- sb.insert(0, _function.getFunctionName() + "(");
- }
- sb.append(")");
- }
+ appendFunctionSql(sb);
return sb.toString();
}
- String alias = getAlias();
+ final String alias = getAlias();
if (alias == null) {
- alias = toStringNoAlias(includeSchemaInColumnPath).toString();
- logger.debug("Could not resolve a reasonable same-query alias for SelectItem: {}", toSql());
+ final String result = toStringNoAlias(includeSchemaInColumnPath).toString();
+ logger.debug("Could not resolve a reasonable same-query alias for SelectItem: {}", result);
+ return result;
}
return alias;
}
@@ -438,27 +423,32 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
}
sb.append(_subQuerySelectItem.getSuperQueryAlias());
}
- if (_function != null) {
- final StringBuilder functionBeginning = new StringBuilder();
- if (_functionApproximationAllowed) {
- functionBeginning.append(FUNCTION_APPROXIMATION_PREFIX);
- }
+ appendFunctionSql(sb);
+ return sb;
+ }
- functionBeginning.append(_function.getFunctionName());
- functionBeginning.append('(');
- final Object[] functionParameters = getFunctionParameters();
- if (functionParameters != null && functionParameters.length != 0) {
- for (int i = 0; i < functionParameters.length; i++) {
- functionBeginning.append('\'');
- functionBeginning.append(functionParameters[i]);
- functionBeginning.append('\'');
- functionBeginning.append(',');
- }
+ private void appendFunctionSql(StringBuilder sb) {
+ if (_function == null) {
+ return;
+ }
+ final StringBuilder functionBeginning = new StringBuilder();
+ if (_functionApproximationAllowed) {
+ functionBeginning.append(FUNCTION_APPROXIMATION_PREFIX);
+ }
+
+ functionBeginning.append(_function.getFunctionName());
+ functionBeginning.append('(');
+ sb.insert(0, functionBeginning.toString());
+ final Object[] functionParameters = getFunctionParameters();
+ if (functionParameters != null && functionParameters.length != 0) {
+ for (int i = 0; i < functionParameters.length; i++) {
+ sb.append(',');
+ sb.append('\'');
+ sb.append(functionParameters[i]);
+ sb.append('\'');
}
- sb.insert(0, functionBeginning.toString());
- sb.append(")");
}
- return sb;
+ sb.append(")");
}
private String getToStringColumnPrefix(boolean includeSchemaInColumnPath) {
@@ -509,7 +499,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
return true;
}
- EqualsBuilder eb = new EqualsBuilder();
+ final EqualsBuilder eb = new EqualsBuilder();
if (exactColumnCompare) {
eb.append(this._column == that._column);
eb.append(this._fromItem, that._fromItem);
@@ -517,6 +507,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
eb.append(this._column, that._column);
}
eb.append(this._function, that._function);
+ eb.appendArrays(this._functionParameters, that._functionParameters);
eb.append(this._functionApproximationAllowed, that._functionApproximationAllowed);
eb.append(this._expression, that._expression);
if (_subQuerySelectItem != null) {
@@ -528,13 +519,14 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
}
return eb.isEquals();
}
-
+
@Override
protected void decorateIdentity(List<Object> identifiers) {
identifiers.add(_expression);
identifiers.add(_alias);
identifiers.add(_column);
identifiers.add(_function);
+ identifiers.add(_functionParameters);
identifiers.add(_functionApproximationAllowed);
if (_fromItem == null && _column != null && _column.getTable() != null) {
// add a FromItem representing the column's table - this makes equal
@@ -587,7 +579,18 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
* @return
*/
public SelectItem replaceFunction(FunctionType function) {
- return new SelectItem(_column, _fromItem, function, _functionParameters, _expression, _subQuerySelectItem,
+ return replaceFunction(function, new Object[0]);
+ }
+
+ /**
+ * Creates a copy of the {@link SelectItem}, with a different {@link FunctionType} and parameters.
+ *
+ * @param function
+ * @param functionParameters
+ * @return
+ */
+ public SelectItem replaceFunction(FunctionType function, Object... functionParameters) {
+ return new SelectItem(_column, _fromItem, function, functionParameters, _expression, _subQuerySelectItem,
_alias, _functionApproximationAllowed);
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/core/src/main/java/org/apache/metamodel/query/SubstringFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/SubstringFunction.java b/core/src/main/java/org/apache/metamodel/query/SubstringFunction.java
new file mode 100644
index 0000000..d26c3f9
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/SubstringFunction.java
@@ -0,0 +1,116 @@
+/**
+ * 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 SubstringFunction implements ScalarFunction {
+
+ private static final long serialVersionUID = 1L;
+
+ public static SubstringFunction createSqlStyle() {
+ return new SubstringFunction(true, true);
+ }
+
+ public static SubstringFunction createJavaStyle() {
+ return new SubstringFunction(false, false);
+ }
+
+ private final boolean oneBased;
+ private final boolean secondParamIsCharCount;
+
+ /**
+ * Creates a {@link SubstringFunction}
+ *
+ * @param oneBased true if the character index parameters are 1 based, like most SQL SUBSTRING functions (instead of
+ * 0 based, like Java).
+ * @param secondParamIsCharCount true if the (optional) second parameter is a "character count", like most SQL
+ * SUBSTRING functions (instead of end-index, like Java)
+ *
+ */
+ public SubstringFunction(boolean oneBased, boolean secondParamIsCharCount) {
+ this.oneBased = oneBased;
+ this.secondParamIsCharCount = secondParamIsCharCount;
+ }
+
+ @Override
+ public ColumnType getExpectedColumnType(ColumnType type) {
+ return ColumnType.STRING;
+ }
+
+ @Override
+ public String getFunctionName() {
+ if (!oneBased && !secondParamIsCharCount) {
+ return "JAVA_SUBSTRING";
+ }
+ return "SUBSTRING";
+ }
+
+ @Override
+ public Object evaluate(Row row, Object[] parameters, SelectItem operandItem) {
+ final String str = (String) FunctionType.TO_STRING.evaluate(row, null, operandItem);
+ if (str == null) {
+ return null;
+ }
+ final int numParameters = parameters == null ? 0 : parameters.length;
+ switch (numParameters) {
+ case 0:
+ return str;
+ case 1:
+ int begin = toInt(parameters[0]);
+ if (oneBased) {
+ begin--;
+ }
+ if (begin >= str.length()) {
+ return "";
+ }
+ return str.substring(begin);
+ default:
+ int from = toInt(parameters[0]);
+ if (oneBased) {
+ from--;
+ }
+ int to = toInt(parameters[1]);
+ if (secondParamIsCharCount) {
+ to = from + to;
+ } else if (oneBased) {
+ to--;
+ }
+
+ if (from >= str.length() || from > to) {
+ return "";
+ }
+ if (to >= str.length()) {
+ return str.substring(from);
+ }
+ return str.substring(from, to);
+ }
+ }
+
+ private int toInt(Object parameter) {
+ final Number number = NumberComparator.toNumber(parameter);
+ if (number == null) {
+ throw new IllegalArgumentException("Not a valid substring parameter: " + parameter);
+ }
+ return Math.max(0, number.intValue());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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
index 97d3fa9..eb88e23 100644
--- a/core/src/main/java/org/apache/metamodel/query/ToStringFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/ToStringFunction.java
@@ -18,8 +18,14 @@
*/
package org.apache.metamodel.query;
+import java.io.Reader;
+import java.sql.Clob;
+import java.sql.SQLException;
+
+import org.apache.metamodel.MetaModelException;
import org.apache.metamodel.data.Row;
import org.apache.metamodel.schema.ColumnType;
+import org.apache.metamodel.util.FileHelper;
public class ToStringFunction extends DefaultScalarFunction {
@@ -44,6 +50,16 @@ public class ToStringFunction extends DefaultScalarFunction {
if (value == null || value instanceof String) {
return value;
}
+ if (value instanceof Clob) {
+ final Clob clob = (Clob) value;
+ try {
+ final Reader reader = clob.getCharacterStream();
+ final String result = FileHelper.readAsString(reader);
+ return result;
+ } catch (SQLException e) {
+ throw new MetaModelException("Failed to read CLOB to String", e);
+ }
+ }
return String.valueOf(value);
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java b/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java
index d358d72..79d5dc3 100644
--- a/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java
+++ b/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java
@@ -18,9 +18,16 @@
*/
package org.apache.metamodel.query.parser;
+import java.util.Arrays;
+
import org.apache.metamodel.MetaModelException;
import org.apache.metamodel.MetaModelHelper;
-import org.apache.metamodel.query.*;
+import org.apache.metamodel.query.CountAggregateFunction;
+import org.apache.metamodel.query.FromItem;
+import org.apache.metamodel.query.FunctionType;
+import org.apache.metamodel.query.FunctionTypeFactory;
+import org.apache.metamodel.query.Query;
+import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.schema.Column;
public final class SelectItemParser implements QueryPartProcessor {
@@ -108,6 +115,8 @@ public final class SelectItemParser implements QueryPartProcessor {
final boolean functionApproximation;
final FunctionType function;
+ Object[] functionParameters = null;
+
final int startParenthesis = expression.indexOf('(');
if (startParenthesis > 0 && expression.endsWith(")")) {
functionApproximation = (expression.startsWith(SelectItem.FUNCTION_APPROXIMATION_PREFIX));
@@ -120,10 +129,18 @@ public final class SelectItemParser implements QueryPartProcessor {
final SelectItem selectItem = SelectItem.getCountAllItem();
selectItem.setFunctionApproximationAllowed(functionApproximation);
return selectItem;
+ } else {
+ final String[] expressionSplit = expression.split(",");
+ if (expressionSplit.length > 1) {
+ // there are multiple parameters to the function
+ expression = expressionSplit[0].trim();
+ functionParameters = Arrays.copyOfRange(expressionSplit, 1, expressionSplit.length, String[].class);
+ }
}
}
} else {
function = null;
+ functionParameters = null;
functionApproximation = false;
}
@@ -170,19 +187,19 @@ public final class SelectItemParser implements QueryPartProcessor {
column = fromItem.getTable().getColumnByName(part1);
if (column != null) {
final String part2 = columnName.substring(offset + 1);
- return new SelectItem(new MapValueFunction(), new Object[] { part2 }, column, fromItem);
+ return new SelectItem(FunctionType.MAP_VALUE, new Object[] { part2 }, column, fromItem);
}
}
if (column != null) {
- final SelectItem selectItem = new SelectItem(function, column, fromItem);
+ final SelectItem selectItem = new SelectItem(function, functionParameters, column, fromItem);
selectItem.setFunctionApproximationAllowed(functionApproximation);
return selectItem;
}
} else if (fromItem.getSubQuery() != null) {
final Query subQuery = fromItem.getSubQuery();
- final SelectItem subQuerySelectItem = new SelectItemParser(subQuery, _allowExpressionBasedSelectItems)
- .findSelectItem(columnName);
+ final SelectItem subQuerySelectItem =
+ new SelectItemParser(subQuery, _allowExpressionBasedSelectItems).findSelectItem(columnName);
if (subQuerySelectItem == null) {
return null;
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/core/src/main/java/org/apache/metamodel/util/EqualsBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/util/EqualsBuilder.java b/core/src/main/java/org/apache/metamodel/util/EqualsBuilder.java
index 656952f..b27d964 100644
--- a/core/src/main/java/org/apache/metamodel/util/EqualsBuilder.java
+++ b/core/src/main/java/org/apache/metamodel/util/EqualsBuilder.java
@@ -40,12 +40,31 @@ public final class EqualsBuilder {
return this;
}
- public EqualsBuilder append(Object o1, Object o2) {
- if (equals) {
- equals = equals(o1, o2);
- }
- return this;
- }
+ public EqualsBuilder append(Object o1, Object o2) {
+ if (equals) {
+ equals = equals(o1, o2);
+ }
+ return this;
+ }
+
+ public EqualsBuilder appendArrays(Object[] o1, Object[] o2) {
+ if (equals) {
+ if (o1 == null) {
+ o1 = new Object[0];
+ }
+ if (o2 == null) {
+ o2 = new Object[0];
+ }
+ if (o1.length != o2.length) {
+ equals = false;
+ } else {
+ for (int i = 0; i < o1.length; i++) {
+ append(o1[i], o2[i]);
+ }
+ }
+ }
+ return this;
+ }
public static boolean equals(final Object obj1, final Object obj2) {
if (obj1 == obj2) {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java b/core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java
index 7196bf4..a2425d3 100644
--- a/core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java
+++ b/core/src/test/java/org/apache/metamodel/MetaModelHelperTest.java
@@ -33,6 +33,7 @@ import org.apache.metamodel.data.SimpleDataSetHeader;
import org.apache.metamodel.data.SubSelectionDataSet;
import org.apache.metamodel.query.FilterItem;
import org.apache.metamodel.query.FromItem;
+import org.apache.metamodel.query.FunctionType;
import org.apache.metamodel.query.JoinType;
import org.apache.metamodel.query.OperatorType;
import org.apache.metamodel.query.OrderByItem;
@@ -44,9 +45,11 @@ import org.apache.metamodel.schema.MutableColumn;
import org.apache.metamodel.schema.MutableTable;
import org.apache.metamodel.schema.Schema;
import org.apache.metamodel.schema.Table;
+import org.junit.Test;
public class MetaModelHelperTest extends MetaModelTestCase {
+ @Test
public void testLeftJoin() throws Exception {
SelectItem si1 = new SelectItem(new MutableColumn("person_id", ColumnType.INTEGER));
SelectItem si2 = new SelectItem(new MutableColumn("person_name", ColumnType.VARCHAR));
@@ -67,8 +70,8 @@ public class MetaModelHelperTest extends MetaModelTestCase {
data2.add(new Object[] { 2, "bad boy", "bb" });
data2.add(new Object[] { 4, "trying harder", "try" });
- DataSet ds1 = createDataSet(Lists.newArrayList(si1, si2, si3, si4 ), data1);
- DataSet ds2 = createDataSet(Lists.newArrayList(si5, si6, si7 ), data2);
+ DataSet ds1 = createDataSet(Lists.newArrayList(si1, si2, si3, si4), data1);
+ DataSet ds2 = createDataSet(Lists.newArrayList(si5, si6, si7), data2);
FilterItem[] onConditions = new FilterItem[1];
onConditions[0] = new FilterItem(si4, OperatorType.EQUALS_TO, si5);
@@ -82,6 +85,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertEquals(5, objectArrays.size());
}
+ @Test
public void testRightJoin() throws Exception {
SelectItem si1 = new SelectItem(new MutableColumn("person_id", ColumnType.INTEGER));
SelectItem si2 = new SelectItem(new MutableColumn("person_name", ColumnType.VARCHAR));
@@ -101,8 +105,8 @@ public class MetaModelHelperTest extends MetaModelTestCase {
data2.add(new Object[] { 2, "bad boy", "bb" });
data2.add(new Object[] { 4, "trying harder", "try" });
- DataSet ds1 = createDataSet(Lists.newArrayList(si1, si2, si3, si4 ), data1);
- DataSet ds2 = createDataSet(Lists.newArrayList( si5, si6, si7 ), data2);
+ DataSet ds1 = createDataSet(Lists.newArrayList(si1, si2, si3, si4), data1);
+ DataSet ds2 = createDataSet(Lists.newArrayList(si5, si6, si7), data2);
FilterItem[] onConditions = new FilterItem[1];
onConditions[0] = new FilterItem(si4, OperatorType.EQUALS_TO, si5);
@@ -114,11 +118,11 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertEquals(3, objectArrays.size());
}
+ @Test
public void testSimpleCarthesianProduct() throws Exception {
DataSet dataSet = MetaModelHelper.getCarthesianProduct(createDataSet1(), createDataSet2());
List<String> results = new ArrayList<String>();
-
while (dataSet.next()) {
results.add(dataSet.getRow().toString());
}
@@ -130,9 +134,45 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertTrue(results.contains("Row[values=[o, b]]"));
assertTrue(results.contains("Row[values=[o, a]]"));
assertTrue(results.contains("Row[values=[o, r]]"));
+ }
+
+ @Test
+ public void testGetFilteredWithScalarFunctionInWhere() throws Exception {
+ final DataSet ds1 = createDataSet3(); // contains ["w00p",true] and ["yippie",false]
+ final SelectItem selectItem1 = ds1.getSelectItems().get(0);
+ final DataSet ds2 = MetaModelHelper.getFiltered(ds1, new FilterItem(
+ selectItem1.replaceFunction(FunctionType.SUBSTRING, 2, 2), OperatorType.EQUALS_TO, "00"));
+ final List<Object[]> resultRows = ds2.toObjectArrays();
+
+ assertEquals(1, resultRows.size());
+ assertEquals("[w00p, true]", Arrays.toString(resultRows.get(0)));
+ }
+
+ @Test
+ public void testGetSelectionWithScalarFunctionInSelectItem() throws Exception {
+ final DataSet ds1 = createDataSet3(); // contains ["w00p",true] and ["yippie",false]
+ final SelectItem selectItem1 = ds1.getSelectItems().get(0);
+ final DataSet ds2 = MetaModelHelper.getSelection(new SelectItem[] { selectItem1.replaceFunction(FunctionType.SUBSTRING, 2, 2) }, ds1);
+ final List<Object[]> resultRows = ds2.toObjectArrays();
+
+ assertEquals(2, resultRows.size());
+ assertEquals("[00]", Arrays.toString(resultRows.get(0)));
+ assertEquals("[ip]", Arrays.toString(resultRows.get(1)));
+ }
+ @Test
+ public void testGetSelectionWithScalarFunctionAndNonFunctionInSelectItem() throws Exception {
+ final DataSet ds1 = createDataSet3(); // contains ["w00p",true] and ["yippie",false]
+ final SelectItem selectItem1 = ds1.getSelectItems().get(0);
+ final DataSet ds2 = MetaModelHelper.getSelection(new SelectItem[] { selectItem1, selectItem1.replaceFunction(FunctionType.SUBSTRING, 2, 2) }, ds1);
+ final List<Object[]> resultRows = ds2.toObjectArrays();
+
+ assertEquals(2, resultRows.size());
+ assertEquals("[w00p, 00]", Arrays.toString(resultRows.get(0)));
+ assertEquals("[yippie, ip]", Arrays.toString(resultRows.get(1)));
}
+ @Test
public void testTripleCarthesianProduct() throws Exception {
DataSet dataSet = MetaModelHelper.getCarthesianProduct(createDataSet1(), createDataSet2(), createDataSet3());
assertEquals(4, dataSet.getSelectItems().size());
@@ -142,6 +182,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertFalse(dataSet.next());
}
+ @Test
public void testTripleCarthesianProductWithWhereItems() throws Exception {
DataSet ds1 = createDataSet1();
DataSet ds2 = createDataSet2();
@@ -156,6 +197,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertFalse(dataSet.next());
}
+ @Test
public void testGetCarthesianProductNoRows() throws Exception {
DataSet dataSet = MetaModelHelper.getCarthesianProduct(createDataSet4(), createDataSet2(), createDataSet3());
assertEquals(4, dataSet.getSelectItems().size());
@@ -170,6 +212,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertFalse(dataSet.next());
}
+ @Test
public void testGetOrdered() throws Exception {
DataSet dataSet = createDataSet3();
List<OrderByItem> orderByItems = new ArrayList<OrderByItem>();
@@ -189,8 +232,8 @@ public class MetaModelHelperTest extends MetaModelTestCase {
data1.add(new Object[] { "o" });
data1.add(new Object[] { "o" });
- DataSet dataSet1 = createDataSet(
- Lists.newArrayList( new SelectItem(new MutableColumn("foo", ColumnType.VARCHAR)) ), data1);
+ DataSet dataSet1 =
+ createDataSet(Lists.newArrayList(new SelectItem(new MutableColumn("foo", ColumnType.VARCHAR))), data1);
return dataSet1;
}
@@ -200,7 +243,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
data2.add(new Object[] { "b" });
data2.add(new Object[] { "a" });
data2.add(new Object[] { "r" });
- DataSet dataSet2 = createDataSet(Lists.newArrayList(new SelectItem("bar", "bar") ), data2);
+ DataSet dataSet2 = createDataSet(Lists.newArrayList(new SelectItem("bar", "bar")), data2);
return dataSet2;
}
@@ -209,15 +252,15 @@ public class MetaModelHelperTest extends MetaModelTestCase {
data3.add(new Object[] { "w00p", true });
data3.add(new Object[] { "yippie", false });
- DataSet dataSet3 = createDataSet(Lists.newArrayList(new SelectItem("expression", "e"),
- new SelectItem("webish?", "w") ), data3);
+ DataSet dataSet3 = createDataSet(
+ Lists.newArrayList(new SelectItem("expression", "e"), new SelectItem("webish?", "w")), data3);
return dataSet3;
}
private DataSet createDataSet4() {
List<Object[]> data4 = new ArrayList<Object[]>();
- DataSet dataSet4 = createDataSet(Lists.newArrayList(new SelectItem("abc", "abc") ), data4);
+ DataSet dataSet4 = createDataSet(Lists.newArrayList(new SelectItem("abc", "abc")), data4);
return dataSet4;
}
@@ -234,9 +277,9 @@ public class MetaModelHelperTest extends MetaModelTestCase {
data5.add(new Object[] { i, "Person_" + i, bigDataSetSize - (i + 1) });
}
- DataSet dataSet5 = createDataSet(Lists.newArrayList( new SelectItem(new MutableColumn("nr", ColumnType.BIGINT)),
- new SelectItem(new MutableColumn("name", ColumnType.STRING)), new SelectItem(new MutableColumn("dnr",
- ColumnType.BIGINT)) ), data5);
+ DataSet dataSet5 = createDataSet(Lists.newArrayList(new SelectItem(new MutableColumn("nr", ColumnType.BIGINT)),
+ new SelectItem(new MutableColumn("name", ColumnType.STRING)),
+ new SelectItem(new MutableColumn("dnr", ColumnType.BIGINT))), data5);
return dataSet5;
}
@@ -256,6 +299,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
return dataSet6;
}
+ @Test
public void testGetTables() throws Exception {
MutableTable table1 = new MutableTable("table1");
MutableTable table2 = new MutableTable("table2");
@@ -281,6 +325,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertTrue(Arrays.asList(tables).contains(table2));
}
+ @Test
public void testGetTableColumns() throws Exception {
MutableTable table1 = new MutableTable("table1");
MutableColumn column1 = new MutableColumn("c1", ColumnType.BIGINT);
@@ -307,6 +352,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertSame(column3, columns[1]);
}
+ @Test
public void testGetTableFromItems() throws Exception {
Schema schema = getExampleSchema();
Table contributorTable = schema.getTableByName(TABLE_CONTRIBUTOR);
@@ -324,6 +370,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
Arrays.toString(fromItems));
}
+ @Test
public void testGetSelectionNoRows() throws Exception {
SelectItem item1 = new SelectItem("foo", "f");
SelectItem item2 = new SelectItem("bar", "b");
@@ -337,6 +384,7 @@ public class MetaModelHelperTest extends MetaModelTestCase {
assertEquals("[bar AS b, foo AS f]", Arrays.toString(ds.getSelectItems().toArray()));
}
+ @Test
public void testLeftJoinNoRowsOrSingleRow() throws Exception {
SelectItem item1 = new SelectItem("foo", "f");
SelectItem item2 = new SelectItem("bar", "b");
@@ -347,8 +395,8 @@ public class MetaModelHelperTest extends MetaModelTestCase {
DataSet ds1 = new EmptyDataSet(selectItems1);
DataSet ds2 = new EmptyDataSet(selectItems2);
- DataSet joinedDs = MetaModelHelper.getLeftJoin(ds1, ds2, new FilterItem[] { new FilterItem(item2,
- OperatorType.EQUALS_TO, item3) });
+ DataSet joinedDs = MetaModelHelper.getLeftJoin(ds1, ds2,
+ new FilterItem[] { new FilterItem(item2, OperatorType.EQUALS_TO, item3) });
assertEquals(SubSelectionDataSet.class, joinedDs.getClass());
assertEquals("[foo AS f, bar AS b, baz AS z]", Arrays.toString(joinedDs.getSelectItems().toArray()));
@@ -357,21 +405,22 @@ public class MetaModelHelperTest extends MetaModelTestCase {
Row row = new DefaultRow(header1, new Object[] { 1, 2 }, null);
ds1 = new InMemoryDataSet(header1, row);
- joinedDs = MetaModelHelper.getLeftJoin(ds1, ds2, new FilterItem[] { new FilterItem(item2,
- OperatorType.EQUALS_TO, item3) });
+ joinedDs = MetaModelHelper.getLeftJoin(ds1, ds2,
+ new FilterItem[] { new FilterItem(item2, OperatorType.EQUALS_TO, item3) });
assertEquals("[foo AS f, bar AS b, baz AS z]", Arrays.toString(joinedDs.getSelectItems().toArray()));
assertTrue(joinedDs.next());
assertEquals("Row[values=[1, 2, null]]", joinedDs.getRow().toString());
assertFalse(joinedDs.next());
}
+ @Test
public void testCarthesianProductScalability() {
DataSet employees = createDataSet5();
DataSet departmens = createDataSet6();
- FilterItem fi = new FilterItem(employees.getSelectItems().get(2), OperatorType.EQUALS_TO, departmens
- .getSelectItems().get(0));
+ FilterItem fi = new FilterItem(employees.getSelectItems().get(2), OperatorType.EQUALS_TO,
+ departmens.getSelectItems().get(0));
DataSet joined = MetaModelHelper.getCarthesianProduct(new DataSet[] { employees, departmens }, fi);
int count = 0;
@@ -380,6 +429,5 @@ public class MetaModelHelperTest extends MetaModelTestCase {
}
assertTrue(count == bigDataSetSize);
-
}
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 04cfae9..79ca76e 100644
--- a/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java
+++ b/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java
@@ -62,18 +62,18 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase {
public void testSchemaTraversalWithAliasTable() {
final MockUpdateableDataContext dc = new MockUpdateableDataContext();
-
+
final Column column = dc.getColumnByQualifiedLabel("foo");
assertEquals("table", column.getTable().getName());
}
-
+
public void testNoAliasTableWhenSystemPropertySet() {
System.setProperty(QueryPostprocessDataContext.SYSTEM_PROPERTY_CREATE_DEFAULT_TABLE_ALIAS, "false");
try {
final MockUpdateableDataContext dc = new MockUpdateableDataContext();
final List<Table> tables = dc.getDefaultSchema().getTables();
assertEquals(1, tables.size());
-
+
assertEquals("table", tables.get(0).getName());
} finally {
System.clearProperty(QueryPostprocessDataContext.SYSTEM_PROPERTY_CREATE_DEFAULT_TABLE_ALIAS);
@@ -87,7 +87,7 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase {
assertEquals("table", tables.get(0).getName());
}
-
+
public void testAliasTableQueries() {
final MockUpdateableDataContext dc = new MockUpdateableDataContext();
final List<Table> tables = dc.getDefaultSchema().getTables();
@@ -338,17 +338,15 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase {
}
public void testScalarFunctionWhere() throws Exception {
- MockDataContext dc = new MockDataContext("sch", "tab", "1");
- Table table = dc.getDefaultSchema().getTable(0);
+ final MockDataContext dc = new MockDataContext("sch", "tab", "1");
+ final Table table = dc.getDefaultSchema().getTable(0);
- Query query = dc.query().from(table).select("foo").where(FunctionType.TO_NUMBER, "bar").eq(1).toQuery();
+ final 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);
+ final DataSet ds = dc.executeQuery(query);
assertTrue(ds.next());
- Row row;
-
- row = ds.getRow();
+ final Row row = ds.getRow();
assertEquals("Row[values=[2]]", row.toString());
assertFalse(ds.next());
@@ -1183,4 +1181,70 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase {
assertEquals("[hello, world]", values.toString());
}
+ public void testColumnOnlyUsedInScalarFunctionInWhereClause() throws Exception {
+ final DataContext dc = getDataContext();
+ final Query query = dc.parseQuery(
+ "SELECT contributor_id FROM contributor WHERE JAVA_SUBSTRING(name, 3, 6) = 'per' ORDER BY contributor_id");
+ try (DataSet ds = dc.executeQuery(query)) {
+ assertTrue(ds.next());
+ // kasper
+ assertEquals("1", ds.getRow().getValue(0).toString());
+ assertTrue(ds.next());
+ // jesper
+ assertEquals("6", ds.getRow().getValue(0).toString());
+ assertFalse(ds.next());
+ }
+ }
+
+ public void testQueryDifferentScalarFunctionsOnSameColumnInBothSelectAndWhere() throws Exception {
+ final DataContext dc = getDataContext();
+ final Query query = dc.parseQuery(
+ "SELECT SUBSTRING(name, 1, 3) FROM contributor WHERE JAVA_SUBSTRING(name, 3, 6) = 'per' ORDER BY contributor_id");
+
+ // assert on the parsed select items just to ensure that nothing gets mangled in the parsing
+ assertSame(FunctionType.SUBSTRING, query.getSelectClause().getItem(0).getScalarFunction());
+ assertEquals(" 1", query.getSelectClause().getItem(0).getFunctionParameters()[0]);
+ assertEquals(" 3", query.getSelectClause().getItem(0).getFunctionParameters()[1]);
+ assertSame(FunctionType.JAVA_SUBSTRING, query.getWhereClause().getItem(0).getSelectItem().getScalarFunction());
+ assertEquals(" 3", query.getWhereClause().getItem(0).getSelectItem().getFunctionParameters()[0]);
+ assertEquals(" 6", query.getWhereClause().getItem(0).getSelectItem().getFunctionParameters()[1]);
+
+ try (DataSet ds = dc.executeQuery(query)) {
+ assertTrue(ds.next());
+ // name is "kasper"
+ final Object value1 = ds.getRow().getValue(0);
+ assertEquals("kas", value1.toString());
+ assertTrue(ds.next());
+ // name is "jesper"
+ final Object value2 = ds.getRow().getValue(0);
+ assertEquals("jes", value2.toString());
+ assertFalse(ds.next());
+ }
+ }
+
+ public void testQuerySameScalarFunctionOnSameColumnButDifferentParamsInBothSelectAndWhere() throws Exception {
+ final DataContext dc = getDataContext();
+ final Query query = dc.parseQuery(
+ "SELECT SUBSTRING(name, 1, 3) FROM contributor WHERE SUBSTRING(name, 4, 3) = 'per' ORDER BY contributor_id");
+
+ // assert on the parsed select items just to ensure that nothing gets mangled in the parsing
+ assertSame(FunctionType.SUBSTRING, query.getSelectClause().getItem(0).getScalarFunction());
+ assertEquals(" 1", query.getSelectClause().getItem(0).getFunctionParameters()[0]);
+ assertEquals(" 3", query.getSelectClause().getItem(0).getFunctionParameters()[1]);
+ assertSame(FunctionType.SUBSTRING, query.getWhereClause().getItem(0).getSelectItem().getScalarFunction());
+ assertEquals(" 4", query.getWhereClause().getItem(0).getSelectItem().getFunctionParameters()[0]);
+ assertEquals(" 3", query.getWhereClause().getItem(0).getSelectItem().getFunctionParameters()[1]);
+
+ try (DataSet ds = dc.executeQuery(query)) {
+ assertTrue(ds.next());
+ // name is "kasper"
+ final Object value1 = ds.getRow().getValue(0);
+ assertEquals("kas", value1.toString());
+ assertTrue(ds.next());
+ // name is "jesper"
+ final Object value2 = ds.getRow().getValue(0);
+ assertEquals("jes", value2.toString());
+ assertFalse(ds.next());
+ }
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/core/src/test/java/org/apache/metamodel/query/SelectItemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/query/SelectItemTest.java b/core/src/test/java/org/apache/metamodel/query/SelectItemTest.java
index 7f412c0..dd46094 100644
--- a/core/src/test/java/org/apache/metamodel/query/SelectItemTest.java
+++ b/core/src/test/java/org/apache/metamodel/query/SelectItemTest.java
@@ -24,11 +24,26 @@ import org.apache.metamodel.schema.MutableColumn;
import org.apache.metamodel.schema.Schema;
import org.apache.metamodel.schema.Table;
+import static org.junit.Assert.assertNotEquals;
+
import java.util.List;
public class SelectItemTest extends MetaModelTestCase {
private Schema _schema = getExampleSchema();
+
+ public void testEqualsAndHashCodeWithScalarFunctionParameters() {
+ final SelectItem selectItem = new SelectItem(_schema.getTableByName(TABLE_PROJECT).getColumns().get(0));
+ final SelectItem item1 = selectItem.replaceFunction(FunctionType.SUBSTRING, 2, 2);
+ final SelectItem item2 = selectItem.replaceFunction(FunctionType.SUBSTRING, 2, 2);
+ final SelectItem item3 = selectItem.replaceFunction(FunctionType.SUBSTRING, 2, 3);
+
+ assertEquals(item1, item2);
+ assertEquals(item1.hashCode(), item2.hashCode());
+
+ assertNotEquals(item1, item3);
+ assertNotEquals(item1.hashCode(), item3.hashCode());
+ }
public void testSelectColumnInFromItem() throws Exception {
final Table projectTable = _schema.getTableByName(TABLE_PROJECT);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/core/src/test/java/org/apache/metamodel/query/SubstringFunctionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/query/SubstringFunctionTest.java b/core/src/test/java/org/apache/metamodel/query/SubstringFunctionTest.java
new file mode 100644
index 0000000..7566bca
--- /dev/null
+++ b/core/src/test/java/org/apache/metamodel/query/SubstringFunctionTest.java
@@ -0,0 +1,77 @@
+/**
+ * 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.DataSetHeader;
+import org.apache.metamodel.data.DefaultRow;
+import org.apache.metamodel.data.SimpleDataSetHeader;
+import org.apache.metamodel.schema.MutableColumn;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SubstringFunctionTest {
+
+ private final SubstringFunction javaStyleFunction = SubstringFunction.createJavaStyle();
+ private final SubstringFunction sqlStyleFunction = SubstringFunction.createSqlStyle();
+
+ @Test
+ public void testSubstringVanilla() {
+ Assert.assertEquals("2", runTest(javaStyleFunction, "123456", 1, 2));
+ Assert.assertEquals("1234", runTest(javaStyleFunction, "123456", 0, 4));
+ Assert.assertEquals("34", runTest(javaStyleFunction, "123456", 2, 4));
+
+ Assert.assertEquals("1", runTest(sqlStyleFunction, "123456", 1, 1));
+ Assert.assertEquals("1234", runTest(sqlStyleFunction, "123456", 1, 4));
+ Assert.assertEquals("34", runTest(sqlStyleFunction, "123456", 3, 2));
+ }
+
+ @Test
+ public void testSubstringBadOrWeirdParamValues() {
+ Assert.assertEquals("", runTest(javaStyleFunction, "123456", 0, 0));
+ Assert.assertEquals("1234", runTest(javaStyleFunction, "123456", -10, 4));
+ Assert.assertEquals("", runTest(javaStyleFunction, "123456", 4, -1));
+ }
+
+ @Test
+ public void testSubstringEndIndexTooLarge() {
+ Assert.assertEquals("123456", runTest(javaStyleFunction, "123456", 0, 200));
+ Assert.assertEquals("56", runTest(javaStyleFunction, "123456", 4, 8));
+
+ Assert.assertEquals("123456", runTest(sqlStyleFunction, "123456", 1, 200));
+ Assert.assertEquals("56", runTest(sqlStyleFunction, "123456", 5, 5));
+ }
+
+ @Test
+ public void testSubstringStartIndexTooLarge() {
+ Assert.assertEquals("", runTest(javaStyleFunction, "123456", 200, 2));
+ }
+
+ @Test
+ public void testSubstringOnlyStartIndex() {
+ Assert.assertEquals("123456", runTest(javaStyleFunction, "123456", 0));
+ Assert.assertEquals("", runTest(javaStyleFunction, "123456", 10));
+ Assert.assertEquals("3456", runTest(javaStyleFunction, "123456", 2));
+ }
+
+ private String runTest(ScalarFunction f, String str, Object... params) {
+ SelectItem selectItem = new SelectItem(new MutableColumn("column"));
+ DataSetHeader header = new SimpleDataSetHeader(new SelectItem[] { selectItem });
+ return (String) f.evaluate(new DefaultRow(header, new Object[] { str }), params, selectItem);
+ }
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/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 fa9b66d..9e16fe4 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
@@ -27,6 +27,7 @@ import org.apache.metamodel.MockDataContext;
import org.apache.metamodel.query.FilterClause;
import org.apache.metamodel.query.FilterItem;
import org.apache.metamodel.query.FromItem;
+import org.apache.metamodel.query.FunctionType;
import org.apache.metamodel.query.OperatorType;
import org.apache.metamodel.query.OrderByItem;
import org.apache.metamodel.query.OrderByItem.Direction;
@@ -83,7 +84,25 @@ public class QueryParserTest extends TestCase {
Query q = MetaModelHelper.parseQuery(dc,
"SELECT sch.tbl.baz.foo.bar, baz.helloworld, baz.hello.world FROM sch.tbl");
assertEquals(
- "SELECT MAP_VALUE('foo.bar',tbl.baz), MAP_VALUE('helloworld',tbl.baz), MAP_VALUE('hello.world',tbl.baz) FROM sch.tbl",
+ "SELECT MAP_VALUE(tbl.baz,'foo.bar'), MAP_VALUE(tbl.baz,'helloworld'), MAP_VALUE(tbl.baz,'hello.world') FROM sch.tbl",
+ q.toSql());
+ }
+
+ public void testWhereMapValueUsingDotNotation() throws Exception {
+ // set 'baz' column to a MAP column
+ MutableColumn col = (MutableColumn) dc.getColumnByQualifiedLabel("tbl.baz");
+ col.setType(ColumnType.MAP);
+
+ Query q = MetaModelHelper.parseQuery(dc,
+ "SELECT baz.lorem, baz.ipsum FROM sch.tbl WHERE baz.hello = 'world'");
+
+ final SelectItem whereSelectItem = q.getWhereClause().getItem(0).getSelectItem();
+ assertEquals(whereSelectItem.getScalarFunction(), FunctionType.MAP_VALUE);
+ assertEquals(col, whereSelectItem.getColumn());
+ assertEquals("[hello]", Arrays.toString(whereSelectItem.getFunctionParameters()));
+
+ assertEquals(
+ "SELECT MAP_VALUE(tbl.baz,'lorem'), MAP_VALUE(tbl.baz,'ipsum') FROM sch.tbl WHERE MAP_VALUE(tbl.baz,'hello') = 'world'",
q.toSql());
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/mongodb/mongo2/src/test/java/org/apache/metamodel/mongodb/mongo2/MongoDbDataContextTest.java
----------------------------------------------------------------------
diff --git a/mongodb/mongo2/src/test/java/org/apache/metamodel/mongodb/mongo2/MongoDbDataContextTest.java b/mongodb/mongo2/src/test/java/org/apache/metamodel/mongodb/mongo2/MongoDbDataContextTest.java
index 398c408..47a009c 100644
--- a/mongodb/mongo2/src/test/java/org/apache/metamodel/mongodb/mongo2/MongoDbDataContextTest.java
+++ b/mongodb/mongo2/src/test/java/org/apache/metamodel/mongodb/mongo2/MongoDbDataContextTest.java
@@ -278,7 +278,7 @@ public class MongoDbDataContextTest extends MongoDbTestCase {
// Instantiate the actual data context
final DataContext dataContext = new MongoDbDataContext(db);
- assertTrue(Arrays.asList(dataContext.getDefaultSchema().getTableNames()).contains(getCollectionName()));
+ assertTrue(dataContext.getDefaultSchema().getTableNames().contains(getCollectionName()));
Table table = dataContext.getDefaultSchema().getTableByName(getCollectionName());
assertEquals("[_id, baz, foo, id, list, name]", Arrays.toString(table.getColumnNames().toArray()));