/*
* Do the task in the network thread associated with the connection
* so that access to the CIHM can be lock free for fast path work.
* Can't access the CIHM from this thread without adding locking.
*/
final Connection c = (Connection)result.clientData;
final ListenableFutureTask<?> ft = ListenableFutureTask.create(new Runnable() {
@Override
public void run() {
if (result.errorMsg == null) {
if (result instanceof AdHocPlannedStmtBatch) {
final AdHocPlannedStmtBatch plannedStmtBatch = (AdHocPlannedStmtBatch) result;
// assume all stmts have the same catalog version
if ((plannedStmtBatch.getPlannedStatementCount() > 0) &&
(plannedStmtBatch.getPlannedStatement(0).core.catalogVersion != m_catalogContext.get().catalogVersion)) {
/* The adhoc planner learns of catalog updates after the EE and the
rest of the system. If the adhoc sql was planned against an
obsolete catalog, re-plan. */
LocalObjectMessage work = new LocalObjectMessage(
AdHocPlannerWork.rework(plannedStmtBatch.work, m_adhocCompletionHandler));
m_mailbox.send(m_plannerSiteId, work);
}
else if( plannedStmtBatch.isExplainWork() ) {
processExplainPlannedStmtBatch( plannedStmtBatch );
}
else {
try {
createAdHocTransaction(plannedStmtBatch, c);
}
catch (VoltTypeException vte) {
String msg = "Unable to execute adhoc sql statement(s): " +
vte.getMessage();
ClientResponseImpl errorResponse =
new ClientResponseImpl(
ClientResponseImpl.GRACEFUL_FAILURE,
new VoltTable[0], msg,
result.clientHandle);
ByteBuffer buf = ByteBuffer.allocate(errorResponse.getSerializedSize() + 4);
buf.putInt(buf.capacity() - 4);
errorResponse.flattenToBuffer(buf);
buf.flip();
c.writeStream().enqueue(buf);
}
}
}
else if (result instanceof CatalogChangeResult) {
final CatalogChangeResult changeResult = (CatalogChangeResult) result;
// if the catalog change is a null change
if (changeResult.encodedDiffCommands.trim().length() == 0) {
ClientResponseImpl shortcutResponse =
new ClientResponseImpl(
ClientResponseImpl.SUCCESS,
new VoltTable[0], "Catalog update with no changes was skipped.",
result.clientHandle);
ByteBuffer buf = ByteBuffer.allocate(shortcutResponse.getSerializedSize() + 4);
buf.putInt(buf.capacity() - 4);
shortcutResponse.flattenToBuffer(buf);
buf.flip();
c.writeStream().enqueue(buf);
}
else {
// create the execution site task
StoredProcedureInvocation task = new StoredProcedureInvocation();
task.procName = "@UpdateApplicationCatalog";
task.setParams(changeResult.encodedDiffCommands,
changeResult.catalogHash,
changeResult.catalogBytes,
changeResult.expectedCatalogVersion,
changeResult.deploymentString,
changeResult.tablesThatMustBeEmpty,
changeResult.reasonsForEmptyTables,
changeResult.requiresSnapshotIsolation ? 1 : 0,
changeResult.worksWithElastic ? 1 : 0,
changeResult.deploymentHash);
task.clientHandle = changeResult.clientHandle;
// DR stuff
task.type = changeResult.invocationType;
task.originalTxnId = changeResult.originalTxnId;
task.originalUniqueId = changeResult.originalUniqueId;
ClientResponseImpl error = null;
if ((error = m_permissionValidator.shouldAccept(task.procName, result.user, task,
SystemProcedureCatalog.listing.get(task.procName).asCatalogProcedure())) != null) {
ByteBuffer buffer = ByteBuffer.allocate(error.getSerializedSize() + 4);
buffer.putInt(buffer.capacity() - 4);
error.flattenToBuffer(buffer).flip();
c.writeStream().enqueue(buffer);
}
else {
/*
* Round trip the invocation to initialize it for command logging
*/
try {
task = MiscUtils.roundTripForCL(task);
} catch (Exception e) {
hostLog.fatal(e);
VoltDB.crashLocalVoltDB(e.getMessage(), true, e);
}
// initiate the transaction. These hard-coded values from catalog
// procedure are horrible, horrible, horrible.
createTransaction(changeResult.connectionId,
task, false, false, false, 0, task.getSerializedSize(),
System.nanoTime());
}
}
}
else {
throw new RuntimeException(
"Should not be able to get here (ClientInterface.checkForFinishedCompilerWork())");
}
}
else {
ClientResponseImpl errorResponse =
new ClientResponseImpl(
ClientResponseImpl.GRACEFUL_FAILURE,
new VoltTable[0], result.errorMsg,
result.clientHandle);
ByteBuffer buf = ByteBuffer.allocate(errorResponse.getSerializedSize() + 4);
buf.putInt(buf.capacity() - 4);
errorResponse.flattenToBuffer(buf);
buf.flip();
c.writeStream().enqueue(buf);
}
}
}, null);
if (c != null) {
c.queueTask(ft);
}
/*
* Add error handling in case of an unexpected exception
*/
ft.addListener(new Runnable() {
@Override
public void run() {
try {
ft.get();
} catch (Exception e) {
String realReason = result.errorMsg;
// Prefer adding detail to reporting an anonymous exception.
// This helped debugging when it caught a programming error
// -- not sure if this ever should catch anything in production code
// that could be explained in friendlier user terms.
// In that case, the root cause stack trace might be more of a distraction.
if (realReason == null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
Throwable cause = e.getCause();
if (cause != null) {
cause.printStackTrace(pw);
}
pw.flush();
realReason = sw.toString();
}
ClientResponseImpl errorResponse =
new ClientResponseImpl(
ClientResponseImpl.UNEXPECTED_FAILURE,
new VoltTable[0], realReason,
result.clientHandle);
ByteBuffer buf = ByteBuffer.allocate(errorResponse.getSerializedSize() + 4);
buf.putInt(buf.capacity() - 4);
errorResponse.flattenToBuffer(buf);
buf.flip();
c.writeStream().enqueue(buf);
}
}
}, CoreUtils.SAMETHREADEXECUTOR);
//Return the future task for test code