Hi Dave

On Thu, Feb 21, 2019 at 8:29 PM Dave Page <dp...@pgadmin.org> wrote:

> Hi
>
> On Thu, Feb 21, 2019 at 12:40 PM Akshay Joshi <
> akshay.jo...@enterprisedb.com> wrote:
>
>> Hi Hackers,
>>
>> Attached is the implementation of feature #2418 "add rollback and commit
>> action buttons on Query Tool". I have added jasmine test cases, modified
>> regression test. I have not added feature test as it is not stable and
>> document is also not updated as we are going to update the new screenshots.
>>
>> Please review it.
>>
>
> I found a few quirks:
>
> - The buttons disable themselves after a short delay when a long running
> query is executed. Shouldn't we just disable them in
> disable_tool_buttons()?
>

   Fixed. We should not disable them in disable_tool_buttons() as behaviour
is different and I don't want to add multiple if else in
disable_tool_buttons().

> - The buttons are initially disabled, until a command is run. Shouldn't
> they return to that state after a commit or rollback (whether from a button
> press or typed SQL query)?
>

   Not able to reproduce this issue. I have tried with button press and
keyboard shortcut, it disabled the commit or rollback button.

> - Please update the docs, even if you don't update the screenshot
> (otherwise we'll forget).
>

   Fixed.
   Attached is the modified patch.

>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


-- 
*Akshay Joshi*

*Sr. Software Architect *



*Phone: +91 20-3058-9517Mobile: +91 976-788-8246*
diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst
index d066270..bafbca3 100644
--- a/docs/en_US/query_tool.rst
+++ b/docs/en_US/query_tool.rst
@@ -49,99 +49,98 @@ icons may support functionality accessed via the :ref:`data editor <editgrid>`.
 
 Hover over an icon to display a tooltip that describes the icon's functionality:
 
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| Icon                 | Behavior                                                                                          | Shortcut    |
-+======================+===================================================================================================+=============+
-| *Open File*          | Click the *Open File* icon to display a previously saved query in the SQL Editor.                 |             |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| *Save*     	       | Click the *Save* icon to perform a quick-save of a previously saved query, or to access the       |             |
-|                      | *Save* menu:                                                                                      |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Save* to save the selected content of the SQL Editor panel in a  file.                 |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Save As* to open a new browser dialog and specify a new location to which to save the  |             |
-|                      |    selected content of the SQL Editor panel.                                                      |             |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| *Find*               | Use the *Find* menu to search, replace, or navigate the code displayed in the SQL Editor:         |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Find* to provide a search target, and search the SQL Editor contents.                  | Cmd+F       |
-|                      |                                                                                                   |             |
-|                      |  * Select *Find next* to locate the next occurrence of the search target.                         | Cmd+G       |
-|                      |                                                                                                   |             |
-|                      |  * Select *Find previous* to move to the last occurrence of the search target.                    | Cmd+Shift+G |
-|                      |                                                                                                   |             |
-|                      |  * Select *Pesistent find* to identify all occurrences of the search target within the editor.    |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Replace* to locate and replace (with prompting) individual occurrences of the target.  | Cmd+Shift+F |
-|                      |                                                                                                   |             |
-|                      |  * Select *Replace all* to locate and replace all occurrences of the target within the editor.    |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Jump* to navigate to the next occurrence of the search target.                         | Alt+G       |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| *Copy*               | Click the *Copy* icon to copy the content that is currently highlighted in the Data Output panel. |             |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| *Edit*               | Use options on the *Edit* menu to access text editing tools; the options operate on the text      |             |
-|                      | displayed in the SQL Editor panel:                                                                |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Indent Selection* to indent the currently selected text.                               | Tab         |
-|                      |                                                                                                   |             |
-|                      |  * Select *Unindent Selection* to remove indentation from the currently selected text.            | Shift+Tab   |
-|                      |                                                                                                   |             |
-|                      |  * Select *Inline Comment Selection* to enclose any lines that contain the selection in           | Cmd+/       |
-|                      |    SQL style comment notation.                                                                    |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Inline Uncomment Selection* to remove SQL style comment notation from the              | Cmd+.       |
-|                      |    selected line.                                                                                 |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Block Comment* to enclose all lines that contain the selection in C style              | Shift+Cmd+/ |
-|                      |    comment notation.  This option acts as a toggle.                                               |             |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| *Execute/Refresh*    | Click the *Execute/Refresh* icon to either execute or refresh the query highlighted in the SQL    |             |
-|                      | editor panel. Click the down arrow to access other execution options:                             |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Execute/Refresh* to invoke the SQL command and refresh the result set.                 | F5          |
-|                      |                                                                                                   |             |
-|                      |  * Select *Explain* to view an explanation plan for the current query.  The result of the         | F7          |
-|                      |    EXPLAIN is displayed graphically on the *Explain* tab of the output panel, and in text         |             |
-|                      |    form on the *Data Output* tab.                                                                 |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Explain analyze* to invoke an EXPLAIN ANALYZE command on the current query.            | Shift+F7    |
-|                      |                                                                                                   |             |
-|                      |  * Navigate through the *Explain Options* menu to select options for the EXPLAIN command:         |             |
-|                      |                                                                                                   |             |
-|                      |       Select *Verbose* to display additional information regarding the query plan.                |             |
-|                      |                                                                                                   |             |
-|                      |       Select *Costs* to include information on the estimated startup and total cost of each       |             |
-|                      |       plan node, as well as the estimated number of rows and the estimated width of each          |             |
-|                      |       row.                                                                                        |             |
-|                      |                                                                                                   |             |
-|                      |       Select *Buffers* to include information on buffer usage.                                    |             |
-|                      |                                                                                                   |             |
-|                      |       Select *Timing* to include information about the startup time and the amount of time        |             |
-|                      |       spent in each node of the query.                                                            |             |
-|                      |                                                                                                   |             |
-|                      |  * Add a check next to *Auto-Rollback* to instruct the server to automatically roll back a        |             |
-|                      |    transaction if an error occurs during the transaction.                                         |             |
-|                      |                                                                                                   |             |
-|                      |  * Add a check next to *Auto-Commit* to instruct the server to automatically commit each          |             |
-|                      |    transaction.  Any changes made by the transaction will be visible to others, and               |             |
-|                      |    durable in the event of a crash.                                                               |             |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| *Stop*               | Click the *Stop* icon to cancel the execution of the currently running query.                     |             |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| *Clear query window* | Use options on the *Clear* drop-down menu to erase display contents:                              |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Clear Query Window* to erase the content of the SQL Editor panel.                      |             |
-|                      |                                                                                                   |             |
-|                      |                                                                                                   |             |
-|                      |  * Select *Explain analyze* to invoke an EXPLAIN ANALYZE command on the current query.            | Shift+F7    |
-|                      |                                                                                                   |             |
-|                      | the SQL editor panel or the *History* tab.                                                        |             |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
-| *Download as CSV*    | Click the *Download as CSV* icon to download the result set of the current query to a             | F8          |
-|                      | comma-separated list. You can specify the CSV settings through                                    |             |
-|                      | *Preferences -> SQL Editor -> CSV output* dialogue.                                               |             |
-+----------------------+---------------------------------------------------------------------------------------------------+-------------+
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| Icon                 | Behavior                                                                                          | Shortcut       |
++======================+===================================================================================================+================+
+| *Open File*          | Click the *Open File* icon to display a previously saved query in the SQL Editor.                 | Accesskey + O  |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Save*     	         | Click the *Save* icon to perform a quick-save of a previously saved query, or to access the       | Accesskey + S  |
+|                      | *Save* menu:                                                                                      |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Save* to save the selected content of the SQL Editor panel in a  file.                 |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Save As* to open a new browser dialog and specify a new location to which to save the  |                |
+|                      |    selected content of the SQL Editor panel.                                                      |                |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Find*               | Use the *Find* menu to search, replace, or navigate the code displayed in the SQL Editor:         |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Find* to provide a search target, and search the SQL Editor contents.                  | Cmd+F          |
+|                      |                                                                                                   |                |
+|                      |  * Select *Find next* to locate the next occurrence of the search target.                         | Cmd+G          |
+|                      |                                                                                                   |                |
+|                      |  * Select *Find previous* to move to the last occurrence of the search target.                    | Cmd+Shift+G    |
+|                      |                                                                                                   |                |
+|                      |  * Select *Pesistent find* to identify all occurrences of the search target within the editor.    |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Replace* to locate and replace (with prompting) individual occurrences of the target.  | Cmd+Shift+F    |
+|                      |                                                                                                   |                |
+|                      |  * Select *Replace all* to locate and replace all occurrences of the target within the editor.    |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Jump* to navigate to the next occurrence of the search target.                         | Alt+G          |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Copy*               | Click the *Copy* icon to copy the content that is currently highlighted in the Data Output panel. | Accesskey + C  |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Edit*               | Use options on the *Edit* menu to access text editing tools; the options operate on the text      |                |
+|                      | displayed in the SQL Editor panel:                                                                |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Indent Selection* to indent the currently selected text.                               | Tab            |
+|                      |                                                                                                   |                |
+|                      |  * Select *Unindent Selection* to remove indentation from the currently selected text.            | Shift+Tab      |
+|                      |                                                                                                   |                |
+|                      |  * Select *Inline Comment Selection* to enclose any lines that contain the selection in           | Cmd+/          |
+|                      |    SQL style comment notation.                                                                    |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Inline Uncomment Selection* to remove SQL style comment notation from the              | Cmd+.          |
+|                      |    selected line.                                                                                 |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Block Comment* to enclose all lines that contain the selection in C style              | Shift+Cmd+/    |
+|                      |    comment notation.  This option acts as a toggle.                                               |                |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Stop*               | Click the *Stop* icon to cancel the execution of the currently running query.                     | Accesskey + Q  |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Execute/Refresh*    | Click the *Execute/Refresh* icon to either execute or refresh the query highlighted in the SQL    | F5             |
+|                      | editor panel. Click the down arrow to access other execution options:                             |                |
+|                      |                                                                                                   |                |
+|                      |  * Add a check next to *Auto-Rollback* to instruct the server to automatically roll back a        |                |
+|                      |    transaction if an error occurs during the transaction.                                         |                |
+|                      |                                                                                                   |                |
+|                      |  * Add a check next to *Auto-Commit* to instruct the server to automatically commit each          |                |
+|                      |    transaction.  Any changes made by the transaction will be visible to others, and               |                |
+|                      |    durable in the event of a crash.                                                               |                |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Explain*            | Click the *Explain* icon to view an explanation plan for the current query. The result of the     | F7             |
+|                      |    EXPLAIN is displayed graphically on the *Explain* tab of the output panel, and in text         |                |
+|                      |    form on the *Data Output* tab.                                                                 |                |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Explain analyze*    | Click the *Explain analyze* icon to invoke an EXPLAIN ANALYZE command on the current query.       | Shift+F7       |
+|                      |                                                                                                   |                |
+|                      |  * Navigate through the *Explain Options* menu to select options for the EXPLAIN command:         |                |
+|                      |                                                                                                   |                |
+|                      |       Select *Verbose* to display additional information regarding the query plan.                |                |
+|                      |                                                                                                   |                |
+|                      |       Select *Costs* to include information on the estimated startup and total cost of each       |                |
+|                      |       plan node, as well as the estimated number of rows and the estimated width of each          |                |
+|                      |       row.                                                                                        |                |
+|                      |                                                                                                   |                |
+|                      |       Select *Buffers* to include information on buffer usage.                                    |                |
+|                      |                                                                                                   |                |
+|                      |       Select *Timing* to include information about the startup time and the amount of time        |                |
+|                      |       spent in each node of the query.                                                            |                |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Commit*             | Click the *Commit* icon to commit the transaction.                                                | Shift+CTRL+M   |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Rollback*           | Click the *Rollback* icon to rollback the transaction.                                            | Shift+CTRL+R   |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Clear*              | Use options on the *Clear* drop-down menu to erase display contents:                              | Accesskey + L  |
+|                      |                                                                                                   |                |
+|                      |  * Select *Clear Query Window* to erase the content of the SQL Editor panel.                      |                |
+|                      |                                                                                                   |                |
+|                      |  * Select *Clear History* to erase the content of the *History* tab.                              |                |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
+| *Download as CSV*    | Click the *Download as CSV* icon to download the result set of the current query to a             | F8             |
+|                      | comma-separated list. You can specify the CSV settings through                                    |                |
+|                      | *Preferences -> SQL Editor -> CSV output* dialogue.                                               |                |
++----------------------+---------------------------------------------------------------------------------------------------+----------------+
 
 
 **The SQL Editor Panel**
diff --git a/web/pgadmin/static/js/keyboard_shortcuts.js b/web/pgadmin/static/js/keyboard_shortcuts.js
index a5c8e13..70aab91 100644
--- a/web/pgadmin/static/js/keyboard_shortcuts.js
+++ b/web/pgadmin/static/js/keyboard_shortcuts.js
@@ -181,6 +181,8 @@ function keyboardShortcutsQueryTool(
   let nextPanelKeys = sqlEditorController.preferences.move_next;
   let previousPanelKeys = sqlEditorController.preferences.move_previous;
   let toggleCaseKeys = sqlEditorController.preferences.toggle_case;
+  let commitKeys = sqlEditorController.preferences.commit_transaction;
+  let rollbackKeys = sqlEditorController.preferences.rollback_transaction;
 
   if (this.validateShortcutKeys(executeKeys, event)) {
     this._stopEventPropagation(event);
@@ -197,6 +199,18 @@ function keyboardShortcutsQueryTool(
   } else if (this.validateShortcutKeys(toggleCaseKeys, event)) {
     this._stopEventPropagation(event);
     queryToolActions.toggleCaseOfSelectedText(sqlEditorController);
+  } else if (this.validateShortcutKeys(commitKeys, event)) {
+    // If transaction buttons are disabled then no need to execute commit.
+    if (!sqlEditorController.is_transaction_buttons_disabled) {
+      this._stopEventPropagation(event);
+      queryToolActions.executeCommit(sqlEditorController);
+    }
+  } else if (this.validateShortcutKeys(rollbackKeys, event)) {
+    // If transaction buttons are disabled then no need to execute rollback.
+    if (!sqlEditorController.is_transaction_buttons_disabled) {
+      this._stopEventPropagation(event);
+      queryToolActions.executeRollback(sqlEditorController);
+    }
   } else if ((
      (this.isMac() && event.metaKey) ||
      (!this.isMac() && event.ctrlKey)
diff --git a/web/pgadmin/static/js/sqleditor/execute_query.js b/web/pgadmin/static/js/sqleditor/execute_query.js
index 5542177..28df00d 100644
--- a/web/pgadmin/static/js/sqleditor/execute_query.js
+++ b/web/pgadmin/static/js/sqleditor/execute_query.js
@@ -81,6 +81,12 @@ class ExecuteQuery {
         } else {
           self.loadingScreen.hide();
           self.enableSQLEditorButtons();
+          // Enable/Disable commit and rollback button.
+          if (result.data.data.transaction_status == 2 || result.data.data.transaction_status == 3) {
+            self.enableTransactionButtons();
+          } else {
+            self.disableTransactionButtons();
+          }
           self.sqlServerObject.update_msg_history(false, httpMessageData.data.result);
           if ('notifies' in httpMessageData.data)
             self.sqlServerObject.update_notifications(httpMessageData.data.notifies);
@@ -114,6 +120,13 @@ class ExecuteQuery {
       })
     ).then(
       (httpMessage) => {
+        // Enable/Disable commit and rollback button.
+        if (httpMessage.data.data.transaction_status == 2 || httpMessage.data.data.transaction_status == 3) {
+          self.enableTransactionButtons();
+        } else {
+          self.disableTransactionButtons();
+        }
+
         if (ExecuteQuery.isQueryFinished(httpMessage)) {
           self.loadingScreen.setMessage('Loading data from the database server and rendering...');
 
@@ -183,6 +196,7 @@ class ExecuteQuery {
     this.sqlServerObject.rows_affected = 0;
     this.sqlServerObject._init_polling_flags();
     this.disableSQLEditorButtons();
+    this.disableTransactionButtons();
   }
 
   static prepareAnalyzeSql(sqlStatement, analyzeSql) {
@@ -246,6 +260,15 @@ class ExecuteQuery {
     this.sqlServerObject.disable_tool_buttons(true);
   }
 
+  enableTransactionButtons() {
+    this.sqlServerObject.disable_transaction_buttons(false);
+  }
+
+  disableTransactionButtons() {
+    this.sqlServerObject.special_sql = undefined;
+    this.sqlServerObject.disable_transaction_buttons(true);
+  }
+
   static wasConnectionLostToPythonServer(httpResponse) {
     return _.isUndefined(httpResponse) || _.isUndefined(httpResponse.data);
   }
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_actions.js b/web/pgadmin/static/js/sqleditor/query_tool_actions.js
index f82ed9d..7739e9b 100644
--- a/web/pgadmin/static/js/sqleditor/query_tool_actions.js
+++ b/web/pgadmin/static/js/sqleditor/query_tool_actions.js
@@ -141,6 +141,18 @@ let queryToolActions = {
       codeMirrorObj.replaceSelection(selectedText.toUpperCase());
     }
   },
+
+  executeCommit: function (sqlEditorController) {
+    var self = this;
+    sqlEditorController.special_sql = 'COMMIT;';
+    self.executeQuery(sqlEditorController);
+  },
+
+  executeRollback: function (sqlEditorController) {
+    var self = this;
+    sqlEditorController.special_sql = 'ROLLBACK;';
+    self.executeQuery(sqlEditorController);
+  },
 };
 
 module.exports = queryToolActions;
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
index 5b63988..daeefad 100644
--- a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
+++ b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
@@ -97,6 +97,14 @@ function updateUIPreferences(sqlEditor) {
     .attr('title',
       shortcut_title('Download as CSV',preferences.download_csv));
 
+  $el.find('#btn-commit')
+    .attr('title',
+      shortcut_title('Commit',preferences.commit_transaction));
+
+  $el.find('#btn-rollback')
+    .attr('title',
+      shortcut_title('Rollback',preferences.rollback_transaction));
+
   /* Set Auto-commit and auto-rollback on query editor */
   if (preferences.auto_commit) {
     $el.find('.auto-commit').removeClass('visibility-hidden');
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index 5dd0bdc..067cd7a 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -218,7 +218,7 @@
                         tabindex="0" disabled >
                     <i class="fa fa-stop sql-icon-lg" aria-hidden="true"></i>
                 </button>
-                <button id="btn-flash" data-test-selector="execute-refresh-button" type="button" class="btn btn-sm btn-secondary" style="width: 40px;"
+                <button id="btn-flash" data-test-selector="execute-refresh-button" type="button" class="btn btn-sm btn-secondary" style="width: 32px;"
                         title=""
                         tabindex="0">
                     <i class="fa fa-bolt sql-icon-lg" aria-hidden="true"></i>
@@ -288,6 +288,19 @@
                 </ul>
             </div>
             <div class="btn-group mr-1" role="group" aria-label="">
+                <button id="btn-commit" type="button" class="btn btn-sm btn-secondary"
+                        title=""
+                        accesskey=""
+                        tabindex="0" disabled>
+                    <i class="icon-commit sql-icon-lg" aria-hidden="true"></i>
+                </button>
+                <button id="btn-rollback" type="button" class="btn btn-sm btn-secondary"
+                        title=""
+                        tabindex="0" disabled>
+                    <i class="icon-rollback sql-icon-lg" aria-hidden="true"></i>
+                </button>
+            </div>
+            <div class="btn-group mr-1" role="group" aria-label="">
                 <button id="btn-clear-dropdown" type="button" class="btn btn-sm btn-secondary dropdown-toggle"
                         data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
                         title=""
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index 51f8129..8d5f857 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -584,6 +584,7 @@ def poll(trans_id):
            result is not None and additional_messages:
             result = additional_messages + result
 
+    transaction_status = conn.transaction_status()
     return make_json_response(
         data={
             'status': status, 'result': result,
@@ -598,7 +599,8 @@ def poll(trans_id):
             'types': types,
             'client_primary_key': client_primary_key,
             'has_oids': has_oids,
-            'oids': oids
+            'oids': oids,
+            'transaction_status': transaction_status,
         },
         encoding=conn.python_encoding
     )
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
index 81e63c3..cac4f66 100644
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
@@ -297,6 +297,26 @@ input.editor-checkbox:focus {
   background-image: url('../img/disconnect.svg');
 }
 
+.icon-commit, .icon-rollback {
+  display: inline-block;
+  align-content: center;
+  vertical-align: middle;
+  height: 18px;
+  width: 18px;
+  background-size: 22px !important;
+  background-repeat: no-repeat;
+  background-position-x: center;
+  background-position-y: center;
+}
+
+.icon-commit {
+  background-image: url('../img/commit.svg') !important;
+}
+
+.icon-rollback {
+  background-image: url('../img/rollback.svg') !important;
+}
+
 .ajs-body .warn-header {
   font-size: 13px;
   font-weight: bold;
diff --git a/web/pgadmin/tools/sqleditor/static/img/commit.svg b/web/pgadmin/tools/sqleditor/static/img/commit.svg
new file mode 100644
index 0000000..7fd36e8
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/img/commit.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 64 64"><defs><style>.cls-1{fill:#222222;}</style></defs><title>commit</title><path class="cls-1" d="M51.89,28.51a21.22,21.22,0,0,0-6.3-2.1,45.22,45.22,0,0,0-8.67-.78,45.12,45.12,0,0,0-8.66.78,21.22,21.22,0,0,0-6.3,2.1c-1.54.89-2.32,1.85-2.32,2.88v2.88c0,1,.78,2,2.32,2.88a20.92,20.92,0,0,0,6.3,2.11,46,46,0,0,0,8.66.77,46.07,46.07,0,0,0,8.67-.77,20.92,20.92,0,0,0,6.3-2.11q2.31-1.32,2.31-2.88V31.39Q54.2,29.85,51.89,28.51Z"/><path class="cls-1" d="M36.92,51.56a49.08,49.08,0,0,1-10-1,19.79,19.79,0,0,1-7.32-2.86v3.82c0,1,.78,2,2.32,2.88a20.92,20.92,0,0,0,6.3,2.11,46,46,0,0,0,8.66.77,46.07,46.07,0,0,0,8.67-.77,20.92,20.92,0,0,0,6.3-2.11q2.31-1.32,2.31-2.88V47.74a19.67,19.67,0,0,1-7.31,2.86A49.11,49.11,0,0,1,36.92,51.56Z"/><path class="cls-1" d="M36.92,42.92a49.08,49.08,0,0,1-10-1,19.79,19.79,0,0,1-7.32-2.86v3.82c0,1,.78,2,2.32,2.88a20.92,20.92,0,0,0,6.3,2.11,46,46,0,0,0,8.66.77,46.07,46.07,0,0,0,8.67-.77,20.92,20.92,0,0,0,6.3-2.11q2.31-1.32,2.31-2.88V39.1A19.67,19.67,0,0,1,46.89,42,49.11,49.11,0,0,1,36.92,42.92Z"/><path class="cls-1" d="M25.67,12.44a11.28,11.28,0,0,1,4.23.8,11.13,11.13,0,0,1,3.6,2.28l-3.08,3.1a1.29,1.29,0,0,0-.31,1.56,1.33,1.33,0,0,0,1.32.9H41.51a1.43,1.43,0,0,0,1-.43,1.4,1.4,0,0,0,.42-1V9.56a1.34,1.34,0,0,0-.9-1.33,1.28,1.28,0,0,0-1.55.31l-2.92,2.91a17.34,17.34,0,0,0-5.51-3.52A17,17,0,0,0,19,8.05a19.15,19.15,0,0,0-3,1.6h0c-.43.3-.85.61-1.25.94l-.4.34h0a16.91,16.91,0,0,0-4.49,6,.8.8,0,0,0,0,.53.65.65,0,0,0,.31.39L14,20a.82.82,0,0,0,.6.05.72.72,0,0,0,.42-.4,11.46,11.46,0,0,1,1.12-2.11h0l.39-.54.24-.3c.18-.22.37-.44.57-.65L17.4,16l.45-.44.09-.09c.16-.15.33-.29.5-.43l.27-.2.21-.16a11.84,11.84,0,0,1,2.29-1.28A11.12,11.12,0,0,1,25.67,12.44Z"/></svg>
diff --git a/web/pgadmin/tools/sqleditor/static/img/rollback.svg b/web/pgadmin/tools/sqleditor/static/img/rollback.svg
new file mode 100644
index 0000000..89713cd
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/img/rollback.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 64 64"><defs><style>.cls-1{fill:#222222;}</style></defs><title>rollback</title><path class="cls-1" d="M53.89,28.51a21.22,21.22,0,0,0-6.3-2.1,45.22,45.22,0,0,0-8.67-.78,45.12,45.12,0,0,0-8.66.78,21.22,21.22,0,0,0-6.3,2.1c-1.54.89-2.32,1.85-2.32,2.88v2.88c0,1,.78,2,2.32,2.88a20.92,20.92,0,0,0,6.3,2.11,46,46,0,0,0,8.66.77,46.07,46.07,0,0,0,8.67-.77,20.92,20.92,0,0,0,6.3-2.11q2.31-1.32,2.31-2.88V31.39Q56.2,29.85,53.89,28.51Z"/><path class="cls-1" d="M38.92,51.56a49.08,49.08,0,0,1-10-1,19.79,19.79,0,0,1-7.32-2.86v3.82c0,1,.78,2,2.32,2.88a20.92,20.92,0,0,0,6.3,2.11,46,46,0,0,0,8.66.77,46.07,46.07,0,0,0,8.67-.77,20.92,20.92,0,0,0,6.3-2.11q2.31-1.32,2.31-2.88V47.74a19.67,19.67,0,0,1-7.31,2.86A49.11,49.11,0,0,1,38.92,51.56Z"/><path class="cls-1" d="M38.92,42.92a49.08,49.08,0,0,1-10-1,19.79,19.79,0,0,1-7.32-2.86v3.82c0,1,.78,2,2.32,2.88a20.92,20.92,0,0,0,6.3,2.11,46,46,0,0,0,8.66.77,46.07,46.07,0,0,0,8.67-.77,20.92,20.92,0,0,0,6.3-2.11q2.31-1.32,2.31-2.88V39.1A19.67,19.67,0,0,1,48.89,42,49.11,49.11,0,0,1,38.92,42.92Z"/><path class="cls-1" d="M25.08,12.44a11.28,11.28,0,0,0-4.23.8,11.13,11.13,0,0,0-3.6,2.28l3.08,3.1a1.29,1.29,0,0,1,.31,1.56,1.33,1.33,0,0,1-1.32.9H9.24a1.43,1.43,0,0,1-1-.43,1.4,1.4,0,0,1-.42-1V9.56a1.34,1.34,0,0,1,.9-1.33,1.28,1.28,0,0,1,1.55.31l2.92,2.91a17.34,17.34,0,0,1,5.51-3.52,17,17,0,0,1,13.1.12,19.15,19.15,0,0,1,3,1.6h0c.43.3.85.61,1.25.94l.4.34h0a16.91,16.91,0,0,1,4.49,6,.8.8,0,0,1,0,.53.65.65,0,0,1-.31.39L36.8,20a.82.82,0,0,1-.6.05.72.72,0,0,1-.42-.4,11.46,11.46,0,0,0-1.12-2.11h0q-.19-.28-.39-.54l-.24-.3c-.18-.22-.37-.44-.57-.65L33.35,16l-.45-.44-.09-.09c-.16-.15-.33-.29-.5-.43l-.27-.2-.21-.16a11.84,11.84,0,0,0-2.29-1.28A11.12,11.12,0,0,0,25.08,12.44Z"/></svg>
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index edac426..b857d80 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -124,6 +124,8 @@ define('tools.querytool', [
       // Indentation options
       'click #btn-indent-code': 'on_indent_code',
       'click #btn-unindent-code': 'on_unindent_code',
+      'click #btn-commit': 'on_commit_transaction',
+      'click #btn-rollback': 'on_rollback_transaction',
     },
 
     reflectPreferences: function() {
@@ -1779,6 +1781,15 @@ define('tools.querytool', [
         }
       }
     },
+    // Callback function for the commit button click.
+    on_commit_transaction: function() {
+      queryToolActions.executeCommit(this.handler);
+    },
+
+    // Callback function for the rollback button click.
+    on_rollback_transaction: function() {
+      queryToolActions.executeRollback(this.handler);
+    },
   });
 
   /* Defining controller class for data grid, which actually
@@ -3405,6 +3416,15 @@ define('tools.querytool', [
         }
       },
 
+      // This function is used to enable/disable commit/rollback buttons
+      disable_transaction_buttons: function(disabled) {
+        this.is_transaction_buttons_disabled = disabled;
+        if (this.is_query_tool) {
+          $('#btn-commit').prop('disabled', disabled);
+          $('#btn-rollback').prop('disabled', disabled);
+        }
+      },
+
       // This function will fetch the sql query from the text box
       // and execute the query.
       execute: function(explain_prefix, shouldReconnect=false) {
@@ -3414,14 +3434,18 @@ define('tools.querytool', [
         self.has_more_rows = false;
         self.fetching_rows = false;
 
-        /* If code is selected in the code mirror then execute
-         * the selected part else execute the complete code.
-         */
-        var selected_code = self.gridView.query_tool_obj.getSelection();
-        if (selected_code.length > 0)
-          sql = selected_code;
-        else
-          sql = self.gridView.query_tool_obj.getValue();
+        if (!_.isUndefined(self.special_sql)) {
+          sql = self.special_sql;
+        } else {
+          /* If code is selected in the code mirror then execute
+           * the selected part else execute the complete code.
+           */
+          var selected_code = self.gridView.query_tool_obj.getSelection();
+          if (selected_code.length > 0)
+            sql = selected_code;
+          else
+            sql = self.gridView.query_tool_obj.getValue();
+        }
 
         const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement);
         executeQuery.execute(sql, explain_prefix, shouldReconnect);
diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
index 9e7cfcd..3be6842 100644
--- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
+++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
@@ -571,3 +571,39 @@ def RegisterQueryToolPreferences(self):
         help_str=gettext('If set to True, Keywords will be displayed '
                          'in upper case for auto completion.')
     )
+
+    self.preference.register(
+        'keyboard_shortcuts',
+        'commit_transaction',
+        gettext('Commit'),
+        'keyboardshortcut',
+        {
+            'alt': False,
+            'shift': True,
+            'control': True,
+            'key': {
+                'key_code': 77,
+                'char': 'm'
+            }
+        },
+        category_label=gettext('Keyboard shortcuts'),
+        fields=shortcut_fields
+    )
+
+    self.preference.register(
+        'keyboard_shortcuts',
+        'rollback_transaction',
+        gettext('Rollback'),
+        'keyboardshortcut',
+        {
+            'alt': False,
+            'shift': True,
+            'control': True,
+            'key': {
+                'key_code': 82,
+                'char': 'r'
+            }
+        },
+        category_label=gettext('Keyboard shortcuts'),
+        fields=shortcut_fields
+    )
diff --git a/web/pgadmin/tools/sqleditor/utils/start_running_query.py b/web/pgadmin/tools/sqleditor/utils/start_running_query.py
index c3fa54b..200b0e0 100644
--- a/web/pgadmin/tools/sqleditor/utils/start_running_query.py
+++ b/web/pgadmin/tools/sqleditor/utils/start_running_query.py
@@ -48,6 +48,7 @@ class StartRunningQuery:
         can_edit = False
         can_filter = False
         notifies = None
+        trans_status = None
         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)
@@ -91,6 +92,7 @@ class StartRunningQuery:
 
             # Get the notifies
             notifies = conn.get_notifies()
+            trans_status = conn.transaction_status()
         else:
             status = False
             result = gettext(
@@ -101,7 +103,8 @@ class StartRunningQuery:
                 'can_edit': can_edit, 'can_filter': can_filter,
                 'info_notifier_timeout':
                     self.blueprint_object.info_notifier_timeout.get(),
-                'notifies': notifies
+                'notifies': notifies,
+                'transaction_status': trans_status,
             }
         )
 
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
index 2734735..6aec9af 100644
--- a/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py
@@ -118,7 +118,8 @@ class StartRunningQueryTest(BaseTestGenerator):
                      can_edit=False,
                      can_filter=False,
                      info_notifier_timeout=5,
-                     notifies=None
+                     notifies=None,
+                     transaction_status=None
                  )
              ),
              expect_internal_server_error_called_with=None,
@@ -278,7 +279,8 @@ class StartRunningQueryTest(BaseTestGenerator):
                      can_edit=True,
                      can_filter=True,
                      info_notifier_timeout=5,
-                     notifies=None
+                     notifies=None,
+                     transaction_status=None
                  )
              ),
              expect_internal_server_error_called_with=None,
@@ -322,7 +324,8 @@ class StartRunningQueryTest(BaseTestGenerator):
                      can_edit=True,
                      can_filter=True,
                      info_notifier_timeout=5,
-                     notifies=None
+                     notifies=None,
+                     transaction_status=None
                  )
              ),
              expect_internal_server_error_called_with=None,
@@ -366,7 +369,8 @@ class StartRunningQueryTest(BaseTestGenerator):
                      can_edit=True,
                      can_filter=True,
                      info_notifier_timeout=5,
-                     notifies=None
+                     notifies=None,
+                     transaction_status=None
                  )
              ),
              expect_internal_server_error_called_with=None,
@@ -411,7 +415,8 @@ class StartRunningQueryTest(BaseTestGenerator):
                      can_edit=True,
                      can_filter=True,
                      info_notifier_timeout=5,
-                     notifies=None
+                     notifies=None,
+                     transaction_status=None
                  )
              ),
              expect_internal_server_error_called_with=None,
@@ -517,9 +522,11 @@ class StartRunningQueryTest(BaseTestGenerator):
             execute_async=MagicMock(),
             execute_void=MagicMock(),
             get_notifies=MagicMock(),
+            transaction_status=MagicMock(),
         )
         self.connection.connect.return_value = self.connection_connect_return
         self.connection.get_notifies.return_value = None
+        self.connection.transaction_status.return_value = None
         self.connection.execute_async.return_value = \
             self.execute_async_return_value
         if self.manager_connection_exception is None:
diff --git a/web/regression/javascript/sqleditor/call_render_after_poll_spec.js b/web/regression/javascript/sqleditor/call_render_after_poll_spec.js
index 14b6fc1..d68f5ee 100644
--- a/web/regression/javascript/sqleditor/call_render_after_poll_spec.js
+++ b/web/regression/javascript/sqleditor/call_render_after_poll_spec.js
@@ -22,6 +22,7 @@ describe('#callRenderAfterPoll', () => {
       trigger: jasmine.createSpy('SQLEditor.trigger'),
       update_msg_history: jasmine.createSpy('SQLEditor.update_msg_history'),
       disable_tool_buttons: jasmine.createSpy('SQLEditor.disable_tool_buttons'),
+      disable_transaction_buttons: jasmine.createSpy('SQLEditor.disable_transaction_buttons'),
       query_start_time: new Date(),
     };
     alertify = jasmine.createSpyObj('alertify', ['success']);
diff --git a/web/regression/javascript/sqleditor/execute_query_spec.js b/web/regression/javascript/sqleditor/execute_query_spec.js
index 71ca979..fb7ad9a 100644
--- a/web/regression/javascript/sqleditor/execute_query_spec.js
+++ b/web/regression/javascript/sqleditor/execute_query_spec.js
@@ -44,6 +44,7 @@ describe('ExecuteQuery', () => {
       'initTransaction',
       'handle_connection_lost',
       'update_notifications',
+      'disable_transaction_buttons',
     ]);
     sqlEditorMock.transId = 123;
     sqlEditorMock.rows_affected = 1000;
@@ -112,6 +113,46 @@ describe('ExecuteQuery', () => {
           });
         });
 
+        describe('when query was successful but in transaction block', () => {
+          beforeEach(() => {
+            response = {
+              data: {status: 'Success', notifies: [{'pid': 100}], transaction_status: 2},
+            };
+            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+
+            executeQuery.poll();
+          });
+
+          it('enable the transaction buttons', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.disable_transaction_buttons)
+                  .toHaveBeenCalledWith(false);
+                done();
+              }, 0);
+          });
+        });
+
+        describe('when query was successful but not in transaction block', () => {
+          beforeEach(() => {
+            response = {
+              data: {status: 'Success', notifies: [{'pid': 100}], transaction_status: 0},
+            };
+            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
+
+            executeQuery.poll();
+          });
+
+          it('disable the transaction buttons', (done) => {
+            setTimeout(
+              () => {
+                expect(sqlEditorMock.disable_transaction_buttons)
+                  .toHaveBeenCalledWith(true);
+                done();
+              }, 0);
+          });
+        });
+
         describe('when query is still running', () => {
           context('when no additional information is returned', () => {
             beforeEach(() => {
diff --git a/web/regression/javascript/sqleditor/keyboard_shortcuts_spec.js b/web/regression/javascript/sqleditor/keyboard_shortcuts_spec.js
index 543ae37..d7e1856 100644
--- a/web/regression/javascript/sqleditor/keyboard_shortcuts_spec.js
+++ b/web/regression/javascript/sqleditor/keyboard_shortcuts_spec.js
@@ -93,6 +93,22 @@ describe('the keyboard shortcuts', () => {
           key_code: null,
         },
       },
+      commit_transaction: {
+        alt: false,
+        shift: true,
+        control: true,
+        key: {
+          key_code: 'm',
+        },
+      },
+      rollback_transaction: {
+        alt: false,
+        shift: true,
+        control: true,
+        key: {
+          key_code: 'r',
+        },
+      },
     };
 
     queryToolActionsSpy = jasmine.createSpyObj(queryToolActions, [
@@ -103,6 +119,8 @@ describe('the keyboard shortcuts', () => {
       'commentLineCode',
       'uncommentLineCode',
       'executeQuery',
+      'executeCommit',
+      'executeRollback',
     ]);
   });
 
@@ -521,6 +539,80 @@ describe('the keyboard shortcuts', () => {
     });
   });
 
+  describe('Shift+Ctrl+C', () => {
+    describe('when there is not a query already running', () => {
+      beforeEach(() => {
+        event.shiftKey = true;
+        event.which = 'm';
+        event.altKey = false;
+        event.ctrlKey = true;
+        keyboardShortcuts.processEventQueryTool(
+          sqlEditorControllerSpy, queryToolActionsSpy, event
+        );
+      });
+
+      it('should commit the transaction', () => {
+        expect(queryToolActionsSpy.executeCommit).toHaveBeenCalledWith(sqlEditorControllerSpy);
+      });
+
+      expectEventPropagationToStop();
+    });
+
+    describe('when the query is already running', () => {
+      it('does nothing', () => {
+        event.shiftKey = true;
+        event.which = 'm';
+        event.altKey = false;
+        event.ctrlKey = true;
+
+        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
+
+        keyboardShortcuts.processEventQueryTool(
+          sqlEditorControllerSpy, queryToolActionsSpy, event
+        );
+
+        expect(queryToolActionsSpy.executeCommit).not.toHaveBeenCalled();
+      });
+    });
+  });
+
+  describe('Shift+Ctrl+R', () => {
+    describe('when there is not a query already running', () => {
+      beforeEach(() => {
+        event.shiftKey = true;
+        event.which = 'r';
+        event.altKey = false;
+        event.ctrlKey = true;
+        keyboardShortcuts.processEventQueryTool(
+          sqlEditorControllerSpy, queryToolActionsSpy, event
+        );
+      });
+
+      it('should rollback the transaction', () => {
+        expect(queryToolActionsSpy.executeRollback).toHaveBeenCalledWith(sqlEditorControllerSpy);
+      });
+
+      expectEventPropagationToStop();
+    });
+
+    describe('when the query is already running', () => {
+      it('does nothing', () => {
+        event.shiftKey = true;
+        event.which = 'r';
+        event.altKey = false;
+        event.ctrlKey = true;
+
+        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
+
+        keyboardShortcuts.processEventQueryTool(
+          sqlEditorControllerSpy, queryToolActionsSpy, event
+        );
+
+        expect(queryToolActionsSpy.executeRollback).not.toHaveBeenCalled();
+      });
+    });
+  });
+
   function expectEventPropagationToStop() {
     describe('stops all event propogation', () => {
 
diff --git a/web/regression/javascript/sqleditor/query_tool_actions_spec.js b/web/regression/javascript/sqleditor/query_tool_actions_spec.js
index 7504b5d..9c18743 100644
--- a/web/regression/javascript/sqleditor/query_tool_actions_spec.js
+++ b/web/regression/javascript/sqleditor/query_tool_actions_spec.js
@@ -521,6 +521,32 @@ describe('queryToolActions', () => {
     });
   });
 
+  describe('executeCommit', () => {
+    describe('when commit action is being run from the query tool', () => {
+      beforeEach(() => {
+        setUpSpies('', '');
+      });
+
+      it('calls the execute commit function', () => {
+        queryToolActions.executeCommit(sqlEditorController);
+        expect(sqlEditorController.special_sql).toEqual('COMMIT;');
+      });
+    });
+  });
+
+  describe('executeRollback', () => {
+    describe('when rollback action is being run from the query tool', () => {
+      beforeEach(() => {
+        setUpSpies('', '');
+      });
+
+      it('calls the execute rollback function', () => {
+        queryToolActions.executeRollback(sqlEditorController);
+        expect(sqlEditorController.special_sql).toEqual('ROLLBACK;');
+      });
+    });
+  });
+
   function setUpSpies(selectedQueryString, entireQueryString) {
     getValueSpy = jasmine.createSpy('getValueSpy').and.returnValue(entireQueryString);
     getSelectionSpy = jasmine.createSpy('getSelectionSpy').and.returnValue(selectedQueryString);

Reply via email to