use of io.seata.rm.datasource.sql.struct.TableMeta in project seata by seata.
the class AbstractUndoLogManager method undo.
/**
* Undo.
*
* @param dataSourceProxy the data source proxy
* @param xid the xid
* @param branchId the branch id
* @throws TransactionException the transaction exception
*/
@Override
public void undo(DataSourceProxy dataSourceProxy, String xid, long branchId) throws TransactionException {
Connection conn = null;
ResultSet rs = null;
PreparedStatement selectPST = null;
boolean originalAutoCommit = true;
for (; ; ) {
try {
conn = dataSourceProxy.getPlainConnection();
// The entire undo process should run in a local transaction.
if (originalAutoCommit = conn.getAutoCommit()) {
conn.setAutoCommit(false);
}
// Find UNDO LOG
selectPST = conn.prepareStatement(SELECT_UNDO_LOG_SQL);
selectPST.setLong(1, branchId);
selectPST.setString(2, xid);
rs = selectPST.executeQuery();
boolean exists = false;
while (rs.next()) {
exists = true;
// It is possible that the server repeatedly sends a rollback request to roll back
// the same branch transaction to multiple processes,
// ensuring that only the undo_log in the normal state is processed.
int state = rs.getInt(ClientTableColumnsName.UNDO_LOG_LOG_STATUS);
if (!canUndo(state)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, ignore {} undo_log", xid, branchId, state);
}
return;
}
String contextString = rs.getString(ClientTableColumnsName.UNDO_LOG_CONTEXT);
Map<String, String> context = parseContext(contextString);
byte[] rollbackInfo = getRollbackInfo(rs);
String serializer = context == null ? null : context.get(UndoLogConstants.SERIALIZER_KEY);
UndoLogParser parser = serializer == null ? UndoLogParserFactory.getInstance() : UndoLogParserFactory.getInstance(serializer);
BranchUndoLog branchUndoLog = parser.decode(rollbackInfo);
try {
// put serializer name to local
setCurrentSerializer(parser.getName());
List<SQLUndoLog> sqlUndoLogs = branchUndoLog.getSqlUndoLogs();
if (sqlUndoLogs.size() > 1) {
Collections.reverse(sqlUndoLogs);
}
for (SQLUndoLog sqlUndoLog : sqlUndoLogs) {
TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dataSourceProxy.getDbType()).getTableMeta(conn, sqlUndoLog.getTableName(), dataSourceProxy.getResourceId());
sqlUndoLog.setTableMeta(tableMeta);
AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(dataSourceProxy.getDbType(), sqlUndoLog);
undoExecutor.executeOn(conn);
}
} finally {
// remove serializer name
removeCurrentSerializer();
}
}
if (exists) {
deleteUndoLog(xid, branchId, conn);
conn.commit();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log deleted with {}", xid, branchId, State.GlobalFinished.name());
}
} else {
insertUndoLogWithGlobalFinished(xid, branchId, UndoLogParserFactory.getInstance(), conn);
conn.commit();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log added with {}", xid, branchId, State.GlobalFinished.name());
}
}
return;
} catch (SQLIntegrityConstraintViolationException e) {
// Possible undo_log has been inserted into the database by other processes, retrying rollback undo_log
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log inserted, retry rollback", xid, branchId);
}
} catch (Throwable e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException rollbackEx) {
LOGGER.warn("Failed to close JDBC resource while undo ... ", rollbackEx);
}
}
throw new BranchTransactionException(BranchRollbackFailed_Retriable, String.format("Branch session rollback failed and try again later xid = %s branchId = %s %s", xid, branchId, e.getMessage()), e);
} finally {
try {
if (rs != null) {
rs.close();
}
if (selectPST != null) {
selectPST.close();
}
if (conn != null) {
if (originalAutoCommit) {
conn.setAutoCommit(true);
}
conn.close();
}
} catch (SQLException closeEx) {
LOGGER.warn("Failed to close JDBC resource while undo ... ", closeEx);
}
}
}
}
use of io.seata.rm.datasource.sql.struct.TableMeta in project seata by seata.
the class PostgresqlTableMetaCache method resultSetMetaToSchema.
private TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableName) throws SQLException {
TableMeta tm = new TableMeta();
tm.setTableName(tableName);
String[] schemaTable = tableName.split("\\.");
String schemaName = schemaTable.length > 1 ? schemaTable[0] : null;
tableName = schemaTable.length > 1 ? schemaTable[1] : tableName;
/*
* use ResultSetMetaData to get the pure table name
* can avoid the problem below
*
* select * from account_tbl
* select * from account_TBL
* select * from account_tbl
* select * from account.account_tbl
* select * from "select"
* select * from "Select"
* select * from "Sel""ect"
* select * from "Sel'ect"
* select * from TEST.test
* select * from test.TEST
* select * from "Test".test
* select * from "Test"."Select"
*/
if (schemaName != null) {
if (schemaName.startsWith("\"") && schemaName.endsWith("\"")) {
schemaName = schemaName.replaceAll("(^\")|(\"$)", "");
} else {
schemaName = schemaName.toLowerCase();
}
}
if (tableName.startsWith("\"") && tableName.endsWith("\"")) {
tableName = tableName.replaceAll("(^\")|(\"$)", "");
} else {
tableName = tableName.toLowerCase();
}
try (ResultSet rsColumns = dbmd.getColumns(null, schemaName, tableName, "%");
ResultSet rsIndex = dbmd.getIndexInfo(null, schemaName, tableName, false, true);
ResultSet rsPrimary = dbmd.getPrimaryKeys(null, schemaName, tableName)) {
while (rsColumns.next()) {
ColumnMeta col = new ColumnMeta();
col.setTableCat(rsColumns.getString("TABLE_CAT"));
col.setTableSchemaName(rsColumns.getString("TABLE_SCHEM"));
col.setTableName(rsColumns.getString("TABLE_NAME"));
col.setColumnName(rsColumns.getString("COLUMN_NAME"));
col.setDataType(rsColumns.getInt("DATA_TYPE"));
col.setDataTypeName(rsColumns.getString("TYPE_NAME"));
col.setColumnSize(rsColumns.getInt("COLUMN_SIZE"));
col.setDecimalDigits(rsColumns.getInt("DECIMAL_DIGITS"));
col.setNumPrecRadix(rsColumns.getInt("NUM_PREC_RADIX"));
col.setNullAble(rsColumns.getInt("NULLABLE"));
col.setRemarks(rsColumns.getString("REMARKS"));
col.setColumnDef(rsColumns.getString("COLUMN_DEF"));
col.setSqlDataType(rsColumns.getInt("SQL_DATA_TYPE"));
col.setSqlDatetimeSub(rsColumns.getInt("SQL_DATETIME_SUB"));
col.setCharOctetLength(rsColumns.getObject("CHAR_OCTET_LENGTH"));
col.setOrdinalPosition(rsColumns.getInt("ORDINAL_POSITION"));
col.setIsNullAble(rsColumns.getString("IS_NULLABLE"));
col.setIsAutoincrement(rsColumns.getString("IS_AUTOINCREMENT"));
tm.getAllColumns().put(col.getColumnName(), col);
}
while (rsIndex.next()) {
String indexName = rsIndex.getString("index_name");
if (StringUtils.isNullOrEmpty(indexName)) {
continue;
}
String colName = rsIndex.getString("column_name");
ColumnMeta col = tm.getAllColumns().get(colName);
if (tm.getAllIndexes().containsKey(indexName)) {
IndexMeta index = tm.getAllIndexes().get(indexName);
index.getValues().add(col);
} else {
IndexMeta index = new IndexMeta();
index.setIndexName(indexName);
index.setNonUnique(rsIndex.getBoolean("non_unique"));
index.setIndexQualifier(rsIndex.getString("index_qualifier"));
index.setIndexName(rsIndex.getString("index_name"));
index.setType(rsIndex.getShort("type"));
index.setOrdinalPosition(rsIndex.getShort("ordinal_position"));
index.setAscOrDesc(rsIndex.getString("asc_or_desc"));
index.setCardinality(rsIndex.getInt("cardinality"));
index.getValues().add(col);
if (!index.isNonUnique()) {
index.setIndextype(IndexType.UNIQUE);
} else {
index.setIndextype(IndexType.NORMAL);
}
tm.getAllIndexes().put(indexName, index);
}
}
while (rsPrimary.next()) {
String pkIndexName = rsPrimary.getString("pk_name");
if (tm.getAllIndexes().containsKey(pkIndexName)) {
IndexMeta index = tm.getAllIndexes().get(pkIndexName);
index.setIndextype(IndexType.PRIMARY);
}
}
if (tm.getAllIndexes().isEmpty()) {
throw new ShouldNeverHappenException("Could not found any index in the table: " + tableName);
}
}
return tm;
}
use of io.seata.rm.datasource.sql.struct.TableMeta in project seata by seata.
the class MultiDeleteExecutor method beforeImage.
@Override
protected TableRecords beforeImage() throws SQLException {
if (sqlRecognizers.size() == 1) {
DeleteExecutor executor = new DeleteExecutor(statementProxy, statementCallback, sqlRecognizers.get(0));
return executor.beforeImage();
}
final TableMeta tmeta = getTableMeta(sqlRecognizers.get(0).getTableName());
final ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
StringBuilder whereCondition = new StringBuilder();
for (SQLRecognizer recognizer : sqlRecognizers) {
sqlRecognizer = recognizer;
SQLDeleteRecognizer visitor = (SQLDeleteRecognizer) recognizer;
ParametersHolder parametersHolder = statementProxy instanceof ParametersHolder ? (ParametersHolder) statementProxy : null;
if (StringUtils.isNotBlank(visitor.getLimit(parametersHolder, paramAppenderList))) {
throw new NotSupportYetException("Multi delete SQL with limit condition is not support yet !");
}
if (StringUtils.isNotBlank(visitor.getOrderBy())) {
throw new NotSupportYetException("Multi delete SQL with orderBy condition is not support yet !");
}
String whereConditionStr = buildWhereCondition(visitor, paramAppenderList);
if (StringUtils.isBlank(whereConditionStr)) {
whereCondition = new StringBuilder();
paramAppenderList.clear();
break;
}
if (whereCondition.length() > 0) {
whereCondition.append(" OR ");
}
whereCondition.append(whereConditionStr);
}
StringBuilder suffix = new StringBuilder(" FROM ").append(getFromTableInSQL());
if (whereCondition.length() > 0) {
suffix.append(" WHERE ").append(whereCondition);
}
suffix.append(" FOR UPDATE");
final StringJoiner selectSQLAppender = new StringJoiner(", ", "SELECT ", suffix.toString());
for (String column : tmeta.getAllColumns().keySet()) {
selectSQLAppender.add(getColumnNameInSQL(ColumnUtils.addEscape(column, getDbType())));
}
return buildTableRecords(tmeta, selectSQLAppender.toString(), paramAppenderList);
}
use of io.seata.rm.datasource.sql.struct.TableMeta in project seata by seata.
the class UpdateExecutor method beforeImage.
@Override
protected TableRecords beforeImage() throws SQLException {
ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
TableMeta tmeta = getTableMeta();
String selectSQL = buildBeforeImageSQL(tmeta, paramAppenderList);
return buildTableRecords(tmeta, selectSQL, paramAppenderList);
}
use of io.seata.rm.datasource.sql.struct.TableMeta in project seata by seata.
the class AbstractTableMetaCache method getTableMeta.
@Override
public TableMeta getTableMeta(final Connection connection, final String tableName, String resourceId) {
if (StringUtils.isNullOrEmpty(tableName)) {
throw new IllegalArgumentException("TableMeta cannot be fetched without tableName");
}
TableMeta tmeta;
final String key = getCacheKey(connection, tableName, resourceId);
tmeta = TABLE_META_CACHE.get(key, mappingFunction -> {
try {
return fetchSchema(connection, tableName);
} catch (SQLException e) {
LOGGER.error("get table meta of the table `{}` error: {}", tableName, e.getMessage(), e);
return null;
}
});
if (tmeta == null) {
throw new ShouldNeverHappenException(String.format("[xid:%s]get table meta failed," + " please check whether the table `%s` exists.", RootContext.getXID(), tableName));
}
return tmeta;
}
Aggregations