diff --git a/docs/en_US/images/query_tool_add_to_macro.png b/docs/en_US/images/query_tool_add_to_macro.png
new file mode 100644
index 000000000..88a22cb7c
Binary files /dev/null and b/docs/en_US/images/query_tool_add_to_macro.png differ
diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst
index e2ead1c05..dabaf316d 100644
--- a/docs/en_US/query_tool.rst
+++ b/docs/en_US/query_tool.rst
@@ -485,6 +485,12 @@ To create a macro, select the *Manage Macros* option from the *Macros* menu on t
:alt: Query Tool Manage Macros dialogue
:align: center
+To add a query to macros, write and select your query, then go to the *Macros* menu in the Query Tool and click *Add to macros*. Your query will be automatically saved to macros.
+
+.. image:: images/query_tool_add_to_macro.png
+ :alt: Query Tool Add To Macros
+ :align: center
+
To delete a macro, select the macro on the *Manage Macros* dialogue, and then click the *Delete* button.
The server will prompt you for confirmation to delete the macro.
diff --git a/web/migrations/versions/ac2c2e27dc2d_.py b/web/migrations/versions/ac2c2e27dc2d_.py
new file mode 100644
index 000000000..ef04174de
--- /dev/null
+++ b/web/migrations/versions/ac2c2e27dc2d_.py
@@ -0,0 +1,61 @@
+
+"""empty message
+
+Revision ID: ac2c2e27dc2d
+Revises: ec0f11f9a4e6
+Create Date: 2024-05-17 19:35:03.700104
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+# revision identifiers, used by Alembic.
+revision = 'ac2c2e27dc2d'
+down_revision = 'ec0f11f9a4e6'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ meta = sa.MetaData()
+ meta.reflect(op.get_bind(), only=('user_macros',))
+ user_macros_table = sa.Table('user_macros', meta)
+
+ # Create a select statement
+ stmt = sa.select(
+ user_macros_table.columns.mid,
+ user_macros_table.columns.uid, user_macros_table.columns.name,
+ user_macros_table.columns.sql
+ )
+ # Fetch the data from the user_macros table
+ results = op.get_bind().execute(stmt).fetchall()
+
+ # Drop and re-create user macro table.
+ op.drop_table('user_macros')
+ op.create_table(
+ 'user_macros',
+ sa.Column('id', sa.Integer(), autoincrement=True),
+ sa.Column('mid', sa.Integer(), nullable=True),
+ sa.Column('uid', sa.Integer(), nullable=False),
+ sa.Column('name', sa.String(length=1024), nullable=False),
+ sa.Column('sql', sa.String()),
+ sa.ForeignKeyConstraint(['mid'], ['macros.id'], ondelete='CASCADE'),
+ sa.ForeignKeyConstraint(['uid'], ['user.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id',))
+
+ # Reflect the new table structure
+ meta.reflect(op.get_bind(), only=('user_macros',))
+ new_user_macros_table = sa.Table('user_macros', meta)
+
+ # Bulk insert the fetched data into the new user_macros table
+ op.bulk_insert(
+ new_user_macros_table,
+ [
+ {'mid': row[0], 'uid': row[1], 'name': row[2], 'sql': row[3]}
+ for row in results
+ ]
+ )
+
+def downgrade():
+ # pgAdmin only upgrades, downgrade not implemented.
+ pass
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index 04c2ce00e..c10c7d053 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -33,7 +33,7 @@ import config
#
##########################################################################
-SCHEMA_VERSION = 39
+SCHEMA_VERSION = 40
##########################################################################
#
@@ -433,11 +433,12 @@ class Macros(db.Model):
class UserMacros(db.Model):
"""Define the macro for a particular user."""
__tablename__ = 'user_macros'
+ id = db.Column(db.Integer, primary_key=True, autoincrement=True)
mid = db.Column(
- db.Integer, db.ForeignKey('macros.id'), primary_key=True
+ db.Integer, db.ForeignKey('macros.id'), nullable=True
)
uid = db.Column(
- db.Integer, db.ForeignKey(USER_ID), primary_key=True
+ db.Integer, db.ForeignKey(USER_ID)
)
name = db.Column(db.String(1024), nullable=False)
sql = db.Column(db.Text(), nullable=False)
diff --git a/web/pgadmin/static/js/SchemaView/DataGridView.jsx b/web/pgadmin/static/js/SchemaView/DataGridView.jsx
index 5458e03e3..dc0235387 100644
--- a/web/pgadmin/static/js/SchemaView/DataGridView.jsx
+++ b/web/pgadmin/static/js/SchemaView/DataGridView.jsx
@@ -518,8 +518,13 @@ export default function DataGridView({
if(!props.canAddRow) {
return;
}
-
let newRow = schemaRef.current.getNewData();
+
+ const current_macros = schemaRef.current?._top?._sessData?.macro || null;
+ if (current_macros){
+ newRow = schemaRef.current.getNewData(current_macros);
+ }
+
if(props.expandEditOnAdd && props.canEdit) {
newRowIndex.current = rows.length;
}
diff --git a/web/pgadmin/static/js/components/Menu.jsx b/web/pgadmin/static/js/components/Menu.jsx
index 7bd2425ce..00e5dabf0 100644
--- a/web/pgadmin/static/js/components/Menu.jsx
+++ b/web/pgadmin/static/js/components/Menu.jsx
@@ -107,11 +107,15 @@ export const PgMenuItem = applyStatics(MenuItem)(({hasCheck=false, checked=false
props.onClick(e);
};
}
+ const keyVal = shortcutToString(shortcut, accesskey);
+
const dataLabel = typeof(children) == 'string' ? children : props.datalabel;
return ;
});
diff --git a/web/pgadmin/static/js/components/ReactCodeMirror/index.jsx b/web/pgadmin/static/js/components/ReactCodeMirror/index.jsx
index a4e56374d..50b25ed3a 100644
--- a/web/pgadmin/static/js/components/ReactCodeMirror/index.jsx
+++ b/web/pgadmin/static/js/components/ReactCodeMirror/index.jsx
@@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
-import React, { useCallback, useMemo, useRef, useState } from 'react';
+import React, { useCallback, useMemo, useRef, useState, useEffect } from 'react';
import { makeStyles } from '@mui/styles';
import FileCopyRoundedIcon from '@mui/icons-material/FileCopyRounded';
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
@@ -62,7 +62,7 @@ CopyButton.propTypes = {
};
-export default function CodeMirror({className, currEditor, showCopyBtn=false, customKeyMap=[], ...props}) {
+export default function CodeMirror({className, currEditor, showCopyBtn=false, customKeyMap=[], onTextSelect, ...props}) {
const classes = useStyles();
const editor = useRef();
const [[showFind, isReplace], setShowFind] = useState([false, false]);
@@ -111,8 +111,36 @@ export default function CodeMirror({className, currEditor, showCopyBtn=false, cu
const onMouseEnter = useCallback(()=>{showCopyBtn && setShowCopy(true);}, []);
const onMouseLeave = useCallback(()=>{showCopyBtn && setShowCopy(false);}, []);
+ // Use to handle text selection events.
+ useEffect(() => {
+ if (!onTextSelect) return;
+
+ const handleSelection = () => {
+ const selectedText = window.getSelection().toString();
+ if (selectedText) {
+ onTextSelect(selectedText);
+ } else {
+ onTextSelect(''); // Reset if no text is selected
+ }
+ };
+
+ const handleKeyUp = () => {
+ handleSelection();
+ };
+ // Add event listeners for mouseup and keyup events to detect text selection.
+ document.addEventListener('mouseup', handleSelection);
+ document.addEventListener('keyup', handleKeyUp);
+
+ // Cleanup function to remove event listeners when component unmounts or onTextSelect changes.
+ return () => {
+ document.removeEventListener('mouseup', handleSelection);
+ document.removeEventListener('keyup', handleKeyUp);
+ };
+ }, [onTextSelect]);
+
+
return (
-
+
{showCopy &&
}
@@ -126,4 +154,5 @@ CodeMirror.propTypes = {
className: CustomPropTypes.className,
showCopyBtn: PropTypes.bool,
customKeyMap: PropTypes.array,
+ onTextSelect:PropTypes.func
};
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index d94868ffe..9b6b61db1 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -49,7 +49,7 @@ from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
read_file_generator
from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
-from pgadmin.tools.sqleditor.utils.macros import get_macros,\
+from pgadmin.tools.sqleditor.utils.macros import get_macros, \
get_user_macros, set_macros
from pgadmin.utils.constants import MIMETYPE_APP_JS, \
SERVER_CONNECTION_CLOSED, ERROR_MSG_TRANS_ID_NOT_FOUND, \
@@ -130,6 +130,7 @@ class SqlEditorModule(PgAdminModule):
'sqleditor.clear_query_history',
'sqleditor.get_macro',
'sqleditor.get_macros',
+ 'sqleditor.get_user_macros',
'sqleditor.set_macros',
'sqleditor.get_new_connection_data',
'sqleditor.get_new_connection_servers',
@@ -2692,3 +2693,15 @@ def update_macros(trans_id):
_, _, _, _, _ = check_transaction_status(trans_id)
return set_macros()
+
+
+@blueprint.route(
+ '/get_user_macros',
+ methods=["GET"], endpoint='get_user_macros'
+)
+@pga_login_required
+def user_macros(json_resp=True):
+ """
+ This method is used to fetch all user macros.
+ """
+ return get_user_macros()
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx
index 4e6e6b6fa..9d72dba1c 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx
@@ -50,6 +50,21 @@ function initConnection(api, params, passdata) {
return api.post(url_for('NODE-server.connect_id', params), passdata);
}
+export function getRandomName(existingNames) {
+ const maxNumber = existingNames.reduce((max, name) => {
+ const match = name.match(/\d+$/); // Extract the number from the name
+ if (match) {
+ const number = parseInt(match[0], 10);
+ return number > max ? number : max;
+ }
+ return max;
+ }, 0);
+
+ // Generate the new name
+ const newName = `Macro ${maxNumber + 1}`;
+ return newName;
+}
+
function setPanelTitle(docker, panelId, title, qtState, dirty=false) {
if(qtState.current_file) {
title = qtState.current_file.split('\\').pop().split('/').pop();
@@ -194,6 +209,8 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
}],
});
+ const [selectedText, setSelectedText] = useState('');
+
const setQtState = (state)=>{
_setQtState((prev)=>({...prev,...evalFunc(null, state, prev)}));
};
@@ -250,7 +267,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
{
maximizable: true,
tabs: [
- LayoutDocker.getPanel({id: PANELS.QUERY, title: gettext('Query'), content:
}),
+ LayoutDocker.getPanel({id: PANELS.QUERY, title: gettext('Query'), content:
setSelectedText(text)}/>}),
LayoutDocker.getPanel({id: PANELS.HISTORY, title: gettext('Query History'), content: ,
cached: undefined}),
],
@@ -796,7 +813,38 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
}}
onClose={onClose}/>
}, 850, 500);
- }, [qtState.preferences.browser]);
+ }, [qtState.preferences.browser]);
+
+ const onAddToMacros = () => {
+ if (selectedText){
+ let currMacros = qtState.params.macros;
+ const existingNames = currMacros.map(macro => macro.name);
+ const newName = getRandomName(existingNames);
+ let changed = [{ 'name': newName, 'sql': selectedText }];
+
+ api.put(
+ url_for('sqleditor.set_macros', {
+ 'trans_id': qtState.params.trans_id,
+ }),
+ { changed: changed }
+ )
+ .then(({ data: respData }) => {
+ const filteredData = respData.filter(m => Boolean(m.name));
+ setQtState(prev => ({
+ ...prev,
+ params: {
+ ...prev.params,
+ macros: filteredData,
+ },
+ }));
+ })
+ .catch(error => {
+ console.error(error);
+ });
+ }
+ setSelectedText('');
+ };
+
const onFilterClick = useCallback(()=>{
const onClose = ()=>docker.current.close('filter-dialog');
@@ -884,8 +932,9 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
), [containerRef.current, onManageMacros, onFilterClick])}
+ />), [containerRef.current, onManageMacros, onFilterClick, onAddToMacros])}
docker.current=obj}
defaultLayout={defaultLayout}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/MacrosDialog.jsx b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/MacrosDialog.jsx
index c3bcb9665..e6183facb 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/MacrosDialog.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/MacrosDialog.jsx
@@ -11,7 +11,7 @@ import React from 'react';
import SchemaView from '../../../../../../static/js/SchemaView';
import BaseUISchema from '../../../../../../static/js/SchemaView/base_schema.ui';
import gettext from 'sources/gettext';
-import { QueryToolContext } from '../QueryToolComponent';
+import { QueryToolContext, getRandomName } from '../QueryToolComponent';
import url_for from 'sources/url_for';
import _ from 'lodash';
import PropTypes from 'prop-types';
@@ -23,17 +23,36 @@ class MacrosCollection extends BaseUISchema {
}
get idAttribute() {
- return 'mid';
+ return 'id';
+ }
+
+ /* Returns the new data row for the schema based on defaults and input */
+ getNewData(current_macros, data={}) {
+ let newRow = {};
+ this.fields.forEach((field)=>{
+ newRow[field.id] = this.defaults[field.id];
+ });
+ newRow = {
+ ...newRow,
+ ...data,
+ };
+ if (current_macros){
+ // Extract an array of existing names from the 'macro' collection
+ const existingNames = current_macros.map(macro => macro.name);
+ const newName = getRandomName(existingNames);
+ newRow.name = newName;
+ }
+ return newRow;
}
get baseFields() {
let obj = this;
return [
{
- id: 'id', label: gettext('Key'), cell: 'select', noEmpty: true,
+ id: 'mid', label: gettext('Key'), cell: 'select', noEmpty: false,
width: 100, options: obj.keyOptions, optionsReloadBasis: obj.keyOptions.length,
controlProps: {
- allowClear: false,
+ allowClear: true,
}
},
{
@@ -73,10 +92,14 @@ class MacrosSchema extends BaseUISchema {
}
validate(state, setError) {
- let allKeys = state.macro.map((m)=>m.id.toString());
+ let allKeys = state.macro.map((m) => m.mid ? m.mid.toString() : null).filter(key => key !== null);
+ let allNames = state.macro.map((m) => m.name ? m.name.toLowerCase() : null);
if(allKeys.length != new Set(allKeys).size) {
setError('macro', gettext('Key must be unique.'));
return true;
+ } else if(allNames.length != new Set(allNames).size) {
+ setError('macro', gettext('Name must be unique.'));
+ return true;
}
return false;
}
@@ -89,26 +112,28 @@ const useStyles = makeStyles((theme)=>({
},
}));
-function getChangedMacros(macrosData, changeData) {
+function getChangedMacros(userMacrosData, changeData) {
/* For backend, added, removed is changed. Convert all added, removed to changed. */
let changed = [];
+
for (const m of (changeData.macro.changed || [])) {
let newM = {...m};
if('id' in m) {
- /* if key changed, clear prev and add new */
- changed.push({id: m.mid, name: null, sql: null});
- let em = _.find(macrosData, (d)=>d.mid==m.mid);
- newM = {name: em.name, sql: em.sql, ...m};
+ let em = _.find(userMacrosData, (d)=>d.id==m.id);
+ newM = {name: m.name ? (m.name) : em.name , sql: m.sql ? m.sql : em.sql, mid: m.mid ? m.mid : em.mid, ...m};
} else {
newM.id = m.mid;
}
- delete newM.mid;
changed.push(newM);
}
for (const m of (changeData.macro.deleted || [])) {
changed.push({id: m.id, name: null, sql: null});
}
for (const m of (changeData.macro.added || [])) {
+ if (m.id && m.id !== 0){
+ m.mid = m.id;
+ delete m.id;
+ }
changed.push(m);
}
return changed;
@@ -118,30 +143,46 @@ export default function MacrosDialog({onClose, onSave}) {
const classes = useStyles();
const queryToolCtx = React.useContext(QueryToolContext);
const [macrosData, setMacrosData] = React.useState([]);
+ const [userMacrosData, setUserMacrosData] = React.useState([]);
const [macrosErr, setMacrosErr] = React.useState(null);
React.useEffect(async ()=>{
try {
- let {data: respData} = await queryToolCtx.api.get(url_for('sqleditor.get_macros', {
- 'trans_id': queryToolCtx.params.trans_id,
- }));
- /* Copying id to mid to track key id changes */
- setMacrosData(respData.macro.map((m)=>({...m, mid: m.id})));
+ // Fetch user macros data
+ let { data: userMacroRespData } = await queryToolCtx.api.get(url_for('sqleditor.get_user_macros'));
+ setUserMacrosData(userMacroRespData);
+
} catch (error) {
setMacrosErr(error);
}
}, []);
+
+ React.useEffect(async ()=>{
+ try {
+ // Fetch macros data
+ let {data: respData} = await queryToolCtx.api.get(url_for('sqleditor.get_macros', {
+ 'trans_id': queryToolCtx.params.trans_id,
+ }));
+ /* Copying id to mid to track key id changes */
+ setMacrosData(respData.macro.map((m)=>({...m, mid: m.id})));
+
+ } catch (error) {
+ setMacrosErr(error);
+ }
+ }, []);
+
+
const onSaveClick = (_isNew, changeData)=>{
return new Promise((resolve, reject)=>{
const setMacros = async ()=>{
try {
- let changed = getChangedMacros(macrosData, changeData);
+ let changed = getChangedMacros(userMacrosData, changeData);
let {data: respData} = await queryToolCtx.api.put(url_for('sqleditor.set_macros', {
'trans_id': queryToolCtx.params.trans_id,
}), {changed: changed});
resolve();
- onSave(respData.macro?.filter((m)=>Boolean(m.name)));
+ onSave(respData.filter((m) => Boolean(m.name)));
onClose();
} catch (error) {
reject(error);
@@ -159,6 +200,7 @@ export default function MacrosDialog({onClose, onSave}) {
if(keyOptions.length <= 0) {
return <>>;
}
+
return (
Boolean(m.name))});
+ return Promise.resolve({macro: userMacrosData.filter((m)=>Boolean(m.name))});
}}
schema={new MacrosSchema(keyOptions)}
viewHelperProps={{
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 b66143877..62549db9d 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx
@@ -54,7 +54,7 @@ function autoCommitRollback(type, api, transId, value) {
return api.post(url, JSON.stringify(value));
}
-export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
+export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddToMacros}) {
const classes = useStyles();
const eventBus = useContext(QueryToolEventsContext);
const queryToolCtx = useContext(QueryToolContext);
@@ -633,6 +633,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
label={gettext('Macros Menu')}
>
{gettext('Manage macros')}
+ {gettext('Add to macros')}
{queryToolCtx.params?.macros?.map((m)=>{
return (
@@ -656,4 +657,5 @@ MainToolBar.propTypes = {
containerRef: CustomPropTypes.ref,
onFilterClick: PropTypes.func,
onManageMacros: PropTypes.func,
+ onAddToMacros: PropTypes.func
};
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 119186ecc..a857e586e 100644
--- a/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx
@@ -23,6 +23,7 @@ import { usePgAdmin } from '../../../../../../static/js/BrowserComponent';
import ConfirmPromotionContent from '../dialogs/ConfirmPromotionContent';
import usePreferences from '../../../../../../preferences/static/js/store';
import { getTitle } from '../../sqleditor_title';
+import PropTypes from 'prop-types';
const useStyles = makeStyles(()=>({
@@ -61,7 +62,7 @@ async function registerAutocomplete(editor, api, transId) {
});
}
-export default function Query() {
+export default function Query({onTextSelect}) {
const classes = useStyles();
const editor = React.useRef();
const eventBus = useContext(QueryToolEventsContext);
@@ -390,7 +391,6 @@ export default function Query() {
const change = useCallback(()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.QUERY_CHANGED, editor.current.isDirty());
-
if(!queryToolCtx.params.is_query_tool && editor.current.isDirty()){
if(queryToolCtx.preferences.sqleditor.view_edit_promotion_warning){
checkViewEditDataPromotion();
@@ -480,5 +480,11 @@ export default function Query() {
onChange={change}
autocomplete={true}
customKeyMap={shortcutOverrideKeys}
+ onTextSelect={onTextSelect}
/>;
}
+
+
+Query.propTypes = {
+ onTextSelect: PropTypes.func,
+};
\ No newline at end of file
diff --git a/web/pgadmin/tools/sqleditor/tests/test_macros.py b/web/pgadmin/tools/sqleditor/tests/test_macros.py
index d3ad4af19..2872e4330 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_macros.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_macros.py
@@ -32,15 +32,15 @@ class TestMacros(BaseTestGenerator):
operation='set',
data={
'changed': [
- {'id': 1,
+ {'mid': 1,
'name': 'Test Macro 1',
'sql': 'SELECT 1;'
},
- {'id': 2,
+ {'mid': 2,
'name': 'Test Macro 2',
'sql': 'SELECT 2;'
},
- {'id': 3,
+ {'mid': 3,
'name': 'Test Macro 3',
'sql': 'SELECT 3;'
},
@@ -129,10 +129,11 @@ class TestMacros(BaseTestGenerator):
self.assertEqual(response.status_code, 200)
for m in self.data['changed']:
+ if self.operation == 'set':
+ m['id'] = m['mid']
url = '/sqleditor/get_macros/{0}/{1}'.format(m['id'],
self.trans_id)
response = self.tester.get(url)
-
if self.operation == 'clear':
self.assertEqual(response.status_code, 410)
elif self.operation == 'set':
diff --git a/web/pgadmin/tools/sqleditor/utils/macros.py b/web/pgadmin/tools/sqleditor/utils/macros.py
index cf1d89aba..4612e018c 100644
--- a/web/pgadmin/tools/sqleditor/utils/macros.py
+++ b/web/pgadmin/tools/sqleditor/utils/macros.py
@@ -27,7 +27,7 @@ def get_macros(macro_id, json_resp):
:param json_resp: Set True to return json response
"""
if macro_id:
- macro = UserMacros.query.filter_by(mid=macro_id,
+ macro = UserMacros.query.filter_by(id=macro_id,
uid=current_user.id).first()
if macro is None:
return make_json_response(
@@ -37,7 +37,8 @@ def get_macros(macro_id, json_resp):
)
else:
return ajax_response(
- response={'id': macro.mid,
+ response={'id': macro.id,
+ 'mid':macro.mid,
'name': macro.name,
'sql': macro.sql},
status=200
@@ -45,13 +46,12 @@ def get_macros(macro_id, json_resp):
else:
macros = db.session.query(Macros.id, Macros.alt, Macros.control,
Macros.key, Macros.key_code,
- UserMacros.name, UserMacros.sql
- ).outerjoin(
+ UserMacros.name, UserMacros.sql,
+ UserMacros.id).outerjoin(
UserMacros, and_(Macros.id == UserMacros.mid,
UserMacros.uid == current_user.id)).all()
data = []
-
for m in macros:
key_label = 'Ctrl + ' + m[3] if m[2] is True else 'Alt + ' + m[3]
data.append({'id': m[0], 'alt': m[1],
@@ -74,7 +74,8 @@ def get_user_macros():
This method is used to get all the user macros.
"""
- macros = db.session.query(UserMacros.name,
+ macros = db.session.query(UserMacros.id,
+ UserMacros.name,
Macros.id,
Macros.alt, Macros.control,
Macros.key, Macros.key_code,
@@ -86,11 +87,17 @@ def get_user_macros():
data = []
for m in macros:
- key_label = 'Ctrl + ' + m[4] if m[3] is True else 'Alt + ' + m[4]
- data.append({'name': m[0], 'id': m[1], 'key': m[4],
- 'key_label': key_label, 'alt': 1 if m[2] else 0,
- 'control': 1 if m[3] else 0, 'key_code': m[5],
- 'sql': m[6]})
+ key_label = (
+ 'Ctrl + ' + str(m[5])
+ if m[4] is True
+ else 'Alt + ' + str(m[5])
+ if m[5] is not None
+ else ''
+ )
+ data.append({'id': m[0], 'name': m[1], 'mid': m[2], 'key': m[5],
+ 'key_label': key_label, 'alt': 1 if m[3] else 0,
+ 'control': 1 if m[4] else 0, 'key_code': m[6],
+ 'sql': m[7]})
return data
@@ -111,21 +118,21 @@ def set_macros():
)
for m in data['changed']:
- if m['id']:
+ if m.get('id'):
macro = UserMacros.query.filter_by(
uid=current_user.id,
- mid=m['id']).first()
+ id=m['id']).first()
if macro:
status, msg = update_macro(m, macro)
- else:
- status, msg = create_macro(m)
+ else:
+ status, msg = create_macro(m)
if not status:
return make_json_response(
status=410, success=0, errormsg=msg
)
- return get_macros(None, True)
+ return get_user_macros()
def create_macro(macro):
@@ -146,7 +153,7 @@ def create_macro(macro):
try:
new_macro = UserMacros(
uid=current_user.id,
- mid=macro['id'],
+ mid=macro['mid'] if macro.get('mid') else None,
name=macro['name'],
sql=macro['sql']
)
@@ -168,6 +175,7 @@ def update_macro(data, macro):
name = data.get('name', None)
sql = data.get('sql', None)
+ mid = data.get('mid', None)
if (name or sql) and macro.sql and 'name' in data and name is None:
return False, gettext(
@@ -177,11 +185,12 @@ def update_macro(data, macro):
"Could not find the required parameter (sql).")
try:
- if name or sql:
+ if name or sql or mid:
if name:
macro.name = name
if sql:
macro.sql = sql
+ macro.mid = mid if mid != 0 else None
else:
db.session.delete(macro)