/* This file is part of VoltDB. * Copyright (C) 2008-2010 VoltDB L.L.C. * * VoltDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VoltDB 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.plannodes; import java.util.ArrayList; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONStringer; import org.voltdb.catalog.Database; import org.voltdb.planner.PlanColumn; import org.voltdb.planner.PlannerContext; import org.voltdb.types.ExpressionType; import org.voltdb.types.PlanNodeType; /** * */ public class AggregatePlanNode extends AbstractPlanNode { public enum Members { AGGREGATE_COLUMNS, AGGREGATE_TYPE, AGGREGATE_NAME, AGGREGATE_GUID, AGGREGATE_OUTPUT_COLUMN, GROUPBY_COLUMNS; } // NOTE: I'm not really keen on how this is all laid out, but it's just // good enough for what we need in TPC-C for now... private List<ExpressionType> m_aggregateTypes = new ArrayList<ExpressionType>(); // a list of column offsets/indexes not plan column guids. private List<Integer> m_aggregateOutputColumns = new ArrayList<Integer>(); // a list of the names of the columns that are being aggregated private List<String> m_aggregateColumnNames = new ArrayList<String>(); // a list of the GUIDs for the columns that are being aggregated private List<Integer> m_aggregateColumnGuids = new ArrayList<Integer>(); private List<Integer> m_groupByColumns = new ArrayList<Integer>(); private List<Integer> m_groupByColumnGuids = new ArrayList<Integer>(); private List<String> m_groupByColumnNames = new ArrayList<String>(); public AggregatePlanNode(PlannerContext context, Integer id) { super(context, id); } @Override public Object clone(boolean clone_children, boolean clone_inline) throws CloneNotSupportedException { AggregatePlanNode clone = (AggregatePlanNode)super.clone(clone_children, clone_inline); clone.m_aggregateTypes = new ArrayList<ExpressionType>(this.m_aggregateTypes); clone.m_aggregateOutputColumns = new ArrayList<Integer>(this.m_aggregateOutputColumns); clone.m_aggregateColumnNames = new ArrayList<String>(this.m_aggregateColumnNames); clone.m_aggregateColumnGuids = new ArrayList<Integer>(this.m_aggregateColumnGuids); clone.m_groupByColumns = new ArrayList<Integer>(this.m_groupByColumns); clone.m_groupByColumnGuids = new ArrayList<Integer>(this.m_groupByColumnGuids); clone.m_groupByColumnNames = new ArrayList<String>(this.m_groupByColumnNames); return (clone); } @Override public PlanNodeType getPlanNodeType() { return PlanNodeType.AGGREGATE; } @Override public void validate() throws Exception { super.validate(); // // We need to have an aggregate type and column // We're not checking that it's a valid ExpressionType because this plannode is a temporary hack // if (m_aggregateTypes.size() != m_aggregateColumnNames.size() || m_aggregateColumnNames.size() != m_aggregateOutputColumns.size()) { throw new Exception("ERROR: Mismatched number of aggregate expression column attributes for PlanNode '" + this + "'"); } else if (m_aggregateTypes.isEmpty()|| m_aggregateTypes.contains(ExpressionType.INVALID)) { throw new Exception("ERROR: Invalid Aggregate ExpressionType or No Aggregate Expression types for PlanNode '" + this + "'"); } else if (m_aggregateColumnNames.isEmpty()) { throw new Exception("ERROR: No Aggregate Columns for PlanNode '" + this + "'"); } } @SuppressWarnings("unchecked") @Override protected ArrayList<Integer> createOutputColumns(Database db, ArrayList<Integer> input) { // columns are created during plan node construction assert(m_outputColumns.size() > 0); return (ArrayList<Integer>)m_outputColumns.clone(); } public void appendOutputColumn(PlanColumn colInfo) { m_outputColumns.add(colInfo.guid()); } /** * @return The list of output column indexes that each aggregate outputs to */ public List<Integer> getAggregateOutputColumns() { return m_aggregateOutputColumns; } /** * @return The type of aggregation for each aggregate column */ public List<ExpressionType> getAggregateTypes() { return m_aggregateTypes; } /** * @param aggregate_types */ public void setAggregateTypes(List<ExpressionType> aggregate_types) { m_aggregateTypes = aggregate_types; } /** * @return the aggregate column GUIDs (aggregated input col GUID) */ public List<Integer> getAggregateColumnGuids() { return m_aggregateColumnGuids; } /** * @param aggregate_column_guids */ public void setAggregateColumnGuids(List<Integer> aggregate_column_guids) { m_aggregateColumnGuids = aggregate_column_guids; } /** * @return the aggregate_column_name */ public List<String> getAggregateColumnNames() { return m_aggregateColumnNames; } /** * @param aggregate_column_names */ public void setAggregateColumnNames(List<String> aggregate_column_names) { m_aggregateColumnNames = aggregate_column_names; } /** * @return Names of the input column that maps to the output column. */ public List<String> getOutputColumnInputAliasNames() { return m_aggregateColumnNames; } /** * @return the groupby_columns */ public List<Integer> getGroupByColumnOffsets() { return m_groupByColumns; } /** * GroupBy PlanColumn GUIDs * @return */ public List<Integer> getGroupByColumnGuids() { return m_groupByColumnGuids; } public void appendGroupByColumn(PlanColumn colInfo) { m_groupByColumnGuids.add(colInfo.guid()); } /** * @return the groupby_column_names */ public List<String> getGroupByColumnNames() { return m_groupByColumnNames; } /** * @param groupby_column_names */ public void setGroupByColumnNames(List<String> groupby_column_names) { m_groupByColumnNames = groupby_column_names; } @Override public void toJSONString(JSONStringer stringer) throws JSONException { super.toJSONString(stringer); stringer.key("AGGREGATE_COLUMNS"); stringer.array(); for (int ii = 0; ii < m_aggregateTypes.size(); ii++) { stringer.object(); stringer.key(Members.AGGREGATE_TYPE.name()).value(m_aggregateTypes.get(ii).name()); stringer.key(Members.AGGREGATE_NAME.name()).value(m_aggregateColumnNames.get(ii)); stringer.key(Members.AGGREGATE_GUID.name()).value(m_aggregateColumnGuids.get(ii)); stringer.key(Members.AGGREGATE_OUTPUT_COLUMN.name()).value(m_aggregateOutputColumns.get(ii)); stringer.endObject(); } stringer.endArray(); if (!m_groupByColumnGuids.isEmpty()) { stringer.key(Members.GROUPBY_COLUMNS.name()).array(); for (int i = 0; i < m_groupByColumnGuids.size(); i++) { PlanColumn column = m_context.get(m_groupByColumnGuids.get(i)); column.toJSONString(stringer); } stringer.endArray(); } } @Override protected void loadFromJSONObject(JSONObject obj, Database db) throws JSONException { JSONArray aggregateColumns = obj.getJSONArray(Members.AGGREGATE_COLUMNS.name()); for (int ii = 0; ii < aggregateColumns.length(); ii++) { JSONObject aggregateColumn = aggregateColumns.getJSONObject(ii); m_aggregateTypes.add(ExpressionType.valueOf(aggregateColumn.getString(Members.AGGREGATE_TYPE.name()))); m_aggregateColumnNames.add(aggregateColumn.getString(Members.AGGREGATE_NAME.name())); m_aggregateColumnGuids.add(aggregateColumn.getInt(Members.AGGREGATE_GUID.name())); m_aggregateOutputColumns.add(aggregateColumn.getInt(Members.AGGREGATE_OUTPUT_COLUMN.name())); } try { JSONArray groupbyColumnGuids = obj.getJSONArray(Members.GROUPBY_COLUMNS.name()); for (int ii = 0; ii < groupbyColumnGuids.length(); ii++) { JSONObject jsonObject = groupbyColumnGuids.getJSONObject(ii); PlanColumn column = PlanColumn.fromJSONObject(jsonObject, db); m_groupByColumnGuids.add(column.guid()); } } catch (JSONException e) { //okay not to be there. } } }