Package org.voltdb.planner

Source Code of org.voltdb.planner.PlanSelector

/* This file is part of VoltDB.
* Copyright (C) 2008-2014 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.planner;

import java.io.File;

import org.hsqldb_voltpatches.VoltXMLElement;
import org.json_voltpatches.JSONException;
import org.json_voltpatches.JSONObject;
import org.voltdb.catalog.Cluster;
import org.voltdb.catalog.Database;
import org.voltdb.compiler.DatabaseEstimates;
import org.voltdb.compiler.DeterminismMode;
import org.voltdb.compiler.ScalarValueHints;
import org.voltdb.compiler.VoltCompiler;
import org.voltdb.plannodes.AbstractPlanNode;
import org.voltdb.plannodes.PlanNodeList;
import org.voltdb.utils.BuildDirectoryUtils;

/**
* The helper class to pick the best cost plan given raw plan, and to
* output a set of complete and correct query plans. The best plan will be selected
* by computing resource usage statistics for the plans, then using those statistics to
* compute the cost of a specific plan. The plan with the lowest cost wins.
*
*/
public class PlanSelector implements Cloneable {
    /** pointer to the cluster object in the catalog */
    final Cluster m_cluster;
    /** pointer to the database object in the catalog */
    final Database m_db;
    /** pointer to the database estimates */
    DatabaseEstimates m_estimates;
    /** The name of the sql statement to be planned */
    String m_stmtName;
    /** The name of the procedure containing the sql statement to be planned */
    String m_procName;
    /** SQL stmt text to be planned */
    String m_sql;
    /** The current cost model to evaluate plans with */
    AbstractCostModel m_costModel;
    /** Hints to use for planning */
    ScalarValueHints[] m_paramHints;
    /** The best cost plan after the evaluation */
    CompiledPlan m_bestPlan = null;
    /** The filename for the best plan */
    String m_bestFilename = null;
    /** Plan statistics */
    PlanStatistics m_stats = null;
    /** The id of the plan under the evaluation */
    int m_planId = 0;
    /** Determinism mode (for micro optimizations) */
    final DeterminismMode m_detMode;
    /** Parameters to drive the output */
    boolean m_quietPlanner;
    boolean m_fullDebug = VoltCompiler.DEBUG_MODE;

    /**
     * Initialize plan processor.
     *
     * @param cluster Catalog info about the physical layout of the cluster.
     * @param db Catalog info about schema, metadata and procedures.
     * @param estimates database estimates
     * @param stmtName The name of the sql statement to be planned.
     * @param procName The name of the procedure containing the sql statement to be planned.
     * @param sql SQL stmt text to be planned.
     * @param costModel The current cost model to evaluate plans with
     * @param paramHints Hints.
     * @param detMode Determinism mode (for micro optimizations)
     * @param quietPlanner Controls the output.
     * @param fullDebug Controls the debug output.
     */
    public PlanSelector(Cluster cluster, Database db, DatabaseEstimates estimates,
            String stmtName, String procName, String sql,
            AbstractCostModel costModel, ScalarValueHints[] paramHints,
            DeterminismMode detMode, boolean quietPlanner)
    {
        m_cluster = cluster;
        m_db = db;
        m_estimates = estimates;
        m_stmtName = stmtName;
        m_procName = procName;
        m_sql = sql;
        m_costModel = costModel;
        m_paramHints = paramHints;
        m_detMode = detMode;
        m_quietPlanner = quietPlanner;
    }

    /**
     * Clone itself.
     * @return deep copy of self
     */
    @Override
    public Object clone() {
        return new PlanSelector(m_cluster, m_db, m_estimates, m_stmtName, m_procName, m_sql,
                m_costModel, m_paramHints, m_detMode, m_quietPlanner);
    }

    /**
     * @param parsedStmt
     */
    public void outputParsedStatement(AbstractParsedStmt parsedStmt) {
        // output a description of the parsed stmt
        if (!m_quietPlanner) {
            BuildDirectoryUtils.writeFile("statement-parsed", m_procName + "_" + m_stmtName + ".txt",
                    parsedStmt.toString(), true);
        }
    }

    /**
     * @param xmlSQL
     */
    public void outputCompiledStatement(VoltXMLElement xmlSQL) {
        if (!m_quietPlanner) {
            // output the xml from hsql to disk for debugging
            BuildDirectoryUtils.writeFile("statement-hsql-xml", m_procName + "_" + m_stmtName + ".xml",
                    xmlSQL.toString(), true);
        }
    }


    public void outputParameterizedCompiledStatement(VoltXMLElement parameterizedXmlSQL) {
        if (!m_quietPlanner && m_fullDebug) {
            // output the xml from hsql to disk for debugging
            BuildDirectoryUtils.writeFile("statement-hsql-xml", m_procName + "_" + m_stmtName + "-parameterized.xml",
                    parameterizedXmlSQL.toString(), true);
        }
    }

    /** Picks the best cost plan for a given raw plan
     * @param rawplan
     */
    public void considerCandidatePlan(CompiledPlan plan, AbstractParsedStmt parsedStmt) {
        //System.out.println(String.format("[Raw plan]:%n%s", rawplan.rootPlanGraph.toExplainPlanString()));

        // run the set of microptimizations, which may return many plans (or not)
        ScanDeterminizer.apply(plan, m_detMode);

        // add in the sql to the plan
        plan.sql = m_sql;

        // compute resource usage using the single stats collector
        m_stats = new PlanStatistics();
        AbstractPlanNode planGraph = plan.rootPlanGraph;

        // compute statistics about a plan
        planGraph.computeEstimatesRecursively(m_stats, m_cluster, m_db, m_estimates, m_paramHints);

        // compute the cost based on the resources using the current cost model
        plan.cost = m_costModel.getPlanCost(m_stats);

        // filename for debug output
        String filename = String.valueOf(m_planId++);

        //* enable for debug */ System.out.println("DEBUG [new plan]: Cost:" + plan.cost + plan.rootPlanGraph.toExplainPlanString());

        // find the minimum cost plan
        if (m_bestPlan == null || plan.cost < m_bestPlan.cost) {
            // free the PlanColumns held by the previous best plan
            m_bestPlan = plan;
            m_bestFilename = filename;
            //* enable for debug */ System.out.println("DEBUG [Best plan] updated ***\n");
        }

        outputPlan(plan, planGraph, filename);
    }

    public void finalizeOutput() {
        if (m_quietPlanner) {
            return;
        }
        outputPlan(m_bestPlan, m_bestPlan.rootPlanGraph, m_bestFilename);

        // find out where debugging is going
        String prefix = BuildDirectoryUtils.getBuildDirectoryPath() +
                "/" + BuildDirectoryUtils.debugRootPrefix + "statement-all-plans/" +
                m_procName + "_" + m_stmtName + "/";
        String winnerFilename, winnerFilenameRenamed;

        // if outputting full stuff
        if (m_fullDebug) {
            // rename the winner json plan
            winnerFilename = prefix + m_bestFilename + "-json.txt";
            winnerFilenameRenamed = prefix + "WINNER-" + m_bestFilename + "-json.txt";
            renameFile(winnerFilename, winnerFilenameRenamed);

            // rename the winner dot plan
            winnerFilename = prefix + m_bestFilename + ".dot";
            winnerFilenameRenamed = prefix + "WINNER-" + m_bestFilename + ".dot";
            renameFile(winnerFilename, winnerFilenameRenamed);
        }

        // rename the winner explain plan
        winnerFilename = prefix + m_bestFilename + ".txt";
        winnerFilenameRenamed = prefix + "WINNER-" + m_bestFilename + ".txt";
        renameFile(winnerFilename, winnerFilenameRenamed);

        if (m_fullDebug) {
            // output the plan statistics to disk for debugging
            BuildDirectoryUtils.writeFile("statement-stats", m_procName + "_" + m_stmtName + ".txt",
                    m_stats.toString(), true);
        }
    }

    /**
     * @param plan
     * @param plan graph
     * @param filename
     */
    private void outputPlan(CompiledPlan plan, AbstractPlanNode planGraph, String filename) {
        if (!m_quietPlanner) {
            if (m_fullDebug) {
                outputPlanFullDebug(plan, planGraph, filename);
            }

            // get the explained plan for the node
            plan.explainedPlan = planGraph.toExplainPlanString();
            outputExplainedPlan(plan, filename);
        }
    }

    /**
     * @param plan
     * @param filename
     */
    private void outputExplainedPlan(CompiledPlan plan, String filename) {
        BuildDirectoryUtils.writeFile("statement-all-plans/" + m_procName + "_" + m_stmtName,
                                      filename + ".txt",
                                      plan.explainedPlan,
                                      true);
    }

    /**
     * @param plan
     * @param planGraph
     * @param filename
     * @return error message if any
     */
    private String outputPlanFullDebug(CompiledPlan plan, AbstractPlanNode planGraph, String filename) {
        // GENERATE JSON DEBUGGING OUTPUT BEFORE WE CLEAN UP THE
        // PlanColumns
        // convert a tree into an execution list
        PlanNodeList nodeList = new PlanNodeList(planGraph);

        // get the json serialized version of the plan
        String json = null;

        try {
            String crunchJson = nodeList.toJSONString();
            //System.out.println(crunchJson);
            //System.out.flush();
            JSONObject jobj = new JSONObject(crunchJson);
            json = jobj.toString(4);
        } catch (JSONException e2) {
            // Any plan that can't be serialized to JSON to
            // write to debugging output is also going to fail
            // to get written to the catalog, to sysprocs, etc.
            // Just bail.
            String errorMsg = "Plan for sql: '" + plan.sql +
                               "' can't be serialized to JSON";
            // This case used to exit the planner
            // -- a strange behavior for something that only gets called when full debug output is enabled.
            // For now, just skip the output and go on to the next plan.
            return errorMsg;
        }

        // output a description of the parsed stmt
        json = "PLAN:\n" + json;
        json = "COST: " + String.valueOf(plan.cost) + "\n" + json;
        assert (plan.sql != null);
        json = "SQL: " + plan.sql + "\n" + json;

        // write json to disk
        BuildDirectoryUtils.writeFile("statement-all-plans/" + m_procName + "_" + m_stmtName,
                                      filename + "-json.txt",
                                      json,
                                      true);

        // create a graph friendly version
        BuildDirectoryUtils.writeFile("statement-all-plans/" + m_procName + "_" + m_stmtName,
                                      filename + ".dot",
                                      nodeList.toDOTString("name"),
                                      true);
        return null;
    }

    /**
     * @param filename
     * @param filenameRenamed
     */
    private static void renameFile(String filename, String filenameRenamed) {
        File file;
        File fileRenamed;
        file = new File(filename);
        fileRenamed = new File(filenameRenamed);
        file.renameTo(fileRenamed);
    }
}
TOP

Related Classes of org.voltdb.planner.PlanSelector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.