Fix an issue where debugger not showing all arguments anymore after hitting SQL error while debugging. Fixes #5101

Added a "Clear All" button to the argument dialog which will clear all the saved arguments values from SQLite DB.
This commit is contained in:
Aditya Toshniwal 2020-02-28 15:27:01 +05:30 committed by Akshay Joshi
parent c9d04684ce
commit 4db0a6524d
7 changed files with 195 additions and 14 deletions

View File

@ -81,6 +81,8 @@ Use the fields on the *Debugger* dialog to provide a value for each parameter:
Provide values required by the program, and click the *Debug* button to start
stepping through the program.
The values of the arguments provided here are saved. The values will be pre-filled
next time the dialog opens. To clear the values, use the *Clear All* button.
.. image:: images/debug_step_in.png
:alt: Debugger step-in demo

BIN
docs/en_US/images/debug_params.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -28,6 +28,7 @@ Bug fixes
| `Issue #4996 <https://redmine.postgresql.org/issues/4996>`_ - Improve the style of the highlighted code after query execution for Dark mode.
| `Issue #5058 <https://redmine.postgresql.org/issues/5058>`_ - Ensure that AlertifyJS should not be visible as a title for alert dialog.
| `Issue #5077 <https://redmine.postgresql.org/issues/5077>`_ - Changed background pattern for geometry viewer to use #fff for all themes.
| `Issue #5101 <https://redmine.postgresql.org/issues/5101>`_ - Fix an issue where debugger not showing all arguments anymore after hitting SQL error while debugging.
| `Issue #5107 <https://redmine.postgresql.org/issues/5107>`_ - Set proper focus on tab navigation for file manager dialog.
| `Issue #5115 <https://redmine.postgresql.org/issues/5115>`_ - Fix an issue where command and statements were parsed incorrectly for Rules.
| `Issue #5142 <https://redmine.postgresql.org/issues/5142>`_ - Ensure that all the transactions should be canceled before closing the connections when a server is disconnected using pgAdmin.

View File

@ -1916,6 +1916,58 @@ define([
},
});
Backgrid.BooleanCell = Backgrid.BooleanCell.extend({
className: 'boolean-cell',
enterEditMode: function() {
this.$el.addClass('editor');
$(this.$el.find('input[type=checkbox]')).trigger('focus');
},
exitEditMode: function() {
this.$el.removeClass('editor');
},
events: {
'change input': 'onChange',
'blur input': 'exitEditMode',
'keydown': 'onKeyDown',
},
onChange: function(e) {
var model = this.model,
column = this.column,
val = this.formatter.toRaw(this.$input.prop('checked'), model);
this.enterEditMode();
// on bootstrap change we also need to change model's value
model.set(column.get('name'), val);
model.trigger('backgrid:edited', model, column, new Backgrid.Command(e));
},
render: function () {
this.$el.empty();
var model = this.model, column = this.column;
var editable = Backgrid.callByNeed(column.editable(), column, model);
var align_center = column.get('align_center') || false;
let checked = this.formatter.fromRaw(model.get(column.get('name')), model);
let id = `column.get('name')_${_.uniqueId()}`;
this.$el.empty();
this.$el.append(
$(`<div class="custom-control custom-checkbox custom-checkbox-no-label ${align_center?'text-center':''}">
<input tabindex="0" type="checkbox" class="custom-control-input" id="${id}" ${!editable?'disabled':''} ${checked?'checked':''}/>
<label class="custom-control-label" for="${id}">
<span class="sr-only">Select<span>
</label>
</div>`)
);
this.$input = this.$el.find('input');
this.delegateEvents();
return this;
},
});
return Backgrid;
});

View File

@ -241,7 +241,7 @@ class DebuggerModule(PgAdminModule):
'debugger.start_execution', 'debugger.set_breakpoint',
'debugger.clear_all_breakpoint', 'debugger.deposit_value',
'debugger.select_frame', 'debugger.get_arguments',
'debugger.set_arguments',
'debugger.set_arguments', 'debugger.clear_arguments',
'debugger.poll_end_execution_result', 'debugger.poll_result'
]
@ -1797,9 +1797,10 @@ def set_arguments_sqlite(sid, did, scid, func_id):
db.session.add(debugger_func_args)
db.session.commit()
db.session.commit()
except Exception as e:
db.session.rollback()
current_app.logger.exception(e)
return make_json_response(
status=410,
@ -1810,6 +1811,51 @@ def set_arguments_sqlite(sid, did, scid, func_id):
return make_json_response(data={'status': True, 'result': 'Success'})
@blueprint.route(
'/clear_arguments/<int:sid>/<int:did>/<int:scid>/<int:func_id>',
methods=['POST'], endpoint='clear_arguments'
)
@login_required
def clear_arguments_sqlite(sid, did, scid, func_id):
"""
clear_arguments_sqlite(sid, did, scid, func_id)
This method is responsible for clearing function arguments
from sqlite database
Parameters:
sid
- Server Id
did
- Database Id
scid
- Schema Id
func_id
- Function Id
"""
try:
db.session.query(DebuggerFunctionArguments) \
.filter(DebuggerFunctionArguments.server_id == sid,
DebuggerFunctionArguments.database_id == did,
DebuggerFunctionArguments.schema_id == scid,
DebuggerFunctionArguments.function_id == func_id) \
.delete()
db.session.commit()
except Exception as e:
db.session.rollback()
current_app.logger.exception(e)
return make_json_response(
status=410,
success=0,
errormsg=str(e)
)
return make_json_response(data={'status': True, 'result': 'Success'})
def convert_data_to_dict(conn, result):
"""
This function helps us to convert result set into dict

View File

@ -69,7 +69,7 @@ define([
// As we are getting this value as text from sqlite database so we need to type cast it.
if (model.get('value') != undefined) {
model.set({
'value': parseInt(model.get('value')),
'value': isNaN(parseInt(model.get('value'))) ? null : parseInt(model.get('value')),
}, {
silent: true,
});
@ -176,6 +176,7 @@ define([
this.set('debug_info', debug_info);
this.set('restart_debug', restart_debug);
this.set('trans_id', trans_id);
this.set('is_edb_proc', is_edb_proc);
// Variables to store the data sent from sqlite database
var func_args_data = this.func_args_data = [];
@ -289,6 +290,7 @@ define([
label: gettext('Null?'),
type: 'boolean',
cell: 'boolean',
align_center: true,
},
{
name: 'expr',
@ -296,6 +298,7 @@ define([
type: 'boolean',
cellFunction: cellExprControlFunction,
editable: disableExpressionControl,
align_center: true,
},
{
name: 'value',
@ -304,6 +307,7 @@ define([
editable: true,
cellFunction: cellFunction,
headerCell: value_header,
align_center: true,
},
{
name: 'use_default',
@ -413,12 +417,12 @@ define([
// Need to update the func_obj variable from sqlite database if available
if (func_args_data.length != 0) {
for (i = 0; i < func_args_data.length; i++) {
index = func_args_data[i]['arg_id'];
if (debug_info['proargmodes'] != null &&
(argmode[i] == 'o' && !is_edb_proc)) {
(argmode[index] == 'o' && !is_edb_proc)) {
continue;
}
index = func_args_data[i]['arg_id'];
values = [];
if (argtype[index].indexOf('[]') != -1) {
vals = func_args_data[i]['value'].split(',');
@ -565,14 +569,17 @@ define([
});
grid.render();
$(this.elements.content).html(grid.el);
let wrap_div = document.createElement('div');
wrap_div.classList.add('debugger-args');
wrap_div.appendChild(grid.el);
$(this.elements.content).html(wrap_div);
// For keyboard navigation in the grid
// we'll set focus on checkbox from the first row if any
var grid_checkbox = $(grid.el).find('input:checkbox').first();
if (grid_checkbox.length) {
setTimeout(function() {
grid_checkbox.trigger('click');
grid_checkbox.trigger('focus');
}, 250);
}
@ -585,6 +592,9 @@ define([
setup: function() {
return {
buttons: [{
text: gettext('Clear All'),
className: 'btn btn-secondary pull-left fa fa-eraser pg-alertify-button',
},{
text: gettext('Cancel'),
key: 27,
className: 'btn btn-secondary fa fa-times pg-alertify-button',
@ -899,6 +909,69 @@ define([
return false;
}
if (e.button.text === gettext('Clear All')) {
let self = this;
let baseUrl = null;
if (self.setting('restart_debug') == 0) {
let selected_item = pgBrowser.tree.selected();
let item_data = pgBrowser.tree.itemData(selected_item);
if (!item_data)
return;
let node = pgBrowser.Nodes[item_data._type];
let treeInfo = node.getTreeNodeHierarchy.call(node, selected_item);
let f_id;
if (item_data._type == 'function') {
f_id = item_data._id;
} else if (item_data._type == 'procedure') {
f_id = item_data._id;
} else if (item_data._type == 'edbfunc') {
f_id = item_data._id;
} else if (item_data._type == 'edbproc') {
f_id = item_data._id;
}
baseUrl = url_for('debugger.clear_arguments', {
'sid': treeInfo.server._id,
'did': treeInfo.database._id,
'scid': treeInfo.schema._id,
'func_id': f_id,
});
} else {
baseUrl = url_for('debugger.clear_arguments', {
'sid': self.setting('debug_info').server_id,
'did': self.setting('debug_info').database_id,
'scid': self.setting('debug_info').schema_id,
'func_id': self.setting('debug_info').function_id,
});
}
$.ajax({
url: baseUrl,
method: 'POST',
data: {
'data': JSON.stringify(args_value_list),
},
}).done(function() {
/* Disable debug button */
self.__internal.buttons[2].element.disabled = true;
self.main(self.setting('title'), self.setting('debug_info'),
self.setting('restart_debug'), self.setting('is_edb_proc'),
self.setting('trans_id')
);
self.prepare();
}).fail(function(e) {
Alertify.alert(
gettext('Clear failed'),
e.responseJSON.errormsg
);
});
e.cancel = true;
return true;
}
},
build: function() {
Alertify.pgDialogBuild.apply(this);
@ -914,9 +987,9 @@ define([
enable the debug button otherwise disable the debug button.
*/
if (this.func_args_data.length == 0) {
this.__internal.buttons[1].element.disabled = true;
this.__internal.buttons[2].element.disabled = true;
} else {
this.__internal.buttons[1].element.disabled = false;
this.__internal.buttons[2].element.disabled = false;
}
/*
@ -933,7 +1006,7 @@ define([
for (var i = 0; i < this.collection.length; i++) {
if (this.collection.models[i].get('is_null')) {
obj.__internal.buttons[1].element.disabled = false;
obj.__internal.buttons[2].element.disabled = false;
enable_btn = true;
continue;
}
@ -944,15 +1017,15 @@ define([
enable_btn = true;
if (this.collection.models[i].get('use_default')) {
obj.__internal.buttons[1].element.disabled = false;
obj.__internal.buttons[2].element.disabled = false;
} else {
obj.__internal.buttons[1].element.disabled = true;
obj.__internal.buttons[2].element.disabled = true;
break;
}
}
}
if (!enable_btn)
obj.__internal.buttons[1].element.disabled = false;
obj.__internal.buttons[2].element.disabled = false;
};
})(this)
);
@ -960,7 +1033,7 @@ define([
this.grid.listenTo(this.debuggerInputArgsColl, 'backgrid:error',
(function(obj) {
return function() {
obj.__internal.buttons[1].element.disabled = true;
obj.__internal.buttons[2].element.disabled = true;
};
})(this)
);

View File

@ -16,3 +16,10 @@
-ms-user-select: text;
user-select: text;
}
.debugger-args {
display: block;
width: 100%;
height: 100%;
overflow: auto;
}