/* * Copyright 2012-2014 the original author or authors. * * Licensed 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.calcite.adapter.jdbc.tools; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import org.apache.calcite.adapter.jdbc.JdbcTableUtils; import org.apache.calcite.plan.Context; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptSchema; import org.apache.calcite.rel.core.TableModify; import org.apache.calcite.rel.logical.LogicalTableModify; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexFieldCollation; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexWindowBound; import org.apache.calcite.schema.Table; import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlWindow; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.tools.RelBuilder; import com.google.common.collect.ImmutableList; @SuppressWarnings({"WeakerAccess", "SameParameterValue"}) // Public API public class JdbcRelBuilder extends RelBuilder { private JdbcRelBuilder(Context context, RelOptCluster cluster, RelOptSchema relOptSchema) { super(context, cluster, relOptSchema); } public RexNode makeOver( SqlAggFunction operator, List<RexNode> expressions, List<RexNode> partitionKeys ) { final Set<SqlKind> flags = EnumSet.noneOf(SqlKind.class); // TODO // This is a temporal fix to make HAWQ work with OVER + UNLIMITED BOUNDS // HAWQ requires ORDER BY if andy BOUNDS are set even unlimited upper and lower BOUNDS (which is equal to // the entire partition - e.g. not setting BOUNDs at all -- // Note that the unnecessary ORDER BY have negative performance impact and has to be remove once either HAWQ // start supporting unbounded bounds without order by or Calcite can generate shorthand OVER PARTITION BY // syntax. List<RexFieldCollation> orderKeys = expressions.stream().map( rexNode -> new RexFieldCollation(rexNode, flags)).collect(Collectors.toList()); return makeOver( operator, expressions, partitionKeys, ImmutableList.copyOf(orderKeys), RexWindowBound.create(SqlWindow.createUnboundedPreceding(SqlParserPos.ZERO), null), RexWindowBound.create(SqlWindow.createUnboundedFollowing(SqlParserPos.ZERO), null), true, true, false ); } public RexNode makeOver( SqlAggFunction operator, List<RexNode> expressions, List<RexNode> partitionKeys, ImmutableList<RexFieldCollation> orderKeys, // Calcite API is weird RexWindowBound lowerBound, RexWindowBound upperBound, boolean physical, boolean allowPartial, boolean nullWhenCountZero ) { List<RelDataType> types = new ArrayList<>(expressions.size()); for (RexNode n : expressions) { types.add(n.getType()); } return getRexBuilder().makeOver( operator.inferReturnType(getTypeFactory(), types), operator, expressions, partitionKeys, orderKeys, lowerBound, upperBound, physical, allowPartial, nullWhenCountZero ); } public RexInputRef appendField(RexNode field) { List<RexNode> fields = new ArrayList<>(); fields.addAll(fields()); fields.add(field); project(fields); return field(fields.size() - 1); } // Table MUST be a JdbcTable (cannot be type-safe since JdbcTable is package-private) public JdbcRelBuilder scanJdbc(Table table, List<String> qualifiedName) { push(JdbcTableUtils.toRel(cluster, relOptSchema, table, qualifiedName)); return this; } public JdbcRelBuilder insertCopying( LogicalTableModify original, Table table ) { List<String> name = JdbcTableUtils.getQualifiedName(original.getTable(), table); push(new LogicalTableModify( cluster, original.getTraitSet(), relOptSchema.getTableForMember(name), original.getCatalogReader(), peek(), TableModify.Operation.INSERT, null, null, original.isFlattened() )); return this; } public static class Factory implements JdbcRelBuilderFactory { private Context context; public Factory(Context context) { this.context = context; } @Override public JdbcRelBuilder create(RelOptCluster cluster, RelOptSchema schema) { return new JdbcRelBuilder(context, cluster, schema); } } public static class FactoryFactory implements JdbcRelBuilderFactoryFactory { @Override public JdbcRelBuilderFactory create(Context context) { return new JdbcRelBuilder.Factory(context); } } }