/* This file is part of VoltDB.
* Copyright (C) 2008-2017 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 <http://www.gnu.org/licenses/>.
*/
package org.voltdb.compiler;
import java.util.ArrayList;
import java.util.List;
import org.hsqldb_voltpatches.VoltXMLElement;
import org.voltdb.planner.ParsedColInfo;
/**
* Tune XMLs for materialized view min/max recalculation queries.
* ENG-8641
*/
public class MatViewFallbackQueryXMLGenerator {
private VoltXMLElement m_xml;
private int m_maxElementId;
private List<ParsedColInfo> m_groupByColumnsParsedInfo;
private List<ParsedColInfo> m_displayColumnsParsedInfo;
private ArrayList<VoltXMLElement> m_fallbackQueryXMLs;
private final boolean m_isMultiTableView;
public MatViewFallbackQueryXMLGenerator(VoltXMLElement xmlquery,
List<ParsedColInfo> groupByColumnsParsedInfo,
List<ParsedColInfo> displayColumnsParsedInfo,
boolean isMultiTableView) {
m_xml = xmlquery.duplicate();
m_groupByColumnsParsedInfo = groupByColumnsParsedInfo;
m_displayColumnsParsedInfo = displayColumnsParsedInfo;
m_isMultiTableView = isMultiTableView;
m_maxElementId = VoltXMLElementHelper.findMaxElementId( m_xml );
m_fallbackQueryXMLs = new ArrayList<>();
generateFallbackQueryXMLs();
}
private String nextElementId() {
++m_maxElementId;
return String.valueOf( m_maxElementId );
}
private String lastElementId() {
return String.valueOf( m_maxElementId );
}
private void generateFallbackQueryXMLs() {
/*********************************************************************************************************
* This function will turn the XML for materialized view definitions like:
* SELECT d1, d2, COUNT(*), MIN(abs(v1)) AS vmin, MAX(abs(v1)) AS vmax FROM ENG6511 GROUP BY d1, d2;
* into fallback query XMLs like:
* SELECT min(v1) FROM ENG6511 WHERE d1=? AND d2=? AND abs(v1)>=?;
* SELECT max(v1) FROM ENG6511 WHERE d1=? AND d2=? AND abs(v1)<=?;
********************************************************************************************************/
List<VoltXMLElement> columns = VoltXMLElementHelper.getFirstChild(m_xml, "columns").children;
List<VoltXMLElement> parameters = VoltXMLElementHelper.getFirstChild(m_xml, "parameters").children;
VoltXMLElement groupcolumnsElement = VoltXMLElementHelper.getFirstChild(m_xml, "groupcolumns");
List<VoltXMLElement> tablescans = VoltXMLElementHelper.getFirstChild(m_xml, "tablescans").children;
VoltXMLElement tablescanForJoinCond = tablescans.get(tablescans.size() - 1);
// Add the joincond to the last table scan element.
List<VoltXMLElement> joincondList = VoltXMLElementHelper.getFirstChild(tablescanForJoinCond, "joincond", true).children;
VoltXMLElement joincond = joincondList.size() == 0 ? null : joincondList.get(0);
joincondList.clear();
// 1. Turn groupby into joincond (WHERE) ================================================================
if (groupcolumnsElement != null) {
// If there is no group by clause, then nothing needs to be transformed.
List<VoltXMLElement> groupcolumns = groupcolumnsElement.children;
for (int i=0; i<m_groupByColumnsParsedInfo.size(); ++i) {
String index = String.valueOf(i);
String valueType = m_groupByColumnsParsedInfo.get(i).expression.getValueType().getName();
VoltXMLElement column = groupcolumns.get(i);
// Add the column to the parameter list.
parameters.add( VoltXMLElementHelper.buildParamElement( nextElementId(), index, valueType ) );
// Put together the where conditions for the groupby columns.
//
// Note that due to ENG-11080 we need to use NOT DISTINCT for
// multi-table views, due to the possibility of GB columns being NULL.
// For single-table views, we can catch NULL GB columns at runtime and
// fall back to a manual scan.
String comparisonOp = m_isMultiTableView ? "notdistinct" : "equal";
VoltXMLElement columnParamJoincond = VoltXMLElementHelper.buildColumnParamJoincondElement(comparisonOp, column, lastElementId(), nextElementId() );
joincond = VoltXMLElementHelper.mergeTwoElementsUsingOperator( "and", nextElementId(), joincond, columnParamJoincond );
}
// Remove the group by columns, they are now in the form of where conditions.
m_xml.children.remove(groupcolumnsElement);
}
// 2. Process aggregation columns =====================================================================
List<VoltXMLElement> originalColumns = new ArrayList<>();
originalColumns.addAll(columns);
columns.clear();
// Parameter index for min/max column
String paramIndex = String.valueOf(m_groupByColumnsParsedInfo.size());
// Add one min/max columns at a time as a new fallback query XML.
for (int i=m_groupByColumnsParsedInfo.size()+1; i<m_displayColumnsParsedInfo.size(); ++i) {
VoltXMLElement column = originalColumns.get(i);
String optype = column.attributes.get("optype");
if ( optype.equals("min") || optype.equals("max") ) {
columns.add(column);
VoltXMLElement aggArg = column.children.get(0);
String operator = optype.equals("min") ? "greaterthanorequalto" : "lessthanorequalto";
String valueType = m_displayColumnsParsedInfo.get(i).expression.getValueType().getName();
parameters.add( VoltXMLElementHelper.buildParamElement(nextElementId(), paramIndex, valueType) );
VoltXMLElement aggArgJoincond = VoltXMLElementHelper.buildColumnParamJoincondElement(operator, aggArg, lastElementId(), nextElementId());
joincondList.add( VoltXMLElementHelper.mergeTwoElementsUsingOperator("and", nextElementId(), joincond, aggArgJoincond) );
m_fallbackQueryXMLs.add(m_xml.duplicate());
// For debug:
// System.out.println(m_xml.toString());
columns.clear();
joincondList.clear();
parameters.remove(m_groupByColumnsParsedInfo.size());
}
}
}
public List<VoltXMLElement> getFallbackQueryXMLs() {
return m_fallbackQueryXMLs;
}
}