use of herddb.model.TransactionContext in project herddb by diennea.
the class ServerSideConnectionPeer method rollbackTransaction.
public void rollbackTransaction(String tableSpace, long txId) throws HDBException {
try {
RollbackTransactionStatement statement = new RollbackTransactionStatement(tableSpace, txId);
TransactionContext transactionContext = new TransactionContext(txId);
server.getManager().executeStatement(statement, StatementEvaluationContext.DEFAULT_EVALUATION_CONTEXT(), transactionContext);
} catch (HerdDBInternalException t) {
throw new HDBException(t);
}
}
use of herddb.model.TransactionContext in project herddb by diennea.
the class ServerSideConnectionPeer method handleExecuteStatement.
private void handleExecuteStatement(Pdu message, Channel channel) {
long txId = PduCodec.ExecuteStatement.readTx(message);
String tablespace = PduCodec.ExecuteStatement.readTablespace(message);
long statementId = PduCodec.ExecuteStatement.readStatementId(message);
String query = statementId > 0 ? preparedStatements.resolveQuery(tablespace, statementId) : PduCodec.ExecuteStatement.readQuery(message);
if (query == null) {
ByteBuf error = PduCodec.ErrorResponse.writeMissingPreparedStatementError(message.messageId, "bad statement id: " + statementId);
channel.sendReplyMessage(message.messageId, error);
message.close();
return;
}
boolean returnValues = PduCodec.ExecuteStatement.readReturnValues(message);
PduCodec.ObjectListReader parametersReader = PduCodec.ExecuteStatement.startReadParameters(message);
List<Object> parameters = new ArrayList<>(parametersReader.getNumParams());
for (int i = 0; i < parametersReader.getNumParams(); i++) {
parameters.add(parametersReader.nextObject());
}
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "query {0} with {1}", new Object[] { query, parameters });
}
RunningStatementInfo statementInfo = new RunningStatementInfo(query, System.currentTimeMillis(), tablespace, "", 1);
TransactionContext transactionContext = new TransactionContext(txId);
TranslatedQuery translatedQuery;
try {
translatedQuery = server.getManager().getPlanner().translate(tablespace, query, parameters, false, true, returnValues, -1);
} catch (StatementExecutionException ex) {
ByteBuf error = composeErrorResponse(message.messageId, ex);
channel.sendReplyMessage(message.messageId, error);
message.close();
return;
}
Statement statement = translatedQuery.plan.mainStatement;
// LOGGER.log(Level.SEVERE, "query " + query + ", " + parameters + ", plan: " + translatedQuery.plan);
RunningStatementsStats runningStatements = server.getManager().getRunningStatements();
runningStatements.registerRunningStatement(statementInfo);
CompletableFuture<StatementExecutionResult> res = server.getManager().executePlanAsync(translatedQuery.plan, translatedQuery.context, transactionContext);
// LOGGER.log(Level.SEVERE, "query " + query + ", " + parameters + ", result:" + result);
res.whenComplete((result, err) -> {
try {
runningStatements.unregisterRunningStatement(statementInfo);
if (err != null) {
while (err instanceof CompletionException) {
err = err.getCause();
}
if (err instanceof DuplicatePrimaryKeyException) {
ByteBuf error = PduCodec.ErrorResponse.writeSqlIntegrityConstraintsViolation(message.messageId, new SQLIntegrityConstraintViolationException(err));
channel.sendReplyMessage(message.messageId, error);
} else if (err instanceof NotLeaderException) {
ByteBuf error = composeErrorResponse(message.messageId, err);
channel.sendReplyMessage(message.messageId, error);
} else if (err instanceof StatementExecutionException) {
ByteBuf error = composeErrorResponse(message.messageId, err);
channel.sendReplyMessage(message.messageId, error);
} else {
LOGGER.log(Level.SEVERE, "unexpected error on query " + query + ", parameters: " + parameters + ":" + err, err);
ByteBuf error = composeErrorResponse(message.messageId, err);
channel.sendReplyMessage(message.messageId, error);
}
return;
}
if (result instanceof DMLStatementExecutionResult) {
DMLStatementExecutionResult dml = (DMLStatementExecutionResult) result;
Map<String, Object> newRecord = null;
if (returnValues && dml.getKey() != null) {
TableAwareStatement tableStatement = statement.unwrap(TableAwareStatement.class);
Table table = server.getManager().getTableSpaceManager(statement.getTableSpace()).getTableManager(tableStatement.getTable()).getTable();
newRecord = new HashMap<>();
Object newKey = RecordSerializer.deserializePrimaryKey(dml.getKey(), table);
newRecord.put("_key", newKey);
if (dml.getNewvalue() != null) {
newRecord.putAll(RecordSerializer.toBean(new Record(dml.getKey(), dml.getNewvalue()), table));
}
}
channel.sendReplyMessage(message.messageId, PduCodec.ExecuteStatementResult.write(message.messageId, dml.getUpdateCount(), dml.transactionId, newRecord));
} else if (result instanceof GetResult) {
GetResult get = (GetResult) result;
if (!get.found()) {
channel.sendReplyMessage(message.messageId, PduCodec.ExecuteStatementResult.write(message.messageId, 0, get.transactionId, null));
} else {
Map<String, Object> record = get.getRecord().toBean(get.getTable());
channel.sendReplyMessage(message.messageId, PduCodec.ExecuteStatementResult.write(message.messageId, 1, get.transactionId, record));
}
} else if (result instanceof TransactionResult) {
TransactionResult txresult = (TransactionResult) result;
channel.sendReplyMessage(message.messageId, PduCodec.ExecuteStatementResult.write(message.messageId, 1, txresult.getTransactionId(), null));
} else if (result instanceof DDLStatementExecutionResult) {
DDLStatementExecutionResult ddl = (DDLStatementExecutionResult) result;
channel.sendReplyMessage(message.messageId, PduCodec.ExecuteStatementResult.write(message.messageId, 1, ddl.transactionId, null));
} else if (result instanceof DataConsistencyStatementResult) {
channel.sendReplyMessage(message.messageId, PduCodec.ExecuteStatementResult.write(message.messageId, 0, 0, null));
} else {
ByteBuf error = PduCodec.ErrorResponse.write(message.messageId, "unknown result type:" + result);
channel.sendReplyMessage(message.messageId, error);
}
} finally {
message.close();
}
});
}
use of herddb.model.TransactionContext in project herddb by diennea.
the class TableManager method executeForeignKeyConstraintsAsParentTable.
private void executeForeignKeyConstraintsAsParentTable(Table childTable, DataAccessor previousValuesOnParentTable, StatementEvaluationContext context, Transaction transaction, boolean delete) throws StatementExecutionException {
// invalidated consistently during DML operations.
for (ForeignKeyDef fk : childTable.foreignKeys) {
String query = parentForeignKeyQueries.computeIfAbsent(childTable.name + "." + fk.name + ".#" + delete, (l -> {
if (fk.onDeleteAction == ForeignKeyDef.ACTION_CASCADE && delete) {
StringBuilder q = new StringBuilder("DELETE FROM ");
q.append(delimit(childTable.tablespace));
q.append(".");
q.append(delimit(childTable.name));
q.append(" WHERE ");
for (int i = 0; i < fk.columns.length; i++) {
if (i > 0) {
q.append(" AND ");
}
q.append(delimit(fk.columns[i]));
q.append("=?");
}
return q.toString();
} else if (fk.onUpdateAction == ForeignKeyDef.ACTION_CASCADE && !delete) {
// the change is more complex, let's keep it for a future work
throw new StatementExecutionException("No supported ON UPDATE CASCADE");
} else if ((fk.onDeleteAction == ForeignKeyDef.ACTION_SETNULL && delete) || (fk.onUpdateAction == ForeignKeyDef.ACTION_SETNULL && !delete)) {
// delete or update it is the same for SET NULL
StringBuilder q = new StringBuilder("UPDATE ");
q.append(delimit(childTable.tablespace));
q.append(".");
q.append(delimit(childTable.name));
q.append(" SET ");
for (int i = 0; i < fk.columns.length; i++) {
if (i > 0) {
q.append(",");
}
q.append(delimit(fk.columns[i]));
q.append("= NULL ");
}
q.append(" WHERE ");
for (int i = 0; i < fk.columns.length; i++) {
if (i > 0) {
q.append(" AND ");
}
q.append(delimit(fk.columns[i]));
q.append("=?");
}
return q.toString();
} else {
// NO ACTION case, check that there is no matching record in the child table that wouble be invalidated
// with '*' we are not going to perform projections or copies
StringBuilder q = new StringBuilder("SELECT * FROM ");
q.append(delimit(childTable.tablespace));
q.append(".");
q.append(delimit(childTable.name));
q.append(" WHERE ");
for (int i = 0; i < fk.columns.length; i++) {
if (i > 0) {
q.append(" AND ");
}
q.append(delimit(fk.columns[i]));
q.append("=?");
}
return q.toString();
}
}));
final List<Object> valuesToMatch = new ArrayList<>(fk.parentTableColumns.length);
for (int i = 0; i < fk.parentTableColumns.length; i++) {
valuesToMatch.add(previousValuesOnParentTable.get(fk.parentTableColumns[i]));
}
TransactionContext tx = transaction != null ? new TransactionContext(transaction.transactionId) : TransactionContext.NO_TRANSACTION;
if (fk.onDeleteAction == ForeignKeyDef.ACTION_CASCADE && delete || fk.onUpdateAction == ForeignKeyDef.ACTION_CASCADE && !delete || fk.onUpdateAction == ForeignKeyDef.ACTION_SETNULL && !delete || fk.onDeleteAction == ForeignKeyDef.ACTION_SETNULL && delete) {
tableSpaceManager.getDbmanager().executeSimpleStatement(tableSpaceManager.getTableSpaceName(), query, valuesToMatch, // every record
-1, // keep read locks in TransactionContext
true, tx, null);
} else {
boolean fkOk;
try (DataScanner scan = tableSpaceManager.getDbmanager().executeSimpleQuery(tableSpaceManager.getTableSpaceName(), query, valuesToMatch, // only one record
1, // keep read locks in TransactionContext
true, tx, null)) {
List<DataAccessor> resultSet = scan.consume();
// we are on the parent side of the relation
// we are okay if there is no matching record
// TODO: return the list of PKs in order to implement CASCADE operations
fkOk = resultSet.isEmpty();
} catch (DataScannerException err) {
throw new StatementExecutionException(err);
}
if (!fkOk) {
throw new ForeignKeyViolationException(fk.name, "foreignKey " + childTable.name + "." + fk.name + " violated");
}
}
}
}
use of herddb.model.TransactionContext in project herddb by diennea.
the class TableSpaceManager method executePlannedOperationStatement.
private CompletableFuture<StatementExecutionResult> executePlannedOperationStatement(Statement statement, TransactionContext transactionContext, StatementEvaluationContext context) {
long lockStamp = context.getTableSpaceLock();
boolean lockAcquired = false;
if (lockStamp == 0) {
lockStamp = acquireReadLock(statement);
context.setTableSpaceLock(lockStamp);
lockAcquired = true;
}
SQLPlannedOperationStatement planned = (SQLPlannedOperationStatement) statement;
CompletableFuture<StatementExecutionResult> res;
try {
res = planned.getRootOp().executeAsync(this, transactionContext, context, false, false);
} catch (HerdDBInternalException err) {
// ensure we are able to release locks correctly
LOGGER.log(Level.SEVERE, "Internal error", err);
res = Futures.exception(err);
}
// });
if (lockAcquired) {
res = releaseReadLock(res, lockStamp, statement).thenApply(s -> {
context.setTableSpaceLock(0);
return s;
});
}
return res;
}
use of herddb.model.TransactionContext in project herddb by diennea.
the class TableSpaceManager method executeStatementAsync.
public CompletableFuture<StatementExecutionResult> executeStatementAsync(Statement statement, StatementEvaluationContext context, TransactionContext transactionContext) throws StatementExecutionException {
if (transactionContext.transactionId == TransactionContext.AUTOTRANSACTION_ID && // Do not autostart transaction on alter table statements
statement.supportsTransactionAutoCreate()) {
AtomicLong capturedTx = new AtomicLong();
boolean wasHoldingTableSpaceLock = context.getTableSpaceLock() != 0;
CompletableFuture<StatementExecutionResult> newTransaction = beginTransactionAsync(context, false);
CompletableFuture<StatementExecutionResult> finalResult = newTransaction.thenCompose((StatementExecutionResult begineTransactionResult) -> {
TransactionContext newtransactionContext = new TransactionContext(begineTransactionResult.transactionId);
capturedTx.set(newtransactionContext.transactionId);
return executeStatementAsyncInternal(statement, context, newtransactionContext, true);
});
finalResult.whenComplete((xx, error) -> {
if (!wasHoldingTableSpaceLock) {
releaseReadLock(context.getTableSpaceLock(), "begin implicit transaction");
}
long txId = capturedTx.get();
if (error != null && txId > 0) {
LOGGER.log(Level.FINE, tableSpaceName + " force rollback of implicit transaction " + txId, error);
try {
rollbackTransaction(new RollbackTransactionStatement(tableSpaceName, txId), context).get();
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, tableSpaceName + " Cannot rollback implicit tx " + txId, ex);
Thread.currentThread().interrupt();
error.addSuppressed(ex);
} catch (ExecutionException ex) {
LOGGER.log(Level.SEVERE, tableSpaceName + " Cannot rollback implicit tx " + txId, ex.getCause());
error.addSuppressed(ex.getCause());
} catch (Throwable t) {
LOGGER.log(Level.SEVERE, tableSpaceName + " Cannot rollback implicittx " + txId, t);
error.addSuppressed(t);
}
}
});
return finalResult;
} else {
return executeStatementAsyncInternal(statement, context, transactionContext, false);
}
}
Aggregations