diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/tests/test_utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/tests/test_utils.py
new file mode 100644
index 00000000..e69de29b
diff --git a/web/pgadmin/static/js/sqleditor/execute_query.js b/web/pgadmin/static/js/sqleditor/execute_query.js
new file mode 100644
index 00000000..e91c9e85
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/execute_query.js
@@ -0,0 +1,287 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import gettext from '../gettext';
+import $ from 'jquery';
+import url_for from '../url_for';
+import axios from 'axios';
+import * as transaction from './is_new_transaction_required';
+
+class LoadingScreen {
+  constructor(sqlEditor) {
+    this.sqlEditor = sqlEditor;
+  }
+
+  setMessage(message) {
+    this.sqlEditor.trigger(
+      'pgadmin-sqleditor:loading-icon:message',
+      gettext(message)
+    );
+  }
+
+  show(withMessage) {
+    this.sqlEditor.trigger(
+      'pgadmin-sqleditor:loading-icon:show',
+      withMessage
+    );
+  }
+
+  hide() {
+    this.sqlEditor.trigger('pgadmin-sqleditor:loading-icon:hide');
+  }
+}
+
+class ExecuteQuery {
+  constructor(sqlEditor, userManagement) {
+    this.sqlServerObject = sqlEditor;
+    this.loadingScreen = new LoadingScreen(sqlEditor);
+    this.userManagement = userManagement;
+  }
+
+  delayedPoll() {
+    const self = this;
+    setTimeout(
+      () => {
+        self.poll();
+      }, self.sqlServerObject.POLL_FALLBACK_TIME());
+  }
+
+  execute(sqlStatement, explainPlan) {
+    // If it is an empty query, do nothing.
+    if (sqlStatement.length <= 0) return;
+
+    const self = this;
+    let service = axios.create({});
+    self.explainPlan = explainPlan;
+
+    const sqlStatementWithAnalyze = ExecuteQuery.prepareAnalyzeSql(sqlStatement, explainPlan);
+
+    self.initializeExecutionOnSqlEditor(sqlStatementWithAnalyze);
+
+    service.post(
+      url_for('sqleditor.query_tool_start', {
+        'trans_id': self.sqlServerObject.transId,
+      }),
+      JSON.stringify(sqlStatementWithAnalyze),
+      {headers: {'Content-Type': 'application/json'}})
+      .then(function (result) {
+        let httpMessageData = result.data;
+        self.removeGridViewMarker();
+
+        if (ExecuteQuery.isSqlCorrect(httpMessageData)) {
+          self.loadingScreen.setMessage('Waiting for the query execution to complete...');
+
+          self.updateSqlEditorStateWithInformationFromServer(httpMessageData.data);
+
+          // If status is True then poll the result.
+          self.delayedPoll();
+        } else {
+          self.loadingScreen.hide();
+          self.enableSQLEditorButtons();
+          self.sqlServerObject.update_msg_history(false, httpMessageData.data.result);
+
+          // Highlight the error in the sql panel
+          self.sqlServerObject._highlight_error(httpMessageData.data.result);
+        }
+      }).catch(function (error) {
+        self.onExecuteHTTPError(error.response.data);
+      }
+    );
+  }
+
+  poll() {
+    const self = this;
+    let service = axios.create({});
+    service.get(
+      url_for('sqleditor.poll', {
+        'trans_id': self.sqlServerObject.transId,
+      })
+    ).then(
+      (httpMessage) => {
+        if (ExecuteQuery.isQueryFinished(httpMessage)) {
+          self.loadingScreen.setMessage('Loading data from the database server and rendering...');
+
+          self.sqlServerObject.call_render_after_poll(httpMessage.data.data);
+        } else if (ExecuteQuery.isQueryStillRunning(httpMessage)) {
+          // If status is Busy then poll the result by recursive call to the poll function
+          this.delayedPoll();
+          self.sqlServerObject.setIsQueryRunning(true);
+          if (httpMessage.data.data.result) {
+            self.sqlServerObject.update_msg_history(httpMessage.data.data.status, httpMessage.data.data.result, false);
+          }
+        } else if (ExecuteQuery.isConnectionToServerLostWhilePolling(httpMessage)) {
+          self.loadingScreen.hide();
+          // Enable/Disable query tool button only if is_query_tool is true.
+          if (self.sqlServerObject.is_query_tool) {
+            self.enableSQLEditorButtons();
+          }
+          self.sqlServerObject.update_msg_history(false, httpMessage.data.data.result, true);
+        } else if (ExecuteQuery.isQueryCancelled(httpMessage)) {
+          self.loadingScreen.hide();
+          self.sqlServerObject.update_msg_history(false, 'Execution Cancelled!', true);
+        }
+      }
+    ).catch(
+      error => {
+        const errorData = error.response.data;
+        // Enable/Disable query tool button only if is_query_tool is true.
+        self.sqlServerObject.resetQueryHistoryObject(self.sqlServerObject);
+        self.loadingScreen.hide();
+        if (self.sqlServerObject.is_query_tool) {
+          self.enableSQLEditorButtons();
+        }
+
+        if (ExecuteQuery.wasConnectionLostToServer(errorData)) {
+          self.handleConnectionToServerLost();
+          return;
+        }
+        if (self.userManagement.is_pga_login_required(errorData)) {
+          return self.userManagement.pga_login();
+        }
+
+        let msg = ExecuteQuery.extractErrorMessage(errorData);
+
+        self.sqlServerObject.update_msg_history(false, msg);
+        // Highlight the error in the sql panel
+        self.sqlServerObject._highlight_error(msg);
+      });
+  }
+
+  initializeExecutionOnSqlEditor(sqlStatement) {
+    this.loadingScreen.show('Initializing query execution...');
+
+    $('#btn-flash').prop('disabled', true);
+
+    this.sqlServerObject.query_start_time = new Date();
+    if(typeof sqlStatement === 'object') {
+      this.sqlServerObject.query = sqlStatement['sql'];
+    } else {
+      this.sqlServerObject.query = sqlStatement;
+    }
+
+    this.sqlServerObject.rows_affected = 0;
+    this.sqlServerObject._init_polling_flags();
+    this.disableSQLEditorButtons();
+  }
+
+  static prepareAnalyzeSql(sqlStatement, analyzeSql) {
+    let sqlStatementWithAnalyze = {
+      sql: sqlStatement,
+      explain_plan: analyzeSql,
+    };
+    return sqlStatementWithAnalyze;
+  }
+
+  onExecuteHTTPError(httpMessage) {
+    this.loadingScreen.hide();
+    this.enableSQLEditorButtons();
+
+    if (ExecuteQuery.wasConnectionLostToServer(httpMessage)) {
+      this.handleConnectionToServerLost();
+      return;
+    }
+
+    if (this.userManagement.is_pga_login_required(httpMessage)) {
+      this.sqlServerObject.save_state('execute', [this.explainPlan]);
+      this.userManagement.pga_login();
+    }
+
+    if (transaction.is_new_transaction_required(httpMessage)) {
+      this.sqlServerObject.save_state('execute', [this.explainPlan]);
+      this.sqlServerObject.init_transaction();
+    }
+
+    let msg = httpMessage.errormsg;
+    if (httpMessage.responseJSON !== undefined) {
+      if (httpMessage.responseJSON.errormsg !== undefined) {
+        msg = httpMessage.responseJSON.errormsg;
+      }
+
+      if (httpMessage.status === 503 && httpMessage.responseJSON.info !== undefined &&
+        httpMessage.responseJSON.info === 'CONNECTION_LOST') {
+        setTimeout(function () {
+          this.sqlServerObject.save_state('execute', [this.explainPlan]);
+          this.sqlServerObject.handle_connection_lost(false, httpMessage);
+        });
+      }
+    }
+
+    this.sqlServerObject.update_msg_history(false, msg);
+  }
+
+  removeGridViewMarker() {
+    if (this.sqlServerObject.gridView.marker) {
+      this.sqlServerObject.gridView.marker.clear();
+      delete this.sqlServerObject.gridView.marker;
+      this.sqlServerObject.gridView.marker = null;
+
+      // Remove already existing marker
+      this.sqlServerObject.gridView.query_tool_obj.removeLineClass(this.sqlServerObject.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
+    }
+  }
+
+  enableSQLEditorButtons() {
+    this.sqlServerObject.disable_tool_buttons(false);
+    $('#btn-cancel-query').prop('disabled', true);
+  }
+
+  disableSQLEditorButtons() {
+    this.sqlServerObject.disable_tool_buttons(true);
+    $('#btn-cancel-query').prop('disabled', false);
+  }
+
+  static wasConnectionLostToServer(errorMessage) {
+    return errorMessage.readyState === 0;
+  }
+
+  handleConnectionToServerLost() {
+    this.sqlServerObject.update_msg_history(false,
+      gettext('Not connected to the server or the connection to the server has been closed.')
+    );
+  }
+
+  updateSqlEditorStateWithInformationFromServer(messageData) {
+    this.sqlServerObject.can_edit = messageData.can_edit;
+    this.sqlServerObject.can_filter = messageData.can_filter;
+    this.sqlServerObject.info_notifier_timeout = messageData.info_notifier_timeout;
+  }
+
+  static isSqlCorrect(httpMessageData) {
+    return httpMessageData.data.status;
+  }
+
+  static extractErrorMessage(httpMessage) {
+    let msg = httpMessage.errormsg;
+    if (httpMessage.responseJSON !== undefined &&
+      httpMessage.responseJSON.errormsg !== undefined)
+      msg = httpMessage.responseJSON.errormsg;
+
+    return msg;
+  }
+
+  static isQueryFinished(httpMessage) {
+    return httpMessage.data.data.status === 'Success';
+  }
+
+  static isQueryStillRunning(httpMessage) {
+    return httpMessage.data.data.status === 'Busy';
+  }
+
+  static isQueryCancelled(httpMessage) {
+    return httpMessage.data.data.status === 'Cancel';
+  }
+
+  static isConnectionToServerLostWhilePolling(httpMessage) {
+    return httpMessage.data.data.status === 'NotConnected';
+  }
+}
+
+module.exports = {
+  ExecuteQuery: ExecuteQuery,
+};
diff --git a/web/pgadmin/static/js/sqleditor/is_new_transaction_required.js b/web/pgadmin/static/js/sqleditor/is_new_transaction_required.js
new file mode 100644
index 00000000..9d83c926
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/is_new_transaction_required.js
@@ -0,0 +1,14 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+export function is_new_transaction_required(xhr) {
+  return xhr.status === 404 && xhr.responseJSON &&
+    xhr.responseJSON.info &&
+    xhr.responseJSON.info === 'DATAGRID_TRANSACTION_REQUIRED';
+}
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_actions.js b/web/pgadmin/static/js/sqleditor/query_tool_actions.js
index 44a674b8..01d19215 100644
--- a/web/pgadmin/static/js/sqleditor/query_tool_actions.js
+++ b/web/pgadmin/static/js/sqleditor/query_tool_actions.js
@@ -2,19 +2,19 @@ import $ from 'jquery';
 
 let queryToolActions = {
   _verbose: function () {
-    return $('.explain-verbose').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+    return !$('.explain-verbose').hasClass('visibility-hidden');
   },
 
   _costsEnabled: function () {
-    return $('.explain-costs').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+    return !$('.explain-costs').hasClass('visibility-hidden');
   },
 
   _buffers: function () {
-    return $('.explain-buffers').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+    return !$('.explain-buffers').hasClass('visibility-hidden');
   },
 
   _timing: function () {
-    return $('.explain-timing').hasClass('visibility-hidden') ? 'OFF' : 'ON';
+    return !$('.explain-timing').hasClass('visibility-hidden');
   },
 
   _clearMessageTab: function () {
@@ -35,18 +35,35 @@ let queryToolActions = {
     let verbose = this._verbose();
     let buffers = this._buffers();
     let timing = this._timing();
-    let explainAnalyzeQuery = `EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE ${verbose}, COSTS ${costEnabled}, BUFFERS ${buffers}, TIMING ${timing}) `;
+    const explainObject = {
+      format: 'json',
+      analyze: true,
+      verbose: verbose,
+      costs: costEnabled,
+      buffers: buffers,
+      timing: timing,
+      summary: false,
+    };
     this._clearMessageTab();
-    sqlEditorController.execute(explainAnalyzeQuery);
+    sqlEditorController.execute(explainObject);
   },
 
   explain: function (sqlEditorController) {
     let costEnabled = this._costsEnabled();
     let verbose = this._verbose();
 
-    let explainQuery = `EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE ${verbose}, COSTS ${costEnabled}, BUFFERS OFF, TIMING OFF) `;
+    // let explainQuery = `EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE ${verbose}, COSTS ${costEnabled}, BUFFERS OFF, TIMING OFF) `;
+    const explainObject = {
+      format: 'json',
+      analyze: false,
+      verbose: verbose,
+      costs: costEnabled,
+      buffers: false,
+      timing: false,
+      summary: false,
+    };
     this._clearMessageTab();
-    sqlEditorController.execute(explainQuery);
+    sqlEditorController.execute(explainObject);
   },
 
   download: function (sqlEditorController) {
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index b1ca3b87..f8870036 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -8,28 +8,32 @@
 ##########################################################################
 
 """A blueprint module implementing the sqleditor frame."""
-import simplejson as json
+import codecs
 import os
 import pickle
 import random
-import codecs
 
-from flask import Response, url_for, render_template, session, request,\
+import simplejson as json
+from flask import Response, url_for, render_template, session, request, \
     current_app
 from flask_babel import gettext
 from flask_security import login_required
+
+from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
+from pgadmin.misc.file_manager import Filemanager
 from pgadmin.tools.sqleditor.command import QueryToolCommand
+from pgadmin.tools.sqleditor.utils.constant_definition import ASYNC_OK, ASYNC_EXECUTION_ABORTED, \
+    CONNECTION_STATUS_MESSAGE_MAPPING, TX_STATUS_INERROR
+from pgadmin.tools.sqleditor.utils.start_running_query import StartRunningQuery
+from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import update_session_grid_transaction
 from pgadmin.utils import PgAdminModule
 from pgadmin.utils import get_storage_directory
 from pgadmin.utils.ajax import make_json_response, bad_request, \
     success_return, internal_server_error, unauthorized
 from pgadmin.utils.driver import get_driver
-from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
-from pgadmin.misc.file_manager import Filemanager
 from pgadmin.utils.menu import MenuItem
 from pgadmin.utils.exception import ConnectionLost
-
-from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
+from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
 
 MODULE_NAME = 'sqleditor'
 
@@ -39,28 +43,6 @@ try:
 except ImportError:
     from urllib.parse import unquote
 
-# Async Constants
-ASYNC_OK = 1
-ASYNC_READ_TIMEOUT = 2
-ASYNC_WRITE_TIMEOUT = 3
-ASYNC_NOT_CONNECTED = 4
-ASYNC_EXECUTION_ABORTED = 5
-
-# Transaction status constants
-TX_STATUS_IDLE = 0
-TX_STATUS__ACTIVE = 1
-TX_STATUS_INTRANS = 2
-TX_STATUS_INERROR = 3
-
-# Connection status codes mapping
-CONNECTION_STATUS_MESSAGE_MAPPING = dict([
-    (0, 'The session is idle and there is no current transaction.'),
-    (1, 'A command is currently in progress.'),
-    (2, 'The session is idle in a valid transaction block.'),
-    (3, 'The session is idle in a failed transaction block.'),
-    (4, 'The connection with the server is bad.')
-])
-
 
 class SqlEditorModule(PgAdminModule):
     """
@@ -376,13 +358,6 @@ def index():
     )
 
 
-def update_session_grid_transaction(trans_id, data):
-    if 'gridData' in session:
-        grid_data = session['gridData']
-        grid_data[str(trans_id)] = data
-        session['gridData'] = grid_data
-
-
 def check_transaction_status(trans_id):
     """
     This function is used to check the transaction id
@@ -458,7 +433,7 @@ def start_view_data(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
@@ -486,7 +461,7 @@ def start_view_data(trans_id):
             )
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
         # set fetched row count to 0 as we are executing query again.
         trans_obj.update_fetched_row_cnt(0)
         session_obj['command_obj'] = pickle.dumps(trans_obj, -1)
@@ -554,107 +529,19 @@ def start_query_tool(trans_id):
         trans_id: unique transaction id
     """
 
-    if request.data:
-        sql = json.loads(request.data, encoding='utf-8')
-    else:
-        sql = request.args or request.form
-
-    connect = True if 'connect' in request.args and \
-                      request.args['connect'] == '1' else False
-
-    if 'gridData' not in session:
-        return make_json_response(
-            success=0,
-            errormsg=gettext('Transaction ID not found in the session.'),
-            info='DATAGRID_TRANSACTION_REQUIRED', status=404)
-
-    grid_data = session['gridData']
-
-    # Return from the function if transaction id not found
-    if str(trans_id) not in grid_data:
-        return make_json_response(
-            success=0,
-            errormsg=gettext('Transaction ID not found in the session.'),
-            info='DATAGRID_TRANSACTION_REQUIRED',
-            status=404)
-
-    # Fetch the object for the specified transaction id.
-    # Use pickle.loads function to get the command object
-    session_obj = grid_data[str(trans_id)]
-    trans_obj = pickle.loads(session_obj['command_obj'])
-    # set fetched row count to 0 as we are executing query again.
-    trans_obj.update_fetched_row_cnt(0)
-
-    can_edit = False
-    can_filter = False
+    sql = extract_sql_from_network_parameters(request.data, request.args, request.form)
 
-    if trans_obj is not None and session_obj is not None:
-        conn_id = trans_obj.conn_id
-        try:
-            manager = get_driver(
-                PG_DEFAULT_DRIVER).connection_manager(trans_obj.sid)
-            conn = manager.connection(did=trans_obj.did, conn_id=conn_id,
-                                      auto_reconnect=False,
-                                      use_binary_placeholder=True,
-                                      array_to_string=True)
-        except ConnectionLost as e:
-            raise
-        except Exception as e:
-            current_app.logger.error(e)
-            return internal_server_error(errormsg=str(e))
+    return StartRunningQuery(blueprint, current_app).execute(sql, trans_id, session)
 
-        # Connect to the Server if not connected.
-        if connect and not conn.connected():
-            status, msg = conn.connect()
-            if not status:
-                current_app.logger.error(msg)
-                return internal_server_error(errormsg=str(msg))
-
-        # on successful connection set the connection id to the
-        # transaction object
-        trans_obj.set_connection_id(conn_id)
-
-        # As we changed the transaction object we need to
-        # restore it and update the session variable.
-        session_obj['command_obj'] = pickle.dumps(trans_obj, -1)
-        update_session_grid_transaction(trans_id, session_obj)
-
-        # If auto commit is False and transaction status is Idle
-        # then call is_begin_not_required() function to check BEGIN
-        # is required or not.
-
-        if not trans_obj.auto_commit \
-                and conn.transaction_status() == TX_STATUS_IDLE \
-                and is_begin_required(sql):
-            conn.execute_void("BEGIN;")
-
-        # Execute sql asynchronously with params is None
-        # and formatted_error is True.
-        try:
-            status, result = conn.execute_async(sql)
-        except ConnectionLost as e:
-            raise
-        # If the transaction aborted for some reason and
-        # Auto RollBack is True then issue a rollback to cleanup.
-        trans_status = conn.transaction_status()
-        if trans_status == TX_STATUS_INERROR and trans_obj.auto_rollback:
-            conn.execute_void("ROLLBACK;")
-
-        can_edit = trans_obj.can_edit()
-        can_filter = trans_obj.can_filter()
 
+def extract_sql_from_network_parameters(request_data, request_arguments, request_form_data):
+    if request_data:
+        sql_parameters = json.loads(request_data, encoding='utf-8')
+        if type(sql_parameters) is str:
+            return dict(sql=sql_parameters, explain_plan=None)
+        return sql_parameters
     else:
-        status = False
-        result = gettext(
-            'Either transaction object or session object not found.')
-
-    return make_json_response(
-        data={
-            'status': status, 'result': result,
-            'can_edit': can_edit, 'can_filter': can_filter,
-            'info_notifier_timeout': blueprint.info_notifier_timeout.get()
-        }
-    )
+        return request_arguments or request_form_data
 
 
 @blueprint.route(
@@ -675,13 +562,13 @@ def preferences(trans_id):
             check_transaction_status(trans_id)
 
         if error_msg == gettext(
-                'Transaction ID not found in the session.'):
+            'Transaction ID not found in the session.'):
             return make_json_response(success=0, errormsg=error_msg,
                                       info='DATAGRID_TRANSACTION_REQUIRED',
                                       status=404)
 
         if status and conn is not None \
-                and trans_obj is not None and session_obj is not None:
+            and trans_obj is not None and session_obj is not None:
             # Call the set_auto_commit and set_auto_rollback method of
             # transaction object
             trans_obj.set_auto_commit(blueprint.auto_commit.get())
@@ -751,7 +638,7 @@ def poll(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
@@ -779,7 +666,7 @@ def poll(trans_id):
             if isinstance(trans_obj, QueryToolCommand):
                 trans_status = conn.transaction_status()
                 if (trans_status == TX_STATUS_INERROR and
-                        trans_obj.auto_rollback):
+                    trans_obj.auto_rollback):
                     conn.execute_void("ROLLBACK;")
 
             st, result = conn.async_fetchmany_2darray(ON_DEMAND_RECORD_COUNT)
@@ -854,15 +741,15 @@ def poll(trans_id):
                                         typname == 'character varying'
                                     ):
                                         typname = typname + '(' + \
-                                            str(col_info['internal_size']) + \
-                                            ')'
+                                                  str(col_info['internal_size']) + \
+                                                  ')'
                                     elif (
                                         typname == 'character[]' or
                                         typname == 'character varying[]'
                                     ):
                                         typname = typname[:-2] + '(' + \
-                                            str(col_info['internal_size']) + \
-                                            ')[]'
+                                                  str(col_info['internal_size']) + \
+                                                  ')[]'
 
                                 col_info['type_name'] = typname
 
@@ -913,7 +800,7 @@ def poll(trans_id):
     if status == 'Success' and result is None:
         result = conn.status_message()
         if (result != 'SELECT 1' or result != 'SELECT 0') \
-                and result is not None and additional_messages:
+            and result is not None and additional_messages:
             result = additional_messages + result
 
     return make_json_response(
@@ -954,7 +841,7 @@ def fetch(trans_id, fetch_all=None):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
@@ -1018,7 +905,7 @@ def fetch_pg_types(columns_info, trans_obj):
 
     if oids:
         status, res = default_conn.execute_dict(
-            u"SELECT oid, format_type(oid,null) as typname FROM pg_type "
+            u"SELECT oid, format_type(oid,NULL) AS typname FROM pg_type "
             u"WHERE oid IN %s ORDER BY oid;", [tuple(oids)]
         )
 
@@ -1077,17 +964,17 @@ def save(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         # If there is no primary key found then return from the function.
         if (len(session_obj['primary_keys']) <= 0 or len(changed_data) <= 0) \
-                and 'has_oids' not in session_obj:
+            and 'has_oids' not in session_obj:
             return make_json_response(
                 data={
                     'status': False,
@@ -1146,12 +1033,12 @@ def get_filter(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         res = trans_obj.get_filter()
     else:
@@ -1183,13 +1070,13 @@ def apply_filter(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         status, res = trans_obj.set_filter(filter_sql)
 
@@ -1226,13 +1113,13 @@ def append_filter_inclusive(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         res = None
         filter_sql = ''
@@ -1282,12 +1169,12 @@ def append_filter_exclusive(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         res = None
         filter_sql = ''
@@ -1335,13 +1222,13 @@ def remove_filter(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         res = None
 
@@ -1380,13 +1267,13 @@ def set_limit(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         res = None
 
@@ -1501,13 +1388,13 @@ def get_object_name(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
         res = trans_obj.object_name
     else:
         status = False
@@ -1538,13 +1425,13 @@ def set_auto_commit(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         res = None
 
@@ -1587,13 +1474,13 @@ def set_auto_rollback(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         res = None
 
@@ -1643,13 +1530,13 @@ def auto_complete(trans_id):
         check_transaction_status(trans_id)
 
     if error_msg == gettext(
-            'Transaction ID not found in the session.'):
+        'Transaction ID not found in the session.'):
         return make_json_response(success=0, errormsg=error_msg,
                                   info='DATAGRID_TRANSACTION_REQUIRED',
                                   status=404)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         # Create object of SQLAutoComplete class and pass connection object
         auto_complete_obj = SQLAutoComplete(
@@ -1680,165 +1567,6 @@ def script():
     )
 
 
-def is_begin_required(query):
-    word_len = 0
-    query = query.strip()
-    query_len = len(query)
-
-    # Check word length (since "beginx" is not "begin").
-    while (word_len < query_len) and query[word_len].isalpha():
-        word_len += 1
-
-    # Transaction control commands.  These should include every keyword that
-    #  gives rise to a TransactionStmt in the backend grammar, except for the
-    #  savepoint-related commands.
-    #
-    #  (We assume that START must be START TRANSACTION, since there is
-    #  presently no other "START foo" command.)
-
-    keyword = query[0:word_len]
-
-    if word_len == 5 and keyword.lower() == "abort":
-        return False
-    if word_len == 5 and keyword.lower() == "begin":
-        return False
-    if word_len == 5 and keyword.lower() == "start":
-        return False
-    if word_len == 6:
-        # SELECT is protected from dirty reads hence don't require transaction
-        if keyword.lower() in ["select", "commit"]:
-            return False
-    if word_len == 3 and keyword.lower() == "end":
-        return False
-    if word_len == 8 and keyword.lower() == "rollback":
-        return False
-    if word_len == 7 and keyword.lower() == "prepare":
-        # PREPARE TRANSACTION is a TC command, PREPARE foo is not
-        query = query[word_len:query_len]
-        query = query.strip()
-        query_len = len(query)
-        word_len = 0
-
-        while (word_len < query_len) and query[word_len].isalpha():
-            word_len += 1
-
-        keyword = query[0:word_len]
-        if word_len == 11 and keyword.lower() == "transaction":
-            return False
-        return True
-
-    # Commands not allowed within transactions. The statements checked for
-    # here should be exactly those that call PreventTransactionChain() in the
-    # backend.
-    if word_len == 6 and keyword.lower() == "vacuum":
-        return False
-
-    if word_len == 7 and keyword.lower() == "cluster":
-        # CLUSTER with any arguments is allowed in transactions
-        query = query[word_len:query_len]
-        query = query.strip()
-
-        if query[0].isalpha():
-            return True  # has additional words
-        return False  # it's CLUSTER without arguments
-
-    if word_len == 6 and keyword.lower() == "create":
-        query = query[word_len:query_len]
-        query = query.strip()
-        query_len = len(query)
-        word_len = 0
-
-        while (word_len < query_len) and query[word_len].isalpha():
-            word_len += 1
-
-        keyword = query[0:word_len]
-        if word_len == 8 and keyword.lower() == "database":
-            return False
-        if word_len == 10 and keyword.lower() == "tablespace":
-            return False
-
-        # CREATE [UNIQUE] INDEX CONCURRENTLY isn't allowed in xacts
-        if word_len == 7 and keyword.lower() == "cluster":
-            query = query[word_len:query_len]
-            query = query.strip()
-            query_len = len(query)
-            word_len = 0
-
-            while (word_len < query_len) and query[word_len].isalpha():
-                word_len += 1
-
-            keyword = query[0:word_len]
-
-        if word_len == 5 and keyword.lower() == "index":
-            query = query[word_len:query_len]
-            query = query.strip()
-            query_len = len(query)
-            word_len = 0
-
-            while (word_len < query_len) and query[word_len].isalpha():
-                word_len += 1
-
-            keyword = query[0:word_len]
-            if word_len == 12 and keyword.lower() == "concurrently":
-                return False
-        return True
-
-    if word_len == 5 and keyword.lower() == "alter":
-        query = query[word_len:query_len]
-        query = query.strip()
-        query_len = len(query)
-        word_len = 0
-
-        while (word_len < query_len) and query[word_len].isalpha():
-            word_len += 1
-
-        keyword = query[0:word_len]
-
-        # ALTER SYSTEM isn't allowed in xacts
-        if word_len == 6 and keyword.lower() == "system":
-            return False
-        return True
-
-    # Note: these tests will match DROP SYSTEM and REINDEX TABLESPACE, which
-    # aren't really valid commands so we don't care much. The other four
-    # possible matches are correct.
-    if word_len == 4 and keyword.lower() == "drop" \
-            or word_len == 7 and keyword.lower() == "reindex":
-        query = query[word_len:query_len]
-        query = query.strip()
-        query_len = len(query)
-        word_len = 0
-
-        while (word_len < query_len) and query[word_len].isalpha():
-            word_len += 1
-
-        keyword = query[0:word_len]
-        if word_len == 8 and keyword.lower() == "database":
-            return False
-        if word_len == 6 and keyword.lower() == "system":
-            return False
-        if word_len == 10 and keyword.lower() == "tablespace":
-            return False
-        return True
-
-    # DISCARD ALL isn't allowed in xacts, but other variants are allowed.
-    if word_len == 7 and keyword.lower() == "discard":
-        query = query[word_len:query_len]
-        query = query.strip()
-        query_len = len(query)
-        word_len = 0
-
-        while (word_len < query_len) and query[word_len].isalpha():
-            word_len += 1
-
-        keyword = query[0:word_len]
-        if word_len == 3 and keyword.lower() == "all":
-            return False
-        return True
-
-    return True
-
-
 @blueprint.route('/load_file/', methods=["PUT", "POST"], endpoint='load_file')
 @login_required
 def load_file():
@@ -1865,9 +1593,9 @@ def load_file():
         )
 
     status, err_msg, is_binary, \
-        is_startswith_bom, enc = Filemanager.check_file_for_bom_and_binary(
-            file_path
-        )
+    is_startswith_bom, enc = Filemanager.check_file_for_bom_and_binary(
+        file_path
+    )
 
     if not status:
         return internal_server_error(
@@ -1960,10 +1688,10 @@ def save_file():
 def start_query_download_tool(trans_id):
     sync_conn = None
     status, error_msg, conn, trans_obj, \
-        session_obj = check_transaction_status(trans_id)
+    session_obj = check_transaction_status(trans_id)
 
     if status and conn is not None \
-            and trans_obj is not None and session_obj is not None:
+        and trans_obj is not None and session_obj is not None:
 
         data = request.args if request.args else None
         try:
@@ -2063,7 +1791,7 @@ def query_tool_status(trans_id):
         TRANSACTION_STATUS_UNKNOWN  = 4
     """
     status, error_msg, conn, trans_obj, \
-        session_obj = check_transaction_status(trans_id)
+    session_obj = check_transaction_status(trans_id)
 
     if not status and error_msg and type(error_msg) == str:
         return internal_server_error(
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 52109ecd..349c9a00 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -12,6 +12,8 @@ define('tools.querytool', [
   'sources/selection/xcell_selection_model',
   'sources/selection/set_staged_rows',
   'sources/sqleditor_utils',
+  'sources/sqleditor/execute_query',
+  'sources/sqleditor/is_new_transaction_required',
   'sources/history/index.js',
   'sources/../jsx/history/query_history',
   'react', 'react-dom',
@@ -28,7 +30,8 @@ define('tools.querytool', [
 ], function(
   babelPollyfill, gettext, url_for, $, _, S, alertify, pgAdmin, Backbone, codemirror,
   pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
-  XCellSelectionModel, setStagedRows, SqlEditorUtils, HistoryBundle, queryHistory, React, ReactDOM,
+  XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, transaction,
+  HistoryBundle, queryHistory, React, ReactDOM,
   keyboardShortcuts, queryToolActions, Datagrid) {
   /* Return back, this has been called more than once */
   if (pgAdmin.SqlEditor)
@@ -43,12 +46,6 @@ define('tools.querytool', [
 
   var is_query_running = false;
 
-  var is_new_transaction_required = function(xhr) {
-    return xhr.status == 404 && xhr.responseJSON &&
-                xhr.responseJSON.info &&
-                xhr.responseJSON.info == 'DATAGRID_TRANSACTION_REQUIRED';
-  };
-
   // Defining Backbone view for the sql grid.
   var SQLEditorView = Backbone.View.extend({
     initialize: function(opts) {
@@ -485,7 +482,7 @@ define('tools.querytool', [
                 if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) {
                   return pgAdmin.Browser.UserManagement.pga_login();
                 }
-                if(is_new_transaction_required(e)) {
+                if(transaction.is_new_transaction_required(e)) {
                   return self.init_transaction();
                 }
               },
@@ -2188,7 +2185,7 @@ define('tools.querytool', [
               pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_run_query', []);
               self.init_transaction();
             }
@@ -2256,74 +2253,8 @@ define('tools.querytool', [
        * 'Success' then call the render method to render the data.
        */
       _poll: function() {
-        var self = this;
-
-        setTimeout(
-          function() {
-            $.ajax({
-              url: url_for('sqleditor.poll', {
-                'trans_id': self.transId,
-              }),
-              method: 'GET',
-              success: function(res) {
-                if (res.data.status === 'Success') {
-                  self.trigger(
-                    'pgadmin-sqleditor:loading-icon:message',
-                    gettext('Loading data from the database server and rendering...')
-                  );
-
-                  self.call_render_after_poll(res.data);
-                } else if (res.data.status === 'Busy') {
-                  // If status is Busy then poll the result by recursive call to the poll function
-                  self._poll();
-                  is_query_running = true;
-                  if (res.data.result) {
-                    self.update_msg_history(res.data.status, res.data.result, false);
-                  }
-                } else if (res.data.status === 'NotConnected') {
-                  self.trigger('pgadmin-sqleditor:loading-icon:hide');
-                  // Enable/Disable query tool button only if is_query_tool is true.
-                  if (self.is_query_tool) {
-                    self.disable_tool_buttons(false);
-                    $('#btn-cancel-query').prop('disabled', true);
-                  }
-                  self.update_msg_history(false, res.data.result, true);
-                } else if (res.data.status === 'Cancel') {
-                  self.trigger('pgadmin-sqleditor:loading-icon:hide');
-                  self.update_msg_history(false, 'Execution Cancelled!', true);
-                }
-              },
-              error: function(e) {
-                // Enable/Disable query tool button only if is_query_tool is true.
-                self.resetQueryHistoryObject(self);
-                self.trigger('pgadmin-sqleditor:loading-icon:hide');
-                if (self.is_query_tool) {
-                  self.disable_tool_buttons(false);
-                  $('#btn-cancel-query').prop('disabled', true);
-                }
-
-                if (e.readyState == 0) {
-                  self.update_msg_history(false,
-                    gettext('Not connected to the server or the connection to the server has been closed.')
-                  );
-                  return;
-                }
-
-                if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) {
-                  return pgAdmin.Browser.UserManagement.pga_login();
-                }
-
-                var msg = e.responseText;
-                if (e.responseJSON != undefined &&
-                  e.responseJSON.errormsg != undefined)
-                  msg = e.responseJSON.errormsg;
-
-                self.update_msg_history(false, msg);
-                // Highlight the error in the sql panel
-                self._highlight_error(msg);
-              },
-            });
-          }, self.POLL_FALLBACK_TIME());
+        const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement);
+        executeQuery.delayedPoll(this);
       },
 
       /* This function is used to create the backgrid columns,
@@ -2941,7 +2872,7 @@ define('tools.querytool', [
                 pgAdmin.Browser.UserManagement.pga_login();
               }
 
-              if(is_new_transaction_required(e)) {
+              if(transaction.is_new_transaction_required(e)) {
                 self.save_state('_save', [view, controller, save_as]);
                 self.init_transaction();
               }
@@ -3267,7 +3198,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_show_filter', []);
               return self.init_transaction();
             }
@@ -3356,7 +3287,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_include_filter', []);
               return self.init_transaction();
             }
@@ -3447,7 +3378,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_exclude_filter', []);
               return self.init_transaction();
             }
@@ -3517,7 +3448,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_remove_filter', []);
               return self.init_transaction();
             }
@@ -3592,7 +3523,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_apply_filter', []);
               return self.init_transaction();
             }
@@ -3747,7 +3678,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_set_limit', []);
               return self.init_transaction();
             }
@@ -3795,10 +3726,7 @@ define('tools.querytool', [
       // and execute the query.
       execute: function(explain_prefix) {
         var self = this,
-          sql = '',
-          url = url_for('sqleditor.query_tool_start', {
-            'trans_id': self.transId,
-          });
+          sql = '';
 
         self.has_more_rows = false;
         self.fetching_rows = false;
@@ -3812,109 +3740,8 @@ define('tools.querytool', [
         else
           sql = self.gridView.query_tool_obj.getValue();
 
-        // If it is an empty query, do nothing.
-        if (sql.length <= 0) return;
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Initializing query execution...')
-        );
-
-        $('#btn-flash').prop('disabled', true);
-
-        if (explain_prefix != undefined &&
-          !S.startsWith(sql.trim().toUpperCase(), 'EXPLAIN')) {
-          sql = explain_prefix + ' ' + sql;
-        }
-
-        self.query_start_time = new Date();
-        self.query = sql;
-        self.rows_affected = 0;
-        self._init_polling_flags();
-        self.disable_tool_buttons(true);
-        $('#btn-cancel-query').prop('disabled', false);
-
-        if (arguments.length > 0 &&
-          arguments[arguments.length - 1] == 'connect') {
-          url += '?connect=1';
-        }
-
-        $.ajax({
-          url: url,
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(sql),
-          success: function(res) {
-            // Remove marker
-            if (self.gridView.marker) {
-              self.gridView.marker.clear();
-              delete self.gridView.marker;
-              self.gridView.marker = null;
-
-              // Remove already existing marker
-              self.gridView.query_tool_obj.removeLineClass(self.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
-            }
-
-            if (res.data.status) {
-              self.trigger(
-                'pgadmin-sqleditor:loading-icon:message',
-                gettext('Waiting for the query execution to complete...')
-              );
-
-              self.can_edit = res.data.can_edit;
-              self.can_filter = res.data.can_filter;
-              self.info_notifier_timeout = res.data.info_notifier_timeout;
-
-              // If status is True then poll the result.
-              self._poll();
-            } else {
-              self.trigger('pgadmin-sqleditor:loading-icon:hide');
-              self.disable_tool_buttons(false);
-              $('#btn-cancel-query').prop('disabled', true);
-              self.update_msg_history(false, res.data.result);
-
-              // Highlight the error in the sql panel
-              self._highlight_error(res.data.result);
-            }
-          },
-          error: function(e) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            self.disable_tool_buttons(false);
-            $('#btn-cancel-query').prop('disabled', true);
-
-            if (e.readyState == 0) {
-              self.update_msg_history(false,
-                gettext('Not connected to the server or the connection to the server has been closed.')
-              );
-              return;
-            }
-
-            if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) {
-              self.save_state('execute', [explain_prefix]);
-              pgAdmin.Browser.UserManagement.pga_login();
-            }
-
-            if(is_new_transaction_required(e)) {
-              self.save_state('execute', [explain_prefix]);
-              self.init_transaction();
-            }
-            var msg = e.responseText;
-            if (e.responseJSON != undefined) {
-              if(e.responseJSON.errormsg != undefined) {
-                msg = e.responseJSON.errormsg;
-              }
-
-              if(e.status == 503 && e.responseJSON.info != undefined &&
-                  e.responseJSON.info == 'CONNECTION_LOST') {
-                setTimeout(function() {
-                  self.save_state('execute', [explain_prefix]);
-                  self.handle_connection_lost(false, e);
-                });
-              }
-            }
-            self.update_msg_history(false, msg);
-          },
-        });
+        const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement);
+        executeQuery.execute(sql, explain_prefix);
       },
 
       /* This function is used to highlight the error line and
@@ -4078,7 +3905,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_auto_rollback', []);
               self.init_transaction();
             }
@@ -4139,7 +3966,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_auto_commit', []);
               return self.init_transaction();
             }
@@ -4201,7 +4028,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_explain_verbose', []);
               return self.init_transaction();
             }
@@ -4250,7 +4077,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_explain_costs', []);
               return self.init_transaction();
             }
@@ -4298,7 +4125,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_explain_buffers', []);
               return self.init_transaction();
             }
@@ -4345,7 +4172,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('_explain_timing', []);
               return self.init_transaction();
             }
@@ -4379,6 +4206,10 @@ define('tools.querytool', [
         return is_query_running;
       },
 
+      setIsQueryRunning: function(value) {
+        is_query_running = value;
+      },
+
       /*
        * This function get explain options and auto rollback/auto commit
        * values from preferences
@@ -4455,7 +4286,7 @@ define('tools.querytool', [
               return pgAdmin.Browser.UserManagement.pga_login();
             }
 
-            if(is_new_transaction_required(e)) {
+            if(transaction.is_new_transaction_required(e)) {
               self.save_state('get_preferences', []);
               return self.init_transaction();
             }
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/10_plus/explain_plan.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/10_plus/explain_plan.sql
new file mode 100644
index 00000000..fc4a8d2d
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/10_plus/explain_plan.sql
@@ -0,0 +1,23 @@
+EXPLAIN (
+{% if format %}
+  FORMAT {{ format.upper() }},
+{% endif %}
+{% if analyze is defined %}
+  ANALYZE {{ analyze }},
+{% endif %}
+{% if verbose is defined %}
+  VERBOSE {{ verbose }},
+{% endif %}
+{% if costs is defined %}
+  COSTS {{ costs }},
+{% endif %}
+{% if timing is defined %}
+  TIMING {{ timing }},
+{% endif %}
+{% if summary is defined %}
+  SUMMARY {{ summary }},
+{% endif %}
+{% if buffers is defined %}
+  BUFFERS {{ buffers }}
+{% endif %}
+) {{ sql }}
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/9.2_plus/explain_plan.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/9.2_plus/explain_plan.sql
new file mode 100644
index 00000000..315174a6
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/9.2_plus/explain_plan.sql
@@ -0,0 +1,20 @@
+EXPLAIN (
+{% if format %}
+  FORMAT {{ format.upper() }},
+{% endif %}
+{% if analyze is defined %}
+  ANALYZE {{ analyze }},
+{% endif %}
+{% if verbose is defined %}
+  VERBOSE {{ verbose }},
+{% endif %}
+{% if costs is defined %}
+  COSTS {{ costs }},
+{% endif %}
+{% if timing is defined %}
+  TIMING {{ timing }},
+{% endif %}
+{% if buffers is defined %}
+  BUFFERS {{ buffers }}
+{% endif %}
+) {{ sql }}
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/explain_plan.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/explain_plan.sql
new file mode 100644
index 00000000..72b20c95
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/explain_plan.sql
@@ -0,0 +1,17 @@
+EXPLAIN (
+{% if format %}
+  FORMAT {{ format.upper() }},
+{% endif %}
+{% if analyze is defined %}
+  ANALYZE {{ analyze }},
+{% endif %}
+{% if verbose is defined %}
+  VERBOSE {{ verbose }},
+{% endif %}
+{% if costs is defined %}
+  COSTS {{ costs }},
+{% endif %}
+{% if buffers is defined %}
+  BUFFERS {{ buffers }}
+{% endif %}
+) {{ sql }}
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/gpdb_5.0_plus/explain_plan.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/gpdb_5.0_plus/explain_plan.sql
new file mode 100644
index 00000000..aacabe45
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/gpdb_5.0_plus/explain_plan.sql
@@ -0,0 +1,5 @@
+EXPLAIN
+{% if analyze %}
+ ANALYZE
+{% endif %}
+ {{ sql }}
diff --git a/web/pgadmin/tools/sqleditor/tests/__init__.py b/web/pgadmin/tools/sqleditor/tests/__init__.py
new file mode 100644
index 00000000..590026ad
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/__init__.py
@@ -0,0 +1,8 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
diff --git a/web/pgadmin/tools/sqleditor/tests/test_explain_plan_templates.py b/web/pgadmin/tools/sqleditor/tests/test_explain_plan_templates.py
new file mode 100644
index 00000000..ab018788
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_explain_plan_templates.py
@@ -0,0 +1,152 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import os
+
+from flask import Flask, render_template
+from jinja2 import FileSystemLoader
+
+from pgadmin import VersionedTemplateLoader
+from pgadmin.utils.route import BaseTestGenerator
+
+
+class TestExplainPlanTemplates(BaseTestGenerator):
+    scenarios = [
+        (
+            'When rendering Postgres 9.0 template, '
+            'when passing all parameters,'
+            'it returns the explain plan with all parameters',
+            dict(
+                template_path=os.path.join('sqleditor', 'sql', 'default', 'explain_plan.sql'),
+                input_parameters=dict(
+                    sql='select * from places',
+                    format='xml',
+                    analyze=True,
+                    verbose=True,
+                    costs=False,
+                    buffers=True
+                ),
+                sql_statement='select * from places',
+                expected_return_value='EXPLAIN '
+                                      '(  FORMAT XML,  ANALYZE True,  VERBOSE True,  '
+                                      'COSTS False,  BUFFERS True) select * from places'
+            )
+        ),
+        (
+            'When rendering Postgres 9.0 template, '
+            'when not all parameters are present,'
+            'it returns the explain plan with the present parameters',
+            dict(
+                template_path=os.path.join('sqleditor', 'sql', 'default', 'explain_plan.sql'),
+                input_parameters=dict(
+                    sql='select * from places',
+                    format='json',
+                    buffers=True
+                ),
+                sql_statement='select * from places',
+                expected_return_value='EXPLAIN '
+                                      '(  FORMAT JSON,  BUFFERS True) select * from places'
+            )
+        ),
+        (
+            'When rendering Postgres 9.2 template, '
+            'when timing is present,'
+            'it returns the explain plan with timing',
+            dict(
+                template_path=os.path.join('sqleditor', 'sql', '9.2_plus', 'explain_plan.sql'),
+                input_parameters=dict(
+                    sql='select * from places',
+                    format='json',
+                    buffers=True,
+                    timing=False
+                ),
+                sql_statement='select * from places',
+                expected_return_value='EXPLAIN '
+                                      '(  FORMAT JSON,  TIMING False,  BUFFERS True) select * from places'
+            )
+        ),
+        (
+            'When rendering Postgres 10 template, '
+            'when summary is present,'
+            'it returns the explain plan with summary',
+            dict(
+                template_path=os.path.join('sqleditor', 'sql', '10_plus', 'explain_plan.sql'),
+                input_parameters=dict(
+                    sql='select * from places',
+                    format='yaml',
+                    buffers=True,
+                    timing=False,
+                    summary=True
+                ),
+                sql_statement='select * from places',
+                expected_return_value='EXPLAIN '
+                                      '(  FORMAT YAML,  TIMING False,  SUMMARY True,  BUFFERS True) select * from places'
+            )
+        ),
+        (
+            'When rendering GreenPlum 5.3 template, '
+            'when all parameters are present,'
+            'it returns the explain without parameters',
+            dict(
+                template_path=os.path.join('sqleditor', 'sql', 'gpdb_5.0_plus', 'explain_plan.sql'),
+                input_parameters=dict(
+                    sql='select * from places',
+                    format='json',
+                    buffers=True
+                ),
+                sql_statement='select * from places',
+                expected_return_value='EXPLAIN select * from places'
+            )
+        ),
+        (
+            'When rendering GreenPlum 5.3 template, '
+            'when analyze is true,'
+            'it returns the explain analyze',
+            dict(
+                template_path=os.path.join('sqleditor', 'sql', 'gpdb_5.0_plus', 'explain_plan.sql'),
+                input_parameters=dict(
+                    sql='select * from places',
+                    analyze=True
+                ),
+                sql_statement='select * from places',
+                expected_return_value='EXPLAIN ANALYZE select * from places'
+            )
+        ),
+        (
+            'When rendering GreenPlum 5.3 template, '
+            'when analyze is false,'
+            'it returns the only explain',
+            dict(
+                template_path=os.path.join('sqleditor', 'sql', 'gpdb_5.0_plus', 'explain_plan.sql'),
+                input_parameters=dict(
+                    sql='select * from places',
+                    analyze=False
+                ),
+                sql_statement='select * from places',
+                expected_return_value='EXPLAIN select * from places'
+            )
+        ),
+    ]
+
+    def setUp(self):
+        self.loader = VersionedTemplateLoader(FakeApp())
+
+    def runTest(self):
+        with FakeApp().app_context():
+            result = render_template(self.template_path, **self.input_parameters)
+            self.assertEqual(
+                str(result).replace("\n", ""), self.expected_return_value)
+
+
+class FakeApp(Flask):
+    def __init__(self):
+        super(FakeApp, self).__init__("")
+        self.jinja_loader = FileSystemLoader(
+            os.path.dirname(os.path.realpath(__file__)) + "/../templates"
+        )
diff --git a/web/pgadmin/tools/sqleditor/tests/test_extract_sql_from_network_parameters.py b/web/pgadmin/tools/sqleditor/tests/test_extract_sql_from_network_parameters.py
new file mode 100644
index 00000000..c4e002cc
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_extract_sql_from_network_parameters.py
@@ -0,0 +1,59 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+from werkzeug.datastructures import ImmutableMultiDict
+
+from pgadmin.tools.sqleditor import extract_sql_from_network_parameters
+from pgadmin.utils.route import BaseTestGenerator
+
+
+class ExtractSQLFromNetworkParametersTest(BaseTestGenerator):
+    """
+    This class validates the change password functionality
+    by defining change password scenarios; where dict of
+    parameters describes the scenario appended by test name.
+    """
+
+    scenarios = [
+        ('Single string in the payload', dict(
+            request_strigified_data='"some sql"',
+            request_arguments=ImmutableMultiDict(),
+            request_form_data=ImmutableMultiDict(),
+
+            expected_result=dict(sql='some sql', explain_plan=None)
+        )),
+        ('Payload that requests explain plan using json', dict(
+            request_strigified_data='{"sql": "some sql", "explain_plan": {"format": "json", "analyze": false, "verbose": false, "costs": false, "buffers": false, "timing": false}}',
+            request_arguments=ImmutableMultiDict(),
+            request_form_data=ImmutableMultiDict(),
+
+            expected_result=dict(
+                sql='some sql',
+                explain_plan=dict(
+                    format='json',
+                    analyze=False,
+                    verbose=False,
+                    buffers=False,
+                    costs=False,
+                    timing=False
+                )
+            )
+        ))
+    ]
+
+    def runTest(self):
+        """Check correct function is called to handle to run query."""
+
+        result = extract_sql_from_network_parameters(
+            self.request_strigified_data,
+            self.request_arguments,
+            self.request_form_data
+        )
+
+        self.assertEquals(result, self.expected_result)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_start_query_tool.py b/web/pgadmin/tools/sqleditor/tests/test_start_query_tool.py
new file mode 100644
index 00000000..5e2752ae
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_start_query_tool.py
@@ -0,0 +1,38 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import sys
+
+from pgadmin.utils.route import BaseTestGenerator
+from pgadmin.tools.sqleditor import StartRunningQuery
+
+if sys.version_info < (3, 3):
+    from mock import patch, ANY
+else:
+    from unittest.mock import patch, ANY
+
+
+class StartQueryTool(BaseTestGenerator):
+    """
+    Ensures that the call to the backend to start running a query
+    calls the needed functions
+    """
+
+    @patch('pgadmin.tools.sqleditor.extract_sql_from_network_parameters')
+    def runTest(self, extract_sql_from_network_parameters_mock):
+        """Check correct function is called to handle to run query."""
+
+        extract_sql_from_network_parameters_mock.return_value = 'transformed sql'
+
+        with patch.object(StartRunningQuery, 'execute', return_value='some result') as StartRunningQuery_execute_mock:
+            response = self.tester.post('/sqleditor/query_tool/start/1234', data='"some sql statement"')
+
+            self.assertEquals(response.status, '200 OK')
+            self.assertEquals(response.data, 'some result')
+            StartRunningQuery_execute_mock.assert_called_with('transformed sql', 1234, ANY)
+            extract_sql_from_network_parameters_mock.assert_called_with('"some sql statement"', ANY, ANY)
diff --git a/web/pgadmin/tools/sqleditor/utils/__init__.py b/web/pgadmin/tools/sqleditor/utils/__init__.py
new file mode 100644
index 00000000..3626b658
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/__init__.py
@@ -0,0 +1,14 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+from .constant_definition import *
+from .is_begin_required import is_begin_required
+from .update_session_grid_transaction import update_session_grid_transaction
+from .start_running_query import *
+from .apply_explain_plan_wrapper import *
diff --git a/web/pgadmin/tools/sqleditor/utils/apply_explain_plan_wrapper.py b/web/pgadmin/tools/sqleditor/utils/apply_explain_plan_wrapper.py
new file mode 100644
index 00000000..84e478de
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/apply_explain_plan_wrapper.py
@@ -0,0 +1,24 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Apply Explain plan wrapper to sql object."""
+from flask import render_template
+
+from pgadmin.utils.compile_template_name import compile_template_name
+
+
+def apply_explain_plan_wrapper_if_needed(manager, sql):
+    if 'explain_plan' in sql and sql['explain_plan']:
+        explain_plan = sql['explain_plan']
+        ver = manager.version
+        server_type = manager.server_type
+        template_path = compile_template_name('sqleditor/sql', 'explain_plan.sql', server_type, ver)
+        return render_template(template_path, sql=sql['sql'], **explain_plan)
+    else:
+        return sql['sql']
diff --git a/web/pgadmin/tools/sqleditor/utils/constant_definition.py b/web/pgadmin/tools/sqleditor/utils/constant_definition.py
new file mode 100644
index 00000000..44c7ecb7
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/constant_definition.py
@@ -0,0 +1,32 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Definition of constants for SQLEditor."""
+
+# Async Constants
+ASYNC_OK = 1
+ASYNC_READ_TIMEOUT = 2
+ASYNC_WRITE_TIMEOUT = 3
+ASYNC_NOT_CONNECTED = 4
+ASYNC_EXECUTION_ABORTED = 5
+
+# Transaction status constants
+TX_STATUS_IDLE = 0
+TX_STATUS__ACTIVE = 1
+TX_STATUS_INTRANS = 2
+TX_STATUS_INERROR = 3
+
+# Connection status codes mapping
+CONNECTION_STATUS_MESSAGE_MAPPING = dict({
+    0: 'The session is idle and there is no current transaction.',
+    1: 'A command is currently in progress.',
+    2: 'The session is idle in a valid transaction block.',
+    3: 'The session is idle in a failed transaction block.',
+    4: 'The connection with the server is bad.'
+})
diff --git a/web/pgadmin/tools/sqleditor/utils/is_begin_required.py b/web/pgadmin/tools/sqleditor/utils/is_begin_required.py
new file mode 100644
index 00000000..8db7b954
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/is_begin_required.py
@@ -0,0 +1,169 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Check if requires BEGIN in the current query."""
+
+
+def is_begin_required(query):
+    word_len = 0
+    query = query.strip()
+    query_len = len(query)
+
+    # Check word length (since "beginx" is not "begin").
+    while (word_len < query_len) and query[word_len].isalpha():
+        word_len += 1
+
+    # Transaction control commands.  These should include every keyword that
+    #  gives rise to a TransactionStmt in the backend grammar, except for the
+    #  savepoint-related commands.
+    #
+    #  (We assume that START must be START TRANSACTION, since there is
+    #  presently no other "START foo" command.)
+
+    keyword = query[0:word_len]
+
+    if word_len == 5 and keyword.lower() == "abort":
+        return False
+    if word_len == 5 and keyword.lower() == "begin":
+        return False
+    if word_len == 5 and keyword.lower() == "start":
+        return False
+    if word_len == 6:
+        # SELECT is protected from dirty reads hence don't require transaction
+        if keyword.lower() in ["select", "commit"]:
+            return False
+    if word_len == 3 and keyword.lower() == "end":
+        return False
+    if word_len == 8 and keyword.lower() == "rollback":
+        return False
+    if word_len == 7 and keyword.lower() == "prepare":
+        # PREPARE TRANSACTION is a TC command, PREPARE foo is not
+        query = query[word_len:query_len]
+        query = query.strip()
+        query_len = len(query)
+        word_len = 0
+
+        while (word_len < query_len) and query[word_len].isalpha():
+            word_len += 1
+
+        keyword = query[0:word_len]
+        if word_len == 11 and keyword.lower() == "transaction":
+            return False
+        return True
+
+    # Commands not allowed within transactions. The statements checked for
+    # here should be exactly those that call PreventTransactionChain() in the
+    # backend.
+    if word_len == 6 and keyword.lower() == "vacuum":
+        return False
+
+    if word_len == 7 and keyword.lower() == "cluster":
+        # CLUSTER with any arguments is allowed in transactions
+        query = query[word_len:query_len]
+        query = query.strip()
+
+        if query[0].isalpha():
+            return True  # has additional words
+        return False  # it's CLUSTER without arguments
+
+    if word_len == 6 and keyword.lower() == "create":
+        query = query[word_len:query_len]
+        query = query.strip()
+        query_len = len(query)
+        word_len = 0
+
+        while (word_len < query_len) and query[word_len].isalpha():
+            word_len += 1
+
+        keyword = query[0:word_len]
+        if word_len == 8 and keyword.lower() == "database":
+            return False
+        if word_len == 10 and keyword.lower() == "tablespace":
+            return False
+
+        # CREATE [UNIQUE] INDEX CONCURRENTLY isn't allowed in xacts
+        if word_len == 7 and keyword.lower() == "cluster":
+            query = query[word_len:query_len]
+            query = query.strip()
+            query_len = len(query)
+            word_len = 0
+
+            while (word_len < query_len) and query[word_len].isalpha():
+                word_len += 1
+
+            keyword = query[0:word_len]
+
+        if word_len == 5 and keyword.lower() == "index":
+            query = query[word_len:query_len]
+            query = query.strip()
+            query_len = len(query)
+            word_len = 0
+
+            while (word_len < query_len) and query[word_len].isalpha():
+                word_len += 1
+
+            keyword = query[0:word_len]
+            if word_len == 12 and keyword.lower() == "concurrently":
+                return False
+        return True
+
+    if word_len == 5 and keyword.lower() == "alter":
+        query = query[word_len:query_len]
+        query = query.strip()
+        query_len = len(query)
+        word_len = 0
+
+        while (word_len < query_len) and query[word_len].isalpha():
+            word_len += 1
+
+        keyword = query[0:word_len]
+
+        # ALTER SYSTEM isn't allowed in xacts
+        if word_len == 6 and keyword.lower() == "system":
+            return False
+        return True
+
+    # Note: these tests will match DROP SYSTEM and REINDEX TABLESPACE, which
+    # aren't really valid commands so we don't care much. The other four
+    # possible matches are correct.
+    if word_len == 4 and keyword.lower() == "drop" \
+            or word_len == 7 and keyword.lower() == "reindex":
+        query = query[word_len:query_len]
+        query = query.strip()
+        query_len = len(query)
+        word_len = 0
+
+        while (word_len < query_len) and query[word_len].isalpha():
+            word_len += 1
+
+        keyword = query[0:word_len]
+        if word_len == 8 and keyword.lower() == "database":
+            return False
+        if word_len == 6 and keyword.lower() == "system":
+            return False
+        if word_len == 10 and keyword.lower() == "tablespace":
+            return False
+        return True
+
+    # DISCARD ALL isn't allowed in xacts, but other variants are allowed.
+    if word_len == 7 and keyword.lower() == "discard":
+        query = query[word_len:query_len]
+        query = query.strip()
+        query_len = len(query)
+        word_len = 0
+
+        while (word_len < query_len) and query[word_len].isalpha():
+            word_len += 1
+
+        keyword = query[0:word_len]
+        if word_len == 3 and keyword.lower() == "all":
+            return False
+        return True
+
+    return True
diff --git a/web/pgadmin/tools/sqleditor/utils/start_running_query.py b/web/pgadmin/tools/sqleditor/utils/start_running_query.py
new file mode 100644
index 00000000..f5e1feae
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/start_running_query.py
@@ -0,0 +1,172 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Start executing the query in async mode."""
+
+import pickle
+import random
+
+from flask import Response
+from flask_babel import gettext
+
+from config import PG_DEFAULT_DRIVER
+from pgadmin.tools.sqleditor.utils.apply_explain_plan_wrapper import apply_explain_plan_wrapper_if_needed
+from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import update_session_grid_transaction
+from pgadmin.tools.sqleditor.utils.is_begin_required import is_begin_required
+from pgadmin.tools.sqleditor.utils.constant_definition import TX_STATUS_IDLE, TX_STATUS_INERROR
+
+from pgadmin.utils.ajax import make_json_response, internal_server_error
+from pgadmin.utils.driver import get_driver
+from pgadmin.utils.exception import ConnectionLost
+
+
+class StartRunningQuery:
+
+    def __init__(self, blueprint_object, logger):
+        self.http_session = None
+        self.blueprint_object = blueprint_object
+        self.connection_id = str(random.randint(1, 9999999))
+        self.logger = logger
+
+    def execute(self, sql, trans_id, http_session):
+        session_obj = StartRunningQuery.retrieve_session_information(http_session, trans_id)
+        if type(session_obj) is Response:
+            return session_obj
+
+        transaction_object = pickle.loads(session_obj['command_obj'])
+        can_edit = False
+        can_filter = False
+        if transaction_object is not None and session_obj is not None:
+            # set fetched row count to 0 as we are executing query again.
+            transaction_object.update_fetched_row_cnt(0)
+            self.__retrieve_connection_id(transaction_object)
+
+            try:
+                manager = get_driver(
+                    PG_DEFAULT_DRIVER).connection_manager(transaction_object.sid)
+                conn = manager.connection(did=transaction_object.did, conn_id=self.connection_id,
+                                          auto_reconnect=False,
+                                          use_binary_placeholder=True,
+                                          array_to_string=True)
+            except ConnectionLost:
+                raise
+            except Exception as e:
+                self.logger.error(e)
+                return internal_server_error(errormsg=str(e))
+
+            # Connect to the Server if not connected.
+            if not conn.connected():
+                status, msg = conn.connect()
+                if not status:
+                    self.logger.error(msg)
+                    return internal_server_error(errormsg=str(msg))
+
+            effective_sql_statement = apply_explain_plan_wrapper_if_needed(manager, sql)
+
+            result, status = self.__execute_query(
+                conn,
+                session_obj,
+                effective_sql_statement,
+                trans_id,
+                transaction_object
+            )
+
+            can_edit = transaction_object.can_edit()
+            can_filter = transaction_object.can_filter()
+
+        else:
+            status = False
+            result = gettext(
+                'Either transaction object or session object not found.')
+        return make_json_response(
+            data={
+                'status': status, 'result': result,
+                'can_edit': can_edit, 'can_filter': can_filter,
+                'info_notifier_timeout': self.blueprint_object.info_notifier_timeout.get()
+            }
+        )
+
+    def __retrieve_connection_id(self, trans_obj):
+        conn_id = trans_obj.conn_id
+        # if conn_id is None then we will have to create a new connection
+        if conn_id is not None:
+            self.connection_id = conn_id
+
+    def __execute_query(self, conn, session_obj, sql, trans_id, trans_obj):
+        if conn.connected():
+            # on successful connection set the connection id to the
+            # transaction object
+            trans_obj.set_connection_id(self.connection_id)
+
+            StartRunningQuery.save_transaction_in_session(session_obj, trans_id, trans_obj)
+
+            # If auto commit is False and transaction status is Idle
+            # then call is_begin_not_required() function to check BEGIN
+            # is required or not.
+
+            if StartRunningQuery.is_begin_required_for_sql_query(trans_obj, conn, sql):
+                conn.execute_void("BEGIN;")
+
+            # Execute sql asynchronously with params is None
+            # and formatted_error is True.
+            try:
+                status, result = conn.execute_async(sql)
+            except ConnectionLost:
+                raise
+
+            # If the transaction aborted for some reason and
+            # Auto RollBack is True then issue a rollback to cleanup.
+            if StartRunningQuery.is_rollback_statement_required(trans_obj, conn):
+                conn.execute_void("ROLLBACK;")
+        else:
+            status = False
+            result = gettext(
+                'Not connected to server or connection with the server has '
+                'been closed.')
+        return result, status
+
+    @staticmethod
+    def is_begin_required_for_sql_query(trans_obj, conn, sql):
+        return not trans_obj.auto_commit \
+               and conn.transaction_status() == TX_STATUS_IDLE \
+               and is_begin_required(sql)
+
+    @staticmethod
+    def is_rollback_statement_required(trans_obj, conn):
+        return conn.transaction_status() == TX_STATUS_INERROR and trans_obj.auto_rollback
+
+    @staticmethod
+    def save_transaction_in_session(session, transaction_id, transaction):
+        # As we changed the transaction object we need to
+        # restore it and update the session variable.
+        session['command_obj'] = pickle.dumps(transaction, -1)
+        update_session_grid_transaction(transaction_id, session)
+
+    @staticmethod
+    def retrieve_session_information(http_session, transaction_id):
+        if 'gridData' not in http_session:
+            return make_json_response(
+                success=0,
+                errormsg=gettext('Transaction ID not found in the session.'),
+                info='DATAGRID_TRANSACTION_REQUIRED', status=404
+            )
+        grid_data = http_session['gridData']
+        # Return from the function if transaction id not found
+        if str(transaction_id) not in grid_data:
+            return make_json_response(
+                success=0,
+                errormsg=gettext('Transaction ID not found in the session.'),
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404
+            )
+        # Fetch the object for the specified transaction id.
+        # Use pickle.loads function to get the command object
+        return grid_data[str(transaction_id)]
+
+
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/__init__.py b/web/pgadmin/tools/sqleditor/utils/tests/__init__.py
new file mode 100644
index 00000000..590026ad
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/tests/__init__.py
@@ -0,0 +1,8 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_apply_explain_plan_wrapper.py b/web/pgadmin/tools/sqleditor/utils/tests/test_apply_explain_plan_wrapper.py
new file mode 100644
index 00000000..6cf320eb
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_apply_explain_plan_wrapper.py
@@ -0,0 +1,121 @@
+#######################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Apply Explain plan wrapper to sql object."""
+import sys
+
+from pgadmin.tools.sqleditor.utils import apply_explain_plan_wrapper_if_needed
+from pgadmin.utils.route import BaseTestGenerator
+
+if sys.version_info < (3, 3):
+    from mock import patch, MagicMock
+else:
+    from unittest.mock import patch, MagicMock
+
+
+class StartRunningQueryTest(BaseTestGenerator):
+    """
+    Check that the apply_explain_plan_weapper_if_needed method works as intended
+    """
+    scenarios = [
+        ('When explain_plan is none, it should return unaltered SQL', dict(
+            function_input_parameters={
+                'manager': MagicMock(),
+                'sql': {
+                    'sql': 'some sql',
+                    'explain_plan': None
+                }
+            },
+
+            expect_render_template_mock_parameters=None,
+
+            expected_return_value='some sql'
+        )),
+        ('When explain_plan is not present, it should return unaltered SQL', dict(
+            function_input_parameters={
+                'manager': MagicMock(),
+                'sql': {
+                    'sql': 'some sql'
+                }
+            },
+
+            expect_render_template_mock_parameters=None,
+
+            expected_return_value='some sql'
+        )),
+        ('When explain_plan is present for a Postgres server version 10, it should return SQL with explain plan', dict(
+            function_input_parameters={
+                'manager': MagicMock(version=10, server_type='pg'),
+                'sql': {
+                    'sql': 'some sql',
+                    'explain_plan': {
+                        'format': 'json',
+                        'analyze': False,
+                        'verbose': True,
+                        'buffers': False,
+                        'timing': True
+                    }
+                }
+            },
+
+            expect_render_template_mock_parameters=dict(
+                template_name_or_list='sqleditor/sql/#10#/explain_plan.sql',
+                named_parameters=dict(
+                    format='json',
+                    analyze=False,
+                    verbose=True,
+                    buffers=False,
+                    timing=True
+                )),
+
+            expected_return_value='EXPLAIN (FORMAT JSON, ANALYZE FALSE, VERBOSE TRUE, COSTS FALSE, BUFFERS FALSE, '
+                                  'TIMING TRUE) some sql'
+        )),
+        ('When explain_plan is present for a GreenPlum server version 5, it should return SQL with explain plan', dict(
+            function_input_parameters={
+                'manager': MagicMock(version=80323, server_type='gpdb'),
+                'sql': {
+                    'sql': 'some sql',
+                    'explain_plan': {
+                        'format': 'json',
+                        'analyze': False,
+                        'verbose': True,
+                        'buffers': False,
+                        'timing': True
+                    }
+                }
+            },
+
+            expect_render_template_mock_parameters=dict(
+                template_name_or_list='sqleditor/sql/#gpdb#80323#/explain_plan.sql',
+                named_parameters=dict(
+                    format='json',
+                    analyze=False,
+                    verbose=True,
+                    buffers=False,
+                    timing=True
+                )),
+
+            expected_return_value='EXPLAIN some sql'
+        ))
+    ]
+
+    def runTest(self):
+        with patch('pgadmin.tools.sqleditor.utils.apply_explain_plan_wrapper.render_template') as render_template_mock:
+            render_template_mock.return_value = self.expected_return_value
+            result = apply_explain_plan_wrapper_if_needed(**self.function_input_parameters)
+            self.assertEquals(result, self.expected_return_value)
+            if self.expect_render_template_mock_parameters:
+                render_template_mock.assert_called_with(
+                    self.expect_render_template_mock_parameters['template_name_or_list'],
+                    sql=self.function_input_parameters['sql']['sql'],
+                    **self.expect_render_template_mock_parameters['named_parameters']
+                )
+            else:
+                render_template_mock.assert_not_called()
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py b/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py
new file mode 100644
index 00000000..d53c0248
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py
@@ -0,0 +1,445 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import sys
+
+from flask import Response
+import simplejson as json
+
+from pgadmin.tools.sqleditor.utils.start_running_query import StartRunningQuery
+from pgadmin.utils.exception import ConnectionLost
+from pgadmin.utils.route import BaseTestGenerator
+
+if sys.version_info < (3, 3):
+    from mock import patch, MagicMock
+else:
+    from unittest.mock import patch, MagicMock
+
+get_driver_exception = Exception('get_driver exception')
+
+
+class StartRunningQueryTest(BaseTestGenerator):
+    """
+    Check that the start_running_query method works as intended
+    """
+
+    scenarios = [
+        ('When gridData is not present in the session, it returns an error', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict()
+            ),
+            pickle_load_return=None,
+            get_driver_exception=False,
+            manager_connection_exception=None,
+
+            is_connected_to_server=False,
+            connection_connect_return=None,
+            execute_async_return_value=None,
+            is_begin_required=False,
+            is_rollback_required=False,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=dict(
+                success=0,
+                errormsg='Transaction ID not found in the session.',
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404,
+            ),
+            expect_internal_server_error_to_have_been_called_with=None,
+            expected_logger_error=None,
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When transactionId is not present in the gridData, it returns an error', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData=dict())
+            ),
+            pickle_load_return=None,
+            get_driver_exception=False,
+            manager_connection_exception=None,
+
+            is_connected_to_server=False,
+            connection_connect_return=None,
+            execute_async_return_value=None,
+            is_begin_required=False,
+            is_rollback_required=False,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=dict(
+                success=0,
+                errormsg='Transaction ID not found in the session.',
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404,
+            ),
+            expect_internal_server_error_to_have_been_called_with=None,
+            expected_logger_error=None,
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When the command information for the transaction cannot be retrieved, it returns an error', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData={'123': dict(command_obj='')})
+            ),
+            pickle_load_return=None,
+            get_driver_exception=False,
+            manager_connection_exception=None,
+
+            is_connected_to_server=False,
+            connection_connect_return=None,
+            execute_async_return_value=None,
+            is_begin_required=False,
+            is_rollback_required=False,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=dict(
+                data=dict(
+                    status=False,
+                    result='Either transaction object or session object not found.',
+                    can_edit=False,
+                    can_filter=False,
+                    info_notifier_timeout=5
+                )
+            ),
+            expect_internal_server_error_to_have_been_called_with=None,
+            expected_logger_error=None,
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When exception happens while retrieving the database driver, it returns an error', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData={'123': dict(command_obj='')})
+            ),
+            pickle_load_return=MagicMock(conn_id=1, update_fetched_row_cnt=MagicMock()),
+            get_driver_exception=True,
+            manager_connection_exception=None,
+
+            is_connected_to_server=False,
+            connection_connect_return=None,
+            execute_async_return_value=None,
+            is_begin_required=False,
+            is_rollback_required=False,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=None,
+            expect_internal_server_error_to_have_been_called_with=dict(
+                errormsg='get_driver exception'
+            ),
+            expected_logger_error=get_driver_exception,
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When ConnectionLost happens while retrieving the database connection, it returns an error', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData={'123': dict(command_obj='')})
+            ),
+            pickle_load_return=MagicMock(conn_id=1, update_fetched_row_cnt=MagicMock()),
+            get_driver_exception=False,
+            manager_connection_exception=ConnectionLost('1', '2', '3'),
+
+            is_connected_to_server=False,
+            connection_connect_return=None,
+            execute_async_return_value=None,
+            is_begin_required=False,
+            is_rollback_required=False,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=None,
+            expect_internal_server_error_to_have_been_called_with=None,
+            expected_logger_error=None,
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When is not connected to the server and fails to connect, it returns an error', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData={'123': dict(command_obj='')})
+            ),
+            pickle_load_return=MagicMock(conn_id=1, update_fetched_row_cnt=MagicMock()),
+            get_driver_exception=False,
+            manager_connection_exception=None,
+
+            is_connected_to_server=False,
+            connection_connect_return=[False, 'Unable to connect to server'],
+            execute_async_return_value=None,
+            is_begin_required=False,
+            is_rollback_required=False,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=None,
+            expect_internal_server_error_to_have_been_called_with=dict(
+                errormsg='Unable to connect to server'
+            ),
+            expected_logger_error='Unable to connect to server',
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When server is connected and start query async request, it returns an success message', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData={'123': dict(command_obj='')})
+            ),
+            pickle_load_return=MagicMock(
+                conn_id=1,
+                update_fetched_row_cnt=MagicMock(),
+                set_connection_id=MagicMock(),
+                auto_commit=True,
+                auto_rollback=False,
+                can_edit=lambda: True,
+                can_filter=lambda: True
+            ),
+            get_driver_exception=False,
+            manager_connection_exception=None,
+
+            is_connected_to_server=True,
+            connection_connect_return=None,
+            execute_async_return_value=[True, 'async function result output'],
+            is_begin_required=False,
+            is_rollback_required=False,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=dict(
+                data=dict(
+                    status=True,
+                    result='async function result output',
+                    can_edit=True,
+                    can_filter=True,
+                    info_notifier_timeout=5
+                )
+            ),
+            expect_internal_server_error_to_have_been_called_with=None,
+            expected_logger_error=None,
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When server is connected and start query async request and begin is required, '
+         'it returns an success message', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData={'123': dict(command_obj='')})
+            ),
+            pickle_load_return=MagicMock(
+                conn_id=1,
+                update_fetched_row_cnt=MagicMock(),
+                set_connection_id=MagicMock(),
+                auto_commit=True,
+                auto_rollback=False,
+                can_edit=lambda: True,
+                can_filter=lambda: True
+            ),
+            get_driver_exception=False,
+            manager_connection_exception=None,
+
+            is_connected_to_server=True,
+            connection_connect_return=None,
+            execute_async_return_value=[True, 'async function result output'],
+            is_begin_required=True,
+            is_rollback_required=False,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=dict(
+                data=dict(
+                    status=True,
+                    result='async function result output',
+                    can_edit=True,
+                    can_filter=True,
+                    info_notifier_timeout=5
+                )
+            ),
+            expect_internal_server_error_to_have_been_called_with=None,
+            expected_logger_error=None,
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When server is connected and start query async request and rollback is required, '
+         'it returns an success message', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData={'123': dict(command_obj='')})
+            ),
+            pickle_load_return=MagicMock(
+                conn_id=1,
+                update_fetched_row_cnt=MagicMock(),
+                set_connection_id=MagicMock(),
+                auto_commit=True,
+                auto_rollback=False,
+                can_edit=lambda: True,
+                can_filter=lambda: True
+            ),
+            get_driver_exception=False,
+            manager_connection_exception=None,
+
+            is_connected_to_server=True,
+            connection_connect_return=None,
+            execute_async_return_value=[True, 'async function result output'],
+            is_begin_required=False,
+            is_rollback_required=True,
+            apply_explain_plan_wrapper_if_needed_return_value='some sql',
+
+            expect_make_json_response_to_have_been_called_with=dict(
+                data=dict(
+                    status=True,
+                    result='async function result output',
+                    can_edit=True,
+                    can_filter=True,
+                    info_notifier_timeout=5
+                )
+            ),
+            expect_internal_server_error_to_have_been_called_with=None,
+            expected_logger_error=None,
+            expect_execute_void_called_with='some sql',
+        )),
+        ('When server is connected and start query async request with explain plan wrapper, '
+         'it returns an success message', dict(
+            function_parameters=dict(
+                sql=dict(sql='some sql', explain_plan=None),
+                trans_id=123,
+                http_session=dict(gridData={'123': dict(command_obj='')})
+            ),
+            pickle_load_return=MagicMock(
+                conn_id=1,
+                update_fetched_row_cnt=MagicMock(),
+                set_connection_id=MagicMock(),
+                auto_commit=True,
+                auto_rollback=False,
+                can_edit=lambda: True,
+                can_filter=lambda: True
+            ),
+            get_driver_exception=False,
+            manager_connection_exception=None,
+
+            is_connected_to_server=True,
+            connection_connect_return=None,
+            execute_async_return_value=[True, 'async function result output'],
+            is_begin_required=False,
+            is_rollback_required=True,
+            apply_explain_plan_wrapper_if_needed_return_value='EXPLAIN PLAN some sql',
+
+            expect_make_json_response_to_have_been_called_with=dict(
+                data=dict(
+                    status=True,
+                    result='async function result output',
+                    can_edit=True,
+                    can_filter=True,
+                    info_notifier_timeout=5
+                )
+            ),
+            expect_internal_server_error_to_have_been_called_with=None,
+            expected_logger_error=None,
+            expect_execute_void_called_with='EXPLAIN PLAN some sql',
+        )),
+    ]
+
+    @patch('pgadmin.tools.sqleditor.utils.start_running_query.apply_explain_plan_wrapper_if_needed')
+    @patch('pgadmin.tools.sqleditor.utils.start_running_query.make_json_response')
+    @patch('pgadmin.tools.sqleditor.utils.start_running_query.pickle')
+    @patch('pgadmin.tools.sqleditor.utils.start_running_query.get_driver')
+    @patch('pgadmin.tools.sqleditor.utils.start_running_query.internal_server_error')
+    @patch('pgadmin.tools.sqleditor.utils.start_running_query.update_session_grid_transaction')
+    def runTest(self, update_session_grid_transaction_mock,
+                internal_server_error_mock, get_driver_mock, pickle_mock,
+                make_json_response_mock, apply_explain_plan_wrapper_if_needed_mock):
+        """Check correct function is called to handle to run query."""
+        self.connection = None
+
+        self.loggerMock = MagicMock(error=MagicMock())
+        expected_response = Response(response=json.dumps({'errormsg': 'some value'}))
+        make_json_response_mock.return_value = expected_response
+        if self.expect_internal_server_error_to_have_been_called_with is not None:
+            internal_server_error_mock.return_value = expected_response
+        pickle_mock.loads.return_value = self.pickle_load_return
+        blueprint_mock = MagicMock(info_notifier_timeout=MagicMock(get=lambda: 5))
+
+        if self.is_begin_required:
+            StartRunningQuery.is_begin_required_for_sql_query = MagicMock(return_value=True)
+        else:
+            StartRunningQuery.is_begin_required_for_sql_query = MagicMock(return_value=False)
+        if self.is_rollback_required:
+            StartRunningQuery.is_rollback_statement_required = MagicMock(return_value=True)
+        else:
+            StartRunningQuery.is_rollback_statement_required = MagicMock(return_value=False)
+
+        apply_explain_plan_wrapper_if_needed_mock.return_value = self.apply_explain_plan_wrapper_if_needed_return_value
+
+        manager = self.__create_manager()
+        if self.get_driver_exception:
+            get_driver_mock.side_effect = get_driver_exception
+        else:
+            get_driver_mock.return_value = MagicMock(connection_manager=lambda session_id: manager)
+
+        try:
+            result = StartRunningQuery(
+                blueprint_mock,
+                self.loggerMock
+            ).execute(
+                **self.function_parameters
+            )
+            if self.manager_connection_exception is not None:
+                self.fail('Exception: "' + str(self.manager_connection_exception) + '" excepted but not raised')
+
+            self.assertEquals(result, expected_response)
+
+        except AssertionError:
+            raise
+        except Exception as exception:
+            self.assertEquals(self.manager_connection_exception, exception)
+
+        self.__mock_assertions(internal_server_error_mock, make_json_response_mock)
+        if self.is_connected_to_server:
+            apply_explain_plan_wrapper_if_needed_mock.assert_called_with(manager, self.function_parameters['sql'])
+
+    def __create_manager(self):
+        self.connection = MagicMock(
+            connected=lambda: self.is_connected_to_server,
+            connect=MagicMock(),
+            execute_async=MagicMock(),
+            execute_void=MagicMock(),
+        )
+        self.connection.connect.return_value = self.connection_connect_return
+        self.connection.execute_async.return_value = self.execute_async_return_value
+        if self.manager_connection_exception is None:
+            manager = MagicMock(
+                connection=lambda did, conn_id, use_binary_placeholder, array_to_string, auto_reconnect: self.connection
+            )
+        else:
+            manager = MagicMock()
+            manager.connection.side_effect = self.manager_connection_exception
+        return manager
+
+    def __mock_assertions(self, internal_server_error_mock, make_json_response_mock):
+        if self.expect_make_json_response_to_have_been_called_with is not None:
+            make_json_response_mock.assert_called_with(**self.expect_make_json_response_to_have_been_called_with)
+        else:
+            make_json_response_mock.assert_not_called()
+        if self.expect_internal_server_error_to_have_been_called_with is not None:
+            internal_server_error_mock.assert_called_with(**self.expect_internal_server_error_to_have_been_called_with)
+        else:
+            internal_server_error_mock.assert_not_called()
+        if self.execute_async_return_value is not None:
+            self.connection.execute_async.assert_called_with(self.expect_execute_void_called_with)
+        else:
+            self.connection.execute_async.assert_not_called()
+
+        if self.expected_logger_error is not None:
+            self.loggerMock.error.assert_called_with(self.expected_logger_error)
+        else:
+            self.loggerMock.error.assert_not_called()
+
+        if self.is_begin_required:
+            self.connection.execute_void.assert_called_with('BEGIN;')
+        elif not self.is_rollback_required:
+            self.connection.execute_void.assert_not_called()
+        if self.is_rollback_required:
+            self.connection.execute_void.assert_called_with('ROLLBACK;')
+        elif not self.is_begin_required:
+            self.connection.execute_void.assert_not_called()
diff --git a/web/pgadmin/tools/sqleditor/utils/update_session_grid_transaction.py b/web/pgadmin/tools/sqleditor/utils/update_session_grid_transaction.py
new file mode 100644
index 00000000..aef78f23
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/update_session_grid_transaction.py
@@ -0,0 +1,18 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Update session with gridData."""
+from flask import session
+
+
+def update_session_grid_transaction(trans_id, data):
+    if 'gridData' in session:
+        grid_data = session['gridData']
+        grid_data[str(trans_id)] = data
+        session['gridData'] = grid_data
diff --git a/web/pgadmin/utils/compile_template_name.py b/web/pgadmin/utils/compile_template_name.py
new file mode 100644
index 00000000..6eb2431f
--- /dev/null
+++ b/web/pgadmin/utils/compile_template_name.py
@@ -0,0 +1,17 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import os
+
+
+def compile_template_name(template_prefix, template_file_name, server_type, version):
+    if server_type == 'gpdb':
+        version_path = '#{0}#{1}#'.format(server_type, version)
+    else:
+        version_path = '#{0}#'.format(version)
+    return os.path.join(template_prefix, version_path, template_file_name)
diff --git a/web/pgadmin/utils/tests/test_compile_template_name.py b/web/pgadmin/utils/tests/test_compile_template_name.py
new file mode 100644
index 00000000..97f1b05c
--- /dev/null
+++ b/web/pgadmin/utils/tests/test_compile_template_name.py
@@ -0,0 +1,34 @@
+#######################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+from pgadmin.utils.compile_template_name import compile_template_name
+from pgadmin.utils.route import BaseTestGenerator
+
+
+class StartRunningQueryTest(BaseTestGenerator):
+    """
+    Check that the apply_explain_plan_weapper_if_needed method works as intended
+    """
+    scenarios = [
+        ('When server is Postgres and version is 10, it returns the path to the postgres template', dict(
+            server_type='pg',
+            version=100000,
+
+            expected_return_value='some/prefix/#100000#/some_file.sql'
+        )),
+        ('When server is GreenPlum and version is 5, it returns the path to the GreenPlum template', dict(
+            server_type='gpdb',
+            version=80323,
+
+            expected_return_value='some/prefix/#gpdb#80323#/some_file.sql'
+        )),
+    ]
+
+    def runTest(self):
+        result = compile_template_name('some/prefix', 'some_file.sql', self.server_type, self.version)
+        self.assertEquals(result, self.expected_return_value)
diff --git a/web/pgadmin/utils/versioned_template_loader.py b/web/pgadmin/utils/versioned_template_loader.py
index 19693012..8e8259fe 100644
--- a/web/pgadmin/utils/versioned_template_loader.py
+++ b/web/pgadmin/utils/versioned_template_loader.py
@@ -6,7 +6,6 @@
 # This software is released under the PostgreSQL Licence
 #
 ##########################################################################
-
 from flask.templating import DispatchingJinjaLoader
 from jinja2 import TemplateNotFound
 
@@ -54,6 +53,7 @@ class VersionedTemplateLoader(DispatchingJinjaLoader):
 
             template_path = path_start + '/' + \
                 server_version['name'] + '/' + file_name
+
             try:
                 return super(VersionedTemplateLoader, self).get_source(
                     environment, template_path
diff --git a/web/regression/javascript/fake_endpoints.js b/web/regression/javascript/fake_endpoints.js
index 0250a0df..63ab05dc 100644
--- a/web/regression/javascript/fake_endpoints.js
+++ b/web/regression/javascript/fake_endpoints.js
@@ -8,5 +8,9 @@
 //////////////////////////////////////////////////////////////////////////
 
 define(function () {
-  return {'static': '/base/pgadmin/static/<path:filename>'};
+  return {
+    'static': '/base/pgadmin/static/<path:filename>',
+    'sqleditor.poll': '/sqleditor/query_tool/poll/<path:trans_id>',
+    'sqleditor.query_tool_start': '/sqleditor/query_tool/start/<path:trans_id>'
+  };
 });
diff --git a/web/regression/javascript/sqleditor/execute_query_spec.js b/web/regression/javascript/sqleditor/execute_query_spec.js
new file mode 100644
index 00000000..98faed7d
--- /dev/null
+++ b/web/regression/javascript/sqleditor/execute_query_spec.js
@@ -0,0 +1,1702 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import * as subject from 'sources/sqleditor/execute_query';
+import * as transaction from 'sources/sqleditor/is_new_transaction_required';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import $ from 'jquery';
+
+const context = describe;
+
+describe('ExecuteQuery', () => {
+  let sqlEditorMock;
+  let networkMock;
+  let executeQuery;
+  let userManagementMock;
+  let isNewTransactionRequiredMock;
+
+  const startTime = new Date(2018, 1, 29, 12, 15, 52);
+  beforeEach(() => {
+    networkMock = new MockAdapter(axios);
+    jasmine.addMatchers({jQuerytoHaveBeenCalledWith: jQuerytoHaveBeenCalledWith});
+    userManagementMock = jasmine.createSpyObj('UserManagement', [
+      'is_pga_login_required',
+      'pga_login',
+    ]);
+
+    sqlEditorMock = jasmine.createSpyObj('SqlEditor', [
+      'call_render_after_poll',
+      'disable_tool_buttons',
+      'resetQueryHistoryObject',
+      'setIsQueryRunning',
+      'trigger',
+      'update_msg_history',
+      '_highlight_error',
+      '_init_polling_flags',
+      'save_state',
+      'init_transaction',
+    ]);
+    sqlEditorMock.transId = 123;
+    sqlEditorMock.rows_affected = 1000;
+    executeQuery = new subject.ExecuteQuery(sqlEditorMock, userManagementMock);
+    isNewTransactionRequiredMock = spyOn(transaction, 'is_new_transaction_required');
+  });
+
+  describe('#poll', () => {
+    let cancelButtonSpy;
+    let response;
+
+    beforeEach(() => {
+      sqlEditorMock.POLL_FALLBACK_TIME = () => {
+        return 0;
+      };
+
+      cancelButtonSpy = spyOn($.fn, 'prop');
+      executeQuery.delayedPoll = jasmine.createSpy('ExecuteQuery.delayedPoll');
+    });
+
+    afterEach(() => {
+    });
+
+    context('when SQLEditor is the query tool', () => {
+      beforeEach(() => {
+        sqlEditorMock.is_query_tool = true;
+      });
+
+      describe('when server answer with success', () => {
+        describe('when query was successful', () => {
+          beforeEach(() => {
+            response = {
+              data: {status: 'Success'},
+            };
+            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+
+            executeQuery.poll();
+          });
+
+          it('should update the loading icon message', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.trigger)
+                .toHaveBeenCalledWith(
+                  'pgadmin-sqleditor:loading-icon:message',
+                  'Loading data from the database server and rendering...'
+                );
+              done();
+            }, 0);
+          });
+
+          it('should render the results', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.call_render_after_poll)
+                .toHaveBeenCalledWith({status: 'Success'});
+              done();
+            }, 0);
+          });
+        });
+
+        describe('when query is still running', () => {
+          context('when no additional information is returned', () => {
+            beforeEach(() => {
+              response = {
+                data: {status: 'Busy'},
+              };
+
+              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+              executeQuery.poll();
+            });
+
+            it('should set the flag to inform SQLEditor a query is running', (done) => {
+              setTimeout(() => {
+                expect(sqlEditorMock.setIsQueryRunning)
+                  .toHaveBeenCalledWith(true);
+                done();
+              }, 0);
+            });
+
+            it('should does not update history', (done) => {
+              setTimeout(() => {
+                expect(sqlEditorMock.update_msg_history).not
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+            });
+
+            it('should recursively call polling', (done) => {
+              setTimeout(() => {
+                expect(executeQuery.delayedPoll)
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+            });
+          });
+
+          context('when additional information is returned', () => {
+            beforeEach(() => {
+              response = {
+                data: {
+                  status: 'Busy',
+                  result: 'Some important result',
+                },
+              };
+
+              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+              executeQuery.poll();
+            });
+
+            it('should set the flag to inform SQLEditor a query is running', (done) => {
+              setTimeout(() => {
+                expect(sqlEditorMock.setIsQueryRunning)
+                  .toHaveBeenCalledWith(true);
+                done();
+              }, 0);
+            });
+
+            it('should update history message', (done) => {
+              setTimeout(() => {
+                expect(sqlEditorMock.update_msg_history)
+                  .toHaveBeenCalledWith('Busy', 'Some important result', false);
+                done();
+              }, 0);
+            });
+
+            it('should recursively call polling', (done) => {
+              setTimeout(() => {
+                expect(executeQuery.delayedPoll)
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+            });
+          });
+        });
+
+        describe('when the application lost connection with the database', () => {
+          beforeEach(() => {
+            response = {
+              data: {
+                status: 'NotConnected',
+                result: 'Some interesting result',
+              },
+            };
+            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+
+            executeQuery.poll();
+          });
+
+          it('should hide the loading icon', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.trigger)
+                .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+              done();
+            }, 0);
+          });
+
+          it('should add new entry to history and update the Messages tab and clear the result grid', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.update_msg_history)
+                .toHaveBeenCalledWith(
+                  false,
+                  'Some interesting result',
+                  true
+                );
+              done();
+            }, 0);
+          });
+
+          it('should enable the tool buttons', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.disable_tool_buttons)
+                  .toHaveBeenCalledWith(false);
+                done();
+              }, 0);
+          });
+
+          it('should disable the cancel button', (done) => {
+            setTimeout(
+              () => {
+                expect(cancelButtonSpy)
+                  .toHaveBeenCalledWith('disabled', true);
+                done();
+              }, 0);
+          });
+        });
+
+        describe('when query was cancelled', () => {
+          beforeEach(() => {
+            response = {
+              data: {status: 'Cancel'},
+            };
+            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+
+            executeQuery.poll();
+          });
+
+          it('should hide the loading icon', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.trigger)
+                .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+              done();
+            }, 0);
+          });
+
+          it('should add new entry to history, add cancellation message to Messages tab and clear the result grid', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.update_msg_history)
+                .toHaveBeenCalledWith(
+                  false,
+                  'Execution Cancelled!',
+                  true
+                );
+              done();
+            }, 0);
+          });
+        });
+      });
+
+      describe('when an error occur', () => {
+        let errorMessageJson = {
+          errormsg: 'Some error in JSON',
+        };
+        let errorMessageText = 'Some plain text error';
+
+        describe('when the connection to the server was lost', () => {
+          describe('when JSON response is available', () => {
+            describe('when login is not required', () => {
+              beforeEach(() => {
+                userManagementMock.is_pga_login_required.and.returnValue(false);
+                response = {responseJSON: errorMessageJson};
+                networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
+
+                executeQuery.poll();
+              });
+
+              it('should hide the loading icon', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.trigger)
+                      .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                    done();
+                  }, 0);
+              });
+
+              it('should reset last query information', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.resetQueryHistoryObject)
+                      .toHaveBeenCalledWith(sqlEditorMock);
+                    done();
+                  }, 0);
+              });
+
+              it('should highlight the error in the SQL panel', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock._highlight_error)
+                      .toHaveBeenCalledWith('Some error in JSON');
+                    done();
+                  }, 0);
+              });
+
+              it('should add new entry to history and update the Messages tab', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.update_msg_history)
+                      .toHaveBeenCalledWith(false, 'Some error in JSON');
+                    done();
+                  }, 0);
+              });
+
+              it('should enable the tool buttons', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.disable_tool_buttons)
+                      .toHaveBeenCalledWith(false);
+                    done();
+                  }, 0);
+              });
+
+              it('should disable the cancel button', (done) => {
+                setTimeout(
+                  () => {
+                    expect(cancelButtonSpy)
+                      .toHaveBeenCalledWith('disabled', true);
+                    done();
+                  }, 0);
+              });
+
+              it('should not login is displayed', (done) => {
+                setTimeout(
+                  () => {
+                    expect(userManagementMock.pga_login).not
+                      .toHaveBeenCalled();
+                    done();
+                  }, 0);
+              });
+            });
+
+            describe('when login is required', () => {
+              beforeEach(() => {
+                userManagementMock.is_pga_login_required.and.returnValue(true);
+                response = {responseJSON: errorMessageJson};
+                networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
+
+                executeQuery.poll();
+              });
+
+              it('should hide the loading icon', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.trigger)
+                      .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                    done();
+                  }, 0);
+              });
+
+              it('should reset last query information', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.resetQueryHistoryObject)
+                      .toHaveBeenCalledWith(sqlEditorMock);
+                    done();
+                  }, 0);
+              });
+
+              it('should not highlight the error in the SQL panel', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock._highlight_error).not
+                      .toHaveBeenCalled();
+                    done();
+                  }, 0);
+              });
+
+              it('should not add new entry to history and update the Messages tab', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.update_msg_history).not
+                      .toHaveBeenCalled();
+                    done();
+                  }, 0);
+              });
+
+              it('should enable the tool buttons', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.disable_tool_buttons)
+                      .toHaveBeenCalledWith(false);
+                    done();
+                  }, 0);
+              });
+
+              it('should disable the cancel button', (done) => {
+                setTimeout(
+                  () => {
+                    expect(cancelButtonSpy)
+                      .toHaveBeenCalledWith('disabled', true);
+                    done();
+                  }, 0);
+              });
+
+              it('should login is displayed', (done) => {
+                setTimeout(
+                  () => {
+                    expect(userManagementMock.pga_login)
+                      .toHaveBeenCalled();
+                    done();
+                  }, 0);
+              });
+            });
+          });
+
+          describe('when no JSON response is available', () => {
+            describe('when login is not required', () => {
+              beforeEach(() => {
+                userManagementMock.is_pga_login_required.and.returnValue(false);
+                response = {
+                  errormsg: errorMessageText,
+                };
+                networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
+
+                executeQuery.poll();
+              });
+
+              it('should hide the loading icon', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.trigger)
+                      .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                    done();
+                  }, 0);
+              });
+
+              it('should reset last query information', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.resetQueryHistoryObject)
+                      .toHaveBeenCalledWith(sqlEditorMock);
+                    done();
+                  }, 0);
+              });
+
+              it('should highlight the error in the SQL panel', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock._highlight_error)
+                      .toHaveBeenCalledWith('Some plain text error');
+                    done();
+                  }, 0);
+              });
+
+              it('should add new entry to history and update the Messages tab', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.update_msg_history)
+                      .toHaveBeenCalledWith(false, 'Some plain text error');
+                    done();
+                  }, 0);
+              });
+
+              it('should enable the tool buttons', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.disable_tool_buttons)
+                      .toHaveBeenCalledWith(false);
+                    done();
+                  }, 0);
+              });
+
+              it('should disable the cancel button', (done) => {
+                setTimeout(
+                  () => {
+                    expect(cancelButtonSpy)
+                      .toHaveBeenCalledWith('disabled', true);
+                    done();
+                  }, 0);
+              });
+
+              it('should login is not displayed', (done) => {
+                setTimeout(
+                  () => {
+                    expect(userManagementMock.pga_login).not
+                      .toHaveBeenCalled();
+                    done();
+                  }, 0);
+              });
+            });
+
+            describe('when login is required', () => {
+              beforeEach(() => {
+                userManagementMock.is_pga_login_required.and.returnValue(true);
+                response = {
+                  errormsg: errorMessageText,
+                };
+                networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
+
+                executeQuery.poll();
+              });
+
+              it('should hide the loading icon', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.trigger)
+                      .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                    done();
+                  }, 0);
+              });
+
+              it('should reset last query information', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.resetQueryHistoryObject)
+                      .toHaveBeenCalledWith(sqlEditorMock);
+                    done();
+                  }, 0);
+              });
+
+              it('should not highlight the error in the SQL panel', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock._highlight_error).not
+                      .toHaveBeenCalled();
+                    done();
+                  }, 0);
+              });
+
+              it('should not add new entry to history and update the Messages tab', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.update_msg_history).not
+                      .toHaveBeenCalled();
+                    done();
+                  }, 0);
+              });
+
+              it('should enable the tool buttons', (done) => {
+                setTimeout(
+                  () => {
+                    expect(sqlEditorMock.disable_tool_buttons)
+                      .toHaveBeenCalledWith(false);
+                    done();
+                  }, 0);
+              });
+
+              it('should disable the cancel button', (done) => {
+                setTimeout(
+                  () => {
+                    expect(cancelButtonSpy)
+                      .toHaveBeenCalledWith('disabled', true);
+                    done();
+                  }, 0);
+              });
+
+              it('should login is displayed', (done) => {
+                setTimeout(
+                  () => {
+                    expect(userManagementMock.pga_login)
+                      .toHaveBeenCalled();
+                    done();
+                  }, 0);
+              });
+            });
+          });
+
+          describe('when cannot reach the Python Server', () => {
+            beforeEach(() => {
+              response = {readyState: 0};
+              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
+
+              executeQuery.poll();
+            });
+
+            it('should hide the loading icon', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.trigger)
+                    .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                  done();
+                }, 0);
+            });
+
+            it('should reset last query information', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.resetQueryHistoryObject)
+                    .toHaveBeenCalledWith(sqlEditorMock);
+                  done();
+                }, 0);
+            });
+
+            it('should not highlight the error in the SQL panel', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock._highlight_error).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+
+            it('should add new entry to history and update the Messages tab', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.update_msg_history)
+                    .toHaveBeenCalledWith(false, 'Not connected to the server or the connection to the server has been closed.');
+                  done();
+                }, 0);
+            });
+
+            it('should enable the tool buttons', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.disable_tool_buttons)
+                    .toHaveBeenCalledWith(false);
+                  done();
+                }, 0);
+            });
+
+            it('should disable the cancel button', (done) => {
+              setTimeout(
+                () => {
+                  expect(cancelButtonSpy)
+                    .toHaveBeenCalledWith('disabled', true);
+                  done();
+                }, 0);
+            });
+
+            it('should login is not displayed', (done) => {
+              setTimeout(
+                () => {
+                  expect(userManagementMock.pga_login).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+          });
+        });
+      });
+    });
+
+    context('when SQLEditor is NOT the query tool', () => {
+      beforeEach(() => {
+        sqlEditorMock.is_query_tool = false;
+      });
+
+      describe('when server answer with success', () => {
+        describe('when query was successful', () => {
+          beforeEach(() => {
+            response = {
+              data: {status: 'Success'},
+            };
+            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+
+            executeQuery.poll();
+          });
+
+          it('should update the loading icon message', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.trigger)
+                .toHaveBeenCalledWith(
+                  'pgadmin-sqleditor:loading-icon:message',
+                  'Loading data from the database server and rendering...'
+                );
+              done();
+            }, 0);
+          });
+
+          it('should render the results', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.call_render_after_poll)
+                .toHaveBeenCalledWith({status: 'Success'});
+              done();
+            }, 0);
+          });
+        });
+
+        describe('when query is still running', () => {
+          context('when no additional information is returned', () => {
+            beforeEach(() => {
+              response = {
+                data: {status: 'Busy'},
+              };
+
+              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+              executeQuery.poll();
+            });
+
+            it('should set the flag to inform SQLEditor a query is running', (done) => {
+              setTimeout(() => {
+                expect(sqlEditorMock.setIsQueryRunning)
+                  .toHaveBeenCalledWith(true);
+                done();
+              }, 0);
+            });
+
+            it('should does not update history', (done) => {
+              setTimeout(() => {
+                expect(sqlEditorMock.update_msg_history).not
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+            });
+
+            it('should recursively call polling', (done) => {
+              setTimeout(() => {
+                expect(executeQuery.delayedPoll)
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+            });
+          });
+
+          context('when additional information is returned', () => {
+            beforeEach(() => {
+              response = {
+                data: {
+                  status: 'Busy',
+                  result: 'Some important result',
+                },
+              };
+
+              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+              executeQuery.poll();
+            });
+
+            it('should set the flag to inform SQLEditor a query is running', (done) => {
+              setTimeout(() => {
+                expect(sqlEditorMock.setIsQueryRunning)
+                  .toHaveBeenCalledWith(true);
+                done();
+              }, 0);
+            });
+
+            it('should update history message', (done) => {
+              setTimeout(() => {
+                expect(sqlEditorMock.update_msg_history)
+                  .toHaveBeenCalledWith('Busy', 'Some important result', false);
+                done();
+              }, 0);
+            });
+
+            it('should recursively call polling', (done) => {
+              setTimeout(() => {
+                expect(executeQuery.delayedPoll)
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+            });
+          });
+        });
+
+        describe('when the application lost connection with the database', () => {
+          beforeEach(() => {
+            response = {
+              data: {
+                status: 'NotConnected',
+                result: 'Some interesting result',
+              },
+            };
+            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+
+            executeQuery.poll();
+          });
+
+          it('should hide the loading icon', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.trigger)
+                .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+              done();
+            }, 0);
+          });
+
+          it('should add new entry to history and update the Messages tab and clear the result grid', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.update_msg_history)
+                .toHaveBeenCalledWith(
+                  false,
+                  'Some interesting result',
+                  true
+                );
+              done();
+            }, 0);
+          });
+
+          it('should NOT enable the tool buttons', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.disable_tool_buttons).not
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+          });
+
+          it('should NOT disable the cancel button', (done) => {
+            setTimeout(
+              () => {
+                expect(cancelButtonSpy).not
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+          });
+        });
+
+        describe('when query was cancelled', () => {
+          beforeEach(() => {
+            response = {
+              data: {status: 'Cancel'},
+            };
+            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+
+            executeQuery.poll();
+          });
+
+          it('should hide the loading icon', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.trigger)
+                .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+              done();
+            }, 0);
+          });
+
+          it('should add new entry to history, add cancellation message to Messages tab and clear the result grid', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.update_msg_history)
+                .toHaveBeenCalledWith(
+                  false,
+                  'Execution Cancelled!',
+                  true
+                );
+              done();
+            }, 0);
+          });
+        });
+      });
+
+      describe('when an error occur', () => {
+        let errorMessageJson = {
+          errormsg: 'Some error in JSON',
+        };
+        let errorMessageText = 'Some plain text error';
+        let response;
+
+        describe('when the connection to the server was lost', () => {
+          describe('when JSON response is available', () => {
+            beforeEach(() => {
+              response = {responseJSON: errorMessageJson};
+              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
+
+              executeQuery.poll();
+            });
+
+            it('should hide the loading icon', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.trigger)
+                    .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                  done();
+                }, 0);
+            });
+
+            it('should reset last query information', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.resetQueryHistoryObject)
+                    .toHaveBeenCalledWith(sqlEditorMock);
+                  done();
+                }, 0);
+            });
+
+            it('should highlight the error in the SQL panel', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock._highlight_error)
+                    .toHaveBeenCalledWith('Some error in JSON');
+                  done();
+                }, 0);
+            });
+
+            it('should add new entry to history and update the Messages tab', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.update_msg_history)
+                    .toHaveBeenCalledWith(false, 'Some error in JSON');
+                  done();
+                }, 0);
+            });
+
+            it('should enable the tool buttons', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.disable_tool_buttons).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+
+            it('should disable the cancel button', (done) => {
+              setTimeout(
+                () => {
+                  expect(cancelButtonSpy).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+          });
+          describe('when no JSON response is available', () => {
+            beforeEach(() => {
+              response = {errormsg: errorMessageText};
+              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
+
+              executeQuery.poll();
+            });
+
+            it('should hide the loading icon', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.trigger)
+                    .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                  done();
+                }, 0);
+            });
+
+            it('should reset last query information', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.resetQueryHistoryObject)
+                    .toHaveBeenCalledWith(sqlEditorMock);
+                  done();
+                }, 0);
+            });
+
+            it('should highlight the error in the SQL panel', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock._highlight_error)
+                    .toHaveBeenCalledWith('Some plain text error');
+                  done();
+                }, 0);
+            });
+
+            it('should add new entry to history and update the Messages tab', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.update_msg_history)
+                    .toHaveBeenCalledWith(false, 'Some plain text error');
+                  done();
+                }, 0);
+            });
+
+            it('should not enable the tool buttons', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.disable_tool_buttons).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+
+            it('should not disable the cancel button', (done) => {
+              setTimeout(
+                () => {
+                  expect(cancelButtonSpy).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+          });
+
+          describe('when cannot reach the Python Server', () => {
+            beforeEach(() => {
+              response = {readyState: 0};
+              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
+
+              executeQuery.poll();
+            });
+
+            it('should hide the loading icon', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.trigger)
+                    .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                  done();
+                }, 0);
+            });
+
+            it('should reset last query information', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.resetQueryHistoryObject)
+                    .toHaveBeenCalledWith(sqlEditorMock);
+                  done();
+                }, 0);
+            });
+
+            it('should not highlight the error in the SQL panel', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock._highlight_error).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+
+            it('should add new entry to history and update the Messages tab', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.update_msg_history)
+                    .toHaveBeenCalledWith(false, 'Not connected to the server or the connection to the server has been closed.');
+                  done();
+                }, 0);
+            });
+
+            it('should enable the tool buttons', (done) => {
+              setTimeout(
+                () => {
+                  expect(sqlEditorMock.disable_tool_buttons).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+
+            it('should disable the cancel button', (done) => {
+              setTimeout(
+                () => {
+                  expect(cancelButtonSpy).not
+                    .toHaveBeenCalled();
+                  done();
+                }, 0);
+            });
+          });
+        });
+      });
+    });
+  });
+
+  describe('#execute', () => {
+    let response;
+
+    beforeEach(() => {
+      response = {
+        'info': '',
+        'errormsg': '',
+        'data': {
+          'status': true,
+          'can_edit': false,
+          'info_notifier_timeout': 5,
+          'result': '2',
+          'can_filter': false,
+        },
+        'result': null,
+        'success': 1,
+      };
+    });
+
+    context('when the SQL statement is empty', () => {
+      it('should return without executing', (done) => {
+        let wasNetworkCalled = false;
+        networkMock.onAny('/sqleditor/query_tool/start/123').reply(() => {
+          wasNetworkCalled = true;
+        });
+
+        executeQuery.execute('', {});
+
+        setTimeout(() => {
+          expect(wasNetworkCalled).toBe(false);
+          done();
+        }, 0);
+      });
+    });
+
+    context('when the SQL statement is not empty', () => {
+      let pollSpy;
+      let jqueryPropSpy;
+
+      beforeEach(() => {
+        jqueryPropSpy = spyOn($.fn, 'prop');
+        sqlEditorMock.gridView = {};
+        sqlEditorMock.gridView.query_tool_obj = jasmine.createSpyObj(
+          'QueryToolObject',
+          ['removeLineClass']
+        );
+      });
+
+      describe('before the backend request', () => {
+        beforeEach(() => {
+          jasmine.clock().install();
+          jasmine.clock().mockDate(startTime);
+          jasmine.clock().tick(50);
+          networkMock.onAny('/sqleditor/query_tool/start/123').reply(200, response);
+          pollSpy = spyOn(executeQuery, 'poll');
+          executeQuery.execute('some sql query', '');
+        });
+
+        afterEach(function () {
+          jasmine.clock().uninstall();
+        });
+
+        it('should update loading text to "Initializing query execution"', () => {
+          expect(sqlEditorMock.trigger)
+            .toHaveBeenCalledWith(
+              'pgadmin-sqleditor:loading-icon:show',
+              'Initializing query execution...'
+            );
+        });
+
+        it('disables the run query button', () => {
+          const callToProp = findJQueryCallWithSelector(jqueryPropSpy, '#btn-flash');
+
+          expect(callToProp).jQuerytoHaveBeenCalledWith('disabled', true);
+        });
+
+        it('enable the cancel query button', () => {
+          const callToProp = findJQueryCallWithSelector(jqueryPropSpy, '#btn-cancel-query');
+
+          expect(callToProp).jQuerytoHaveBeenCalledWith('disabled', false);
+        });
+
+        it('disable the query tool buttons', () => {
+          expect(sqlEditorMock.disable_tool_buttons).toHaveBeenCalledWith(true);
+        });
+
+        it('initializes the polling flags', () => {
+          expect(sqlEditorMock._init_polling_flags).toHaveBeenCalled();
+        });
+
+        it('save the query', () => {
+          expect(sqlEditorMock.query).toEqual('some sql query');
+        });
+
+        it('reset the number of rows that were affected', () => {
+          expect(sqlEditorMock.rows_affected).toBe(0);
+        });
+
+        it('reset query start time', () => {
+          expect(sqlEditorMock.query_start_time.getTime()).toEqual(startTime.getTime() + 50);
+        });
+      });
+
+      describe('when HTTP return 200', () => {
+        describe('when backend informs that query started successfully', () => {
+          beforeEach(() => {
+            networkMock.onAny('/sqleditor/query_tool/start/123').reply(200, response);
+            pollSpy = spyOn(executeQuery, 'delayedPoll');
+            executeQuery.execute('some sql query', '');
+          });
+
+          it('should changes the loading message to "Waiting for the query execution to complete"', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.trigger).toHaveBeenCalledWith(
+                'pgadmin-sqleditor:loading-icon:message',
+                'Waiting for the query execution to complete...'
+              );
+              done();
+            }, 0);
+          });
+
+          it('should update the can edit flag', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.can_edit).toBe(false);
+              done();
+            }, 0);
+          });
+
+          it('should update the can filter flag', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.can_filter).toBe(false);
+              done();
+            }, 0);
+          });
+
+          it('should update information notifier timeout', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.info_notifier_timeout).toBe(5);
+              done();
+            }, 0);
+          });
+
+          it('should start polling', (done) => {
+            setTimeout(() => {
+              expect(pollSpy).toHaveBeenCalled();
+              done();
+            }, 0);
+          });
+        });
+
+        describe('when explain plan is not empty', () => {
+          it('should send the explain plan informatioon through the wire', (done) => {
+            networkMock.onAny('/sqleditor/query_tool/start/123').reply((config) => {
+              setTimeout(() => {
+                expect(config.data).toEqual(JSON.stringify({
+                  sql: 'some sql query',
+                  explain_plan: {
+                    buffers: true,
+                    analyze: false,
+                    timing: true,
+                    summary: false,
+                  },
+                }));
+
+                done();
+              }, 0);
+              return [200, ''];
+            });
+            pollSpy = spyOn(executeQuery, 'delayedPoll');
+            executeQuery.execute('some sql query', {
+              buffers: true,
+              analyze: false,
+              timing: true,
+              summary: false,
+            });
+          });
+
+
+        });
+
+        describe('when backend informs that there was a problem with the query', () => {
+          beforeEach(() => {
+            response.data.status = false;
+            response.data.result = 'something went wrong';
+            networkMock.onAny('/sqleditor/query_tool/start/123').reply(200, response);
+            pollSpy = spyOn(executeQuery, 'poll');
+            executeQuery.execute('some sql query', '');
+          });
+
+          it('hide the loading screen', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+              done();
+            }, 0);
+          });
+
+          it('disable the cancel query button', (done) => {
+            setTimeout(() => {
+              const callToProp = findJQueryCallWithSelector(jqueryPropSpy, '#btn-cancel-query');
+
+              expect(callToProp).jQuerytoHaveBeenCalledWith('disabled', true);
+              done();
+            }, 0);
+          });
+
+          it('enable the query tool buttons', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.disable_tool_buttons).toHaveBeenCalledWith(false);
+              done();
+            }, 0);
+          });
+
+          it('update the history tab with the result message', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.update_msg_history).toHaveBeenCalledWith(false, 'something went wrong');
+              done();
+            }, 0);
+          });
+
+          it('highlight the error in the editor', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock._highlight_error).toHaveBeenCalledWith('something went wrong');
+              done();
+            }, 0);
+          });
+
+          it('should not start polling', (done) => {
+            setTimeout(() => {
+              expect(pollSpy).not.toHaveBeenCalled();
+              done();
+            }, 0);
+          });
+        });
+
+        describe('when there is a marker set in the grid', () => {
+          let markerClearSpy;
+          beforeEach(() => {
+            sqlEditorMock.gridView.marker = jasmine.createSpyObj(
+              'GridViewMarker',
+              ['clear']
+            );
+            markerClearSpy = sqlEditorMock.gridView.marker.clear;
+            networkMock.onAny('/sqleditor/query_tool/start/123').reply(200, response);
+            pollSpy = spyOn(executeQuery, 'poll');
+            executeQuery.execute('some sql query', '');
+          });
+
+          it('should call clear function on marker', (done) => {
+            setTimeout(() => {
+              expect(markerClearSpy).toHaveBeenCalled();
+              done();
+            }, 0);
+          });
+
+          it('should removes the marker', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.gridView.marker).toEqual(null);
+              done();
+            }, 0);
+          });
+
+          it('should remove CSS classes from the editor', (done) => {
+            setTimeout(() => {
+              expect(sqlEditorMock.gridView.query_tool_obj.removeLineClass)
+                .toHaveBeenCalledWith(sqlEditorMock.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
+              done();
+            }, 0);
+          });
+        });
+      });
+
+      describe('when cannot reach the Python Server', () => {
+        beforeEach(() => {
+          response = {
+            readyState: 0,
+          };
+          networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
+
+
+          executeQuery.execute('some sql query', '');
+        });
+
+        it('should hide the loading icon', (done) => {
+          setTimeout(
+            () => {
+              expect(sqlEditorMock.trigger)
+                .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+              done();
+            }, 0);
+        });
+
+        it('should not highlight the error in the SQL panel', (done) => {
+          setTimeout(
+            () => {
+              expect(sqlEditorMock._highlight_error).not
+                .toHaveBeenCalled();
+              done();
+            }, 0);
+        });
+
+        it('should add new entry to history and update the Messages tab', (done) => {
+          setTimeout(
+            () => {
+              expect(sqlEditorMock.update_msg_history)
+                .toHaveBeenCalledWith(false, 'Not connected to the server or the connection to the server has been closed.');
+              done();
+            }, 0);
+        });
+
+        it('should enable the tool buttons', (done) => {
+          setTimeout(
+            () => {
+              expect(sqlEditorMock.disable_tool_buttons)
+                .toHaveBeenCalledWith(false);
+              done();
+            }, 0);
+        });
+
+        it('should disable the cancel button', (done) => {
+          setTimeout(
+            () => {
+              const callToProp = findJQueryCallWithSelector(jqueryPropSpy, '#btn-cancel-query');
+
+              expect(callToProp).jQuerytoHaveBeenCalledWith('disabled', true);
+              done();
+            }, 0);
+        });
+      });
+
+      describe('when error is returned by the server', () => {
+        describe('when login is not required', () => {
+          beforeEach(() => {
+            userManagementMock.is_pga_login_required.and.returnValue(false);
+            response.errormsg = 'some error message';
+            networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
+
+
+            executeQuery.execute('some sql query', '');
+          });
+
+          it('should hide the loading icon', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.trigger)
+                  .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                done();
+              }, 0);
+          });
+
+          it('should not highlight the error in the SQL panel', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock._highlight_error).not
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+          });
+
+          it('should add new entry to history and update the Messages tab', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.update_msg_history)
+                  .toHaveBeenCalledWith(false, 'some error message');
+                done();
+              }, 0);
+          });
+
+          it('should enable the tool buttons', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.disable_tool_buttons)
+                  .toHaveBeenCalledWith(false);
+                done();
+              }, 0);
+          });
+
+          it('should disable the cancel button', (done) => {
+            setTimeout(
+              () => {
+                const callToProp = findJQueryCallWithSelector(jqueryPropSpy, '#btn-cancel-query');
+
+                expect(callToProp).jQuerytoHaveBeenCalledWith('disabled', true);
+                done();
+              }, 0);
+          });
+
+          it('should not save the state', () => {
+            setTimeout(() => {
+              expect(sqlEditorMock.save_state).not.toHaveBeenCalled();
+            }, 0);
+          });
+
+          it('should not display pga login', () => {
+            setTimeout(() => {
+              expect(userManagementMock.pga_login).not.toHaveBeenCalled();
+            }, 0);
+          });
+        });
+        describe('when login is required', () => {
+          beforeEach(() => {
+            userManagementMock.is_pga_login_required.and.returnValue(true);
+            response.errormsg = 'some error message';
+            networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
+
+
+            executeQuery.execute('some sql query', '');
+          });
+
+          it('should hide the loading icon', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.trigger)
+                  .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                done();
+              }, 0);
+          });
+
+          it('should not highlight the error in the SQL panel', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock._highlight_error).not
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+          });
+
+          it('should add new entry to history and update the Messages tab', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.update_msg_history)
+                  .toHaveBeenCalledWith(false, 'some error message');
+                done();
+              }, 0);
+          });
+
+          it('should enable the tool buttons', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.disable_tool_buttons)
+                  .toHaveBeenCalledWith(false);
+                done();
+              }, 0);
+          });
+
+          it('should disable the cancel button', (done) => {
+            setTimeout(
+              () => {
+                const callToProp = findJQueryCallWithSelector(jqueryPropSpy, '#btn-cancel-query');
+
+                expect(callToProp).jQuerytoHaveBeenCalledWith('disabled', true);
+                done();
+              }, 0);
+          });
+
+          it('should save the state', () => {
+            setTimeout(() => {
+              expect(sqlEditorMock.save_state).toHaveBeenCalledWith(
+                'execute',
+                ['']
+              );
+            }, 0);
+          });
+
+          it('should display pga login', () => {
+            setTimeout(() => {
+              expect(userManagementMock.pga_login).toHaveBeenCalled();
+            }, 0);
+          });
+        });
+        describe('when a new transaction is not required', () => {
+          beforeEach(() => {
+            isNewTransactionRequiredMock.and.returnValue(false);
+            response.errormsg = 'some error message';
+            networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
+
+
+            executeQuery.execute('some sql query', '');
+          });
+
+          it('should hide the loading icon', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.trigger)
+                  .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                done();
+              }, 0);
+          });
+
+          it('should not highlight the error in the SQL panel', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock._highlight_error).not
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+          });
+
+          it('should add new entry to history and update the Messages tab', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.update_msg_history)
+                  .toHaveBeenCalledWith(false, 'some error message');
+                done();
+              }, 0);
+          });
+
+          it('should enable the tool buttons', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.disable_tool_buttons)
+                  .toHaveBeenCalledWith(false);
+                done();
+              }, 0);
+          });
+
+          it('should disable the cancel button', (done) => {
+            setTimeout(
+              () => {
+                const callToProp = findJQueryCallWithSelector(jqueryPropSpy, '#btn-cancel-query');
+
+                expect(callToProp).jQuerytoHaveBeenCalledWith('disabled', true);
+                done();
+              }, 0);
+          });
+
+          it('should not save the state', () => {
+            setTimeout(() => {
+              expect(sqlEditorMock.save_state).not.toHaveBeenCalled();
+            }, 0);
+          });
+
+          it('should not display pga login', () => {
+            setTimeout(() => {
+              expect(userManagementMock.pga_login).not.toHaveBeenCalled();
+            }, 0);
+          });
+
+          it('should not initialize a new transaction', () => {
+            setTimeout(() => {
+              expect(sqlEditorMock.init_transaction).not.toHaveBeenCalled();
+            }, 0);
+          });
+        });
+
+        describe('when a new transaction is required', () => {
+          beforeEach(() => {
+            isNewTransactionRequiredMock.and.returnValue(true);
+            response.errormsg = 'some error message';
+            networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
+
+            executeQuery.execute('some sql query', '');
+          });
+
+          it('should hide the loading icon', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.trigger)
+                  .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
+                done();
+              }, 0);
+          });
+
+          it('should not highlight the error in the SQL panel', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock._highlight_error).not
+                  .toHaveBeenCalled();
+                done();
+              }, 0);
+          });
+
+          it('should add new entry to history and update the Messages tab', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.update_msg_history)
+                  .toHaveBeenCalledWith(false, 'some error message');
+                done();
+              }, 0);
+          });
+
+          it('should enable the tool buttons', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.disable_tool_buttons)
+                  .toHaveBeenCalledWith(false);
+                done();
+              }, 0);
+          });
+
+          it('should disable the cancel button', (done) => {
+            setTimeout(
+              () => {
+                const callToProp = findJQueryCallWithSelector(jqueryPropSpy, '#btn-cancel-query');
+
+                expect(callToProp).jQuerytoHaveBeenCalledWith('disabled', true);
+                done();
+              }, 0);
+          });
+
+          it('should save the state', () => {
+            setTimeout(() => {
+              expect(sqlEditorMock.save_state).toHaveBeenCalledWith(
+                'execute',
+                ['']
+              );
+            }, 0);
+          });
+
+          it('should not display pga login', () => {
+            setTimeout(() => {
+              expect(userManagementMock.pga_login).not.toHaveBeenCalled();
+            }, 0);
+          });
+
+          it('should initialize a new transaction', () => {
+            setTimeout(() => {
+              expect(sqlEditorMock.init_transaction).toHaveBeenCalled();
+            }, 0);
+          });
+        });
+      });
+    });
+  });
+
+  let findJQueryCallWithSelector = (jquerySpy, selector) => {
+    let result = undefined;
+
+    jquerySpy.calls.all().forEach((call) => {
+      if (call.object.selector === selector) {
+        result = call;
+      }
+    });
+    return result;
+  };
+});
+
+const jQuerytoHaveBeenCalledWith = function (util) {
+  return {
+    compare: function (actual) {
+      let result = {};
+      let expectedArgs = jasmine.util.argsToArray(arguments).slice(1);
+      if (actual.object === undefined || actual.object.selector === undefined) {
+        throw new Error('Expected a JQuery object, but got ' + jasmine.pp(actual) + '.');
+      }
+
+      result.pass = util.equals(actual.args, expectedArgs, '');
+      if (result.pass) {
+        result.message = 'larifo';
+      } else {
+        result.message =
+          'Expected jquery with selector "' +
+          actual.object.selector +
+          '" to have been called with ' +
+          jasmine.pp(expectedArgs) +
+          ' but was called with ' +
+          jasmine.pp(actual.args);
+      }
+
+      return result;
+    },
+  };
+};
+
diff --git a/web/regression/javascript/sqleditor/is_new_transaction_required_spec.js b/web/regression/javascript/sqleditor/is_new_transaction_required_spec.js
new file mode 100644
index 00000000..d323700d
--- /dev/null
+++ b/web/regression/javascript/sqleditor/is_new_transaction_required_spec.js
@@ -0,0 +1,65 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import {is_new_transaction_required} from '../../../pgadmin/static/js/sqleditor/is_new_transaction_required';
+
+describe('#is_new_transaction_required', () => {
+  describe('when status is not 404', () => {
+    it('should return false', () => {
+      expect(is_new_transaction_required({
+        status: 300,
+      })).toBe(false);
+    });
+  });
+
+  describe('when status is 404', () => {
+    describe('when responseJSON is not present', () => {
+      it('should return false', () => {
+        expect(is_new_transaction_required({
+          status: 404,
+        })).toBeFalsy();
+      });
+    });
+
+    describe('when responseJSON is present', () => {
+      describe('when info is not present inside responseJSON', () => {
+        it('should return false', () => {
+          expect(is_new_transaction_required({
+            status: 404,
+            responseJSON: {},
+          })).toBeFalsy();
+        });
+      });
+
+      describe('when info is present inside responseJSON', () => {
+        describe('when info value is not "DATAGRID_TRANSACTION_REQUIRED"', () => {
+          it('should return false', () => {
+            expect(is_new_transaction_required({
+              status: 404,
+              responseJSON: {
+                info: 'some information',
+              },
+            })).toBe(false);
+          });
+        });
+
+        describe('when info value is "DATAGRID_TRANSACTION_REQUIRED"', () => {
+          it('should return false', () => {
+            expect(is_new_transaction_required({
+              status: 404,
+              responseJSON: {
+                info: 'DATAGRID_TRANSACTION_REQUIRED',
+              },
+            })).toBe(true);
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/sqleditor/query_tool_actions_spec.js b/web/regression/javascript/sqleditor/query_tool_actions_spec.js
index c46b4a24..f3ac7815 100644
--- a/web/regression/javascript/sqleditor/query_tool_actions_spec.js
+++ b/web/regression/javascript/sqleditor/query_tool_actions_spec.js
@@ -43,7 +43,6 @@ describe('queryToolActions', () => {
 
         expect(sqlEditorController.execute_data_query).toHaveBeenCalled();
       });
-
     });
   });
 
@@ -51,60 +50,100 @@ describe('queryToolActions', () => {
     describe('when verbose and costs are not selected and buffers and timing are not selected', () => {
       beforeEach(() => {
         setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue('OFF');
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue('OFF');
-        spyOn(queryToolActions, '_buffers').and.returnValue('OFF');
-        spyOn(queryToolActions, '_timing').and.returnValue('OFF');
+        spyOn(queryToolActions, '_verbose').and.returnValue(false);
+        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
+        spyOn(queryToolActions, '_buffers').and.returnValue(false);
+        spyOn(queryToolActions, '_timing').and.returnValue(false);
       });
+
       it('calls the execute function', () => {
         queryToolActions.explainAnalyze(sqlEditorController);
-        let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE OFF, COSTS OFF, BUFFERS OFF, TIMING OFF) ';
-        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainAnalyzeQuery);
+
+        // let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE OFF, COSTS OFF, BUFFERS OFF, TIMING OFF) ';
+        const explainObject = {
+          format: 'json',
+          analyze: true,
+          verbose: false,
+          costs: false,
+          buffers: false,
+          timing: false,
+          summary: false,
+        };
+
+        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
       });
     });
 
     describe('when verbose and costs and buffers and timing are all selected', () => {
       beforeEach(() => {
         setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue('ON');
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue('ON');
-        spyOn(queryToolActions, '_buffers').and.returnValue('ON');
-        spyOn(queryToolActions, '_timing').and.returnValue('ON');
+        spyOn(queryToolActions, '_verbose').and.returnValue(true);
+        spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
+        spyOn(queryToolActions, '_buffers').and.returnValue(true);
+        spyOn(queryToolActions, '_timing').and.returnValue(true);
       });
       it('calls the execute function', () => {
         queryToolActions.explainAnalyze(sqlEditorController);
-        let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE ON, COSTS ON, BUFFERS ON, TIMING ON) ';
-        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainAnalyzeQuery);
+        const explainObject = {
+          format: 'json',
+          analyze: true,
+          verbose: true,
+          costs: true,
+          buffers: true,
+          timing: true,
+          summary: false,
+        };
+        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
       });
     });
 
     describe('when verbose is selected and costs is not selected and buffer is selected and timing is not selected', () => {
       beforeEach(() => {
         setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue('ON');
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue('OFF');
-        spyOn(queryToolActions, '_buffers').and.returnValue('ON');
-        spyOn(queryToolActions, '_timing').and.returnValue('OFF');
+        spyOn(queryToolActions, '_verbose').and.returnValue(true);
+        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
+        spyOn(queryToolActions, '_buffers').and.returnValue(true);
+        spyOn(queryToolActions, '_timing').and.returnValue(false);
       });
       it('calls the execute function', () => {
         queryToolActions.explainAnalyze(sqlEditorController);
-        let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE ON, COSTS OFF, BUFFERS ON, TIMING OFF) ';
-        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainAnalyzeQuery);
+
+        const explainObject = {
+          format: 'json',
+          analyze: true,
+          verbose: true,
+          costs: false,
+          buffers: true,
+          timing: false,
+          summary: false,
+        };
+
+        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
       });
     });
 
     describe('when verbose is  not selected and costs is selected and buffer is not selected and timing is  selected', () => {
       beforeEach(() => {
         setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue('OFF');
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue('ON');
-        spyOn(queryToolActions, '_buffers').and.returnValue('OFF');
-        spyOn(queryToolActions, '_timing').and.returnValue('ON');
+        spyOn(queryToolActions, '_verbose').and.returnValue(false);
+        spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
+        spyOn(queryToolActions, '_buffers').and.returnValue(false);
+        spyOn(queryToolActions, '_timing').and.returnValue(true);
       });
       it('calls the execute function', () => {
         queryToolActions.explainAnalyze(sqlEditorController);
-        let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE OFF, COSTS ON, BUFFERS OFF, TIMING ON) ';
-        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainAnalyzeQuery);
+
+        const explainObject = {
+          format: 'json',
+          analyze: true,
+          verbose: false,
+          costs: true,
+          buffers: false,
+          timing: true,
+          summary: false,
+        };
+
+        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
       });
     });
   });
@@ -113,39 +152,67 @@ describe('queryToolActions', () => {
     describe('when verbose and costs are selected', () => {
       beforeEach(() => {
         setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue('ON');
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue('ON');
+        spyOn(queryToolActions, '_verbose').and.returnValue(true);
+        spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
       });
+
       it('calls the execute function', () => {
         queryToolActions.explain(sqlEditorController);
-        let explainQuery = 'EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE ON, COSTS ON, BUFFERS OFF, TIMING OFF) ';
-        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainQuery);
+        const explainObject = {
+          format: 'json',
+          analyze: false,
+          verbose: true,
+          costs: true,
+          buffers: false,
+          timing: false,
+          summary: false,
+        };
+        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
       });
     });
 
     describe('when verbose and costs are not selected', () => {
       beforeEach(() => {
         setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue('OFF');
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue('OFF');
+        spyOn(queryToolActions, '_verbose').and.returnValue(false);
+        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
       });
+
       it('calls the execute function', () => {
         queryToolActions.explain(sqlEditorController);
-        let explainQuery = 'EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE OFF, COSTS OFF, BUFFERS OFF, TIMING OFF) ';
-        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainQuery);
+        const explainObject = {
+          format: 'json',
+          analyze: false,
+          verbose: false,
+          costs: false,
+          buffers: false,
+          timing: false,
+          summary: false,
+        };
+
+        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
       });
     });
 
     describe('when verbose is selected and costs is not selected', () => {
       beforeEach(() => {
         setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue('ON');
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue('OFF');
+        spyOn(queryToolActions, '_verbose').and.returnValue(true);
+        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
       });
+
       it('calls the execute function', () => {
         queryToolActions.explain(sqlEditorController);
-        let explainQuery = 'EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE ON, COSTS OFF, BUFFERS OFF, TIMING OFF) ';
-        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainQuery);
+        const explainObject = {
+          format: 'json',
+          analyze: false,
+          verbose: true,
+          costs: false,
+          buffers: false,
+          timing: false,
+          summary: false,
+        };
+        expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
       });
     });
   });
