OmniSciDB  72c90bc290
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
HeavyDBRelWriterImpl.java
Go to the documentation of this file.
1 /*
2  * Copyright 2023 HEAVY.AI, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.apache.calcite.rel.externalize;
18 
19 import com.google.common.collect.ImmutableList;
20 
21 import org.apache.calcite.avatica.util.Spacer;
22 import org.apache.calcite.linq4j.Ord;
23 import org.apache.calcite.plan.RelOptTable;
24 import org.apache.calcite.rel.RelNode;
25 import org.apache.calcite.rel.RelWriter;
26 import org.apache.calcite.rel.core.AggregateCall;
27 import org.apache.calcite.rel.logical.LogicalAggregate;
28 import org.apache.calcite.rel.metadata.RelColumnOrigin;
29 import org.apache.calcite.rel.metadata.RelMetadataQuery;
30 import org.apache.calcite.rex.RexInputRef;
31 import org.apache.calcite.rex.RexNode;
32 import org.apache.calcite.rex.RexShuttle;
33 import org.apache.calcite.sql.SqlExplainLevel;
34 import org.apache.calcite.util.Pair;
35 import org.checkerframework.checker.nullness.qual.Nullable;
36 
37 import java.io.PrintWriter;
38 import java.util.*;
39 
43 public class HeavyDBRelWriterImpl implements RelWriter {
44  //~ Instance fields --------------------------------------------------------
45 
46  protected final PrintWriter pw;
47  protected final SqlExplainLevel detailLevel;
48  protected final boolean withIdPrefix;
49  protected final Spacer spacer = new Spacer();
50  private final List<Pair<String, @Nullable Object>> values = new ArrayList<>();
51 
52  //~ Constructors -----------------------------------------------------------
53 
54  public HeavyDBRelWriterImpl(PrintWriter pw) {
55  this(pw, SqlExplainLevel.EXPPLAN_ATTRIBUTES, true);
56  }
57 
59  PrintWriter pw, SqlExplainLevel detailLevel, boolean withIdPrefix) {
60  this.pw = pw;
61  this.detailLevel = detailLevel;
62  this.withIdPrefix = withIdPrefix;
63  }
64 
65  //~ Methods ----------------------------------------------------------------
66 
67  public Set<String> collectRelColumnOrigin(
68  RelMetadataQuery mq, RelNode rel, int targetColumnIndex) {
69  Set<RelColumnOrigin> columnOrigins = new HashSet<>();
70  Map<String, Set<String>> originInfoMap = new HashMap<>();
71  Set<String> originInfo = new HashSet<>();
72  try {
73  if (!rel.getInputs().isEmpty()) {
74  RelNode sourceNode = rel.getInputs().size() == 1 ? rel.getInput(0) : rel;
75  columnOrigins = mq.getColumnOrigins(sourceNode, targetColumnIndex);
76  }
77  for (RelColumnOrigin rco : columnOrigins) {
78  RelOptTable relOptTable = rco.getOriginTable();
79  String dbName = relOptTable.getQualifiedName().get(0);
80  String tableName = relOptTable.getQualifiedName().get(1);
81  String colName = relOptTable.getRowType()
82  .getFieldList()
83  .get(rco.getOriginColumnOrdinal())
84  .getName();
85  String key = "$" + targetColumnIndex;
86  String info = "db:" + dbName + ",tableName:" + tableName + ",colName:" + colName;
87  if (originInfoMap.containsKey(key)) {
88  originInfoMap.get(key).add(info);
89  } else {
90  Set<String> originList = new HashSet<>();
91  originList.add(info);
92  originInfoMap.put(key, originList);
93  }
94  }
95  } catch (Exception ex) {
96  throw new RuntimeException("EXPLAIN CALCITE DETAILED error: " + ex.getMessage());
97  }
98 
99  for (Map.Entry<String, Set<String>> entry : originInfoMap.entrySet()) {
100  int index = 1;
101  for (String colInfo : entry.getValue()) {
102  if (entry.getValue().size() > 1) {
103  originInfo.add(entry.getKey() + "-" + index + "->" + colInfo);
104  } else {
105  originInfo.add(entry.getKey() + "->" + colInfo);
106  }
107  index++;
108  }
109  }
110  return originInfo;
111  }
112 
113  protected void explain_(RelNode rel, List<Pair<String, @Nullable Object>> values) {
114  List<RelNode> inputs = rel.getInputs();
115  final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
116  if (!mq.isVisibleInExplain(rel, detailLevel)) {
117  // render children in place of this, at same level
118  explainInputs(inputs);
119  return;
120  }
121 
122  StringBuilder s = new StringBuilder();
123  spacer.spaces(s);
124  if (withIdPrefix) {
125  s.append(rel.getId()).append(":");
126  }
127  s.append(rel.getRelTypeName());
128  if (detailLevel != SqlExplainLevel.NO_ATTRIBUTES) {
129  int j = 0;
130  for (Pair<String, @Nullable Object> value : values) {
131  if (value.right instanceof RelNode) {
132  continue;
133  }
134  if (j++ == 0) {
135  s.append("(");
136  } else {
137  s.append(", ");
138  }
139  s.append(value.left).append("=[").append(value.right).append("]");
140  }
141  if (j > 0) {
142  s.append(")");
143  }
144  }
145  switch (detailLevel) {
146  case ALL_ATTRIBUTES:
147  s.append(": rowcount = ")
148  .append(mq.getRowCount(rel))
149  .append(", cumulative cost = ")
150  .append(mq.getCumulativeCost(rel));
151  break;
152  default:
153  break;
154  }
155  switch (detailLevel) {
156  case NON_COST_ATTRIBUTES:
157  case ALL_ATTRIBUTES:
158  if (!withIdPrefix) {
159  // If we didn't print the rel id at the start of the line, print
160  // it at the end.
161  s.append(", id = ").append(rel.getId());
162  }
163  break;
164  default:
165  break;
166  }
167  Set<String> originInfo = new HashSet<>();
168  if (rel instanceof LogicalAggregate) {
169  LogicalAggregate aggregate = (LogicalAggregate) rel;
170  for (AggregateCall aggCall : aggregate.getAggCallList()) {
171  for (int expressionIndex : aggCall.getArgList()) {
172  originInfo.addAll(collectRelColumnOrigin(mq, rel, expressionIndex));
173  }
174  }
175  } else {
176  rel.accept(new RexShuttle() {
177  @Override
178  public RexNode visitInputRef(final RexInputRef inputRef) {
179  originInfo.addAll(collectRelColumnOrigin(mq, rel, inputRef.getIndex()));
180  return inputRef;
181  }
182  });
183  }
184  if (!originInfo.isEmpty()) {
185  s.append("\t{");
186  int index = 1;
187  for (String info : originInfo) {
188  s.append("[").append(info).append("]");
189  if (index < originInfo.size()) {
190  s.append(", ");
191  }
192  index++;
193  }
194  s.append("}");
195  }
196  pw.println(s);
197  spacer.add(2);
198  explainInputs(inputs);
199  spacer.subtract(2);
200  }
201 
202  private void explainInputs(List<RelNode> inputs) {
203  for (RelNode input : inputs) {
204  input.explain(this);
205  }
206  }
207 
208  @Override
209  public final void explain(RelNode rel, List<Pair<String, @Nullable Object>> valueList) {
210  explain_(rel, valueList);
211  }
212 
213  @Override
214  public SqlExplainLevel getDetailLevel() {
215  return detailLevel;
216  }
217 
218  @Override
219  public RelWriter item(String term, @Nullable Object value) {
220  values.add(Pair.of(term, value));
221  return this;
222  }
223 
224  @Override
225  public RelWriter done(RelNode node) {
226  assert checkInputsPresentInExplain(node);
227  final List<Pair<String, @Nullable Object>> valuesCopy = ImmutableList.copyOf(values);
228  values.clear();
229  explain_(node, valuesCopy);
230  pw.flush();
231  return this;
232  }
233 
234  private boolean checkInputsPresentInExplain(RelNode node) {
235  int i = 0;
236  if (values.size() > 0 && values.get(0).left.equals("subset")) {
237  ++i;
238  }
239  for (RelNode input : node.getInputs()) {
240  assert values.get(i).right == input;
241  ++i;
242  }
243  return true;
244  }
245 
250  public String simple() {
251  final StringBuilder buf = new StringBuilder("(");
252  for (Ord<Pair<String, @Nullable Object>> ord : Ord.zip(values)) {
253  if (ord.i > 0) {
254  buf.append(", ");
255  }
256  buf.append(ord.e.left).append("=[").append(ord.e.right).append("]");
257  }
258  buf.append(")");
259  return buf.toString();
260  }
261 }
size_t append(FILE *f, const size_t size, const int8_t *buf)
Appends the specified number of bytes to the end of the file f from buf.
Definition: File.cpp:158
Set< String > collectRelColumnOrigin(RelMetadataQuery mq, RelNode rel, int targetColumnIndex)
final void explain(RelNode rel, List< Pair< String,@Nullable Object >> valueList)
final List< Pair< String,@Nullable Object > > values
RelWriter item(String term,@Nullable Object value)
void explain_(RelNode rel, List< Pair< String,@Nullable Object >> values)
HeavyDBRelWriterImpl(PrintWriter pw, SqlExplainLevel detailLevel, boolean withIdPrefix)