diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java index b05806e6f54b..1f1cb99f5c61 100644 --- a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java +++ b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java @@ -52,6 +52,7 @@ import org.eigenbase.sql.util.ChainedSqlOperatorTable; import org.eigenbase.sql.validate.*; import org.eigenbase.sql2rel.SqlToRelConverter; +import org.eigenbase.util.Bug; import org.eigenbase.util.Util; import com.google.common.collect.*; @@ -496,9 +497,26 @@ private static RelDataType makeStruct( return typeFactory.builder().add("$0", type).build(); } - /** Executes an optimize action. */ + /** Executes an optimize action. + * + * @deprecated Will be removed after optiq-0.5; use + * {@link Frameworks#withPlanner} + */ public R withPlanner(OptiqServerStatement statement, - Frameworks.PlannerAction action) { + final Frameworks.PlannerAction action) { + Bug.upgrade("remove after 0.5"); + return perform(statement, + new Frameworks.PrepareAction() { + public R apply(RelOptCluster cluster, RelOptSchema relOptSchema, + SchemaPlus rootSchema, OptiqServerStatement statement) { + return action.apply(cluster, relOptSchema, rootSchema); + } + }); + } + + /** Executes a prepare action. */ + public R perform(OptiqServerStatement statement, + Frameworks.PrepareAction action) { final Context context = statement.createPrepareContext(); final JavaTypeFactory typeFactory = context.getTypeFactory(); OptiqCatalogReader catalogReader = new OptiqCatalogReader( @@ -511,7 +529,8 @@ public R withPlanner(OptiqServerStatement statement, final RelOptQuery query = new RelOptQuery(planner); final RelOptCluster cluster = query.createCluster(rexBuilder.getTypeFactory(), rexBuilder); - return action.apply(cluster, catalogReader, context.getRootSchema().plus()); + return action.apply(cluster, catalogReader, context.getRootSchema().plus(), + statement); } static class OptiqPreparingStmt extends Prepare diff --git a/core/src/main/java/net/hydromatic/optiq/server/OptiqServerStatement.java b/core/src/main/java/net/hydromatic/optiq/server/OptiqServerStatement.java index 15c11e311a46..52d0f803d373 100644 --- a/core/src/main/java/net/hydromatic/optiq/server/OptiqServerStatement.java +++ b/core/src/main/java/net/hydromatic/optiq/server/OptiqServerStatement.java @@ -17,6 +17,7 @@ */ package net.hydromatic.optiq.server; +import net.hydromatic.optiq.jdbc.OptiqConnection; import net.hydromatic.optiq.jdbc.OptiqPrepare; /** @@ -25,6 +26,9 @@ public interface OptiqServerStatement { /** Creates a context for preparing a statement for execution. */ OptiqPrepare.Context createPrepareContext(); + + /** Returns the connection. */ + OptiqConnection getConnection(); } // End OptiqServerStatement.java diff --git a/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java b/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java index 8dfa7f637678..1ee3658bf3d7 100644 --- a/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java +++ b/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java @@ -80,13 +80,40 @@ R apply(RelOptCluster cluster, RelOptSchema relOptSchema, SchemaPlus rootSchema); } + /** Piece of code to be run in a context where a planner and statement are + * available. The planner is accessible from the {@code cluster} parameter, as + * are several other useful objects. The connection and + * {@link net.hydromatic.optiq.DataContext} are accessible from the + * statement. */ + public interface PrepareAction { + R apply(RelOptCluster cluster, RelOptSchema relOptSchema, + SchemaPlus rootSchema, OptiqServerStatement statement); + } + /** * Initializes a container then calls user-specified code with a planner. * * @param action Callback containing user-specified code - * @return Result of optimization + * @return Return value from action + */ + public static R withPlanner(final PlannerAction action) { + return withPrepare( + new Frameworks.PrepareAction() { + public R apply(RelOptCluster cluster, RelOptSchema relOptSchema, + SchemaPlus rootSchema, OptiqServerStatement statement) { + return action.apply(cluster, relOptSchema, rootSchema); + } + }); + } + + /** + * Initializes a container then calls user-specified code with a planner + * and statement. + * + * @param action Callback containing user-specified code + * @return Return value from action */ - public static R withPlanner(PlannerAction action) { + public static R withPrepare(PrepareAction action) { try { Class.forName("net.hydromatic.optiq.jdbc.Driver"); Connection connection = @@ -95,7 +122,8 @@ public static R withPlanner(PlannerAction action) { connection.unwrap(OptiqConnection.class); final OptiqServerStatement statement = optiqConnection.createStatement().unwrap(OptiqServerStatement.class); - return new OptiqPrepareImpl().withPlanner(statement, action); + //noinspection deprecation + return new OptiqPrepareImpl().perform(statement, action); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/core/src/test/java/net/hydromatic/optiq/test/OptiqSuite.java b/core/src/test/java/net/hydromatic/optiq/test/OptiqSuite.java index f9a66f7217dd..ed04d1dbc66a 100644 --- a/core/src/test/java/net/hydromatic/optiq/test/OptiqSuite.java +++ b/core/src/test/java/net/hydromatic/optiq/test/OptiqSuite.java @@ -27,6 +27,7 @@ import org.eigenbase.relopt.RelWriterTest; import org.eigenbase.relopt.volcano.VolcanoPlannerTest; import org.eigenbase.relopt.volcano.VolcanoPlannerTraitTest; +import org.eigenbase.rex.RexExecutorTest; import org.eigenbase.sql.parser.SqlParserTest; import org.eigenbase.sql.test.*; import org.eigenbase.test.*; @@ -77,6 +78,7 @@ RelMetadataTest.class, HepPlannerTest.class, RelOptRulesTest.class, + RexExecutorTest.class, MaterializationTest.class, SqlLimitsTest.class, LinqFrontJdbcBackTest.class, diff --git a/core/src/test/java/org/eigenbase/rex/RexExecutorTest.java b/core/src/test/java/org/eigenbase/rex/RexExecutorTest.java new file mode 100644 index 000000000000..96c8afc4f95c --- /dev/null +++ b/core/src/test/java/org/eigenbase/rex/RexExecutorTest.java @@ -0,0 +1,116 @@ +/* +// Licensed to Julian Hyde under one or more contributor license +// agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. +// +// Julian Hyde 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.eigenbase.rex; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import org.eigenbase.relopt.RelOptCluster; +import org.eigenbase.relopt.RelOptSchema; +import org.eigenbase.sql.fun.SqlStdOperatorTable; +import org.eigenbase.util.NlsString; + +import net.hydromatic.optiq.DataContext; +import net.hydromatic.optiq.SchemaPlus; +import net.hydromatic.optiq.Schemas; +import net.hydromatic.optiq.server.OptiqServerStatement; +import net.hydromatic.optiq.tools.Frameworks; + +import com.google.common.collect.ImmutableList; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +/** + * Unit test for {@link org.eigenbase.rex.RexExecutorImpl}. + */ +public class RexExecutorTest { + public RexExecutorTest() { + } + + protected void check(final Action action) throws Exception { + Frameworks.withPrepare( + new Frameworks.PrepareAction() { + public Void apply(RelOptCluster cluster, RelOptSchema relOptSchema, + SchemaPlus rootSchema, OptiqServerStatement statement) { + final RexBuilder rexBuilder = cluster.getRexBuilder(); + DataContext dataContext = + Schemas.createDataContext(statement.getConnection()); + final RexExecutorImpl executor = new RexExecutorImpl(dataContext); + action.check(rexBuilder, executor); + return null; + } + }); + } + + @Test public void testConstant() throws Exception { + check(new Action() { + public void check(RexBuilder rexBuilder, RexExecutorImpl executor) { + final List reducedValues = new ArrayList(); + final RexLiteral ten = rexBuilder.makeExactLiteral(BigDecimal.TEN); + executor.execute(rexBuilder, ImmutableList.of(ten), + reducedValues); + assertThat(reducedValues.size(), equalTo(1)); + assertThat(reducedValues.get(0), instanceOf(RexLiteral.class)); + assertThat(((RexLiteral) reducedValues.get(0)).getValue2(), + equalTo((Object) 10L)); + } + }); + } + + @Test public void testSubstring() throws Exception { + check(new Action() { + public void check(RexBuilder rexBuilder, RexExecutorImpl executor) { + final List reducedValues = new ArrayList(); + final RexLiteral hello = + rexBuilder.makeCharLiteral( + new NlsString("Hello world!", null, null)); + final RexNode plus = + rexBuilder.makeCall(SqlStdOperatorTable.PLUS, + rexBuilder.makeExactLiteral(BigDecimal.ONE), + rexBuilder.makeExactLiteral(BigDecimal.ONE)); + RexLiteral four = rexBuilder.makeExactLiteral(BigDecimal.valueOf(4)); + final RexNode substring = + rexBuilder.makeCall(SqlStdOperatorTable.SUBSTRING, + hello, plus, four); + executor.execute(rexBuilder, ImmutableList.of(substring, plus), + reducedValues); + assertThat(reducedValues.size(), equalTo(2)); + assertThat(reducedValues.get(0), instanceOf(RexLiteral.class)); + assertThat(((RexLiteral) reducedValues.get(0)).getValue2(), + equalTo((Object) "ello")); // substring('Hello world!, 2, 4) + assertThat(reducedValues.get(1), instanceOf(RexLiteral.class)); + assertThat(((RexLiteral) reducedValues.get(1)).getValue2(), + equalTo((Object) 2L)); + } + }); + } + + /** Callback for {@link #check}. Test code will typically use {@code builder} + * to create some expressions, call + * {@link org.eigenbase.rex.RexExecutorImpl#execute} to evaluate them into + * a list, then check that the results are as expected. */ + interface Action { + void check(RexBuilder rexBuilder, RexExecutorImpl executor); + } +} + +// End RexExecutorTest.java diff --git a/src/main/config/checkstyle/suppressions.xml b/src/main/config/checkstyle/suppressions.xml index 6ccc72de8c9f..b1c95d921a17 100644 --- a/src/main/config/checkstyle/suppressions.xml +++ b/src/main/config/checkstyle/suppressions.xml @@ -39,6 +39,9 @@ + + +