/* 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.plannodes; import java.util.Set; import org.json_voltpatches.JSONException; import org.json_voltpatches.JSONObject; import org.json_voltpatches.JSONStringer; import org.voltdb.catalog.Database; import org.voltdb.compiler.DatabaseEstimates; import org.voltdb.compiler.ScalarValueHints; import org.voltdb.expressions.AbstractExpression; import org.voltdb.expressions.ParameterValueExpression; import org.voltdb.expressions.TupleValueExpression; import org.voltdb.expressions.VectorValueExpression; import org.voltdb.types.PlanNodeType; import org.voltdb.types.SortDirectionType; /** * Used for SQL-IN that are accelerated with indexes. * A MaterializedScanPlanNode is created from the list part * of the SQL-IN-LIST. It is inner-joined with NLIJ to another * table to make the SQL-IN fast. * */ public class MaterializedScanPlanNode extends AbstractPlanNode { private AbstractExpression m_tableData; private final TupleValueExpression m_outputExpression = new TupleValueExpression( "materialized_temp_table", "materialized_temp_table", "list_element", null, 0); private SortDirectionType m_sortDirection = SortDirectionType.INVALID; public enum Members { TABLE_DATA, SORT_DIRECTION; } public MaterializedScanPlanNode() { super(); } @Override public PlanNodeType getPlanNodeType() { return PlanNodeType.MATERIALIZEDSCAN; } public void setRowData(AbstractExpression tableData) { assert(tableData instanceof VectorValueExpression || tableData instanceof ParameterValueExpression); m_tableData = tableData; m_outputExpression.setTypeSizeAndInBytes(m_tableData); } public void setSortDirection(SortDirectionType direction) { m_sortDirection = direction; } public SortDirectionType getSortDirection() { return m_sortDirection; } // Extract a TVE for the single column of a MaterializedScan for use as a join key for an IndexScan public AbstractExpression getOutputExpression() { return m_outputExpression; } /** * Accessor for flag marking the plan as guaranteeing an identical result/effect * when "replayed" against the same database state, such as during replication or CL recovery. * @return true */ @Override public boolean isOrderDeterministic() { return true; } @Override public void computeCostEstimates(long childOutputTupleCountEstimate, DatabaseEstimates estimates, ScalarValueHints[] paramHints) { // assume constant cost. Most of the cost of the SQL-IN will be measured by the NLIJ that is always paired with this element m_estimatedProcessedTupleCount = 1; m_estimatedOutputTupleCount = 1; } @Override protected String explainPlanForNode(String indent) { return "MATERIALIZED SCAN of SQL-IN-LIST (Sort " + m_sortDirection.toString() + ")"; } @Override public void generateOutputSchema(Database db) { assert(m_children.size() == 0); m_hasSignificantOutputSchema = true; // fill in the table schema if we haven't already if (m_outputSchema == null) { m_outputSchema = new NodeSchema(); // must produce a tuple value expression for the one column. m_outputSchema.addColumn( m_outputExpression.getTableName(), m_outputExpression.getTableAlias(), m_outputExpression.getColumnName(), m_outputExpression.getColumnAlias(), m_outputExpression); } } @Override public void resolveColumnIndexes() { // MaterializedScanPlanNodes have no children assert(m_children.size() == 0); } @Override public void toJSONString(JSONStringer stringer) throws JSONException { super.toJSONString(stringer); stringer.key(Members.TABLE_DATA.name()); stringer.object(); assert(m_tableData != null); m_tableData.toJSONString(stringer); stringer.endObject(); if (m_sortDirection == SortDirectionType.DESC) { stringer.keySymbolValuePair(Members.SORT_DIRECTION.name(), m_sortDirection.toString()); } } @Override protected void loadFromJSONObject(JSONObject obj, Database db) throws JSONException { helpLoadFromJSONObject(obj, db); assert(!obj.isNull(Members.TABLE_DATA.name())); m_tableData = AbstractExpression.fromJSONChild(obj, Members.TABLE_DATA.name()); if (!obj.isNull(Members.SORT_DIRECTION.name())) { m_sortDirection = SortDirectionType.get(obj.getString( Members.SORT_DIRECTION.name())); } } @Override public void findAllExpressionsOfClass(Class< ? extends AbstractExpression> aeClass, Set<AbstractExpression> collected) { super.findAllExpressionsOfClass(aeClass, collected); collected.addAll(m_tableData.findAllSubexpressionsOfClass(aeClass)); } }