com.hazelcast.org.apache.calcite.rel.metadata.JaninoRelMetadataProvider 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 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 com.hazelcast.org.apache.calcite.rel.metadata;
import com.hazelcast.org.apache.calcite.config.CalciteSystemProperty;
import com.hazelcast.org.apache.calcite.interpreter.JaninoRexCompiler;
import com.hazelcast.org.apache.calcite.rel.RelNode;
import com.hazelcast.org.apache.calcite.rel.metadata.janino.RelMetadataHandlerGeneratorUtil;
import com.hazelcast.org.apache.calcite.util.Util;
import com.hazelcast.com.google.common.annotations.VisibleForTesting;
import com.hazelcast.com.google.common.cache.CacheBuilder;
import com.hazelcast.com.google.common.cache.CacheLoader;
import com.hazelcast.com.google.common.cache.LoadingCache;
import com.hazelcast.com.google.common.collect.Multimap;
import com.hazelcast.com.google.common.util.concurrent.UncheckedExecutionException;
import org.apiguardian.api.API;
import com.hazelcast.org.checkerframework.checker.nullness.qual.Nullable;
import com.hazelcast.org.codehaus.commons.compiler.CompileException;
import com.hazelcast.org.codehaus.commons.compiler.CompilerFactoryFactory;
import com.hazelcast.org.codehaus.commons.compiler.ICompilerFactory;
import com.hazelcast.org.codehaus.commons.compiler.ISimpleCompiler;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
/**
* Implementation of the {@link RelMetadataProvider} interface that generates
* a class that dispatches to the underlying providers.
*/
public class JaninoRelMetadataProvider implements RelMetadataProvider, MetadataHandlerProvider {
private final RelMetadataProvider provider;
// Constants and static fields
public static final JaninoRelMetadataProvider DEFAULT =
JaninoRelMetadataProvider.of(DefaultRelMetadataProvider.INSTANCE);
/** Cache of pre-generated handlers by provider and kind of metadata.
* For the cache to be effective, providers should implement identity
* correctly. */
private static final LoadingCache> HANDLERS =
maxSize(CacheBuilder.newBuilder(),
CalciteSystemProperty.METADATA_HANDLER_CACHE_MAXIMUM_SIZE.value())
.build(
CacheLoader.from(key ->
generateCompileAndInstantiate(key.handlerClass,
key.provider.handlers(key.handlerClass))));
/** Private constructor; use {@link #of}. */
private JaninoRelMetadataProvider(RelMetadataProvider provider) {
this.provider = provider;
}
/** Creates a JaninoRelMetadataProvider.
*
* @param provider Underlying provider
*/
public static JaninoRelMetadataProvider of(RelMetadataProvider provider) {
if (provider instanceof JaninoRelMetadataProvider) {
return (JaninoRelMetadataProvider) provider;
}
return new JaninoRelMetadataProvider(provider);
}
// helper for initialization
private static CacheBuilder maxSize(CacheBuilder builder,
int size) {
if (size >= 0) {
builder.maximumSize(size);
}
return builder;
}
@Override public boolean equals(@Nullable Object obj) {
return obj == this
|| obj instanceof JaninoRelMetadataProvider
&& ((JaninoRelMetadataProvider) obj).provider.equals(provider);
}
@Override public int hashCode() {
return 109 + provider.hashCode();
}
@Deprecated // to be removed before 2.0
@Override public <@Nullable M extends @Nullable Metadata> UnboundMetadata apply(
Class extends RelNode> relClass, Class extends M> metadataClass) {
throw new UnsupportedOperationException();
}
@Deprecated // to be removed before 2.0
@Override public Multimap>
handlers(MetadataDef def) {
return provider.handlers(def);
}
@Override public List> handlers(
Class extends MetadataHandler>> handlerClass) {
return provider.handlers(handlerClass);
}
private static > MH generateCompileAndInstantiate(
Class handlerClass,
List extends MetadataHandler extends Metadata>> handlers) {
final List extends MetadataHandler extends Metadata>> uniqueHandlers = handlers.stream()
.distinct()
.collect(Collectors.toList());
RelMetadataHandlerGeneratorUtil.HandlerNameAndGeneratedCode handlerNameAndGeneratedCode =
RelMetadataHandlerGeneratorUtil.generateHandler(handlerClass, uniqueHandlers);
try {
return compile(handlerNameAndGeneratedCode.getHandlerName(),
handlerNameAndGeneratedCode.getGeneratedCode(), handlerClass, uniqueHandlers);
} catch (CompileException | IOException e) {
throw new RuntimeException("Error compiling:\n"
+ handlerNameAndGeneratedCode.getGeneratedCode(), e);
}
}
static > MH compile(String className,
String generatedCode, Class handlerClass,
List extends Object> argList) throws CompileException, IOException {
final ICompilerFactory compilerFactory;
ClassLoader classLoader =
Objects.requireNonNull(JaninoRelMetadataProvider.class.getClassLoader(), "classLoader");
try {
compilerFactory = CompilerFactoryFactory.getDefaultCompilerFactory(classLoader);
} catch (Exception e) {
throw new IllegalStateException(
"Unable to instantiate java compiler", e);
}
final ISimpleCompiler compiler = compilerFactory.newSimpleCompiler();
compiler.setParentClassLoader(JaninoRexCompiler.class.getClassLoader());
if (CalciteSystemProperty.DEBUG.value()) {
// Add line numbers to the generated janino class
compiler.setDebuggingInformation(true, true, true);
System.out.println(generatedCode);
}
compiler.cook(generatedCode);
final Constructor constructor;
final Object o;
try {
constructor = compiler.getClassLoader().loadClass(className)
.getDeclaredConstructors()[0];
o = constructor.newInstance(argList.toArray());
} catch (InstantiationException
| IllegalAccessException
| InvocationTargetException
| ClassNotFoundException e) {
throw new RuntimeException(e);
}
return handlerClass.cast(o);
}
@Override public synchronized > H revise(Class handlerClass) {
try {
final Key key = new Key(handlerClass, provider);
//noinspection unchecked
return handlerClass.cast(HANDLERS.get(key));
} catch (UncheckedExecutionException | ExecutionException e) {
throw Util.throwAsRuntime(Util.causeOrSelf(e));
}
}
/** Registers some classes. Does not flush the providers, but next time we
* need to generate a provider, it will handle all of these classes. So,
* calling this method reduces the number of times we need to re-generate. */
@Deprecated
public void register(Iterable> classes) {
}
/** Exception that indicates there there should be a handler for
* this class but there is not. The action is probably to
* re-generate the handler class. Use {@link MetadataHandlerProvider.NoHandler} instead.
* */
@Deprecated
public static class NoHandler extends MetadataHandlerProvider.NoHandler {
public NoHandler(Class extends RelNode> relClass) {
super(relClass);
}
}
/** Key for the cache. */
private static class Key {
final Class extends MetadataHandler extends Metadata>> handlerClass;
final RelMetadataProvider provider;
private Key(Class extends MetadataHandler>> handlerClass,
RelMetadataProvider provider) {
this.handlerClass = handlerClass;
this.provider = provider;
}
@Override public int hashCode() {
return (handlerClass.hashCode() * 37
+ provider.hashCode()) * 37;
}
@Override public boolean equals(@Nullable Object obj) {
return this == obj
|| obj instanceof Key
&& ((Key) obj).handlerClass.equals(handlerClass)
&& ((Key) obj).provider.equals(provider);
}
}
@SuppressWarnings("deprecation")
@Override public > MH handler(final Class handlerClass) {
return handlerClass.cast(
Proxy.newProxyInstance(RelMetadataQuery.class.getClassLoader(),
new Class[] {handlerClass}, (proxy, method, args) -> {
final RelNode r = requireNonNull((RelNode) args[0], "(RelNode) args[0]");
throw new NoHandler(r.getClass());
}));
}
@API(status = API.Status.INTERNAL)
@VisibleForTesting
public static void clearStaticCache() {
HANDLERS.invalidateAll();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy