You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2015/12/09 11:44:49 UTC
metamodel git commit: METAMODEL-195: Fixed
Repository: metamodel
Updated Branches:
refs/heads/master 67ef25f38 -> 272461944
METAMODEL-195: Fixed
Fixes #70
Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/27246194
Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/27246194
Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/27246194
Branch: refs/heads/master
Commit: 272461944aba90ea4b6fd5538dbd6c93a9c28bb7
Parents: 67ef25f
Author: Kasper Sørensen <i....@gmail.com>
Authored: Wed Dec 9 11:44:41 2015 +0100
Committer: Kasper Sørensen <i....@gmail.com>
Committed: Wed Dec 9 11:44:41 2015 +0100
----------------------------------------------------------------------
CHANGES.md | 1 +
.../cassandra/CassandraDataContextTest.java | 3 +-
.../metamodel/data/ScalarFunctionRow.java | 2 +-
.../metamodel/query/AbstractQueryClause.java | 5 +
.../org/apache/metamodel/query/FromClause.java | 8 +-
.../apache/metamodel/query/FunctionType.java | 1 +
.../metamodel/query/FunctionTypeFactory.java | 2 +
.../metamodel/query/MapValueFunction.java | 57 ++++++++
.../java/org/apache/metamodel/query/Query.java | 24 +++-
.../org/apache/metamodel/query/QueryClause.java | 2 +
.../apache/metamodel/query/ScalarFunction.java | 4 +-
.../org/apache/metamodel/query/SelectItem.java | 137 ++++++++++++++++---
.../metamodel/query/ToBooleanFunction.java | 2 +-
.../apache/metamodel/query/ToDateFunction.java | 2 +-
.../metamodel/query/ToNumberFunction.java | 2 +-
.../metamodel/query/ToStringFunction.java | 2 +-
.../builder/FunctionSelectBuilderImpl.java | 56 ++++----
.../query/builder/GroupedQueryBuilderImpl.java | 8 +-
.../query/builder/SatisfiedFromBuilder.java | 10 +-
.../builder/SatisfiedFromBuilderCallback.java | 46 +++++--
.../query/parser/SelectItemParser.java | 38 +++--
.../QueryPostprocessDataContextTest.java | 2 +-
.../ConvertedDataSetInterceptorTest.java | 111 +++++++--------
.../metamodel/query/MapValueFunctionTest.java | 57 ++++++++
.../metamodel/query/parser/QueryParserTest.java | 42 ++++--
.../ElasticSearchDataContextTest.java | 14 +-
.../rest/JestElasticSearchDataContextTest.java | 3 +-
.../org/apache/metamodel/jdbc/DerbyTest.java | 2 +-
.../metamodel/json/JsonDataContextTest.java | 44 +++++-
29 files changed, 520 insertions(+), 167 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index d21df5a..2baa60f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,6 +5,7 @@
* [METAMODEL-207] - Ensured the serializability of the SingleLineCsvRow class.
* [METAMODEL-211] - Fixed a bug related to lookup by primary key (_id) on MongoDB.
* [METAMODEL-216] - Added new aggregate functions: FIRST, LAST and RANDOM.
+ * [METAMODEL-195] - Added new function MAP_VALUE which allows extracting a nested value from within a key/value map field.
* [METAMODEL-15] - Query parser support for table names with space. Delimitters can be double quote or square brackets.
* [METAMODEL-215] - Improved the capability of NumberComparator to support Integer, Long, Double, BigInteger and other built-in Number classes.
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java
----------------------------------------------------------------------
diff --git a/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java b/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java
index a52d171..4921639 100644
--- a/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java
+++ b/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java
@@ -33,6 +33,7 @@ import org.apache.metamodel.data.DataSet;
import org.apache.metamodel.data.DataSetTableModel;
import org.apache.metamodel.data.FilteredDataSet;
import org.apache.metamodel.query.Query;
+import org.apache.metamodel.query.parser.QueryParserException;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.schema.Table;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
@@ -178,7 +179,7 @@ public class CassandraDataContextTest {
boolean thrown = false;
try {
dc.query().from(testTableName).select("nonExistingField").execute();
- } catch (IllegalArgumentException IAex) {
+ } catch (QueryParserException ex) {
thrown = true;
}
assertTrue(thrown);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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
index 7ba8c7c..1fe86cb 100644
--- a/core/src/main/java/org/apache/metamodel/data/ScalarFunctionRow.java
+++ b/core/src/main/java/org/apache/metamodel/data/ScalarFunctionRow.java
@@ -50,7 +50,7 @@ final class ScalarFunctionRow extends AbstractRow {
}
final SelectItem selectItem = scalarFunctionSelectItems.get(index);
final SelectItem selectItemWithoutFunction = selectItem.replaceFunction(null);
- return selectItem.getScalarFunction().evaluate(_row, selectItemWithoutFunction);
+ return selectItem.getScalarFunction().evaluate(_row, selectItem.getFunctionParameters(), selectItemWithoutFunction);
}
@Override
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/core/src/main/java/org/apache/metamodel/query/AbstractQueryClause.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/AbstractQueryClause.java b/core/src/main/java/org/apache/metamodel/query/AbstractQueryClause.java
index 13d8ec5..572d69f 100644
--- a/core/src/main/java/org/apache/metamodel/query/AbstractQueryClause.java
+++ b/core/src/main/java/org/apache/metamodel/query/AbstractQueryClause.java
@@ -95,6 +95,11 @@ public abstract class AbstractQueryClause<E extends QueryItem> extends BaseObjec
public int getItemCount() {
return _items.size();
}
+
+ @Override
+ public int indexOf(E item) {
+ return _items.indexOf(item);
+ }
@Override
public boolean isEmpty() {
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/core/src/main/java/org/apache/metamodel/query/FromClause.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FromClause.java b/core/src/main/java/org/apache/metamodel/query/FromClause.java
index 808886d..e86b89d 100644
--- a/core/src/main/java/org/apache/metamodel/query/FromClause.java
+++ b/core/src/main/java/org/apache/metamodel/query/FromClause.java
@@ -75,7 +75,13 @@ public class FromClause extends AbstractQueryClause<FromItem> {
return null;
}
- private FromItem getItemByReference(FromItem item, String reference) {
+ private FromItem getItemByReference(final FromItem item, final String reference) {
+ if (reference.equals(item.toStringNoAlias(false))) {
+ return item;
+ }
+ if (reference.equals(item.toStringNoAlias(true))) {
+ return item;
+ }
final String alias = item.getAlias();
if (reference.equals(alias)) {
return item;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 bb4a11b..8d06dcc 100644
--- a/core/src/main/java/org/apache/metamodel/query/FunctionType.java
+++ b/core/src/main/java/org/apache/metamodel/query/FunctionType.java
@@ -39,6 +39,7 @@ public interface FunctionType {
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 MAP_VALUE = new MapValueFunction();
public ColumnType getExpectedColumnType(ColumnType type);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 be98f07..95e25ce 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,8 @@ public class FunctionTypeFactory {
case "TO_DATE":
case "DATE":
return FunctionType.TO_DATE;
+ case "MAP_VALUE":
+ return FunctionType.MAP_VALUE;
default:
return null;
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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
new file mode 100644
index 0000000..5cb743a
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java
@@ -0,0 +1,57 @@
+/**
+ * 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.Map;
+
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.schema.ColumnType;
+import org.apache.metamodel.util.CollectionUtils;
+
+/**
+ * Represents a function that retrieves a value from within a column of type
+ * {@link ColumnType#MAP} or similar.
+ */
+public final class MapValueFunction extends DefaultScalarFunction {
+
+ @Override
+ public Object evaluate(Row row, Object[] parameters, SelectItem operandItem) {
+ if (parameters.length == 0) {
+ throw new IllegalArgumentException("Expecting path parameter to MAP_VALUE function");
+ }
+ Object value = row.getValue(operandItem);
+ if (value instanceof Map) {
+ Map<?, ?> map = (Map<?, ?>) value;
+ return CollectionUtils.find(map, (String) parameters[0]);
+ }
+ return null;
+ }
+
+ @Override
+ public ColumnType getExpectedColumnType(ColumnType type) {
+ // the column type cannot be inferred so null is returned
+ return null;
+ }
+
+ @Override
+ public String getFunctionName() {
+ return "MAP_VALUE";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/core/src/main/java/org/apache/metamodel/query/Query.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/Query.java b/core/src/main/java/org/apache/metamodel/query/Query.java
index 209e71f..f8194a7 100644
--- a/core/src/main/java/org/apache/metamodel/query/Query.java
+++ b/core/src/main/java/org/apache/metamodel/query/Query.java
@@ -122,13 +122,29 @@ public final class Query extends BaseObject implements Cloneable, Serializable {
* @return
*/
public Query select(String expression) {
- QueryPartParser clauseParser = new QueryPartParser(new SelectItemParser(this, true), expression, ",");
+ return select(expression, false);
+ }
+
+ /**
+ * Adds a selection to this query.
+ *
+ * @param expression
+ * a textual representation of the select item, e.g. "MAX(foo)"
+ * or just "foo", where "foo" is a column name.
+ * @param allowExpressionBasedSelectItem
+ * whether or not expression-based select items are allowed or
+ * not (see {@link SelectItem#getExpression()}.
+ * @return
+ */
+ public Query select(String expression, boolean allowExpressionBasedSelectItem) {
+ final QueryPartParser clauseParser = new QueryPartParser(new SelectItemParser(this,
+ allowExpressionBasedSelectItem), expression, ",");
clauseParser.parse();
return this;
}
private SelectItem findSelectItem(String expression, boolean allowExpressionBasedSelectItem) {
- SelectItemParser parser = new SelectItemParser(this, allowExpressionBasedSelectItem);
+ final SelectItemParser parser = new SelectItemParser(this, allowExpressionBasedSelectItem);
return parser.findSelectItem(expression);
}
@@ -579,7 +595,7 @@ public final class Query extends BaseObject implements Cloneable, Serializable {
@Override
public Query clone() {
- Query q = new Query();
+ final Query q = new Query();
q.setMaxRows(_maxRows);
q.setFirstRow(_firstRow);
q.getSelectClause().setDistinct(_selectClause.isDistinct());
@@ -587,7 +603,7 @@ public final class Query extends BaseObject implements Cloneable, Serializable {
q.from(item.clone());
}
for (SelectItem item : _selectClause.getItems()) {
- q.select(item.clone());
+ q.select(item.clone(q));
}
for (FilterItem item : _whereClause.getItems()) {
q.where(item.clone());
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/core/src/main/java/org/apache/metamodel/query/QueryClause.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/QueryClause.java b/core/src/main/java/org/apache/metamodel/query/QueryClause.java
index 33b09f3..19a9472 100644
--- a/core/src/main/java/org/apache/metamodel/query/QueryClause.java
+++ b/core/src/main/java/org/apache/metamodel/query/QueryClause.java
@@ -50,4 +50,6 @@ public interface QueryClause<E> extends Serializable {
public String toSql(boolean includeSchemaInColumnPaths);
public String toSql();
+
+ public int indexOf(E item);
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 8262c2b..dcdfd7b 100644
--- a/core/src/main/java/org/apache/metamodel/query/ScalarFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/ScalarFunction.java
@@ -33,11 +33,13 @@ public interface ScalarFunction extends FunctionType {
*
* @param row
* the row containing data
+ * @param parameters
+ * any parameters associated with the function call
* @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);
+ public Object evaluate(Row row, Object[] parameters, SelectItem operandItem);
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 cbbe919..bc872d1 100644
--- a/core/src/main/java/org/apache/metamodel/query/SelectItem.java
+++ b/core/src/main/java/org/apache/metamodel/query/SelectItem.java
@@ -20,6 +20,7 @@ package org.apache.metamodel.query;
import java.util.List;
+import org.apache.metamodel.DataContext;
import org.apache.metamodel.schema.Column;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.schema.Schema;
@@ -39,8 +40,7 @@ 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
@@ -55,6 +55,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
// immutable fields (essense)
private final Column _column;
private final FunctionType _function;
+ private final Object[] _functionParameters;
private final String _expression;
private final SelectItem _subQuerySelectItem;
private final FromItem _fromItem;
@@ -70,17 +71,19 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
* @param column
* @param fromItem
* @param function
+ * @param functionParameters
* @param expression
* @param subQuerySelectItem
* @param alias
* @param functionApproximationAllowed
*/
- private SelectItem(Column column, FromItem fromItem, FunctionType function, String expression,
- SelectItem subQuerySelectItem, String alias, boolean functionApproximationAllowed) {
+ private SelectItem(Column column, FromItem fromItem, FunctionType function, Object[] functionParameters,
+ String expression, SelectItem subQuerySelectItem, String alias, boolean functionApproximationAllowed) {
super();
_column = column;
_fromItem = fromItem;
_function = function;
+ _functionParameters = functionParameters;
_expression = expression;
_subQuerySelectItem = subQuerySelectItem;
_alias = alias;
@@ -123,6 +126,17 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
}
/**
+ * Create a SelectItem that uses a function with parameters on a column.
+ *
+ * @param function
+ * @param functionParameters
+ * @param column
+ */
+ public SelectItem(FunctionType function, Object[] functionParameters, Column column) {
+ this(function, functionParameters, column, null);
+ }
+
+ /**
* Creates a SelectItem that references a column from a particular
* {@link FromItem}, for example a.price or p.age
*
@@ -152,7 +166,24 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
* @param fromItem
*/
public SelectItem(FunctionType function, Column column, FromItem fromItem) {
- this(column, fromItem, function, null, null, null, false);
+ this(column, fromItem, function, null, null, null, null, false);
+ if (column == null) {
+ throw new IllegalArgumentException("column=null");
+ }
+ }
+
+ /**
+ * Creates a SelectItem that uses a function with parameters on a column
+ * from a particular {@link FromItem}, for example
+ * MAP_VALUE('path.to.value', doc)
+ *
+ * @param function
+ * @param functionParameters
+ * @param column
+ * @param fromItem
+ */
+ public SelectItem(FunctionType function, Object[] functionParameters, Column column, FromItem fromItem) {
+ this(column, fromItem, function, functionParameters, null, null, null, false);
if (column == null) {
throw new IllegalArgumentException("column=null");
}
@@ -178,7 +209,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
* @param alias
*/
public SelectItem(FunctionType function, String expression, String alias) {
- this(null, null, function, expression, null, alias, false);
+ this(null, null, function, null, expression, null, alias, false);
if (expression == null) {
throw new IllegalArgumentException("expression=null");
}
@@ -192,7 +223,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
* the FromItem that holds the sub-query
*/
public SelectItem(SelectItem subQuerySelectItem, FromItem subQueryFromItem) {
- this(null, subQueryFromItem, null, null, subQuerySelectItem, null, false);
+ this(null, subQueryFromItem, null, null, null, subQuerySelectItem, null, false);
if (subQueryFromItem.getSubQuery() == null) {
throw new IllegalArgumentException("Only sub-query based FromItems allowed.");
}
@@ -243,6 +274,15 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
}
/**
+ * Gets any parameters to the {@link #getFunction()} used.
+ *
+ * @return
+ */
+ public Object[] getFunctionParameters() {
+ return _functionParameters;
+ }
+
+ /**
* @return if this is a function based SelectItem where function calculation
* is allowed to be approximated (if the datastore type has an
* approximate calculation method). Approximated function results
@@ -285,6 +325,13 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
return null;
}
+ /**
+ * Returns an "expression" that this select item represents. Expressions are
+ * not necesarily portable across {@link DataContext} implementations, but
+ * may be useful for utilizing database-specific behaviour in certain cases.
+ *
+ * @return
+ */
public String getExpression() {
return _expression;
}
@@ -399,7 +446,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
@Override
public String toSql(boolean includeSchemaInColumnPath) {
- StringBuilder sb = toStringNoAlias(includeSchemaInColumnPath);
+ final StringBuilder sb = toStringNoAlias(includeSchemaInColumnPath);
if (_alias != null) {
sb.append(" AS ");
sb.append(_alias);
@@ -412,7 +459,7 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
}
public StringBuilder toStringNoAlias(boolean includeSchemaInColumnPath) {
- StringBuilder sb = new StringBuilder();
+ final StringBuilder sb = new StringBuilder();
if (_column != null) {
sb.append(getToStringColumnPrefix(includeSchemaInColumnPath));
sb.append(_column.getQuotedName());
@@ -427,18 +474,30 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
sb.append(_subQuerySelectItem.getSuperQueryAlias());
}
if (_function != null) {
+ final StringBuilder functionBeginning = new StringBuilder();
if (_functionApproximationAllowed) {
- sb.insert(0, FUNCTION_APPROXIMATION_PREFIX + _function.getFunctionName() + "(");
- } else {
- sb.insert(0, _function.getFunctionName() + "(");
+ functionBeginning.append(FUNCTION_APPROXIMATION_PREFIX);
}
+
+ 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(',');
+ }
+ }
+ sb.insert(0, functionBeginning.toString());
sb.append(")");
}
return sb;
}
private String getToStringColumnPrefix(boolean includeSchemaInColumnPath) {
- StringBuilder sb = new StringBuilder();
+ final StringBuilder sb = new StringBuilder();
if (_fromItem != null && _fromItem.getAlias() != null) {
sb.append(_fromItem.getAlias());
sb.append('.');
@@ -506,16 +565,50 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
identifiers.add(_column);
identifiers.add(_function);
identifiers.add(_functionApproximationAllowed);
- identifiers.add(_fromItem);
+ if (_fromItem == null && _column != null && _column.getTable() != null) {
+ // add a FromItem representing the column's table - this makes equal
+ // comparison work when the only difference is whether or not
+ // FromItem is specified
+ identifiers.add(new FromItem(_column.getTable()));
+ } else {
+ identifiers.add(_fromItem);
+ }
identifiers.add(_subQuerySelectItem);
}
@Override
protected SelectItem clone() {
+ return clone(null);
+ }
+
+ /**
+ * Creates a clone of the {@link SelectItem} for use within a cloned
+ * {@link Query}.
+ *
+ * @param clonedQuery
+ * a new {@link Query} object that represents the clone-to-be of
+ * a query. It is expected that {@link FromItem}s have already
+ * been cloned in this {@link Query}.
+ * @return
+ */
+ protected SelectItem clone(Query clonedQuery) {
final SelectItem subQuerySelectItem = (_subQuerySelectItem == null ? null : _subQuerySelectItem.clone());
- final FromItem fromItem = (_fromItem == null ? null : _fromItem.clone());
- final SelectItem s = new SelectItem(_column, fromItem, _function, _expression, subQuerySelectItem, _alias,
- _functionApproximationAllowed);
+ final FromItem fromItem;
+ if (_fromItem == null) {
+ fromItem = null;
+ } else if (clonedQuery != null && _query != null) {
+ final int indexOfFromItem = _query.getFromClause().indexOf(_fromItem);
+ if (indexOfFromItem != -1) {
+ fromItem = clonedQuery.getFromClause().getItem(indexOfFromItem);
+ } else {
+ fromItem = _fromItem.clone();
+ }
+ } else {
+ fromItem = _fromItem.clone();
+ }
+
+ final SelectItem s = new SelectItem(_column, fromItem, _function, _functionParameters, _expression,
+ subQuerySelectItem, _alias, _functionApproximationAllowed);
return s;
}
@@ -527,8 +620,8 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
* @return
*/
public SelectItem replaceFunction(FunctionType function) {
- return new SelectItem(_column, _fromItem, function, _expression, _subQuerySelectItem, _alias,
- _functionApproximationAllowed);
+ return new SelectItem(_column, _fromItem, function, _functionParameters, _expression, _subQuerySelectItem,
+ _alias, _functionApproximationAllowed);
}
/**
@@ -539,8 +632,8 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
* @return
*/
public SelectItem replaceFunctionApproximationAllowed(boolean functionApproximationAllowed) {
- return new SelectItem(_column, _fromItem, _function, _expression, _subQuerySelectItem, _alias,
- functionApproximationAllowed);
+ return new SelectItem(_column, _fromItem, _function, _functionParameters, _expression, _subQuerySelectItem,
+ _alias, functionApproximationAllowed);
}
/**
@@ -568,4 +661,4 @@ public class SelectItem extends BaseObject implements QueryItem, Cloneable {
public String toString() {
return toSql();
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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
index 829f251..c8684cf 100644
--- a/core/src/main/java/org/apache/metamodel/query/ToBooleanFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/ToBooleanFunction.java
@@ -38,7 +38,7 @@ public class ToBooleanFunction extends DefaultScalarFunction {
}
@Override
- public Object evaluate(Row row, SelectItem item) {
+ public Object evaluate(Row row, Object[] parameters, SelectItem item) {
final Object value = row.getValue(item);
if (value == null || value instanceof Boolean) {
return value;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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
index 5d66787..e6f932b 100644
--- a/core/src/main/java/org/apache/metamodel/query/ToDateFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/ToDateFunction.java
@@ -40,7 +40,7 @@ public class ToDateFunction extends DefaultScalarFunction {
}
@Override
- public Object evaluate(Row row, SelectItem item) {
+ public Object evaluate(Row row, Object[] parameters, SelectItem item) {
final Object value = row.getValue(item);
if (value == null || value instanceof Date) {
return value;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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
index 0d30e13..a97a84a 100644
--- a/core/src/main/java/org/apache/metamodel/query/ToNumberFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/ToNumberFunction.java
@@ -38,7 +38,7 @@ public class ToNumberFunction extends DefaultScalarFunction {
}
@Override
- public Object evaluate(Row row, SelectItem item) {
+ public Object evaluate(Row row, Object[] parameters, SelectItem item) {
final Object value = row.getValue(item);
if (value == null || value instanceof Number) {
return value;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 6eaec39..ce85f0e 100644
--- a/core/src/main/java/org/apache/metamodel/query/ToStringFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/ToStringFunction.java
@@ -37,7 +37,7 @@ public class ToStringFunction extends DefaultScalarFunction {
}
@Override
- public Object evaluate(Row row, SelectItem item) {
+ public Object evaluate(Row row, Object[] parameters, SelectItem item) {
final Object value = row.getValue(item);
if (value == null || value instanceof String) {
return value;
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/core/src/main/java/org/apache/metamodel/query/builder/FunctionSelectBuilderImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/builder/FunctionSelectBuilderImpl.java b/core/src/main/java/org/apache/metamodel/query/builder/FunctionSelectBuilderImpl.java
index 6e1ced1..0b55f1c 100644
--- a/core/src/main/java/org/apache/metamodel/query/builder/FunctionSelectBuilderImpl.java
+++ b/core/src/main/java/org/apache/metamodel/query/builder/FunctionSelectBuilderImpl.java
@@ -25,32 +25,32 @@ import org.apache.metamodel.query.Query;
import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.schema.Column;
-final class FunctionSelectBuilderImpl extends SatisfiedSelectBuilderImpl
- implements FunctionSelectBuilder<GroupedQueryBuilder> {
-
- private SelectItem selectItem;
-
- public FunctionSelectBuilderImpl(FunctionType functionType, Column column,
- Query query, GroupedQueryBuilder queryBuilder) {
- super(queryBuilder);
-
- this.selectItem = new SelectItem(functionType, column);
-
- query.select(selectItem);
- }
-
- @Override
- public GroupedQueryBuilder as(String alias) {
- if (alias == null) {
- throw new IllegalArgumentException("alias cannot be null");
- }
- selectItem.setAlias(alias);
- return getQueryBuilder();
- }
-
- @Override
- protected void decorateIdentity(List<Object> identifiers) {
- super.decorateIdentity(identifiers);
- identifiers.add(selectItem);
- }
+final class FunctionSelectBuilderImpl extends SatisfiedSelectBuilderImpl implements
+ FunctionSelectBuilder<GroupedQueryBuilder> {
+
+ private SelectItem selectItem;
+
+ public FunctionSelectBuilderImpl(FunctionType functionType, Column column, Object[] functionParameters,
+ Query query, GroupedQueryBuilder queryBuilder) {
+ super(queryBuilder);
+
+ this.selectItem = new SelectItem(functionType, functionParameters, column);
+
+ query.select(selectItem);
+ }
+
+ @Override
+ public GroupedQueryBuilder as(String alias) {
+ if (alias == null) {
+ throw new IllegalArgumentException("alias cannot be null");
+ }
+ selectItem.setAlias(alias);
+ return getQueryBuilder();
+ }
+
+ @Override
+ protected void decorateIdentity(List<Object> identifiers) {
+ super.decorateIdentity(identifiers);
+ identifiers.add(selectItem);
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 4c05bfe..0ea0098 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
@@ -68,7 +68,7 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
throw new IllegalArgumentException("function cannot be null");
}
final Column column = findColumn(columnName);
- return new FunctionSelectBuilderImpl(function, column, _query, this);
+ return new FunctionSelectBuilderImpl(function, column, null, _query, this);
}
@Override
@@ -79,7 +79,7 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
if (column == null) {
throw new IllegalArgumentException("column cannot be null");
}
- return new FunctionSelectBuilderImpl(function, column, _query, this);
+ return new FunctionSelectBuilderImpl(function, column, null, _query, this);
}
@Override
@@ -127,13 +127,13 @@ final class GroupedQueryBuilderImpl extends BaseObject implements GroupedQueryBu
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);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 c01bef3..f1a706c 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,13 @@ public interface SatisfiedFromBuilder {
public ColumnSelectBuilder<?> select(Column column);
- public SatisfiedQueryBuilder<?> select(FunctionType function, String columnName);
+ public FunctionSelectBuilder<?> select(FunctionType function, String columnName);
public FunctionSelectBuilder<?> select(FunctionType function, Column column);
+
+ public FunctionSelectBuilder<?> select(FunctionType function, String columnName, Object[] functionParameters);
+
+ public FunctionSelectBuilder<?> select(FunctionType function, Column column, Object[] functionParameters);
public CountSelectBuilder<?> selectCount();
@@ -46,7 +50,9 @@ public interface SatisfiedFromBuilder {
public SatisfiedSelectBuilder<?> selectAll();
- public ColumnSelectBuilder<?> select(String columnName);
+ public SatisfiedSelectBuilder<?> select(String selectExpression);
+
+ public SatisfiedSelectBuilder<?> select(String selectExpression, boolean allowExpressionBasedSelectItem);
public SatisfiedSelectBuilder<?> select(String... columnNames);
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilderCallback.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilderCallback.java b/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilderCallback.java
index e142a79..74831e9 100644
--- a/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilderCallback.java
+++ b/core/src/main/java/org/apache/metamodel/query/builder/SatisfiedFromBuilderCallback.java
@@ -71,25 +71,37 @@ abstract class SatisfiedFromBuilderCallback extends BaseObject implements Satisf
GroupedQueryBuilder queryBuilder = new GroupedQueryBuilderImpl(dataContext, query);
return new SatisfiedSelectBuilderImpl(queryBuilder);
}
-
+
+ @Override
+ public FunctionSelectBuilder<?> select(FunctionType function, String columnName) {
+ GroupedQueryBuilderImpl queryBuilder = new GroupedQueryBuilderImpl(dataContext, query);
+ Column column = queryBuilder.findColumn(columnName);
+ return select(function, column);
+ }
+
@Override
- public SatisfiedQueryBuilder<?> select(FunctionType functionType, String columnName) {
+ public FunctionSelectBuilder<?> select(FunctionType function, String columnName, Object[] functionParameters) {
GroupedQueryBuilderImpl queryBuilder = new GroupedQueryBuilderImpl(dataContext, query);
Column column = queryBuilder.findColumn(columnName);
- return select(functionType, column);
+ return select(function, column, functionParameters);
+ }
+
+ @Override
+ public FunctionSelectBuilder<?> select(FunctionType function, Column column) {
+ return select(function, column, new Object[0]);
}
@Override
- public FunctionSelectBuilder<?> select(FunctionType functionType, Column column) {
- if (functionType == null) {
+ public FunctionSelectBuilder<?> select(FunctionType function, Column column, Object[] functionParameters) {
+ if (function == null) {
throw new IllegalArgumentException("functionType cannot be null");
}
if (column == null) {
throw new IllegalArgumentException("column cannot be null");
}
- GroupedQueryBuilder queryBuilder = new GroupedQueryBuilderImpl(dataContext, query);
- return new FunctionSelectBuilderImpl(functionType, column, query, queryBuilder);
+ final GroupedQueryBuilder queryBuilder = new GroupedQueryBuilderImpl(dataContext, query);
+ return new FunctionSelectBuilderImpl(function, column, functionParameters, query, queryBuilder);
}
@Override
@@ -148,16 +160,22 @@ abstract class SatisfiedFromBuilderCallback extends BaseObject implements Satisf
GroupedQueryBuilder queryBuilder = new GroupedQueryBuilderImpl(dataContext, query);
return new SatisfiedSelectBuilderImpl(queryBuilder);
}
-
+
@Override
- public ColumnSelectBuilder<?> select(String columnName) {
- if (columnName == null) {
- throw new IllegalArgumentException("columnName cannot be null");
+ public SatisfiedSelectBuilder<?> select(String selectExpression, boolean allowExpressionBasedSelectItem) {
+ if (selectExpression == null) {
+ throw new IllegalArgumentException("selectExpression cannot be null");
}
- GroupedQueryBuilderImpl queryBuilder = new GroupedQueryBuilderImpl(dataContext, query);
- Column column = queryBuilder.findColumn(columnName);
- return select(column);
+ query.select(selectExpression, allowExpressionBasedSelectItem);
+
+ final GroupedQueryBuilder queryBuilder = new GroupedQueryBuilderImpl(dataContext, query);
+ return new SatisfiedSelectBuilderImpl(queryBuilder);
+ }
+
+ @Override
+ public SatisfiedSelectBuilder<?> select(String selectExpression) {
+ return select(selectExpression, false);
}
@Override
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 9cea18e..d358d72 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
@@ -111,7 +111,8 @@ public final class SelectItemParser implements QueryPartProcessor {
final int startParenthesis = expression.indexOf('(');
if (startParenthesis > 0 && expression.endsWith(")")) {
functionApproximation = (expression.startsWith(SelectItem.FUNCTION_APPROXIMATION_PREFIX));
- final String functionName = expression.substring((functionApproximation ? SelectItem.FUNCTION_APPROXIMATION_PREFIX.length() : 0), startParenthesis);
+ final String functionName = expression.substring(
+ (functionApproximation ? SelectItem.FUNCTION_APPROXIMATION_PREFIX.length() : 0), startParenthesis);
function = FunctionTypeFactory.get(functionName.toUpperCase());
if (function != null) {
expression = expression.substring(startParenthesis + 1, expression.length() - 1).trim();
@@ -126,15 +127,20 @@ public final class SelectItemParser implements QueryPartProcessor {
functionApproximation = false;
}
- int lastIndexOfDot = expression.lastIndexOf(".");
-
String columnName = null;
FromItem fromItem = null;
- if (lastIndexOfDot != -1) {
- String prefix = expression.substring(0, lastIndexOfDot);
- columnName = expression.substring(lastIndexOfDot + 1);
- fromItem = _query.getFromClause().getItemByReference(prefix);
+ // attempt to find from item by cutting up the string in prefix and
+ // suffix around dot.
+ {
+ int splitIndex = expression.lastIndexOf('.');
+ while (fromItem == null && splitIndex != -1) {
+ final String prefix = expression.substring(0, splitIndex);
+ columnName = expression.substring(splitIndex + 1);
+ fromItem = _query.getFromClause().getItemByReference(prefix);
+
+ splitIndex = expression.lastIndexOf('.', splitIndex - 1);
+ }
}
if (fromItem == null) {
@@ -151,7 +157,23 @@ public final class SelectItemParser implements QueryPartProcessor {
if ("*".equals(columnName)) {
throw new MultipleSelectItemsParsedException(fromItem);
} else if (fromItem.getTable() != null) {
- final Column column = fromItem.getTable().getColumnByName(columnName);
+ Column column = fromItem.getTable().getColumnByName(columnName);
+ int offset = -1;
+ while (function == null && column == null) {
+ // check for MAP_VALUE shortcut syntax
+ offset = columnName.indexOf('.', offset + 1);
+ if (offset == -1) {
+ break;
+ }
+
+ final String part1 = columnName.substring(0, offset);
+ 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);
+ }
+ }
+
if (column != null) {
final SelectItem selectItem = new SelectItem(function, column, fromItem);
selectItem.setFunctionApproximationAllowed(functionApproximation);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 7dcaa12..6a8438a 100644
--- a/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java
+++ b/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java
@@ -1107,7 +1107,7 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase {
DataSet set = getDataContext().executeQuery(query1);
assertEquals(true, set.next());
assertEquals("Row[values=[1, kasper]]", set.getRow().toString());
- Query query2 = new Query().from(table1).select("Greatest(1,2,3),max(contributer_id)");
+ Query query2 = new Query().from(table1).select("Greatest(1,2,3),max(contributer_id)", true);
assertEquals("SELECT Greatest(1,2,3), MAX(contributer_id) FROM MetaModelSchema.contributor", query2.toString());
Query query3 = new Query().from(table1).select("*,count(*)");
assertEquals("SELECT contributor.contributor_id, contributor.name, contributor.country, COUNT(*)"
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/core/src/test/java/org/apache/metamodel/convert/ConvertedDataSetInterceptorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/convert/ConvertedDataSetInterceptorTest.java b/core/src/test/java/org/apache/metamodel/convert/ConvertedDataSetInterceptorTest.java
index 04a5e45..15fbb47 100644
--- a/core/src/test/java/org/apache/metamodel/convert/ConvertedDataSetInterceptorTest.java
+++ b/core/src/test/java/org/apache/metamodel/convert/ConvertedDataSetInterceptorTest.java
@@ -28,65 +28,66 @@ import org.apache.metamodel.MockUpdateableDataContext;
import org.apache.metamodel.UpdateableDataContext;
import org.apache.metamodel.data.DataSet;
import org.apache.metamodel.data.InMemoryDataSet;
+import org.apache.metamodel.data.WrappingDataSet;
import org.apache.metamodel.query.Query;
import org.apache.metamodel.schema.Column;
import org.apache.metamodel.schema.Table;
public class ConvertedDataSetInterceptorTest extends TestCase {
- public void testConvertedQuery() throws Exception {
- UpdateableDataContext dc = new MockUpdateableDataContext();
- Column fooColumn = dc.getColumnByQualifiedLabel("schema.table.foo");
- assertNotNull(fooColumn);
-
- dc = Converters.addTypeConverter(dc, fooColumn,
- new StringToIntegerConverter());
-
- Table table = dc.getDefaultSchema().getTableByName("table");
- Query query = dc.query().from(table).select(table.getColumns())
- .toQuery();
- assertEquals("SELECT table.foo, table.bar FROM schema.table",
- query.toSql());
-
- DataSet ds = dc.executeQuery(query);
- assertEquals(ConvertedDataSet.class, ds.getClass());
-
- assertTrue(ds.next());
- assertEquals("Row[values=[1, hello]]", ds.getRow().toString());
- assertEquals(Integer.class, ds.getRow().getValue(0).getClass());
- assertEquals(String.class, ds.getRow().getValue(1).getClass());
-
- assertTrue(ds.next());
- assertEquals("Row[values=[2, there]]", ds.getRow().toString());
- assertEquals(Integer.class, ds.getRow().getValue(0).getClass());
- assertEquals(String.class, ds.getRow().getValue(1).getClass());
-
- assertTrue(ds.next());
- assertEquals("Row[values=[3, world]]", ds.getRow().toString());
- assertEquals(Integer.class, ds.getRow().getValue(0).getClass());
- assertEquals(String.class, ds.getRow().getValue(1).getClass());
-
- assertFalse(ds.next());
- ds.close();
- }
-
- public void testNonConvertedQuery() throws Exception {
- MockUpdateableDataContext source = new MockUpdateableDataContext();
- Column fooColumn = source.getColumnByQualifiedLabel("schema.table.foo");
- assertNotNull(fooColumn);
-
- Map<Column, TypeConverter<?, ?>> converters = new HashMap<Column, TypeConverter<?, ?>>();
- converters.put(fooColumn, new StringToIntegerConverter());
- DataContext converted = Converters.addTypeConverter(source, fooColumn,
- new StringToIntegerConverter());
-
- // only select "bar" which is not converted
- Table table = converted.getDefaultSchema().getTableByName("table");
- Query query = converted.query().from(table).select("bar").toQuery();
- assertEquals("SELECT table.bar FROM schema.table", query.toSql());
-
- DataSet ds = converted.executeQuery(query);
- assertEquals(InMemoryDataSet.class, ds.getClass());
-
- }
+ public void testConvertedQuery() throws Exception {
+ UpdateableDataContext dc = new MockUpdateableDataContext();
+ Column fooColumn = dc.getColumnByQualifiedLabel("schema.table.foo");
+ assertNotNull(fooColumn);
+
+ dc = Converters.addTypeConverter(dc, fooColumn, new StringToIntegerConverter());
+
+ Table table = dc.getDefaultSchema().getTableByName("table");
+ Query query = dc.query().from(table).select(table.getColumns()).toQuery();
+ assertEquals("SELECT table.foo, table.bar FROM schema.table", query.toSql());
+
+ DataSet ds = dc.executeQuery(query);
+ assertEquals(ConvertedDataSet.class, ds.getClass());
+
+ assertTrue(ds.next());
+ assertEquals("Row[values=[1, hello]]", ds.getRow().toString());
+ assertEquals(Integer.class, ds.getRow().getValue(0).getClass());
+ assertEquals(String.class, ds.getRow().getValue(1).getClass());
+
+ assertTrue(ds.next());
+ assertEquals("Row[values=[2, there]]", ds.getRow().toString());
+ assertEquals(Integer.class, ds.getRow().getValue(0).getClass());
+ assertEquals(String.class, ds.getRow().getValue(1).getClass());
+
+ assertTrue(ds.next());
+ assertEquals("Row[values=[3, world]]", ds.getRow().toString());
+ assertEquals(Integer.class, ds.getRow().getValue(0).getClass());
+ assertEquals(String.class, ds.getRow().getValue(1).getClass());
+
+ assertFalse(ds.next());
+ ds.close();
+ }
+
+ @SuppressWarnings("resource")
+ public void testNonConvertedQuery() throws Exception {
+ MockUpdateableDataContext source = new MockUpdateableDataContext();
+ Column fooColumn = source.getColumnByQualifiedLabel("schema.table.foo");
+ assertNotNull(fooColumn);
+
+ Map<Column, TypeConverter<?, ?>> converters = new HashMap<Column, TypeConverter<?, ?>>();
+ converters.put(fooColumn, new StringToIntegerConverter());
+ DataContext converted = Converters.addTypeConverter(source, fooColumn, new StringToIntegerConverter());
+
+ // only select "bar" which is not converted
+ Table table = converted.getDefaultSchema().getTableByName("table");
+ Query query = converted.query().from(table).select("bar").toQuery();
+ assertEquals("SELECT table.bar FROM schema.table", query.toSql());
+
+ DataSet ds = converted.executeQuery(query);
+ while (ds instanceof WrappingDataSet) {
+ ds = ((WrappingDataSet) ds).getWrappedDataSet();
+ }
+ ds.close();
+ assertEquals(InMemoryDataSet.class, ds.getClass());
+ }
}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/core/src/test/java/org/apache/metamodel/query/MapValueFunctionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/query/MapValueFunctionTest.java b/core/src/test/java/org/apache/metamodel/query/MapValueFunctionTest.java
new file mode 100644
index 0000000..0d87c0c
--- /dev/null
+++ b/core/src/test/java/org/apache/metamodel/query/MapValueFunctionTest.java
@@ -0,0 +1,57 @@
+/**
+ * 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 static org.junit.Assert.assertEquals;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.metamodel.data.DefaultRow;
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.data.SimpleDataSetHeader;
+import org.junit.Test;
+
+public class MapValueFunctionTest {
+
+ private final ScalarFunction function = FunctionType.MAP_VALUE;
+
+ @Test
+ public void testGetValueFromMap() throws Exception {
+ final Map<String, Map<String, Object>> value = new HashMap<>();
+ final Map<String, Object> innerMap = new HashMap<>();
+ innerMap.put("bar", "baz");
+ value.put("foo", innerMap);
+ final SelectItem operandItem = new SelectItem("foo", "f");
+ final Row row = new DefaultRow(new SimpleDataSetHeader(new SelectItem[] { operandItem }),
+ new Object[] { value });
+ final Object v1 = function.evaluate(row, new Object[] { "foo.bar" }, operandItem);
+ assertEquals("baz", v1.toString());
+ }
+
+ @Test
+ public void testNotAMap() throws Exception {
+ final SelectItem operandItem = new SelectItem("foo", "f");
+ final Row row = new DefaultRow(new SimpleDataSetHeader(new SelectItem[] { operandItem }),
+ new Object[] { "not a map" });
+ final Object v1 = function.evaluate(row, new Object[] { "foo.bar" }, operandItem);
+ assertEquals(null, v1);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/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 1059f9a..d081c39 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
@@ -68,6 +68,18 @@ public class QueryParserTest extends TestCase {
assertEquals("SELECT TO_NUMBER(a.foo) FROM sch.tbl a WHERE TO_BOOLEAN(a.bar) = FALSE", q.toSql());
}
+ public void testSelectMapValueUsingDotNotation() 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 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",
+ q.toSql());
+ }
+
public void testSelectEverythingFromTable() throws Exception {
Query q = MetaModelHelper.parseQuery(dc, "SELECT * FROM sch.tbl");
assertEquals("SELECT tbl.foo, tbl.bar, tbl.baz FROM sch.tbl", q.toSql());
@@ -221,7 +233,8 @@ public class QueryParserTest extends TestCase {
q = MetaModelHelper.parseQuery(dc,
"SELECT COUNT(*) FROM sch.tbl a LEFT JOIN sch.tbl b ON a.foo = b.foo AND a.bar = b.baz");
- assertEquals("SELECT COUNT(*) FROM sch.tbl a LEFT JOIN sch.tbl b ON a.foo = b.foo AND a.bar = b.baz", q.toSql());
+ assertEquals("SELECT COUNT(*) FROM sch.tbl a LEFT JOIN sch.tbl b ON a.foo = b.foo AND a.bar = b.baz",
+ q.toSql());
}
public void testSimpleSelectFromWhere() throws Exception {
@@ -230,11 +243,11 @@ public class QueryParserTest extends TestCase {
FilterClause whereClause = q.getWhereClause();
assertEquals(2, whereClause.getItemCount());
- assertNull("WHERE item was an expression based item, which indicates it was not parsed", whereClause.getItem(0)
- .getExpression());
+ assertNull("WHERE item was an expression based item, which indicates it was not parsed",
+ whereClause.getItem(0).getExpression());
assertEquals(2, whereClause.getItemCount());
- assertNull("WHERE item was an expression based item, which indicates it was not parsed", whereClause.getItem(1)
- .getExpression());
+ assertNull("WHERE item was an expression based item, which indicates it was not parsed",
+ whereClause.getItem(1).getExpression());
assertEquals("baz", whereClause.getItem(0).getOperand());
assertEquals(Integer.class, whereClause.getItem(1).getOperand().getClass());
@@ -264,8 +277,8 @@ public class QueryParserTest extends TestCase {
}
public void testCoumpoundWhereClause() throws Exception {
- Query q = MetaModelHelper
- .parseQuery(dc, "SELECT foo FROM sch.tbl WHERE (bar = 'baz' OR (baz > 5 AND baz < 7))");
+ Query q = MetaModelHelper.parseQuery(dc,
+ "SELECT foo FROM sch.tbl WHERE (bar = 'baz' OR (baz > 5 AND baz < 7))");
assertEquals("SELECT tbl.foo FROM sch.tbl WHERE (tbl.bar = 'baz' OR (tbl.baz > 5 AND tbl.baz < 7))", q.toSql());
FilterClause wc = q.getWhereClause();
@@ -281,8 +294,8 @@ public class QueryParserTest extends TestCase {
}
public void testCoumpoundWhereClauseDelimInLoweCase() throws Exception {
- Query q = MetaModelHelper
- .parseQuery(dc, "SELECT foo FROM sch.tbl WHERE (bar = 'baz' OR (baz > 5 and baz < 7))");
+ Query q = MetaModelHelper.parseQuery(dc,
+ "SELECT foo FROM sch.tbl WHERE (bar = 'baz' OR (baz > 5 and baz < 7))");
assertEquals("SELECT tbl.foo FROM sch.tbl WHERE (tbl.bar = 'baz' OR (tbl.baz > 5 AND tbl.baz < 7))", q.toSql());
FilterClause wc = q.getWhereClause();
@@ -302,8 +315,8 @@ public class QueryParserTest extends TestCase {
assertEquals("SELECT tbl.foo FROM sch.tbl WHERE tbl.bar IS NULL", q.toSql());
assertEquals(1, q.getWhereClause().getItemCount());
- assertNull("WHERE item was an expression based item, which indicates it was not parsed", q.getWhereClause()
- .getItem(0).getExpression());
+ assertNull("WHERE item was an expression based item, which indicates it was not parsed",
+ q.getWhereClause().getItem(0).getExpression());
assertNull(q.getWhereClause().getItem(0).getOperand());
assertEquals(OperatorType.EQUALS_TO, q.getWhereClause().getItem(0).getOperator());
}
@@ -313,8 +326,8 @@ public class QueryParserTest extends TestCase {
assertEquals("SELECT tbl.foo FROM sch.tbl WHERE tbl.bar IS NOT NULL", q.toSql());
assertEquals(1, q.getWhereClause().getItemCount());
- assertNull("WHERE item was an expression based item, which indicates it was not parsed", q.getWhereClause()
- .getItem(0).getExpression());
+ assertNull("WHERE item was an expression based item, which indicates it was not parsed",
+ q.getWhereClause().getItem(0).getExpression());
assertNull(q.getWhereClause().getItem(0).getOperand());
assertEquals(OperatorType.DIFFERENT_FROM, q.getWhereClause().getItem(0).getOperator());
}
@@ -407,7 +420,8 @@ public class QueryParserTest extends TestCase {
+ "GROUP BY foo HAVING COUNT(*) > 2 ORDER BY foo LIMIT 20 OFFSET 10");
assertEquals(
"SELECT tbl.foo, COUNT(*), MAX(tbl.baz) FROM sch.tbl WHERE tbl.bar = 'baz' AND tbl.foo = tbl.bar AND tbl.baz > 5 "
- + "GROUP BY tbl.foo HAVING COUNT(*) > 2 ORDER BY tbl.foo ASC", q.toSql());
+ + "GROUP BY tbl.foo HAVING COUNT(*) > 2 ORDER BY tbl.foo ASC",
+ q.toSql());
assertEquals(20, q.getMaxRows().intValue());
assertEquals(11, q.getFirstRow().intValue());
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/elasticsearch/native/src/test/java/org/apache/metamodel/elasticsearch/nativeclient/ElasticSearchDataContextTest.java
----------------------------------------------------------------------
diff --git a/elasticsearch/native/src/test/java/org/apache/metamodel/elasticsearch/nativeclient/ElasticSearchDataContextTest.java b/elasticsearch/native/src/test/java/org/apache/metamodel/elasticsearch/nativeclient/ElasticSearchDataContextTest.java
index 0ae170c..8a83263 100644
--- a/elasticsearch/native/src/test/java/org/apache/metamodel/elasticsearch/nativeclient/ElasticSearchDataContextTest.java
+++ b/elasticsearch/native/src/test/java/org/apache/metamodel/elasticsearch/nativeclient/ElasticSearchDataContextTest.java
@@ -18,6 +18,14 @@
*/
package org.apache.metamodel.elasticsearch.nativeclient;
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
@@ -43,6 +51,7 @@ import org.apache.metamodel.elasticsearch.nativeclient.utils.EmbeddedElasticsear
import org.apache.metamodel.query.FunctionType;
import org.apache.metamodel.query.Query;
import org.apache.metamodel.query.SelectItem;
+import org.apache.metamodel.query.parser.QueryParserException;
import org.apache.metamodel.schema.Column;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.schema.Schema;
@@ -62,9 +71,6 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
-import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
-import static org.junit.Assert.*;
-
public class ElasticSearchDataContextTest {
private static final String indexName = "twitter";
@@ -535,7 +541,7 @@ public class ElasticSearchDataContextTest {
dataContext.query().from("nonExistingTable").select("user").and("message").execute();
}
- @Test(expected = IllegalArgumentException.class)
+ @Test(expected = QueryParserException.class)
public void testQueryForAnExistingTableAndNonExistingField() throws Exception {
indexTweeterDocument(indexType1, 1);
dataContext.query().from(indexType1).select("nonExistingField").execute();
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/JestElasticSearchDataContextTest.java
----------------------------------------------------------------------
diff --git a/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/JestElasticSearchDataContextTest.java b/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/JestElasticSearchDataContextTest.java
index aac963a..9d7d2a8 100644
--- a/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/JestElasticSearchDataContextTest.java
+++ b/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/JestElasticSearchDataContextTest.java
@@ -36,6 +36,7 @@ import org.apache.metamodel.elasticsearch.rest.utils.EmbeddedElasticsearchServer
import org.apache.metamodel.query.FunctionType;
import org.apache.metamodel.query.Query;
import org.apache.metamodel.query.SelectItem;
+import org.apache.metamodel.query.parser.QueryParserException;
import org.apache.metamodel.schema.Column;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.schema.Schema;
@@ -539,7 +540,7 @@ public class JestElasticSearchDataContextTest {
dataContext.query().from("nonExistingTable").select("user").and("message").execute();
}
- @Test(expected = IllegalArgumentException.class)
+ @Test(expected = QueryParserException.class)
public void testQueryForAnExistingTableAndNonExistingField() throws Exception {
indexTweeterDocument(indexType1, 1);
dataContext.query().from(indexType1).select("nonExistingField").execute();
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/jdbc/src/test/java/org/apache/metamodel/jdbc/DerbyTest.java
----------------------------------------------------------------------
diff --git a/jdbc/src/test/java/org/apache/metamodel/jdbc/DerbyTest.java b/jdbc/src/test/java/org/apache/metamodel/jdbc/DerbyTest.java
index 2d0de42..ff6ef22 100644
--- a/jdbc/src/test/java/org/apache/metamodel/jdbc/DerbyTest.java
+++ b/jdbc/src/test/java/org/apache/metamodel/jdbc/DerbyTest.java
@@ -253,7 +253,7 @@ public class DerbyTest extends TestCase {
Schema schema = dc.getSchemaByName("APP");
Table customersTable = schema.getTableByName("CUSTOMERS");
- Query q = dc.query().from(customersTable).as("cus-tomers").select("CUSTOMERNAME").as("c|o|d|e").toQuery();
+ Query q = dc.query().from(customersTable).as("cus-tomers").select("CUSTOMERNAME AS c|o|d|e").toQuery();
assertEquals(25000, dc.getFetchSizeCalculator().getFetchSize(q));
q.setMaxRows(5);
http://git-wip-us.apache.org/repos/asf/metamodel/blob/27246194/json/src/test/java/org/apache/metamodel/json/JsonDataContextTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/org/apache/metamodel/json/JsonDataContextTest.java b/json/src/test/java/org/apache/metamodel/json/JsonDataContextTest.java
index 79d0e22..0dba661 100644
--- a/json/src/test/java/org/apache/metamodel/json/JsonDataContextTest.java
+++ b/json/src/test/java/org/apache/metamodel/json/JsonDataContextTest.java
@@ -24,7 +24,9 @@ import java.util.Arrays;
import junit.framework.TestCase;
import org.apache.metamodel.data.DataSet;
+import org.apache.metamodel.query.FunctionType;
import org.apache.metamodel.schema.Column;
+import org.apache.metamodel.schema.Schema;
import org.apache.metamodel.schema.Table;
import org.apache.metamodel.schema.builder.SchemaBuilder;
import org.apache.metamodel.schema.builder.SimpleTableDefSchemaBuilder;
@@ -90,7 +92,47 @@ public class JsonDataContextTest extends TestCase {
assertTrue(ds.next());
assertEquals("Row[values=[John, Doe, MALE, football, null, null]]", ds.getRow().toString());
assertTrue(ds.next());
- assertEquals("Row[values=[John, Doe, MALE, {type=sport, name=soccer}, sport, soccer]]", ds.getRow().toString());
+ assertEquals("Row[values=[John, Doe, MALE, {type=sport, name=soccer}, sport, soccer]]", ds.getRow()
+ .toString());
+ assertFalse(ds.next());
+ } finally {
+ ds.close();
+ }
+ }
+
+ public void testUseMapValueFunctionToGetFromNestedMap() throws Exception {
+ final Resource resource = new FileResource("src/test/resources/nested_fields.json");
+ final JsonDataContext dataContext = new JsonDataContext(resource);
+
+ final Schema schema = dataContext.getDefaultSchema();
+ assertEquals("[nested_fields.json]", Arrays.toString(schema.getTableNames()));
+
+ final DataSet ds = dataContext.query().from(schema.getTable(0))
+ .select(FunctionType.MAP_VALUE, "name", new Object[] { "first" }).execute();
+ try {
+ assertTrue(ds.next());
+ assertEquals("Row[values=[John]]", ds.getRow().toString());
+ assertTrue(ds.next());
+ assertEquals("Row[values=[John]]", ds.getRow().toString());
+ assertFalse(ds.next());
+ } finally {
+ ds.close();
+ }
+ }
+
+ public void testUseDotNotationToGetFromNestedMap() throws Exception {
+ final Resource resource = new FileResource("src/test/resources/nested_fields.json");
+ final JsonDataContext dataContext = new JsonDataContext(resource);
+
+ final Schema schema = dataContext.getDefaultSchema();
+ assertEquals("[nested_fields.json]", Arrays.toString(schema.getTableNames()));
+
+ final DataSet ds = dataContext.query().from(schema.getTable(0)).select("name.first").execute();
+ try {
+ assertTrue(ds.next());
+ assertEquals("Row[values=[John]]", ds.getRow().toString());
+ assertTrue(ds.next());
+ assertEquals("Row[values=[John]]", ds.getRow().toString());
assertFalse(ds.next());
} finally {
ds.close();