blob: 851a96b4b736ebc73d76d2129204f0ec957dfd35 [file] [log] [blame]
/*
* 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 org.apache.calcite.rel.metadata;
import org.apache.calcite.rel.RelNode;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import static java.util.Objects.requireNonNull;
/**
* Base class for the RelMetadataQuery that uses the metadata handler class
* generated by the Janino.
*
* <p>To add a new implementation to this interface, follow
* these steps:
*
* <ol>
* <li>Extends {@link RelMetadataQuery} (name it MyRelMetadataQuery for example)
* to reuse the Calcite builtin metadata query interfaces. In this class, define all the
* extended Handlers for your metadata and implement the metadata query interfaces.
* <li>Write your customized provider class <code>RelMdXyz</code>. Follow
* the pattern from an existing class such as {@link RelMdColumnOrigins},
* overloading on all of the logical relational expressions to which the query
* applies.
* <li>Add a {@code SOURCE} static member to each of your provider class, similar to
* {@link RelMdColumnOrigins#SOURCE}.
* <li>Extends {@link DefaultRelMetadataProvider} (name it MyRelMetadataProvider for example)
* and supplement the "SOURCE"s into the builtin list
* (This is not required, use {@link ChainedRelMetadataProvider} to chain your customized
* "SOURCE"s with default ones also works).
* <li>Set {@code MyRelMetadataProvider} into the cluster instance.
* <li>Use
* {@link org.apache.calcite.plan.RelOptCluster#setMetadataQuerySupplier(Supplier)}
* to set the metadata query {@link Supplier} into the cluster instance. This {@link Supplier}
* should return a <strong>fresh new</strong> instance.
* <li>Use the cluster instance to create
* {@link org.apache.calcite.sql2rel.SqlToRelConverter}.</li>
* <li>Query your metadata within {@link org.apache.calcite.plan.RelOptRuleCall} with the
* interfaces you defined in {@code MyRelMetadataQuery}.
* </ol>
*/
public class RelMetadataQueryBase {
//~ Instance fields --------------------------------------------------------
/** Set of active metadata queries, and cache of previous results. */
public final Table<RelNode, List, Object> map = HashBasedTable.create();
public final @Nullable JaninoRelMetadataProvider metadataProvider;
//~ Static fields/initializers ---------------------------------------------
public static final ThreadLocal<@Nullable JaninoRelMetadataProvider> THREAD_PROVIDERS =
new ThreadLocal<>();
//~ Constructors -----------------------------------------------------------
protected RelMetadataQueryBase(@Nullable JaninoRelMetadataProvider metadataProvider) {
this.metadataProvider = metadataProvider;
}
protected static <H> H initialHandler(Class<H> 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 JaninoRelMetadataProvider.NoHandler(r.getClass());
}));
}
//~ Methods ----------------------------------------------------------------
/** Re-generates the handler for a given kind of metadata, adding support for
* {@code class_} if it is not already present. */
protected <M extends Metadata, H extends MetadataHandler<M>> H
revise(Class<? extends RelNode> class_, MetadataDef<M> def) {
requireNonNull(metadataProvider, "metadataProvider");
return metadataProvider.revise(class_, def);
}
/**
* Removes cached metadata values for specified RelNode.
*
* @param rel RelNode whose cached metadata should be removed
* @return true if cache for the provided RelNode was not empty
*/
public boolean clearCache(RelNode rel) {
Map<List, Object> row = map.row(rel);
if (row.isEmpty()) {
return false;
}
row.clear();
return true;
}
}