Search in sources :

Example 11 with TableMeta

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);
            }
        }
    }
}
Also used : SQLException(java.sql.SQLException) Connection(java.sql.Connection) PreparedStatement(java.sql.PreparedStatement) SQLIntegrityConstraintViolationException(java.sql.SQLIntegrityConstraintViolationException) ResultSet(java.sql.ResultSet) TableMeta(io.seata.rm.datasource.sql.struct.TableMeta) BranchTransactionException(io.seata.core.exception.BranchTransactionException)

Example 12 with TableMeta

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;
}
Also used : ColumnMeta(io.seata.rm.datasource.sql.struct.ColumnMeta) ResultSet(java.sql.ResultSet) ShouldNeverHappenException(io.seata.common.exception.ShouldNeverHappenException) IndexMeta(io.seata.rm.datasource.sql.struct.IndexMeta) TableMeta(io.seata.rm.datasource.sql.struct.TableMeta)

Example 13 with TableMeta

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);
}
Also used : ParametersHolder(io.seata.sqlparser.ParametersHolder) SQLRecognizer(io.seata.sqlparser.SQLRecognizer) ArrayList(java.util.ArrayList) ArrayList(java.util.ArrayList) List(java.util.List) TableMeta(io.seata.rm.datasource.sql.struct.TableMeta) SQLDeleteRecognizer(io.seata.sqlparser.SQLDeleteRecognizer) NotSupportYetException(io.seata.common.exception.NotSupportYetException) StringJoiner(java.util.StringJoiner)

Example 14 with TableMeta

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);
}
Also used : ArrayList(java.util.ArrayList) ArrayList(java.util.ArrayList) List(java.util.List) TableMeta(io.seata.rm.datasource.sql.struct.TableMeta)

Example 15 with TableMeta

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;
}
Also used : Caffeine(com.github.benmanes.caffeine.cache.Caffeine) Connection(java.sql.Connection) Logger(org.slf4j.Logger) TableMetaCache(io.seata.rm.datasource.sql.struct.TableMetaCache) LoggerFactory(org.slf4j.LoggerFactory) Cache(com.github.benmanes.caffeine.cache.Cache) ConcurrentMap(java.util.concurrent.ConcurrentMap) TimeUnit(java.util.concurrent.TimeUnit) RootContext(io.seata.core.context.RootContext) TableMeta(io.seata.rm.datasource.sql.struct.TableMeta) SQLException(java.sql.SQLException) Map(java.util.Map) StringUtils(io.seata.common.util.StringUtils) ShouldNeverHappenException(io.seata.common.exception.ShouldNeverHappenException) SQLException(java.sql.SQLException) ShouldNeverHappenException(io.seata.common.exception.ShouldNeverHappenException) TableMeta(io.seata.rm.datasource.sql.struct.TableMeta)

Aggregations

TableMeta (io.seata.rm.datasource.sql.struct.TableMeta)38 ArrayList (java.util.ArrayList)15 TableRecords (io.seata.rm.datasource.sql.struct.TableRecords)14 Test (org.junit.jupiter.api.Test)12 Row (io.seata.rm.datasource.sql.struct.Row)11 Field (io.seata.rm.datasource.sql.struct.Field)8 ResultSet (java.sql.ResultSet)7 DataSourceProxy (io.seata.rm.datasource.DataSourceProxy)6 SQLUndoLog (io.seata.rm.datasource.undo.SQLUndoLog)6 DruidDataSource (com.alibaba.druid.pool.DruidDataSource)5 ConnectionProxy (io.seata.rm.datasource.ConnectionProxy)5 PreparedStatementProxy (io.seata.rm.datasource.PreparedStatementProxy)5 MockDriver (io.seata.rm.datasource.mock.MockDriver)5 SQLInsertRecognizer (io.seata.sqlparser.SQLInsertRecognizer)5 PreparedStatement (java.sql.PreparedStatement)5 List (java.util.List)5 BeforeEach (org.junit.jupiter.api.BeforeEach)5 ShouldNeverHappenException (io.seata.common.exception.ShouldNeverHappenException)4 ColumnMeta (io.seata.rm.datasource.sql.struct.ColumnMeta)4 MySQLInsertExecutor (io.seata.rm.datasource.exec.mysql.MySQLInsertExecutor)3