Search in sources :

Example 1 with BoundPlan

use of org.voltdb.planner.BoundPlan in project voltdb by VoltDB.

the class AdHocCompilerCache method put.

/**
     * Called from the PlannerTool directly when it finishes planning.
     * This is the only way to populate the cache.
     *
     * Note that one goal here is to reduce the number of times two
     * separate plan instances with the same value are input for the
     * same SQL literal.
     *
     * L1 cache (literal cache) cache SQL queries without user provided parameters.
     * L2 cache (core cache) cache parameterized queries: including user parameters and auto extracted parameters.
     *
     * @param sql               original query text
     * @param parsedToken       massaged query text, possibly with literals purged
     * @param planIn
     * @param extractedLiterals the basis values for any "bound parameter" restrictions to plan re-use
     * @param hasUserQuestionMarkParameters is user provided parameterized query
     * @param hasAutoParameterizedException is the auto parameterized query has parameter exception
     */
public synchronized void put(String sql, String parsedToken, AdHocPlannedStatement planIn, String[] extractedLiterals, boolean hasUserQuestionMarkParameters, boolean hasAutoParameterizedException) {
    assert (sql != null);
    assert (parsedToken != null);
    assert (planIn != null);
    AdHocPlannedStatement plan = planIn;
    assert (new String(plan.sql, Constants.UTF8ENCODING).equals(sql));
    // it means that a query can not be both user parameterized query and auto parameterized query.
    assert (!hasUserQuestionMarkParameters || !hasAutoParameterizedException);
    // deal with L2 cache
    if (!hasAutoParameterizedException) {
        BoundPlan matched = null;
        BoundPlan unmatched = new BoundPlan(planIn.core, planIn.parameterBindings(extractedLiterals));
        // deal with the parameterized plan cache first
        List<BoundPlan> boundVariants = m_coreCache.get(parsedToken);
        if (boundVariants == null) {
            boundVariants = new ArrayList<BoundPlan>();
            m_coreCache.put(parsedToken, boundVariants);
            // Note that there is an edge case in which more than one plan is getting counted as one
            // "plan insertion". This only happens when two different plans arose from the same parameterized
            // query (token) because one invocation used the correct constants to trigger an expression index and
            // another invocation did not.  These are not counted separately (which would have to happen below
            // after each call to boundVariants.add) because they are not evicted separately.
            // It seems saner to use consistent units when counting insertions vs. evictions.
            ++m_planInsertions;
        } else {
            for (BoundPlan boundPlan : boundVariants) {
                if (boundPlan.equals(unmatched)) {
                    matched = boundPlan;
                    break;
                }
            }
            if (matched != null) {
                // this is useful when updating the literal cache
                if (unmatched.m_core != matched.m_core) {
                    plan = new AdHocPlannedStatement(planIn, matched.m_core);
                    plan.setBoundConstants(matched.m_constants);
                }
            }
        }
        if (matched == null) {
            // Don't count insertions (of possibly repeated tokens) here
            //  -- see the comment above where only UNIQUE token insertions are being counted, instead.
            boundVariants.add(unmatched);
        }
    }
    // then deal with the L1 cache
    if (!hasUserQuestionMarkParameters) {
        AdHocPlannedStatement cachedPlan = m_literalCache.get(sql);
        if (cachedPlan == null) {
            //* enable to debug */ System.out.println("DEBUG: Caching literal '" + sql + "'");
            m_literalCache.put(sql, plan);
            ++m_literalInsertions;
        } else {
            assert (cachedPlan.equals(plan));
        }
    }
}
Also used : BoundPlan(org.voltdb.planner.BoundPlan)

Example 2 with BoundPlan

use of org.voltdb.planner.BoundPlan in project voltdb by VoltDB.

the class PlannerTool method planSql.

public synchronized AdHocPlannedStatement planSql(String sqlIn, StatementPartitioning partitioning, boolean isExplainMode, final Object[] userParams, boolean isSwapTables) {
    CacheUse cacheUse = CacheUse.FAIL;
    if (m_plannerStats != null) {
        m_plannerStats.startStatsCollection();
    }
    boolean hasUserQuestionMark = false;
    boolean wrongNumberParameters = false;
    try {
        if ((sqlIn == null) || (sqlIn.length() == 0)) {
            throw new RuntimeException("Can't plan empty or null SQL.");
        }
        // remove any spaces or newlines
        String sql = sqlIn.trim();
        // if the cases tended to have mostly overlapping queries.
        if (partitioning.isInferred()) {
            // Check the literal cache for a match.
            AdHocPlannedStatement cachedPlan = m_cache.getWithSQL(sqlIn);
            if (cachedPlan != null) {
                cacheUse = CacheUse.HIT1;
                return cachedPlan;
            } else {
                cacheUse = CacheUse.MISS;
            }
        }
        // Reset plan node id counter
        AbstractPlanNode.resetPlanNodeIds();
        //////////////////////
        // PLAN THE STMT
        //////////////////////
        TrivialCostModel costModel = new TrivialCostModel();
        DatabaseEstimates estimates = new DatabaseEstimates();
        QueryPlanner planner = new QueryPlanner(sql, "PlannerTool", "PlannerToolProc", m_database, partitioning, m_hsql, estimates, !VoltCompiler.DEBUG_MODE, AD_HOC_JOINED_TABLE_LIMIT, costModel, null, null, DeterminismMode.FASTER);
        CompiledPlan plan = null;
        String[] extractedLiterals = null;
        String parsedToken = null;
        try {
            if (isSwapTables) {
                planner.planSwapTables();
            } else {
                planner.parse();
            }
            parsedToken = planner.parameterize();
            // check the parameters count
            // check user input question marks with input parameters
            int inputParamsLengh = userParams == null ? 0 : userParams.length;
            if (planner.getAdhocUserParamsCount() != inputParamsLengh) {
                wrongNumberParameters = true;
                if (!isExplainMode) {
                    throw new PlanningErrorException(String.format("Incorrect number of parameters passed: expected %d, passed %d", planner.getAdhocUserParamsCount(), inputParamsLengh));
                }
            }
            hasUserQuestionMark = planner.getAdhocUserParamsCount() > 0;
            // do not put wrong parameter explain query into cache
            if (!wrongNumberParameters && partitioning.isInferred()) {
                // QueryPlanner.
                assert (parsedToken != null);
                extractedLiterals = planner.extractedParamLiteralValues();
                List<BoundPlan> boundVariants = m_cache.getWithParsedToken(parsedToken);
                if (boundVariants != null) {
                    assert (!boundVariants.isEmpty());
                    BoundPlan matched = null;
                    for (BoundPlan boundPlan : boundVariants) {
                        if (boundPlan.allowsParams(extractedLiterals)) {
                            matched = boundPlan;
                            break;
                        }
                    }
                    if (matched != null) {
                        CorePlan core = matched.m_core;
                        ParameterSet params = null;
                        if (planner.compiledAsParameterizedPlan()) {
                            params = planner.extractedParamValues(core.parameterTypes);
                        } else if (hasUserQuestionMark) {
                            params = ParameterSet.fromArrayNoCopy(userParams);
                        } else {
                            // No constants AdHoc queries
                            params = ParameterSet.emptyParameterSet();
                        }
                        AdHocPlannedStatement ahps = new AdHocPlannedStatement(sql.getBytes(Constants.UTF8ENCODING), core, params, null);
                        ahps.setBoundConstants(matched.m_constants);
                        // parameterized plan from the cache does not have exception
                        m_cache.put(sql, parsedToken, ahps, extractedLiterals, hasUserQuestionMark, false);
                        cacheUse = CacheUse.HIT2;
                        return ahps;
                    }
                }
            }
            // If not caching or there was no cache hit, do the expensive full planning.
            plan = planner.plan();
            assert (plan != null);
            if (plan != null && plan.getStatementPartitioning() != null) {
                partitioning = plan.getStatementPartitioning();
            }
        } catch (Exception e) {
            /*
                 * Don't log PlanningErrorExceptions or HSQLParseExceptions, as
                 * they are at least somewhat expected.
                 */
            String loggedMsg = "";
            if (!((e instanceof PlanningErrorException) || (e instanceof HSQLParseException))) {
                logException(e, "Error compiling query");
                loggedMsg = " (Stack trace has been written to the log.)";
            }
            throw new RuntimeException("Error compiling query: " + e.toString() + loggedMsg, e);
        }
        if (plan == null) {
            throw new RuntimeException("Null plan received in PlannerTool.planSql");
        }
        //////////////////////
        // OUTPUT THE RESULT
        //////////////////////
        CorePlan core = new CorePlan(plan, m_catalogHash);
        AdHocPlannedStatement ahps = new AdHocPlannedStatement(plan, core);
        // do not put wrong parameter explain query into cache
        if (!wrongNumberParameters && partitioning.isInferred()) {
            // Note either the parameter index (per force to a user-provided parameter) or
            // the actual constant value of the partitioning key inferred from the plan.
            // Either or both of these two values may simply default
            // to -1 and to null, respectively.
            core.setPartitioningParamIndex(partitioning.getInferredParameterIndex());
            core.setPartitioningParamValue(partitioning.getInferredPartitioningValue());
            assert (parsedToken != null);
            // Again, plans with inferred partitioning are the only ones supported in the cache.
            m_cache.put(sqlIn, parsedToken, ahps, extractedLiterals, hasUserQuestionMark, planner.wasBadPameterized());
        }
        return ahps;
    } finally {
        if (m_plannerStats != null) {
            m_plannerStats.endStatsCollection(m_cache.getLiteralCacheSize(), m_cache.getCoreCacheSize(), cacheUse, -1);
        }
    }
}
Also used : CompiledPlan(org.voltdb.planner.CompiledPlan) ParameterSet(org.voltdb.ParameterSet) PlanningErrorException(org.voltdb.planner.PlanningErrorException) CacheUse(org.voltdb.PlannerStatsCollector.CacheUse) CorePlan(org.voltdb.planner.CorePlan) QueryPlanner(org.voltdb.planner.QueryPlanner) PlanningErrorException(org.voltdb.planner.PlanningErrorException) HSQLParseException(org.hsqldb_voltpatches.HSQLInterface.HSQLParseException) HSQLParseException(org.hsqldb_voltpatches.HSQLInterface.HSQLParseException) TrivialCostModel(org.voltdb.planner.TrivialCostModel) BoundPlan(org.voltdb.planner.BoundPlan)

Aggregations

BoundPlan (org.voltdb.planner.BoundPlan)2 HSQLParseException (org.hsqldb_voltpatches.HSQLInterface.HSQLParseException)1 ParameterSet (org.voltdb.ParameterSet)1 CacheUse (org.voltdb.PlannerStatsCollector.CacheUse)1 CompiledPlan (org.voltdb.planner.CompiledPlan)1 CorePlan (org.voltdb.planner.CorePlan)1 PlanningErrorException (org.voltdb.planner.PlanningErrorException)1 QueryPlanner (org.voltdb.planner.QueryPlanner)1 TrivialCostModel (org.voltdb.planner.TrivialCostModel)1