/* * 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.materialize; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rex.RexNode; import org.apache.calcite.util.Util; import org.apache.calcite.util.graph.AttributedDirectedGraph; import org.apache.calcite.util.mapping.IntPair; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; /** Space within which lattices exist. */ class LatticeSpace { final SqlStatisticProvider statisticProvider; private final Map<List<String>, LatticeTable> tableMap = new HashMap<>(); final AttributedDirectedGraph<LatticeTable, Step> g = new AttributedDirectedGraph<>(new Step.Factory(this)); private final Map<List<String>, String> simpleTableNames = new HashMap<>(); private final Set<String> simpleNames = new HashSet<>(); /** Root nodes, indexed by digest. */ final Map<String, LatticeRootNode> nodeMap = new HashMap<>(); final Map<ImmutableList<Step>, Path> pathMap = new HashMap<>(); final Map<LatticeTable, List<RexNode>> tableExpressions = new HashMap<>(); LatticeSpace(SqlStatisticProvider statisticProvider) { this.statisticProvider = Objects.requireNonNull(statisticProvider); } /** Derives a unique name for a table, qualifying with schema name only if * necessary. */ String simpleName(LatticeTable table) { return simpleName(table.t.getQualifiedName()); } String simpleName(RelOptTable table) { return simpleName(table.getQualifiedName()); } String simpleName(List<String> table) { final String name = simpleTableNames.get(table); if (name != null) { return name; } final String name2 = Util.last(table); if (simpleNames.add(name2)) { simpleTableNames.put(ImmutableList.copyOf(table), name2); return name2; } final String name3 = table.toString(); simpleTableNames.put(ImmutableList.copyOf(table), name3); return name3; } LatticeTable register(RelOptTable t) { final LatticeTable table = tableMap.get(t.getQualifiedName()); if (table != null) { return table; } final LatticeTable table2 = new LatticeTable(t); tableMap.put(t.getQualifiedName(), table2); g.addVertex(table2); return table2; } Step addEdge(LatticeTable source, LatticeTable target, List<IntPair> keys) { keys = sortUnique(keys); final Step step = g.addEdge(source, target, keys); if (step != null) { return step; } for (Step step2 : g.getEdges(source, target)) { if (step2.keys.equals(keys)) { return step2; } } throw new AssertionError("addEdge failed, yet no edge present"); } /** Returns a list of {@link IntPair} that is sorted and unique. */ static List<IntPair> sortUnique(List<IntPair> keys) { if (keys.size() > 1) { // list may not be sorted; sort it keys = IntPair.ORDERING.immutableSortedCopy(keys); if (!IntPair.ORDERING.isStrictlyOrdered(keys)) { // list may contain duplicates; sort and eliminate duplicates final Set<IntPair> set = new TreeSet<>(IntPair.ORDERING); set.addAll(keys); keys = ImmutableList.copyOf(set); } } return keys; } /** Returns a list of {@link IntPair}, transposing source and target fields, * and ensuring the result is sorted and unique. */ static List<IntPair> swap(List<IntPair> keys) { return sortUnique(Lists.transform(keys, IntPair.SWAP)); } Path addPath(List<Step> steps) { final ImmutableList<Step> key = ImmutableList.copyOf(steps); final Path path = pathMap.get(key); if (path != null) { return path; } final Path path2 = new Path(key, pathMap.size()); pathMap.put(key, path2); return path2; } /** Registers an expression as a derived column of a given table. * * <p>Its ordinal is the number of fields in the row type plus the ordinal * of the extended expression. For example, if a table has 10 fields then its * derived columns will have ordinals 10, 11, 12 etc. */ int registerExpression(LatticeTable table, RexNode e) { final List<RexNode> expressions = tableExpressions.computeIfAbsent(table, t -> new ArrayList<>()); final int fieldCount = table.t.getRowType().getFieldCount(); for (int i = 0; i < expressions.size(); i++) { if (expressions.get(i).toString().equals(e.toString())) { return fieldCount + i; } } final int result = fieldCount + expressions.size(); expressions.add(e); return result; } /** Returns the name of field {@code field} of {@code table}. * * <p>If the field is derived (see * {@link #registerExpression(LatticeTable, RexNode)}) its name is its * {@link RexNode#toString()}. */ public String fieldName(LatticeTable table, int field) { final List<RelDataTypeField> fieldList = table.t.getRowType().getFieldList(); final int fieldCount = fieldList.size(); if (field < fieldCount) { return fieldList.get(field).getName(); } else { return tableExpressions.get(table).get(field - fieldCount).toString(); } } }