Repository: metamodel Updated Branches: refs/heads/master a8ab3c5a8 -> 300e6b67c
METAMODEL-216: Fixed Fixes #74 Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/300e6b67 Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/300e6b67 Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/300e6b67 Branch: refs/heads/master Commit: 300e6b67c33cb04feecef7dbf99beb3bc19ffc45 Parents: a8ab3c5 Author: Kasper Sørensen <i.am.kasper.soren...@gmail.com> Authored: Mon Dec 7 14:41:26 2015 +0100 Committer: Kasper Sørensen <i.am.kasper.soren...@gmail.com> Committed: Mon Dec 7 14:41:26 2015 +0100 ---------------------------------------------------------------------- CHANGES.md | 1 + .../metamodel/query/FirstAggregateBuilder.java | 43 ++++++++++++ .../metamodel/query/FirstAggregateFunction.java | 35 ++++++++++ .../apache/metamodel/query/FunctionType.java | 3 + .../metamodel/query/FunctionTypeFactory.java | 7 ++ .../metamodel/query/LastAggregateBuilder.java | 43 ++++++++++++ .../metamodel/query/LastAggregateFunction.java | 35 ++++++++++ .../metamodel/query/RandomAggregateBuilder.java | 55 +++++++++++++++ .../query/RandomAggregateFunction.java | 35 ++++++++++ .../QueryPostprocessDataContextTest.java | 18 +++++ .../query/FirstAggregateFunctionTest.java | 51 ++++++++++++++ .../query/LastAggregateFunctionTest.java | 51 ++++++++++++++ .../query/RandomAggregateBuilderTest.java | 71 ++++++++++++++++++++ .../query/RandomAggregateFunctionTest.java | 55 +++++++++++++++ .../apache/metamodel/jdbc/JdbcDataContext.java | 9 +++ .../jdbc/dialects/DefaultQueryRewriter.java | 20 +++++- .../metamodel/jdbc/dialects/IQueryRewriter.java | 13 ++++ .../metamodel/jdbc/JdbcDataContextTest.java | 12 ++++ 18 files changed, 556 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/CHANGES.md ---------------------------------------------------------------------- diff --git a/CHANGES.md b/CHANGES.md index 6df60a6..e739b5f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ * [METAMODEL-212] - New module for ElasticSearch via REST client. * [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-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/300e6b67/core/src/main/java/org/apache/metamodel/query/FirstAggregateBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/query/FirstAggregateBuilder.java b/core/src/main/java/org/apache/metamodel/query/FirstAggregateBuilder.java new file mode 100644 index 0000000..2002076 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/query/FirstAggregateBuilder.java @@ -0,0 +1,43 @@ +/** + * 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.util.AggregateBuilder; + +public class FirstAggregateBuilder implements AggregateBuilder<Object> { + + private Object _result; + + public FirstAggregateBuilder() { + _result = null; + } + + @Override + public void add(Object o) { + if (_result == null) { + _result = o; + } + } + + @Override + public Object getAggregate() { + return _result; + } + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/main/java/org/apache/metamodel/query/FirstAggregateFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/query/FirstAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/FirstAggregateFunction.java new file mode 100644 index 0000000..b83f2e1 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/query/FirstAggregateFunction.java @@ -0,0 +1,35 @@ +/** + * 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.util.AggregateBuilder; + +public class FirstAggregateFunction extends DefaultAggregateFunction<Object> { + + @Override + public AggregateBuilder<?> createAggregateBuilder() { + return new FirstAggregateBuilder(); + } + + @Override + public String getFunctionName() { + return "FIRST"; + } + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/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 0ef259a..bb4a11b 100644 --- a/core/src/main/java/org/apache/metamodel/query/FunctionType.java +++ b/core/src/main/java/org/apache/metamodel/query/FunctionType.java @@ -32,6 +32,9 @@ public interface FunctionType { public static final AggregateFunction SUM = new SumAggregateFunction(); public static final AggregateFunction MAX = new MaxAggregateFunction(); public static final AggregateFunction MIN = new MinAggregateFunction(); + public static final AggregateFunction RANDOM = new RandomAggregateFunction(); + public static final AggregateFunction FIRST = new FirstAggregateFunction(); + public static final AggregateFunction LAST = new LastAggregateFunction(); public static final ScalarFunction TO_STRING = new ToStringFunction(); public static final ScalarFunction TO_NUMBER = new ToNumberFunction(); public static final ScalarFunction TO_DATE = new ToDateFunction(); http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/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 c021ae3..be98f07 100644 --- a/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java +++ b/core/src/main/java/org/apache/metamodel/query/FunctionTypeFactory.java @@ -42,6 +42,13 @@ public class FunctionTypeFactory { return FunctionType.MAX; case "MIN": return FunctionType.MIN; + case "RANDOM": + case "RAND": + return FunctionType.RANDOM; + case "FIRST": + return FunctionType.FIRST; + case "LAST": + return FunctionType.LAST; case "TO_NUMBER": case "NUMBER": case "TO_NUM": http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/main/java/org/apache/metamodel/query/LastAggregateBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/query/LastAggregateBuilder.java b/core/src/main/java/org/apache/metamodel/query/LastAggregateBuilder.java new file mode 100644 index 0000000..beac269 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/query/LastAggregateBuilder.java @@ -0,0 +1,43 @@ +/** + * 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.util.AggregateBuilder; + +public class LastAggregateBuilder implements AggregateBuilder<Object> { + + private Object _result; + + public LastAggregateBuilder() { + _result = null; + } + + @Override + public void add(Object o) { + if (o != null) { + _result = o; + } + } + + @Override + public Object getAggregate() { + return _result; + } + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/main/java/org/apache/metamodel/query/LastAggregateFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/query/LastAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/LastAggregateFunction.java new file mode 100644 index 0000000..4fa532e --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/query/LastAggregateFunction.java @@ -0,0 +1,35 @@ +/** + * 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.util.AggregateBuilder; + +public class LastAggregateFunction extends DefaultAggregateFunction<Object> { + + @Override + public AggregateBuilder<?> createAggregateBuilder() { + return new LastAggregateBuilder(); + } + + @Override + public String getFunctionName() { + return "LAST"; + } + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/main/java/org/apache/metamodel/query/RandomAggregateBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/query/RandomAggregateBuilder.java b/core/src/main/java/org/apache/metamodel/query/RandomAggregateBuilder.java new file mode 100644 index 0000000..3fe9427 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/query/RandomAggregateBuilder.java @@ -0,0 +1,55 @@ +/** + * 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.Random; + +import org.apache.metamodel.util.AggregateBuilder; + +public class RandomAggregateBuilder implements AggregateBuilder<Object> { + + private Object _result; + private long _count; + private final Random _random; + + public RandomAggregateBuilder() { + _result = null; + _count = 0; + _random = new Random(); + } + + @Override + public void add(Object o) { + if (o == null) { + return; + } + + _count++; + + if (_random.nextDouble() < (1d / _count)) { + _result = o; + } + } + + @Override + public Object getAggregate() { + return _result; + } + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/main/java/org/apache/metamodel/query/RandomAggregateFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/metamodel/query/RandomAggregateFunction.java b/core/src/main/java/org/apache/metamodel/query/RandomAggregateFunction.java new file mode 100644 index 0000000..1a53816 --- /dev/null +++ b/core/src/main/java/org/apache/metamodel/query/RandomAggregateFunction.java @@ -0,0 +1,35 @@ +/** + * 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.util.AggregateBuilder; + +public class RandomAggregateFunction extends DefaultAggregateFunction<Object> { + + @Override + public AggregateBuilder<?> createAggregateBuilder() { + return new RandomAggregateBuilder(); + } + + @Override + public String getFunctionName() { + return "RANDOM"; + } + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/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 d8d046c..7dcaa12 100644 --- a/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java +++ b/core/src/test/java/org/apache/metamodel/QueryPostprocessDataContextTest.java @@ -185,6 +185,24 @@ public class QueryPostprocessDataContextTest extends MetaModelTestCase { dataSet.close(); } + public void testNewAggregateFunctions() throws Exception { + MockDataContext dc = new MockDataContext("sch", "tab", null); + Table table = dc.getDefaultSchema().getTables()[0]; + DataSet dataSet = dc.query().from(table).select(FunctionType.FIRST, "foo").select(FunctionType.LAST, "foo") + .select(FunctionType.RANDOM, "foo").execute(); + assertTrue(dataSet.next()); + + final Row row = dataSet.getRow(); + assertEquals("1", row.getValue(0)); + assertEquals("4", row.getValue(1)); + + final Object randomValue = row.getValue(2); + assertTrue(Arrays.asList("1", "2", "3", "4").contains(randomValue)); + + assertFalse(dataSet.next()); + dataSet.close(); + } + public void testAggregateQueryWhereClauseExcludingAll() throws Exception { MockDataContext dc = new MockDataContext("sch", "tab", "1"); assertSingleRowResult("Row[values=[0]]", http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/test/java/org/apache/metamodel/query/FirstAggregateFunctionTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/query/FirstAggregateFunctionTest.java b/core/src/test/java/org/apache/metamodel/query/FirstAggregateFunctionTest.java new file mode 100644 index 0000000..f29e5c4 --- /dev/null +++ b/core/src/test/java/org/apache/metamodel/query/FirstAggregateFunctionTest.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.metamodel.query; + +import static org.junit.Assert.*; + +import org.apache.metamodel.schema.ColumnType; +import org.apache.metamodel.util.AggregateBuilder; +import org.junit.Test; + +public class FirstAggregateFunctionTest { + + private static final AggregateFunction FUNCTION = FunctionType.FIRST; + + @Test + public void testGetName() throws Exception { + assertEquals("FIRST", FUNCTION.getFunctionName()); + assertEquals("FIRST", FUNCTION.toString()); + } + + @Test + public void testDataType() throws Exception { + assertEquals(ColumnType.BIGINT, FUNCTION.getExpectedColumnType(ColumnType.BIGINT)); + assertEquals(ColumnType.STRING, FUNCTION.getExpectedColumnType(ColumnType.STRING)); + } + + @Test + public void testBuildAggregate() throws Exception { + final AggregateBuilder<?> aggregateBuilder = FUNCTION.createAggregateBuilder(); + aggregateBuilder.add("1"); + aggregateBuilder.add("2"); + aggregateBuilder.add("3"); + assertEquals("1", aggregateBuilder.getAggregate()); + } +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/test/java/org/apache/metamodel/query/LastAggregateFunctionTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/query/LastAggregateFunctionTest.java b/core/src/test/java/org/apache/metamodel/query/LastAggregateFunctionTest.java new file mode 100644 index 0000000..1c26b78 --- /dev/null +++ b/core/src/test/java/org/apache/metamodel/query/LastAggregateFunctionTest.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.metamodel.query; + +import static org.junit.Assert.*; + +import org.apache.metamodel.schema.ColumnType; +import org.apache.metamodel.util.AggregateBuilder; +import org.junit.Test; + +public class LastAggregateFunctionTest { + + private static final AggregateFunction FUNCTION = FunctionType.LAST; + + @Test + public void testGetName() throws Exception { + assertEquals("LAST", FUNCTION.getFunctionName()); + assertEquals("LAST", FUNCTION.toString()); + } + + @Test + public void testDataType() throws Exception { + assertEquals(ColumnType.BIGINT, FUNCTION.getExpectedColumnType(ColumnType.BIGINT)); + assertEquals(ColumnType.STRING, FUNCTION.getExpectedColumnType(ColumnType.STRING)); + } + + @Test + public void testBuildAggregate() throws Exception { + final AggregateBuilder<?> aggregateBuilder = FUNCTION.createAggregateBuilder(); + aggregateBuilder.add("1"); + aggregateBuilder.add("2"); + aggregateBuilder.add("3"); + assertEquals("3", aggregateBuilder.getAggregate()); + } +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/test/java/org/apache/metamodel/query/RandomAggregateBuilderTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/query/RandomAggregateBuilderTest.java b/core/src/test/java/org/apache/metamodel/query/RandomAggregateBuilderTest.java new file mode 100644 index 0000000..005da55 --- /dev/null +++ b/core/src/test/java/org/apache/metamodel/query/RandomAggregateBuilderTest.java @@ -0,0 +1,71 @@ +/** + * 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 java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +public class RandomAggregateBuilderTest { + + @Test + public void testOnlyOne() throws Exception { + RandomAggregateBuilder aggregateBuilder = new RandomAggregateBuilder(); + aggregateBuilder.add("foo"); + assertEquals("foo", aggregateBuilder.getAggregate()); + } + + @Test + public void testRandomBehaviour() throws Exception { + // run 1000 tests with 3 values to ensure that there is at least an + // approximate fair distribution of randomized results + final int samples = 10000; + final int minimumExpectation = samples / 5; + + final Map<String, AtomicInteger> counterMap = new HashMap<>(); + counterMap.put("foo", new AtomicInteger()); + counterMap.put("bar", new AtomicInteger()); + counterMap.put("baz", new AtomicInteger()); + + for (int i = 0; i < samples; i++) { + final RandomAggregateBuilder aggregateBuilder = new RandomAggregateBuilder(); + aggregateBuilder.add("foo"); + aggregateBuilder.add(null); + aggregateBuilder.add("bar"); + aggregateBuilder.add("baz"); + + counterMap.get(aggregateBuilder.getAggregate()).incrementAndGet(); + } + + final String messageString = "got: " + counterMap.toString(); + final Set<Entry<String, AtomicInteger>> entries = counterMap.entrySet(); + for (Entry<String, AtomicInteger> entry : entries) { + final int count = entry.getValue().get(); + if (count < minimumExpectation) { + assertEquals(messageString, minimumExpectation, count); + } + } + } +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/core/src/test/java/org/apache/metamodel/query/RandomAggregateFunctionTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/metamodel/query/RandomAggregateFunctionTest.java b/core/src/test/java/org/apache/metamodel/query/RandomAggregateFunctionTest.java new file mode 100644 index 0000000..a6bf6c4 --- /dev/null +++ b/core/src/test/java/org/apache/metamodel/query/RandomAggregateFunctionTest.java @@ -0,0 +1,55 @@ +/** + * 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.*; + +import java.util.Arrays; + +import org.apache.metamodel.schema.ColumnType; +import org.apache.metamodel.util.AggregateBuilder; +import org.junit.Test; + +public class RandomAggregateFunctionTest { + + private static final AggregateFunction FUNCTION = FunctionType.RANDOM; + + @Test + public void testGetName() throws Exception { + assertEquals("RANDOM", FUNCTION.getFunctionName()); + assertEquals("RANDOM", FUNCTION.toString()); + } + + @Test + public void testDataType() throws Exception { + assertEquals(ColumnType.BIGINT, FUNCTION.getExpectedColumnType(ColumnType.BIGINT)); + assertEquals(ColumnType.STRING, FUNCTION.getExpectedColumnType(ColumnType.STRING)); + } + + @Test + public void testBuildAggregate() throws Exception { + final AggregateBuilder<?> aggregateBuilder = FUNCTION.createAggregateBuilder(); + aggregateBuilder.add("foo"); + aggregateBuilder.add("bar"); + aggregateBuilder.add("baz"); + final Object aggregate = aggregateBuilder.getAggregate(); + + assertTrue(Arrays.asList("foo", "bar", "baz").contains(aggregate)); + } +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java ---------------------------------------------------------------------- diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java index 27f638b..84254f9 100644 --- a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java +++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java @@ -55,6 +55,7 @@ import org.apache.metamodel.jdbc.dialects.OracleQueryRewriter; import org.apache.metamodel.jdbc.dialects.PostgresqlQueryRewriter; import org.apache.metamodel.jdbc.dialects.SQLServerQueryRewriter; import org.apache.metamodel.jdbc.dialects.SQLiteQueryRewriter; +import org.apache.metamodel.query.AggregateFunction; import org.apache.metamodel.query.CompiledQuery; import org.apache.metamodel.query.Query; import org.apache.metamodel.query.SelectItem; @@ -353,6 +354,14 @@ public class JdbcDataContext extends AbstractDataContext implements UpdateableDa + query); } + for (SelectItem selectItem : selectItems) { + final AggregateFunction aggregateFunction = selectItem.getAggregateFunction(); + if (aggregateFunction != null && !_queryRewriter.isAggregateFunctionSupported(aggregateFunction)) { + throw new MetaModelException("Aggregate function '" + aggregateFunction.getFunctionName() + + "' is not supported on this JDBC database. Query rejected: " + query); + } + } + if (_databaseProductName.equals(DATABASE_PRODUCT_POSTGRESQL)) { try { // this has to be done in order to make a result set not load http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java ---------------------------------------------------------------------- diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java index 13db9df..cbdb3c5 100644 --- a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java +++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/DefaultQueryRewriter.java @@ -19,16 +19,26 @@ package org.apache.metamodel.jdbc.dialects; import java.sql.Timestamp; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; +import java.util.Set; import org.apache.metamodel.jdbc.JdbcDataContext; +import org.apache.metamodel.query.AggregateFunction; +import org.apache.metamodel.query.AverageAggregateFunction; +import org.apache.metamodel.query.CountAggregateFunction; import org.apache.metamodel.query.FilterItem; import org.apache.metamodel.query.FromItem; +import org.apache.metamodel.query.FunctionType; +import org.apache.metamodel.query.MaxAggregateFunction; +import org.apache.metamodel.query.MinAggregateFunction; import org.apache.metamodel.query.OperatorType; import org.apache.metamodel.query.Query; import org.apache.metamodel.query.ScalarFunction; import org.apache.metamodel.query.SelectItem; +import org.apache.metamodel.query.SumAggregateFunction; import org.apache.metamodel.schema.ColumnType; import org.apache.metamodel.util.CollectionUtils; @@ -39,6 +49,9 @@ import org.apache.metamodel.util.CollectionUtils; public class DefaultQueryRewriter extends AbstractQueryRewriter { private static final String SPECIAL_ALIAS_CHARACTERS = "- ,.|*%()!#¤/\\=?;:~"; + private static final Set<Class<? extends FunctionType>> SUPPORTED_FUNCTION_CLASSES = new HashSet<>( + Arrays.<Class<? extends FunctionType>> asList(CountAggregateFunction.class, SumAggregateFunction.class, + MaxAggregateFunction.class, MinAggregateFunction.class, AverageAggregateFunction.class)); public DefaultQueryRewriter(JdbcDataContext dataContext) { super(dataContext); @@ -184,7 +197,12 @@ public class DefaultQueryRewriter extends AbstractQueryRewriter { @Override public boolean isScalarFunctionSupported(ScalarFunction function) { - return false; + return SUPPORTED_FUNCTION_CLASSES.contains(function.getClass()); + } + + @Override + public boolean isAggregateFunctionSupported(AggregateFunction function) { + return SUPPORTED_FUNCTION_CLASSES.contains(function.getClass()); } @Override http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java ---------------------------------------------------------------------- diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java index 9562b67..f867cb1 100644 --- a/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java +++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/dialects/IQueryRewriter.java @@ -21,6 +21,7 @@ package org.apache.metamodel.jdbc.dialects; import java.sql.Types; import org.apache.metamodel.jdbc.JdbcDataContext; +import org.apache.metamodel.query.AggregateFunction; import org.apache.metamodel.query.FilterItem; import org.apache.metamodel.query.FromItem; import org.apache.metamodel.query.Query; @@ -75,6 +76,18 @@ public interface IQueryRewriter { public boolean isScalarFunctionSupported(ScalarFunction function); /** + * Determines whether a specific aggregate function is supported by the + * database or not. + * + * If the function is not supported then MetaModel will handle the function + * on the client side. + * + * @param function + * @return + */ + public boolean isAggregateFunctionSupported(AggregateFunction function); + + /** * Escapes the quotes within a String literal of a query item. * * @return String item with quotes escaped. http://git-wip-us.apache.org/repos/asf/metamodel/blob/300e6b67/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java ---------------------------------------------------------------------- diff --git a/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java b/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java index 63af5d3..17b86fa 100644 --- a/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java +++ b/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java @@ -261,6 +261,18 @@ public class JdbcDataContextTest extends JdbcTestCase { } } + public void testUnsupportedAggregateFunction() throws Exception { + final Connection connection = getTestDbConnection(); + final JdbcDataContext dataContext = new JdbcDataContext(connection); + try { + dataContext.query().from("customers").select(FunctionType.RANDOM, "customernumber").execute(); + fail("Exception expected"); + } catch (MetaModelException e) { + assertEquals("Aggregate function 'RANDOM' is not supported on this JDBC database. Query rejected: " + + "SELECT RANDOM(\"CUSTOMERS\".\"CUSTOMERNUMBER\") FROM PUBLIC.\"CUSTOMERS\"", e.getMessage()); + } + } + public void testExecuteQueryWithComparisonGreaterThanOrEquals() throws Exception { Connection connection = getTestDbConnection(); JdbcDataContext dataContext = new JdbcDataContext(connection,