Hi Hackers,

Attached is the workaround patch to set the default connection timeout to
30 seconds instead of the current value of infinite - 0, zero. The input is
present in the server properties dialogs advanced tab. The workaround
applies to the RM4724 as well.
The permanent solution would be to expand the nodes asynchronously -
https://redmine.postgresql.org/issues/4933

Also fixed a bug where sql editor is not closing if the server is
disconnected in between.

Kindly review.

-- 
Thanks and Regards,
Aditya Toshniwal
Sr. Software Engineer | EnterpriseDB India | Pune
"Don't Complain about Heat, Plant a TREE"
diff --git a/web/migrations/versions/aff1436e3c8c_.py b/web/migrations/versions/aff1436e3c8c_.py
new file mode 100644
index 000000000..b5952418b
--- /dev/null
+++ b/web/migrations/versions/aff1436e3c8c_.py
@@ -0,0 +1,27 @@
+
+""" Update the default timeout to 30 seconds instead on 0.
+0 indicates wait indefinitely which causes trouble when network connection
+to server is lost.
+
+Revision ID: aff1436e3c8c
+Revises: a77a0932a568
+Create Date: 2019-10-28 12:47:36.828709
+
+"""
+from pgadmin.model import db
+
+# revision identifiers, used by Alembic.
+revision = 'aff1436e3c8c'
+down_revision = 'a77a0932a568'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    db.engine.execute(
+        'UPDATE server SET connect_timeout=30 WHERE connect_timeout=0 OR connect_timeout IS NULL'
+    )
+
+
+def downgrade():
+    pass
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index dc39a1393..72f9e11a8 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -28,6 +28,7 @@ from pgadmin.model import db, Server, ServerGroup, User
 from pgadmin.utils.driver import get_driver
 from pgadmin.utils.master_password import get_crypt_key
 from pgadmin.utils.exception import CryptKeyMissing
+from psycopg2 import OperationalError
 
 
 def has_any(data, keys):
@@ -128,12 +129,20 @@ class ServerModule(sg.ServerGroupPluginModule):
             except CryptKeyMissing:
                 # show the nodes at least even if not able to connect.
                 pass
+            except OperationalError as e:
+                current_app.logger.exception(e)
+                connected = False
 
             in_recovery = None
             wal_paused = None
 
             if connected:
-                in_recovery, wal_paused = recovery_state(conn, manager.version)
+                try:
+                    in_recovery, wal_paused = recovery_state(conn, manager.version)
+                except OperationalError as e:
+                    current_app.logger.exception(e)
+                    connected = False
+
             yield self.generate_browser_node(
                 "%d" % (server.id),
                 gid,
diff --git a/web/pgadmin/browser/server_groups/servers/static/js/server.js b/web/pgadmin/browser/server_groups/servers/static/js/server.js
index 99fed949e..99297d4da 100644
--- a/web/pgadmin/browser/server_groups/servers/static/js/server.js
+++ b/web/pgadmin/browser/server_groups/servers/static/js/server.js
@@ -732,7 +732,7 @@ define('pgadmin.node.server', [
           tunnel_password: undefined,
           tunnel_authentication: 0,
           save_tunnel_password: false,
-          connect_timeout: 0,
+          connect_timeout: 30,
         },
         // Default values!
         initialize: function(attrs, args) {
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index 629e98f37..b22868fbb 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -29,7 +29,7 @@ from flask_sqlalchemy import SQLAlchemy
 #
 ##########################################################################
 
-SCHEMA_VERSION = 23
+SCHEMA_VERSION = 24
 
 ##########################################################################
 #
diff --git a/web/pgadmin/static/js/sqleditor/execute_query.js b/web/pgadmin/static/js/sqleditor/execute_query.js
index f714c2b50..63ad7721b 100644
--- a/web/pgadmin/static/js/sqleditor/execute_query.js
+++ b/web/pgadmin/static/js/sqleditor/execute_query.js
@@ -12,6 +12,7 @@ import $ from 'jquery';
 import url_for from '../url_for';
 import axios from 'axios';
 import * as httpErrorHandler from './query_tool_http_error_handler';
+import * as queryTxnStatus from 'sources/sqleditor/query_txn_status_constants';
 
 class LoadingScreen {
   constructor(sqlEditor) {
@@ -83,7 +84,8 @@ class ExecuteQuery {
           self.loadingScreen.hide();
           self.enableSQLEditorButtons();
           // Enable/Disable commit and rollback button.
-          if (result.data.data.transaction_status == 2 || result.data.data.transaction_status == 3) {
+          if (result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
+            || result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
             self.enableTransactionButtons();
           } else {
             self.disableTransactionButtons();
@@ -123,7 +125,8 @@ class ExecuteQuery {
         self.updateSqlEditorLastTransactionStatus(httpMessage.data.data.transaction_status);
 
         // Enable/Disable commit and rollback button.
-        if (httpMessage.data.data.transaction_status == 2 || httpMessage.data.data.transaction_status == 3) {
+        if (httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
+          || httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
           self.enableTransactionButtons();
         } else {
           self.disableTransactionButtons();
@@ -131,7 +134,7 @@ class ExecuteQuery {
 
         if (ExecuteQuery.isQueryFinished(httpMessage)) {
           if (this.sqlServerObject.close_on_idle_transaction &&
-              httpMessage.data.data.transaction_status == 0)
+              httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_IDLE)
             this.sqlServerObject.check_needed_confirmations_before_closing_panel();
 
           self.loadingScreen.setMessage('Loading data from the database server and rendering...');
diff --git a/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js b/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js
new file mode 100644
index 000000000..075264296
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js
@@ -0,0 +1,11 @@
+/* psycopg2 transaction status constants
+ * http://initd.org/psycopg/docs/extensions.html#transaction-status-constants
+ */
+
+module.exports = {
+  TRANSACTION_STATUS_IDLE: 0,
+  TRANSACTION_STATUS_ACTIVE: 1,
+  TRANSACTION_STATUS_INTRANS: 2,
+  TRANSACTION_STATUS_INERROR: 3,
+  TRANSACTION_STATUS_UNKNOWN: 5,
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 70d4bd348..5512382d4 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -36,6 +36,7 @@ define('tools.querytool', [
   'sources/sqleditor/calculate_query_run_time',
   'sources/sqleditor/call_render_after_poll',
   'sources/sqleditor/query_tool_preferences',
+  'sources/sqleditor/query_txn_status_constants',
   'sources/csrf',
   'tools/datagrid/static/js/datagrid_panel_title',
   'sources/window',
@@ -52,7 +53,7 @@ define('tools.querytool', [
   XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
   GeometryViewer, historyColl, queryHist, querySources,
   keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
-  modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, csrfToken, panelTitleFunc,
+  modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
   pgWindow) {
   /* Return back, this has been called more than once */
   if (pgAdmin.SqlEditor)
@@ -4178,8 +4179,9 @@ define('tools.querytool', [
           self.unsaved_changes_user_confirmation(msg, false);
         } // If a transaction is currently ongoing
         else if (self.preferences.prompt_commit_transaction
-                 && self.last_transaction_status > 0) { // 0 -> idle (no transaction)
-          var is_commit_disabled = self.last_transaction_status == 3;  // 3 -> Failed transaction
+                 && (self.last_transaction_status === queryTxnStatus.TRANSACTION_STATUS_INTRANS
+                    || self.last_transaction_status === queryTxnStatus.TRANSACTION_STATUS_INERROR)) {
+          var is_commit_disabled = self.last_transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR;
           self.uncommitted_transaction_user_confirmation(is_commit_disabled);
         }
         else {

Reply via email to