/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.table;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import org.h2.command.dml.AllColumnsForPlan;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.message.Trace;
import org.h2.table.TableFilter.TableFilterVisitor;

A possible query execution plan. The time required to execute a query depends on the order the tables are accessed.
/** * A possible query execution plan. The time required to execute a query depends * on the order the tables are accessed. */
public class Plan { private final TableFilter[] filters; private final HashMap<TableFilter, PlanItem> planItems = new HashMap<>(); private final Expression[] allConditions; private final TableFilter[] allFilters;
Create a query plan with the given order.
Params:
  • filters – the tables of the query
  • count – the number of table items
  • condition – the condition in the WHERE clause
/** * Create a query plan with the given order. * * @param filters the tables of the query * @param count the number of table items * @param condition the condition in the WHERE clause */
public Plan(TableFilter[] filters, int count, Expression condition) { this.filters = new TableFilter[count]; System.arraycopy(filters, 0, this.filters, 0, count); final ArrayList<Expression> allCond = new ArrayList<>(); final ArrayList<TableFilter> all = new ArrayList<>(); if (condition != null) { allCond.add(condition); } for (int i = 0; i < count; i++) { TableFilter f = filters[i]; f.visit(new TableFilterVisitor() { @Override public void accept(TableFilter f) { all.add(f); if (f.getJoinCondition() != null) { allCond.add(f.getJoinCondition()); } } }); } allConditions = allCond.toArray(new Expression[0]); allFilters = all.toArray(new TableFilter[0]); }
Get the plan item for the given table.
Params:
  • filter – the table
Returns:the plan item
/** * Get the plan item for the given table. * * @param filter the table * @return the plan item */
public PlanItem getItem(TableFilter filter) { return planItems.get(filter); }
The the list of tables.
Returns:the list of tables
/** * The the list of tables. * * @return the list of tables */
public TableFilter[] getFilters() { return filters; }
Remove all index conditions that can not be used.
/** * Remove all index conditions that can not be used. */
public void removeUnusableIndexConditions() { for (int i = 0; i < allFilters.length; i++) { TableFilter f = allFilters[i]; setEvaluatable(f, true); if (i < allFilters.length - 1 || f.getSession().getDatabase().getSettings().earlyFilter) { // the last table doesn't need the optimization, // otherwise the expression is calculated twice unnecessarily // (not that bad but not optimal) f.optimizeFullCondition(false); } f.removeUnusableIndexConditions(); } for (TableFilter f : allFilters) { setEvaluatable(f, false); } }
Calculate the cost of this query plan.
Params:
  • session – the session
  • allColumnsSet – calculates all columns on-demand
Returns:the cost
/** * Calculate the cost of this query plan. * * @param session the session * @param allColumnsSet calculates all columns on-demand * @return the cost */
public double calculateCost(Session session, AllColumnsForPlan allColumnsSet) { Trace t = session.getTrace(); if (t.isDebugEnabled()) { t.debug("Plan : calculate cost for plan {0}", Arrays.toString(allFilters)); } double cost = 1; boolean invalidPlan = false; for (int i = 0; i < allFilters.length; i++) { TableFilter tableFilter = allFilters[i]; if (t.isDebugEnabled()) { t.debug("Plan : for table filter {0}", tableFilter); } PlanItem item = tableFilter.getBestPlanItem(session, allFilters, i, allColumnsSet); planItems.put(tableFilter, item); if (t.isDebugEnabled()) { t.debug("Plan : best plan item cost {0} index {1}", item.cost, item.getIndex().getPlanSQL()); } cost += cost * item.cost; setEvaluatable(tableFilter, true); Expression on = tableFilter.getJoinCondition(); if (on != null) { if (!on.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR)) { invalidPlan = true; break; } } } if (invalidPlan) { cost = Double.POSITIVE_INFINITY; } if (t.isDebugEnabled()) { session.getTrace().debug("Plan : plan cost {0}", cost); } for (TableFilter f : allFilters) { setEvaluatable(f, false); } return cost; } private void setEvaluatable(TableFilter filter, boolean b) { filter.setEvaluatable(filter, b); for (Expression e : allConditions) { e.setEvaluatable(filter, b); } } }