org.voltdb.compilereport.ViewExplainer Maven / Gradle / Ivy
/* This file is part of VoltDB.
* Copyright (C) 2008-2020 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see .
*/
package org.voltdb.compilereport;
import java.util.ArrayList;
import java.util.List;
import org.voltdb.catalog.CatalogMap;
import org.voltdb.catalog.Column;
import org.voltdb.catalog.IndexRef;
import org.voltdb.catalog.MaterializedViewHandlerInfo;
import org.voltdb.catalog.MaterializedViewInfo;
import org.voltdb.catalog.Statement;
import org.voltdb.catalog.Table;
import org.voltdb.types.ExpressionType;
import org.voltdb.utils.CatalogUtil;
import org.voltdb.utils.Encoder;
// Generate a report that can explain to the users how we maintain a view.
public class ViewExplainer {
private static String getIndexNameUsedInStatement(Statement stmt) {
// Get the name of the index used in a statement (query plan)
// that is used for refreshing a min/max column in a single-table view.
String[] indicesUsedInStatement = stmt.getIndexesused().split(",");
assert(indicesUsedInStatement.length <= 1);
for (String tableDotIndexPair : indicesUsedInStatement) {
if (tableDotIndexPair.length() == 0) {
continue;
}
String parts[] = tableDotIndexPair.split("\\.", 2);
assert(parts.length == 2);
if (parts.length != 2) {
continue;
}
return parts[1];
}
return "";
}
public static ArrayList explain(Table viewTable) throws Exception {
String viewName = viewTable.getTypeName();
MaterializedViewHandlerInfo mvHandlerInfo = viewTable.getMvhandlerinfo().get("mvHandlerInfo");
MaterializedViewInfo mvInfo = null;
CatalogMap fallBackQueryStmts;
List destColumnArray = CatalogUtil.getSortedCatalogItems(viewTable.getColumns(), "index");
ArrayList retval = new ArrayList();
// Is this view single-table?
if (mvHandlerInfo == null) {
// If this is not a multi-table view, we need to go to its source table for metadata.
// (Legacy code for single table view uses a different model and code path)
if (viewTable.getMaterializer() == null) {
// If we cannot find view metadata from both locations, this table is not a materialized view.
throw new Exception("Table " + viewName + " is not a view.");
}
mvInfo = viewTable.getMaterializer().getViews().get(viewName);
fallBackQueryStmts = mvInfo.getFallbackquerystmts();
}
else {
// For multi-table views we need to show the query plan for evaluating joins.
Statement createQuery = mvHandlerInfo.getCreatequery().get("createQuery");
retval.add(new String[]
{"Join Evaluation", Encoder.hexDecodeToString(createQuery.getExplainplan())});
fallBackQueryStmts = mvHandlerInfo.getFallbackquerystmts();
}
// For each min/max column find out if an execution plan is used.
int minMaxAggIdx = 0;
for (int j = 0; j < destColumnArray.size(); j++) {
Column destColumn = destColumnArray.get(j);
ExpressionType aggType = ExpressionType.get(destColumn.getAggregatetype());
if (aggType == ExpressionType.AGGREGATE_MIN || aggType == ExpressionType.AGGREGATE_MAX) {
Statement fallBackQueryStmt = fallBackQueryStmts.get(String.valueOf(minMaxAggIdx));
// How this min/max will be refreshed?
String plan = "";
// For single-table views, check if we uses:
// * built-in sequential scan
// * built-in index scan
// * execution plan
if (mvHandlerInfo == null) {
CatalogMap hardCodedIndicesForSingleTableView = mvInfo.getIndexforminmax();
String hardCodedIndexName = hardCodedIndicesForSingleTableView.get(String.valueOf(minMaxAggIdx)).getName();
String indexNameUsedInStatement = getIndexNameUsedInStatement(fallBackQueryStmt);
if (! indexNameUsedInStatement.equalsIgnoreCase(hardCodedIndexName)) {
plan = Encoder.hexDecodeToString(fallBackQueryStmt.getExplainplan());
}
// If we do not use execution plan, see which built-in method is used.
if (plan.equals("")) {
if (hardCodedIndexName.equals("")) {
plan = "Built-in sequential scan.";
}
else {
plan = "Built-in index scan \"" + hardCodedIndexName + "\".";
}
}
}
else {
plan = Encoder.hexDecodeToString(fallBackQueryStmt.getExplainplan());
}
retval.add(new String[]
{"Refresh " + (aggType == ExpressionType.AGGREGATE_MIN ? "MIN" : "MAX") + " column \"" +
destColumn.getName() + "\"", plan});
minMaxAggIdx++;
}
}
return retval;
}
}