diff --git a/docs/en_US/clear_saved_passwords.rst b/docs/en_US/clear_saved_passwords.rst
index 5ffda2c10..229ab4152 100644
--- a/docs/en_US/clear_saved_passwords.rst
+++ b/docs/en_US/clear_saved_passwords.rst
@@ -8,6 +8,7 @@ Use *Clear Saved Password* functionality to clear the saved password for the
database server.
.. image:: images/clear_saved_password.png
+ :alt: Clear saved password
:align: center
*Clear Saved Password* shows in the context menu for the selected server as well
@@ -17,6 +18,7 @@ Use *Clear SSH Tunnel Password* functionality to clear the saved password of SSH
Tunnel to connect to the database server.
.. image:: images/clear_tunnel_password.png
+ :alt: Clear SSH tunnel password
:align: center
*Clear SSH Tunnel Password* shows in the context menu for the selected server as
diff --git a/docs/en_US/connect_to_server.rst b/docs/en_US/connect_to_server.rst
index 57d836ce9..ec830a41b 100644
--- a/docs/en_US/connect_to_server.rst
+++ b/docs/en_US/connect_to_server.rst
@@ -11,6 +11,7 @@ and select *Connect Server...* from the context menu.
.. image:: images/connect_to_server.png
:alt: Connect to server dialog
+ :align: center
Provide authentication information for the selected server:
@@ -26,6 +27,7 @@ Tunnel and Database server passwords if not already saved.
.. image:: images/connect_to_tunneled_server.png
:alt: Connect to server dialog
+ :align: center
Provide authentication information for the selected server:
diff --git a/docs/en_US/debugger.rst b/docs/en_US/debugger.rst
index 067e77873..c68d62c67 100644
--- a/docs/en_US/debugger.rst
+++ b/docs/en_US/debugger.rst
@@ -61,6 +61,7 @@ values required by the program:
.. image:: images/debug_params.png
:alt: Debugger parameter dialog
+ :align: center
Use the fields on the *Debugger* dialog to provide a value for each parameter:
diff --git a/docs/en_US/images/editgrid.png b/docs/en_US/images/editgrid.png
index 042d242e2..a4b4dc32d 100644
Binary files a/docs/en_US/images/editgrid.png and b/docs/en_US/images/editgrid.png differ
diff --git a/docs/en_US/images/preferences_sql_editor.png b/docs/en_US/images/preferences_sql_editor.png
index e139e168f..ca2c3086f 100644
Binary files a/docs/en_US/images/preferences_sql_editor.png and b/docs/en_US/images/preferences_sql_editor.png differ
diff --git a/docs/en_US/images/promote_view_edit_data_warning.png b/docs/en_US/images/promote_view_edit_data_warning.png
index 6b3a43dd1..dcd3d2f51 100644
Binary files a/docs/en_US/images/promote_view_edit_data_warning.png and b/docs/en_US/images/promote_view_edit_data_warning.png differ
diff --git a/docs/en_US/images/query_autocomplete.png b/docs/en_US/images/query_autocomplete.png
index 3f164ef48..56e77606a 100644
Binary files a/docs/en_US/images/query_autocomplete.png and b/docs/en_US/images/query_autocomplete.png differ
diff --git a/docs/en_US/images/query_data_editing.png b/docs/en_US/images/query_data_editing.png
index 47ae2e20a..6dfd69b78 100644
Binary files a/docs/en_US/images/query_data_editing.png and b/docs/en_US/images/query_data_editing.png differ
diff --git a/docs/en_US/images/query_editing.png b/docs/en_US/images/query_editing.png
index 708b978d5..e5e9df2a7 100644
Binary files a/docs/en_US/images/query_editing.png and b/docs/en_US/images/query_editing.png differ
diff --git a/docs/en_US/images/query_execute_query.png b/docs/en_US/images/query_execute_query.png
new file mode 100644
index 000000000..8d8836740
Binary files /dev/null and b/docs/en_US/images/query_execute_query.png differ
diff --git a/docs/en_US/images/query_execute_script.png b/docs/en_US/images/query_execute_script.png
new file mode 100644
index 000000000..c7e3dce9e
Binary files /dev/null and b/docs/en_US/images/query_execute_script.png differ
diff --git a/docs/en_US/images/query_execute_warning.png b/docs/en_US/images/query_execute_warning.png
new file mode 100644
index 000000000..d0dfb4bb0
Binary files /dev/null and b/docs/en_US/images/query_execute_warning.png differ
diff --git a/docs/en_US/images/query_execution.png b/docs/en_US/images/query_execution.png
index bf58ba6f2..ef44b4aec 100644
Binary files a/docs/en_US/images/query_execution.png and b/docs/en_US/images/query_execution.png differ
diff --git a/docs/en_US/images/query_output_data.png b/docs/en_US/images/query_output_data.png
index 673ab5aaf..36575f1c9 100644
Binary files a/docs/en_US/images/query_output_data.png and b/docs/en_US/images/query_output_data.png differ
diff --git a/docs/en_US/images/query_output_error.png b/docs/en_US/images/query_output_error.png
index 3dd1ce267..70dfe896b 100644
Binary files a/docs/en_US/images/query_output_error.png and b/docs/en_US/images/query_output_error.png differ
diff --git a/docs/en_US/images/query_output_history.png b/docs/en_US/images/query_output_history.png
index 7ae49b84a..538c3c0bc 100644
Binary files a/docs/en_US/images/query_output_history.png and b/docs/en_US/images/query_output_history.png differ
diff --git a/docs/en_US/images/query_output_messages.png b/docs/en_US/images/query_output_messages.png
index 35fbfee53..357c2fe1c 100644
Binary files a/docs/en_US/images/query_output_messages.png and b/docs/en_US/images/query_output_messages.png differ
diff --git a/docs/en_US/images/query_output_notifications_listen.png b/docs/en_US/images/query_output_notifications_listen.png
index 155a3a7aa..951b38879 100644
Binary files a/docs/en_US/images/query_output_notifications_listen.png and b/docs/en_US/images/query_output_notifications_listen.png differ
diff --git a/docs/en_US/images/query_output_notifications_notify.png b/docs/en_US/images/query_output_notifications_notify.png
index 69be64faf..eaa3d7e4d 100644
Binary files a/docs/en_US/images/query_output_notifications_notify.png and b/docs/en_US/images/query_output_notifications_notify.png differ
diff --git a/docs/en_US/images/query_output_notifications_panel.png b/docs/en_US/images/query_output_notifications_panel.png
index 0558ebf7a..ce741d49b 100644
Binary files a/docs/en_US/images/query_output_notifications_panel.png and b/docs/en_US/images/query_output_notifications_panel.png differ
diff --git a/docs/en_US/images/query_sql_editor.png b/docs/en_US/images/query_sql_editor.png
index 9af945e3f..d37381dd2 100644
Binary files a/docs/en_US/images/query_sql_editor.png and b/docs/en_US/images/query_sql_editor.png differ
diff --git a/docs/en_US/images/query_tool.png b/docs/en_US/images/query_tool.png
index 1a2b13e20..5704c2e45 100644
Binary files a/docs/en_US/images/query_tool.png and b/docs/en_US/images/query_tool.png differ
diff --git a/docs/en_US/images/query_tool_connection_status.png b/docs/en_US/images/query_tool_connection_status.png
index 0f27695c3..5e587e19d 100644
Binary files a/docs/en_US/images/query_tool_connection_status.png and b/docs/en_US/images/query_tool_connection_status.png differ
diff --git a/docs/en_US/images/query_tool_editable_columns.png b/docs/en_US/images/query_tool_editable_columns.png
index 19b299eb7..21e2784ee 100644
Binary files a/docs/en_US/images/query_tool_editable_columns.png and b/docs/en_US/images/query_tool_editable_columns.png differ
diff --git a/docs/en_US/images/query_tool_manage_macros.png b/docs/en_US/images/query_tool_manage_macros.png
index 860507fa8..07627c248 100644
Binary files a/docs/en_US/images/query_tool_manage_macros.png and b/docs/en_US/images/query_tool_manage_macros.png differ
diff --git a/docs/en_US/images/query_tool_message.png b/docs/en_US/images/query_tool_message.png
index 898468e91..196de437c 100644
Binary files a/docs/en_US/images/query_tool_message.png and b/docs/en_US/images/query_tool_message.png differ
diff --git a/docs/en_US/images/query_toolbar.png b/docs/en_US/images/query_toolbar.png
index deb51f387..19079cec9 100644
Binary files a/docs/en_US/images/query_toolbar.png and b/docs/en_US/images/query_toolbar.png differ
diff --git a/docs/en_US/images/query_toolbar_explain.png b/docs/en_US/images/query_toolbar_explain.png
index 9772d81b3..b9207c19d 100644
Binary files a/docs/en_US/images/query_toolbar_explain.png and b/docs/en_US/images/query_toolbar_explain.png differ
diff --git a/docs/en_US/preferences.rst b/docs/en_US/preferences.rst
index 73aa97a42..8df26d56d 100644
--- a/docs/en_US/preferences.rst
+++ b/docs/en_US/preferences.rst
@@ -399,9 +399,6 @@ Use the fields on the *Editor* panel to change settings of the query editor.
changed to text/plain. Keyword highlighting and code folding will be disabled.
This will improve editor performance with large files.
-* When the *Show View/Edit Data Promotion Warning?* switch is set to *True*
- View/Edit Data tool will show promote to Query tool confirm dialog on query edit.
-
.. image:: images/preferences_sql_explain.png
:alt: Preferences dialog sqleditor explain options
:align: center
@@ -474,6 +471,16 @@ Use the fields on the *Options* panel to manage editor preferences.
by the Primary Key columns by default. When using the First/Last 100 Rows options,
data is always sorted.
+* When the *Show View/Edit Data Promotion Warning?* switch is set to *True*
+ View/Edit Data tool will show promote to Query tool confirm dialog on query edit.
+
+* When the *Underline query at cursor?* switch is set to *True*, query tool will
+ parse and underline the query at the cursor position.
+
+* When the *Underlined query execute warning?* switch is set to *True*, query tool
+ will warn upon clicking the *Execute Query* button in the query tool. The warning
+ will appear only if *Underline query at cursor?* is set to *False*.
+
.. image:: images/preferences_sql_results_grid.png
:alt: Preferences dialog sql results grid section
:align: center
@@ -503,7 +510,7 @@ preferences for copied data.
:align: center
Use the fields on the *Keyboard shortcuts* panel to configure shortcuts for the
-Query Tool window navigation:
+Query Tool window navigation.
.. image:: images/preferences_sql_formatting.png
:alt: Preferences dialog SQL Formatting section
diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst
index dabaf316d..be0e74fe5 100644
--- a/docs/en_US/query_tool.rst
+++ b/docs/en_US/query_tool.rst
@@ -26,13 +26,12 @@ allows you to:
:align: center
You can open multiple copies of the Query tool in individual tabs
-simultaneously. To close a copy of the Query tool, click the *X* in the
-upper-right hand corner of the tab bar.
+simultaneously. To close a copy of the Query tool, click the *X* of the tab.
The Query Tool features two panels:
* The upper panel displays the *SQL Editor*. You can use the panel to enter,
- edit, or execute a query. It also shows the *History* tab which can be used
+ edit, or execute a query or a script. It also shows the *History* tab which can be used
to view the queries that have been executed in the session, and a *Scratch Pad*
which can be used to hold text snippets during editing. If the Scratch Pad is
closed, it can be re-opened (or additional ones opened) by right-clicking in
@@ -75,14 +74,30 @@ key combination to select from a popup menu of autocomplete options.
After entering a query, select the *Execute script* icon from the toolbar. The
complete contents of the SQL editor panel will be sent to the database server
-for execution. To execute only a section of the code that is displayed in the
-SQL editor, highlight the text that you want the server to execute, and click
-the *Execute script* icon.
+for execution. To execute only a section of the code that is displayed in the
+SQL editor, highlight the text that you want the server to execute, and click the
+*Execute script* icon.
-.. image:: images/query_execute_section.png
+.. image:: images/query_execute_script.png
+ :alt: Query tool execute script section
+ :align: center
+
+You can also execute a query based on cursor position. Query tool will detect
+a query and underline it when cursor position changes. Now, to execute the
+current underlined query, hit the *Execute query* button on the toolbar. If a section
+is highlighted then it will behave like normal execute.
+
+.. image:: images/query_execute_query.png
:alt: Query tool execute query section
:align: center
+The warning will appear only if *Underline query at cursor?* is set to *False* and
+the *Underlined query execute warning?* switch is set to *True* Preferences Query tool's Options.
+
+.. image:: images/query_execute_warning.png
+ :alt: Query tool execute query warning
+ :align: center
+
The message returned by the server when a command executes is displayed on the
*Messages* tab. If the command is successful, the *Messages* tab displays
execution details.
@@ -159,8 +174,6 @@ The *Data Output* tab displays the result set of the query in a table format.
You can:
* Select and copy from the displayed result set.
-* Use the *Execute script* options to retrieve query execution information and
- set query execution options.
* Use the *Save results to file* icon to save the content of the *Data Output*
tab as a comma-delimited file.
* Edit the data in the result set of a SELECT query if it is updatable.
diff --git a/docs/en_US/query_tool_toolbar.rst b/docs/en_US/query_tool_toolbar.rst
index 93c9a6fad..3f61e184a 100644
--- a/docs/en_US/query_tool_toolbar.rst
+++ b/docs/en_US/query_tool_toolbar.rst
@@ -128,6 +128,9 @@ Query Execution
| | transaction. Any changes made by the transaction will be visible to others, and | |
| | durable in the event of a crash. | |
+----------------------+---------------------------------------------------------------------------------------------------+----------------+
+ | *Execute query* | Click the *Execute query* icon to either execute the query where the cursor is present or | Option+F5 (MAC)|
+ | | refresh the query highlighted in the SQL editor panel. | Alt+F5 (Others)|
+ +----------------------+---------------------------------------------------------------------------------------------------+----------------+
| *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. | |
@@ -206,6 +209,8 @@ Data Editing Options
+----------------------+---------------------------------------------------------------------------------------------------+----------------+
| Graph Visualiser | Use the Graph Visualiser button to generate graphs of the query results. | |
+----------------------+---------------------------------------------------------------------------------------------------+----------------+
+ | SQL | Use the SQL button to check the current query that gave the data. | |
+ +----------------------+---------------------------------------------------------------------------------------------------+----------------+
Status Bar
**********
diff --git a/web/migrations/versions/ac2c2e27dc2d_.py b/web/migrations/versions/ac2c2e27dc2d_.py
index ef04174de..7ab1dd751 100644
--- a/web/migrations/versions/ac2c2e27dc2d_.py
+++ b/web/migrations/versions/ac2c2e27dc2d_.py
@@ -8,6 +8,8 @@ Create Date: 2024-05-17 19:35:03.700104
"""
from alembic import op
import sqlalchemy as sa
+from pgadmin.model import Preferences
+from pgadmin.model import db
# revision identifiers, used by Alembic.
revision = 'ac2c2e27dc2d'
@@ -17,6 +19,10 @@ depends_on = None
def upgrade():
+ db.session.query(Preferences).filter(
+ Preferences.name == 'execute_query').update({'name': 'execute_script'})
+ db.session.commit()
+
meta = sa.MetaData()
meta.reflect(op.get_bind(), only=('user_macros',))
user_macros_table = sa.Table('user_macros', meta)
diff --git a/web/pgadmin/static/img/execute_query.svg b/web/pgadmin/static/img/execute_query.svg
new file mode 100644
index 000000000..02f27d552
--- /dev/null
+++ b/web/pgadmin/static/img/execute_query.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/web/pgadmin/static/img/sql_query.svg b/web/pgadmin/static/img/sql_query.svg
new file mode 100644
index 000000000..aaba59cf6
--- /dev/null
+++ b/web/pgadmin/static/img/sql_query.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Theme/dark.js b/web/pgadmin/static/js/Theme/dark.js
index 0c366e4da..ddf4640a8 100644
--- a/web/pgadmin/static/js/Theme/dark.js
+++ b/web/pgadmin/static/js/Theme/dark.js
@@ -133,7 +133,7 @@ export default function(basicSettings) {
foldmarker: '#0000FF',
activeline: '#323e43',
activelineLight: '#323e43',
- activelineBorderColor: 'none',
+ currentQueryBorderColor: '#A5CBE2',
guttersBg: '#303030',
guttersFg: '#8A8A8A',
},
diff --git a/web/pgadmin/static/js/Theme/high_contrast.js b/web/pgadmin/static/js/Theme/high_contrast.js
index 49605b836..208f65d39 100644
--- a/web/pgadmin/static/js/Theme/high_contrast.js
+++ b/web/pgadmin/static/js/Theme/high_contrast.js
@@ -132,7 +132,7 @@ export default function(basicSettings) {
foldmarker: '#FFFFFF',
activeline: '#063057',
activelineLight: '#063057',
- activelineBorderColor: 'none',
+ currentQueryBorderColor: '#A5CBE2',
guttersBg: '#2d3a48',
guttersFg: '#8b9cac',
},
diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx
index 52cda3121..e98c7b654 100644
--- a/web/pgadmin/static/js/Theme/index.jsx
+++ b/web/pgadmin/static/js/Theme/index.jsx
@@ -335,6 +335,9 @@ function getFinalTheme(baseTheme) {
},
right: {
borderRight: '1px solid '+baseTheme.otherVars.borderColor,
+ },
+ left: {
+ borderLeft: '1px solid '+baseTheme.otherVars.borderColor,
}
},
nodeIcon: {
diff --git a/web/pgadmin/static/js/Theme/overrides/codemirror.override.js b/web/pgadmin/static/js/Theme/overrides/codemirror.override.js
index 83cea7087..56ed86e56 100644
--- a/web/pgadmin/static/js/Theme/overrides/codemirror.override.js
+++ b/web/pgadmin/static/js/Theme/overrides/codemirror.override.js
@@ -39,6 +39,10 @@ export default function cmOverride(theme) {
backgroundColor: editor.activeline,
},
+ '& .cm-current-query': {
+ borderBottom: `1px solid ${editor.currentQueryBorderColor}`
+ },
+
'& .tok-keyword': {
color: editor.keyword,
fontWeight: 600
diff --git a/web/pgadmin/static/js/Theme/standard.js b/web/pgadmin/static/js/Theme/standard.js
index 8004551bd..5eeb594cc 100644
--- a/web/pgadmin/static/js/Theme/standard.js
+++ b/web/pgadmin/static/js/Theme/standard.js
@@ -156,7 +156,7 @@ export default function(basicSettings) {
foldmarker: '#0000FF',
activeline: '#EDF9FF',
activelineLight: '#EDF9FF',
- activelineBorderColor: '#BCDEF3',
+ currentQueryBorderColor: '#A5CBE2',
guttersBg: '#f3f5f9',
guttersFg: '#848ea0',
},
diff --git a/web/pgadmin/static/js/components/ExternalIcon.jsx b/web/pgadmin/static/js/components/ExternalIcon.jsx
index 2d59f7679..04eba6e81 100644
--- a/web/pgadmin/static/js/components/ExternalIcon.jsx
+++ b/web/pgadmin/static/js/components/ExternalIcon.jsx
@@ -18,6 +18,8 @@ import AWS from '../../img/aws.svg?svgr';
import BigAnimal from '../../img/biganimal.svg?svgr';
import Azure from '../../img/azure.svg?svgr';
import SQLFileSvg from '../../img/sql_file.svg?svgr';
+import SQLQuerySvg from '../../img/sql_query.svg?svgr';
+import ExecuteQuerySvg from '../../img/execute_query.svg?svgr';
import MagicSvg from '../../img/magic.svg?svgr';
import MsAzure from '../../img/ms_azure.svg?svgr';
import GoogleCloud from '../../img/google-cloud-1.svg?svgr';
@@ -96,6 +98,12 @@ GoogleCloudIcon.propTypes = {style: PropTypes.object};
export const SQLFileIcon = ({style})=>;
SQLFileIcon.propTypes = {style: PropTypes.object};
+export const SQLQueryIcon = ({style})=>;
+SQLQueryIcon.propTypes = {style: PropTypes.object};
+
+export const ExecuteQueryIcon = ({style})=>;
+ExecuteQueryIcon.propTypes = {style: PropTypes.object};
+
export const MagicIcon = ({style})=>;
MagicIcon.propTypes = {style: PropTypes.object};
diff --git a/web/pgadmin/static/js/components/ReactCodeMirror/CustomEditorView.js b/web/pgadmin/static/js/components/ReactCodeMirror/CustomEditorView.js
index a9808a821..cfcdc1a2c 100644
--- a/web/pgadmin/static/js/components/ReactCodeMirror/CustomEditorView.js
+++ b/web/pgadmin/static/js/components/ReactCodeMirror/CustomEditorView.js
@@ -6,6 +6,7 @@ import { syntaxTree } from '@codemirror/language';
import { autocompletion } from '@codemirror/autocomplete';
import {undo, indentMore, indentLess, toggleComment} from '@codemirror/commands';
import { errorMarkerEffect } from './extensions/errorMarker';
+import { currentQueryHighlighterEffect } from './extensions/currentQueryHighlighter';
import { activeLineEffect, activeLineField } from './extensions/activeLineMarker';
import { clearBreakpoints, hasBreakpoint, toggleBreakpoint } from './extensions/breakpointGutter';
@@ -102,7 +103,7 @@ export default class CustomEditorView extends EditorView {
// We already had found valid text
if(validTextFound) {
// continue till it reaches start so we can check for empty lines, etc.
- if(statementStartPos > 0 && statementStartPos < startPos) {
+ if(statementStartPos >= 0 && statementStartPos < startPos) {
startPos -= 1;
continue;
}
@@ -166,10 +167,18 @@ export default class CustomEditorView extends EditorView {
if(startPos < 0) startPos = 0;
if(endPos > this.state.doc.length) endPos = this.state.doc.length;
- return this.state.sliceDoc(startPos, endPos).trim();
+ return {
+ value: this.state.sliceDoc(startPos, endPos).trim(),
+ from: startPos,
+ to: endPos,
+ };
} catch (error) {
console.error(error);
- return this.getValue();
+ return {
+ value: '',
+ from: 0,
+ to: 0,
+ };
}
}
@@ -314,4 +323,8 @@ export default class CustomEditorView extends EditorView {
let scrollEffect = line >= 0 ? [EditorView.scrollIntoView(this.state.doc.line(line).from, {y: 'center'})] : [];
this.dispatch({ effects: [activeLineEffect.of({ from: line, to: line })].concat(scrollEffect) });
}
+
+ setQueryHighlightMark(from,to) {
+ this.dispatch({ effects: currentQueryHighlighterEffect.of({ from, to }) });
+ }
}
diff --git a/web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx b/web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx
index 24ecd0d7c..f504362bb 100644
--- a/web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx
+++ b/web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx
@@ -45,6 +45,7 @@ import errorMarkerExtn from '../extensions/errorMarker';
import CustomEditorView from '../CustomEditorView';
import breakpointGutter, { breakpointEffect } from '../extensions/breakpointGutter';
import activeLineExtn from '../extensions/activeLineMarker';
+import currentQueryHighlighterExtn from '../extensions/currentQueryHighlighter';
const arrowRightHtml = ReactDOMServer.renderToString();
const arrowDownHtml = ReactDOMServer.renderToString();
@@ -329,6 +330,9 @@ export default function Editor({
if (pref.brace_matching) {
newConfigExtn.push(bracketMatching());
}
+ if (pref.underline_query_cursor){
+ newConfigExtn.push(currentQueryHighlighterExtn());
+ }
editor.current.dispatch({
effects: configurables.current.reconfigure(newConfigExtn)
diff --git a/web/pgadmin/static/js/components/ReactCodeMirror/extensions/currentQueryHighlighter.js b/web/pgadmin/static/js/components/ReactCodeMirror/extensions/currentQueryHighlighter.js
new file mode 100644
index 000000000..d6590442a
--- /dev/null
+++ b/web/pgadmin/static/js/components/ReactCodeMirror/extensions/currentQueryHighlighter.js
@@ -0,0 +1,32 @@
+import {
+ EditorView,
+ Decoration,
+} from '@codemirror/view';
+import { StateEffect, StateField } from '@codemirror/state';
+
+export const currentQueryHighlighterEffect = StateEffect.define({
+ map: ({ from, to }, change) => ({ from: change.mapPos(from), to: change.mapPos(to) })
+});
+
+const currentQueryHighlighterDeco = Decoration.mark({ class: 'cm-current-query' });
+
+export const currentQueryHighlighterField = StateField.define({
+ create() {
+ return Decoration.none;
+ },
+ update(value, tr) {
+ value = value.map(tr.changes);
+ for (let e of tr.effects) if (e.is(currentQueryHighlighterEffect)) {
+ if (e.value.from < e.value.to) {
+ return Decoration.set([currentQueryHighlighterDeco.range(e.value.from, e.value.to)]);
+ }
+ return Decoration.none;
+ }
+ return value;
+ },
+ provide: f => EditorView.decorations.from(f)
+});
+
+export default function currentQueryHighlighterExtn() {
+ return [currentQueryHighlighterField];
+}
\ No newline at end of file
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolConstants.js b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolConstants.js
index 900673bf4..a1c382567 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolConstants.js
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolConstants.js
@@ -26,6 +26,7 @@ export const QUERY_TOOL_EVENTS = {
TRIGGER_REMOVE_FILTER: 'TRIGGER_REMOVE_FILTER',
TRIGGER_SET_LIMIT: 'TRIGGER_SET_LIMIT',
TRIGGER_FORMAT_SQL: 'TRIGGER_FORMAT_SQL',
+ TRIGGER_GRAPH_VISUALISER: 'TRIGGER_GRAPH_VISUALISER',
COPY_DATA: 'COPY_DATA',
SET_LIMIT_VALUE: 'SET_LIMIT_VALUE',
@@ -67,6 +68,7 @@ export const QUERY_TOOL_EVENTS = {
WARN_SAVE_DATA_CLOSE: 'WARN_SAVE_DATA_CLOSE',
WARN_SAVE_TEXT_CLOSE: 'WARN_SAVE_TEXT_CLOSE',
WARN_TXN_CLOSE: 'WARN_TXN_CLOSE',
+ EXECUTE_CURSOR_WARNING: 'EXECUTE_CURSOR_WARNING',
RESET_LAYOUT: 'RESET_LAYOUT',
FORCE_CLOSE_PANEL: 'FORCE_CLOSE_PANEL',
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx
index 9e486300d..0abc4c54a 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx
@@ -120,7 +120,7 @@ function isValidArray(val) {
return !(val != '' && (val.charAt(0) != '{' || val.charAt(val.length - 1) != '}'));
}
-function setEditorPosition(cellEle, editorEle) {
+export function setEditorPosition(cellEle, editorEle, closestEle, topValue) {
if(!editorEle || !cellEle) {
return;
}
@@ -128,12 +128,12 @@ function setEditorPosition(cellEle, editorEle) {
if(editorEle.style.left || editorEle.style.top) {
return;
}
- let gridEle = cellEle.closest('.rdg');
+ let gridEle = cellEle.closest(closestEle);
let cellRect = cellEle.getBoundingClientRect();
let gridEleRect = gridEle.getBoundingClientRect();
let position = {
left: cellRect.left,
- top: Math.max(cellRect.top - editorEle.offsetHeight + 12, 0)
+ top: Math.max(cellRect.top - editorEle.offsetHeight + topValue, 0)
};
if ((position.left + editorEle.offsetWidth + 10) > gridEle.offsetWidth) {
@@ -204,7 +204,7 @@ export function TextEditor({row, column, onRowChange, onClose}) {
return(
{
- setEditorPosition(getCellElement(column.idx), ele);
+ setEditorPosition(getCellElement(column.idx), ele, '.rdg', 12);
}} className={classes.textEditor} data-label="pg-editor" onKeyDown={suppressEnterKey} >
@@ -374,7 +374,7 @@ export function JsonTextEditor({row, column, onRowChange, onClose}) {
return (
{
- setEditorPosition(getCellElement(column.idx), ele);
+ setEditorPosition(getCellElement(column.idx), ele, '.rdg', 12);
}} className={classes.jsonEditor} data-label="pg-editor" onKeyDown={suppressEnterKey} >
({
+ flexGrow: 1,
+ backgroundColor: theme.palette.background.default,
+ padding: '1rem',
+ '& .textarea': {
+ ...theme.mixins.panelBorder?.all,
+ outline: 0,
+ resize: 'both',
+ maxHeight: '500px',
+ overflowY: 'scroll',
+ },
+}));
+
+const StyledFooter = styled('div')(({theme})=>({
+ display: 'flex',
+ justifyContent: 'space-between',
+ padding: '0.5rem',
+ ...theme.mixins.panelBorder?.top,
+ '& .margin': {
+ marginLeft: '0.25rem',
+ },
+}));
+
+export default function ConfirmExecuteQueryContent({ onContinue, onClose, closeModal, text }) {
+ const [formData, setFormData] = useState({
+ save_user_choice: false
+ });
+
+ return (
+
+
+ Do you want to run this query -
+
+
+
+ setFormData((prev) => ({ ...prev, 'save_user_choice': e.target.checked }))} />
+
+ } onClick={() => {
+ onClose?.();
+ closeModal();
+ }} >{gettext('Cancel')}
+ } onClick={() => {
+ let postFormData = new FormData();
+ postFormData.append('pref_data', JSON.stringify([{ 'name': 'underlined_query_execute_warning', 'value': !formData.save_user_choice, 'module': 'sqleditor' }]));
+ onContinue?.(postFormData);
+ closeModal();
+ }} autoFocus={true} >{gettext('Continue')}
+
+
+
+ );
+}
+
+ConfirmExecuteQueryContent.propTypes = {
+ closeModal: PropTypes.func,
+ text: PropTypes.string,
+ onContinue: PropTypes.func,
+ onClose: PropTypes.func
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmPromotionContent.jsx b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmPromotionContent.jsx
index 346ba1dd0..71fd0d9aa 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmPromotionContent.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmPromotionContent.jsx
@@ -1,8 +1,7 @@
import React, { useState } from 'react';
-import { useModalStyles } from '../../../../../../static/js/helpers/ModalProvider';
+import { styled } from '@mui/styles';
import gettext from 'sources/gettext';
import { Box } from '@mui/material';
-import { makeStyles } from '@mui/styles';
import { DefaultButton, PrimaryButton } from '../../../../../../static/js/components/Buttons';
import CloseIcon from '@mui/icons-material/CloseRounded';
import HTMLReactParser from 'html-react-parser';
@@ -10,48 +9,40 @@ import PropTypes from 'prop-types';
import CheckRounded from '@mui/icons-material/CheckRounded';
import { InputCheckbox } from '../../../../../../static/js/components/FormComponents';
-
-const useStyles = makeStyles(() => ({
- saveChoice: {
- margin: '10px 0 10px 10px',
- }
+const StyledFooter = styled('div')(({theme})=>({
+ display: 'flex',
+ justifyContent: 'space-between',
+ padding: '0.5rem',
+ ...theme.mixins.panelBorder?.top,
+ '& .margin': {
+ marginLeft: '0.25rem',
+ },
}));
-
export default function ConfirmPromotionContent({ onContinue, onClose, closeModal, text }) {
const [formData, setFormData] = useState({
save_user_choice: false
});
- const onDataChange = (e, id) => {
- let val = e;
- if (e?.target) {
- val = e.target.value;
- }
- setFormData((prev) => ({ ...prev, [id]: val }));
- };
- const modalClasses = useModalStyles();
- const classes = useStyles();
-
return (
{typeof (text) == 'string' ? HTMLReactParser(text) : text}
-
+
onDataChange(e.target.checked, 'save_user_choice')} />
-
-
- } onClick={() => {
- onClose?.();
- closeModal();
- }} >{gettext('Cancel')}
- } onClick={() => {
- let postFormData = new FormData();
- postFormData.append('pref_data', JSON.stringify([{ 'name': 'view_edit_promotion_warning', 'value': !formData.save_user_choice, 'module': 'sqleditor' }]));
- onContinue?.(postFormData);
- closeModal();
- }} autoFocus={true} >{gettext('Continue')}
-
+ onChange={(e) => setFormData((prev) => ({ ...prev, 'save_user_choice': e.target.checked }))} />
+
+ } onClick={() => {
+ onClose?.();
+ closeModal();
+ }} >{gettext('Cancel')}
+ } onClick={() => {
+ let postFormData = new FormData();
+ postFormData.append('pref_data', JSON.stringify([{ 'name': 'view_edit_promotion_warning', 'value': !formData.save_user_choice, 'module': 'sqleditor' }]));
+ onContinue?.(postFormData);
+ closeModal();
+ }} autoFocus={true} >{gettext('Continue')}
+
+
);
}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx
index 62549db9d..0b0c687eb 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx
@@ -15,7 +15,7 @@ import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import SaveRoundedIcon from '@mui/icons-material/SaveRounded';
import StopRoundedIcon from '@mui/icons-material/StopRounded';
import PlayArrowRoundedIcon from '@mui/icons-material/PlayArrowRounded';
-import { FilterIcon, CommitIcon, RollbackIcon } from '../../../../../../static/js/components/ExternalIcon';
+import { FilterIcon, CommitIcon, RollbackIcon, ExecuteQueryIcon } from '../../../../../../static/js/components/ExternalIcon';
import EditRoundedIcon from '@mui/icons-material/EditRounded';
import AssessmentRoundedIcon from '@mui/icons-material/AssessmentRounded';
import ExplicitRoundedIcon from '@mui/icons-material/ExplicitRounded';
@@ -88,7 +88,14 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
setButtonsDisabled((prev)=>({...prev, [name]: disable}));
}, []);
- const executeQuery = useCallback(()=>{
+ const executeCursor = useCallback(()=>{
+ if(!queryToolCtx.preferences.sqleditor.underline_query_cursor && queryToolCtx.preferences.sqleditor.underlined_query_execute_warning){
+ eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTE_CURSOR_WARNING);
+ } else {
+ eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION,true);
+ }
+ }, [queryToolCtx.preferences.sqleditor]);
+ const executeScript = useCallback(()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
}, []);
const cancelQuery = useCallback(()=>{
@@ -96,7 +103,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
}, []);
const explain = useCallback((analyze=false)=>{
- eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION, {
+ eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION, false, {
format: 'json',
analyze: analyze,
verbose: Boolean(checkedMenuItems['explain_verbose']),
@@ -278,7 +285,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, 'ROLLBACK;', null, true);
};
const executeMacro = (m)=>{
- eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION, null, m.sql);
+ eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION,false, null, m.sql);
};
const onLimitChange=(e)=>{
setLimit(e.target.value);
@@ -389,9 +396,15 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
}
},
{
- shortcut: queryToolPref.execute_query,
+ shortcut: queryToolPref.execute_script,
options: {
- callback: ()=>{!buttonsDisabled['execute']&&executeQuery();}
+ callback: ()=>{!buttonsDisabled['execute']&&executeScript();}
+ }
+ },
+ {
+ shortcut: queryToolPref.execute_cursor,
+ options: {
+ callback: ()=>{!buttonsDisabled['execute']&&executeCursor();}
}
},
{
@@ -521,7 +534,9 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
}
onClick={cancelQuery} disabled={buttonsDisabled['cancel']} shortcut={queryToolPref.btn_cancel_query} />
}
- onClick={executeQuery} disabled={buttonsDisabled['execute']} shortcut={queryToolPref.execute_query}/>
+ onClick={executeScript} disabled={buttonsDisabled['execute']} shortcut={queryToolPref.execute_script}/>
+ }
+ onClick={executeCursor} disabled={buttonsDisabled['execute'] || !queryToolCtx.params.is_query_tool} shortcut={queryToolPref.execute_cursor}/>
} splitButton
name="menu-autocommit" ref={autoCommitMenuRef} shortcut={queryToolPref.btn_execute_options}
onClick={toggleMenu} disabled={buttonsDisabled['execute-options']}/>
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx
index a857e586e..727945422 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx
@@ -21,6 +21,7 @@ import { checkTrojanSource, isShortcutValue, toCodeMirrorKey } from '../../../..
import { parseApiError } from '../../../../../../static/js/api_instance';
import { usePgAdmin } from '../../../../../../static/js/BrowserComponent';
import ConfirmPromotionContent from '../dialogs/ConfirmPromotionContent';
+import ConfirmExecuteQueryContent from '../dialogs/ConfirmExecuteQueryContent';
import usePreferences from '../../../../../../preferences/static/js/store';
import { getTitle } from '../../sqleditor_title';
import PropTypes from 'prop-types';
@@ -74,7 +75,7 @@ export default function Query({onTextSelect}) {
const queryToolPref = queryToolCtx.preferences.sqleditor;
- const highlightError = (cmObj, {errormsg: result, data})=>{
+ const highlightError = (cmObj, {errormsg: result, data}, executeCursor)=>{
let errorLineNo = 0,
startMarker = 0,
endMarker = 0,
@@ -84,9 +85,9 @@ export default function Query({onTextSelect}) {
cmObj.removeErrorMark();
// In case of selection we need to find the actual line no
- if (cmObj.getSelection().length > 0) {
+ if (cmObj.getSelection().length > 0 || executeCursor) {
selectedLineNo = cmObj.getCurrentLineNo();
- origQueryLen = cmObj.line(selectedLineNo).length;
+ origQueryLen = cmObj.getLine(selectedLineNo).length;
}
// Fetch the LINE string using regex from the result
@@ -144,7 +145,7 @@ export default function Query({onTextSelect}) {
}
};
- const triggerExecution = (explainObject, macroSQL)=>{
+ const triggerExecution = (executeCursor=false, explainObject, macroSQL)=>{
if(queryToolCtx.params.is_query_tool) {
let external = null;
let query = editor.current?.getSelection();
@@ -152,12 +153,15 @@ export default function Query({onTextSelect}) {
const regex = /\$SELECTION\$/gi;
query = macroSQL.replace(regex, query);
external = true;
- } else{
+ } else if(executeCursor) {
+ /* Execute query at cursor position */
+ query = query || editor.current?.getQueryAt(editor.current?.state.selection.head).value || '';
+ } else {
/* Normal execution */
query = query || editor.current?.getValue() || '';
}
if(query) {
- eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, query, explainObject, external, null);
+ eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, query, explainObject, external, null, executeCursor);
}
} else {
eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, null, null);
@@ -170,10 +174,11 @@ export default function Query({onTextSelect}) {
});
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION, triggerExecution);
+ eventBus.registerListener(QUERY_TOOL_EVENTS.EXECUTE_CURSOR_WARNING, checkUnderlineQueryCursorWarning);
- eventBus.registerListener(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, (result)=>{
+ eventBus.registerListener(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, (result, executeCursor)=>{
if(result) {
- highlightError(editor.current, result);
+ highlightError(editor.current, result, executeCursor);
} else {
editor.current.removeErrorMark();
}
@@ -385,6 +390,11 @@ export default function Query({onTextSelect}) {
}, [queryToolCtx.params.trans_id]);
const cursorActivity = useCallback(_.debounce((cursor)=>{
+ if (queryToolCtx.preferences.sqleditor.underline_query_cursor){
+ let {from, to}=editor.current.getQueryAt(editor.current?.state.selection.head);
+ editor.current.setQueryHighlightMark(from,to);
+ }
+
lastCursorPos.current = cursor;
eventBus.fireEvent(QUERY_TOOL_EVENTS.CURSOR_ACTIVITY, [lastCursorPos.current.line, lastCursorPos.current.ch+1]);
}, 100), []);
@@ -434,6 +444,28 @@ export default function Query({onTextSelect}) {
});
};
+ const checkUnderlineQueryCursorWarning = () => {
+ let query = editor.current?.getSelection();
+ query = query || editor.current?.getQueryAt(editor.current?.state.selection.head).value || '';
+ query && queryToolCtx.modal.showModal(gettext('Execute query'), (closeModal) =>{
+ return ({
+ preferencesStore.setPreference(formData);
+ eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION,true);
+ }}
+ onClose={()=>{
+ closeModal?.();
+ }}
+ />);
+ }, {
+ onClose:(closeModal)=>{
+ closeModal?.();
+ }
+ });
+ };
+
const promoteToQueryTool = () => {
if(!queryToolCtx.params.is_query_tool){
queryToolCtx.toggleQueryTool();
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx
index fbaa907b3..083530c82 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx
@@ -181,7 +181,7 @@ export class ResultSetUtils {
}
async startExecution(query, explainObject, onIncorrectSQL, flags={
- isQueryTool: true, external: false, reconnect: false
+ isQueryTool: true, external: false, reconnect: false, executeCursor: false
}) {
let startTime = new Date();
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, '');
@@ -232,7 +232,7 @@ export class ResultSetUtils {
is_pgadmin_query: false,
});
if(!flags.external) {
- this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, httpMessageData.data.result);
+ this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, httpMessageData.data.result, flags.executeCursor);
}
}
} catch(e) {
@@ -241,7 +241,7 @@ export class ResultSetUtils {
e,
{
connectionLostCallback: ()=>{
- this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, query, explainObject, flags.external, true);
+ this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, query, explainObject, flags.external, true, flags.executeCursor);
},
checkTransaction: true,
}
@@ -282,7 +282,7 @@ export class ResultSetUtils {
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.MESSAGES);
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, error.response.data.data?.transaction_status);
if (!flags.external) {
- this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, parseApiError(error, true));
+ this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, parseApiError(error, true), flags.executeCursor);
}
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.PUSH_HISTORY, {
status: false,
@@ -296,7 +296,7 @@ export class ResultSetUtils {
});
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, error, {
connectionLostCallback: ()=>{
- this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, this.query, explainObject, flags.external, true);
+ this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, this.query, explainObject, flags.external, true, flags.executeCursor);
},
checkTransaction: true,
});
@@ -751,6 +751,7 @@ export function ResultSet() {
const queryToolCtx = useContext(QueryToolContext);
const layoutDocker = useContext(LayoutDockerContext);
const [loaderText, setLoaderText] = useState('');
+ const [dataOutputQuery,setDataOutputQuery] = useState('');
const [queryData, setQueryData] = useState(null);
const [rows, setRows] = useState([]);
const [columns, setColumns] = useState([]);
@@ -791,7 +792,7 @@ export function ResultSet() {
eventBus.fireEvent(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CELL_CHANGED, selectedRows.size, selectedColumns.size, selectedRange.current, selectedCell.current?.length);
};
- const executionStartCallback = async (query, explainObject, external=false, reconnect=false)=>{
+ const executionStartCallback = async (query, explainObject, external=false, reconnect=false, executeCursor=false)=>{
const yesCallback = async ()=>{
/* Reset */
eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, null);
@@ -800,13 +801,14 @@ export function ResultSet() {
setSelectedColumns(new Set());
rsu.current.resetClientPKIndex();
setLoaderText(gettext('Waiting for the query to complete...'));
+ setDataOutputQuery(query);
return await rsu.current.startExecution(
query, explainObject,
()=>{
setColumns([]);
setRows([]);
},
- {isQueryTool: queryToolCtx.params.is_query_tool, external: external, reconnect: reconnect}
+ {isQueryTool: queryToolCtx.params.is_query_tool, external: external, reconnect: reconnect, executeCursor: executeCursor}
);
};
@@ -835,7 +837,7 @@ export function ResultSet() {
setRows([]);
},
explainObject,
- {isQueryTool: queryToolCtx.params.is_query_tool, external: external, reconnect: reconnect}
+ {isQueryTool: queryToolCtx.params.is_query_tool, external: external, reconnect: reconnect, executeCursor: executeCursor}
);
};
@@ -1386,7 +1388,7 @@ export function ResultSet() {
}
{queryData && <>
-
+
({
- root: {
- padding: '2px',
- display: 'flex',
- alignItems: 'center',
- gap: '4px',
- backgroundColor: theme.otherVars.editorToolbarBg,
- ...theme.mixins.panelBorder.bottom,
- },
+const StyledDiv = styled('div')(({theme})=>({
+ padding: '2px',
+ display: 'flex',
+ alignItems: 'center',
+ gap: '4px',
+ backgroundColor: theme.otherVars.editorToolbarBg,
+ ...theme.mixins.panelBorder.bottom,
}));
-export function ResultSetToolbar({canEdit, totalRowCount}) {
- const classes = useStyles();
+const StyledEditor = styled('div')(({theme})=>({
+ position: 'absolute',
+ backgroundColor: theme.palette.background.default,
+ fontSize: '12px',
+ ...theme.mixins.panelBorder.all,
+ maxWidth:'50%',
+ overflow:'auto',
+ maxHeight:'35%',
+ '& .textarea': {
+ border: 0,
+ outline: 0,
+ resize: 'both',
+ }
+}));
+
+export function ResultSetToolbar({query,canEdit, totalRowCount}) {
const eventBus = useContext(QueryToolEventsContext);
const queryToolCtx = useContext(QueryToolContext);
-
+ const [dataOutputQueryBtn,setDataOutputQueryBtn] = useState(false);
const [buttonsDisabled, setButtonsDisabled] = useState({
'save-data': true,
'delete-rows': true,
@@ -168,9 +182,31 @@ export function ResultSetToolbar({canEdit, totalRowCount}) {
},
], queryToolCtx.mainContainerRef);
+ function suppressEnterKey(e) {
+ if(e.keyCode == 13) {
+ e.stopPropagation();
+ }
+ }
+
+ const ShowDataOutputQueryPopup =()=> {
+ return (
+
+ {
+ setEditorPosition(document.getElementById('sql-query'), ele, '.MuiBox-root', 29);
+ }} onKeyDown={suppressEnterKey}>
+
+
+
+ );
+ };
+
return (
<>
-
+
}
shortcut={queryToolPref.btn_add_row} disabled={!canEdit} onClick={addRow} />
@@ -198,7 +234,16 @@ export function ResultSetToolbar({canEdit, totalRowCount}) {
}
onClick={showGraphVisualiser} disabled={buttonsDisabled['save-result']} />
-
+ {query &&
+ <>
+
+ }
+ onClick={()=>{setDataOutputQueryBtn(prev=>!prev);}} onBlur={()=>{setDataOutputQueryBtn(false);}} disabled={!query} id='sql-query'/>
+
+ { dataOutputQueryBtn && }
+ >
+ }
+
{
+ const ThemedCM = withTheme(CodeMirror);
+ let cmInstance, editor;
+
+ const cmRerender = (props)=>{
+ cmInstance.rerender(
+ {
+ editor = obj;
+ }}
+ {...props}
+ />
+ );
+ };
+ beforeEach(()=>{
+ cmInstance = render(
+ {
+ editor = obj;
+ }}
+ />);
+ });
+
+ it('single query with no cursor position',()=>{
+ cmRerender({value:'select * from public.actor;'});
+ expect(editor.getQueryAt()).toEqual({'value': 'select * from public.actor;', 'from': 0, 'to': 27});
+ });
+
+ it('cursor within a query in multiple queries',()=>{
+ cmRerender({value: 'select * from public.actor; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5;\n\nselect * from public.city;\n\nselect 1;'});
+ expect(editor.getQueryAt(20)).toEqual({'value': 'select * from public.actor;', 'from': 0, 'to': 27});
+ });
+
+ it('cursor outside the semicolon of a query in multiple queries',()=>{
+ cmRerender({value: 'select * from public.actor; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5;\n\nselect * from public.city;\n\nselect 1;'});
+ expect(editor.getQueryAt(29)).toEqual({'value': 'select * from public.actor;', 'from': 0, 'to': 27});
+ });
+
+ it('cursor at the starting of a comment block',()=>{
+ cmRerender({value: 'select * from public.actor; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5;\n\nselect * from public.city;\n\nselect 1;'});
+ expect(editor.getQueryAt(31)).toEqual({'value': '--rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff', 'from': 27, 'to': 107});
+ });
+
+ it('cursor inside a comment block',()=>{
+ cmRerender({value: 'select * from public.actor; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5;\n\nselect * from public.city;\n\nselect 1;'});
+ expect(editor.getQueryAt(72)).toEqual({'value': '--rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff', 'from': 27, 'to': 107});
+ });
+
+ it('cursor inside a comment block`s 2nd line',()=>{
+ cmRerender({value: 'select * from public.actor; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5;\n\nselect * from public.city;\n\nselect 1;'});
+ expect(editor.getQueryAt(107)).toEqual({'value': '--rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff', 'from': 27, 'to': 107});
+ });
+
+ it('cursor at the starting of a query in multiple queries',()=>{
+ cmRerender({value: 'select * from public.actor; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5;\n\nselect * from public.city;\n\nselect 1;'});
+ expect(editor.getQueryAt(109)).toEqual({'value': 'select * from public.address where address_id=5;', 'from': 109, 'to': 157});
+ });
+
+ it('cursor at the next line where query ends with semicolon',()=>{
+ cmRerender({value: 'select * from public.actor; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5;\n\nselect * from public.city;\n\nselect 1;'});
+ expect(editor.getQueryAt(158)).toEqual({'value': 'select * from public.address where address_id=5;', 'from': 109, 'to': 157});
+ });
+
+ it('cursor at an empty line where query is present one empty line above',()=>{
+ cmRerender({value: 'select * from public.actor; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5;\n\nselect * from public.city;\n\nselect 1;\n\n\n'});
+ expect(editor.getQueryAt(198)).toEqual({'value': '', 'from': 198, 'to': 199});
+ });
+
+ it('cursor at 2nd line and query is in 2 lines',()=>{
+ cmRerender({value: 'select * from public.actor --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address \n\twhere address_id=5;\n\nselect * from public.city;\n\nselect 1;'});
+ expect(editor.getQueryAt(141)).toEqual({'value':'select * from public.address \n\twhere address_id=5;', 'from': 108, 'to': 158});
+ });
+
+ it('cursor at the start of query and multiple queries without semicolon',()=>{
+ cmRerender({value: 'select * from public.actor --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5\n\nselect * from public.city\n\nselect 1'});
+ expect(editor.getQueryAt(0)).toEqual({'value': 'select * from public.actor --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff', 'from': 0, 'to': 106});
+ });
+
+ it('cursor at the end of query and multiple queries without semicolon',()=>{
+ cmRerender({value: 'select * from public.actor --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5\n\nselect * from public.city\n\nselect 1'});
+ expect(editor.getQueryAt(26)).toEqual({'value': 'select * from public.actor --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff', 'from': 0, 'to': 106});
+ });
+
+ it('cursor in between of a query and multiple queries without semicolon',()=>{
+ cmRerender({value: 'select * from public.actor --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5\n\nselect * from public.city\n\nselect 1'});
+ expect(editor.getQueryAt(17)).toEqual({'value': 'select * from public.actor --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff', 'from': 0, 'to': 106});
+ });
+
+ it('cursor is at a new empty line and just above it a query without semicolon',()=>{
+ cmRerender({value: 'select * from public.actor --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5\n\nselect * from public.city\n\nselect 1'});
+ expect(editor.getQueryAt(156)).toEqual({'value': 'select * from public.address where address_id=5', 'from': 108, 'to': 156});
+ });
+
+ it('cursor at a empty query with semicolon',()=>{
+ cmRerender({value: 'select * from public.actor; ; --rhhryyr select * from public.film\n--skskks\n--sksksksksks\n--sksksksksksdff\n\t\nselect * from public.address where address_id=5\n\nselect * from public.city\n\nselect 1'});
+ expect(editor.getQueryAt(29)).toEqual({'value': 'select * from public.actor;', 'from': 0, 'to': 27});
+ });
+
+});