/* 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.JSONString; import org.json.JSONStringer; import org.voltdb.catalog.Cluster; import org.voltdb.catalog.Database; import org.voltdb.catalog.Table; import org.voltdb.compiler.DatabaseEstimates; import org.voltdb.compiler.ScalarValueHints; import org.voltdb.expressions.AbstractExpression; import org.voltdb.planner.PlanStatistics; import org.voltdb.planner.PlannerContext; import org.voltdb.planner.StatsField; import org.voltdb.types.IndexLookupType; import org.voltdb.types.PlanNodeType; import org.voltdb.types.SortDirectionType; /** * */ public class IndexScanPlanNode extends AbstractScanPlanNode { public enum Members { TARGET_INDEX_NAME, END_EXPRESSION, SEARCHKEY_EXPRESSIONS, KEY_ITERATE, LOOKUP_TYPE, SORT_DIRECTION; } /** * Attributes * NOTE: The IndexScanPlanNode will use AbstractScanPlanNode's m_predicate * as the "Post-Scan Predicate Expression". When this is defined, the EE will * run a tuple through an additional predicate to see whether it qualifies. * This is necessary when we have a predicate that includes columns that are not * all in the index that was selected. */ // The index to use in the scan operation private String m_targetIndexName; // When this expression evaluates to true, we will stop scanning private AbstractExpression m_endExpression; // This list of expressions corresponds to the values that we will use // at runtime in the lookup on the index private List<AbstractExpression> m_searchkeyExpressions = new ArrayList<AbstractExpression>(); // ??? private Boolean m_keyIterate = false; // The overall index lookup operation type private IndexLookupType m_lookupType = IndexLookupType.EQ; // The sorting direction private SortDirectionType m_sortDirection = SortDirectionType.INVALID; /** * @param id */ public IndexScanPlanNode(PlannerContext context, Integer id) { super(context, id); } @Override public Object clone(boolean clone_children, boolean clone_inline) throws CloneNotSupportedException { IndexScanPlanNode clone = (IndexScanPlanNode)super.clone(clone_children, clone_inline); clone.m_searchkeyExpressions = new ArrayList<AbstractExpression>(); clone.m_endExpression = (AbstractExpression)this.m_endExpression.clone(); for (AbstractExpression exp : this.m_searchkeyExpressions) { AbstractExpression clone_exp = (AbstractExpression)exp.clone(); clone.m_searchkeyExpressions.add(clone_exp); } return (clone); } @Override public PlanNodeType getPlanNodeType() { return PlanNodeType.INDEXSCAN; } @Override public void validate() throws Exception { super.validate(); // There needs to be at least one search key expression if (m_searchkeyExpressions.isEmpty()) { throw new Exception("ERROR: There were no search key expressions defined for " + this); } // Validate Expression Trees if (m_endExpression != null) { m_endExpression.validate(); } for (AbstractExpression exp : m_searchkeyExpressions) { exp.validate(); } } /** * * @param keyIterate */ public void setKeyIterate(Boolean keyIterate) { m_keyIterate = keyIterate; } /** * * @return Does this scan iterate over values in the index. */ public Boolean getKeyIterate() { return m_keyIterate; } /** * * @return The type of this lookup. */ public IndexLookupType getLookupType() { return m_lookupType; } /** * @return The sorting direction. */ public SortDirectionType getSortDirection() { return m_sortDirection; } /** * * @param lookupType */ public void setLookupType(IndexLookupType lookupType) { m_lookupType = lookupType; } /** * @param sortDirection * the sorting direction */ public void setSortDirection(SortDirectionType sortDirection) { m_sortDirection = sortDirection; } /** * @return the target_index_name */ public String getTargetIndexName() { return m_targetIndexName; } /** * @param targetIndexName the target_index_name to set */ public void setTargetIndexName(String targetIndexName) { m_targetIndexName = targetIndexName; } /** * @return the post_predicate */ public AbstractExpression getEndExpression() { return m_endExpression; } /** * @param endExpression the end expression to set */ public void setEndExpression(AbstractExpression endExpression) { m_endExpression = endExpression; } /** * @return the searchkey_expressions */ public List<AbstractExpression> getSearchKeyExpressions() { return m_searchkeyExpressions; } @Override public boolean computeEstimatesRecursively(PlanStatistics stats, Cluster cluster, Database db, DatabaseEstimates estimates, ScalarValueHints[] paramHints) { Table target = db.getTables().getIgnoreCase(m_targetTableName); assert(target != null); DatabaseEstimates.TableEstimates tableEstimates = estimates.getEstimatesForTable(target.getTypeName()); stats.incrementStatistic(0, StatsField.TREE_INDEX_LEVELS_TRAVERSED, (long)(Math.log(tableEstimates.maxTuples))); stats.incrementStatistic(0, StatsField.TUPLES_READ, 1); m_estimatedOutputTupleCount = 1; return true; } @Override public void toJSONString(JSONStringer stringer) throws JSONException { super.toJSONString(stringer); stringer.key(Members.KEY_ITERATE.name()).value(m_keyIterate); stringer.key(Members.LOOKUP_TYPE.name()).value(m_lookupType.toString()); stringer.key(Members.SORT_DIRECTION.name()).value(m_sortDirection.toString()); stringer.key(Members.TARGET_INDEX_NAME.name()).value(m_targetIndexName); stringer.key(Members.END_EXPRESSION.name()); stringer.value(m_endExpression); stringer.key(Members.SEARCHKEY_EXPRESSIONS.name()).array(); for (AbstractExpression ae : m_searchkeyExpressions) { assert (ae instanceof JSONString); stringer.value(ae); } stringer.endArray(); } @Override protected void loadFromJSONObject(JSONObject obj, Database db) throws JSONException { super.loadFromJSONObject(obj, db); m_keyIterate = obj.getBoolean(Members.KEY_ITERATE.name()); m_lookupType = IndexLookupType.valueOf(obj.getString(Members.LOOKUP_TYPE.name())); m_targetIndexName = obj.getString(Members.TARGET_INDEX_NAME.name()); JSONObject endExpressionObject = null; if (!obj.isNull(Members.END_EXPRESSION.name())) { try { endExpressionObject = obj.getJSONObject(Members.END_EXPRESSION.name()); } catch (JSONException e) { //okay for it not to be there } } if (endExpressionObject != null) { m_endExpression = AbstractExpression.fromJSONObject(endExpressionObject, db); } JSONArray searchkeyExpressions = obj.getJSONArray(Members.SEARCHKEY_EXPRESSIONS.name()); for (int ii = 0; ii < searchkeyExpressions.length(); ii++) { JSONObject searchkeyExpressionObj = searchkeyExpressions.getJSONObject(ii); m_searchkeyExpressions.add(AbstractExpression.fromJSONObject(searchkeyExpressionObj, db)); } } }