Package org.voltdb.compiler

Source Code of org.voltdb.compiler.StatementCompiler

/* 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.compiler;

import java.io.PrintStream;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;
import org.hsqldb.HSQLInterface;
import org.json.JSONException;
import org.json.JSONObject;
import org.voltdb.catalog.Catalog;
import org.voltdb.catalog.Column;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.PlanFragment;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Statement;
import org.voltdb.catalog.StmtParameter;
import org.voltdb.catalog.Table;
import org.voltdb.messaging.FastSerializer;
import org.voltdb.planner.CompiledPlan;
import org.voltdb.planner.ParameterInfo;
import org.voltdb.planner.PlanColumn;
import org.voltdb.planner.QueryPlanner;
import org.voltdb.planner.TrivialCostModel;
import org.voltdb.plannodes.AbstractPlanNode;
import org.voltdb.plannodes.AbstractScanPlanNode;
import org.voltdb.plannodes.DeletePlanNode;
import org.voltdb.plannodes.InsertPlanNode;
import org.voltdb.plannodes.PlanNodeList;
import org.voltdb.plannodes.UpdatePlanNode;
import org.voltdb.types.QueryType;
import org.voltdb.utils.BuildDirectoryUtils;
import org.voltdb.utils.Encoder;

import edu.brown.catalog.CatalogUtil;
import edu.brown.catalog.PlanFragmentIdGenerator;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.plannodes.PlanNodeUtil;

/**
* Compiles individual SQL statements and updates the given catalog.
* <br/>Invokes the Optimizer to generate plans.
*
*/
public abstract class StatementCompiler {
    private static final Logger LOG = Logger.getLogger(StatementCompiler.class);
    private static final LoggerBoolean debug = new LoggerBoolean();
    private static final LoggerBoolean trace = new LoggerBoolean();

    private static AtomicInteger NEXT_FRAGMENT_ID = null;
   
    public static int getNextFragmentId(Database catalog_db) {
        return getNextFragmentId(catalog_db, false, false, false);
    }
   
    public synchronized static int getNextFragmentId(Database catalog_db,
                                                        boolean readonly,
                                                        boolean fastAggregate,
                                                        boolean fastCombine) {
        // If this is the first time we are being called, figure out
        // where our ids should start at
        if (NEXT_FRAGMENT_ID == null) {
            int max_id = 100;
            for (Statement catalog_stmt : CatalogUtil.getAllStatements(catalog_db)) {
                for (PlanFragment catalog_frag : CatalogUtil.getAllPlanFragments(catalog_stmt)) {
                    max_id = Math.max(max_id, catalog_frag.getId());
                } // FOR
            } // FOR
            NEXT_FRAGMENT_ID = new AtomicInteger(max_id);
            if (trace.val) LOG.trace("Initialized NEXT_FRAGMENT_ID = " + NEXT_FRAGMENT_ID.get());
        }
        // If it's not readonly, then we'll offset it so that we can
        // easily identify it at runtime
        int next_id = NEXT_FRAGMENT_ID.incrementAndGet();
        return (PlanFragmentIdGenerator.createPlanFragmentId(next_id,
                                                              readonly,
                                                              fastAggregate,
                                                              fastCombine));
    }
   
    public static void compile(VoltCompiler compiler, HSQLInterface hsql,
            Catalog catalog, Database db, DatabaseEstimates estimates,
            Statement catalogStmt, String stmt, boolean singlePartition)
    throws VoltCompiler.VoltCompilerException {

        // Always add in a unique Id
        catalogStmt.setId(compiler.getNextStatementId());
       
        // Strip newlines for catalog compatibility
        stmt = stmt.replaceAll("\n", " ");
        // remove leading and trailing whitespace so the lines not
        // too far below this doesn't fail (starts with "insert", etc...)
        stmt = stmt.trim();

        //LOG.fine("Compiling Statement: ");
        //LOG.fine(stmt);
        compiler.addInfo("Compiling Statement: " + stmt);

        // determine the type of the query
        QueryType qtype;
        if (stmt.toLowerCase().startsWith("insert")) {
            qtype = QueryType.INSERT;
            catalogStmt.setReadonly(false);
        }
        else if (stmt.toLowerCase().startsWith("update")) {
            qtype = QueryType.UPDATE;
            catalogStmt.setReadonly(false);
        }
        else if (stmt.toLowerCase().startsWith("delete")) {
            qtype = QueryType.DELETE;
            catalogStmt.setReadonly(false);
        }
        else if (stmt.toLowerCase().startsWith("select")) {
            qtype = QueryType.SELECT;
            catalogStmt.setReadonly(true);
        }
        else {
            throw compiler.new VoltCompilerException("Unparsable SQL statement.");
        }
        catalogStmt.setQuerytype(qtype.getValue());

        // put the data in the catalog that we have
        catalogStmt.setSqltext(stmt);
        catalogStmt.setSinglepartition(singlePartition);
        catalogStmt.setBatched(false);
        catalogStmt.setParamnum(0);
        catalogStmt.setHas_singlesited(false);
        catalogStmt.setHas_multisited(false);

        // PAVLO: Super Hack!
        // Always compile the multi-partition and single-partition query plans!
        // We don't need the multi-partition query plans for MapReduce transactions
        Procedure catalog_proc = catalogStmt.getParent();
        boolean isMapReduce = catalog_proc.getMapreduce();

        CompiledPlan plan = null;
        CompiledPlan last_plan = null;
        PlanNodeList node_list = null;
       
        QueryPlanner planner = new QueryPlanner(catalog.getClusters().get("cluster"), db, hsql, estimates, true, false);

        Throwable first_exception = null;
        for (boolean _singleSited : new boolean[]{ true, false }) {
            if (_singleSited == false && isMapReduce) continue;
           
            QueryType stmt_type = QueryType.get(catalogStmt.getQuerytype());
            String msg = "Creating " + stmt_type.name() + " query plan for " + catalogStmt.fullName() + ": singleSited=" + _singleSited;
            if (trace.val) LOG.trace(msg);
            compiler.addInfo(msg);

            catalogStmt.setSinglepartition(_singleSited);
            String name = catalogStmt.getParent().getName() + "-" + catalogStmt.getName();
   
            TrivialCostModel costModel = new TrivialCostModel();
            try {
                plan = planner.compilePlan(costModel, catalogStmt.getSqltext(),
                        catalogStmt.getName(), catalogStmt.getParent().getName(),
                        catalogStmt.getSinglepartition(), null);
            } catch (Throwable e) {
                LOG.error("Failed to plan for stmt: " + catalogStmt.fullName(), e);
                if (first_exception == null) {
                    if (debug.val) LOG.warn("Ignoring first error for " + catalogStmt.getName() + " :: " + e.getMessage());
                    first_exception = e;
                    continue;
                }
                e.printStackTrace();
                throw compiler.new VoltCompilerException("Failed to plan for stmt: " + catalogStmt.fullName());
            }

            if (plan == null) {
                msg = "Failed to plan for stmt: " + catalogStmt.fullName();
                String plannerMsg = planner.getErrorMessage();

                if (plannerMsg == null) plannerMsg = "PlannerMessage was empty!";
               
                // HACK: Ignore if they were trying to do a single-sited INSERT/UPDATE/DELETE
                //       on a replicated table
                if (plannerMsg.contains("replicated table") && _singleSited) {
                    if (debug.val)
                        LOG.warn(String.format("Ignoring error for %s: %s", catalogStmt.fullName(), plannerMsg));
                    continue;
                // HACK: If we get an unknown error message on an multi-sited INSERT/UPDATE/DELETE, assume
                //       that it's because we are trying to insert on a non-replicated table
                } else if (!_singleSited && stmt_type == QueryType.INSERT && plannerMsg.contains("Error unknown")) {
                    if (debug.val)
                        LOG.warn(String.format("Ignoring multi-sited %s %s on non-replicated table: %s",
                                               stmt_type.name(), catalogStmt.fullName(), plannerMsg));
                    continue;
                } else if (planner.getError() != null) {
                    if (debug.val) LOG.error(msg);
                    throw compiler.new VoltCompilerException(msg, planner.getError());
                // Otherwise, report the error
                } else {
                    if (plannerMsg != null)
                        msg += " with error: \"" + plannerMsg + "\"";
                    if (debug.val) LOG.error(msg);
                    throw compiler.new VoltCompilerException(msg);
                }
            }
            if (trace.val)
                LOG.trace(String.format("%s Analyzing %s query plan",
                                        catalogStmt.fullName(), (_singleSited == false ? "DTXN" : "SP")))

            // serialize full where clause to the catalog
            // for the benefit of the designer
            if (plan.fullWhereClause != null) {
                String json = "ERROR";
                try {
                    // serialize to pretty printed json
                    String jsonCompact = plan.fullWhereClause.toJSONString();
                    // pretty printing seems to cause issues
                    //JSONObject jobj = new JSONObject(jsonCompact);
                    //json = jobj.toString(4);
                    json = jsonCompact;
                } catch (Exception e) {
                    // hopefully someone will notice
                    e.printStackTrace();
                }
                String hexString = Encoder.hexEncode(json);
                catalogStmt.setExptree(hexString);
            }

            // serialize full plan to the catalog
            // for the benefit of the designer
            if (plan.fullWinnerPlan != null) {
                String json = plan.fullplan_json;
                String hexString = Encoder.hexEncode(json);
                if (_singleSited) {
                    catalogStmt.setFullplan(hexString);
                } else {
                    catalogStmt.setMs_fullplan(hexString);
                }
            }
   
            //Store the list of parameters types and indexes in the plan node list.
   
            /*List<Pair<Integer, VoltType>> parameters = node_list.getParameters();
            for (ParameterInfo param : plan.parameters) {
                Pair<Integer, VoltType> parameter = new Pair<Integer, VoltType>(param.index, param.type);
                parameters.add(parameter);
            }*/
   
            int i = 0;
            Collections.sort(plan.fragments);
            if (trace.val)
                LOG.trace(catalogStmt.fullName() + " Plan Fragments: " + plan.fragments);
            for (CompiledPlan.Fragment fragment : plan.fragments) {
                node_list = new PlanNodeList(fragment.planGraph);
               
                boolean readonly = fragmentReadOnly(fragment.planGraph);
                boolean fastAggregate = false; // FIXME
                boolean fastCombine = false; // FIXME
               
                // Now update our catalog information
                int id = getNextFragmentId(db, readonly, fastAggregate, fastCombine);
                String planFragmentName = Integer.toString(id);
                PlanFragment planFragment = null;
                   
                if (_singleSited) {
                    planFragment = catalogStmt.getFragments().add(planFragmentName);
                    catalogStmt.setHas_singlesited(true);
                    if (trace.val)
                        LOG.trace(String.format("%s SP PLAN FRAGMENT: %s", catalogStmt.fullName(), planFragment));
                } else {
                    planFragment = catalogStmt.getMs_fragments().add(planFragmentName);
                    catalogStmt.setHas_multisited(true);
                    if (trace.val)
                        LOG.trace(String.format("%s DTXN PLAN FRAGMENT: %s", catalogStmt.fullName(), planFragment));
                }
   
                // mark a fragment as non-transactional if it never touches a persistent table
                planFragment.setNontransactional(!fragmentReferencesPersistentTable(fragment.planGraph));
                planFragment.setReadonly(readonly);
                planFragment.setHasdependencies(fragment.hasDependencies);
                planFragment.setMultipartition(fragment.multiPartition);
                planFragment.setId(id);

                String json = null;
                try {
                    JSONObject jobj = new JSONObject(node_list.toJSONString());
                    json = jobj.toString(4);
                } catch (JSONException e2) {
                    throw new RuntimeException(e2);
                }
   
                // TODO: can't re-enable this until the EE accepts PlanColumn GUIDs
                // instead of column names because the deserialization is done without
                // any connection to the child nodes - required to map the PlanColumn's
                // GUID to the child's column name.
   
                // verify the plan serializes and deserializes correctly.
                // assert(node_list.testJSONSerialization(db));
   
                // output the plan to disk for debugging
                PrintStream plansOut = BuildDirectoryUtils.getDebugOutputPrintStream(
                        "statement-winner-plans", name + "-" + String.valueOf(i++) + ".txt");
                plansOut.println(json);
                plansOut.close();
   
                //
                // We then stick a serialized version of PlanNodeTree into a PlanFragment
                //
                try {
                    FastSerializer fs = new FastSerializer(false, false); // C++ needs little-endian
                    fs.write(json.getBytes());
                    String hexString = fs.getHexEncodedBytes();
                    planFragment.setPlannodetree(hexString);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw compiler.new VoltCompilerException(e.getMessage());
                }
            }
           
            last_plan = plan;
        } // FOR (multipartition + singlepartition)
        if (last_plan == null) {
            throw compiler.new VoltCompilerException("Bad news! We don't have a last plan!!");
        }
        plan = last_plan;
       
        // HACK
        AbstractPlanNode root = null;
        try {
            root = PlanNodeUtil.getRootPlanNodeForStatement(catalogStmt, true);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        assert(root != null);
        Collection<Table> tables_accessed = CatalogUtil.getReferencedTablesForTree(db, root);
        assert(tables_accessed.isEmpty() == false) : "Failed to find accessed tables for " + catalogStmt + "-- Plan:\n" + PlanNodeUtil.debug(plan.fullWinnerPlan);
        boolean all_replicated = true;
        for (Table catalog_tbl : tables_accessed) {
            if (catalog_tbl.getIsreplicated() == false) {
                all_replicated = false;
                break;
            }
        } // FOR
        catalogStmt.setReplicatedonly(all_replicated);
       
        // Input Parameters
        // We will need to update the system catalogs with this new information
        // If this is an ad hoc query then there won't be any parameters
        for (ParameterInfo param : plan.parameters) {
            StmtParameter catalogParam = catalogStmt.getParameters().add(String.valueOf(param.index));
            catalogParam.setJavatype(param.type.getValue());
            catalogParam.setIndex(param.index);
        }

        // Output Columns
        int index = 0;
        for (Integer colguid : plan.columns) {
            PlanColumn planColumn = planner.getPlannerContext().get(colguid);
            Column catColumn = catalogStmt.getOutput_columns().add(planColumn.getDisplayName()); // String.valueOf(index));
            catColumn.setNullable(false);
            catColumn.setIndex(index);
//            catColumn.setName(planColumn.displayName());
            catColumn.setType(planColumn.type().getValue());
            catColumn.setSize(planColumn.width());
            index++;
        }

        catalogStmt.setReplicatedtabledml(plan.replicatedTableDML);

        //Store the list of parameters types and indexes in the plan node list.

        /*List<Pair<Integer, VoltType>> parameters = node_list.getParameters();
        for (ParameterInfo param : plan.parameters) {
            Pair<Integer, VoltType> parameter = new Pair<Integer, VoltType>(param.index, param.type);
            parameters.add(parameter);
        }*/


    }

    /**
     * Check through a plan graph and return true if it ever touches a persistent table.
     */
    static boolean fragmentReferencesPersistentTable(AbstractPlanNode node) {
        if (node == null)
            return false;

        // these nodes can read/modify persistent tables
        if (node instanceof AbstractScanPlanNode)
            return true;
        if (node instanceof InsertPlanNode)
            return true;
        if (node instanceof DeletePlanNode)
            return true;
        if (node instanceof UpdatePlanNode)
            return true;

        // recursively check out children
        for (int i = 0; i < node.getChildPlanNodeCount(); i++) {
            AbstractPlanNode child = node.getChild(i);
            if (fragmentReferencesPersistentTable(child))
                return true;
        }

        // if nothing found, return false
        return false;
    }
   
    /**
     * Check through a plan graph and return true if it is read only
     */
    static boolean fragmentReadOnly(AbstractPlanNode node) {
        if (node == null)
            return true;

        if (node instanceof InsertPlanNode)
            return false;
        if (node instanceof DeletePlanNode)
            return false;
        if (node instanceof UpdatePlanNode)
            return false;

        // recursively check out children
        for (int i = 0; i < node.getChildPlanNodeCount(); i++) {
            if (fragmentReadOnly(node.getChild(i)) == false) return (false);
        }

        // if nothing found, return true
        return true;
    }
}
TOP

Related Classes of org.voltdb.compiler.StatementCompiler

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.