diff --git a/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/PostgreConstants.java b/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/PostgreConstants.java index 876931e321b71e335f12247c97aad862778042ae..43163829784914e7dd3744d549436469ee0803c3 100644 --- a/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/PostgreConstants.java +++ b/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/PostgreConstants.java @@ -88,7 +88,7 @@ public class PostgreConstants { public static final String HANDLER_SSL = "postgre_ssl"; /** - * @see https://www.postgresql.org/docs/9.2/static/errcodes-appendix.html + * @see [https://www.postgresql.org/docs/9.2/static/errcodes-appendix.html] */ public static final String EC_PERMISSION_DENIED = "42501"; //$NON-NLS-1$ public static final String EC_QUERY_CANCELED = "57014"; //$NON-NLS-1$ @@ -111,6 +111,8 @@ public class PostgreConstants { public static final String TYPE_FLOAT8 = "float8"; public static final String ERROR_ADMIN_SHUTDOWN = "57P01"; + public static final String ERROR_TRANSACTION_ABORTED = "25P02"; + public static final String PSQL_EXCEPTION_CLASS_NAME = "org.postgresql.util.PSQLException"; public static final String COLLATION_DEFAULT = "default"; diff --git a/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/model/PostgreDataSource.java b/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/model/PostgreDataSource.java index 9a83011ef0ed81cad9343f1b27a63d7579579a25..0acdfbbdaf2afaf766534768615375bb3868b6f9 100644 --- a/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/model/PostgreDataSource.java +++ b/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/model/PostgreDataSource.java @@ -550,11 +550,13 @@ public class PostgreDataSource extends JDBCDataSource implements DBSObjectSelect } @Override - public ErrorType discoverErrorType(Throwable error) { + public ErrorType discoverErrorType(@NotNull Throwable error) { String sqlState = SQLState.getStateFromException(error); if (sqlState != null) { if (PostgreConstants.ERROR_ADMIN_SHUTDOWN.equals(sqlState)) { return ErrorType.CONNECTION_LOST; + } else if (PostgreConstants.ERROR_TRANSACTION_ABORTED.equals(sqlState)) { + return ErrorType.TRANSACTION_ABORTED; } } diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/DBPErrorAssistant.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/DBPErrorAssistant.java index 3204bd3580f2d619e6b2db3102143a032734a441..c796c1b006fa96d7b17d095c0aae1e6cf7849fd2 100644 --- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/DBPErrorAssistant.java +++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/DBPErrorAssistant.java @@ -32,7 +32,8 @@ public interface DBPErrorAssistant CONNECTION_LOST, DRIVER_CLASS_MISSING, PERMISSION_DENIED, - FEATURE_UNSUPPORTED + FEATURE_UNSUPPORTED, + TRANSACTION_ABORTED } class ErrorPosition diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/exec/DBExecUtils.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/exec/DBExecUtils.java index 84d0679f90da8b018914b1d444943a2e2cb167b5..56a0c3af4dd9b3fab5001e4c60c0c03966ee653e 100644 --- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/exec/DBExecUtils.java +++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/exec/DBExecUtils.java @@ -148,10 +148,15 @@ public class DBExecUtils { break; } catch (InvocationTargetException e) { lastError = e.getTargetException(); - if (!recoverEnabled || discoverErrorType(dataSource, lastError) != DBPErrorAssistant.ErrorType.CONNECTION_LOST) { + if (!recoverEnabled) { // Can't recover break; } + DBPErrorAssistant.ErrorType errorType = discoverErrorType(dataSource, lastError); + if (errorType != DBPErrorAssistant.ErrorType.TRANSACTION_ABORTED && errorType != DBPErrorAssistant.ErrorType.CONNECTION_LOST) { + // Some other error + break; + } log.debug("Invalidate datasource '" + dataSource.getContainer().getName() + "' connections..."); DBRProgressMonitor monitor; if (param instanceof DBRProgressMonitor) { @@ -162,11 +167,17 @@ public class DBExecUtils { monitor = new VoidProgressMonitor(); } if (!monitor.isCanceled()) { - // Do not recover if connection was canceled - InvalidateJob.invalidateDataSource(monitor, dataSource, false, - () -> DBWorkbench.getPlatformUI().openConnectionEditor(dataSource.getContainer())); - if (i < tryCount - 1) { - log.error("Operation failed. Retry count remains = " + (tryCount - i - 1), lastError); + + if (errorType == DBPErrorAssistant.ErrorType.TRANSACTION_ABORTED) { + // Transaction aborted + InvalidateJob.invalidateTransaction(monitor, dataSource); + } else { + // Do not recover if connection was canceled + InvalidateJob.invalidateDataSource(monitor, dataSource, false, + () -> DBWorkbench.getPlatformUI().openConnectionEditor(dataSource.getContainer())); + if (i < tryCount - 1) { + log.error("Operation failed. Retry count remains = " + (tryCount - i - 1), lastError); + } } } } catch (InterruptedException e) { diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/runtime/jobs/InvalidateJob.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/runtime/jobs/InvalidateJob.java index 18508fbb5a263d7e278a1649f8142f7c26ac80f1..204bf6c75791ac6b2150891cd0be76e15672c242 100644 --- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/runtime/jobs/InvalidateJob.java +++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/runtime/jobs/InvalidateJob.java @@ -22,7 +22,8 @@ import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.DBPMessageType; -import org.jkiss.dbeaver.model.exec.DBCExecutionContext; +import org.jkiss.dbeaver.model.DBUtils; +import org.jkiss.dbeaver.model.exec.*; import org.jkiss.dbeaver.model.net.DBWNetworkHandler; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.struct.DBSInstance; @@ -182,6 +183,32 @@ public class InvalidateJob extends DataSourceJob return invalidateResults; } + public static void invalidateTransaction(DBRProgressMonitor monitor, DBPDataSource dataSource) { + // Invalidate transactions + monitor.subTask("Invalidate transactions of [" + dataSource.getContainer().getName() + "]"); + for (DBSInstance instance : dataSource.getAvailableInstances()) { + for (DBCExecutionContext context : instance.getAllContexts()) { + invalidateTransaction(monitor, context); + } + } + } + + public static void invalidateTransaction(DBRProgressMonitor monitor, DBCExecutionContext context) { + DBCTransactionManager txnManager = DBUtils.getTransactionManager(context); + if (txnManager != null) { + try { + if (!txnManager.isAutoCommit()) { + try (DBCSession session = context.openSession(monitor, DBCExecutionPurpose.UTIL, "Rollback failed transaction")) { + txnManager.rollback(session, null); + } + } + } catch (DBCException e) { + log.error("Error invalidating aborted transaction", e); + } + } + } + + @Override protected void canceling() {