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));
}
}
}
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);
}
}
}
Aggregations