All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableTableScan Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * 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 com.hazelcast.com.liance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.com.hazelcast.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 com.hazelcast.org.apache.calcite.adapter.enumerable;

import com.hazelcast.org.apache.calcite.adapter.java.JavaTypeFactory;
import com.hazelcast.org.apache.calcite.config.CalciteSystemProperty;
import com.hazelcast.org.apache.calcite.interpreter.Row;
import com.hazelcast.org.apache.calcite.linq4j.Enumerable;
import com.hazelcast.org.apache.calcite.linq4j.Queryable;
import com.hazelcast.org.apache.calcite.linq4j.function.Function1;
import com.hazelcast.org.apache.calcite.linq4j.tree.Blocks;
import com.hazelcast.org.apache.calcite.linq4j.tree.Expression;
import com.hazelcast.org.apache.calcite.linq4j.tree.Expressions;
import com.hazelcast.org.apache.calcite.linq4j.tree.MethodCallExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.ParameterExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.Primitive;
import com.hazelcast.org.apache.calcite.linq4j.tree.Types;
import com.hazelcast.org.apache.calcite.plan.RelOptCluster;
import com.hazelcast.org.apache.calcite.plan.RelOptTable;
import com.hazelcast.org.apache.calcite.plan.RelOptUtil;
import com.hazelcast.org.apache.calcite.plan.RelTraitSet;
import com.hazelcast.org.apache.calcite.rel.RelCollationTraitDef;
import com.hazelcast.org.apache.calcite.rel.RelNode;
import com.hazelcast.org.apache.calcite.rel.core.TableScan;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeField;
import com.hazelcast.org.apache.calcite.schema.FilterableTable;
import com.hazelcast.org.apache.calcite.schema.ProjectableFilterableTable;
import com.hazelcast.org.apache.calcite.schema.QueryableTable;
import com.hazelcast.org.apache.calcite.schema.ScannableTable;
import com.hazelcast.org.apache.calcite.schema.StreamableTable;
import com.hazelcast.org.apache.calcite.schema.Table;
import com.hazelcast.org.apache.calcite.schema.TransientTable;
import com.hazelcast.org.apache.calcite.sql.SqlExplainLevel;
import com.hazelcast.org.apache.calcite.util.BuiltInMethod;

import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableList;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/** Implementation of {@link com.hazelcast.org.apache.calcite.rel.core.TableScan} in
 * {@link com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
public class EnumerableTableScan
    extends TableScan
    implements EnumerableRel {
  private final Class elementType;

  /** Creates an EnumerableTableScan.
   *
   * 

Use {@link #create} unless you know what you are doing. */ public EnumerableTableScan(RelOptCluster cluster, RelTraitSet traitSet, RelOptTable table, Class elementType) { super(cluster, traitSet, ImmutableList.of(), table); assert getConvention() instanceof EnumerableConvention; this.elementType = elementType; assert canHandle(table) : "EnumerableTableScan can't implement " + table + ", see EnumerableTableScan#canHandle"; } /** Creates an EnumerableTableScan. */ public static EnumerableTableScan create(RelOptCluster cluster, RelOptTable relOptTable) { final Table table = relOptTable.unwrap(Table.class); Class elementType = EnumerableTableScan.deduceElementType(table); final RelTraitSet traitSet = cluster.traitSetOf(EnumerableConvention.INSTANCE) .replaceIfs(RelCollationTraitDef.INSTANCE, () -> { if (table != null) { return table.getStatistic().getCollations(); } return ImmutableList.of(); }); return new EnumerableTableScan(cluster, traitSet, relOptTable, elementType); } /** Returns whether EnumerableTableScan can generate code to handle a * particular variant of the Table SPI. * @deprecated **/ @Deprecated public static boolean canHandle(Table table) { if (table instanceof TransientTable) { // CALCITE-3673: TransientTable can't be implemented with Enumerable return false; } // See com.hazelcast.org.apache.calcite.prepare.RelOptTableImpl.getClassExpressionFunction return table instanceof QueryableTable || table instanceof FilterableTable || table instanceof ProjectableFilterableTable || table instanceof ScannableTable; } /** Returns whether EnumerableTableScan can generate code to handle a * particular variant of the Table SPI. **/ public static boolean canHandle(RelOptTable relOptTable) { Table table = relOptTable.unwrap(Table.class); if (table != null && !canHandle(table)) { return false; } boolean supportArray = CalciteSystemProperty.ENUMERABLE_ENABLE_TABLESCAN_ARRAY.value(); boolean supportMap = CalciteSystemProperty.ENUMERABLE_ENABLE_TABLESCAN_MAP.value(); boolean supportMultiset = CalciteSystemProperty.ENUMERABLE_ENABLE_TABLESCAN_MULTISET.value(); if (supportArray && supportMap && supportMultiset) { return true; } // Struct fields are not supported in EnumerableTableScan for (RelDataTypeField field : relOptTable.getRowType().getFieldList()) { boolean unsupportedType = false; switch (field.getType().getSqlTypeName()) { case ARRAY: unsupportedType = supportArray; break; case MAP: unsupportedType = supportMap; break; case MULTISET: unsupportedType = supportMultiset; break; default: break; } if (unsupportedType) { return false; } } return true; } public static Class deduceElementType(Table table) { if (table instanceof QueryableTable) { final QueryableTable queryableTable = (QueryableTable) table; final Type type = queryableTable.getElementType(); if (type instanceof Class) { return (Class) type; } else { return Object[].class; } } else if (table instanceof ScannableTable || table instanceof FilterableTable || table instanceof ProjectableFilterableTable || table instanceof StreamableTable) { return Object[].class; } else { return Object.class; } } public static JavaRowFormat deduceFormat(RelOptTable table) { final Class elementType = deduceElementType(table.unwrap(Table.class)); return elementType == Object[].class ? JavaRowFormat.ARRAY : JavaRowFormat.CUSTOM; } private Expression getExpression(PhysType physType) { final Expression expression = table.getExpression(Queryable.class); if (expression == null) { throw new IllegalStateException( "Unable to implement " + RelOptUtil.toString(this, SqlExplainLevel.ALL_ATTRIBUTES) + ": " + table + ".getExpression(Queryable.class) returned null"); } final Expression expression2 = toEnumerable(expression); assert Types.isAssignableFrom(Enumerable.class, expression2.getType()); return toRows(physType, expression2); } private Expression toEnumerable(Expression expression) { final Type type = expression.getType(); if (Types.isArray(type)) { if (Types.toClass(type).getComponentType().isPrimitive()) { expression = Expressions.call(BuiltInMethod.AS_LIST.method, expression); } return Expressions.call(BuiltInMethod.AS_ENUMERABLE.method, expression); } else if (Types.isAssignableFrom(Iterable.class, type) && !Types.isAssignableFrom(Enumerable.class, type)) { return Expressions.call(BuiltInMethod.AS_ENUMERABLE2.method, expression); } else if (Types.isAssignableFrom(Queryable.class, type)) { // Queryable extends Enumerable, but it's too "clever", so we call // Queryable.asEnumerable so that operations such as take(int) will be // evaluated directly. return Expressions.call(expression, BuiltInMethod.QUERYABLE_AS_ENUMERABLE.method); } return expression; } private Expression toRows(PhysType physType, Expression expression) { if (physType.getFormat() == JavaRowFormat.SCALAR && Object[].class.isAssignableFrom(elementType) && getRowType().getFieldCount() == 1 && (table.unwrap(ScannableTable.class) != null || table.unwrap(FilterableTable.class) != null || table.unwrap(ProjectableFilterableTable.class) != null)) { return Expressions.call(BuiltInMethod.SLICE0.method, expression); } JavaRowFormat oldFormat = format(); if (physType.getFormat() == oldFormat && !hasCollectionField(rowType)) { return expression; } final ParameterExpression row_ = Expressions.parameter(elementType, "row"); final int fieldCount = table.getRowType().getFieldCount(); List expressionList = new ArrayList<>(fieldCount); for (int i = 0; i < fieldCount; i++) { expressionList.add(fieldExpression(row_, i, physType, oldFormat)); } return Expressions.call(expression, BuiltInMethod.SELECT.method, Expressions.lambda(Function1.class, physType.record(expressionList), row_)); } private Expression fieldExpression(ParameterExpression row_, int i, PhysType physType, JavaRowFormat format) { final Expression e = format.field(row_, i, null, physType.getJavaFieldType(i)); final RelDataType relFieldType = physType.getRowType().getFieldList().get(i).getType(); switch (relFieldType.getSqlTypeName()) { case ARRAY: case MULTISET: final RelDataType fieldType = relFieldType.getComponentType(); if (fieldType.isStruct()) { // We can't represent a multiset or array as a List, because // the consumer does not know the element type. // The standard element type is List. // We need to convert to a List. final JavaTypeFactory typeFactory = (JavaTypeFactory) getCluster().getTypeFactory(); final PhysType elementPhysType = PhysTypeImpl.of( typeFactory, fieldType, JavaRowFormat.CUSTOM); final MethodCallExpression e2 = Expressions.call(BuiltInMethod.AS_ENUMERABLE2.method, e); final Expression e3 = elementPhysType.convertTo(e2, JavaRowFormat.LIST); return Expressions.call(e3, BuiltInMethod.ENUMERABLE_TO_LIST.method); } else { return e; } default: return e; } } private JavaRowFormat format() { int fieldCount = getRowType().getFieldCount(); if (fieldCount == 0) { return JavaRowFormat.LIST; } if (Object[].class.isAssignableFrom(elementType)) { return fieldCount == 1 ? JavaRowFormat.SCALAR : JavaRowFormat.ARRAY; } if (Row.class.isAssignableFrom(elementType)) { return JavaRowFormat.ROW; } if (fieldCount == 1 && (Object.class == elementType || Primitive.is(elementType) || Number.class.isAssignableFrom(elementType) || String.class == elementType)) { return JavaRowFormat.SCALAR; } return JavaRowFormat.CUSTOM; } private boolean hasCollectionField(RelDataType rowType) { for (RelDataTypeField field : rowType.getFieldList()) { switch (field.getType().getSqlTypeName()) { case ARRAY: case MULTISET: return true; } } return false; } @Override public RelNode copy(RelTraitSet traitSet, List inputs) { return new EnumerableTableScan(getCluster(), traitSet, table, elementType); } public Result implement(EnumerableRelImplementor implementor, Prefer pref) { // Note that representation is ARRAY. This assumes that the table // returns a Object[] for each record. Actually a Table can // return any type T. And, if it is a JdbcTable, we'd like to be // able to generate alternate accessors that return e.g. synthetic // records {T0 f0; T1 f1; ...} and don't box every primitive value. final PhysType physType = PhysTypeImpl.of( implementor.getTypeFactory(), getRowType(), format()); final Expression expression = getExpression(physType); return implementor.result(physType, Blocks.toBlock(expression)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy