Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.hazelcast.org.apache.calcite.adapter.enumerable.EnumerableRelImplementor Maven / Gradle / Ivy
/*
* 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.DataContext;
import com.hazelcast.org.apache.calcite.jdbc.JavaTypeFactoryImpl;
import com.hazelcast.org.apache.calcite.linq4j.Enumerable;
import com.hazelcast.org.apache.calcite.linq4j.function.Function1;
import com.hazelcast.org.apache.calcite.linq4j.tree.BlockBuilder;
import com.hazelcast.org.apache.calcite.linq4j.tree.BlockStatement;
import com.hazelcast.org.apache.calcite.linq4j.tree.Blocks;
import com.hazelcast.org.apache.calcite.linq4j.tree.ClassDeclaration;
import com.hazelcast.org.apache.calcite.linq4j.tree.ConditionalStatement;
import com.hazelcast.org.apache.calcite.linq4j.tree.ConstantExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.Expression;
import com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType;
import com.hazelcast.org.apache.calcite.linq4j.tree.Expressions;
import com.hazelcast.org.apache.calcite.linq4j.tree.FunctionExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.GotoStatement;
import com.hazelcast.org.apache.calcite.linq4j.tree.MemberDeclaration;
import com.hazelcast.org.apache.calcite.linq4j.tree.MethodCallExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.NewArrayExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.NewExpression;
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.Statement;
import com.hazelcast.org.apache.calcite.linq4j.tree.Types;
import com.hazelcast.org.apache.calcite.linq4j.tree.UnaryExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.VisitorImpl;
import com.hazelcast.org.apache.calcite.plan.RelOptUtil;
import com.hazelcast.org.apache.calcite.rex.RexBuilder;
import com.hazelcast.org.apache.calcite.runtime.Bindable;
import com.hazelcast.org.apache.calcite.sql.SqlExplainLevel;
import com.hazelcast.org.apache.calcite.sql.validate.SqlConformance;
import com.hazelcast.org.apache.calcite.sql.validate.SqlConformanceEnum;
import com.hazelcast.org.apache.calcite.util.BuiltInMethod;
import com.hazelcast.com.google.com.hazelcast.com.on.annotations.VisibleForTesting;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Collections2;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableList;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Iterables;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Subclass of {@link com.hazelcast.org.apache.calcite.plan.RelImplementor} for relational
* operators of {@link EnumerableConvention} calling convention.
*/
public class EnumerableRelImplementor extends JavaRelImplementor {
public final Map map;
private final Map corrVars =
new HashMap<>();
private final Map stashedParameters =
new IdentityHashMap<>();
protected final Function1 allCorrelateVariables =
this::getCorrelVariableGetter;
public EnumerableRelImplementor(RexBuilder rexBuilder,
Map internalParameters) {
super(rexBuilder);
this.map = internalParameters;
}
public EnumerableRel.Result visitChild(
EnumerableRel parent,
int ordinal,
EnumerableRel child,
EnumerableRel.Prefer prefer) {
if (parent != null) {
assert child == parent.getInputs().get(ordinal);
}
return child.implement(this, prefer);
}
public ClassDeclaration implementRoot(EnumerableRel rootRel,
EnumerableRel.Prefer prefer) {
EnumerableRel.Result result;
try {
result = rootRel.implement(this, prefer);
} catch (RuntimeException e) {
IllegalStateException ex = new IllegalStateException("Unable to implement "
+ RelOptUtil.toString(rootRel, SqlExplainLevel.ALL_ATTRIBUTES));
ex.addSuppressed(e);
throw ex;
}
switch (prefer) {
case ARRAY:
if (result.physType.getFormat() == JavaRowFormat.ARRAY
&& rootRel.getRowType().getFieldCount() == 1) {
BlockBuilder bb = new BlockBuilder();
Expression e = null;
for (Statement statement : result.block.statements) {
if (statement instanceof GotoStatement) {
e = bb.append("v", ((GotoStatement) statement).expression);
} else {
bb.add(statement);
}
}
if (e != null) {
bb.add(
Expressions.return_(null,
Expressions.call(null, BuiltInMethod.SLICE0.method, e)));
}
result = new EnumerableRel.Result(bb.toBlock(), result.physType,
JavaRowFormat.SCALAR);
}
}
final List memberDeclarations = new ArrayList<>();
new TypeRegistrar(memberDeclarations).go(result);
// This creates the following code
// final Integer v1stashed = (Integer) root.get("v1stashed")
// It is convenient for passing non-literal "com.hazelcast.com.ile-time" constants
final Collection stashed =
Collections2.transform(stashedParameters.values(),
input -> Expressions.declare(Modifier.FINAL, input,
Expressions.convert_(
Expressions.call(DataContext.ROOT,
BuiltInMethod.DATA_CONTEXT_GET.method,
Expressions.constant(input.name)),
input.type)));
final BlockStatement block = Expressions.block(
Iterables.concat(
stashed,
result.block.statements));
memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
Enumerable.class,
BuiltInMethod.BINDABLE_BIND.method.getName(),
Expressions.list(DataContext.ROOT),
block));
memberDeclarations.add(
Expressions.methodDecl(Modifier.PUBLIC, Class.class,
BuiltInMethod.TYPED_GET_ELEMENT_TYPE.method.getName(),
ImmutableList.of(),
Blocks.toFunctionBlock(
Expressions.return_(null,
Expressions.constant(result.physType.getJavaRowType())))));
return Expressions.classDecl(Modifier.PUBLIC,
"Baz",
null,
Collections.singletonList(Bindable.class),
memberDeclarations);
}
private ClassDeclaration classDecl(
JavaTypeFactoryImpl.SyntheticRecordType type) {
ClassDeclaration classDeclaration =
Expressions.classDecl(
Modifier.PUBLIC | Modifier.STATIC,
type.getName(),
null,
ImmutableList.of(Serializable.class),
new ArrayList<>());
// For each field:
// public T0 f0;
// ...
for (Types.RecordField field : type.getRecordFields()) {
classDeclaration.memberDeclarations.add(
Expressions.fieldDecl(
field.getModifiers(),
Expressions.parameter(
field.getType(), field.getName()),
null));
}
// Constructor:
// Foo(T0 f0, ...) { this.f0 = f0; ... }
final BlockBuilder blockBuilder = new BlockBuilder();
final List parameters = new ArrayList<>();
final ParameterExpression thisParameter =
Expressions.parameter(type, "this");
// Here a constructor without parameter is used because the generated
// code could cause error if number of fields is too large.
classDeclaration.memberDeclarations.add(
Expressions.constructorDecl(
Modifier.PUBLIC,
type,
parameters,
blockBuilder.toBlock()));
// equals method():
// public boolean equals(Object o) {
// if (this == o) return true;
// if (!(o instanceof MyClass)) return false;
// final MyClass that = (MyClass) o;
// return this.f0 == that.f0
// && equal(this.f1, that.f1)
// ...
// }
final BlockBuilder blockBuilder2 = new BlockBuilder();
final ParameterExpression thatParameter =
Expressions.parameter(type, "that");
final ParameterExpression oParameter =
Expressions.parameter(Object.class, "o");
blockBuilder2.add(
Expressions.ifThen(
Expressions.equal(thisParameter, oParameter),
Expressions.return_(null, Expressions.constant(true))));
blockBuilder2.add(
Expressions.ifThen(
Expressions.not(
Expressions.typeIs(oParameter, type)),
Expressions.return_(null, Expressions.constant(false))));
blockBuilder2.add(
Expressions.declare(
Modifier.FINAL,
thatParameter,
Expressions.convert_(oParameter, type)));
final List conditions = new ArrayList<>();
for (Types.RecordField field : type.getRecordFields()) {
conditions.add(
Primitive.is(field.getType())
? Expressions.equal(
Expressions.field(thisParameter, field.getName()),
Expressions.field(thatParameter, field.getName()))
: Expressions.call(BuiltInMethod.OBJECTS_EQUAL.method,
Expressions.field(thisParameter, field.getName()),
Expressions.field(thatParameter, field.getName())));
}
blockBuilder2.add(
Expressions.return_(null, Expressions.foldAnd(conditions)));
classDeclaration.memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
boolean.class,
"equals",
Collections.singletonList(oParameter),
blockBuilder2.toBlock()));
// hashCode method:
// public int hashCode() {
// int h = 0;
// h = hash(h, f0);
// ...
// return h;
// }
final BlockBuilder blockBuilder3 = new BlockBuilder();
final ParameterExpression hParameter =
Expressions.parameter(int.class, "h");
final ConstantExpression constantZero =
Expressions.constant(0);
blockBuilder3.add(
Expressions.declare(0, hParameter, constantZero));
for (Types.RecordField field : type.getRecordFields()) {
final Method method = BuiltInMethod.HASH.method;
blockBuilder3.add(
Expressions.statement(
Expressions.assign(
hParameter,
Expressions.call(
method.getDeclaringClass(),
method.getName(),
ImmutableList.of(
hParameter,
Expressions.field(thisParameter, field))))));
}
blockBuilder3.add(
Expressions.return_(null, hParameter));
classDeclaration.memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
int.class,
"hashCode",
Collections.emptyList(),
blockBuilder3.toBlock()));
// com.hazelcast.com.areTo method:
// public int com.hazelcast.com.areTo(MyClass that) {
// int c;
// c = com.hazelcast.com.are(this.f0, that.f0);
// if (c != 0) return c;
// ...
// return 0;
// }
final BlockBuilder blockBuilder4 = new BlockBuilder();
final ParameterExpression cParameter =
Expressions.parameter(int.class, "c");
final int mod = type.getRecordFields().size() == 1 ? Modifier.FINAL : 0;
blockBuilder4.add(
Expressions.declare(mod, cParameter, null));
final ConditionalStatement conditionalStatement =
Expressions.ifThen(
Expressions.notEqual(cParameter, constantZero),
Expressions.return_(null, cParameter));
for (Types.RecordField field : type.getRecordFields()) {
MethodCallExpression com.hazelcast.com.areCall;
try {
final Method method = (field.nullable()
? BuiltInMethod.COMPARE_NULLS_LAST
: BuiltInMethod.COMPARE).method;
com.hazelcast.com.areCall = Expressions.call(method.getDeclaringClass(),
method.getName(),
Expressions.field(thisParameter, field),
Expressions.field(thatParameter, field));
} catch (RuntimeException e) {
if (e.getCause() instanceof NoSuchMethodException) {
// Just ignore the field in com.hazelcast.com.areTo
// "create synthetic record class" blindly creates com.hazelcast.com.areTo for
// all the fields, however not all the records will actually be used
// as sorting keys (e.g. temporary state for aggregate calculation).
// In those cases it is fine if we skip the problematic fields.
continue;
}
throw e;
}
blockBuilder4.add(
Expressions.statement(
Expressions.assign(
cParameter,
com.hazelcast.com.areCall)));
blockBuilder4.add(conditionalStatement);
}
blockBuilder4.add(
Expressions.return_(null, constantZero));
classDeclaration.memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
int.class,
"com.hazelcast.com.areTo",
Collections.singletonList(thatParameter),
blockBuilder4.toBlock()));
// toString method:
// public String toString() {
// return "{f0=" + f0
// + ", f1=" + f1
// ...
// + "}";
// }
final BlockBuilder blockBuilder5 = new BlockBuilder();
Expression expression5 = null;
for (Types.RecordField field : type.getRecordFields()) {
if (expression5 == null) {
expression5 =
Expressions.constant("{" + field.getName() + "=");
} else {
expression5 =
Expressions.add(
expression5,
Expressions.constant(", " + field.getName() + "="));
}
expression5 =
Expressions.add(
expression5,
Expressions.field(thisParameter, field.getName()));
}
expression5 =
expression5 == null
? Expressions.constant("{}")
: Expressions.add(
expression5,
Expressions.constant("}"));
blockBuilder5.add(
Expressions.return_(
null,
expression5));
classDeclaration.memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
String.class,
"toString",
Collections.emptyList(),
blockBuilder5.toBlock()));
return classDeclaration;
}
/**
* Stashes a value for the executor. Given values are de-duplicated if
* identical (see {@link java.util.IdentityHashMap}).
*
* For instance, to pass {@code ArrayList} to your method, you can use
* {@code Expressions.call(method, implementor.stash(arrayList))}.
*
*
For simple literals (strings, numbers) the result is equivalent to
* {@link com.hazelcast.org.apache.calcite.linq4j.tree.Expressions#constant(Object, java.lang.reflect.Type)}.
*
*
Note: the input value is held in memory as long as the statement
* is alive. If you are using just a subset of its content, consider creating
* a slimmer holder.
*
* @param input Value to be stashed
* @param clazz Java class type of the value when it is used
* @param Java class type of the value when it is used
* @return Expression that will represent {@code input} in runtime
*/
public Expression stash(T input, Class clazz) {
// Well-known final classes that can be used as literals
if (input == null
|| input instanceof String
|| input instanceof Boolean
|| input instanceof Byte
|| input instanceof Short
|| input instanceof Integer
|| input instanceof Long
|| input instanceof Float
|| input instanceof Double) {
return Expressions.constant(input, clazz);
}
ParameterExpression cached = stashedParameters.get(input);
if (cached != null) {
return cached;
}
// "stashed" avoids name clash since this name will be used as the variable
// name at the very start of the method.
final String name = "v" + map.size() + "stashed";
final ParameterExpression x = Expressions.variable(clazz, name);
map.put(name, input);
stashedParameters.put(input, x);
return x;
}
public void registerCorrelVariable(final String name,
final ParameterExpression pe,
final BlockBuilder corrBlock, final PhysType physType) {
corrVars.put(name, (list, index, storageType) -> {
Expression fieldReference =
physType.fieldReference(pe, index, storageType);
return corrBlock.append(name + "_" + index, fieldReference);
});
}
public void clearCorrelVariable(String name) {
assert corrVars.containsKey(name) : "Correlation variable " + name
+ " should be defined";
corrVars.remove(name);
}
public RexToLixTranslator.InputGetter getCorrelVariableGetter(String name) {
assert corrVars.containsKey(name) : "Correlation variable " + name
+ " should be defined";
return corrVars.get(name);
}
public EnumerableRel.Result result(PhysType physType, BlockStatement block) {
return new EnumerableRel.Result(
block, physType, ((PhysTypeImpl) physType).format);
}
public SqlConformance getConformance() {
return (SqlConformance) map.getOrDefault("_conformance",
SqlConformanceEnum.DEFAULT);
}
/** Visitor that finds types in an {@link Expression} tree. */
@VisibleForTesting
static class TypeFinder extends VisitorImpl {
private final Collection types;
TypeFinder(Collection types) {
this.types = types;
}
@Override public Void visit(NewExpression newExpression) {
types.add(newExpression.type);
return super.visit(newExpression);
}
@Override public Void visit(NewArrayExpression newArrayExpression) {
Type type = newArrayExpression.type;
for (;;) {
final Type com.hazelcast.com.onentType = Types.getComponentType(type);
if (com.hazelcast.com.onentType == null) {
break;
}
type = com.hazelcast.com.onentType;
}
types.add(type);
return super.visit(newArrayExpression);
}
@Override public Void visit(ConstantExpression constantExpression) {
final Object value = constantExpression.value;
if (value instanceof Type) {
types.add((Type) value);
}
if (value == null) {
// null literal
Type type = constantExpression.getType();
types.add(type);
}
return super.visit(constantExpression);
}
@Override public Void visit(FunctionExpression functionExpression) {
final List list = functionExpression.parameterList;
for (ParameterExpression pe : list) {
types.add(pe.getType());
}
if (functionExpression.body == null) {
return super.visit(functionExpression);
}
types.add(functionExpression.body.getType());
return super.visit(functionExpression);
}
@Override public Void visit(UnaryExpression unaryExpression) {
if (unaryExpression.nodeType == ExpressionType.Convert) {
types.add(unaryExpression.getType());
}
return super.visit(unaryExpression);
}
}
/** Adds a declaration of each synthetic type found in a code block. */
private class TypeRegistrar {
private final List memberDeclarations;
private final Set seen = new HashSet<>();
TypeRegistrar(List memberDeclarations) {
this.memberDeclarations = memberDeclarations;
}
private void register(Type type) {
if (!seen.add(type)) {
return;
}
if (type instanceof JavaTypeFactoryImpl.SyntheticRecordType) {
memberDeclarations.add(
classDecl((JavaTypeFactoryImpl.SyntheticRecordType) type));
}
if (type instanceof ParameterizedType) {
for (Type type1 : ((ParameterizedType) type).getActualTypeArguments()) {
register(type1);
}
}
}
public void go(EnumerableRel.Result result) {
final Set types = new LinkedHashSet<>();
result.block.accept(new TypeFinder(types));
types.add(result.physType.getJavaRowType());
for (Type type : types) {
register(type);
}
}
}
}