public inbox for [email protected]
help / color / mirror / Atom feed[pgAdmin4][Debugger]: Initial Patch
15+ messages / 3 participants
[nested] [flat]
* [pgAdmin4][Debugger]: Initial Patch
@ 2016-04-05 11:40 Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-05 11:40 UTC (permalink / raw)
To: pgadmin-hackers
Hi,
Please find attached debugger v1 patch. To test this patch we need to apply
the "function" module patch submitted by Khushboo.
For the debugger functionality, direct and indirect debugging is working as
per pgadmin3 except below TODOs.
Below are the TODOs which will be submitted in next patch along with the
fixed review comments.
- For the direct debugging, 'NULL' and 'Expression' column value should
be validated before pressing the debug button.
- User can deposit the value of input arguments and local variable but
need to be
validated against respective data types.
- Currently we have tested for plpgsql functions only, We need to test
against EDB SPL functions and trigger functions.
- For the direct debugging, once the execution is completed, only
"Continue/Restart" button will be enabled and user should allow to restart
the debugging with same function and previous values.
- Values entered by the user in input dialog during direct debugging
should be saved.
Currently we have tested this patch with PostgreSQL v9.5.
Do review it and let us know for the comments.
Thanks,
Neel Patel
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] debugger_5_April_v1.patch (169.0K, 3-debugger_5_April_v1.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/static/js/frame.js b/web/pgadmin/browser/static/js/frame.js
index 92ea5bf..193a01d 100644
--- a/web/pgadmin/browser/static/js/frame.js
+++ b/web/pgadmin/browser/static/js/frame.js
@@ -29,7 +29,8 @@ function(_, pgAdmin) {
title: that.title,
isPrivate: that.isPrivate,
onCreate: function(myPanel) {
- $(myPanel).data('pgAdminName', that.name);
+ $(myPanel).data('frameInitialized', false);
+ $(myPanel).data('pgFrameName', that.name);
myPanel.initSize(that.width, that.height);
if (myPanel.showTitle == false)
myPanle.title(false);
@@ -41,7 +42,11 @@ function(_, pgAdmin) {
var frame = new wcIFrame($frameArea, myPanel);
$(myPanel).data('embededFrame', frame);
- setTimeout(function() { frame.openURL(that.url); }, 500);
+ setTimeout(function() {
+ frame.openURL(that.url);
+
+ pgBrowser.Events.trigger('pgadmin-browser:frame:urlloaded:' + that.name, frame, that.url, self);
+ }, 1);
if (that.events && _.isObject(that.events)) {
_.each(that.events, function(v, k) {
diff --git a/web/pgadmin/static/css/overrides.css b/web/pgadmin/static/css/overrides.css
index ca87e7a..cfddaa3 100755
--- a/web/pgadmin/static/css/overrides.css
+++ b/web/pgadmin/static/css/overrides.css
@@ -911,4 +911,12 @@ ul.nav.nav-tabs {
.select2-cell .select2-container > .backgrid th, .backgrid td
{
padding: 0px;
-}
\ No newline at end of file
+}
+
+.breakpoints {
+ width: 0.8em;
+}
+
+.CodeMirror-activeline-background {
+ background: #50B0F0;
+}
diff --git a/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js b/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
index 9965f1b..611e54e 100644
--- a/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
+++ b/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
@@ -576,6 +576,236 @@
editor: TextareaCellEditor
});
+
+ /**
+ * Custom header icon cell to add the icon in table header.
+ */
+ var CustomHeaderIconCell = Backgrid.Extension.CustomHeaderIconCell = Backgrid.HeaderCell.extend({
+ /** @property */
+ className: "header-icon-cell",
+ events: {
+ "click": "addHeaderIcon"
+ },
+ addHeaderIcon: function (e) {
+ self = this;
+ m = new (this.collection.model);
+ this.collection.add(m)
+ e.preventDefault();
+ },
+ render: function () {
+ this.$el.empty();
+ //this.$el.html("<i class='fa fa-plus-circle'></i>");
+ this.$el.html("<label><a><span style='font-weight:normal;'>Array Values</a></span></label> <button class='btn-sm btn-default add'>ADD</button>");
+ this.delegateEvents();
+ return this;
+ }
+ });
+
+
+ var arrayCellModel = Backbone.Model.extend({
+ defaults: {
+ value: undefined
+ }
+ });
+
+ /**
+ Custom InputArrayCellEditor for editing user input array for debugger.
+ */
+ var InputArrayCellEditor = Backgrid.Extension.InputArrayCellEditor =
+ Backgrid.CellEditor.extend({
+ tagName: "div",
+
+ events: {
+ 'blur': 'lostFocus'
+ },
+
+ render: function () {
+ var self = this,
+ arrayValuesCol = this.model.get(this.column.get("name")),
+ tbl = $("<table></table>").appendTo(this.$el),
+ gridCols = [
+ {name: 'value', label:'Array Values', type: 'text', cell:'string', headerCell: Backgrid.Extension.CustomHeaderIconCell, cellHeaderClasses: 'width_percent_100'},
+ ],
+ gridBody = $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>");
+
+ this.$el.attr('tabindex', '1');
+
+ gridCols.unshift({
+ name: "pg-backform-delete", label: "",
+ cell: Backgrid.Extension.DeleteCell,
+ //headerCell: Backgrid.Extension.CustomHeaderIconCell,
+ editable: false, cell_priority: -1
+ });
+
+ this.$el.empty();
+ var grid = self.grid = new Backgrid.Grid({
+ columns: gridCols,
+ collection:arrayValuesCol
+ });
+
+ grid.render();
+
+ gridBody.append(grid.$el)
+
+ this.$el.html(gridBody);
+
+ $(self.$el).pgMakeVisible('backform-tab');
+ self.delegateEvents();
+
+ return this;
+ },
+
+ /*
+ * Call back function when the grid lost the focus
+ */
+ lostFocus: function(ev) {
+
+ var self = this,
+ /*
+ * Function to determine whether one dom element is descendant of another
+ * dom element.
+ */
+ isDescendant = function (parent, child) {
+ var node = child.parentNode;
+ while (node != null) {
+ if (node == parent) {
+ return true;
+ }
+ node = node.parentNode;
+ }
+ return false;
+ }
+ /*
+ * Between leaving the old element focus and entering the new element focus the
+ * active element is the document/body itself so add timeout to get the proper
+ * focused active element.
+ */
+ setTimeout(function() {
+ if (self.$el[0] != document.activeElement && !isDescendant(self.$el[0], document.activeElement)){
+ var m = self.model,
+ column = self.column;
+ m.trigger('backgrid:edited', m, column, new Backgrid.Command(ev));
+
+ setTimeout(function(){
+ if (self.grid) {
+ self.grid.remove();
+ self.grid = null;
+ }
+ }, 10);
+
+
+ }},10);
+ return;
+ }
+ });
+
+ /*
+ * This will help us transform the user input string array values in proper format to be
+ * displayed in the cell.
+ */
+ var InputStringArrayCellFormatter = Backgrid.Extension.InputStringArrayCellFormatter =
+ function () {};
+ _.extend(InputStringArrayCellFormatter.prototype, {
+ /**
+ * Takes a raw value from a model and returns an optionally formatted
+ * string for display.
+ */
+ fromRaw: function (rawData, model) {
+ var values = []
+ rawData.each(function(m){
+ var val = m.get('value');
+ if (_.isUndefined(val)) {
+ values.push("null");
+ } else {
+ values.push(m.get("value"));
+ }
+ }
+ );
+ return values.toString();
+ },
+ toRaw: function (formattedData, model) {
+ return formattedData;
+ }
+ });
+
+ /*
+ * This will help us transform the user input integer array values in proper format to be
+ * displayed in the cell.
+ */
+ var InputIntegerArrayCellFormatter = Backgrid.Extension.InputIntegerArrayCellFormatter =
+ function () {};
+ _.extend(InputIntegerArrayCellFormatter.prototype, {
+ /**
+ * Takes a raw value from a model and returns an optionally formatted
+ * string for display.
+ */
+ fromRaw: function (rawData, model) {
+ var values = []
+ rawData.each(function(m){
+ var val = m.get('value');
+ if (_.isUndefined(val)) {
+ values.push("null");
+ } else {
+ values.push(m.get("value"));
+ }
+ }
+ );
+ return values.toString();
+ },
+ toRaw: function (formattedData, model) {
+ formattedData.each(function(m){
+ m.set("value", parseInt(m.get('value')), {silent: true});
+ });
+
+ return formattedData;
+ }
+ });
+
+ /*
+ * InputStringArrayCell for rendering and taking input for string array type in debugger
+ */
+ var InputStringArrayCell = Backgrid.Extension.InputStringArrayCell = Backgrid.Cell.extend({
+ className: "width_percent_25",
+ formatter: InputStringArrayCellFormatter,
+ editor: InputArrayCellEditor,
+
+ initialize: function() {
+ Backgrid.Cell.prototype.initialize.apply(this, arguments);
+ // set value to empty array.
+ if (_.isUndefined(this.collection)) {
+ this.collection = new (Backbone.Collection.extend({
+ model: arrayCellModel}));
+ }
+
+ this.model.set(this.column.get('name'), this.collection);
+
+ this.listenTo(this.collection, "remove", this.render);
+ },
+ });
+
+ /*
+ * InputIntegerArrayCell for rendering and taking input for integer array type in debugger
+ */
+ var InputIntegerArrayCell = Backgrid.Extension.InputIntegerArrayCell = Backgrid.Cell.extend({
+ className: "width_percent_25",
+ formatter: InputIntegerArrayCellFormatter,
+ editor: InputArrayCellEditor,
+
+ initialize: function() {
+ Backgrid.Cell.prototype.initialize.apply(this, arguments);
+ // set value to empty array.
+ if (_.isUndefined(this.collection)) {
+ this.collection = new (Backbone.Collection.extend({
+ model: arrayCellModel}));
+ }
+
+
+ this.model.set(this.column.get('name'),this.collection);
+
+ this.listenTo(this.collection, "remove", this.render);
+ },
+ });
+
return Backgrid;
}));
diff --git a/web/pgadmin/static/js/codemirror/addon/selection/active-line.js b/web/pgadmin/static/js/codemirror/addon/selection/active-line.js
index 22da2e0..421cef5 100644
--- a/web/pgadmin/static/js/codemirror/addon/selection/active-line.js
+++ b/web/pgadmin/static/js/codemirror/addon/selection/active-line.js
@@ -9,9 +9,9 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"));
+ mod(require("codemirror"));
else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror"], mod);
+ define(["codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py
new file mode 100644
index 0000000..0bd9904
--- /dev/null
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -0,0 +1,1088 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""A blueprint module implementing the debugger"""
+
+MODULE_NAME = 'debugger'
+
+import json
+import random
+from flask import url_for, Response, render_template, request, make_response, jsonify, session
+from pgadmin.utils import PgAdminModule
+from pgadmin.utils.ajax import bad_request
+from flask.ext.babel import gettext
+from flask.ext.security import login_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from pgadmin.utils.ajax import make_json_response, \
+ make_response as ajax_response, internal_server_error
+from pgadmin.utils.menu import Panel
+
+# Constants
+ASYNC_OK = 1
+
+
+class DebuggerModule(PgAdminModule):
+ """
+ class DebuggerModule(PgAdminModule)
+
+ A module class for debugger which is derived from PgAdminModule.
+
+ Methods:
+ -------
+ * get_own_javascripts(self)
+ - Method is used to load the required javascript files for debugger module
+
+ """
+
+ def get_own_javascripts(self):
+ scripts = list()
+ for name, script in [
+ ['pgadmin.tools.debugger.controller', 'js/debugger'],
+ ['pgadmin.tools.debugger.ui', 'js/debugger_ui'],
+ ['pgadmin.tools.debugger.direct', 'js/direct']
+ ]:
+ scripts.append({
+ 'name': name,
+ 'path': url_for('debugger.index') + script,
+ 'when': None
+ })
+ scripts.append({
+ 'name': 'codemirror.activeline',
+ 'path': url_for('static', filename='js/codemirror/addon/selection/active-line'),
+ 'deps': ['codemirror'],
+ 'exports': 'CodeMirror.addon.selection.active-line',
+ 'preloaded': True
+ })
+
+ return scripts
+
+blueprint = DebuggerModule(MODULE_NAME, __name__)
+
+
[email protected]("/")
+@login_required
+def index():
+ return bad_request(errormsg=gettext("This URL can not be called directly!"))
+
+
[email protected]("/js/debugger.js")
+@login_required
+def script():
+ """render the main debugger javascript file"""
+ return Response(response=render_template("debugger/js/debugger.js", _=gettext),
+ status=200,
+ mimetype="application/javascript")
+
+
[email protected]("/js/debugger_ui.js")
+@login_required
+def script_debugger_js():
+ """render the debugger UI javascript file"""
+ return Response(response=render_template("debugger/js/debugger_ui.js", _=gettext),
+ status=200,
+ mimetype="application/javascript")
+
+
[email protected]("/js/direct.js")
+@login_required
+def script_debugger_direct_js():
+ """render the javascript file required send and receive the response from server for debugging"""
+ return Response(response=render_template("debugger/js/direct.js", _=gettext),
+ status=200,
+ mimetype="application/javascript")
+
+
[email protected]('/init/function/<int:sid>/<int:did>/<int:scid>/<int:fid>', methods=['GET'])
+@login_required
+def init_function(sid, did, scid, fid):
+ """
+ init_function(sid, did, scid, fid)
+
+ This method is responsible to initialize the function required for debugging.
+ This method is also responsible for storing the all functions data to session variable.
+ This is only required for direct debugging. As Indirect debugging does not require these data because user will
+ provide all the arguments and other functions information through another session to invoke the target.
+
+ Parameters:
+ sid
+ - Server Id
+ did
+ - Database Id
+ scid
+ - Schema Id
+ fid
+ - Function Id
+ """
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection(did=did)
+
+ # Get the server version, server type and user information
+ ver = manager.version
+ server_type = manager.server_type
+ user = manager.user_info
+
+ # Check server type is ppas or not
+ ppas_server = False
+ if server_type == 'ppas':
+ ppas_server = True;
+
+ # Set the template path required to read the sql files
+ template_path = 'debugger/sql'
+
+ sql = render_template("/".join([template_path, 'get_function_debug_info.sql']), is_ppas_database=ppas_server, hasFeatureFunctionDefaults= True, fid=fid)
+ status, r_set = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=r_set)
+
+ ret_status = status
+
+ # Check the condition that function is actually debuggable or not....
+ if r_set['rows'][0]:
+ # Function with a colon in the name cannot be debugged.
+ # If this is an EDB wrapped function, no debugging allowed
+ # Function with return type "trigger" can not be debugged.
+ if ":" in r_set['rows'][0]['name']:
+ ret_status = False
+ elif r_set['rows'][0]['rettype'] == 'trigger':
+ ret_status = False
+ elif ppas_server and r_set['rows'][0]['prosrc'].lstrip().startswith('$__EDBwrapped__$'):
+ ret_status = False
+ else:
+ # If user is super user then we should check debugger library is loaded or not
+ if user['is_superuser']:
+ status_in, rid_pre = conn.execute_scalar("SHOW shared_preload_libraries")
+ if not status_in:
+ return internal_server_error("ERROR: Couldn't fetch debugger plugin information")
+
+ # Need to check if plugin is really loaded or not with "plugin_debugger" string
+ if rid_pre:
+ if "plugin_debugger" not in rid_pre:
+ ret_status = False
+
+ status_in, rid_tar = conn.execute_scalar(" SELECT count(*) FROM pg_proc WHERE proname = 'pldbg_get_target_info' ")
+ if not status_in:
+ return internal_server_error("ERROR: Couldn't fetch debugger target information")
+
+ if rid_tar == 0:
+ ret_status = False
+ else:
+ ret_status = False
+
+ # Return the response that function can not be debug...
+ if not ret_status:
+ return internal_server_error("ERROR: Function can not be debug.")
+
+ # Store the function information in session variable
+ if 'functionData' not in session:
+ function_data = dict()
+ else:
+ function_data = session['functionData']
+
+ function_data = {
+ 'oid': fid,
+ 'name': r_set['rows'][0]['name'],
+ 'is_func': r_set['rows'][0]['isfunc'],
+ 'is_callable': False,
+ 'schema': r_set['rows'][0]['schemaname'],
+ 'language': r_set['rows'][0]['lanname'],
+ 'return_type': r_set['rows'][0]['rettype'],
+ 'args_type': r_set['rows'][0]['proargtypenames'],
+ 'args_name': r_set['rows'][0]['proargnames'],
+ 'arg_mode': r_set['rows'][0]['proargmodes'],
+ 'args_value': ''
+ }
+
+ session['functionData'] = function_data;
+
+ data = {}
+ data['name'] = r_set['rows'][0]['proargnames']
+ data['type'] = r_set['rows'][0]['proargtypenames']
+ data['use_default'] = r_set['rows'][0]['pronargdefaults']
+ data['default_value'] = r_set['rows'][0]['proargdefaults']
+ data['require_input'] = True
+
+ # Below will check do we really required for the user input arguments and show input dialog
+ if not r_set['rows'][0]['proargtypenames']:
+ data['require_input'] = False
+ else:
+ if r_set['rows'][0]['pkg'] != 0 and r_set['rows'][0]['pkgconsoid'] != 0:
+ data['require_input'] = True
+
+ if r_set['rows'][0]['proargmodes']:
+ pro_arg_modes = r_set['rows'][0]['proargmodes'].split(",");
+ for pr_arg_mode in pro_arg_modes:
+ if pr_arg_mode == 'o' or pr_arg_mode == 't':
+ data['require_input'] = False
+ continue;
+ else:
+ data['require_input'] = True
+ break;
+
+ r_set['rows'][0]['require_input'] = data['require_input']
+
+ return make_json_response(
+ data=r_set['rows'],
+ status=200
+ )
+
+
[email protected]('/direct/<int:trans_id>', methods=['GET'])
+@login_required
+def direct_new(trans_id):
+ debugger_data = session['debuggerData']
+ # Return from the function if transaction id not found
+ if str(trans_id) not in debugger_data:
+ return make_json_response(data={'status': True})
+
+ obj = debugger_data[str(trans_id)]
+
+ # if indirect debugging pass value 0 to client and for direct debugging pass it to 1
+ debug_type = 0 if obj['debug_type'] == 'indirect' else 1
+
+ return render_template(
+ "debugger/direct.html",
+ _=gettext,
+ function_name='test',
+ uniqueId=trans_id,
+ debug_type=debug_type,
+ stylesheets=[url_for('debugger.static', filename='css/debugger.css')]
+ )
+
+
[email protected]('/initialize_target/<debug_type>/<int:sid>/<int:did>/<int:scid>/<int:func_id>', methods=['GET', 'POST'])
+@login_required
+def initialize_target(debug_type, sid, did, scid, func_id):
+ """
+ initialize_target(debug_type, sid, did, scid, func_id)
+
+ This method is responsible for creating an asynchronous connection.
+ It will also create a unique transaction id and store the information
+ into session variable.
+
+ Parameters:
+ debug_type
+ - Type of debugging (Direct or Indirect)
+ sid
+ - Server Id
+ did
+ - Database Id
+ scid
+ - Schema Id
+ func_id
+ - Function Id
+ """
+
+ # Create asynchronous connection using random connection id.
+ conn_id = str(random.randint(1, 9999999))
+ try:
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection(did=did, conn_id=conn_id)
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ # Connect the Server
+ status, msg = conn.connect()
+ if not status:
+ return internal_server_error(errormsg=str(msg))
+
+ # Create a unique id for the transaction
+ trans_id = str(random.randint(1, 9999999))
+
+ # If there is no debugging information in session variable then create the store that information
+ if 'debuggerData' not in session:
+ debugger_data = dict()
+ else:
+ debugger_data = session['debuggerData']
+
+ status = True;
+
+ # Find out the debugger version and store it in session variables
+ status, rid = conn.execute_scalar(
+ "SELECT COUNT(*) FROM pg_catalog.pg_proc p \
+ LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid \
+ WHERE n.nspname = ANY(current_schemas(false)) AND p.proname = 'pldbg_get_proxy_info';"
+ )
+
+ if not status:
+ return internal_server_error(errormsg=rid)
+ else:
+ if rid == 0:
+ debugger_version = 1
+ else:
+ status, rid = conn.execute_scalar(
+ "SELECT proxyapiver FROM pldbg_get_proxy_info();"
+ )
+ if rid == 2 or rid == 3:
+ debugger_version = rid;
+ else:
+ status = False;
+
+ # We need to pass the value entered by the user in dialog for direct debugging
+ # Here we get the value in case of direct debugging so update the session variables accordingly
+ # For indirect debugging user will provide the data from another session so below condition will be be required
+ if request.method == 'POST':
+ data = json.loads(request.values['data'])
+ if data:
+ d = session['functionData']
+ d['args_value'] = data
+ session['functionData'] = d
+
+ # Update the debugger data session variable
+ # Here frame_id is required when user debug the multilevel function. When user select the frame from client we
+ # need to update the frame here and set the breakpoint information on that function oid
+ debugger_data[str(trans_id)] = {
+ 'conn_id': conn_id,
+ 'server_id': sid,
+ 'database_id': did,
+ 'function_id': func_id,
+ 'debug_type': debug_type,
+ 'debugger_version': debugger_version,
+ 'frame_id': 0
+ }
+
+ # Store the grid dictionary into the session variable
+ session['debuggerData'] = debugger_data
+
+ return make_json_response(data={'status': status, 'debuggerTransId': trans_id})
+
+
[email protected]('/close/<int:trans_id>', methods=["GET"])
+def close(trans_id):
+ """
+ close(trans_id)
+
+ This method is used to close the asynchronous connection
+ and remove the information of unique transaction id from
+ the session variable.
+
+ Parameters:
+ trans_id
+ - unique transaction id.
+ """
+ # As debugger data is not in session that means we have already deleted and close the target
+ if 'debuggerData' not in session:
+ return make_json_response(data={'status': True})
+
+ debugger_data = session['debuggerData']
+ # Return from the function if transaction id not found
+ if str(trans_id) not in debugger_data:
+ return make_json_response(data={'status': True})
+
+ obj = debugger_data[str(trans_id)]
+ try:
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ # Release the connection
+ if conn.connected():
+ if obj['debug_type'] == 'indirect':
+ # render the SQL template and send the query to server
+ sql = render_template("/".join([template_path, 'abort_target.sql']),
+ session_id=obj['session_id'])
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Delete the existing debugger data in session variable
+ del debugger_data[str(trans_id)]
+ del session['debuggerData'][str(trans_id)]
+
+ # Release the connection acquired during the debugging
+ manager.release(did=obj['database_id'], conn_id=obj['conn_id'])
+ return make_json_response(data={'status': True})
+ else:
+ return make_json_response(data={'status': False})
+
+
[email protected]('/start_listener/<int:trans_id>', methods=['GET'])
+@login_required
+def start_debugger_listener(trans_id):
+ """
+ start_debugger_listener(trans_id)
+
+ This method is responsible to listen and get the required information requested by user during debugging
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+
+ ver = manager.version
+ server_type = manager.server_type
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ if conn.connected():
+
+ # For the direct debugging extract the function arguments values from user and pass to jinja template
+ # to create the query for execution.
+ if obj['debug_type'] == 'direct':
+ str_query = ''
+
+ # Form the function name with schema name
+ func_name = session['functionData']['schema'] + '.' + session['functionData']['name']
+
+ # render the SQL template and send the query to server
+ sql = render_template("/".join([template_path, 'debug_plpgsql_execute_target.sql']),
+ function_oid=obj['function_id'])
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ if session['functionData']['arg_mode']:
+ # In EDBAS 90, if an SPL-function has both an OUT-parameter
+ # and a return value (which is not possible on PostgreSQL otherwise),
+ # the return value is transformed into an extra OUT-parameter
+ # named "_retval_"
+ if session['functionData']['args_name']:
+ arg_name = session['functionData']['args_name'].split(",");
+ if '_retval_' in arg_name:
+ arg_mode = session['functionData']['arg_mode'].split(",");
+ arg_mode.pop();
+ else:
+ arg_mode = session['functionData']['arg_mode'].split(",");
+ else:
+ arg_mode = session['functionData']['arg_mode'].split(",");
+ else:
+ arg_mode = ['i'] * len(session['functionData']['args_type'].split(","));
+
+ if session['functionData']['args_type']:
+ if session['functionData']['args_name']:
+ arg_name = session['functionData']['args_name'].split(",");
+ if '_retval_' in arg_name:
+ arg_type = session['functionData']['args_type'].split(",");
+ arg_type.pop();
+ else:
+ arg_type = session['functionData']['args_type'].split(",");
+ else:
+ arg_type = session['functionData']['args_type'].split(",");
+
+ # Below are two different template to execute and start executer
+ if manager.server_type != 'pg' and manager.version < 90300:
+ str_query = render_template("/".join(['debugger/sql', 'execute_edbspl.sql']),
+ func_name=func_name, is_func=session['functionData']['is_func'],
+ lan_name=session['functionData']['language'],
+ ret_type=session['functionData']['return_type'],
+ data=session['functionData']['args_value'],
+ arg_type=arg_type,
+ args_mode=arg_mode
+ )
+ else:
+ str_query = render_template("/".join(['debugger/sql', 'execute_plpgsql.sql']),
+ func_name=func_name, is_func=session['functionData']['is_func'],
+ ret_type=session['functionData']['return_type'],
+ data=session['functionData']['args_value']
+ )
+
+ status, result = conn.execute_async(str_query)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ if conn.connected():
+ # For indirect debugging first create the listener and then wait for the target
+ sql = render_template("/".join([template_path, 'create_listener.sql']))
+
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Get and store the session variable which is required to fetch other information during debugging
+ int_session_id = res['rows'][0]['pldbg_create_listener']
+
+ # In EnterpriseDB versions <= 9.1 the
+ # pldbg_set_global_breakpoint function took five arguments,
+ # the 2nd argument being the package's OID, if any. Starting
+ # with 9.2, the package OID argument is gone, and the function
+ # takes four arguments like the community version has always
+ # done.
+ if server_type == 'ppas' and ver <= 90100:
+ sql = render_template("/".join([template_path, 'add_breakpoint_edb.sql']),
+ session_id=int_session_id,
+ function_oid=obj['function_id'])
+
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+ else:
+ sql = render_template("/".join([template_path, 'add_breakpoint_pg.sql']),
+ session_id=int_session_id,
+ function_oid=obj['function_id'])
+
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # wait for the target
+ sql = render_template("/".join([template_path, 'wait_for_target.sql']),
+ session_id=int_session_id)
+
+ status, res = conn.execute_async(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Delete the existing debugger data in session variable and update with new data
+ del debugger_data[str(trans_id)]
+ del session['debuggerData'][str(trans_id)]
+
+ if 'debuggerData' not in session:
+ debugger_data = dict()
+ else:
+ debugger_data = session['debuggerData']
+
+ debugger_data[str(trans_id)] = {
+ 'conn_id': obj['conn_id'],
+ 'server_id': obj['server_id'],
+ 'database_id': obj['database_id'],
+ 'function_id': obj['function_id'],
+ 'debug_type': obj['debug_type'],
+ 'exe_conn_id': obj['conn_id'],
+ 'debugger_version': obj['debugger_version'],
+ 'session_id': int_session_id,
+ 'frame_id': 0
+ }
+
+ # Store the dictionary into the session variable
+ session['debuggerData'] = debugger_data
+ return make_json_response(data={'status': status, 'result': res})
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+ return make_json_response(data={'status': status, 'result': result})
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result})
+
+
[email protected]('/execute_query/<int:trans_id>/<query_type>', methods=['GET'])
+@login_required
+def execute_debugger_query(trans_id, query_type):
+ """
+ execute_debugger_query(trans_id, query_type)
+
+ This method is responsible to execute the query and return value. As this method is generic so user has to pass the
+ query_type to get the required information for debugging.
+
+ e.g. If user want to execute 'step_into' then query_type='step_into'.
+ If user want to execute 'continue' then query_type='continue'
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ query_type
+ - Type of query to execute
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ if conn.connected():
+ sql = render_template("/".join([template_path, query_type + ".sql"]), session_id=obj['session_id'])
+ # As the query type is continue or step_into or step_over then we may get result after some time so poll the
+ # result. We need to update the frame id variable when user move the next step for debugging.
+ if query_type == 'continue' or query_type == 'step_into' or query_type == 'step_over':
+ status, result = conn.execute_async(sql)
+ return make_json_response(data={'status': status, 'result': result})
+ elif query_type == 'abort_target':
+ status, result = conn.execute_dict(sql)
+
+ manager.release(did=obj['database_id'], conn_id=obj['conn_id'])
+
+ # Delete the existing debugger data in session variable
+ del debugger_data[str(trans_id)]
+ del session['debuggerData'][str(trans_id)]
+
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ return make_json_response(info='Target Aborted.', data={'status': status, 'result': result})
+ else:
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': 'Success', 'result': result['rows']})
+
+
[email protected]('/messages/<int:trans_id>/', methods=["GET"])
+@login_required
+def messages(trans_id):
+ """
+ messages(trans_id)
+
+ This method polls the messages returned by the database server.
+
+ Parameters:
+ trans_id
+ - unique transaction id.
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': 'NotConnected',
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+
+ port_number = ''
+
+ if conn.connected():
+ status, result, my_result = conn.poll()
+ notify = conn.messages()
+ if notify:
+ # In notice message we need to find "PLDBGBREAK" string to find the port number to attach.
+ offset = notify[0].find('PLDBGBREAK');
+ str_len = len('PLDBGBREAK')
+ str_len = str_len + 1
+ tmpOffset = 0
+ tmpFlag = False
+
+ while notify[0][offset+str_len+tmpOffset].isdigit():
+ status = 'Success'
+ tmpFlag = True
+ port_number = port_number + notify[0][offset+str_len+tmpOffset]
+ tmpOffset = tmpOffset + 1
+
+ if tmpFlag == False:
+ status = 'Busy'
+ else:
+ status = 'Busy'
+ else:
+ status = 'NotConnected'
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': port_number})
+
+
[email protected]('/start_execution/<int:trans_id>/<int:port_num>', methods=['GET'])
+@login_required
+def start_execution(trans_id, port_num):
+ """
+ start_execution(trans_id, port_num)
+
+ This method is responsible for creating an asynchronous connection for execution thread.
+ Also store the session id into session return with attach port query for the direct debugging.
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ port_num
+ - Port number to attach
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': 'NotConnected',
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ dbg_version = obj['debugger_version']
+
+ # Delete the existing debugger data in session variable
+ del debugger_data[str(trans_id)]
+ del session['debuggerData'][str(trans_id)]
+
+ # Create asynchronous connection using random connection id.
+ exe_conn_id = str(random.randint(1, 9999999))
+ try:
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=exe_conn_id)
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ # Connect the Server
+ status, msg = conn.connect()
+ if not status:
+ return internal_server_error(errormsg=str(msg))
+
+ if 'debuggerData' not in session:
+ debugger_data = dict()
+ else:
+ debugger_data = session['debuggerData']
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ # connect to port and store the session ID in the session variables
+ sql = render_template("/".join([template_path, 'attach_to_port.sql']), port=port_num)
+ status_port, res_port = conn.execute_dict(sql)
+ if not status_port:
+ return internal_server_error(errormsg=res_port)
+
+ debugger_data[str(trans_id)] = {
+ 'conn_id': obj['conn_id'],
+ 'server_id': obj['server_id'],
+ 'database_id': obj['database_id'],
+ 'function_id': obj['function_id'],
+ 'debug_type': obj['debug_type'],
+ 'exe_conn_id': exe_conn_id,
+ 'debugger_version': dbg_version,
+ 'session_id': res_port['rows'][0]['pldbg_attach_to_port']
+ }
+
+ # Store the debugger data dictionary into the session variable
+ session['debuggerData'] = debugger_data
+
+ return make_json_response(data={'status': 'Success', 'result': res_port['rows'][0]['pldbg_attach_to_port']})
+
+
[email protected]('/set_breakpoint/<int:trans_id>/<int:line_no>/<int:set_type>', methods=['GET'])
+@login_required
+def set_clear_breakpoint(trans_id, line_no, set_type):
+ """
+ set_clear_breakpoint(trans_id, line_no, set_type)
+
+ This method is responsible to set and clean the breakpoint
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ line_no
+ - Line number to set
+ set_type
+ - 0 - clear the breakpoint, 1 - set the breakpoint
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ query_type = ''
+
+ # We need to find out function OID before sending the foid to set the breakpoint because it may possible that
+ # debugging function has multi level function for debugging so we need to save the debug level to session variable
+ # and pass tha appropriate foid to set the breakpoint.
+ sql_ = render_template("/".join([template_path, "get_stack_info.sql"]), session_id=obj['session_id'])
+ status, res_stack = conn.execute_dict(sql_)
+ if not status:
+ return internal_server_error(errormsg=res_stack)
+
+ foid = res_stack['rows'][0]['func']
+
+ # Check the result of the stack before setting the breakpoint
+ if conn.connected():
+ if set_type == 1:
+ query_type = 'set_breakpoint'
+ else:
+ query_type = 'clear_breakpoint'
+
+ sql = render_template("/".join([template_path, query_type + ".sql"]), session_id=obj['session_id'],
+ foid=foid, line_number=line_no)
+
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result['rows']})
+
+
[email protected]('/clear_all_breakpoint/<int:trans_id>', methods=['POST'])
+@login_required
+def clear_all_breakpoint(trans_id):
+ """
+ clear_all_breakpoint(trans_id)
+
+ This method is responsible to clear all the breakpoint
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ """
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ query_type = ''
+
+ if conn.connected():
+ # get the data sent through post from client
+ if request.form['breakpoint_list']:
+ line_numbers = request.form['breakpoint_list'].split(",")
+ for line_no in line_numbers:
+ sql = render_template("/".join([template_path, "clear_breakpoint.sql"]), session_id=obj['session_id'],
+ foid=obj['function_id'], line_number=line_no)
+
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ return make_json_response(data={'status': False})
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result['rows']})
+
+
[email protected]('/deposit_value/<int:trans_id>', methods=['POST'])
+@login_required
+def deposit_parameter_value(trans_id):
+ """
+ deposit_parameter_value(trans_id)
+
+ This method is responsible to change the value of variables
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ """
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ query_type = ''
+
+ if conn.connected():
+ # get the data sent through post from client
+ data = json.loads(request.values['data'])
+
+ if data:
+ sql = render_template("/".join([template_path, "deposit_value.sql"]), session_id=obj['session_id'],
+ var_name=data[0]['name'], line_number=-1, val=data[0]['value'])
+
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result['rows']})
+
+
[email protected]('/select_frame/<int:trans_id>/<int:frame_id>', methods=['GET'])
+@login_required
+def select_frame(trans_id, frame_id):
+ """
+ select_frame(trans_id, frame_id)
+
+ This method is responsible to select the frame from stack info
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ frame_id
+ - Frame id selected
+ """
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ if conn.connected():
+ sql = render_template("/".join([template_path, "select_frame.sql"]), session_id=obj['session_id'],
+ frame_id=frame_id)
+
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result['rows']})
+
+
[email protected]('/poll_end_execution_result/<int:trans_id>/', methods=["GET"])
+@login_required
+def poll_end_execution_result(trans_id):
+ """
+ poll_end_execution_result(trans_id)
+
+ This method polls the end of execution result messages returned by the database server.
+
+ Parameters:
+ trans_id
+ - unique transaction id.
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': 'NotConnected',
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ if obj['debug_type'] == 'direct':
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+ else:
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ if conn.connected():
+ statusmsg = conn.status_message()
+ status, result, my_result = conn.poll()
+ if result:
+ status = 'Success'
+ data = {}
+ for i in result:
+ for k, v in i.items():
+ data["name"] = k
+ data.setdefault("value",[]).append(v)
+
+ return make_json_response(success=1, info=gettext("Execution Completed."),
+ data={'status': status, 'result': data, 'status_message': statusmsg})
+ else:
+ status = 'Busy'
+ else:
+ status = 'NotConnected'
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result})
+
+
[email protected]('/poll_result/<int:trans_id>/', methods=["GET"])
+@login_required
+def poll_result(trans_id):
+ """
+ poll_result(trans_id)
+
+ This method polls the result of the asynchronous query and returns the result.
+
+ Parameters:
+ trans_id
+ - unique transaction id.
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': 'NotConnected',
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ if conn.connected():
+ status, result, my_result = conn.poll()
+ if status == ASYNC_OK:
+ status = 'Success'
+ else:
+ status = 'Busy'
+ else:
+ status = 'NotConnected'
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result})
diff --git a/web/pgadmin/tools/debugger/static/css/debugger.css b/web/pgadmin/tools/debugger/static/css/debugger.css
new file mode 100644
index 0000000..b3a577a
--- /dev/null
+++ b/web/pgadmin/tools/debugger/static/css/debugger.css
@@ -0,0 +1,64 @@
+.navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom {
+ background-image: linear-gradient(to bottom, #CCC 0%, #D2D2D2 100%);
+}
+
+.btn-default {
+ background-color: #D2D2D2;
+ left: 0px;
+ right: 0px;
+ padding: 7px;
+}
+
+.btn-toolbar {
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+
+#container {
+ position: absolute;
+ top: 44px;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
+}
+
+.full-container {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ bottom:0px;
+}
+
+.full-container-pane {
+ width: 100%;
+ height: 100%;
+}
+
+.top-container {
+ min-height: 300px;
+ height: 100%;
+}
+
+.pg-debugger-panel {
+ height: 100%;
+ width: 100%;
+ position: absolute;
+ top: 0; bottom: 0; right: 0; left: 0;
+}
+
+.wcLoadingIcon {
+ position: absolute;
+ font-size: 100px;
+ left: calc(50% - 100px);
+ top: calc(50% - 100px);
+ height: 95px;
+}
+
+.wcLoadingLabel {
+ position: absolute;
+ width: 103%;
+ font-size: 30px;
+ top: calc(50% + 0px);
+ text-align: center;
+}
diff --git a/web/pgadmin/tools/debugger/templates/debugger/direct.html b/web/pgadmin/tools/debugger/templates/debugger/direct.html
new file mode 100644
index 0000000..02d9803
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/direct.html
@@ -0,0 +1,63 @@
+{% extends "base.html" %}
+{% block title %}{{ _('Debugger - ') + function_name }}{% endblock %}
+{% block init_script %}
+try {
+ require(
+ ['pgadmin', 'pgadmin.tools.debugger.direct'],
+ function(pgAdmin, pgDirectDebug) {
+ pgDirectDebug.init({{ uniqueId }}, {{ debug_type }});
+ },
+ function() {
+ /* TODO:: Show proper error dialog */
+ console.log(arguments);
+ });
+} catch (err) {
+ /* Show proper error dialog */
+ console.log(err);
+}
+{% endblock %}
+{% block body %}
+
+<nav class="navbar-inverse navbar-fixed-top">
+ <div class="container-fluid">
+ <div class="collapse navbar-collapse">
+ <ul class="nav navbar-nav">
+ <div id="btn-toolbar" class="btn-toolbar" role="toolbar" aria-label="">
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-step-into" title="{{ _('Step into') }}">
+ <span class="glyphicon glyphicon-circle-arrow-down"></span>
+ </button>
+ <button type="button" class="btn btn-default btn-step-over" title="{{ _('Step over') }}">
+ <span class="glyphicon glyphicon-new-window"></span>
+ </button>
+ <button type="button" class="btn btn-default btn-continue" title="{{ _('Continue/Start') }}">
+ <span class="glyphicon glyphicon-play"></span>
+ </button>
+ </div>
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-toggle-breakpoint" title="{{ _('Toggle breakpoint') }}">
+ <span class="glyphicon glyphicon-record"></span>
+ </button>
+ <button type="button" class="btn btn-default btn-clear-breakpoint" title="{{ _('Clear all breakpoints') }}">
+ <span class="glyphicon glyphicon-ban-circle"></span>
+ </button>
+ </div>
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-stop" title="{{ _('Stop') }}">
+ <span class="glyphicon glyphicon-stop"></span>
+ </button>
+ </div>
+ </div>
+ </ul>
+ </div>
+ </div>
+</nav>
+<div id="container"></div>
+{% endblock %}
+
+
+{% block css_link %}
+{% for stylesheet in stylesheets %}
+<link type="text/css" rel="stylesheet" href="{{ stylesheet }}"/>
+{% endfor %}
+{% endblock %}
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js
new file mode 100644
index 0000000..fa1b6c1
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js
@@ -0,0 +1,305 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin',
+ 'pgadmin.browser', 'backbone', 'backgrid', 'codemirror', 'backform',
+ 'pgadmin.tools.debugger.ui', 'wcdocker', 'pgadmin.backform',
+ 'pgadmin.backgrid', 'pgadmin.browser.frame'],
+ function($, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, CodeMirror, Backform, get_function_arguments) {
+
+ pgAdmin = pgAdmin || window.pgAdmin || {};
+
+ var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+
+ /* Return back, this has been called more than once */
+ if (pgAdmin.Tools.Debugger)
+ return pgAdmin.Tools.Debugger;
+
+ pgTools.Debugger = {
+ init: function() {
+ // We do not want to initialize the module multiple times.
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ // Initialize the context menu to display the debugging options when user open the context menu for functions
+ pgBrowser.add_menus([{
+ name: 'direct_debugger', node: 'function', module: this,
+ applies: ['object', 'context'], callback: 'get_function_information',
+ category: 'Debugging', priority: 10, label: '{{ _('Debug') }}',
+ data: {object: 'function'}, icon: 'fa fa-arrow-circle-right',
+ enable: 'can_debug'
+ },{
+ name: 'global_debugger', node: 'function', module: this,
+ applies: ['object', 'context'], callback: 'check_func_debuggable',
+ category: 'Debugging', priority: 10, label: '{{ _('Set breakpoint') }}',
+ data: {object: 'function'}, icon: 'fa fa-arrow-circle-right',
+ enable: 'can_debug'
+ }]);
+
+ // Create and load the new frame required for debugger panel
+ this.frame = new pgBrowser.Frame({
+ name: 'frm_debugger',
+ title: '{{ _('Debugger') }}',
+ width: 500,
+ isCloseable: true,
+ isPrivate: true,
+ url: 'about:blank'
+ });
+
+ this.frame.load(pgBrowser.docker);
+ },
+ // It will check weather the function is actually debuggable or not with pre-required condition.
+ can_debug: function(itemData, item, data) {
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ if ('catalog' == d._type) {
+ //Check if we are not child of catalog
+ return false;
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+
+ // Find the function is really available in database
+ var tree = pgBrowser.tree,
+ info = tree.selected(),
+ d_ = info && info.length == 1 ? tree.itemData(info) : undefined,
+ node = d_ && pgBrowser.Nodes[d_._type];
+
+ if (!d_)
+ return false;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [info]);
+
+ // Must be a super user or object owner to create breakpoints of any kind
+ if (!(treeInfo.server.user.is_superuser || treeInfo.function.funcowner == treeInfo.server.user.name))
+ return false;
+
+ if (d_.language != 'plpgsql' && d_.language != 'edbspl') {
+ return false;
+ }
+
+ return true;
+ },
+ /*
+ For the direct debugging, we need to fetch the function information to display in the dialog so "generate_url"
+ will dynamically generate the URL from the server_id, database_id, schema_id and function id.
+ */
+ generate_url: function(_url, treeInfo, node) {
+ var url = '{BASEURL}{URL}/{OBJTYPE}{REF}',
+ ref = '';
+
+ _.each(
+ _.sortBy(
+ _.values(
+ _.pick(treeInfo,
+ function(v, k, o) {
+ return (k != 'server-group');
+ })
+ ),
+ function(o) { return o.priority; }
+ ),
+ function(o) {
+ ref = S('%s/%s').sprintf(ref, encodeURI(o._id)).value();
+ });
+
+ var args = {
+ 'URL': _url,
+ 'BASEURL': '{{ url_for('debugger.index')}}',
+ 'REF': ref,
+ 'OBJTYPE': encodeURI(node.type)
+ };
+
+ return url.replace(/{(\w+)}/g, function(match, arg) {
+ return args[arg];
+ });
+ },
+
+ check_func_debuggable: function(args, item) {
+ var input = args || {},
+ t = pgBrowser.tree,
+ i = item || t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var objName = d.label,
+ treeInfo = node.getTreeNodeHierarchy.apply(node, [i]),
+ _url = this.generate_url('init', treeInfo, node);
+
+ var self = this;
+ $.ajax({
+ url: _url,
+ cache: false,
+ success: function(res) {
+ self.start_global_debugger();
+ },
+ error: function(xhr, status, error) {
+ try {
+ var err = $.parseJSON(xhr.responseText);
+ if (err.success == 0) {
+ msg = S('{{ _(' + err.errormsg + ')}}').value();
+ Alertify.alert("{{ _('" + err.errormsg + "') }}");
+ }
+ } catch (e) {}
+ }
+ });
+ },
+
+ //Callback function when user start the indirect debugging ( Listen to another session to invoke the target )
+ start_global_debugger: function(args, item) {
+ // Initialize the target and create asynchronous connection and unique transaction ID
+ var t = pgBrowser.tree,
+ i = t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
+
+ pgBrowser.Events.once(
+ 'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+ frame.openURL(url);
+ });
+
+ // Create the debugger panel as per the data received from user input dialog.
+ var dashboardPanel = pgBrowser.docker.findPanels(
+ 'dashboard'
+ ),
+ panel = pgBrowser.docker.addPanel(
+ 'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+ );
+
+ panel.focus();
+
+ // Panel Closed event
+ panel.on(wcDocker.EVENT.CLOSED, function() {
+ var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
+ $.ajax({
+ url: closeUrl,
+ method: 'GET'
+ });
+ });
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger target Initialize Error'
+ );
+ }
+ });
+ },
+
+ /*
+ Get the function information for the direct debugging to display the functions arguments and other informations
+ in the user input dialog
+ */
+ get_function_information: function(args, item) {
+ var input = args || {},
+ t = pgBrowser.tree,
+ i = item || t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var objName = d.label,
+ treeInfo = node.getTreeNodeHierarchy.apply(node, [i]),
+ _url = this.generate_url('init', treeInfo, node);
+
+ var self = this;
+ $.ajax({
+ url: _url,
+ cache: false,
+ success: function(res) {
+
+ // Open Alertify the dialog to take the input arguments from user if function having input arguments
+ if (res.data[0]['require_input']) {
+ get_function_arguments(res.data[0]);
+ }
+ else {
+ // Initialize the target and create asynchronous connection and unique transaction ID
+ // If there is no arguments to the functions then we should not ask for for function arguments and
+ // Directly open the panel
+ var t = pgBrowser.tree,
+ i = t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "direct/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+
+ var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
+
+ pgBrowser.Events.once(
+ 'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+ frame.openURL(url);
+ });
+
+ // Create the debugger panel as per the data received from user input dialog.
+ var dashboardPanel = pgBrowser.docker.findPanels(
+ 'dashboard'
+ ),
+ panel = pgBrowser.docker.addPanel(
+ 'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+ );
+
+ panel.focus();
+
+ // Register Panel Closed event
+ panel.on(wcDocker.EVENT.CLOSED, function() {
+ var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
+ $.ajax({
+ url: closeUrl,
+ method: 'GET'
+ });
+ });
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger target Initialize Error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }
+ },
+ error: function(xhr, status, error) {
+ try {
+ var err = $.parseJSON(xhr.responseText);
+ if (err.success == 0) {
+ msg = S('{{ _(' + err.errormsg + ')}}').value();
+ Alertify.alert("{{ _('" + err.errormsg + "') }}");
+ }
+ } catch (e) {}
+ }
+ });
+ }
+ };
+
+ return pgAdmin.Tools.Debugger;
+ });
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
new file mode 100644
index 0000000..57232b8
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
@@ -0,0 +1,414 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin',
+ 'pgadmin.browser', 'backbone', 'backgrid', 'codemirror', 'backform',
+ 'wcdocker', 'pgadmin.backform', 'pgadmin.backgrid',
+ 'pgadmin.browser.panel'],
+ function($, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, CodeMirror, Backform ) {
+
+ /*
+ * Function used to return the respective Backgrid control based on the data type
+ * of function input argument.
+ */
+ var cellFunction = function(model) {
+ var self = this,
+ variable_type = model.get("type");
+
+ // if variable type is an array then we need to render the custom control to take the input from user.
+ if (variable_type.indexOf("[]") !=-1) {
+ if (variable_type.indexOf("integer") != -1) {
+ return Backgrid.Extension.InputIntegerArrayCell;
+ }
+ return Backgrid.Extension.InputStringArrayCell;
+ }
+
+ switch(variable_type) {
+ case "bool":
+ return Backgrid.BooleanCell;
+ break;
+
+ case "integer":
+ return Backgrid.IntegerCell;
+ break;
+
+ case "real":
+ return Backgrid.NumberCell;
+ break;
+
+ case "string":
+ return Backgrid.StringCell;
+ break;
+ case "date":
+ return Backgrid.DateCell;
+ break;
+ default:
+ return Backgrid.Cell;
+ break;
+ }
+ }
+
+ /*
+ * Function used to return the respective Backgrid string or boolean control based on the data type
+ * of function input argument.
+ */
+ var cellExprControlFunction = function(model) {
+ var self = this,
+ variable_type = model.get("type");
+ if (variable_type.indexOf("[]") !=-1) {
+ return Backgrid.StringCell;
+ }
+ return Backgrid.BooleanCell;
+ }
+
+ /**
+ * DebuggerInputArgsModel used to represent input parameters for the function to debug
+ * for function objects.
+ **/
+ var DebuggerInputArgsModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined,
+ type: undefined,
+ is_null: undefined,
+ expr: undefined,
+ value: undefined,
+ use_default: undefined,
+ default_value: undefined,
+ },
+ validate: function() {
+ if (_.isUndefined(this.get('value')) ||
+ _.isNull(this.get('value')) ||
+ String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
+ var msg = '{{ _('Please enter some value!') }}';
+ this.errorModel.set('value', msg);
+ return msg;
+ } else {
+ this.errorModel.unset('value');
+ }
+ return null;
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var DebuggerInputArgCollections = Backbone.Collection.extend({
+ model: DebuggerInputArgsModel
+ });
+
+ // function will enable/disable the use_default column based on the value received.
+ var disableDefaultCell = function(d) {
+ if (d instanceof Backbone.Model) {
+ return d.get('use_default');
+ }
+ return false;
+ };
+
+ // Enable/Disable the control based on the array data type of the function input arguments
+ var disableExpressionControl = function(d) {
+ var argType = d.get('type');
+ if (d instanceof Backbone.Model) {
+ var argType = d.get('type');
+ if (argType.indexOf("[]") !=-1) {
+ return false;
+ }
+ return true;
+ }
+ };
+
+ var res = function(args) {
+ if (!Alertify.debuggerInputArgsDialog) {
+ Alertify.dialog('debuggerInputArgsDialog', function factory() {
+ return {
+ main:function(title, data) {
+ this.set('title', title);
+ this.data = data;
+
+ var argname, argtype, argmode, default_args_count, default_args, arg_cnt;
+
+ var value_header = Backgrid.HeaderCell.extend({
+ // Add fixed width to the "value" column
+ className: 'width_percent_25'
+ });
+
+ var def_val_list = [],
+ gridCols = [
+ {name: 'name', label:'Name', type:'text', editable: false, cell:'string' },
+ {name: 'type', label:'Type', type: 'text', editable: false, cell:'string' },
+ {name: 'is_null', label:'Null?', type: 'boolean', cell: 'boolean' },
+ {name: 'expr', label:'Expression?', type: 'boolean', cellFunction: cellExprControlFunction, editable: disableExpressionControl },
+ {name: 'value', label:'Value', type: 'text', editable: true, cellFunction: cellFunction, headerCell: value_header },
+ {name: 'use_default', label:'Use Default?', type: 'boolean', cell:"boolean", editable: disableDefaultCell },
+ {name: 'default_value', label:'Default value', type: 'text', editable: false, cell:'string' }
+ ];
+
+ var my_obj = [];
+
+ argtype = this.data['proargtypenames'].split(",");
+
+ if (this.data['proargmodes'] != null) {
+ argmode = this.data['proargmodes'].split(",");
+ }
+
+ if (this.data['pronargdefaults']) {
+ default_args_count = this.data['pronargdefaults'];
+ default_args = this.data['proargdefaults'].split(",");
+ arg_cnt = default_args_count;
+ }
+
+ if (this.data['proargnames'] != null) {
+ argname = this.data['proargnames'].split(",");
+
+ // It will assign default values to "Default value" column
+ for (j = (argname.length - 1);j >= 0; j--) {
+ if (this.data['proargmodes'] != null) {
+ if (arg_cnt && (argmode[j] == 'i' || argmode[j] == 'b')) {
+ arg_cnt = arg_cnt - 1;
+ def_val_list[j] = default_args[arg_cnt]
+ }
+ else {
+ def_val_list[j] = "<No default value>";
+ }
+ }
+ else {
+ if (arg_cnt) {
+ arg_cnt = arg_cnt - 1;
+ def_val_list[j] = default_args[arg_cnt]
+ }
+ else {
+ def_val_list[j] = "<No default value>";
+ }
+ }
+ }
+
+ if (argtype.length != 0)
+ {
+ for (i = 0; i < argtype.length; i++) {
+ if (this.data['proargmodes'] != null) {
+ if (argmode[i] == 'i' || argmode[i] == 'b') {
+ var use_def_value = false
+ if (def_val_list[i] != "<No default value>") {
+ use_def_value = true;
+ }
+ my_obj.push({ "name": argname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+ }
+ }
+ else {
+ var use_def_value = false
+ if (def_val_list[i] != "<No default value>") {
+ use_def_value = true;
+ }
+ my_obj.push({ "name": argname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+ }
+ }
+ }
+ }
+ else {
+ /*
+ Generate the name parameter if function do not have arguments name
+ like dbgparam1, dbgparam2 etc.
+ */
+ var myargname = [];
+
+ for (i = 0; i < argtype.length; i++) {
+ myargname[i] = "dbgparam" + (i+1);
+ }
+
+ // If there is no default arguments
+ if (!this.data['pronargdefaults']) {
+ for (i = 0; i < argtype.length; i++) {
+ my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": false, "default_value": "<No default value>"});
+ }
+ }
+ else {
+ // If there is default arguments
+ //Below logic will assign default values to "Default value" column
+ for (j = (myargname.length - 1);j >= 0; j--) {
+ if (this.data['proargmodes'] == null) {
+ if (arg_cnt) {
+ arg_cnt = arg_cnt - 1;
+ def_val_list[j] = default_args[arg_cnt]
+ }
+ else {
+ def_val_list[j] = "<No default value>";
+ }
+ }
+ else {
+ if (arg_cnt && (argmode[j] == 'i' || argmode[j] == 'b')) {
+ arg_cnt = arg_cnt - 1;
+ def_val_list[j] = default_args[arg_cnt]
+ }
+ else {
+ def_val_list[j] = "<No default value>";
+ }
+ }
+ }
+
+ for (i = 0; i < argtype.length; i++) {
+ if (this.data['proargmodes'] == null) {
+ var use_def_value = false
+ if (def_val_list[i] != "<No default value>") {
+ use_def_value = true;
+ }
+ my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+ }
+ else {
+ if (argmode[i] == 'i' || argmode[i] == 'b') {
+ var use_def_value = false
+ if (def_val_list[i] != "<No default value>") {
+ use_def_value = true;
+ }
+ my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+ }
+ }
+ }
+ }
+ }
+
+ var debuggerInputArgsColl = this.debuggerInputArgsColl = new DebuggerInputArgCollections(my_obj);
+
+ // Initialize a new Grid instance
+ var grid = this.grid = new Backgrid.Grid({
+ columns: gridCols,
+ collection: debuggerInputArgsColl,
+ className: "backgrid table-bordered"
+ });
+
+ grid.render();
+ $(this.elements.content).html(grid.el);
+ },
+ setup:function() {
+ return {
+ buttons:[{ text: "Debug", key: 27, className: "btn btn-primary" },
+ { text: "Cancel", key: 27, className: "btn btn-primary" }],
+ options: { modal: 0, resizable: true }
+ };
+ },
+ // Callback functions when click on the buttons of the Alertify dialogs
+ callback: function(e) {
+ if (e.button.text === "Debug") {
+
+ // Initialize the target once the debug button is clicked and
+ // create asynchronous connection and unique transaction ID
+ var self = this;
+ var t = pgBrowser.tree,
+ i = t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+ var args_value_list = [];
+
+ this.grid.collection.each(function(m) {
+ // Check if default value to be used or not
+ if (m.get('use_default')) {
+ args_value_list.push({ 'name': m.get('name'),
+ 'type': m.get('type'),
+ 'value': m.get('default_value')});
+ }
+ else {
+ args_value_list.push({ 'name': m.get('name'),
+ 'type': m.get('type'),
+ 'value': m.get('value')});
+ }
+ });
+
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "direct/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'POST',
+ data:{'data':JSON.stringify(args_value_list)},
+ success: function(res) {
+
+ var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
+
+ pgBrowser.Events.once(
+ 'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+ frame.openURL(url);
+ });
+
+ // Create the debugger panel as per the data received from user input dialog.
+ var dashboardPanel = pgBrowser.docker.findPanels(
+ 'dashboard'
+ ),
+ panel = pgBrowser.docker.addPanel(
+ 'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+ );
+
+ panel.focus();
+
+ // Panel Closed event
+ panel.on(wcDocker.EVENT.CLOSED, function() {
+ var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
+ $.ajax({
+ url: closeUrl,
+ method: 'GET'
+ });
+ });
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger target Initialize Error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+
+ return true;
+ }
+
+ if (e.button.text === "Cancel") {
+ //close the dialog...
+ return false;
+ }
+ },
+ build:function() {
+ },
+ prepare:function() {
+ // Once the dialog open disable the button by default
+ this.__internal.buttons[0].element.disabled = true;
+
+ this.grid.listenTo(this.debuggerInputArgsColl,"backgrid:edited",
+ (function(obj) {
+
+ return function() {
+
+ var self = this;
+ var enable_btn = false;
+
+ for (i = 0; i < this.collection.length; i++ ) {
+
+ // TODO: Need to check the "NULL" and "Expression" column value to enable/disable the "Debug" button
+ if (this.collection.models[i].get('value') == "" ||
+ this.collection.models[i].get('value') == null ||
+ this.collection.models[i].get('value') == undefined) {
+ enable_btn = true;
+
+ if (this.collection.models[i].get('use_default')) {
+ obj.__internal.buttons[0].element.disabled = false;
+ }
+ else{
+ obj.__internal.buttons[0].element.disabled = true;
+ break;
+ }
+ }
+ }
+ if (!enable_btn)
+ obj.__internal.buttons[0].element.disabled = false;
+ }
+ }
+ )(this)
+ );
+ }
+ };
+ });
+ }
+
+ Alertify.debuggerInputArgsDialog('Debugger',args).resizeTo('60%', '60%');
+
+ };
+
+ return res;
+});
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
new file mode 100644
index 0000000..e4b688b
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
@@ -0,0 +1,1318 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin',
+ 'backbone', 'backgrid', 'codemirror', 'backform','pgadmin.tools.debugger.ui',
+ 'wcdocker', 'pgadmin.backform', 'pgadmin.backgrid', 'codemirror.activeline'],
+ function($, _, S, Alertify, pgAdmin, Backbone, Backgrid, CodeMirror, Backform, get_function_arguments) {
+
+ pgAdmin = pgAdmin || window.pgAdmin || {};
+
+ var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+
+ if (pgTools.DirectDebug)
+ return pgTools.DirectDebug;
+
+ var controller = new (function() {});
+
+ _.extend(
+ controller, Backbone.Events, {
+ enable: function(btn, enable) {
+ // trigger the event and change the button view to enable/disable the buttons for debugging
+ this.trigger('pgDebugger:button:state:' + btn, enable);
+ },
+
+ /*
+ Function to set the breakpoint and send the line no. which is set to server
+ trans_id :- Unique Transaction ID, line_no - line no. to set the breakpoint, set_type = 0 - clear , 1 - set
+ */
+ set_breakpoint: function(trans_id, line_no, set_type) {
+ var self = this;
+
+ // Make ajax call to set/clear the break point by user
+ var baseUrl = "{{ url_for('debugger.index') }}" + "set_breakpoint/" + trans_id + "/" + line_no + "/" + set_type;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ // Breakpoint has been set by the user
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Breakpoint set execution error'
+ );
+ }
+ });
+ },
+
+ // Function to get the latest breakpoint information and update the gutters of codemirror
+ UpdateBreakpoint: function(trans_id) {
+ var self = this;
+
+ var br_list = self.GetBreakpointInformation(trans_id);
+
+ // If there is no break point to clear then we should return from here.
+ if ((br_list.length == 1) && (br_list[0].linenumber == -1))
+ return;
+
+ var breakpoint_list = new Array();
+
+ for (i = 0; i < br_list.length; i++) {
+ if (br_list[i].linenumber != -1) {
+ breakpoint_list.push(br_list[i].linenumber)
+ }
+ }
+
+ for (i = 0;i< breakpoint_list.length;i++) {
+ var info = pgTools.DirectDebug.editor.lineInfo((breakpoint_list[i] - 1));
+
+ if (info.gutterMarkers != undefined) {
+ pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", null);
+ }
+ else {
+ pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", function() {
+ var marker = document.createElement("div");
+ marker.style.color = "#822";
+ marker.innerHTML = "●";
+ return marker;
+ }());
+ }
+ }
+ },
+
+ // Function to get the breakpoint information from the server
+ GetBreakpointInformation: function(trans_id) {
+ var self = this;
+ var result = '';
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "get_breakpoints";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ async: false,
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ result = res.data.result;
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Error fetching breakpoint information'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Error fetching breakpoint information'
+ );
+ }
+ });
+
+ return result;
+ },
+
+ // Function to start the executer and execute the user requested option for debugging
+ start_execution: function(trans_id, port_num) {
+ var self = this;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "start_execution/" + trans_id + "/" + port_num;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // If status is Success then find the port number to attach the executer.
+ self.execute_query(trans_id);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Start execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Start execution error'
+ );
+ }
+ });
+ },
+
+ // Execute the query and get the first functions debug information from the server
+ execute_query: function(trans_id) {
+ var self = this;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "wait_for_breakpoint";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // set the return code to the code editor text area
+ if (res.data.result[0].src != null && res.data.result[0].linenumber != null) {
+ pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+ active_line_no = self.active_line_no = (res.data.result[0].linenumber - 2);
+ pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+ }
+
+ // Call function to create and update local variables ....
+ self.GetLocalVariables(trans_id);
+ self.GetStackInformation(trans_id);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Execution error'
+ );
+ }
+ });
+ },
+
+ // Get the local variable information of the functions and update the grid
+ GetLocalVariables: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "get_variables";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // Call function to create and update local variables
+ self.AddLocalVariables(res.data.result);
+ self.AddParameters(res.data.result);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Error fetching variables information'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Error fetching variables information'
+ );
+ }
+ });
+ },
+
+ // Get the stack information of the functions and update the grid
+ GetStackInformation: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "get_stack_info";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // Call function to create and update stack information
+ self.AddStackInformation(res.data.result);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Error fetching stack information'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Error fetching stack information'
+ );
+ }
+ });
+ },
+
+ /*
+ poll the actual result after user has executed the "continue", "step-into", "step-over" actions and get the
+ other updated information from the server.
+ */
+ poll_result: function(trans_id) {
+ var self = this;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "poll_result/" + trans_id;
+
+
+ setTimeout(
+ function() {
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ if (res.data.result[0].src != undefined || res.data.result[0].src != null) {
+ pgTools.DirectDebug.docker.finishLoading(500);
+ pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+ self.UpdateBreakpoint(trans_id);
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+ self.active_line_no = (res.data.result[0].linenumber - 2);
+
+ // Update the stack, local variables and parameters information
+ self.GetStackInformation(trans_id);
+ self.GetLocalVariables(trans_id);
+ }
+ else if (!pgTools.DirectDebug.debug_type && !pgTools.DirectDebug.first_time_indirect_debug) {
+ pgTools.DirectDebug.docker.finishLoading(500);
+ if (self.active_line_no != undefined) {
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ }
+ self.clear_all_breakpoint(trans_id);
+ self.execute_query(trans_id);
+ pgTools.DirectDebug.first_time_indirect_debug = true;
+ }
+ else {
+ pgTools.DirectDebug.docker.finishLoading(500);
+ // If the source is really changed then only update the breakpoint information
+ if (res.data.result[0].src != pgTools.DirectDebug.editor.getValue()) {
+ pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+ self.UpdateBreakpoint(trans_id);
+ }
+
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+ self.active_line_no = (res.data.result[0].linenumber - 2);
+
+ // Update the stack, local variables and parameters information
+ self.GetStackInformation(trans_id);
+ self.GetLocalVariables(trans_id);
+ }
+
+ // Enable all the buttons as we got the results
+ self.enable('stop', true);
+ self.enable('step_over', true);
+ self.enable('step_into', true);
+ self.enable('continue', true);
+ self.enable('toggle_breakpoint', true);
+ self.enable('clear_all_breakpoints', true);
+ }
+ else if (res.data.status === 'Busy') {
+ // If status is Busy then poll the result by recursive call to the poll function
+ if (!pgTools.DirectDebug.debug_type) {
+ pgTools.DirectDebug.docker.startLoading('{{ _('Waiting for another session to invoke the target...') }}');
+ // As we are waiting for another session to invoke the target so disable all the buttons
+ self.enable('stop', false);
+ self.enable('step_over', false);
+ self.enable('step_into', false);
+ self.enable('continue', false);
+ self.enable('toggle_breakpoint', false);
+ self.enable('clear_all_breakpoints', false);
+ pgTools.DirectDebug.first_time_indirect_debug = false;
+ self.poll_result(trans_id);
+ }
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger Poll Result Error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger Poll Result Error'
+ );
+ }
+ });
+ }, 800 );
+
+ },
+
+ /*
+ For the direct debugging, we need to check weather the functions execution is completed or not. After completion
+ of the debugging, we will stop polling the result until new execution starts.
+ */
+ poll_end_execution_result: function(trans_id) {
+ var self = this;
+ //return;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "poll_end_execution_result/" + trans_id;
+ self.execution_completed = false;
+
+ setTimeout(
+ function() {
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // Call function to create and update local variables ....
+ if (res.data.result.name != null) {
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ self.AddResults(res.data.result);
+ pgTools.DirectDebug.paramsTabFrame.tab(3,true);
+ self.execution_completed = true;
+
+ //Set the alertify message to inform the user that execution is completed.
+ Alertify.notify(
+ res.info,
+ 'success',
+ 3,
+ function() { }
+ );
+
+ // Update the message tab of the debugger
+ pgTools.DirectDebug.dbmsMessages.$elem.text(res.data.status_message);
+
+ // Execution completed so disable the buttons other than "Continue/Start" button because user can still
+ // start the same execution again.
+ self.enable('stop', false);
+ self.enable('step_over', false);
+ self.enable('step_into', false);
+ self.enable('toggle_breakpoint', false);
+ self.enable('clear_all_breakpoints', false);
+
+ //TODO: Continue button should be enable and user will be able to do next debugging with same function.
+ self.enable('continue', false);
+ }
+ }
+ else if (res.data.status === 'Busy') {
+ // If status is Busy then poll the result by recursive call to the poll function
+ //self.poll_end_execution_result(trans_id);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger poll end execution error',
+ res.data.result
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger poll end execution error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }, 1000);
+
+ },
+
+ // Continue the execution until the next breakpoint
+ Continue: function(trans_id) {
+ var self = this;
+
+ //Check first if previous execution was completed or not
+ if (self.execution_completed) {
+ self.execution_completed = false;
+ get_function_arguments();
+ // TODO: we need to again ask for the input dialog
+ //Previous execution was completed so we need to again start the execution with first step.....
+ }
+ else {
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "continue";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.poll_result(trans_id);
+ if (pgTools.DirectDebug.debug_type) {
+ self.poll_end_execution_result(trans_id);
+ }
+ }
+ else {
+ Alertify.alert(
+ 'Debugger: Continue execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Continue execution error'
+ );
+ }
+ });
+ }
+ },
+
+ Step_over: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "step_over";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.poll_result(trans_id);
+ if (pgTools.DirectDebug.debug_type) {
+ self.poll_end_execution_result(trans_id);
+ }
+ }
+ else {
+ Alertify.alert(
+ 'Debugger: Step over execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Step over execution error'
+ );
+ }
+ });
+ },
+
+ Step_into: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "step_into";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.poll_result(trans_id);
+ if (pgTools.DirectDebug.debug_type) {
+ self.poll_end_execution_result(trans_id);
+ }
+ }
+ else {
+ Alertify.alert(
+ 'Debugger: Step into execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Step into execution error'
+ );
+ }
+ });
+ },
+
+ Stop: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "abort_target";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ // Call function to create and update local variables ....
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ self.execution_completed = true;
+
+ //Set the alertify message to inform the user that execution is completed.
+ Alertify.notify(
+ res.info,
+ 'success',
+ 3,
+ function() { }
+ );
+
+ //Disable the buttons other than continue button. If user wants to again then it should allow to debug again...
+ self.enable('stop', false);
+ self.enable('step_over', false);
+ self.enable('step_into', false);
+ self.enable('continue', false);
+ self.enable('toggle_breakpoint', false);
+ self.enable('clear_all_breakpoints', false);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Stop execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Stop execution error'
+ );
+ }
+ });
+ },
+
+ toggle_breakpoint: function(trans_id) {
+ var self = this;
+
+ var info = pgTools.DirectDebug.editor.lineInfo(self.active_line_no);
+ var baseUrl = '';
+
+ // If gutterMarker is undefined that means there is no marker defined previously
+ // So we need to set the breakpoint command here...
+ if (info.gutterMarkers == undefined) {
+ baseUrl = "{{ url_for('debugger.index') }}" + "set_breakpoint/" + trans_id + "/" + self.active_line_no + "/" + "1";
+ }
+ else {
+ baseUrl = "{{ url_for('debugger.index') }}" + "set_breakpoint/" + trans_id + "/" + self.active_line_no + "/" + "0";
+ }
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ // Call function to create and update local variables ....
+ var info = pgTools.DirectDebug.editor.lineInfo(self.active_line_no);
+
+ if (info.gutterMarkers != undefined) {
+ pgTools.DirectDebug.editor.setGutterMarker(self.active_line_no, "breakpoints", null);
+ }
+ else {
+ pgTools.DirectDebug.editor.setGutterMarker(self.active_line_no, "breakpoints", function() {
+ var marker = document.createElement("div");
+ marker.style.color = "#822";
+ marker.innerHTML = "●";
+ return marker;
+ }());
+ }
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Toggle breakpoint execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Toggle breakpoint execution error'
+ );
+ }
+ });
+ },
+
+ clear_all_breakpoint: function(trans_id) {
+ var self = this;
+
+ var br_list = self.GetBreakpointInformation(trans_id);
+
+ // If there is no break point to clear then we should return from here.
+ if ((br_list.length == 1) && (br_list[0].linenumber == -1))
+ return;
+
+ var breakpoint_list = new Array();
+
+ for (i = 0; i < br_list.length; i++) {
+ if (br_list[i].linenumber != -1) {
+ breakpoint_list.push(br_list[i].linenumber)
+ }
+ }
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "clear_all_breakpoint/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'POST',
+ data: { 'breakpoint_list': breakpoint_list.join() },
+ success: function(res) {
+ if (res.data.status) {
+ for (i = 0;i< breakpoint_list.length;i++) {
+ var info = pgTools.DirectDebug.editor.lineInfo((breakpoint_list[i] - 1));
+
+ if (info) {
+ if (info.gutterMarkers != undefined) {
+ pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", null);
+ }
+ }
+ }
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Clear all breakpoint execution error'
+ );
+ }
+ });
+ },
+
+ AddStackInformation: function(result) {
+ var self = this;
+
+ // Remove the existing created grid and update the stack values
+ if (self.stack_grid) {
+ self.stack_grid.remove();
+ self.stack_grid = null;
+ }
+
+ pgTools.DirectDebug.stackTab.$elem.empty();
+
+ var DebuggerStackModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined,
+ value: undefined,
+ line_no: undefined
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var StackCollection = Backbone.Collection.extend({
+ model: DebuggerStackModel
+ });
+
+ stackGridCols = [
+ {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+ {name: 'value', label:'Value', type:'text', editable: false, cell:'string'},
+ {name: 'line_no', label:'Line No.', type:'text', editable: false, cell:'string'}
+ ];
+
+ var my_obj = [];
+ if (result.length != 0)
+ {
+ for (i = 0; i < result.length; i++) {
+ // TODO: change the my_func_test_2 with name of the function to be executed.
+ my_obj.push({ "name": result[i].targetname, "value": result[i].args, "line_no": result[i].linenumber });
+ }
+ }
+
+ var stackColl = this.stackColl = new StackCollection(my_obj);
+ this.stackColl.on('backgrid:row:selected', self.select_frame, self);
+
+ // Initialize a new Grid instance
+ var stack_grid = this.stack_grid = new Backgrid.Grid({
+ columns: stackGridCols,
+ row: Backgrid.Row.extend({
+ highlightColor: "#D9EDF7",
+ disabledColor: "#F1F1F1",
+ events: {
+ click: "rowClick"
+ },
+ rowClick: function(e) {
+ //Find which row is selected and depending on that send the frame id
+ for (i = 0; i < this.model.collection.length; i++) {
+ if (this.model.collection.models[i].get('name') == this.model.get('name')) {
+ self.frame_id_ = i;
+ break;
+ }
+ }
+ this.model.trigger('backgrid:row:selected', this);
+ self.stack_grid.$el.find("td").css("background-color", this.disabledColor);
+ this.$el.find("td").css("background-color", this.highlightColor);
+ }
+ }),
+ collection: stackColl,
+ className: "backgrid table-bordered"
+ });
+
+ stack_grid.render();
+ pgTools.DirectDebug.stackTab.$elem.append(stack_grid.el);
+ },
+
+ AddResults: function(result) {
+ var self = this;
+
+ // Remove the existing created grid and update the result values
+ if (self.result_grid) {
+ self.result_grid.remove();
+ self.result_grid = null;
+ }
+
+ pgTools.DirectDebug.retResults.$elem.empty();
+
+ var DebuggerResultsModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var ResultsCollection = Backbone.Collection.extend({
+ model: DebuggerResultsModel
+ });
+
+ resultGridCols = [
+ {name: 'value', label:result.name, type:'text', editable: false, cell:'string'}
+ ];
+
+ var my_obj = [];
+ if (result.value.length != 0)
+ {
+ for (i = 0; i < result.value.length; i++) {
+ // TODO: change the my_func_test_2 with name of the function to be executed.
+ my_obj.push({ "value": result.value[i]});
+ }
+ }
+
+ // Initialize a new Grid instance
+ var result_grid = this.result_grid = new Backgrid.Grid({
+ columns: resultGridCols,
+ collection: new ResultsCollection(my_obj),
+ className: "backgrid table-bordered"
+ });
+
+ result_grid.render();
+ pgTools.DirectDebug.retResults.$elem.append(result_grid.el);
+ },
+
+ AddLocalVariables: function(result) {
+ var self = this;
+
+ // Remove the existing created grid and update the variables values
+ if (self.variable_grid) {
+ self.variable_grid.remove();
+ self.variable_grid = null;
+ }
+
+ pgTools.DirectDebug.localVars.$elem.empty();
+
+ var DebuggerVariablesModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined,
+ type: undefined,
+ value: undefined
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var VariablesCollection = Backbone.Collection.extend({
+ model: DebuggerVariablesModel
+ });
+
+ gridCols = [
+ {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+ {name: 'type', label:'Type', type: 'text', editable: false, cell:'string'},
+ {name: 'value', label:'Value', type: 'text', cell: 'string'}
+ ];
+
+ var my_obj = [];
+ if (result.length != 0)
+ {
+ for (i = 0; i < result.length; i++) {
+ if (result[i].varclass == 'L') {
+ my_obj.push({ "name": result[i].name, "type": result[i].dtype, "value": result[i].value});
+ }
+ }
+ }
+
+ // Initialize a new Grid instance
+ var variable_grid = this.variable_grid = new Backgrid.Grid({
+ columns: gridCols,
+ collection: new VariablesCollection(my_obj),
+ className: "backgrid table-bordered"
+ });
+
+ variable_grid.render();
+ pgTools.DirectDebug.localVars.$elem.append(variable_grid.el);
+ },
+
+ AddParameters: function(result) {
+ var self = this;
+
+ // Remove the existing created grid and update the parameter values
+ if (self.param_grid) {
+ self.param_grid.remove();
+ self.param_grid = null;
+ }
+
+ pgTools.DirectDebug.paramsTab.$elem.empty();
+
+ var DebuggerParametersModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined,
+ type: undefined,
+ value: undefined
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var ParametersCollection = self.ParametersCollection = Backbone.Collection.extend({
+ model: DebuggerParametersModel
+ });
+
+ self.ParametersCollection.prototype.on('change', self.deposit_parameter_value, self);
+
+ paramGridCols = [
+ {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+ {name: 'type', label:'Type', type: 'text', editable: false, cell:'string'},
+ {name: 'value', label:'Value', type: 'text', cell: 'string'}
+ ];
+
+ var param_obj = [];
+ if (result.length != 0)
+ {
+ for (i = 0; i < result.length; i++) {
+ if (result[i].varclass == 'A') {
+ param_obj.push({ "name": result[i].name, "type": result[i].dtype, "value": result[i].value});
+ }
+ }
+ }
+
+ // Initialize a new Grid instance
+ var param_grid = this.param_grid = new Backgrid.Grid({
+ columns: paramGridCols,
+ collection: new ParametersCollection(param_obj),
+ className: "backgrid table-bordered"
+ });
+
+ param_grid.render();
+ pgTools.DirectDebug.paramsTab.$elem.append(param_grid.el);
+ },
+
+ deposit_parameter_value: function(model) {
+ var self = this;
+
+ // variable name and value list that is changed by user
+ var name_value_list = [];
+
+ name_value_list.push({ 'name': model.get('name'),'type': model.get('type'), 'value': model.get('value')});
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "deposit_value/" + pgTools.DirectDebug.trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'POST',
+ data:{'data':JSON.stringify(name_value_list)},
+ success: function(res) {
+ if (res.data.status) {
+ // Get the updated variables value
+ self.GetLocalVariables(pgTools.DirectDebug.trans_id);
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Deposit value execution error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ },
+
+ select_frame: function(model, selected) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "select_frame/" + pgTools.DirectDebug.trans_id + "/" + self.frame_id_;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+ self.UpdateBreakpoint(pgTools.DirectDebug.trans_id);
+ //active_line_no = self.active_line_no = (res.data.result[0].linenumber - 2);
+ pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+
+ // Call function to create and update local variables ....
+ self.GetLocalVariables(pgTools.DirectDebug.trans_id);
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Select frame execution error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ },
+ }
+ )
+
+ /*
+ Debugger tool var view to create the button toolbar and listen to the button click event and inform the
+ controller about the click and controller will take the action for the specified button click.
+ */
+ var DebuggerToolbarView = Backbone.View.extend({
+ el: '#btn-toolbar',
+ initialize: function() {
+ controller.on('pgDebugger:button:state:stop', this.enable_stop, this);
+ controller.on('pgDebugger:button:state:step_over', this.enable_step_over, this);
+ controller.on('pgDebugger:button:state:step_into', this.enable_step_into, this);
+ controller.on('pgDebugger:button:state:continue', this.enable_continue, this);
+ controller.on('pgDebugger:button:state:toggle_breakpoint', this.enable_toggle_breakpoint, this);
+ controller.on('pgDebugger:button:state:clear_all_breakpoints', this.enable_clear_all_breakpoints, this);
+ },
+ events: {
+ 'click .btn-stop': 'on_stop',
+ 'click .btn-clear-breakpoint': 'on_clear_all_breakpoint',
+ 'click .btn-toggle-breakpoint': 'on_toggle_breakpoint',
+ 'click .btn-continue': 'on_continue',
+ 'click .btn-step-over': 'on_step_over',
+ 'click .btn-step-into': 'on_step_into'
+ },
+ enable_stop: function(enable) {
+ var $btn = this.$el.find('.btn-stop');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_step_over: function(enable) {
+ var $btn = this.$el.find('.btn-step-over');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_step_into: function(enable) {
+ var $btn = this.$el.find('.btn-step-into');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_continue: function(enable) {
+ var $btn = this.$el.find('.btn-continue');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_toggle_breakpoint: function(enable) {
+ var $btn = this.$el.find('.btn-toggle-breakpoint');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_clear_all_breakpoints: function(enable) {
+ var $btn = this.$el.find('.btn-clear-breakpoint');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+
+ on_stop: function() {
+ controller.Stop(pgTools.DirectDebug.trans_id);
+ },
+ on_clear_all_breakpoint: function() {
+ controller.clear_all_breakpoint(pgTools.DirectDebug.trans_id);
+ },
+ on_toggle_breakpoint: function() {
+ controller.toggle_breakpoint(pgTools.DirectDebug.trans_id);
+ },
+ on_continue: function() {
+ controller.Continue(pgTools.DirectDebug.trans_id);
+ },
+ on_step_over: function() {
+ controller.Step_over(pgTools.DirectDebug.trans_id);
+ },
+ on_step_into: function() {
+ controller.Step_into(pgTools.DirectDebug.trans_id);
+ },
+ });
+
+
+ /*
+ Function is responsible to create the new wcDocker instance for debugger and initialize the debugger panel inside
+ the docker instance.
+ */
+ var DirectDebug = function() {};
+
+ _.extend(DirectDebug.prototype, {
+ init: function(trans_id, debug_type) { /* We should get the transaction id from the server during initialization here */
+ // We do not want to initialize the module multiple times.
+
+ var self = this;
+ _.bindAll(pgTools.DirectDebug, 'messages');
+
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+ this.trans_id = trans_id;
+ this.debug_type = debug_type;
+ this.first_time_indirect_debug = false;
+
+ var docker = this.docker = new wcDocker(
+ '#container', {
+ allowContextMenu: false,
+ allowCollapse: false,
+ themePath: '{{ url_for('static', filename='css/wcDocker/Themes') }}',
+ theme: 'pgadmin'
+ });
+
+ this.panels = [];
+
+ // Below code will be executed for indirect debugging
+ if (trans_id != undefined && !debug_type) {
+ // Make ajax call to execute the and start the target for execution
+ var baseUrl = "{{ url_for('debugger.index') }}" + "start_listener/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.intializePanels();
+ controller.poll_result(trans_id);
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger listener starting error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }
+ else if (trans_id != undefined && debug_type)
+ {
+ // Make ajax call to execute the and start the target for execution
+ var baseUrl = "{{ url_for('debugger.index') }}" + "start_listener/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.messages(trans_id);
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger listener starting error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }
+ else
+ this.intializePanels();
+ },
+
+ // Read the messages of the database server and get the port ID and attach the executer to that port.
+ messages: function(trans_id) {
+ var self = this;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "messages/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ self.intializePanels();
+ // If status is Success then find the port number to attach the executer.
+ //self.start_execution(trans_id, res.data.result);
+ controller.start_execution(trans_id, res.data.result);
+ }
+ else if (res.data.status === 'Busy') {
+ // If status is Busy then poll the result by recursive call to the poll function
+ self.messages(trans_id);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Data Grid Poll Result Error',
+ res.data.result
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger listener starting error'
+ );
+ }
+ });
+
+ },
+
+ // Callback function when user click on gutters of codemirror to set/clear the breakpoint
+ onBreakPoint: function(cm, m) {
+ var self = this;
+
+ // TODO::
+ // We may want to check, if break-point is allowed at this moment or not
+ var info = cm.lineInfo(m);
+
+ // If gutterMarker is undefined that means there is no marker defined previously
+ // So we need to set the breakpoint command here...
+ if (info.gutterMarkers == undefined) {
+ controller.set_breakpoint(self.trans_id,m+1,1); //set the breakpoint
+ }
+ else {
+ controller.set_breakpoint(self.trans_id,m+1,0); //clear the breakpoint
+ }
+
+ cm.setGutterMarker(
+ m, "breakpoints", info.gutterMarkers ? null : function() {
+ var marker = document.createElement("div");
+
+ marker.style.color = "#822";
+ marker.innerHTML = "●";
+
+ return marker;
+ }());
+ },
+
+ // Create the debugger layout with splitter and display the appropriate data received from server.
+ intializePanels: function() {
+ var self = this;
+ this.registerPanel(
+ 'code', false, '100%', '100%',
+ function(panel) {
+ var container = panel.layout().scene().find('.pg-debugger-panel');
+
+ // Create the wcSplitter used by wcDocker to split the single panel.
+ var hSplitter = new wcSplitter(
+ container, panel,
+ wcDocker.ORIENTATION.VERTICAL
+ );
+
+ hSplitter.scrollable(0, false, false);
+ hSplitter.scrollable(1, true, true);
+
+ // Initialize this splitter with a layout in each pane.
+ hSplitter.initLayouts(wcDocker.LAYOUT.SIMPLE, wcDocker.LAYOUT.SIMPLE);
+
+ // By default, the splitter splits down the middle, we split the main panel by 80%.
+ hSplitter.pos(0.65);
+
+ var $params = $('<div class="full-container params"></div>');
+ hSplitter.right().addItem($params);
+
+ // Add Local parameters tab to display function arguments value
+ var paramsTabFrame = self.paramsTabFrame = new wcTabFrame($params, panel);
+ var paramsTab = self.paramsTab = paramsTabFrame.addTab(
+ '{{_('Parameters')}}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ paramsTab.addItem($('<div id=parameters class="info"></div>'));
+
+ // Add Local variables tab
+ var localVars = self.localVars = paramsTabFrame.addTab(
+ '{{ _('Local variables') }}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ localVars.addItem($('<div id=local_variables class="info"></div>'));
+
+ // Add DBMS messages tab
+ var dbmsMessages = self.dbmsMessages = paramsTabFrame.addTab(
+ '{{ _('Messages') }}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ dbmsMessages.addItem($('<div id=dbms_messages class="info"></div>'));
+
+ // Add function return results tab
+ var retResults = self.retResults = paramsTabFrame.addTab(
+ '{{ _('Results') }}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ retResults.addItem($('<div id=ret_results class="info"></div>'));
+
+ // Now create a second splitter to go inside the existing one.
+ var $topContainer = $('<div class="debugger top-container"></div>');
+ hSplitter.left().addItem($topContainer);
+
+ // Create the wcSplitter used by wcDocker to split the single panel.
+ var vSplitter = new wcSplitter(
+ $topContainer, panel,
+ wcDocker.ORIENTATION.HORIZONTAL
+ );
+
+ // Initialize this splitter with a layout in each pane.
+ vSplitter.initLayouts(wcDocker.LAYOUT.SIMPLE, wcDocker.LAYOUT.SIMPLE);
+
+ // Now create a tab widget and put that into one of the sub splits.
+ var $stack = $('<div class="full-container stack">');
+ vSplitter.bottom().addItem($stack);
+
+ var stackFrame = new wcTabFrame($stack, panel);
+ var stackTab = self.stackTab = stackFrame.addTab(
+ '{{ _('Stack pane') }}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ stackTab.addItem(
+ $('<div id="stack_pane" class="full-container-pane info"></div>'));
+
+ // By default, the splitter splits down the middle, we split the main panel by 80%.
+ vSplitter.pos(0.75);
+
+ // Now create a tab widget and put that into one of the sub splits.
+ var editor_pane = $('<div id="stack_pane" class="full-container-pane info"></div>');
+ var code_editor_area = $('<textarea id="debugger-editor-textarea"></textarea>').append(editor_pane);
+ vSplitter.top().addItem(code_editor_area);
+
+ // To show the line-number and set breakpoint marker details by user.
+ var editor = self.editor = CodeMirror.fromTextArea(
+ code_editor_area.get(0), {
+ lineNumbers: true,
+ gutters: ["note-gutter", "CodeMirror-linenumbers", "breakpoints"],
+ mode: "text/x-sql",
+ readOnly: true
+ });
+ });
+
+ // On loading the docker, register the callbacks
+ var onLoad = function() {
+ self.docker.finishLoading(100);
+ self.docker.off(wcDocker.EVENT.LOADED);
+ // Register the callback when user set/clear the breakpoint on gutter area.
+ self.editor.on("gutterClick", self.onBreakPoint.bind(self), self);
+ };
+
+ self.docker.startLoading('{{ _('Loading...') }}');
+ self.docker.on(wcDocker.EVENT.LOADED, onLoad);
+
+ self.main_panel = self.docker.addPanel(
+ 'code', wcDocker.DOCK.TOP, null, {h: '100%', w: '100%'}
+ );
+
+ // Create the toolbar view for debugging the function
+ this.toolbarView = new DebuggerToolbarView();
+ },
+
+ // Register the panel with new debugger docker instance.
+ registerPanel: function(name, title, width, height, onInit) {
+ var self = this;
+
+ this.docker.registerPanelType(name, {
+ title: title,
+ isPrivate: true,
+ onCreate: function(panel) {
+ self.panels[name] = panel;
+ panel.initSize(width, height);
+ if (!title)
+ panel.title(false);
+ else
+ panel.title(title);
+ panel.closeable(false);
+ panel.layout().addItem(
+ $('<div>', {'class': 'pg-debugger-panel'})
+ );
+ if (onInit) {
+ onInit.apply(self, [panel]);
+ }
+ }
+ });
+ }
+ });
+
+ pgTools.DirectDebug = new DirectDebug();
+
+ return pgTools.DirectDebug;
+});
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql
new file mode 100755
index 0000000..04c0953
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql
@@ -0,0 +1,115 @@
+{### Create executer function for edb spl function debugging ###}
+{% set inside_loop = {'value': False} %}
+
+{% if lan_name == 'edbspl' %}
+{% set useAnonymousBlock = "true" %}
+{% if not is_func %}
+ {% set str_statement = "\tEXEC " ~ func_name %}
+{% elif ret_type == 'void' %}
+ {% set str_statement = "\tPERFORM " ~ func_name %}
+{% else %}
+ {% set resultVar = "v_retVal" %}
+ {% set str_statement = "\t" ~ resultVar ~ " := " ~ func_name %}
+ {% set str_declare = str_declare ~ "\t" ~ resultVar ~ " " ~ ret_type ~ ";\n" %}
+ {% set str_result = "\tDBMS_OUTPUT.PUT_LINE(E'\\n\\nResult:\\n--------\\n' || " ~ resultVar ~ "::text || E'\\n\\nNOTE: This is the result generated during the function execution by the debugger.\\n');\n" %}
+{% endif %}
+
+{% else %}
+{% if ret_type == 'record' %}
+ {% set str_statement = "\tSELECT " ~ func_name %}
+{% else %}
+ {% set str_statement = "\tSELECT * FROM " ~ func_name %}
+{% endif %}
+
+{% endif %}
+
+
+{% set firstProceesed = "false" %}
+{% set input_value_index = 0 %}
+
+{% if arg_type|length > 0 %}
+{% set str_statement = str_statement ~ "(" %}
+
+{% for arg_mode in args_mode %}
+.... ARG_MODE: .... {{ arg_mode }}... Index.... {{ loop.index }}....
+{% if useAnonymousBlock == "true" and (arg_mode == 'o' or arg_mode == 'b') %}
+{% set strParam = "p_param" ~ (loop.index - 1) %}
+{% set str_declare = str_declare ~ "\t" ~ strParam ~ " " ~ arg_type[loop.index - 1] %}
+{% if arg_mode == 'b' %}
+{### TODO: to check for Null parameters received from client ###}
+{% set str_declare = str_declare ~ " := " ~ strParam ~ " " ~ data[input_value_index]['value'] ~ "::" ~ data[input_value_index]['type'] %}
+{% set input_value_index = input_value_index + 1 %}
+{% endif %}
+{% set str_declare = str_declare ~ ";\n" %}
+
+{% if firstProceesed == "true" %}
+{% set str_statement = str_statement ~ ", " %}
+{% endif %}
+{% set firstProceesed = "true" %}
+{% set str_statement = str_statement ~ strParam %}
+
+
+
+
+
+{% elif arg_mode != 'o' %}
+{% if firstProceesed == "true" %}
+{% set str_statement = str_statement ~ ", " %}
+{% endif %}
+{% set firstProceesed = "true" %}
+
+{% if arg_mode == 'v' %}
+{% set str_statement = str_statement ~ "VARIADIC " %}
+{% endif %}
+
+{### TODO: to check for Null parameters received from client ###}
+
+{% set str_statement = str_statement ~ data[input_value_index]['value'] ~ "::" ~ data[input_value_index]['type'] %}
+{% set input_value_index = input_value_index + 1 %}
+
+
+{% endif %}
+
+{% if loop.last %}
+{% set str_statement = str_statement ~ ")" %}
+{% set strQuery = str_statement %}
+{% if useAnonymousBlock == "true" %}
+{% set strQuery = "DECLARE\n" ~ str_declare ~ "BEGIN\n" ~ str_statement ~ ";\n" ~ str_result ~ "END;" %}
+{% endif %}
+
+{{ strQuery }}
+{% if inside_loop.update({'value': True}) %} {% endif %}
+{% endif %}
+
+{% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+{% elif not is_func and lan_name == 'edbspl' %}
+{% set strQuery = str_statement %}
+{% if useAnonymousBlock == "true" %}
+{% set strQuery = "DECLARE\n" ~ str_declare ~ "BEGIN\n" ~ str_statement ~ ";\n" ~ str_result ~ "END;" %}
+{% endif %}
+{% else %}
+{% set strQuery = str_statement ~ "()" %}
+{% if useAnonymousBlock == "true" %}
+{% set strQuery = "DECLARE\n" ~ str_declare ~ "BEGIN\n" ~ str_statement ~ ";\n" ~ str_result ~ "END;" %}
+{% endif %}
+{% endif %}
+
+
+
+
+{### Return final query formed with above condition ###}
+{% if not inside_loop.value %}
+{{ strQuery }}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql
new file mode 100755
index 0000000..f4201c3
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql
@@ -0,0 +1,20 @@
+{### Create executer function for plpgsql function debugging ###}
+{% if is_func == 'False' %}
+ EXEC {{ func_name }} (
+{% elif ret_type == 'record' %}
+ SELECT {{ func_name }} (
+{% else %}
+ SELECT * FROM {{ func_name }} (
+{% endif %}
+{% if data %}
+{% for dict_item in data %}
+{% if 'type' in dict_item and 'value' in dict_item %}
+{% if dict_item['type'] == 'text' %}
+{{ dict_item['value']|qtLiteral }}::{{ dict_item['type'] }}{% if not loop.last %}, {% endif %}
+{% else %}
+{% if '[]' in dict_item['type'] %} ARRAY{{ dict_item['value'] }}::{{ dict_item['type'] }} {% else %} {{ dict_item['value'] }}::{{ dict_item['type'] }} {% endif %} {% if not loop.last %}, {% endif %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+)
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/get_function_debug_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/get_function_debug_info.sql
new file mode 100755
index 0000000..30e47e5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/get_function_debug_info.sql
@@ -0,0 +1,66 @@
+{### To fetch debug function information ###}
+SELECT
+ p.proname AS name, p.prosrc, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proallargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proallargtypes, 1)) AS s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proargtypes, 1)) AS s(i)), ',')
+ END AS proargtypenames,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proallargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')
+ END AS proargtypes,
+ pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,
+ pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,
+
+ {% if is_ppas_database %}
+ CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,
+ CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,
+ CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,
+ CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,
+ CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,
+ NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,
+ {%else%}
+ 0 AS pkg,
+ '' AS pkgname,
+ 0 AS pkgconsoid,
+ n.oid AS schema,
+ n.nspname AS schemaname,
+ true AS isfunc,
+ {%endif%}
+ pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,
+
+ {% if hasFeatureFunctionDefaults %}
+ pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,
+ p.pronargdefaults
+ {%else%}
+ '' AS proargdefaults, 0 AS pronargdefaults
+ {%endif%}
+ FROM
+ pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
+ LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid
+ LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid
+
+ {% if is_ppas_database %}
+ LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid
+ {% endif %}
+
+ {% if fid %}
+ WHERE p.oid = {{fid}}::int;
+ {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/Backend_running.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/Backend_running.sql
new file mode 100755
index 0000000..c5770d0
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/Backend_running.sql
@@ -0,0 +1,4 @@
+{### Check backend target is running or not for debugging ###}
+{% if backend_pid %}
+ SELECT COUNT(*) FROM (SELECT pg_catalog.pg_stat_get_backend_idset() AS bid) AS s WHERE pg_catalog.pg_stat_get_backend_pid(s.bid) = {{ conn|qtIdent(backend_pid) }}::integer;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/abort_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/abort_target.sql
new file mode 100755
index 0000000..8f89ae3
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/abort_target.sql
@@ -0,0 +1,4 @@
+{### Abort the target for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_abort_target({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_edb.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_edb.sql
new file mode 100755
index 0000000..0129462
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_edb.sql
@@ -0,0 +1,4 @@
+{### Add EDB breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_global_breakpoint({{session_id}}::int, {{package_oid}}::int, {{function_oid}}::OID, -1, {{target_oid}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_pg.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_pg.sql
new file mode 100755
index 0000000..f83e1cf
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_pg.sql
@@ -0,0 +1,4 @@
+{### Add PG breakpoint for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_global_breakpoint({{session_id}}, {{function_oid}}, -1, NULL)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/attach_to_port.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/attach_to_port.sql
new file mode 100755
index 0000000..bb74477
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/attach_to_port.sql
@@ -0,0 +1,4 @@
+{### Attach the target to port for debugging ###}
+{% if port %}
+ SELECT * FROM pldbg_attach_to_port({{port}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/clear_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/clear_breakpoint.sql
new file mode 100755
index 0000000..eecc674
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/clear_breakpoint.sql
@@ -0,0 +1,4 @@
+{### Clear breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_drop_breakpoint({{session_id}}::int, {{poid}}::OID, {{foid}}::OID, {{line_number}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/continue.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/continue.sql
new file mode 100755
index 0000000..071c53e
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/continue.sql
@@ -0,0 +1,11 @@
+{### Continue for debugging ###}
+{% if session_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber, pldbg_get_source({{session_id}}::INTEGER, p.pkg, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_continue({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/create_listener.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/create_listener.sql
new file mode 100755
index 0000000..1d47739
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/create_listener.sql
@@ -0,0 +1,2 @@
+{### Create listener for debugging ###}
+ SELECT * from pldbg_create_listener()
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_execute_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_execute_target.sql
new file mode 100755
index 0000000..5be078d
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_execute_target.sql
@@ -0,0 +1,4 @@
+{### Debug execute target for plpgsql function ###}
+{% if function_oid %}
+ SELECT plpgsql_oid_debug({{packge_oid}}::OID, {{function_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_init.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_init.sql
new file mode 100755
index 0000000..14ad3c5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_init.sql
@@ -0,0 +1,4 @@
+{### Debug Initialization for plpgsql function ###}
+{% if packge_init_oid %}
+ SELECT plpgsql_oid_debug({{packge_oid}}::OID, {{packge_init_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_execute_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_execute_target.sql
new file mode 100755
index 0000000..84afb34
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_execute_target.sql
@@ -0,0 +1,4 @@
+{### Debug execute target for EDB spl function ###}
+{% if function_oid %}
+ SELECT edb_oid_debug({{packge_oid}}::OID, {{function_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_init.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_init.sql
new file mode 100755
index 0000000..aa5b324
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_init.sql
@@ -0,0 +1,4 @@
+{### Debug Initialization for EDB spl function ###}
+{% if packge_init_oid %}
+ SELECT edb_oid_debug({{packge_oid}}::OID, {{packge_init_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/deposit_value.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/deposit_value.sql
new file mode 100755
index 0000000..86e9f4a
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/deposit_value.sql
@@ -0,0 +1,4 @@
+{### Change the variable value and submit during debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_deposit_value({{session_id}}::int, {{var_name|qtLiteral}}, {{line_number}}, {{val|qtLiteral}})
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_breakpoints.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_breakpoints.sql
new file mode 100755
index 0000000..19da5db
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_breakpoints.sql
@@ -0,0 +1,4 @@
+{### Get the breakpoint information for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_get_breakpoints({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_function_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_function_info.sql
new file mode 100755
index 0000000..7989da7
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_function_info.sql
@@ -0,0 +1,66 @@
+{### To fetch debug function information ###}
+SELECT
+ p.proname AS name, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proallargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proallargtypes, 1)) AS s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proargtypes, 1)) AS s(i)), ',')
+ END AS proargtypenames,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proallargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')
+ END AS proargtypes,
+ pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,
+ pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,
+
+ {% if is_ppas_database %}
+ CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,
+ CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,
+ CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,
+ CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,
+ CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,
+ NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,
+ {%else%}
+ 0 AS pkg,
+ '' AS pkgname,
+ 0 AS pkgconsoid,
+ n.oid AS schema,
+ n.nspname AS schemaname,
+ true AS isfunc,
+ {%endif%}
+ pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,
+
+ {% if hasFeatureFunctionDefaults %}
+ pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,
+ p.pronargdefaults
+ {%else%}
+ '' AS proargdefaults, 0 AS pronargdefaults
+ {%endif%}
+ FROM
+ pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
+ LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid
+ LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid
+
+ {% if is_ppas_database %}
+ LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid
+ {% endif %}
+
+ {% if fid %}
+ WHERE p.oid = {{fid}}::int;
+ {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_stack_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_stack_info.sql
new file mode 100755
index 0000000..05034c9
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_stack_info.sql
@@ -0,0 +1,4 @@
+{### Get the stack information for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_get_stack({{session_id}}::int) ORDER BY level
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_variables.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_variables.sql
new file mode 100755
index 0000000..47ebed5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_variables.sql
@@ -0,0 +1,8 @@
+{### Get the variables information for debugging ###}
+{% if session_id %}
+ SELECT
+ name, varClass, value,
+ pg_catalog.format_type(dtype, NULL) as dtype, isconst
+ FROM pldbg_get_variables({{session_id}}::int)
+ ORDER BY varClass
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/select_frame.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/select_frame.sql
new file mode 100755
index 0000000..e31e634
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/select_frame.sql
@@ -0,0 +1,12 @@
+{### select the frame to debug the function ###}
+{% if session_id and frame_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber,
+ CASE WHEN p.func <> 0 THEN pldbg_get_source({{session_id}}::INTEGER, p.func, p.pkg) ELSE '<No source available>' END AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_select_frame({{session_id}}::INTEGER, {{frame_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/set_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/set_breakpoint.sql
new file mode 100755
index 0000000..61c8b13
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/set_breakpoint.sql
@@ -0,0 +1,4 @@
+{### Set the breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_breakpoint({{session_id}}::int ,{{poid}}::OID, {{foid}}::OID, {{line_number}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_into.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_into.sql
new file mode 100755
index 0000000..837287d
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_into.sql
@@ -0,0 +1,11 @@
+{### Step into function for debugging ###}
+{% if session_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber, pldbg_get_source({{session_id}}::INTEGER, p.pkg, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_step_into({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_over.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_over.sql
new file mode 100755
index 0000000..a5ced66
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_over.sql
@@ -0,0 +1,11 @@
+{### Step over function for debugging ###}
+{% if session_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber, pldbg_get_source({{session_id}}::INTEGER, p.pkg, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_step_over({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_breakpoint.sql
new file mode 100755
index 0000000..0271056
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_breakpoint.sql
@@ -0,0 +1,11 @@
+{### select the frame to debug the function ###}
+{% if session_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber, pldbg_get_source({{session_id}}::INTEGER, p.pkg, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_wait_for_breakpoint({{session_id}}::INTEGER) p;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_target.sql
new file mode 100755
index 0000000..c4f5634
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_target.sql
@@ -0,0 +1,4 @@
+{### Wait for the target for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_wait_for_target({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/Backend_running.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/Backend_running.sql
new file mode 100755
index 0000000..c5770d0
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/Backend_running.sql
@@ -0,0 +1,4 @@
+{### Check backend target is running or not for debugging ###}
+{% if backend_pid %}
+ SELECT COUNT(*) FROM (SELECT pg_catalog.pg_stat_get_backend_idset() AS bid) AS s WHERE pg_catalog.pg_stat_get_backend_pid(s.bid) = {{ conn|qtIdent(backend_pid) }}::integer;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/abort_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/abort_target.sql
new file mode 100755
index 0000000..8f89ae3
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/abort_target.sql
@@ -0,0 +1,4 @@
+{### Abort the target for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_abort_target({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_edb.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_edb.sql
new file mode 100755
index 0000000..0129462
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_edb.sql
@@ -0,0 +1,4 @@
+{### Add EDB breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_global_breakpoint({{session_id}}::int, {{package_oid}}::int, {{function_oid}}::OID, -1, {{target_oid}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_pg.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_pg.sql
new file mode 100755
index 0000000..f83e1cf
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_pg.sql
@@ -0,0 +1,4 @@
+{### Add PG breakpoint for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_global_breakpoint({{session_id}}, {{function_oid}}, -1, NULL)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/attach_to_port.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/attach_to_port.sql
new file mode 100755
index 0000000..bb74477
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/attach_to_port.sql
@@ -0,0 +1,4 @@
+{### Attach the target to port for debugging ###}
+{% if port %}
+ SELECT * FROM pldbg_attach_to_port({{port}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/clear_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/clear_breakpoint.sql
new file mode 100755
index 0000000..c3d83f8
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/clear_breakpoint.sql
@@ -0,0 +1,4 @@
+{### Clear breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_drop_breakpoint({{session_id}}::int, {{foid}}::OID, {{line_number}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/continue.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/continue.sql
new file mode 100755
index 0000000..85dcd27
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/continue.sql
@@ -0,0 +1,11 @@
+{### Continue for debugging ###}
+{% if session_id %}
+ SELECT
+ p.func, p.targetName, p.linenumber,
+ pldbg_get_source({{session_id}}::INTEGER, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_continue({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/create_listener.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/create_listener.sql
new file mode 100755
index 0000000..1d47739
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/create_listener.sql
@@ -0,0 +1,2 @@
+{### Create listener for debugging ###}
+ SELECT * from pldbg_create_listener()
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_execute_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_execute_target.sql
new file mode 100755
index 0000000..9bdb8ef
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_execute_target.sql
@@ -0,0 +1,4 @@
+{### Debug execute target for plpgsql function ###}
+{% if function_oid %}
+ SELECT plpgsql_oid_debug({{function_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_init.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_init.sql
new file mode 100755
index 0000000..e56ad98
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_init.sql
@@ -0,0 +1,4 @@
+{### Debug Initialization for plpgsql function ###}
+{% if packge_init_oid %}
+ SELECT plpgsql_oid_debug({{packge_init_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_execute_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_execute_target.sql
new file mode 100755
index 0000000..7cb382f
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_execute_target.sql
@@ -0,0 +1,4 @@
+{### Debug execute target for EDB spl function ###}
+{% if function_oid %}
+ SELECT edb_oid_debug({{function_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_init.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_init.sql
new file mode 100755
index 0000000..d16b1b1
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_init.sql
@@ -0,0 +1,4 @@
+{### Debug Initialization for EDB spl function ###}
+{% if packge_init_oid %}
+ SELECT edb_oid_debug({{packge_init_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/deposit_value.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/deposit_value.sql
new file mode 100755
index 0000000..6ee3259
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/deposit_value.sql
@@ -0,0 +1,4 @@
+{### Change the variable value and submit during debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_deposit_value({{session_id}}::int, {{var_name|qtLiteral }}, {{line_number}}, {{val|qtLiteral}})
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_breakpoints.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_breakpoints.sql
new file mode 100755
index 0000000..19da5db
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_breakpoints.sql
@@ -0,0 +1,4 @@
+{### Get the breakpoint information for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_get_breakpoints({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_function_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_function_info.sql
new file mode 100755
index 0000000..7989da7
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_function_info.sql
@@ -0,0 +1,66 @@
+{### To fetch debug function information ###}
+SELECT
+ p.proname AS name, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proallargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proallargtypes, 1)) AS s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proargtypes, 1)) AS s(i)), ',')
+ END AS proargtypenames,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proallargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')
+ END AS proargtypes,
+ pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,
+ pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,
+
+ {% if is_ppas_database %}
+ CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,
+ CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,
+ CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,
+ CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,
+ CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,
+ NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,
+ {%else%}
+ 0 AS pkg,
+ '' AS pkgname,
+ 0 AS pkgconsoid,
+ n.oid AS schema,
+ n.nspname AS schemaname,
+ true AS isfunc,
+ {%endif%}
+ pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,
+
+ {% if hasFeatureFunctionDefaults %}
+ pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,
+ p.pronargdefaults
+ {%else%}
+ '' AS proargdefaults, 0 AS pronargdefaults
+ {%endif%}
+ FROM
+ pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
+ LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid
+ LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid
+
+ {% if is_ppas_database %}
+ LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid
+ {% endif %}
+
+ {% if fid %}
+ WHERE p.oid = {{fid}}::int;
+ {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_stack_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_stack_info.sql
new file mode 100755
index 0000000..05034c9
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_stack_info.sql
@@ -0,0 +1,4 @@
+{### Get the stack information for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_get_stack({{session_id}}::int) ORDER BY level
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_variables.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_variables.sql
new file mode 100755
index 0000000..47ebed5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_variables.sql
@@ -0,0 +1,8 @@
+{### Get the variables information for debugging ###}
+{% if session_id %}
+ SELECT
+ name, varClass, value,
+ pg_catalog.format_type(dtype, NULL) as dtype, isconst
+ FROM pldbg_get_variables({{session_id}}::int)
+ ORDER BY varClass
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/select_frame.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/select_frame.sql
new file mode 100755
index 0000000..4a18839
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/select_frame.sql
@@ -0,0 +1,11 @@
+{### select the frame to debug the function ###}
+{% if session_id %}
+ SELECT
+ p.func AS func, p.targetName AS targetName, p.linenumber AS linenumber,
+ CASE WHEN p.func <> 0 THEN pldbg_get_source({{session_id}}::INTEGER, p.func) ELSE '<No source available>' END AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_select_frame({{session_id}}::INTEGER, {{frame_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/set_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/set_breakpoint.sql
new file mode 100755
index 0000000..a56d6f6
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/set_breakpoint.sql
@@ -0,0 +1,4 @@
+{### Set the breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_breakpoint({{session_id}}::int, {{foid}}::OID,{{line_number}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_into.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_into.sql
new file mode 100755
index 0000000..cd05240
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_into.sql
@@ -0,0 +1,11 @@
+{### Step into function for debugging ###}
+{% if session_id %}
+ SELECT
+ p.func, p.targetName, p.linenumber,
+ pldbg_get_source({{session_id}}::INTEGER, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_step_into({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_over.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_over.sql
new file mode 100755
index 0000000..ed3153d
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_over.sql
@@ -0,0 +1,11 @@
+{### Step over function for debugging ###}
+{% if session_id %}
+ SELECT
+ p.func, p.targetName, p.linenumber,
+ pldbg_get_source({{session_id}}::INTEGER, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_step_over({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_breakpoint.sql
new file mode 100755
index 0000000..96efcb8
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_breakpoint.sql
@@ -0,0 +1,12 @@
+{### select the frame to debug the function ###}
+{% if session_id %}
+ SELECT
+ p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber,
+ pldbg_get_source({{session_id}}::INTEGER, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_wait_for_breakpoint({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_target.sql
new file mode 100755
index 0000000..c4f5634
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_target.sql
@@ -0,0 +1,4 @@
+{### Wait for the target for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_wait_for_target({{session_id}}::int)
+{% endif %}
\ No newline at end of file
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-04-05 16:29 ` Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Dave Page @ 2016-04-05 16:29 UTC (permalink / raw)
To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers
Hi
On Tue, Apr 5, 2016 at 12:40 PM, Neel Patel <[email protected]> wrote:
> Hi,
>
> Please find attached debugger v1 patch. To test this patch we need to apply
> the "function" module patch submitted by Khushboo.
>
> For the debugger functionality, direct and indirect debugging is working as
> per pgadmin3 except below TODOs.
>
> Below are the TODOs which will be submitted in next patch along with the
> fixed review comments.
>
> For the direct debugging, 'NULL' and 'Expression' column value should be
> validated before pressing the debug button.
> User can deposit the value of input arguments and local variable but need to
> be
> validated against respective data types.
> Currently we have tested for plpgsql functions only, We need to test against
> EDB SPL functions and trigger functions.
> For the direct debugging, once the execution is completed, only
> "Continue/Restart" button will be enabled and user should allow to restart
> the debugging with same function and previous values.
> Values entered by the user in input dialog during direct debugging should be
> saved.
>
>
> Currently we have tested this patch with PostgreSQL v9.5.
>
> Do review it and let us know for the comments.
I just get a blank tab when I try to debug something. I do see the
params dialogue, but then nothing. Note:
- I have Akshay's Query Tool patch applied, and am running from git master
- PostgreSQL 9.4, with the debugger plugin in the public schema.
- Various functions in the pem schema tested, both with and without parameters.
- Both direct and indirect debugging have the same issue.
- No errors seen in the console, except when closing the debugger tab:
2016-04-05 17:27:37,720: INFO werkzeug: 127.0.0.1 - - [05/Apr/2016
17:27:37] "GET /debugger/close/8569717 HTTP/1.1" 500 -
Traceback (most recent call last):
File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/dpage/git/pgadmin4-demo/web/pgadmin/tools/debugger/__init__.py",
line 397, in close
session_id=obj['session_id'])
KeyError: 'session_id'
Thanks.
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
@ 2016-04-06 06:08 ` Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-06 06:08 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
We will fix the error "KeyError: 'session_id'" but regarding the blank tab,
we have tried with PostgreSQL 9.4 with both the version of python (2.7 &
3.4) but we are not able to reproduce the issue.
Below are some of the questions to debug the issue.
- Is it only reproduce on PostgreSQL 9.4 ? Is it working on PostgreSQL
9.5 ?
- Is it possible to know the debugger version ?
- The issue is reproduce on specific functions ? If yes, can you please
give definition of the function ?
- Are you able to see toolbar buttons ? Or nothing on the screen ? Only
blank panel ?
- Can you please share request/response of last request URL sent to
server in browser ?
Thanks,
Neel Patel
On Tue, Apr 5, 2016 at 9:59 PM, Dave Page <[email protected]> wrote:
> Hi
>
> On Tue, Apr 5, 2016 at 12:40 PM, Neel Patel <[email protected]>
> wrote:
> > Hi,
> >
> > Please find attached debugger v1 patch. To test this patch we need to
> apply
> > the "function" module patch submitted by Khushboo.
> >
> > For the debugger functionality, direct and indirect debugging is working
> as
> > per pgadmin3 except below TODOs.
> >
> > Below are the TODOs which will be submitted in next patch along with the
> > fixed review comments.
> >
> > For the direct debugging, 'NULL' and 'Expression' column value should be
> > validated before pressing the debug button.
> > User can deposit the value of input arguments and local variable but
> need to
> > be
> > validated against respective data types.
> > Currently we have tested for plpgsql functions only, We need to test
> against
> > EDB SPL functions and trigger functions.
> > For the direct debugging, once the execution is completed, only
> > "Continue/Restart" button will be enabled and user should allow to
> restart
> > the debugging with same function and previous values.
> > Values entered by the user in input dialog during direct debugging
> should be
> > saved.
> >
> >
> > Currently we have tested this patch with PostgreSQL v9.5.
> >
> > Do review it and let us know for the comments.
>
> I just get a blank tab when I try to debug something. I do see the
> params dialogue, but then nothing. Note:
>
> - I have Akshay's Query Tool patch applied, and am running from git master
>
> - PostgreSQL 9.4, with the debugger plugin in the public schema.
>
> - Various functions in the pem schema tested, both with and without
> parameters.
>
> - Both direct and indirect debugging have the same issue.
>
> - No errors seen in the console, except when closing the debugger tab:
>
> 2016-04-05 17:27:37,720: INFO werkzeug: 127.0.0.1 - - [05/Apr/2016
> 17:27:37] "GET /debugger/close/8569717 HTTP/1.1" 500 -
> Traceback (most recent call last):
> File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1836, in __call__
> return self.wsgi_app(environ, start_response)
> File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1820, in wsgi_app
> response = self.make_response(self.handle_exception(e))
> File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1403, in handle_exception
> reraise(exc_type, exc_value, tb)
> File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1817, in wsgi_app
> response = self.full_dispatch_request()
> File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1477, in full_dispatch_request
> rv = self.handle_user_exception(e)
> File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1381, in handle_user_exception
> reraise(exc_type, exc_value, tb)
> File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1475, in full_dispatch_request
> rv = self.dispatch_request()
> File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 1461, in dispatch_request
> return self.view_functions[rule.endpoint](**req.view_args)
> File
> "/Users/dpage/git/pgadmin4-demo/web/pgadmin/tools/debugger/__init__.py",
> line 397, in close
> session_id=obj['session_id'])
> KeyError: 'session_id'
>
>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-04-06 06:20 ` Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-06 06:20 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
One more additional information.
Follow the below steps and send us pgAdmin4.log file.
- Browse till function which we want to debug. Do not click on the
"Debug" context menu.
- Stop the server
- Enable the "Debug" option in the config file under "web" folder.
- Start the server.
- Now click on the "Debug" context menu and add requirement input. After
clicking on the debug button on the input dialog send us the pgAdmin4.log
file.
Thanks,
Neel Patel
On Wed, Apr 6, 2016 at 11:38 AM, Neel Patel <[email protected]>
wrote:
> Hi Dave,
>
> We will fix the error "KeyError: 'session_id'" but regarding the blank
> tab, we have tried with PostgreSQL 9.4 with both the version of python (2.7
> & 3.4) but we are not able to reproduce the issue.
>
> Below are some of the questions to debug the issue.
>
> - Is it only reproduce on PostgreSQL 9.4 ? Is it working on PostgreSQL
> 9.5 ?
> - Is it possible to know the debugger version ?
> - The issue is reproduce on specific functions ? If yes, can you
> please give definition of the function ?
> - Are you able to see toolbar buttons ? Or nothing on the screen ?
> Only blank panel ?
> - Can you please share request/response of last request URL sent to
> server in browser ?
>
> Thanks,
> Neel Patel
>
> On Tue, Apr 5, 2016 at 9:59 PM, Dave Page <[email protected]> wrote:
>
>> Hi
>>
>> On Tue, Apr 5, 2016 at 12:40 PM, Neel Patel <[email protected]>
>> wrote:
>> > Hi,
>> >
>> > Please find attached debugger v1 patch. To test this patch we need to
>> apply
>> > the "function" module patch submitted by Khushboo.
>> >
>> > For the debugger functionality, direct and indirect debugging is
>> working as
>> > per pgadmin3 except below TODOs.
>> >
>> > Below are the TODOs which will be submitted in next patch along with the
>> > fixed review comments.
>> >
>> > For the direct debugging, 'NULL' and 'Expression' column value should be
>> > validated before pressing the debug button.
>> > User can deposit the value of input arguments and local variable but
>> need to
>> > be
>> > validated against respective data types.
>> > Currently we have tested for plpgsql functions only, We need to test
>> against
>> > EDB SPL functions and trigger functions.
>> > For the direct debugging, once the execution is completed, only
>> > "Continue/Restart" button will be enabled and user should allow to
>> restart
>> > the debugging with same function and previous values.
>> > Values entered by the user in input dialog during direct debugging
>> should be
>> > saved.
>> >
>> >
>> > Currently we have tested this patch with PostgreSQL v9.5.
>> >
>> > Do review it and let us know for the comments.
>>
>> I just get a blank tab when I try to debug something. I do see the
>> params dialogue, but then nothing. Note:
>>
>> - I have Akshay's Query Tool patch applied, and am running from git master
>>
>> - PostgreSQL 9.4, with the debugger plugin in the public schema.
>>
>> - Various functions in the pem schema tested, both with and without
>> parameters.
>>
>> - Both direct and indirect debugging have the same issue.
>>
>> - No errors seen in the console, except when closing the debugger tab:
>>
>> 2016-04-05 17:27:37,720: INFO werkzeug: 127.0.0.1 - - [05/Apr/2016
>> 17:27:37] "GET /debugger/close/8569717 HTTP/1.1" 500 -
>> Traceback (most recent call last):
>> File
>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>> line 1836, in __call__
>> return self.wsgi_app(environ, start_response)
>> File
>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>> line 1820, in wsgi_app
>> response = self.make_response(self.handle_exception(e))
>> File
>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>> line 1403, in handle_exception
>> reraise(exc_type, exc_value, tb)
>> File
>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>> line 1817, in wsgi_app
>> response = self.full_dispatch_request()
>> File
>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>> line 1477, in full_dispatch_request
>> rv = self.handle_user_exception(e)
>> File
>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>> line 1381, in handle_user_exception
>> reraise(exc_type, exc_value, tb)
>> File
>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>> line 1475, in full_dispatch_request
>> rv = self.dispatch_request()
>> File
>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>> line 1461, in dispatch_request
>> return self.view_functions[rule.endpoint](**req.view_args)
>> File
>> "/Users/dpage/git/pgadmin4-demo/web/pgadmin/tools/debugger/__init__.py",
>> line 397, in close
>> session_id=obj['session_id'])
>> KeyError: 'session_id'
>>
>>
>> Thanks.
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>
>
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-04-06 06:37 ` Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-06 06:37 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
Updated above information.
We want this pgadmin4 log information to debug the debugger blank tab open
issue.
Thanks,
Neel Patel
On Wed, Apr 6, 2016 at 11:50 AM, Neel Patel <[email protected]>
wrote:
> One more additional information.
>
> Follow the below steps and send us pgAdmin4.log file.
>
> - Browse till function which we want to debug. Do not click on the
> "Debug" context menu.
> - Stop the server
>
> Stop the web server.
>
> - Enable the "Debug" option in the config file under "web" folder.
>
> Set the Log setting to "Debug" and save the log to files and
send the updated debug log information.
>
> - Start the server.
>
> Start the Web Server.
>
> - Now click on the "Debug" context menu and add requirement input.
> After clicking on the debug button on the input dialog send us the
> pgAdmin4.log file.
>
> Thanks,
> Neel Patel
>
> On Wed, Apr 6, 2016 at 11:38 AM, Neel Patel <[email protected]>
> wrote:
>
>> Hi Dave,
>>
>> We will fix the error "KeyError: 'session_id'" but regarding the blank
>> tab, we have tried with PostgreSQL 9.4 with both the version of python (2.7
>> & 3.4) but we are not able to reproduce the issue.
>>
>> Below are some of the questions to debug the issue.
>>
>> - Is it only reproduce on PostgreSQL 9.4 ? Is it working on
>> PostgreSQL 9.5 ?
>> - Is it possible to know the debugger version ?
>> - The issue is reproduce on specific functions ? If yes, can you
>> please give definition of the function ?
>> - Are you able to see toolbar buttons ? Or nothing on the screen ?
>> Only blank panel ?
>> - Can you please share request/response of last request URL sent to
>> server in browser ?
>>
>> Thanks,
>> Neel Patel
>>
>> On Tue, Apr 5, 2016 at 9:59 PM, Dave Page <[email protected]> wrote:
>>
>>> Hi
>>>
>>> On Tue, Apr 5, 2016 at 12:40 PM, Neel Patel <[email protected]>
>>> wrote:
>>> > Hi,
>>> >
>>> > Please find attached debugger v1 patch. To test this patch we need to
>>> apply
>>> > the "function" module patch submitted by Khushboo.
>>> >
>>> > For the debugger functionality, direct and indirect debugging is
>>> working as
>>> > per pgadmin3 except below TODOs.
>>> >
>>> > Below are the TODOs which will be submitted in next patch along with
>>> the
>>> > fixed review comments.
>>> >
>>> > For the direct debugging, 'NULL' and 'Expression' column value should
>>> be
>>> > validated before pressing the debug button.
>>> > User can deposit the value of input arguments and local variable but
>>> need to
>>> > be
>>> > validated against respective data types.
>>> > Currently we have tested for plpgsql functions only, We need to test
>>> against
>>> > EDB SPL functions and trigger functions.
>>> > For the direct debugging, once the execution is completed, only
>>> > "Continue/Restart" button will be enabled and user should allow to
>>> restart
>>> > the debugging with same function and previous values.
>>> > Values entered by the user in input dialog during direct debugging
>>> should be
>>> > saved.
>>> >
>>> >
>>> > Currently we have tested this patch with PostgreSQL v9.5.
>>> >
>>> > Do review it and let us know for the comments.
>>>
>>> I just get a blank tab when I try to debug something. I do see the
>>> params dialogue, but then nothing. Note:
>>>
>>> - I have Akshay's Query Tool patch applied, and am running from git
>>> master
>>>
>>> - PostgreSQL 9.4, with the debugger plugin in the public schema.
>>>
>>> - Various functions in the pem schema tested, both with and without
>>> parameters.
>>>
>>> - Both direct and indirect debugging have the same issue.
>>>
>>> - No errors seen in the console, except when closing the debugger tab:
>>>
>>> 2016-04-05 17:27:37,720: INFO werkzeug: 127.0.0.1 - - [05/Apr/2016
>>> 17:27:37] "GET /debugger/close/8569717 HTTP/1.1" 500 -
>>> Traceback (most recent call last):
>>> File
>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>> line 1836, in __call__
>>> return self.wsgi_app(environ, start_response)
>>> File
>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>> line 1820, in wsgi_app
>>> response = self.make_response(self.handle_exception(e))
>>> File
>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>> line 1403, in handle_exception
>>> reraise(exc_type, exc_value, tb)
>>> File
>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>> line 1817, in wsgi_app
>>> response = self.full_dispatch_request()
>>> File
>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>> line 1477, in full_dispatch_request
>>> rv = self.handle_user_exception(e)
>>> File
>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>> line 1381, in handle_user_exception
>>> reraise(exc_type, exc_value, tb)
>>> File
>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>> line 1475, in full_dispatch_request
>>> rv = self.dispatch_request()
>>> File
>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>> line 1461, in dispatch_request
>>> return self.view_functions[rule.endpoint](**req.view_args)
>>> File
>>> "/Users/dpage/git/pgadmin4-demo/web/pgadmin/tools/debugger/__init__.py",
>>> line 397, in close
>>> session_id=obj['session_id'])
>>> KeyError: 'session_id'
>>>
>>>
>>> Thanks.
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
>>
>
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-04-06 07:57 ` Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-06 07:57 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
I got the root cause. Issue will reproduce when we applied Akshay's query
tool patch.
Issue was once the panel was initialized, HTML file was not getting loaded
in to the panel due to the change in frame.js.
Both query tool and debugger patch file contains the change in "frame.js"
file. Once you applied the query tool patch and after that debugger then
debugger patch should fail but i don't know why you do not get any error.
*Solution:-*
- Open "frame.js" file and add the below line in "setTimeout" function
which is required by debugger.
pgBrowser.Events.trigger('pgadmin-browser:frame:urlloaded:' + that.name,
frame, that.url, self);
Do let us know if you still face the same issue.
Thanks,
Neel Patel
On Wed, Apr 6, 2016 at 12:07 PM, Neel Patel <[email protected]>
wrote:
> Hi Dave,
>
> Updated above information.
> We want this pgadmin4 log information to debug the debugger blank tab open
> issue.
>
> Thanks,
> Neel Patel
>
> On Wed, Apr 6, 2016 at 11:50 AM, Neel Patel <[email protected]>
> wrote:
>
>> One more additional information.
>>
>> Follow the below steps and send us pgAdmin4.log file.
>>
>> - Browse till function which we want to debug. Do not click on the
>> "Debug" context menu.
>> - Stop the server
>>
>> Stop the web server.
>
>>
>> - Enable the "Debug" option in the config file under "web" folder.
>>
>> Set the Log setting to "Debug" and save the log to files and
> send the updated debug log information.
>
>>
>> - Start the server.
>>
>> Start the Web Server.
>
>>
>> - Now click on the "Debug" context menu and add requirement input.
>> After clicking on the debug button on the input dialog send us the
>> pgAdmin4.log file.
>>
>> Thanks,
>> Neel Patel
>>
>> On Wed, Apr 6, 2016 at 11:38 AM, Neel Patel <[email protected]>
>> wrote:
>>
>>> Hi Dave,
>>>
>>> We will fix the error "KeyError: 'session_id'" but regarding the blank
>>> tab, we have tried with PostgreSQL 9.4 with both the version of python (2.7
>>> & 3.4) but we are not able to reproduce the issue.
>>>
>>> Below are some of the questions to debug the issue.
>>>
>>> - Is it only reproduce on PostgreSQL 9.4 ? Is it working on
>>> PostgreSQL 9.5 ?
>>> - Is it possible to know the debugger version ?
>>> - The issue is reproduce on specific functions ? If yes, can you
>>> please give definition of the function ?
>>> - Are you able to see toolbar buttons ? Or nothing on the screen ?
>>> Only blank panel ?
>>> - Can you please share request/response of last request URL sent to
>>> server in browser ?
>>>
>>> Thanks,
>>> Neel Patel
>>>
>>> On Tue, Apr 5, 2016 at 9:59 PM, Dave Page <[email protected]> wrote:
>>>
>>>> Hi
>>>>
>>>> On Tue, Apr 5, 2016 at 12:40 PM, Neel Patel <
>>>> [email protected]> wrote:
>>>> > Hi,
>>>> >
>>>> > Please find attached debugger v1 patch. To test this patch we need to
>>>> apply
>>>> > the "function" module patch submitted by Khushboo.
>>>> >
>>>> > For the debugger functionality, direct and indirect debugging is
>>>> working as
>>>> > per pgadmin3 except below TODOs.
>>>> >
>>>> > Below are the TODOs which will be submitted in next patch along with
>>>> the
>>>> > fixed review comments.
>>>> >
>>>> > For the direct debugging, 'NULL' and 'Expression' column value should
>>>> be
>>>> > validated before pressing the debug button.
>>>> > User can deposit the value of input arguments and local variable but
>>>> need to
>>>> > be
>>>> > validated against respective data types.
>>>> > Currently we have tested for plpgsql functions only, We need to test
>>>> against
>>>> > EDB SPL functions and trigger functions.
>>>> > For the direct debugging, once the execution is completed, only
>>>> > "Continue/Restart" button will be enabled and user should allow to
>>>> restart
>>>> > the debugging with same function and previous values.
>>>> > Values entered by the user in input dialog during direct debugging
>>>> should be
>>>> > saved.
>>>> >
>>>> >
>>>> > Currently we have tested this patch with PostgreSQL v9.5.
>>>> >
>>>> > Do review it and let us know for the comments.
>>>>
>>>> I just get a blank tab when I try to debug something. I do see the
>>>> params dialogue, but then nothing. Note:
>>>>
>>>> - I have Akshay's Query Tool patch applied, and am running from git
>>>> master
>>>>
>>>> - PostgreSQL 9.4, with the debugger plugin in the public schema.
>>>>
>>>> - Various functions in the pem schema tested, both with and without
>>>> parameters.
>>>>
>>>> - Both direct and indirect debugging have the same issue.
>>>>
>>>> - No errors seen in the console, except when closing the debugger tab:
>>>>
>>>> 2016-04-05 17:27:37,720: INFO werkzeug: 127.0.0.1 - - [05/Apr/2016
>>>> 17:27:37] "GET /debugger/close/8569717 HTTP/1.1" 500 -
>>>> Traceback (most recent call last):
>>>> File
>>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>>> line 1836, in __call__
>>>> return self.wsgi_app(environ, start_response)
>>>> File
>>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>>> line 1820, in wsgi_app
>>>> response = self.make_response(self.handle_exception(e))
>>>> File
>>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>>> line 1403, in handle_exception
>>>> reraise(exc_type, exc_value, tb)
>>>> File
>>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>>> line 1817, in wsgi_app
>>>> response = self.full_dispatch_request()
>>>> File
>>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>>> line 1477, in full_dispatch_request
>>>> rv = self.handle_user_exception(e)
>>>> File
>>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>>> line 1381, in handle_user_exception
>>>> reraise(exc_type, exc_value, tb)
>>>> File
>>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>>> line 1475, in full_dispatch_request
>>>> rv = self.dispatch_request()
>>>> File
>>>> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
>>>> line 1461, in dispatch_request
>>>> return self.view_functions[rule.endpoint](**req.view_args)
>>>> File
>>>> "/Users/dpage/git/pgadmin4-demo/web/pgadmin/tools/debugger/__init__.py",
>>>> line 397, in close
>>>> session_id=obj['session_id'])
>>>> KeyError: 'session_id'
>>>>
>>>>
>>>> Thanks.
>>>>
>>>> --
>>>> Dave Page
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>
>>>
>>
>
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-04-06 08:07 ` Dave Page <[email protected]>
2016-04-14 12:52 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Dave Page @ 2016-04-06 08:07 UTC (permalink / raw)
To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers
On Wed, Apr 6, 2016 at 8:57 AM, Neel Patel <[email protected]> wrote:
> Hi Dave,
>
> I got the root cause. Issue will reproduce when we applied Akshay's query
> tool patch.
> Issue was once the panel was initialized, HTML file was not getting loaded
> in to the panel due to the change in frame.js.
>
> Both query tool and debugger patch file contains the change in "frame.js"
> file. Once you applied the query tool patch and after that debugger then
> debugger patch should fail but i don't know why you do not get any error.
>
> Solution:-
>
> Open "frame.js" file and add the below line in "setTimeout" function which
> is required by debugger.
>
> pgBrowser.Events.trigger('pgadmin-browser:frame:urlloaded:'
> + that.name, frame, that.url, self);
>
> Do let us know if you still face the same issue.
Ahh, good catch - that got it :-)
Thanks!
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
@ 2016-04-14 12:52 ` Neel Patel <[email protected]>
2016-04-14 20:39 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-14 12:52 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi,
Please find attached v2 patch file of the debugger which fixes the below
issues which was not present in the first patch.
In this patch, we have added new table in sqlite database to store the
functions arguments value user has given during debugging.
After applying this patch, user needs to execute "setup.py" to create the
new table in pgadmin4.db file.
- In direct debugging, when user debug the function then arguments
values will be stored in the sqlite database so when user debug the same
function again then previous values will be filled in the user input dialog.
- Once the execution is completed then user will be able to do the debug
of the same function again by pressing the "Continue/Restart" button.
- User can debug the "procedure" which is supported in PPAS database.
- Replaced the "Glyphicon" with the "font-awesome" icons.
Below are the TODOs
- Validate the input arguments values changed by user while depositing
the value during debugging.
- Need to implement the code folding in the codemirror editor area.
- As per the Ashesh's suggestion, need to add debug logs information so
that we can get the state of the debug function. Also need to add "arrow"
next to breakpoint in the gutters as per the pgadmin3.
- Need to add "Debug package initializer" in the user input dialog for
the direct debugging.
- Last but not least "Review comments" :)
Below functionalities are implemented but testing are pending.
- Trigger functions need to test with the debugger.
- Functions are tested with data types (like text, integer etc.) but it
needs to be tested with all the data types for direct debugging.
- Functions/Procedures need to test with PPAS 9.2 and earlier version
where debugger version is different.
Can you please review the patch file and let us know the comments.
Thanks,
Neel Patel
On Wed, Apr 6, 2016 at 1:37 PM, Dave Page <[email protected]> wrote:
> On Wed, Apr 6, 2016 at 8:57 AM, Neel Patel <[email protected]>
> wrote:
> > Hi Dave,
> >
> > I got the root cause. Issue will reproduce when we applied Akshay's query
> > tool patch.
> > Issue was once the panel was initialized, HTML file was not getting
> loaded
> > in to the panel due to the change in frame.js.
> >
> > Both query tool and debugger patch file contains the change in "frame.js"
> > file. Once you applied the query tool patch and after that debugger then
> > debugger patch should fail but i don't know why you do not get any error.
> >
> > Solution:-
> >
> > Open "frame.js" file and add the below line in "setTimeout" function
> which
> > is required by debugger.
> >
> >
> pgBrowser.Events.trigger('pgadmin-browser:frame:urlloaded:'
> > + that.name, frame, that.url, self);
> >
> > Do let us know if you still face the same issue.
>
> Ahh, good catch - that got it :-)
>
> Thanks!
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] debugger_14_April_v2.patch (196.8K, 3-debugger_14_April_v2.patch)
download | inline diff:
diff --git a/web/config.py b/web/config.py
index 5f22c88..9cf2644 100644
--- a/web/config.py
+++ b/web/config.py
@@ -141,7 +141,7 @@ MAX_SESSION_IDLE_TIME = 60
# The schema version number for the configuration database
# DO NOT CHANGE UNLESS YOU ARE A PGADMIN DEVELOPER!!
-SETTINGS_SCHEMA_VERSION = 8
+SETTINGS_SCHEMA_VERSION = 9
# The default path to the SQLite database used to store user accounts and
# settings. This default places the file in the same directory as this
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index 253bd67..b79f085 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -148,3 +148,23 @@ class UserPreference(db.Model):
db.Integer, db.ForeignKey('user.id'), primary_key=True
)
value = db.Column(db.String(1024), nullable=False)
+
+class DebuggerFunctionArguments(db.Model):
+ """Define the debugger input function arguments."""
+ __tablename__ = 'debugger_function_arguments'
+ server_id = db.Column(db.Integer(), nullable=False, primary_key=True)
+ database_id = db.Column(db.Integer(), nullable=False, primary_key=True)
+ schema_id = db.Column(db.Integer(), nullable=False, primary_key=True)
+ function_id = db.Column(db.Integer(), nullable=False, primary_key=True)
+ arg_id = db.Column(db.Integer(), nullable=False, primary_key=True)
+ is_null = db.Column(db.Integer(),
+ db.CheckConstraint('is_null >= 0 AND is_null <= 1'),
+ nullable=False)
+ is_expression = db.Column(db.Integer(),
+ db.CheckConstraint('is_expression >= 0 AND is_expression <= 1'),
+ nullable=False)
+ use_default = db.Column(db.Integer(),
+ db.CheckConstraint('use_default >= 0 AND use_default <= 1'),
+ nullable=False)
+
+ value = db.Column(db.String(), nullable=True)
diff --git a/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js b/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
index e40898e..aa86853 100644
--- a/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
+++ b/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
@@ -564,6 +564,236 @@
editor: TextareaCellEditor
});
+
+ /**
+ * Custom header icon cell to add the icon in table header.
+ */
+ var CustomHeaderIconCell = Backgrid.Extension.CustomHeaderIconCell = Backgrid.HeaderCell.extend({
+ /** @property */
+ className: "header-icon-cell",
+ events: {
+ "click": "addHeaderIcon"
+ },
+ addHeaderIcon: function (e) {
+ self = this;
+ m = new (this.collection.model);
+ this.collection.add(m)
+ e.preventDefault();
+ },
+ render: function () {
+ this.$el.empty();
+ //this.$el.html("<i class='fa fa-plus-circle'></i>");
+ this.$el.html("<label><a><span style='font-weight:normal;'>Array Values</a></span></label> <button class='btn-sm btn-default add'>ADD</button>");
+ this.delegateEvents();
+ return this;
+ }
+ });
+
+
+ var arrayCellModel = Backbone.Model.extend({
+ defaults: {
+ value: undefined
+ }
+ });
+
+ /**
+ Custom InputArrayCellEditor for editing user input array for debugger.
+ */
+ var InputArrayCellEditor = Backgrid.Extension.InputArrayCellEditor =
+ Backgrid.CellEditor.extend({
+ tagName: "div",
+
+ events: {
+ 'blur': 'lostFocus'
+ },
+
+ render: function () {
+ var self = this,
+ arrayValuesCol = this.model.get(this.column.get("name")),
+ tbl = $("<table></table>").appendTo(this.$el),
+ gridCols = [
+ {name: 'value', label:'Array Values', type: 'text', cell:'string', headerCell: Backgrid.Extension.CustomHeaderIconCell, cellHeaderClasses: 'width_percent_100'},
+ ],
+ gridBody = $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>");
+
+ this.$el.attr('tabindex', '1');
+
+ gridCols.unshift({
+ name: "pg-backform-delete", label: "",
+ cell: Backgrid.Extension.DeleteCell,
+ //headerCell: Backgrid.Extension.CustomHeaderIconCell,
+ editable: false, cell_priority: -1
+ });
+
+ this.$el.empty();
+ var grid = self.grid = new Backgrid.Grid({
+ columns: gridCols,
+ collection:arrayValuesCol
+ });
+
+ grid.render();
+
+ gridBody.append(grid.$el)
+
+ this.$el.html(gridBody);
+
+ $(self.$el).pgMakeVisible('backform-tab');
+ self.delegateEvents();
+
+ return this;
+ },
+
+ /*
+ * Call back function when the grid lost the focus
+ */
+ lostFocus: function(ev) {
+
+ var self = this,
+ /*
+ * Function to determine whether one dom element is descendant of another
+ * dom element.
+ */
+ isDescendant = function (parent, child) {
+ var node = child.parentNode;
+ while (node != null) {
+ if (node == parent) {
+ return true;
+ }
+ node = node.parentNode;
+ }
+ return false;
+ }
+ /*
+ * Between leaving the old element focus and entering the new element focus the
+ * active element is the document/body itself so add timeout to get the proper
+ * focused active element.
+ */
+ setTimeout(function() {
+ if (self.$el[0] != document.activeElement && !isDescendant(self.$el[0], document.activeElement)){
+ var m = self.model,
+ column = self.column;
+ m.trigger('backgrid:edited', m, column, new Backgrid.Command(ev));
+
+ setTimeout(function(){
+ if (self.grid) {
+ self.grid.remove();
+ self.grid = null;
+ }
+ }, 10);
+
+
+ }},10);
+ return;
+ }
+ });
+
+ /*
+ * This will help us transform the user input string array values in proper format to be
+ * displayed in the cell.
+ */
+ var InputStringArrayCellFormatter = Backgrid.Extension.InputStringArrayCellFormatter =
+ function () {};
+ _.extend(InputStringArrayCellFormatter.prototype, {
+ /**
+ * Takes a raw value from a model and returns an optionally formatted
+ * string for display.
+ */
+ fromRaw: function (rawData, model) {
+ var values = []
+ rawData.each(function(m){
+ var val = m.get('value');
+ if (_.isUndefined(val)) {
+ values.push("null");
+ } else {
+ values.push(m.get("value"));
+ }
+ }
+ );
+ return values.toString();
+ },
+ toRaw: function (formattedData, model) {
+ return formattedData;
+ }
+ });
+
+ /*
+ * This will help us transform the user input integer array values in proper format to be
+ * displayed in the cell.
+ */
+ var InputIntegerArrayCellFormatter = Backgrid.Extension.InputIntegerArrayCellFormatter =
+ function () {};
+ _.extend(InputIntegerArrayCellFormatter.prototype, {
+ /**
+ * Takes a raw value from a model and returns an optionally formatted
+ * string for display.
+ */
+ fromRaw: function (rawData, model) {
+ var values = []
+ rawData.each(function(m){
+ var val = m.get('value');
+ if (_.isUndefined(val)) {
+ values.push("null");
+ } else {
+ values.push(m.get("value"));
+ }
+ }
+ );
+ return values.toString();
+ },
+ toRaw: function (formattedData, model) {
+ formattedData.each(function(m){
+ m.set("value", parseInt(m.get('value')), {silent: true});
+ });
+
+ return formattedData;
+ }
+ });
+
+ /*
+ * InputStringArrayCell for rendering and taking input for string array type in debugger
+ */
+ var InputStringArrayCell = Backgrid.Extension.InputStringArrayCell = Backgrid.Cell.extend({
+ className: "width_percent_25",
+ formatter: InputStringArrayCellFormatter,
+ editor: InputArrayCellEditor,
+
+ initialize: function() {
+ Backgrid.Cell.prototype.initialize.apply(this, arguments);
+ // set value to empty array.
+ if (_.isUndefined(this.collection)) {
+ this.collection = new (Backbone.Collection.extend({
+ model: arrayCellModel}));
+ }
+
+ this.model.set(this.column.get('name'), this.collection);
+
+ this.listenTo(this.collection, "remove", this.render);
+ },
+ });
+
+ /*
+ * InputIntegerArrayCell for rendering and taking input for integer array type in debugger
+ */
+ var InputIntegerArrayCell = Backgrid.Extension.InputIntegerArrayCell = Backgrid.Cell.extend({
+ className: "width_percent_25",
+ formatter: InputIntegerArrayCellFormatter,
+ editor: InputArrayCellEditor,
+
+ initialize: function() {
+ Backgrid.Cell.prototype.initialize.apply(this, arguments);
+ // set value to empty array.
+ if (_.isUndefined(this.collection)) {
+ this.collection = new (Backbone.Collection.extend({
+ model: arrayCellModel}));
+ }
+
+
+ this.model.set(this.column.get('name'),this.collection);
+
+ this.listenTo(this.collection, "remove", this.render);
+ },
+ });
+
return Backgrid;
}));
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py
new file mode 100644
index 0000000..332acd7
--- /dev/null
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -0,0 +1,1324 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""A blueprint module implementing the debugger"""
+
+MODULE_NAME = 'debugger'
+
+import json
+import random
+from flask import url_for, Response, render_template, request, make_response, jsonify, session, current_app
+from pgadmin.utils import PgAdminModule
+from pgadmin.utils.ajax import bad_request
+from flask.ext.babel import gettext
+from flask.ext.security import login_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from pgadmin.model import db, DebuggerFunctionArguments
+from pgadmin.utils.ajax import make_json_response, \
+ make_response as ajax_response, internal_server_error
+from pgadmin.utils.menu import Panel
+
+# Constants
+ASYNC_OK = 1
+
+
+class DebuggerModule(PgAdminModule):
+ """
+ class DebuggerModule(PgAdminModule)
+
+ A module class for debugger which is derived from PgAdminModule.
+
+ Methods:
+ -------
+ * get_own_javascripts(self)
+ - Method is used to load the required javascript files for debugger module
+
+ """
+
+ def get_own_javascripts(self):
+ scripts = list()
+ for name, script in [
+ ['pgadmin.tools.debugger.controller', 'js/debugger'],
+ ['pgadmin.tools.debugger.ui', 'js/debugger_ui'],
+ ['pgadmin.tools.debugger.direct', 'js/direct']
+ ]:
+ scripts.append({
+ 'name': name,
+ 'path': url_for('debugger.index') + script,
+ 'when': None
+ })
+
+ return scripts
+
+blueprint = DebuggerModule(MODULE_NAME, __name__)
+
+
[email protected]("/")
+@login_required
+def index():
+ return bad_request(errormsg=gettext("This URL can not be called directly!"))
+
+
[email protected]("/js/debugger.js")
+@login_required
+def script():
+ """render the main debugger javascript file"""
+ return Response(response=render_template("debugger/js/debugger.js", _=gettext),
+ status=200,
+ mimetype="application/javascript")
+
+
[email protected]("/js/debugger_ui.js")
+@login_required
+def script_debugger_js():
+ """render the debugger UI javascript file"""
+ return Response(response=render_template("debugger/js/debugger_ui.js", _=gettext),
+ status=200,
+ mimetype="application/javascript")
+
+
[email protected]("/js/direct.js")
+@login_required
+def script_debugger_direct_js():
+ """render the javascript file required send and receive the response from server for debugging"""
+ return Response(response=render_template("debugger/js/direct.js", _=gettext),
+ status=200,
+ mimetype="application/javascript")
+
+
+def update_session_debugger_transaction(trans_id, data):
+ """Update the session variables required for debugger with transaction ID"""
+ debugger_data = session['debuggerData']
+ debugger_data[str(trans_id)] = data
+
+ session['debuggerData'] = debugger_data
+
+
+def update_session_function_transaction(trans_id, data):
+ """Update the session variables of functions required to debug with transaction ID"""
+ function_data = session['functionData']
+ function_data[str(trans_id)] = data
+ session['functionData'] = function_data
+
+
[email protected]('/init/<node_type>/<int:sid>/<int:did>/<int:scid>/<int:fid>', methods=['GET'])
+@login_required
+def init_function(node_type, sid, did, scid, fid):
+ """
+ init_function(node_type, sid, did, scid, fid)
+
+ This method is responsible to initialize the function required for debugging.
+ This method is also responsible for storing the all functions data to session variable.
+ This is only required for direct debugging. As Indirect debugging does not require these data because user will
+ provide all the arguments and other functions information through another session to invoke the target.
+
+ Parameters:
+ node_type
+ - Node type - Function or Procedure
+ sid
+ - Server Id
+ did
+ - Database Id
+ scid
+ - Schema Id
+ fid
+ - Function Id
+ """
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection(did=did)
+
+ # Get the server version, server type and user information
+ ver = manager.version
+ server_type = manager.server_type
+ user = manager.user_info
+
+ # Check server type is ppas or not
+ ppas_server = False
+ if server_type == 'ppas':
+ ppas_server = True;
+
+ # Set the template path required to read the sql files
+ template_path = 'debugger/sql'
+
+ sql = render_template("/".join([template_path, 'get_function_debug_info.sql']), is_ppas_database=ppas_server, hasFeatureFunctionDefaults= True, fid=fid)
+ status, r_set = conn.execute_dict(sql)
+ if not status:
+ current_app.logger.debug("Error getting function information from database")
+ return internal_server_error(errormsg=r_set)
+
+ ret_status = status
+
+ # Check the condition that function is actually debuggable or not....
+ if r_set['rows'][0]:
+ # Function with a colon in the name cannot be debugged.
+ # If this is an EDB wrapped function, no debugging allowed
+ # Function with return type "trigger" can not be debugged.
+ if ":" in r_set['rows'][0]['name']:
+ ret_status = False
+ elif r_set['rows'][0]['rettype'] == 'trigger':
+ ret_status = False
+ elif ppas_server and r_set['rows'][0]['prosrc'].lstrip().startswith('$__EDBwrapped__$'):
+ ret_status = False
+ else:
+ # If user is super user then we should check debugger library is loaded or not
+ if user['is_superuser']:
+ status_in, rid_pre = conn.execute_scalar("SHOW shared_preload_libraries")
+ if not status_in:
+ return internal_server_error("ERROR: Couldn't fetch debugger plugin information")
+
+ # Need to check if plugin is really loaded or not with "plugin_debugger" string
+ if rid_pre:
+ if "plugin_debugger" not in rid_pre:
+ ret_status = False
+
+ status_in, rid_tar = conn.execute_scalar(" SELECT count(*) FROM pg_proc WHERE proname = 'pldbg_get_target_info' ")
+ if not status_in:
+ current_app.logger.debug("ERROR: Couldn't fetch debugger target information")
+ return internal_server_error("ERROR: Couldn't fetch debugger target information")
+
+ if rid_tar == 0:
+ ret_status = False
+ else:
+ ret_status = False
+
+ # Return the response that function can not be debug...
+ if not ret_status:
+ current_app.logger.debug("Error: Function/Procedure can not be debug")
+ return internal_server_error("ERROR: Function/Procedure can not be debug.")
+
+ # Store the function information in session variable
+ if 'funcData' not in session:
+ function_data = dict()
+ else:
+ function_data = session['funcData']
+
+ function_data = {
+ 'oid': fid,
+ 'name': r_set['rows'][0]['name'],
+ 'is_func': r_set['rows'][0]['isfunc'],
+ 'is_callable': False,
+ 'schema': r_set['rows'][0]['schemaname'],
+ 'language': r_set['rows'][0]['lanname'],
+ 'return_type': r_set['rows'][0]['rettype'],
+ 'args_type': r_set['rows'][0]['proargtypenames'],
+ 'args_name': r_set['rows'][0]['proargnames'],
+ 'arg_mode': r_set['rows'][0]['proargmodes'],
+ 'use_default': r_set['rows'][0]['pronargdefaults'],
+ 'default_value': r_set['rows'][0]['proargdefaults'],
+ 'pkg': r_set['rows'][0]['pkg'],
+ 'args_value': ''
+ }
+
+ session['funcData'] = function_data;
+
+ data = {}
+ data['name'] = r_set['rows'][0]['proargnames']
+ data['type'] = r_set['rows'][0]['proargtypenames']
+ data['use_default'] = r_set['rows'][0]['pronargdefaults']
+ data['default_value'] = r_set['rows'][0]['proargdefaults']
+ data['require_input'] = True
+
+ # Below will check do we really required for the user input arguments and show input dialog
+ if not r_set['rows'][0]['proargtypenames']:
+ data['require_input'] = False
+ else:
+ if r_set['rows'][0]['pkg'] != 0 and r_set['rows'][0]['pkgconsoid'] != 0:
+ data['require_input'] = True
+
+ if r_set['rows'][0]['proargmodes']:
+ pro_arg_modes = r_set['rows'][0]['proargmodes'].split(",");
+ for pr_arg_mode in pro_arg_modes:
+ if pr_arg_mode == 'o' or pr_arg_mode == 't':
+ data['require_input'] = False
+ continue;
+ else:
+ data['require_input'] = True
+ break;
+
+ r_set['rows'][0]['require_input'] = data['require_input']
+
+ return make_json_response(
+ data=r_set['rows'],
+ status=200
+ )
+
+
[email protected]('/direct/<int:trans_id>', methods=['GET'])
+@login_required
+def direct_new(trans_id):
+ debugger_data = session['debuggerData']
+ # Return from the function if transaction id not found
+ if str(trans_id) not in debugger_data:
+ return make_json_response(data={'status': True})
+
+ obj = debugger_data[str(trans_id)]
+
+ # if indirect debugging pass value 0 to client and for direct debugging pass it to 1
+ debug_type = 0 if obj['debug_type'] == 'indirect' else 1
+
+ return render_template(
+ "debugger/direct.html",
+ _=gettext,
+ function_name='test',
+ uniqueId=trans_id,
+ debug_type=debug_type,
+ stylesheets=[url_for('debugger.static', filename='css/debugger.css')]
+ )
+
+
[email protected]('/initialize_target/<debug_type>/<int:sid>/<int:did>/<int:scid>/<int:func_id>', methods=['GET', 'POST'])
+@login_required
+def initialize_target(debug_type, sid, did, scid, func_id):
+ """
+ initialize_target(debug_type, sid, did, scid, func_id)
+
+ This method is responsible for creating an asynchronous connection.
+ It will also create a unique transaction id and store the information
+ into session variable.
+
+ Parameters:
+ debug_type
+ - Type of debugging (Direct or Indirect)
+ sid
+ - Server Id
+ did
+ - Database Id
+ scid
+ - Schema Id
+ func_id
+ - Function Id
+ """
+
+ # Create asynchronous connection using random connection id.
+ conn_id = str(random.randint(1, 9999999))
+ try:
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection(did=did, conn_id=conn_id)
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ # Connect the Server
+ status, msg = conn.connect()
+ if not status:
+ return internal_server_error(errormsg=str(msg))
+
+ # Create a unique id for the transaction
+ trans_id = str(random.randint(1, 9999999))
+
+ # If there is no debugging information in session variable then create the store that information
+ if 'debuggerData' not in session:
+ debugger_data = dict()
+ else:
+ debugger_data = session['debuggerData']
+
+ status = True;
+
+ # Find out the debugger version and store it in session variables
+ status, rid = conn.execute_scalar(
+ "SELECT COUNT(*) FROM pg_catalog.pg_proc p \
+ LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid \
+ WHERE n.nspname = ANY(current_schemas(false)) AND p.proname = 'pldbg_get_proxy_info';"
+ )
+
+ if not status:
+ return internal_server_error(errormsg=rid)
+ else:
+ if rid == 0:
+ debugger_version = 1
+
+ status, rid = conn.execute_scalar(
+ "SELECT proxyapiver FROM pldbg_get_proxy_info();"
+ )
+
+ if status:
+ if rid == 2 or rid == 3:
+ debugger_version = rid;
+ else:
+ status = False;
+
+ # Add the debugger version information to pgadmin4 log file
+ current_app.logger.debug("Debugger version is: %d", debugger_version)
+
+ # We need to pass the value entered by the user in dialog for direct debugging
+ # Here we get the value in case of direct debugging so update the session variables accordingly
+ # For indirect debugging user will provide the data from another session so below condition will be be required
+ if request.method == 'POST':
+ data = json.loads(request.values['data'])
+ if data:
+ d = session['funcData']
+ d['args_value'] = data
+ session['funcData'] = d
+
+ # Update the debugger data session variable
+ # Here frame_id is required when user debug the multilevel function. When user select the frame from client we
+ # need to update the frame here and set the breakpoint information on that function oid
+ debugger_data[str(trans_id)] = {
+ 'conn_id': conn_id,
+ 'server_id': sid,
+ 'database_id': did,
+ 'schema_id': scid,
+ 'function_id': func_id,
+ 'debug_type': debug_type,
+ 'debugger_version': debugger_version,
+ 'frame_id': 0,
+ 'restart_debug': 0
+ }
+
+ # Store the grid dictionary into the session variable
+ session['debuggerData'] = debugger_data
+
+ # Update the function session information with same transaction id
+ func_data = session['funcData']
+
+ # Store the function information in session variable
+ if 'functionData' not in session:
+ function_data = dict()
+ else:
+ function_data = session['functionData']
+
+ function_data[str(trans_id)] = {
+ 'oid': func_data['oid'],
+ 'name': func_data['name'],
+ 'is_func': func_data['is_func'],
+ 'is_callable': func_data['is_callable'],
+ 'schema': func_data['schema'],
+ 'language': func_data['language'],
+ 'return_type': func_data['return_type'],
+ 'args_type': func_data['args_type'],
+ 'args_name': func_data['args_name'],
+ 'arg_mode': func_data['arg_mode'],
+ 'use_default': func_data['use_default'],
+ 'default_value': func_data['default_value'],
+ 'pkg': func_data['pkg'],
+ 'args_value': func_data['args_value']
+ }
+
+ # Update the session variable of function information
+ session['functionData'] = function_data;
+
+ # Delete the 'funcData' session variables as it is not used now as target is initialized
+ del session['funcData']
+
+ return make_json_response(data={'status': status, 'debuggerTransId': trans_id})
+
+
[email protected]('/close/<int:trans_id>', methods=["GET"])
+def close(trans_id):
+ """
+ close(trans_id)
+
+ This method is used to close the asynchronous connection
+ and remove the information of unique transaction id from
+ the session variable.
+
+ Parameters:
+ trans_id
+ - unique transaction id.
+ """
+ # As debugger data is not in session that means we have already deleted and close the target
+ if 'debuggerData' not in session:
+ return make_json_response(data={'status': True})
+
+ debugger_data = session['debuggerData']
+ # Return from the function if transaction id not found
+ if str(trans_id) not in debugger_data:
+ return make_json_response(data={'status': True})
+
+ obj = debugger_data[str(trans_id)]
+ try:
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ # Release the connection
+ if conn.connected():
+ if obj['debug_type'] == 'indirect':
+ # render the SQL template and send the query to server
+ sql = render_template("/".join([template_path, 'abort_target.sql']),
+ session_id=obj['session_id'])
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Delete the existing debugger data in session variable
+ del session['debuggerData'][str(trans_id)]
+ del session['functionData'][str(trans_id)]
+
+ # Release the connection acquired during the debugging
+ manager.release(did=obj['database_id'], conn_id=obj['conn_id'])
+ return make_json_response(data={'status': True})
+ else:
+ return make_json_response(data={'status': False})
+
+
[email protected]('/restart/<int:trans_id>', methods=['GET'])
+@login_required
+def restart_debugging(trans_id):
+ """
+ restart_debugging(trans_id)
+
+ This method is responsible to restart the same function for the debugging.
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+
+ if conn.connected():
+ # Update the session variable "restart_debug" to know that same function debugging has been restarted.
+ # Delete the existing debugger data in session variable and update with new data
+ if obj['restart_debug'] == 0:
+ debugger_data = session['debuggerData']
+ session_obj = debugger_data[str(trans_id)]
+ session_obj['restart_debug'] = 1
+ update_session_debugger_transaction(trans_id, session_obj)
+
+ # Store the function information in session variable
+ if 'functionData' not in session:
+ function_data = dict()
+ else:
+ function_data = {
+ 'server_id': obj['server_id'],
+ 'database_id': obj['database_id'],
+ 'schema_id': obj['schema_id'],
+ 'function_id': obj['function_id'],
+ 'trans_id': str(trans_id),
+ 'proargmodes': session['functionData'][str(trans_id)]['arg_mode'],
+ 'proargtypenames': session['functionData'][str(trans_id)]['args_type'],
+ 'pronargdefaults': session['functionData'][str(trans_id)]['use_default'],
+ 'proargdefaults': session['functionData'][str(trans_id)]['default_value'],
+ 'proargnames': session['functionData'][str(trans_id)]['args_name']
+ }
+
+ return make_json_response(data={'status': True, 'restart_debug': True, 'result': function_data})
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status})
+
+
[email protected]('/start_listener/<int:trans_id>', methods=['GET', 'POST'])
+@login_required
+def start_debugger_listener(trans_id):
+ """
+ start_debugger_listener(trans_id)
+
+ This method is responsible to listen and get the required information requested by user during debugging
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+
+ ver = manager.version
+ server_type = manager.server_type
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ # If user again start the same debug function with different arguments then we need to save that values to session
+ # variable and database.
+ if request.method == 'POST':
+ data = json.loads(request.values['data'])
+ if data:
+ function_data = session['functionData']
+ session_obj = function_data[str(trans_id)]
+ session_obj['args_value'] = data
+ update_session_function_transaction(trans_id, session_obj)
+
+ if conn.connected():
+
+ # For the direct debugging extract the function arguments values from user and pass to jinja template
+ # to create the query for execution.
+ if obj['debug_type'] == 'direct':
+ str_query = ''
+
+ # Form the function name with schema name
+ func_name = session['functionData'][str(trans_id)]['schema'] + '.' + session['functionData'][str(trans_id)]['name']
+
+ if obj['restart_debug'] == 0:
+ # render the SQL template and send the query to server
+ if session['functionData'][str(trans_id)]['language'] == 'plpgsql':
+ sql = render_template("/".join([template_path, 'debug_plpgsql_execute_target.sql']),
+ packge_oid=session['functionData'][str(trans_id)]['pkg'],
+ function_oid=obj['function_id'])
+ else:
+ sql = render_template("/".join([template_path, 'debug_spl_execute_target.sql']),
+ packge_oid=session['functionData'][str(trans_id)]['pkg'],
+ function_oid=obj['function_id'])
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ if session['functionData'][str(trans_id)]['arg_mode']:
+ # In EDBAS 90, if an SPL-function has both an OUT-parameter
+ # and a return value (which is not possible on PostgreSQL otherwise),
+ # the return value is transformed into an extra OUT-parameter
+ # named "_retval_"
+ if session['functionData'][str(trans_id)]['args_name']:
+ arg_name = session['functionData'][str(trans_id)]['args_name'].split(",");
+ if '_retval_' in arg_name:
+ arg_mode = session['functionData'][str(trans_id)]['arg_mode'].split(",");
+ arg_mode.pop();
+ else:
+ arg_mode = session['functionData'][str(trans_id)]['arg_mode'].split(",");
+ else:
+ arg_mode = session['functionData'][str(trans_id)]['arg_mode'].split(",");
+ else:
+ arg_mode = ['i'] * len(session['functionData'][str(trans_id)]['args_type'].split(","));
+
+ if session['functionData'][str(trans_id)]['args_type']:
+ if session['functionData'][str(trans_id)]['args_name']:
+ arg_name = session['functionData'][str(trans_id)]['args_name'].split(",");
+ if '_retval_' in arg_name:
+ arg_type = session['functionData'][str(trans_id)]['args_type'].split(",");
+ arg_type.pop();
+ else:
+ arg_type = session['functionData'][str(trans_id)]['args_type'].split(",");
+ else:
+ arg_type = session['functionData'][str(trans_id)]['args_type'].split(",");
+
+ # Below are two different template to execute and start executer
+ if manager.server_type != 'pg' and manager.version < 90300:
+ str_query = render_template("/".join(['debugger/sql', 'execute_edbspl.sql']),
+ func_name=func_name, is_func=session['functionData'][str(trans_id)]['is_func'],
+ lan_name=session['functionData'][str(trans_id)]['language'],
+ ret_type=session['functionData'][str(trans_id)]['return_type'],
+ data=session['functionData'][str(trans_id)]['args_value'],
+ arg_type=arg_type,
+ args_mode=arg_mode
+ )
+ else:
+ str_query = render_template("/".join(['debugger/sql', 'execute_plpgsql.sql']),
+ func_name=func_name, is_func=session['functionData'][str(trans_id)]['is_func'],
+ ret_type=session['functionData'][str(trans_id)]['return_type'],
+ data=session['functionData'][str(trans_id)]['args_value']
+ )
+
+ status, result = conn.execute_async(str_query)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ if conn.connected():
+ # For indirect debugging first create the listener and then wait for the target
+ sql = render_template("/".join([template_path, 'create_listener.sql']))
+
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Get and store the session variable which is required to fetch other information during debugging
+ int_session_id = res['rows'][0]['pldbg_create_listener']
+
+ # In EnterpriseDB versions <= 9.1 the
+ # pldbg_set_global_breakpoint function took five arguments,
+ # the 2nd argument being the package's OID, if any. Starting
+ # with 9.2, the package OID argument is gone, and the function
+ # takes four arguments like the community version has always
+ # done.
+ if server_type == 'ppas' and ver <= 90100:
+ sql = render_template("/".join([template_path, 'add_breakpoint_edb.sql']),
+ session_id=int_session_id,
+ function_oid=obj['function_id'])
+
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+ else:
+ sql = render_template("/".join([template_path, 'add_breakpoint_pg.sql']),
+ session_id=int_session_id,
+ function_oid=obj['function_id'])
+
+ status, res = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # wait for the target
+ sql = render_template("/".join([template_path, 'wait_for_target.sql']),
+ session_id=int_session_id)
+
+ status, res = conn.execute_async(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ debugger_data = session['debuggerData']
+ session_obj = debugger_data[str(trans_id)]
+ session_obj['exe_conn_id'] = obj['conn_id']
+ session_obj['restart_debug'] = 1
+ session_obj['frame_id'] = 0
+ session_obj['session_id'] = int_session_id
+ update_session_debugger_transaction(trans_id, session_obj)
+ return make_json_response(data={'status': status, 'result': res})
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+ return make_json_response(data={'status': status, 'result': result})
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result})
+
+
[email protected]('/execute_query/<int:trans_id>/<query_type>', methods=['GET'])
+@login_required
+def execute_debugger_query(trans_id, query_type):
+ """
+ execute_debugger_query(trans_id, query_type)
+
+ This method is responsible to execute the query and return value. As this method is generic so user has to pass the
+ query_type to get the required information for debugging.
+
+ e.g. If user want to execute 'step_into' then query_type='step_into'.
+ If user want to execute 'continue' then query_type='continue'
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ query_type
+ - Type of query to execute
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ if conn.connected():
+ sql = render_template("/".join([template_path, query_type + ".sql"]), session_id=obj['session_id'])
+ # As the query type is continue or step_into or step_over then we may get result after some time so poll the
+ # result. We need to update the frame id variable when user move the next step for debugging.
+ if query_type == 'continue' or query_type == 'step_into' or query_type == 'step_over':
+ # We should set the frame_id to 0 when execution starts.
+ if obj['frame_id'] != 0:
+ session_obj = debugger_data[str(trans_id)]
+ session_obj['frame_id'] = 0
+ update_session_debugger_transaction(trans_id, session_obj)
+
+ status, result = conn.execute_async(sql)
+ return make_json_response(data={'status': status, 'result': result})
+ elif query_type == 'abort_target':
+ status, result = conn.execute_dict(sql)
+
+ manager.release(did=obj['database_id'], conn_id=obj['conn_id'])
+
+ # Delete the existing debugger data in session variable
+ del session['debuggerData'][str(trans_id)]
+ del session['functionData'][str(trans_id)]
+
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ return make_json_response(info='Target Aborted.', data={'status': status, 'result': result})
+ else:
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': 'Success', 'result': result['rows']})
+
+
[email protected]('/messages/<int:trans_id>/', methods=["GET"])
+@login_required
+def messages(trans_id):
+ """
+ messages(trans_id)
+
+ This method polls the messages returned by the database server.
+
+ Parameters:
+ trans_id
+ - unique transaction id.
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': 'NotConnected',
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+
+ port_number = ''
+
+ if conn.connected():
+ status, result, my_result = conn.poll()
+ notify = conn.messages()
+ if notify:
+ # In notice message we need to find "PLDBGBREAK" string to find the port number to attach.
+ # Notice message returned by the server is "NOTICE: PLDBGBREAK:7".
+ # From the above message we need to find out port number as "7" so below logic will find 7 as port number
+ # and attach listened to that port number
+ offset = notify[0].find('PLDBGBREAK');
+ str_len = len('PLDBGBREAK')
+ str_len = str_len + 1
+ tmpOffset = 0
+ tmpFlag = False
+
+ while notify[0][offset+str_len+tmpOffset].isdigit():
+ status = 'Success'
+ tmpFlag = True
+ port_number = port_number + notify[0][offset+str_len+tmpOffset]
+ tmpOffset = tmpOffset + 1
+
+ if tmpFlag == False:
+ status = 'Busy'
+ else:
+ status = 'Busy'
+ else:
+ status = 'NotConnected'
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': port_number})
+
+
[email protected]('/start_execution/<int:trans_id>/<int:port_num>', methods=['GET'])
+@login_required
+def start_execution(trans_id, port_num):
+ """
+ start_execution(trans_id, port_num)
+
+ This method is responsible for creating an asynchronous connection for execution thread.
+ Also store the session id into session return with attach port query for the direct debugging.
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ port_num
+ - Port number to attach
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': 'NotConnected',
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ dbg_version = obj['debugger_version']
+
+ # Create asynchronous connection using random connection id.
+ exe_conn_id = str(random.randint(1, 9999999))
+ try:
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=exe_conn_id)
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ # Connect the Server
+ status, msg = conn.connect()
+ if not status:
+ return internal_server_error(errormsg=str(msg))
+
+ if 'debuggerData' not in session:
+ debugger_data = dict()
+ else:
+ debugger_data = session['debuggerData']
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ # connect to port and store the session ID in the session variables
+ sql = render_template("/".join([template_path, 'attach_to_port.sql']), port=port_num)
+ status_port, res_port = conn.execute_dict(sql)
+ if not status_port:
+ return internal_server_error(errormsg=res_port)
+
+ session_obj = debugger_data[str(trans_id)]
+ session_obj['restart_debug'] = 0
+ session_obj['frame_id'] = 0
+ session_obj['exe_conn_id'] = exe_conn_id
+ session_obj['debugger_version'] = dbg_version
+ session_obj['session_id'] = res_port['rows'][0]['pldbg_attach_to_port']
+ update_session_debugger_transaction(trans_id, session_obj)
+
+ return make_json_response(data={'status': 'Success', 'result': res_port['rows'][0]['pldbg_attach_to_port']})
+
+
[email protected]('/set_breakpoint/<int:trans_id>/<int:line_no>/<int:set_type>', methods=['GET'])
+@login_required
+def set_clear_breakpoint(trans_id, line_no, set_type):
+ """
+ set_clear_breakpoint(trans_id, line_no, set_type)
+
+ This method is responsible to set and clean the breakpoint
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ line_no
+ - Line number to set
+ set_type
+ - 0 - clear the breakpoint, 1 - set the breakpoint
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ query_type = ''
+
+ # We need to find out function OID before sending the foid to set the breakpoint because it may possible that
+ # debugging function has multi level function for debugging so we need to save the debug level to session variable
+ # and pass tha appropriate foid to set the breakpoint.
+ sql_ = render_template("/".join([template_path, "get_stack_info.sql"]), session_id=obj['session_id'])
+ status, res_stack = conn.execute_dict(sql_)
+ if not status:
+ return internal_server_error(errormsg=res_stack)
+
+ # For multilevel function debugging, we need to fetch current selected frame's function oid for setting the
+ # breakpoint. For single function the frame id will be 0.
+ foid = res_stack['rows'][obj['frame_id']]['func']
+
+ # Check the result of the stack before setting the breakpoint
+ if conn.connected():
+ if set_type == 1:
+ query_type = 'set_breakpoint'
+ else:
+ query_type = 'clear_breakpoint'
+
+ sql = render_template("/".join([template_path, query_type + ".sql"]), session_id=obj['session_id'],
+ foid=foid, line_number=line_no)
+
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result['rows']})
+
+
[email protected]('/clear_all_breakpoint/<int:trans_id>', methods=['POST'])
+@login_required
+def clear_all_breakpoint(trans_id):
+ """
+ clear_all_breakpoint(trans_id)
+
+ This method is responsible to clear all the breakpoint
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ """
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ query_type = ''
+
+ if conn.connected():
+ # get the data sent through post from client
+ if request.form['breakpoint_list']:
+ line_numbers = request.form['breakpoint_list'].split(",")
+ for line_no in line_numbers:
+ sql = render_template("/".join([template_path, "clear_breakpoint.sql"]), session_id=obj['session_id'],
+ foid=obj['function_id'], line_number=line_no)
+
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ return make_json_response(data={'status': False})
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result['rows']})
+
+
[email protected]('/deposit_value/<int:trans_id>', methods=['POST'])
+@login_required
+def deposit_parameter_value(trans_id):
+ """
+ deposit_parameter_value(trans_id)
+
+ This method is responsible to change the value of variables
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ """
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ query_type = ''
+
+ if conn.connected():
+ # get the data sent through post from client
+ data = json.loads(request.values['data'])
+
+ if data:
+ sql = render_template("/".join([template_path, "deposit_value.sql"]), session_id=obj['session_id'],
+ var_name=data[0]['name'], line_number=-1, val=data[0]['value'])
+
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result['rows']})
+
+
[email protected]('/select_frame/<int:trans_id>/<int:frame_id>', methods=['GET'])
+@login_required
+def select_frame(trans_id, frame_id):
+ """
+ select_frame(trans_id, frame_id)
+
+ This method is responsible to select the frame from stack info
+
+ Parameters:
+ trans_id
+ - Transaction ID
+ frame_id
+ - Frame id selected
+ """
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': False,
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ # find the debugger version and execute the query accordingly
+ dbg_version = obj['debugger_version']
+ if dbg_version <= 2:
+ template_path = 'debugger/sql/v1'
+ else:
+ template_path = 'debugger/sql/v2'
+
+ session_obj = debugger_data[str(trans_id)]
+ session_obj['frame_id'] = frame_id
+ update_session_debugger_transaction(trans_id, session_obj)
+
+ if conn.connected():
+ sql = render_template("/".join([template_path, "select_frame.sql"]), session_id=obj['session_id'],
+ frame_id=frame_id)
+
+ status, result = conn.execute_dict(sql)
+ if not status:
+ return internal_server_error(errormsg=result)
+ else:
+ status = False
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result['rows']})
+
+
[email protected]('/get_arguments/<int:sid>/<int:did>/<int:scid>/<int:func_id>', methods=['GET'])
+@login_required
+def get_arguments_sqlite(sid, did, scid, func_id):
+ """
+ get_arguments_sqlite(sid, did, scid, func_id)
+
+ This method is responsible to get the function arguments saved to sqlite database during first debugging.
+
+ Parameters:
+ sid
+ - Server Id
+ did
+ - Database Id
+ scid
+ - Schema Id
+ func_id
+ - Function Id
+ """
+
+ """Get the count of the existing data available in sqlite database"""
+ DbgFuncArgsCount = DebuggerFunctionArguments.query.filter_by(
+ server_id=sid, database_id=did, schema_id=scid, function_id=func_id).count()
+
+ args_data = []
+
+ if DbgFuncArgsCount:
+ """Update the Debugger Function Arguments settings"""
+ DbgFuncArgs = DebuggerFunctionArguments.query.filter_by(
+ server_id=sid, database_id=did, schema_id=scid, function_id=func_id)
+
+ args_list = DbgFuncArgs.all()
+
+ for i in range(0, DbgFuncArgsCount):
+ info = {
+ "arg_id": args_list[i].arg_id,
+ "is_null": args_list[i].is_null,
+ "is_expression": args_list[i].is_expression,
+ "use_default": args_list[i].use_default,
+ "value": args_list[i].value
+ }
+ args_data.append(info)
+
+ # As we do have entry available for that function so we need to add that entry
+ return make_json_response(data={'result': args_data, 'args_count': DbgFuncArgsCount})
+ else:
+ # As we do not have any entry available for that function so we need to add that entry
+ return make_json_response(data={'result': 'result', 'args_count': DbgFuncArgsCount})
+
+
[email protected]('/set_arguments/<int:sid>/<int:did>/<int:scid>/<int:func_id>', methods=['POST'])
+@login_required
+def set_arguments_sqlite(sid, did, scid, func_id):
+ """
+ set_arguments_sqlite(sid, did, scid, func_id)
+
+ This method is responsible for setting the value of function arguments to sqlite database
+
+ Parameters:
+ sid
+ - Server Id
+ did
+ - Database Id
+ scid
+ - Schema Id
+ func_id
+ - Function Id
+ """
+
+ if request.values['data']:
+ data = json.loads(request.values['data'])
+
+ try:
+ for i in range(0, len(data)):
+ DbgFuncArgsExists = DebuggerFunctionArguments.query.filter_by(
+ server_id=data[i]['server_id'], database_id=data[i]['database_id'], schema_id=data[i]['schema_id'],
+ function_id=data[i]['function_id'], arg_id=data[i]['arg_id']).count()
+
+ # Check if data is already available in database then update the existing value otherwise add the new value
+ if DbgFuncArgsExists:
+ DbgFuncArgs = DebuggerFunctionArguments.query.filter_by(
+ server_id=data[i]['server_id'], database_id=data[i]['database_id'], schema_id=data[i]['schema_id'],
+ function_id=data[i]['function_id'], arg_id=data[i]['arg_id']).first()
+
+ DbgFuncArgs.is_null = data[i]['is_null']
+ DbgFuncArgs.is_expression = data[i]['is_expression']
+ DbgFuncArgs.use_default = data[i]['use_default']
+ DbgFuncArgs.value = data[i]['value']
+ else:
+ debugger_func_args = DebuggerFunctionArguments(
+ server_id = data[i]['server_id'],
+ database_id = data[i]['database_id'],
+ schema_id = data[i]['schema_id'],
+ function_id = data[i]['function_id'],
+ arg_id = data[i]['arg_id'],
+ is_null = data[i]['is_null'],
+ is_expression = data[i]['is_expression'],
+ use_default = data[i]['use_default'],
+ value = data[i]['value']
+ )
+
+ db.session.add(debugger_func_args)
+
+ db.session.commit()
+
+ except Exception as e:
+ current_app.logger.exception(e)
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=e.message
+ )
+
+ return make_json_response(data={'status': True, 'result': 'Success'})
+
+
[email protected]('/poll_end_execution_result/<int:trans_id>/', methods=["GET"])
+@login_required
+def poll_end_execution_result(trans_id):
+ """
+ poll_end_execution_result(trans_id)
+
+ This method polls the end of execution result messages returned by the database server.
+
+ Parameters:
+ trans_id
+ - unique transaction id.
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': 'NotConnected',
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['conn_id'])
+
+ if conn.connected():
+ statusmsg = conn.status_message()
+ status, result, my_result = conn.poll()
+ if status == ASYNC_OK and session['functionData'][str(trans_id)]['language'] == 'edbspl':
+ status = 'Success'
+ return make_json_response(success=1, info=gettext("Execution Completed."),
+ data={'status': status, 'status_message': statusmsg})
+ if result:
+ status = 'Success'
+ data = {}
+ for i in result:
+ for k, v in i.items():
+ data["name"] = k
+ data.setdefault("value",[]).append(v)
+
+ return make_json_response(success=1, info=gettext("Execution Completed."),
+ data={'status': status, 'result': data, 'status_message': statusmsg})
+ else:
+ status = 'Busy'
+ else:
+ status = 'NotConnected'
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result})
+
+
[email protected]('/poll_result/<int:trans_id>/', methods=["GET"])
+@login_required
+def poll_result(trans_id):
+ """
+ poll_result(trans_id)
+
+ This method polls the result of the asynchronous query and returns the result.
+
+ Parameters:
+ trans_id
+ - unique transaction id.
+ """
+
+ debugger_data = session['debuggerData']
+ if str(trans_id) not in debugger_data:
+ return make_json_response(
+ data={'status': 'NotConnected',
+ 'result': gettext('Not connected to server Or connection with the server has been closed.')}
+ )
+ obj = debugger_data[str(trans_id)]
+
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(obj['server_id'])
+ conn = manager.connection(did=obj['database_id'], conn_id=obj['exe_conn_id'])
+
+ if conn.connected():
+ status, result, my_result = conn.poll()
+ if status == ASYNC_OK and result is not None:
+ status = 'Success'
+ else:
+ status = 'Busy'
+ else:
+ status = 'NotConnected'
+ result = gettext('Not connected to server Or connection with the server has been closed.')
+
+ return make_json_response(data={'status': status, 'result': result})
diff --git a/web/pgadmin/tools/debugger/static/css/debugger.css b/web/pgadmin/tools/debugger/static/css/debugger.css
new file mode 100644
index 0000000..2dceb53
--- /dev/null
+++ b/web/pgadmin/tools/debugger/static/css/debugger.css
@@ -0,0 +1,72 @@
+.navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom {
+ background-image: linear-gradient(to bottom, #CCC 0%, #D2D2D2 100%);
+}
+
+.btn-default {
+ background-color: #D2D2D2;
+ left: 0px;
+ right: 0px;
+ padding: 7px;
+}
+
+.btn-toolbar {
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+
+#container {
+ position: absolute;
+ top: 44px;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
+}
+
+.full-container {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ bottom:0px;
+}
+
+.full-container-pane {
+ width: 100%;
+ height: 100%;
+}
+
+.top-container {
+ min-height: 300px;
+ height: 100%;
+}
+
+.pg-debugger-panel {
+ height: 100%;
+ width: 100%;
+ position: absolute;
+ top: 0; bottom: 0; right: 0; left: 0;
+}
+
+.wcLoadingIcon {
+ position: absolute;
+ font-size: 100px;
+ left: calc(50% - 100px);
+ top: calc(50% - 100px);
+ height: 95px;
+}
+
+.wcLoadingLabel {
+ position: absolute;
+ width: 103%;
+ font-size: 30px;
+ top: calc(50% + 0px);
+ text-align: center;
+}
+
+.top-container .breakpoints {
+ width: 0.9em;
+}
+
+.top-container .CodeMirror-activeline-background {
+ background: #50B0F0;
+}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/direct.html b/web/pgadmin/tools/debugger/templates/debugger/direct.html
new file mode 100644
index 0000000..718ef2f
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/direct.html
@@ -0,0 +1,63 @@
+{% extends "base.html" %}
+{% block title %}{{ _('Debugger - ') + function_name }}{% endblock %}
+{% block init_script %}
+try {
+ require(
+ ['pgadmin', 'pgadmin.tools.debugger.direct'],
+ function(pgAdmin, pgDirectDebug) {
+ pgDirectDebug.init({{ uniqueId }}, {{ debug_type }});
+ },
+ function() {
+ /* TODO:: Show proper error dialog */
+ console.log(arguments);
+ });
+} catch (err) {
+ /* Show proper error dialog */
+ console.log(err);
+}
+{% endblock %}
+{% block body %}
+
+<nav class="navbar-inverse navbar-fixed-top">
+ <div class="container-fluid">
+ <div class="collapse navbar-collapse">
+ <ul class="nav navbar-nav">
+ <div id="btn-toolbar" class="btn-toolbar" role="toolbar" aria-label="">
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-step-into" title="{{ _('Step into') }}">
+ <i class="fa fa-indent"></i>
+ </button>
+ <button type="button" class="btn btn-default btn-step-over" title="{{ _('Step over') }}">
+ <i class="fa fa-outdent"></i>
+ </button>
+ <button type="button" class="btn btn-default btn-continue" title="{{ _('Continue/Start') }}">
+ <i class="fa fa-play-circle"></i>
+ </button>
+ </div>
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-toggle-breakpoint" title="{{ _('Toggle breakpoint') }}">
+ <i class="fa fa-circle"></i>
+ </button>
+ <button type="button" class="btn btn-default btn-clear-breakpoint" title="{{ _('Clear all breakpoints') }}">
+ <i class="fa fa-ban"></i>
+ </button>
+ </div>
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-stop" title="{{ _('Stop') }}">
+ <i class="fa fa-stop-circle"></i>
+ </button>
+ </div>
+ </div>
+ </ul>
+ </div>
+ </div>
+</nav>
+<div id="container"></div>
+{% endblock %}
+
+
+{% block css_link %}
+{% for stylesheet in stylesheets %}
+<link type="text/css" rel="stylesheet" href="{{ stylesheet }}"/>
+{% endfor %}
+{% endblock %}
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js
new file mode 100644
index 0000000..6d24848
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js
@@ -0,0 +1,329 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin',
+ 'pgadmin.browser', 'backbone', 'backgrid', 'codemirror', 'backform',
+ 'pgadmin.tools.debugger.ui', 'wcdocker', 'pgadmin.backform',
+ 'pgadmin.backgrid', 'pgadmin.browser.frame'],
+ function($, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, CodeMirror, Backform, get_function_arguments) {
+
+ pgAdmin = pgAdmin || window.pgAdmin || {};
+
+ var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+
+ /* Return back, this has been called more than once */
+ if (pgAdmin.Tools.Debugger)
+ return pgAdmin.Tools.Debugger;
+
+ pgTools.Debugger = {
+ init: function() {
+ // We do not want to initialize the module multiple times.
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ // Initialize the context menu to display the debugging options when user open the context menu for functions
+ pgBrowser.add_menus([{
+ name: 'direct_debugger', node: 'function', module: this,
+ applies: ['object', 'context'], callback: 'get_function_information',
+ category: 'Debugging', priority: 10, label: '{{ _('Debug') }}',
+ data: {object: 'function'}, icon: 'fa fa-arrow-circle-right',
+ enable: 'can_debug'
+ },{
+ name: 'global_debugger', node: 'function', module: this,
+ applies: ['object', 'context'], callback: 'check_func_debuggable',
+ category: 'Debugging', priority: 10, label: '{{ _('Set breakpoint') }}',
+ data: {object: 'function'}, icon: 'fa fa-arrow-circle-right',
+ enable: 'can_debug'
+ },{
+ name: 'procedure_direct_debugger', node: 'procedure', module: this,
+ applies: ['object', 'context'], callback: 'get_function_information',
+ category: 'Debugging', priority: 10, label: '{{ _('Debug') }}',
+ data: {object: 'procedure'}, icon: 'fa fa-arrow-circle-right',
+ enable: 'can_debug'
+ }, {
+ name: 'procedure_indirect_debugger', node: 'procedure', module: this,
+ applies: ['object', 'context'], callback: 'check_func_debuggable',
+ category: 'Debugging', priority: 10, label: '{{ _('Set breakpoint') }}',
+ data: {object: 'procedure'}, icon: 'fa fa-arrow-circle-right',
+ enable: 'can_debug'
+ }]);
+
+ // Create and load the new frame required for debugger panel
+ this.frame = new pgBrowser.Frame({
+ name: 'frm_debugger',
+ title: '{{ _('Debugger') }}',
+ width: 500,
+ isCloseable: true,
+ isPrivate: true,
+ url: 'about:blank'
+ });
+
+ this.frame.load(pgBrowser.docker);
+ },
+ // It will check weather the function is actually debuggable or not with pre-required condition.
+ can_debug: function(itemData, item, data) {
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ if ('catalog' == d._type) {
+ //Check if we are not child of catalog
+ return false;
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+
+ // Find the function is really available in database
+ var tree = pgBrowser.tree,
+ info = tree.selected(),
+ d_ = info && info.length == 1 ? tree.itemData(info) : undefined,
+ node = d_ && pgBrowser.Nodes[d_._type];
+
+ if (!d_)
+ return false;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [info]);
+
+ // Must be a super user or object owner to create breakpoints of any kind
+ if (!(treeInfo.server.user.is_superuser || treeInfo.function.funcowner == treeInfo.server.user.name))
+ return false;
+
+ if (d_.language != 'plpgsql' && d_.language != 'edbspl') {
+ return false;
+ }
+
+ return true;
+ },
+ /*
+ For the direct debugging, we need to fetch the function information to display in the dialog so "generate_url"
+ will dynamically generate the URL from the server_id, database_id, schema_id and function id.
+ */
+ generate_url: function(_url, treeInfo, node) {
+ var url = '{BASEURL}{URL}/{OBJTYPE}{REF}',
+ ref = '';
+
+ _.each(
+ _.sortBy(
+ _.values(
+ _.pick(treeInfo,
+ function(v, k, o) {
+ return (k != 'server-group');
+ })
+ ),
+ function(o) { return o.priority; }
+ ),
+ function(o) {
+ ref = S('%s/%s').sprintf(ref, encodeURI(o._id)).value();
+ });
+
+ var args = {
+ 'URL': _url,
+ 'BASEURL': '{{ url_for('debugger.index')}}',
+ 'REF': ref,
+ 'OBJTYPE': encodeURI(node.type)
+ };
+
+ return url.replace(/{(\w+)}/g, function(match, arg) {
+ return args[arg];
+ });
+ },
+
+ check_func_debuggable: function(args, item) {
+ var input = args || {},
+ t = pgBrowser.tree,
+ i = item || t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var objName = d.label,
+ treeInfo = node.getTreeNodeHierarchy.apply(node, [i]),
+ _url = this.generate_url('init', treeInfo, node);
+
+ var self = this;
+ $.ajax({
+ url: _url,
+ cache: false,
+ success: function(res) {
+ self.start_global_debugger();
+ },
+ error: function(xhr, status, error) {
+ try {
+ var err = $.parseJSON(xhr.responseText);
+ if (err.success == 0) {
+ msg = S('{{ _(' + err.errormsg + ')}}').value();
+ Alertify.alert("{{ _('" + err.errormsg + "') }}");
+ }
+ } catch (e) {}
+ }
+ });
+ },
+
+ //Callback function when user start the indirect debugging ( Listen to another session to invoke the target )
+ start_global_debugger: function(args, item) {
+ // Initialize the target and create asynchronous connection and unique transaction ID
+ var t = pgBrowser.tree,
+ i = t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+ if (d._type == "function") {
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
+ }
+ else {
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.procedure._id;
+ }
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
+
+ pgBrowser.Events.once(
+ 'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+ frame.openURL(url);
+ });
+
+ // Create the debugger panel as per the data received from user input dialog.
+ var dashboardPanel = pgBrowser.docker.findPanels(
+ 'dashboard'
+ ),
+ panel = pgBrowser.docker.addPanel(
+ 'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+ );
+
+ panel.focus();
+
+ // Panel Closed event
+ panel.on(wcDocker.EVENT.CLOSED, function() {
+ var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
+ $.ajax({
+ url: closeUrl,
+ method: 'GET'
+ });
+ });
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger target Initialize Error'
+ );
+ }
+ });
+ },
+
+ /*
+ Get the function information for the direct debugging to display the functions arguments and other informations
+ in the user input dialog
+ */
+ get_function_information: function(args, item) {
+ var input = args || {},
+ t = pgBrowser.tree,
+ i = item || t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var objName = d.label,
+ treeInfo = node.getTreeNodeHierarchy.apply(node, [i]),
+ _url = this.generate_url('init', treeInfo, node);
+
+ var self = this;
+ $.ajax({
+ url: _url,
+ cache: false,
+ success: function(res) {
+
+ // Open Alertify the dialog to take the input arguments from user if function having input arguments
+ if (res.data[0]['require_input']) {
+ get_function_arguments(res.data[0], 0);
+ }
+ else {
+ // Initialize the target and create asynchronous connection and unique transaction ID
+ // If there is no arguments to the functions then we should not ask for for function arguments and
+ // Directly open the panel
+ var t = pgBrowser.tree,
+ i = t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+ if (d._type == "function") {
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "direct/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
+ }
+ else {
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "direct/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.procedure._id;
+ }
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+
+ var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
+
+ pgBrowser.Events.once(
+ 'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+ frame.openURL(url);
+ });
+
+ // Create the debugger panel as per the data received from user input dialog.
+ var dashboardPanel = pgBrowser.docker.findPanels(
+ 'dashboard'
+ ),
+ panel = pgBrowser.docker.addPanel(
+ 'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+ );
+
+ panel.focus();
+
+ // Register Panel Closed event
+ panel.on(wcDocker.EVENT.CLOSED, function() {
+ var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
+ $.ajax({
+ url: closeUrl,
+ method: 'GET'
+ });
+ });
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger target Initialize Error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }
+ },
+ error: function(xhr, status, error) {
+ try {
+ var err = $.parseJSON(xhr.responseText);
+ if (err.success == 0) {
+ msg = S('{{ _(' + err.errormsg + ')}}').value();
+ Alertify.alert("{{ _('" + err.errormsg + "') }}");
+ }
+ } catch (e) {}
+ }
+ });
+ }
+ };
+
+ return pgAdmin.Tools.Debugger;
+ });
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
new file mode 100644
index 0000000..8b58bf3
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
@@ -0,0 +1,657 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin',
+ 'pgadmin.browser', 'backbone', 'backgrid', 'codemirror', 'backform',
+ 'wcdocker', 'pgadmin.backform', 'pgadmin.backgrid',
+ 'pgadmin.browser.panel'],
+ function($, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, CodeMirror, Backform ) {
+
+ /*
+ * Function used to return the respective Backgrid control based on the data type
+ * of function input argument.
+ */
+ var cellFunction = function(model) {
+ var self = this,
+ variable_type = model.get("type");
+
+ // if variable type is an array then we need to render the custom control to take the input from user.
+ if (variable_type.indexOf("[]") !=-1) {
+ if (variable_type.indexOf("integer") != -1) {
+ return Backgrid.Extension.InputIntegerArrayCell;
+ }
+ return Backgrid.Extension.InputStringArrayCell;
+ }
+
+ switch(variable_type) {
+ case "bool":
+ return Backgrid.BooleanCell;
+ break;
+
+ case "integer":
+ // 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'))},{silent:true});
+ }
+
+ return Backgrid.IntegerCell;
+ break;
+
+ case "real":
+ // 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': parseFloat(model.get('value'))} ,{silent:true});
+ }
+ return Backgrid.NumberCell;
+ break;
+
+ case "string":
+ return Backgrid.StringCell;
+ break;
+ case "date":
+ return Backgrid.DateCell;
+ break;
+ default:
+ return Backgrid.Cell;
+ break;
+ }
+ }
+
+ /*
+ * Function used to return the respective Backgrid string or boolean control based on the data type
+ * of function input argument.
+ */
+ var cellExprControlFunction = function(model) {
+ var self = this,
+ variable_type = model.get("type");
+ if (variable_type.indexOf("[]") !=-1) {
+ return Backgrid.StringCell;
+ }
+ return Backgrid.BooleanCell;
+ }
+
+ /**
+ * DebuggerInputArgsModel used to represent input parameters for the function to debug
+ * for function objects.
+ **/
+ var DebuggerInputArgsModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined,
+ type: undefined,
+ is_null: undefined,
+ expr: undefined,
+ value: undefined,
+ use_default: undefined,
+ default_value: undefined,
+ },
+ validate: function() {
+ if (_.isUndefined(this.get('value')) ||
+ _.isNull(this.get('value')) ||
+ String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
+ var msg = '{{ _('Please enter some value!') }}';
+ this.errorModel.set('value', msg);
+ return msg;
+ } else {
+ this.errorModel.unset('value');
+ }
+ return null;
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var DebuggerInputArgCollections = Backbone.Collection.extend({
+ model: DebuggerInputArgsModel
+ });
+
+ // function will enable/disable the use_default column based on the value received.
+ var disableDefaultCell = function(d) {
+ if (d instanceof Backbone.Model) {
+ return d.get('use_default');
+ }
+ return false;
+ };
+
+ // Enable/Disable the control based on the array data type of the function input arguments
+ var disableExpressionControl = function(d) {
+ var argType = d.get('type');
+ if (d instanceof Backbone.Model) {
+ var argType = d.get('type');
+ if (argType.indexOf("[]") !=-1) {
+ return false;
+ }
+ return true;
+ }
+ };
+
+ var res = function(args, restart_debug) {
+ if (!Alertify.debuggerInputArgsDialog) {
+ Alertify.dialog('debuggerInputArgsDialog', function factory() {
+ return {
+ main:function(title, data, restart_debug) {
+ this.set('title', title);
+ this.data = data;
+ this.restart_debug = restart_debug;
+
+ // Variables to store the data sent from sqlite database
+ var func_args_data = this.func_args_data = [];
+
+ // As we are not getting pgBrowser.tree when we debug again so tree info will be updated from the server data
+ if (restart_debug == 0) {
+ var t = pgBrowser.tree,
+ i = t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+
+ if (d._type == "function") {
+ // Get the existing function parameters available from sqlite database
+ var _Url = "{{ url_for('debugger.index') }}" + "get_arguments/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
+ }
+ else {
+ // Get the existing function parameters available from sqlite database
+ var _Url = "{{ url_for('debugger.index') }}" + "get_arguments/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.procedure._id;
+ }
+ }
+ else {
+ // Get the existing function parameters available from sqlite database
+ var _Url = "{{ url_for('debugger.index') }}" + "get_arguments/" + this.data.server_id +
+ "/" + this.data.database_id + "/" + this.data.schema_id + "/" + this.data.function_id;
+ }
+
+ $.ajax({
+ url: _Url,
+ method: 'GET',
+ async: false,
+ success: function(res) {
+ if (res.data.args_count != 0) {
+ for (i = 0; i < res.data.result.length; i++) {
+ // Below will format the data to be stored in sqlite database
+ func_args_data.push({
+ 'arg_id': res.data.result[i]['arg_id'],
+ 'is_null': res.data.result[i]['is_null'],
+ 'is_expression': res.data.result[i]['is_expression'],
+ 'use_default': res.data.result[i]['use_default'],
+ 'value': res.data.result[i]['value']
+ });
+ }
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger Set arguments error'
+ );
+ }
+ });
+
+ var argname, argtype, argmode, default_args_count, default_args, arg_cnt;
+
+ var value_header = Backgrid.HeaderCell.extend({
+ // Add fixed width to the "value" column
+ className: 'width_percent_25'
+ });
+
+ var def_val_list = [],
+ gridCols = [
+ {name: 'name', label:'Name', type:'text', editable: false, cell:'string' },
+ {name: 'type', label:'Type', type: 'text', editable: false, cell:'string' },
+ {name: 'is_null', label:'Null?', type: 'boolean', cell: 'boolean' },
+ {name: 'expr', label:'Expression?', type: 'boolean', cellFunction: cellExprControlFunction, editable: disableExpressionControl },
+ {name: 'value', label:'Value', type: 'text', editable: true, cellFunction: cellFunction, headerCell: value_header },
+ {name: 'use_default', label:'Use Default?', type: 'boolean', cell:"boolean", editable: disableDefaultCell },
+ {name: 'default_value', label:'Default value', type: 'text', editable: false, cell:'string' }
+ ];
+
+ var my_obj = [];
+ var func_obj = [];
+
+ // Below will calculate the input argument id required to store in sqlite database
+ var input_arg_id = this.input_arg_id = [];
+ if (this.data['proargmodes'] != null) {
+ argmode_1 = this.data['proargmodes'].split(",");
+ for (k = 0; k < argmode_1.length; k++) {
+ if (argmode_1[k] == 'i' || argmode_1[k] == 'b') {
+ input_arg_id.push(k)
+ }
+ }
+ }
+ else {
+ argtype_1 = this.data['proargtypenames'].split(",");
+ for (k = 0; k < argtype_1.length; k++) {
+ input_arg_id.push(k)
+ }
+ }
+
+ argtype = this.data['proargtypenames'].split(",");
+
+ if (this.data['proargmodes'] != null) {
+ argmode = this.data['proargmodes'].split(",");
+ }
+
+ if (this.data['pronargdefaults']) {
+ default_args_count = this.data['pronargdefaults'];
+ default_args = this.data['proargdefaults'].split(",");
+ arg_cnt = default_args_count;
+ }
+
+ if (this.data['proargnames'] != null) {
+ argname = this.data['proargnames'].split(",");
+
+ // It will assign default values to "Default value" column
+ for (j = (argname.length - 1);j >= 0; j--) {
+ if (this.data['proargmodes'] != null) {
+ if (arg_cnt && (argmode[j] == 'i' || argmode[j] == 'b')) {
+ arg_cnt = arg_cnt - 1;
+ def_val_list[j] = default_args[arg_cnt]
+ }
+ else {
+ def_val_list[j] = "<No default value>";
+ }
+ }
+ else {
+ if (arg_cnt) {
+ arg_cnt = arg_cnt - 1;
+ def_val_list[j] = default_args[arg_cnt]
+ }
+ else {
+ def_val_list[j] = "<No default value>";
+ }
+ }
+ }
+
+ if (argtype.length != 0)
+ {
+ for (i = 0; i < argtype.length; i++) {
+ if (this.data['proargmodes'] != null) {
+ if (argmode[i] == 'i' || argmode[i] == 'b') {
+ var use_def_value = false
+ if (def_val_list[i] != "<No default value>") {
+ use_def_value = true;
+ }
+ my_obj.push({ "name": argname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+ }
+ }
+ else {
+ var use_def_value = false
+ if (def_val_list[i] != "<No default value>") {
+ use_def_value = true;
+ }
+ my_obj.push({ "name": argname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+ }
+
+
+ }
+ }
+
+ // Need to update the func_obj variable from sqlite database if available
+ // TODO: Need to check, how to update the value in Array fields....
+ if (func_args_data.length != 0) {
+ for (i = 0; i < func_args_data.length; i++) {
+ var index = func_args_data[i]['arg_id'];
+ func_obj.push({ "name": argname[index], "type": argtype[index], "is_null": func_args_data[i]['is_null'] ? true: false, "expr": func_args_data[i]['is_expression']? true: false, "value": func_args_data[i]['value'], "use_default": func_args_data[i]['use_default']? true: false, "default_value": def_val_list[index]});
+ }
+ }
+ }
+ else {
+ /*
+ Generate the name parameter if function do not have arguments name
+ like dbgparam1, dbgparam2 etc.
+ */
+ var myargname = [];
+
+ for (i = 0; i < argtype.length; i++) {
+ myargname[i] = "dbgparam" + (i+1);
+ }
+
+ // If there is no default arguments
+ if (!this.data['pronargdefaults']) {
+ for (i = 0; i < argtype.length; i++) {
+ my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": false, "default_value": "<No default value>"});
+ def_val_list[i] = "<No default value>";
+ }
+ }
+ else {
+ // If there is default arguments
+ //Below logic will assign default values to "Default value" column
+ for (j = (myargname.length - 1);j >= 0; j--) {
+ if (this.data['proargmodes'] == null) {
+ if (arg_cnt) {
+ arg_cnt = arg_cnt - 1;
+ def_val_list[j] = default_args[arg_cnt]
+ }
+ else {
+ def_val_list[j] = "<No default value>";
+ }
+ }
+ else {
+ if (arg_cnt && (argmode[j] == 'i' || argmode[j] == 'b')) {
+ arg_cnt = arg_cnt - 1;
+ def_val_list[j] = default_args[arg_cnt]
+ }
+ else {
+ def_val_list[j] = "<No default value>";
+ }
+ }
+ }
+
+ for (i = 0; i < argtype.length; i++) {
+ if (this.data['proargmodes'] == null) {
+ var use_def_value = false
+ if (def_val_list[i] != "<No default value>") {
+ use_def_value = true;
+ }
+ my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+ }
+ else {
+ if (argmode[i] == 'i' || argmode[i] == 'b') {
+ var use_def_value = false
+ if (def_val_list[i] != "<No default value>") {
+ use_def_value = true;
+ }
+ my_obj.push({ "name": myargname[i], "type": argtype[i], "use_default": use_def_value, "default_value": def_val_list[i]});
+ }
+ }
+ }
+ }
+
+ // Need to update the func_obj variable from sqlite database if available
+ // TODO: Need to check, how to update the value in Array fields....
+ if (func_args_data.length != 0) {
+ for (i = 0; i < func_args_data.length; i++) {
+ var index = func_args_data[i]['arg_id'];
+ func_obj.push({ "name": myargname[index], "type": argtype[index], "is_null": func_args_data[i]['is_null'] ? true: false, "expr": func_args_data[i]['is_expression']? true: false, "value": func_args_data[i]['value'], "use_default": func_args_data[i]['use_default']? true: false, "default_value": def_val_list[index]});
+ }
+ }
+ }
+
+ // Check if the arguments already available in the sqlite database then we should use the existing arguments
+ if (func_args_data.length == 0) {
+ var debuggerInputArgsColl = this.debuggerInputArgsColl = new DebuggerInputArgCollections(my_obj);
+ }
+ else {
+ var debuggerInputArgsColl = this.debuggerInputArgsColl = new DebuggerInputArgCollections(func_obj);
+ }
+
+ // Initialize a new Grid instance
+ if (this.grid) {
+ this.grid.remove();
+ this.grid = null;
+ }
+ var grid = this.grid = new Backgrid.Grid({
+ columns: gridCols,
+ collection: debuggerInputArgsColl,
+ className: "backgrid table-bordered"
+ });
+
+ grid.render();
+ $(this.elements.content).html(grid.el);
+ },
+ setup:function() {
+ return {
+ buttons:[{ text: "Debug", key: 27, className: "btn btn-primary" },
+ { text: "Cancel", key: 27, className: "btn btn-primary" }],
+ options: { modal: 0, resizable: true }
+ };
+ },
+ // Callback functions when click on the buttons of the Alertify dialogs
+ callback: function(e) {
+ if (e.button.text === "Debug") {
+
+ // Initialize the target once the debug button is clicked and
+ // create asynchronous connection and unique transaction ID
+ var self = this;
+
+ // If the debugging is started again then treeInfo is already stored in this.data so we can use the same.
+ if (self.restart_debug == 0) {
+ var t = pgBrowser.tree,
+ i = t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
+ }
+
+ var args_value_list = [];
+ var sqlite_func_args_list = this.sqlite_func_args_list = [];
+ var int_count = 0;
+
+ this.grid.collection.each(function(m) {
+
+ // TODO: Removed temporary for testing.....
+ // Check if value is set to NULL then we should ignore the value field
+ if (m.get('is_null')) {
+ args_value_list.push({ 'name': m.get('name'),
+ 'type': m.get('type'),
+ 'value': 'NULL'});
+ }
+ else {
+ // Check if default value to be used or not
+ if (m.get('use_default')) {
+ args_value_list.push({ 'name': m.get('name'),
+ 'type': m.get('type'),
+ 'value': m.get('default_value')});
+ }
+ else {
+ args_value_list.push({ 'name': m.get('name'),
+ 'type': m.get('type'),
+ 'value': m.get('value')});
+ }
+ }
+
+ if (self.restart_debug == 0) {
+ // Below will format the data to be stored in sqlite database
+ sqlite_func_args_list.push({
+ 'server_id': treeInfo.server._id,
+ 'database_id': treeInfo.database._id,
+ 'schema_id': treeInfo.schema._id ,
+ 'function_id': d._type == "function" ? treeInfo.function._id : treeInfo.procedure._id,
+ 'arg_id': self.input_arg_id[int_count],
+ 'is_null': m.get('is_null') ? 1 : 0,
+ 'is_expression': m.get('expr') ? 1 : 0,
+ 'use_default': m.get('use_default') ? 1 : 0,
+ 'value': m.get('value')
+ });
+ }
+ else {
+ // Below will format the data to be stored in sqlite database
+ sqlite_func_args_list.push({
+ 'server_id': self.data.server_id,
+ 'database_id': self.data.database_id,
+ 'schema_id': self.data.schema_id ,
+ 'function_id': self.data.function_id,
+ 'arg_id': self.input_arg_id[int_count],
+ 'is_null': m.get('is_null') ? 1 : 0,
+ 'is_expression': m.get('expr') ? 1 : 0,
+ 'use_default': m.get('use_default') ? 1 : 0,
+ 'value': m.get('value')
+ });
+ }
+
+ int_count = int_count + 1;
+ });
+
+ // If debugging is not started again then we should initialize the target otherwise not
+ if (self.restart_debug == 0) {
+ if (d._type == "function") {
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "direct/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
+ }
+ else {
+ var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "direct/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.procedure._id;
+ }
+
+ $.ajax({
+ url: baseUrl,
+ method: 'POST',
+ data:{'data':JSON.stringify(args_value_list)},
+ success: function(res) {
+
+ var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
+
+ pgBrowser.Events.once(
+ 'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
+ frame.openURL(url);
+ });
+
+ // Create the debugger panel as per the data received from user input dialog.
+ var dashboardPanel = pgBrowser.docker.findPanels(
+ 'dashboard'
+ ),
+ panel = pgBrowser.docker.addPanel(
+ 'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
+ );
+
+ panel.focus();
+
+ // Panel Closed event
+ panel.on(wcDocker.EVENT.CLOSED, function() {
+ var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
+ $.ajax({
+ url: closeUrl,
+ method: 'GET'
+ });
+ });
+
+ if (d._type == "function") {
+ var _Url = "{{ url_for('debugger.index') }}" + "set_arguments/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
+ }
+ else {
+ var _Url = "{{ url_for('debugger.index') }}" + "set_arguments/" + treeInfo.server._id +
+ "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.procedure._id;
+ }
+
+ $.ajax({
+ url: _Url,
+ method: 'POST',
+ data:{'data':JSON.stringify(sqlite_func_args_list)},
+ success: function(res) {
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger Set arguments error'
+ );
+ }
+ });
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger target Initialize Error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }
+ else {
+ // If the debugging is started again then we should only set the arguments and start the listener again
+ var baseUrl = "{{ url_for('debugger.index') }}" + "start_listener/" + self.data.trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'POST',
+ data:{'data':JSON.stringify(args_value_list)},
+ success: function(res) {
+ //TODO: Anything required ? .....
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger listener starting error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+
+ // Set the new input arguments given by the user during debugging
+ var _Url = "{{ url_for('debugger.index') }}" + "set_arguments/" + self.data.server_id +
+ "/" + self.data.database_id + "/" + self.data.schema_id + "/" + self.data.function_id;
+
+ $.ajax({
+ url: _Url,
+ method: 'POST',
+ data:{'data':JSON.stringify(sqlite_func_args_list)},
+ success: function(res) {
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger Set arguments error'
+ );
+ }
+ });
+
+ }
+
+ return true;
+ }
+
+ if (e.button.text === "Cancel") {
+ //close the dialog...
+ return false;
+ }
+ },
+ build:function() {
+ },
+ prepare:function() {
+ /*
+ If we already have data available in sqlite database then we should enable the debug button otherwise
+ disable the debug button.
+ */
+ if (this.func_args_data.length == 0) {
+ this.__internal.buttons[0].element.disabled = true;
+ }
+ else {
+ this.__internal.buttons[0].element.disabled = false;
+ }
+
+ /*
+ Listen to the grid change event so that if any value changed by user then we can enable/disable the
+ debug button.
+ */
+ this.grid.listenTo(this.debuggerInputArgsColl,"backgrid:edited",
+ (function(obj) {
+
+ return function() {
+
+ var self = this;
+ var enable_btn = false;
+
+ for (i = 0; i < this.collection.length; i++ ) {
+
+ // TODO: Need to check the "NULL" and "Expression" column value to enable/disable the "Debug" button
+ if (this.collection.models[i].get('value') == "" ||
+ this.collection.models[i].get('value') == null ||
+ this.collection.models[i].get('value') == undefined) {
+ enable_btn = true;
+
+ if (this.collection.models[i].get('use_default')) {
+ obj.__internal.buttons[0].element.disabled = false;
+ }
+ else{
+ obj.__internal.buttons[0].element.disabled = true;
+ break;
+ }
+ }
+ }
+ if (!enable_btn)
+ obj.__internal.buttons[0].element.disabled = false;
+ }
+ }
+ )(this)
+ );
+ }
+ };
+ });
+ }
+
+ Alertify.debuggerInputArgsDialog('Debugger',args, restart_debug).resizeTo('60%', '60%');
+
+ };
+
+ return res;
+});
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
new file mode 100644
index 0000000..bc86b0d
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
@@ -0,0 +1,1388 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin','pgadmin.browser',
+ 'backbone', 'backgrid', 'codemirror', 'backform','pgadmin.tools.debugger.ui',
+ 'wcdocker', 'pgadmin.backform', 'pgadmin.backgrid', 'codemirror/addon/selection/active-line'],
+ function($, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, CodeMirror, Backform, debug_function_again) {
+
+ if (pgAdmin.Browser.tree != null) {
+ pgAdmin = pgAdmin || window.pgAdmin || {};
+ }
+
+ var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+
+ if (pgTools.DirectDebug)
+ return pgTools.DirectDebug;
+
+ var controller = new (function() {});
+
+ _.extend(
+ controller, Backbone.Events, {
+ enable: function(btn, enable) {
+ // trigger the event and change the button view to enable/disable the buttons for debugging
+ this.trigger('pgDebugger:button:state:' + btn, enable);
+ },
+
+ /*
+ Function to set the breakpoint and send the line no. which is set to server
+ trans_id :- Unique Transaction ID, line_no - line no. to set the breakpoint, set_type = 0 - clear , 1 - set
+ */
+ set_breakpoint: function(trans_id, line_no, set_type) {
+ var self = this;
+
+ // Make ajax call to set/clear the break point by user
+ var baseUrl = "{{ url_for('debugger.index') }}" + "set_breakpoint/" + trans_id + "/" + line_no + "/" + set_type;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ // Breakpoint has been set by the user
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Breakpoint set execution error'
+ );
+ }
+ });
+ },
+
+ // Function to get the latest breakpoint information and update the gutters of codemirror
+ UpdateBreakpoint: function(trans_id) {
+ var self = this;
+
+ var br_list = self.GetBreakpointInformation(trans_id);
+
+ // If there is no break point to clear then we should return from here.
+ if ((br_list.length == 1) && (br_list[0].linenumber == -1))
+ return;
+
+ var breakpoint_list = new Array();
+
+ for (i = 0; i < br_list.length; i++) {
+ if (br_list[i].linenumber != -1) {
+ breakpoint_list.push(br_list[i].linenumber)
+ }
+ }
+
+ for (i = 0;i< breakpoint_list.length;i++) {
+ var info = pgTools.DirectDebug.editor.lineInfo((breakpoint_list[i] - 1));
+
+ if (info.gutterMarkers != undefined) {
+ pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", null);
+ }
+ else {
+ pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", function() {
+ var marker = document.createElement("div");
+ marker.style.color = "#822";
+ marker.innerHTML = "●";
+ return marker;
+ }());
+ }
+ }
+ },
+
+ // Function to get the breakpoint information from the server
+ GetBreakpointInformation: function(trans_id) {
+ var self = this;
+ var result = '';
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "get_breakpoints";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ async: false,
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ result = res.data.result;
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Error fetching breakpoint information'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Error fetching breakpoint information'
+ );
+ }
+ });
+
+ return result;
+ },
+
+ // Function to start the executer and execute the user requested option for debugging
+ start_execution: function(trans_id, port_num) {
+ var self = this;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "start_execution/" + trans_id + "/" + port_num;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // If status is Success then find the port number to attach the executer.
+ self.execute_query(trans_id);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Start execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Start execution error'
+ );
+ }
+ });
+ },
+
+ // Execute the query and get the first functions debug information from the server
+ execute_query: function(trans_id) {
+ var self = this;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "wait_for_breakpoint";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // set the return code to the code editor text area
+ if (res.data.result[0].src != null && res.data.result[0].linenumber != null) {
+ pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+ active_line_no = self.active_line_no = (res.data.result[0].linenumber - 2);
+ pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+ }
+
+ // Call function to create and update local variables ....
+ self.GetLocalVariables(trans_id);
+ self.GetStackInformation(trans_id);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Execution error'
+ );
+ }
+ });
+ },
+
+ // Get the local variable information of the functions and update the grid
+ GetLocalVariables: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "get_variables";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // Call function to create and update local variables
+ self.AddLocalVariables(res.data.result);
+ self.AddParameters(res.data.result);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Error fetching variables information'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Error fetching variables information'
+ );
+ }
+ });
+ },
+
+ // Get the stack information of the functions and update the grid
+ GetStackInformation: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "get_stack_info";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // Call function to create and update stack information
+ self.AddStackInformation(res.data.result);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Error fetching stack information'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Error fetching stack information'
+ );
+ }
+ });
+ },
+
+ /*
+ poll the actual result after user has executed the "continue", "step-into", "step-over" actions and get the
+ other updated information from the server.
+ */
+ poll_result: function(trans_id) {
+ var self = this;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "poll_result/" + trans_id;
+
+
+ setTimeout(
+ function() {
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ // If no result then poll again to wait for results.
+ if (res.data.result == null || res.data.result.length == 0) {
+ // NEEL: THIS IS CONDITION IS ADDED
+ self.poll_result(trans_id);
+ }
+ else {
+ if (res.data.result[0].src != undefined || res.data.result[0].src != null) {
+ pgTools.DirectDebug.docker.finishLoading(500);
+ pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+ self.UpdateBreakpoint(trans_id);
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+ self.active_line_no = (res.data.result[0].linenumber - 2);
+
+ // Update the stack, local variables and parameters information
+ self.GetStackInformation(trans_id);
+ self.GetLocalVariables(trans_id);
+ }
+ else if (!pgTools.DirectDebug.debug_type && !pgTools.DirectDebug.first_time_indirect_debug) {
+ pgTools.DirectDebug.docker.finishLoading(500);
+ if (self.active_line_no != undefined) {
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ }
+ self.clear_all_breakpoint(trans_id);
+ self.execute_query(trans_id);
+ pgTools.DirectDebug.first_time_indirect_debug = true;
+ }
+ else {
+ pgTools.DirectDebug.docker.finishLoading(500);
+ // If the source is really changed then only update the breakpoint information
+ if (res.data.result[0].src != pgTools.DirectDebug.editor.getValue()) {
+ pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+ self.UpdateBreakpoint(trans_id);
+ }
+
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+ self.active_line_no = (res.data.result[0].linenumber - 2);
+
+ // Update the stack, local variables and parameters information
+ self.GetStackInformation(trans_id);
+ self.GetLocalVariables(trans_id);
+ }
+
+ // Enable all the buttons as we got the results
+ self.enable('stop', true);
+ self.enable('step_over', true);
+ self.enable('step_into', true);
+ self.enable('continue', true);
+ self.enable('toggle_breakpoint', true);
+ self.enable('clear_all_breakpoints', true);
+ }
+ }
+ else if (res.data.status === 'Busy') {
+ // If status is Busy then poll the result by recursive call to the poll function
+ if (!pgTools.DirectDebug.debug_type) {
+ pgTools.DirectDebug.docker.startLoading('{{ _('Waiting for another session to invoke the target...') }}');
+ // As we are waiting for another session to invoke the target so disable all the buttons
+ self.enable('stop', false);
+ self.enable('step_over', false);
+ self.enable('step_into', false);
+ self.enable('continue', false);
+ self.enable('toggle_breakpoint', false);
+ self.enable('clear_all_breakpoints', false);
+ pgTools.DirectDebug.first_time_indirect_debug = false;
+ self.poll_result(trans_id);
+ }
+ else {
+ //TODO: NEEL ADDED
+ self.poll_result(trans_id);
+ }
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger Poll Result Error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger Poll Result Error'
+ );
+ }
+ });
+ }, 1000 );
+
+ },
+
+ /*
+ For the direct debugging, we need to check weather the functions execution is completed or not. After completion
+ of the debugging, we will stop polling the result until new execution starts.
+ */
+ poll_end_execution_result: function(trans_id) {
+ var self = this;
+ //return;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "poll_end_execution_result/" + trans_id;
+
+ setTimeout(
+ function() {
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ if(res.data.result == undefined ) {
+ /*
+ "result" is undefined only in case of EDB procedure. As Once the EDB procedure execution is completed
+ then we are not getting any result so we need ignore the result.
+ */
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.direct_execution_completed = true;
+
+ //Set the alertify message to inform the user that execution is completed.
+ Alertify.notify(
+ res.info,
+ 'success',
+ 3,
+ function() { }
+ );
+
+ // Update the message tab of the debugger
+ pgTools.DirectDebug.dbmsMessages.$elem.text(res.data.status_message);
+
+ // Execution completed so disable the buttons other than "Continue/Start" button because user can still
+ // start the same execution again.
+ self.enable('stop', false);
+ self.enable('step_over', false);
+ self.enable('step_into', false);
+ self.enable('toggle_breakpoint', false);
+ self.enable('clear_all_breakpoints', false);
+ }
+ else {
+ // Call function to create and update local variables ....
+ if (res.data.result.name != null) {
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ self.AddResults(res.data.result);
+ pgTools.DirectDebug.paramsTabFrame.tab(3,true);
+ pgTools.DirectDebug.direct_execution_completed = true;
+
+ //Set the alertify message to inform the user that execution is completed.
+ Alertify.notify(
+ res.info,
+ 'success',
+ 3,
+ function() { }
+ );
+
+ // Update the message tab of the debugger
+ pgTools.DirectDebug.dbmsMessages.$elem.text(res.data.status_message);
+
+ // Execution completed so disable the buttons other than "Continue/Start" button because user can still
+ // start the same execution again.
+ self.enable('stop', false);
+ self.enable('step_over', false);
+ self.enable('step_into', false);
+ self.enable('toggle_breakpoint', false);
+ self.enable('clear_all_breakpoints', false);
+
+ //TODO: Continue button should be enable and user will be able to do next debugging with same function.
+ //self.enable('continue', false);
+
+ // NEEL: TODO: Added Execution is completed so again poll the result until user start the another debugging
+ //self.poll_end_execution_result(pgTools.DirectDebug.trans_id);
+ }
+ }
+ }
+ else if (res.data.status === 'Busy') {
+ // If status is Busy then poll the result by recursive call to the poll function
+ //self.poll_end_execution_result(trans_id);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger poll end execution error',
+ res.data.result
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger poll end execution error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }, 1200);
+
+ },
+
+ Restart: function(trans_id) {
+
+ var baseUrl = "{{ url_for('debugger.index') }}" + "restart/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ success: function(res) {
+ // Restart the same function debugging with previous arguments
+ var restart_dbg = res.data.restart_debug ? 1 : 0;
+ debug_function_again(res.data.result, restart_dbg);
+ },
+ error: function(xhr, status, error) {
+ try {
+ var err = $.parseJSON(xhr.responseText);
+ if (err.success == 0) {
+ msg = S('{{ _(' + err.errormsg + ')}}').value();
+ Alertify.alert("{{ _('" + err.errormsg + "') }}");
+ }
+ } catch (e) {}
+ }
+ });
+ },
+
+ // Continue the execution until the next breakpoint
+ Continue: function(trans_id) {
+ var self = this;
+
+ //Check first if previous execution was completed or not
+ if (pgTools.DirectDebug.direct_execution_completed) {
+ pgTools.DirectDebug.direct_execution_completed = false;
+ // TODO: We need to get the arguments given by the user from sqlite database
+ self.Restart(trans_id);
+ }
+ else {
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "continue";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.poll_result(trans_id);
+ if (pgTools.DirectDebug.debug_type) {
+ self.poll_end_execution_result(trans_id);
+ }
+ //NEEL: ADDED
+ //self.poll_end_execution_result(trans_id);
+ }
+ else {
+ Alertify.alert(
+ 'Debugger: Continue execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Continue execution error'
+ );
+ }
+ });
+ }
+ },
+
+ Step_over: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "step_over";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.poll_result(trans_id);
+ if (pgTools.DirectDebug.debug_type) {
+ self.poll_end_execution_result(trans_id);
+ }
+ }
+ else {
+ Alertify.alert(
+ 'Debugger: Step over execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Step over execution error'
+ );
+ }
+ });
+ },
+
+ Step_into: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "step_into";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.poll_result(trans_id);
+ if (pgTools.DirectDebug.debug_type) {
+ self.poll_end_execution_result(trans_id);
+ }
+ }
+ else {
+ Alertify.alert(
+ 'Debugger: Step into execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Step into execution error'
+ );
+ }
+ });
+ },
+
+ Stop: function(trans_id) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "execute_query/" + trans_id + "/" + "abort_target";
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ // Call function to create and update local variables ....
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.direct_execution_completed = true;
+
+ //Set the alertify message to inform the user that execution is completed.
+ Alertify.notify(
+ res.info,
+ 'success',
+ 3,
+ function() { }
+ );
+
+ //Disable the buttons other than continue button. If user wants to again then it should allow to debug again...
+ self.enable('stop', false);
+ self.enable('step_over', false);
+ self.enable('step_into', false);
+ self.enable('continue', false);
+ self.enable('toggle_breakpoint', false);
+ self.enable('clear_all_breakpoints', false);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Stop execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Stop execution error'
+ );
+ }
+ });
+ },
+
+ toggle_breakpoint: function(trans_id) {
+ var self = this;
+
+ var info = pgTools.DirectDebug.editor.lineInfo(self.active_line_no);
+ var baseUrl = '';
+
+ // If gutterMarker is undefined that means there is no marker defined previously
+ // So we need to set the breakpoint command here...
+ if (info.gutterMarkers == undefined) {
+ baseUrl = "{{ url_for('debugger.index') }}" + "set_breakpoint/" + trans_id + "/" + self.active_line_no + "/" + "1";
+ }
+ else {
+ baseUrl = "{{ url_for('debugger.index') }}" + "set_breakpoint/" + trans_id + "/" + self.active_line_no + "/" + "0";
+ }
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ // Call function to create and update local variables ....
+ var info = pgTools.DirectDebug.editor.lineInfo(self.active_line_no);
+
+ if (info.gutterMarkers != undefined) {
+ pgTools.DirectDebug.editor.setGutterMarker(self.active_line_no, "breakpoints", null);
+ }
+ else {
+ pgTools.DirectDebug.editor.setGutterMarker(self.active_line_no, "breakpoints", function() {
+ var marker = document.createElement("div");
+ marker.style.color = "#822";
+ marker.innerHTML = "●";
+ return marker;
+ }());
+ }
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Debugger: Toggle breakpoint execution error'
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Toggle breakpoint execution error'
+ );
+ }
+ });
+ },
+
+ clear_all_breakpoint: function(trans_id) {
+ var self = this;
+
+ var br_list = self.GetBreakpointInformation(trans_id);
+
+ // If there is no break point to clear then we should return from here.
+ if ((br_list.length == 1) && (br_list[0].linenumber == -1))
+ return;
+
+ var breakpoint_list = new Array();
+
+ for (i = 0; i < br_list.length; i++) {
+ if (br_list[i].linenumber != -1) {
+ breakpoint_list.push(br_list[i].linenumber)
+ }
+ }
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "clear_all_breakpoint/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'POST',
+ data: { 'breakpoint_list': breakpoint_list.join() },
+ success: function(res) {
+ if (res.data.status) {
+ for (i = 0;i< breakpoint_list.length;i++) {
+ var info = pgTools.DirectDebug.editor.lineInfo((breakpoint_list[i] - 1));
+
+ if (info) {
+ if (info.gutterMarkers != undefined) {
+ pgTools.DirectDebug.editor.setGutterMarker((breakpoint_list[i] - 1), "breakpoints", null);
+ }
+ }
+ }
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Clear all breakpoint execution error'
+ );
+ }
+ });
+ },
+
+ AddStackInformation: function(result) {
+ var self = this;
+
+ // Remove the existing created grid and update the stack values
+ if (self.stack_grid) {
+ self.stack_grid.remove();
+ self.stack_grid = null;
+ }
+
+ pgTools.DirectDebug.stackTab.$elem.empty();
+
+ var DebuggerStackModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined,
+ value: undefined,
+ line_no: undefined
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var StackCollection = Backbone.Collection.extend({
+ model: DebuggerStackModel
+ });
+
+ stackGridCols = [
+ {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+ {name: 'value', label:'Value', type:'text', editable: false, cell:'string'},
+ {name: 'line_no', label:'Line No.', type:'text', editable: false, cell:'string'}
+ ];
+
+ var my_obj = [];
+ if (result.length != 0)
+ {
+ for (i = 0; i < result.length; i++) {
+ // TODO: change the my_func_test_2 with name of the function to be executed.
+ my_obj.push({ "name": result[i].targetname, "value": result[i].args, "line_no": result[i].linenumber });
+ }
+ }
+
+ var stackColl = this.stackColl = new StackCollection(my_obj);
+ this.stackColl.on('backgrid:row:selected', self.select_frame, self);
+
+ // Initialize a new Grid instance
+ var stack_grid = this.stack_grid = new Backgrid.Grid({
+ columns: stackGridCols,
+ row: Backgrid.Row.extend({
+ highlightColor: "#D9EDF7",
+ disabledColor: "#F1F1F1",
+ events: {
+ click: "rowClick"
+ },
+ rowClick: function(e) {
+ //Find which row is selected and depending on that send the frame id
+ for (i = 0; i < this.model.collection.length; i++) {
+ if (this.model.collection.models[i].get('name') == this.model.get('name')) {
+ self.frame_id_ = i;
+ break;
+ }
+ }
+ this.model.trigger('backgrid:row:selected', this);
+ self.stack_grid.$el.find("td").css("background-color", this.disabledColor);
+ this.$el.find("td").css("background-color", this.highlightColor);
+ }
+ }),
+ collection: stackColl,
+ className: "backgrid table-bordered"
+ });
+
+ stack_grid.render();
+ pgTools.DirectDebug.stackTab.$elem.append(stack_grid.el);
+ },
+
+ AddResults: function(result) {
+ var self = this;
+
+ // Remove the existing created grid and update the result values
+ if (self.result_grid) {
+ self.result_grid.remove();
+ self.result_grid = null;
+ }
+
+ pgTools.DirectDebug.retResults.$elem.empty();
+
+ var DebuggerResultsModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var ResultsCollection = Backbone.Collection.extend({
+ model: DebuggerResultsModel
+ });
+
+ resultGridCols = [
+ {name: 'value', label:result.name, type:'text', editable: false, cell:'string'}
+ ];
+
+ var my_obj = [];
+ if (result.value.length != 0)
+ {
+ for (i = 0; i < result.value.length; i++) {
+ // TODO: change the my_func_test_2 with name of the function to be executed.
+ my_obj.push({ "value": result.value[i]});
+ }
+ }
+
+ // Initialize a new Grid instance
+ var result_grid = this.result_grid = new Backgrid.Grid({
+ columns: resultGridCols,
+ collection: new ResultsCollection(my_obj),
+ className: "backgrid table-bordered"
+ });
+
+ result_grid.render();
+ pgTools.DirectDebug.retResults.$elem.append(result_grid.el);
+ },
+
+ AddLocalVariables: function(result) {
+ var self = this;
+
+ // Remove the existing created grid and update the variables values
+ if (self.variable_grid) {
+ self.variable_grid.remove();
+ self.variable_grid = null;
+ }
+
+ pgTools.DirectDebug.localVars.$elem.empty();
+
+ var DebuggerVariablesModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined,
+ type: undefined,
+ value: undefined
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var VariablesCollection = Backbone.Collection.extend({
+ model: DebuggerVariablesModel
+ });
+
+ gridCols = [
+ {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+ {name: 'type', label:'Type', type: 'text', editable: false, cell:'string'},
+ {name: 'value', label:'Value', type: 'text', cell: 'string'}
+ ];
+
+ var my_obj = [];
+ if (result.length != 0)
+ {
+ for (i = 0; i < result.length; i++) {
+ if (result[i].varclass == 'L') {
+ my_obj.push({ "name": result[i].name, "type": result[i].dtype, "value": result[i].value});
+ }
+ }
+ }
+
+ // Initialize a new Grid instance
+ var variable_grid = this.variable_grid = new Backgrid.Grid({
+ columns: gridCols,
+ collection: new VariablesCollection(my_obj),
+ className: "backgrid table-bordered"
+ });
+
+ variable_grid.render();
+ pgTools.DirectDebug.localVars.$elem.append(variable_grid.el);
+ },
+
+ AddParameters: function(result) {
+ var self = this;
+
+ // Remove the existing created grid and update the parameter values
+ if (self.param_grid) {
+ self.param_grid.remove();
+ self.param_grid = null;
+ }
+
+ pgTools.DirectDebug.paramsTab.$elem.empty();
+
+ var DebuggerParametersModel = Backbone.Model.extend({
+ defaults: {
+ name: undefined,
+ type: undefined,
+ value: undefined
+ }
+ });
+
+ // Collection which contains the model for function informations.
+ var ParametersCollection = self.ParametersCollection = Backbone.Collection.extend({
+ model: DebuggerParametersModel
+ });
+
+ self.ParametersCollection.prototype.on('change', self.deposit_parameter_value, self);
+
+ paramGridCols = [
+ {name: 'name', label:'Name', type:'text', editable: false, cell:'string'},
+ {name: 'type', label:'Type', type: 'text', editable: false, cell:'string'},
+ {name: 'value', label:'Value', type: 'text', cell: 'string'}
+ ];
+
+ var param_obj = [];
+ if (result.length != 0)
+ {
+ for (i = 0; i < result.length; i++) {
+ if (result[i].varclass == 'A') {
+ param_obj.push({ "name": result[i].name, "type": result[i].dtype, "value": result[i].value});
+ }
+ }
+ }
+
+ // Initialize a new Grid instance
+ var param_grid = this.param_grid = new Backgrid.Grid({
+ columns: paramGridCols,
+ collection: new ParametersCollection(param_obj),
+ className: "backgrid table-bordered"
+ });
+
+ param_grid.render();
+ pgTools.DirectDebug.paramsTab.$elem.append(param_grid.el);
+ },
+
+ deposit_parameter_value: function(model) {
+ var self = this;
+
+ // variable name and value list that is changed by user
+ var name_value_list = [];
+
+ name_value_list.push({ 'name': model.get('name'),'type': model.get('type'), 'value': model.get('value')});
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "deposit_value/" + pgTools.DirectDebug.trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'POST',
+ data:{'data':JSON.stringify(name_value_list)},
+ success: function(res) {
+ if (res.data.status) {
+ // Get the updated variables value
+ self.GetLocalVariables(pgTools.DirectDebug.trans_id);
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Deposit value execution error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ },
+
+ select_frame: function(model, selected) {
+ var self = this;
+
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "select_frame/" + pgTools.DirectDebug.trans_id + "/" + self.frame_id_;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
+ self.UpdateBreakpoint(pgTools.DirectDebug.trans_id);
+ //active_line_no = self.active_line_no = (res.data.result[0].linenumber - 2);
+ pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2), 'wrap', 'CodeMirror-activeline-background');
+
+ // Call function to create and update local variables ....
+ self.GetLocalVariables(pgTools.DirectDebug.trans_id);
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger: Select frame execution error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ },
+ }
+ )
+
+ /*
+ Debugger tool var view to create the button toolbar and listen to the button click event and inform the
+ controller about the click and controller will take the action for the specified button click.
+ */
+ var DebuggerToolbarView = Backbone.View.extend({
+ el: '#btn-toolbar',
+ initialize: function() {
+ controller.on('pgDebugger:button:state:stop', this.enable_stop, this);
+ controller.on('pgDebugger:button:state:step_over', this.enable_step_over, this);
+ controller.on('pgDebugger:button:state:step_into', this.enable_step_into, this);
+ controller.on('pgDebugger:button:state:continue', this.enable_continue, this);
+ controller.on('pgDebugger:button:state:toggle_breakpoint', this.enable_toggle_breakpoint, this);
+ controller.on('pgDebugger:button:state:clear_all_breakpoints', this.enable_clear_all_breakpoints, this);
+ },
+ events: {
+ 'click .btn-stop': 'on_stop',
+ 'click .btn-clear-breakpoint': 'on_clear_all_breakpoint',
+ 'click .btn-toggle-breakpoint': 'on_toggle_breakpoint',
+ 'click .btn-continue': 'on_continue',
+ 'click .btn-step-over': 'on_step_over',
+ 'click .btn-step-into': 'on_step_into'
+ },
+ enable_stop: function(enable) {
+ var $btn = this.$el.find('.btn-stop');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_step_over: function(enable) {
+ var $btn = this.$el.find('.btn-step-over');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_step_into: function(enable) {
+ var $btn = this.$el.find('.btn-step-into');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_continue: function(enable) {
+ var $btn = this.$el.find('.btn-continue');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_toggle_breakpoint: function(enable) {
+ var $btn = this.$el.find('.btn-toggle-breakpoint');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+ enable_clear_all_breakpoints: function(enable) {
+ var $btn = this.$el.find('.btn-clear-breakpoint');
+
+ if (enable) {
+ $btn.prop('disabled', false);
+ $btn.removeAttr('disabled');
+ } else {
+ $btn.prop('disabled', true);
+ $btn.attr('disabled', 'disabled');
+ }
+ },
+
+ on_stop: function() {
+ controller.Stop(pgTools.DirectDebug.trans_id);
+ },
+ on_clear_all_breakpoint: function() {
+ controller.clear_all_breakpoint(pgTools.DirectDebug.trans_id);
+ },
+ on_toggle_breakpoint: function() {
+ controller.toggle_breakpoint(pgTools.DirectDebug.trans_id);
+ },
+ on_continue: function() {
+ controller.Continue(pgTools.DirectDebug.trans_id);
+ },
+ on_step_over: function() {
+ controller.Step_over(pgTools.DirectDebug.trans_id);
+ },
+ on_step_into: function() {
+ controller.Step_into(pgTools.DirectDebug.trans_id);
+ },
+ });
+
+
+ /*
+ Function is responsible to create the new wcDocker instance for debugger and initialize the debugger panel inside
+ the docker instance.
+ */
+ var DirectDebug = function() {};
+
+ _.extend(DirectDebug.prototype, {
+ init: function(trans_id, debug_type) { /* We should get the transaction id from the server during initialization here */
+ // We do not want to initialize the module multiple times.
+
+ var self = this;
+ _.bindAll(pgTools.DirectDebug, 'messages');
+
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+ this.trans_id = trans_id;
+ this.debug_type = debug_type;
+ this.first_time_indirect_debug = false;
+ this.direct_execution_completed = false;
+
+ var docker = this.docker = new wcDocker(
+ '#container', {
+ allowContextMenu: false,
+ allowCollapse: false,
+ themePath: '{{ url_for('static', filename='css/wcDocker/Themes') }}',
+ theme: 'pgadmin'
+ });
+
+ this.panels = [];
+
+ // Below code will be executed for indirect debugging
+ // indirect debugging - 0 and for direct debugging - 1
+ if (trans_id != undefined && !debug_type) {
+ // Make ajax call to execute the and start the target for execution
+ var baseUrl = "{{ url_for('debugger.index') }}" + "start_listener/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.intializePanels();
+ controller.poll_result(trans_id);
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger listener starting error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }
+ else if (trans_id != undefined && debug_type)
+ {
+ // Make ajax call to execute the and start the target for execution
+ var baseUrl = "{{ url_for('debugger.index') }}" + "start_listener/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status) {
+ self.messages(trans_id);
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger listener starting error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }
+ else
+ this.intializePanels();
+ },
+
+ // Read the messages of the database server and get the port ID and attach the executer to that port.
+ messages: function(trans_id) {
+ var self = this;
+ // Make ajax call to listen the database message
+ var baseUrl = "{{ url_for('debugger.index') }}" + "messages/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ if (res.data.status === 'Success') {
+ self.intializePanels();
+ // If status is Success then find the port number to attach the executer.
+ //self.start_execution(trans_id, res.data.result);
+ controller.start_execution(trans_id, res.data.result);
+ }
+ else if (res.data.status === 'Busy') {
+ // If status is Busy then poll the result by recursive call to the poll function
+ self.messages(trans_id);
+ }
+ else if (res.data.status === 'NotConnected') {
+ Alertify.alert(
+ 'Data Grid Poll Result Error',
+ res.data.result
+ );
+ }
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger listener starting error'
+ );
+ }
+ });
+
+ },
+
+ // Callback function when user click on gutters of codemirror to set/clear the breakpoint
+ onBreakPoint: function(cm, m) {
+ var self = this;
+
+ // TODO::
+ // We may want to check, if break-point is allowed at this moment or not
+ var info = cm.lineInfo(m);
+
+ // If gutterMarker is undefined that means there is no marker defined previously
+ // So we need to set the breakpoint command here...
+ if (info.gutterMarkers == undefined) {
+ controller.set_breakpoint(self.trans_id,m+1,1); //set the breakpoint
+ }
+ else {
+ controller.set_breakpoint(self.trans_id,m+1,0); //clear the breakpoint
+ }
+
+ cm.setGutterMarker(
+ m, "breakpoints", info.gutterMarkers ? null : function() {
+ var marker = document.createElement("div");
+
+ marker.style.color = "#822";
+ marker.innerHTML = "●";
+
+ return marker;
+ }());
+ },
+
+ // Create the debugger layout with splitter and display the appropriate data received from server.
+ intializePanels: function() {
+ var self = this;
+ this.registerPanel(
+ 'code', false, '100%', '100%',
+ function(panel) {
+ var container = panel.layout().scene().find('.pg-debugger-panel');
+
+ // Create the wcSplitter used by wcDocker to split the single panel.
+ var hSplitter = new wcSplitter(
+ container, panel,
+ wcDocker.ORIENTATION.VERTICAL
+ );
+
+ hSplitter.scrollable(0, false, false);
+ hSplitter.scrollable(1, true, true);
+
+ // Initialize this splitter with a layout in each pane.
+ hSplitter.initLayouts(wcDocker.LAYOUT.SIMPLE, wcDocker.LAYOUT.SIMPLE);
+
+ // By default, the splitter splits down the middle, we split the main panel by 80%.
+ hSplitter.pos(0.65);
+
+ var $params = $('<div class="full-container params"></div>');
+ hSplitter.right().addItem($params);
+
+ // Add Local parameters tab to display function arguments value
+ var paramsTabFrame = self.paramsTabFrame = new wcTabFrame($params, panel);
+ var paramsTab = self.paramsTab = paramsTabFrame.addTab(
+ '{{_('Parameters')}}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ paramsTab.addItem($('<div id=parameters class="info"></div>'));
+
+ // Add Local variables tab
+ var localVars = self.localVars = paramsTabFrame.addTab(
+ '{{ _('Local variables') }}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ localVars.addItem($('<div id=local_variables class="info"></div>'));
+
+ // Add DBMS messages tab
+ var dbmsMessages = self.dbmsMessages = paramsTabFrame.addTab(
+ '{{ _('Messages') }}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ dbmsMessages.addItem($('<div id=dbms_messages class="info"></div>'));
+
+ // Add function return results tab
+ var retResults = self.retResults = paramsTabFrame.addTab(
+ '{{ _('Results') }}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ retResults.addItem($('<div id=ret_results class="info"></div>'));
+
+ // Now create a second splitter to go inside the existing one.
+ var $topContainer = $('<div class="debugger top-container"></div>');
+ hSplitter.left().addItem($topContainer);
+
+ // Create the wcSplitter used by wcDocker to split the single panel.
+ var vSplitter = new wcSplitter(
+ $topContainer, panel,
+ wcDocker.ORIENTATION.HORIZONTAL
+ );
+
+ // Initialize this splitter with a layout in each pane.
+ vSplitter.initLayouts(wcDocker.LAYOUT.SIMPLE, wcDocker.LAYOUT.SIMPLE);
+
+ // Now create a tab widget and put that into one of the sub splits.
+ var $stack = $('<div class="full-container stack">');
+ vSplitter.bottom().addItem($stack);
+
+ var stackFrame = new wcTabFrame($stack, panel);
+ var stackTab = self.stackTab = stackFrame.addTab(
+ '{{ _('Stack pane') }}', -1, wcDocker.LAYOUT.SIMPLE
+ );
+ stackTab.addItem(
+ $('<div id="stack_pane" class="full-container-pane info"></div>'));
+
+ // By default, the splitter splits down the middle, we split the main panel by 80%.
+ vSplitter.pos(0.75);
+
+ // Now create a tab widget and put that into one of the sub splits.
+ var editor_pane = $('<div id="stack_pane" class="full-container-pane info"></div>');
+ var code_editor_area = $('<textarea id="debugger-editor-textarea"></textarea>').append(editor_pane);
+ vSplitter.top().addItem(code_editor_area);
+
+ // To show the line-number and set breakpoint marker details by user.
+ var editor = self.editor = CodeMirror.fromTextArea(
+ code_editor_area.get(0), {
+ lineNumbers: true,
+ gutters: ["note-gutter", "CodeMirror-linenumbers", "breakpoints"],
+ mode: "text/x-sql",
+ readOnly: true
+ });
+ });
+
+ // On loading the docker, register the callbacks
+ var onLoad = function() {
+ self.docker.finishLoading(100);
+ self.docker.off(wcDocker.EVENT.LOADED);
+ // Register the callback when user set/clear the breakpoint on gutter area.
+ self.editor.on("gutterClick", self.onBreakPoint.bind(self), self);
+ };
+
+ self.docker.startLoading('{{ _('Loading...') }}');
+ self.docker.on(wcDocker.EVENT.LOADED, onLoad);
+
+ self.main_panel = self.docker.addPanel(
+ 'code', wcDocker.DOCK.TOP, null, {h: '100%', w: '100%'}
+ );
+
+ // Create the toolbar view for debugging the function
+ this.toolbarView = new DebuggerToolbarView();
+ },
+
+ // Register the panel with new debugger docker instance.
+ registerPanel: function(name, title, width, height, onInit) {
+ var self = this;
+
+ this.docker.registerPanelType(name, {
+ title: title,
+ isPrivate: true,
+ onCreate: function(panel) {
+ self.panels[name] = panel;
+ panel.initSize(width, height);
+ if (!title)
+ panel.title(false);
+ else
+ panel.title(title);
+ panel.closeable(false);
+ panel.layout().addItem(
+ $('<div>', {'class': 'pg-debugger-panel'})
+ );
+ if (onInit) {
+ onInit.apply(self, [panel]);
+ }
+ }
+ });
+ }
+ });
+
+ pgTools.DirectDebug = new DirectDebug();
+
+ return pgTools.DirectDebug;
+});
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql
new file mode 100755
index 0000000..d43f0e2
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql
@@ -0,0 +1,104 @@
+{### Create executer function for edb spl function debugging ###}
+{% set inside_loop = {'value': False} %}
+
+{% if lan_name == 'edbspl' %}
+{% set useAnonymousBlock = "true" %}
+{% if not is_func %}
+ {% set str_statement = "\tEXEC " ~ func_name %}
+{% elif ret_type == 'void' %}
+ {% set str_statement = "\tPERFORM " ~ func_name %}
+{% else %}
+ {% set resultVar = "v_retVal" %}
+ {% set str_statement = "\t" ~ resultVar ~ " := " ~ func_name %}
+ {% set str_declare = str_declare ~ "\t" ~ resultVar ~ " " ~ ret_type ~ ";\n" %}
+ {% set str_result = "\tDBMS_OUTPUT.PUT_LINE(E'\\n\\nResult:\\n--------\\n' || " ~ resultVar ~ "::text || E'\\n\\nNOTE: This is the result generated during the function execution by the debugger.\\n');\n" %}
+{% endif %}
+
+{% else %}
+{% if ret_type == 'record' %}
+ {% set str_statement = "\tSELECT " ~ func_name %}
+{% else %}
+ {% set str_statement = "\tSELECT * FROM " ~ func_name %}
+{% endif %}
+{% endif %}
+
+{% set firstProceesed = "false" %}
+{% set input_value_index = 0 %}
+
+{% if arg_type|length > 0 %}
+{% set str_statement = str_statement ~ "(" %}
+
+{% for arg_mode in args_mode %}
+
+{% if useAnonymousBlock == "true" and (arg_mode == 'o' or arg_mode == 'b') %}
+{% set strParam = "p_param" ~ (loop.index - 1) %}
+{% set str_declare = str_declare ~ "\t" ~ strParam ~ " " ~ arg_type[loop.index - 1] %}
+{% if arg_mode == 'b' %}
+{### TODO: to check for Null parameters received from client ###}
+{% if data[input_value_index]['type'] == 'text' and data[input_value_index]['value'] != 'NULL' %}
+{% set tmp_val = data[input_value_index]['value']|qtLiteral %}
+{% set str_declare = str_declare ~ " := " ~ strParam ~ " " ~ tmp_val ~ "::" ~ data[input_value_index]['type'] %}
+{% else %}
+{% set str_declare = str_declare ~ " := " ~ strParam ~ " " ~ data[input_value_index]['value'] ~ "::" ~ data[input_value_index]['type'] %}
+{% endif %}
+{% set input_value_index = input_value_index + 1 %}
+{% endif %}
+{% set str_declare = str_declare ~ ";\n" %}
+
+{% if firstProceesed == "true" %}
+{% set str_statement = str_statement ~ ", " %}
+{% endif %}
+{% set firstProceesed = "true" %}
+{% set str_statement = str_statement ~ strParam %}
+
+{% elif arg_mode != 'o' %}
+{% if firstProceesed == "true" %}
+{% set str_statement = str_statement ~ ", " %}
+{% endif %}
+{% set firstProceesed = "true" %}
+
+{% if arg_mode == 'v' %}
+{% set str_statement = str_statement ~ "VARIADIC " %}
+{% endif %}
+
+{### TODO: to check for Null parameters received from client ###}
+{% if data[input_value_index]['type'] == 'text' and data[input_value_index]['value'] != 'NULL' %}
+{% set tmp_var = data[input_value_index]['value']|qtLiteral %}
+{% set str_statement = str_statement ~ tmp_var ~ "::" ~ data[input_value_index]['type'] %}
+{% else %}
+{% set str_statement = str_statement ~ data[input_value_index]['value'] ~ "::" ~ data[input_value_index]['type'] %}
+{% endif %}
+{% set input_value_index = input_value_index + 1 %}
+
+
+{% endif %}
+
+{% if loop.last %}
+{% set str_statement = str_statement ~ ")" %}
+{% set strQuery = str_statement %}
+{% if useAnonymousBlock == "true" %}
+{% set strQuery = "DECLARE\n" ~ str_declare ~ "BEGIN\n" ~ str_statement ~ ";\n" ~ str_result ~ "END;" %}
+{% endif %}
+
+{{ strQuery }}
+{% if inside_loop.update({'value': True}) %} {% endif %}
+{% endif %}
+
+{% endfor %}
+
+{% elif not is_func and lan_name == 'edbspl' %}
+{% set strQuery = str_statement %}
+{% if useAnonymousBlock == "true" %}
+{% set strQuery = "DECLARE\n" ~ str_declare ~ "BEGIN\n" ~ str_statement ~ ";\n" ~ str_result ~ "END;" %}
+{% endif %}
+{% else %}
+{% set strQuery = str_statement ~ "()" %}
+{% if useAnonymousBlock == "true" %}
+{% set strQuery = "DECLARE\n" ~ str_declare ~ "BEGIN\n" ~ str_statement ~ ";\n" ~ str_result ~ "END;" %}
+{% endif %}
+{% endif %}
+
+{### Return final query formed with above condition ###}
+{% if not inside_loop.value %}
+{{ strQuery }}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql
new file mode 100755
index 0000000..ecd75f1
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql
@@ -0,0 +1,32 @@
+{### Create executer function for plpgsql function debugging ###}
+{% if not is_func %}
+ EXEC {{ func_name }} (
+{% elif ret_type == 'record' %}
+ SELECT {{ func_name }} (
+{% else %}
+ SELECT * FROM {{ func_name }} (
+{% endif %}
+{% if data %}
+{% for dict_item in data %}
+{% if 'type' in dict_item and 'value' in dict_item %}
+{% if dict_item['type'] == 'text' and dict_item['value'] != 'NULL' %}
+{{ dict_item['value']|qtLiteral }}::{{ dict_item['type'] }}{% if not loop.last %}, {% endif %}
+{% elif dict_item['type'] == 'text' and dict_item['value'] == 'NULL' %}
+{{ dict_item['value'] }}::{{ dict_item['type'] }}{% if not loop.last %}, {% endif %}
+{% else %}
+{% if '[]' in dict_item['type'] %}
+ ARRAY[
+{% for dict_list in dict_item['value'] %}
+{% if 'value' in dict_list %}
+{{ dict_list['value']|qtLiteral }}{% if not loop.last %}, {% endif %}
+{% endif %}
+{% endfor %}
+]::{{ dict_item['type'] }}
+
+{% else %} {{ dict_item['value'] }}::{{ dict_item['type'] }}
+{% endif %} {% if not loop.last %}, {% endif %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+)
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/get_function_debug_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/get_function_debug_info.sql
new file mode 100755
index 0000000..30e47e5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/get_function_debug_info.sql
@@ -0,0 +1,66 @@
+{### To fetch debug function information ###}
+SELECT
+ p.proname AS name, p.prosrc, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proallargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proallargtypes, 1)) AS s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proargtypes, 1)) AS s(i)), ',')
+ END AS proargtypenames,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proallargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')
+ END AS proargtypes,
+ pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,
+ pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,
+
+ {% if is_ppas_database %}
+ CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,
+ CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,
+ CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,
+ CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,
+ CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,
+ NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,
+ {%else%}
+ 0 AS pkg,
+ '' AS pkgname,
+ 0 AS pkgconsoid,
+ n.oid AS schema,
+ n.nspname AS schemaname,
+ true AS isfunc,
+ {%endif%}
+ pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,
+
+ {% if hasFeatureFunctionDefaults %}
+ pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,
+ p.pronargdefaults
+ {%else%}
+ '' AS proargdefaults, 0 AS pronargdefaults
+ {%endif%}
+ FROM
+ pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
+ LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid
+ LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid
+
+ {% if is_ppas_database %}
+ LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid
+ {% endif %}
+
+ {% if fid %}
+ WHERE p.oid = {{fid}}::int;
+ {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/Backend_running.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/Backend_running.sql
new file mode 100755
index 0000000..c5770d0
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/Backend_running.sql
@@ -0,0 +1,4 @@
+{### Check backend target is running or not for debugging ###}
+{% if backend_pid %}
+ SELECT COUNT(*) FROM (SELECT pg_catalog.pg_stat_get_backend_idset() AS bid) AS s WHERE pg_catalog.pg_stat_get_backend_pid(s.bid) = {{ conn|qtIdent(backend_pid) }}::integer;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/abort_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/abort_target.sql
new file mode 100755
index 0000000..8f89ae3
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/abort_target.sql
@@ -0,0 +1,4 @@
+{### Abort the target for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_abort_target({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_edb.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_edb.sql
new file mode 100755
index 0000000..0129462
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_edb.sql
@@ -0,0 +1,4 @@
+{### Add EDB breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_global_breakpoint({{session_id}}::int, {{package_oid}}::int, {{function_oid}}::OID, -1, {{target_oid}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_pg.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_pg.sql
new file mode 100755
index 0000000..f83e1cf
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/add_breakpoint_pg.sql
@@ -0,0 +1,4 @@
+{### Add PG breakpoint for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_global_breakpoint({{session_id}}, {{function_oid}}, -1, NULL)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/attach_to_port.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/attach_to_port.sql
new file mode 100755
index 0000000..bb74477
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/attach_to_port.sql
@@ -0,0 +1,4 @@
+{### Attach the target to port for debugging ###}
+{% if port %}
+ SELECT * FROM pldbg_attach_to_port({{port}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/clear_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/clear_breakpoint.sql
new file mode 100755
index 0000000..eecc674
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/clear_breakpoint.sql
@@ -0,0 +1,4 @@
+{### Clear breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_drop_breakpoint({{session_id}}::int, {{poid}}::OID, {{foid}}::OID, {{line_number}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/continue.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/continue.sql
new file mode 100755
index 0000000..071c53e
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/continue.sql
@@ -0,0 +1,11 @@
+{### Continue for debugging ###}
+{% if session_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber, pldbg_get_source({{session_id}}::INTEGER, p.pkg, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_continue({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/create_listener.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/create_listener.sql
new file mode 100755
index 0000000..1d47739
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/create_listener.sql
@@ -0,0 +1,2 @@
+{### Create listener for debugging ###}
+ SELECT * from pldbg_create_listener()
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_execute_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_execute_target.sql
new file mode 100755
index 0000000..7fd6694
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_execute_target.sql
@@ -0,0 +1,4 @@
+{### Debug execute target for plpgsql function ###}
+{% if function_oid %}
+ SELECT plpgsql_oid_debug({{packge_oid}}, {{function_oid}})
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_init.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_init.sql
new file mode 100755
index 0000000..14ad3c5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_plpgsql_init.sql
@@ -0,0 +1,4 @@
+{### Debug Initialization for plpgsql function ###}
+{% if packge_init_oid %}
+ SELECT plpgsql_oid_debug({{packge_oid}}::OID, {{packge_init_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_execute_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_execute_target.sql
new file mode 100755
index 0000000..e1c324e
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_execute_target.sql
@@ -0,0 +1,4 @@
+{### Debug execute target for EDB spl function ###}
+{% if function_oid %}
+ SELECT edb_oid_debug({{packge_oid}}, {{function_oid}})
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_init.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_init.sql
new file mode 100755
index 0000000..aa5b324
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/debug_spl_init.sql
@@ -0,0 +1,4 @@
+{### Debug Initialization for EDB spl function ###}
+{% if packge_init_oid %}
+ SELECT edb_oid_debug({{packge_oid}}::OID, {{packge_init_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/deposit_value.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/deposit_value.sql
new file mode 100755
index 0000000..86e9f4a
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/deposit_value.sql
@@ -0,0 +1,4 @@
+{### Change the variable value and submit during debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_deposit_value({{session_id}}::int, {{var_name|qtLiteral}}, {{line_number}}, {{val|qtLiteral}})
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_breakpoints.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_breakpoints.sql
new file mode 100755
index 0000000..19da5db
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_breakpoints.sql
@@ -0,0 +1,4 @@
+{### Get the breakpoint information for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_get_breakpoints({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_function_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_function_info.sql
new file mode 100755
index 0000000..7989da7
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_function_info.sql
@@ -0,0 +1,66 @@
+{### To fetch debug function information ###}
+SELECT
+ p.proname AS name, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proallargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proallargtypes, 1)) AS s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proargtypes, 1)) AS s(i)), ',')
+ END AS proargtypenames,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proallargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')
+ END AS proargtypes,
+ pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,
+ pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,
+
+ {% if is_ppas_database %}
+ CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,
+ CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,
+ CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,
+ CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,
+ CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,
+ NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,
+ {%else%}
+ 0 AS pkg,
+ '' AS pkgname,
+ 0 AS pkgconsoid,
+ n.oid AS schema,
+ n.nspname AS schemaname,
+ true AS isfunc,
+ {%endif%}
+ pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,
+
+ {% if hasFeatureFunctionDefaults %}
+ pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,
+ p.pronargdefaults
+ {%else%}
+ '' AS proargdefaults, 0 AS pronargdefaults
+ {%endif%}
+ FROM
+ pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
+ LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid
+ LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid
+
+ {% if is_ppas_database %}
+ LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid
+ {% endif %}
+
+ {% if fid %}
+ WHERE p.oid = {{fid}}::int;
+ {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_stack_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_stack_info.sql
new file mode 100755
index 0000000..05034c9
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_stack_info.sql
@@ -0,0 +1,4 @@
+{### Get the stack information for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_get_stack({{session_id}}::int) ORDER BY level
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_variables.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_variables.sql
new file mode 100755
index 0000000..47ebed5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/get_variables.sql
@@ -0,0 +1,8 @@
+{### Get the variables information for debugging ###}
+{% if session_id %}
+ SELECT
+ name, varClass, value,
+ pg_catalog.format_type(dtype, NULL) as dtype, isconst
+ FROM pldbg_get_variables({{session_id}}::int)
+ ORDER BY varClass
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/select_frame.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/select_frame.sql
new file mode 100755
index 0000000..e31e634
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/select_frame.sql
@@ -0,0 +1,12 @@
+{### select the frame to debug the function ###}
+{% if session_id and frame_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber,
+ CASE WHEN p.func <> 0 THEN pldbg_get_source({{session_id}}::INTEGER, p.func, p.pkg) ELSE '<No source available>' END AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_select_frame({{session_id}}::INTEGER, {{frame_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/set_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/set_breakpoint.sql
new file mode 100755
index 0000000..61c8b13
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/set_breakpoint.sql
@@ -0,0 +1,4 @@
+{### Set the breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_breakpoint({{session_id}}::int ,{{poid}}::OID, {{foid}}::OID, {{line_number}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_into.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_into.sql
new file mode 100755
index 0000000..837287d
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_into.sql
@@ -0,0 +1,11 @@
+{### Step into function for debugging ###}
+{% if session_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber, pldbg_get_source({{session_id}}::INTEGER, p.pkg, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_step_into({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_over.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_over.sql
new file mode 100755
index 0000000..a5ced66
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/step_over.sql
@@ -0,0 +1,11 @@
+{### Step over function for debugging ###}
+{% if session_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber, pldbg_get_source({{session_id}}::INTEGER, p.pkg, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_step_over({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_breakpoint.sql
new file mode 100755
index 0000000..0271056
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_breakpoint.sql
@@ -0,0 +1,11 @@
+{### select the frame to debug the function ###}
+{% if session_id %}
+ SELECT
+ p.pkg AS pkg, p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber, pldbg_get_source({{session_id}}::INTEGER, p.pkg, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func AND s.pkg = p.pkg) AS args
+ FROM pldbg_wait_for_breakpoint({{session_id}}::INTEGER) p;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_target.sql
new file mode 100755
index 0000000..c4f5634
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v1/wait_for_target.sql
@@ -0,0 +1,4 @@
+{### Wait for the target for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_wait_for_target({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/Backend_running.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/Backend_running.sql
new file mode 100755
index 0000000..c5770d0
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/Backend_running.sql
@@ -0,0 +1,4 @@
+{### Check backend target is running or not for debugging ###}
+{% if backend_pid %}
+ SELECT COUNT(*) FROM (SELECT pg_catalog.pg_stat_get_backend_idset() AS bid) AS s WHERE pg_catalog.pg_stat_get_backend_pid(s.bid) = {{ conn|qtIdent(backend_pid) }}::integer;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/abort_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/abort_target.sql
new file mode 100755
index 0000000..8f89ae3
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/abort_target.sql
@@ -0,0 +1,4 @@
+{### Abort the target for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_abort_target({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_edb.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_edb.sql
new file mode 100755
index 0000000..0129462
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_edb.sql
@@ -0,0 +1,4 @@
+{### Add EDB breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_global_breakpoint({{session_id}}::int, {{package_oid}}::int, {{function_oid}}::OID, -1, {{target_oid}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_pg.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_pg.sql
new file mode 100755
index 0000000..f83e1cf
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/add_breakpoint_pg.sql
@@ -0,0 +1,4 @@
+{### Add PG breakpoint for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_global_breakpoint({{session_id}}, {{function_oid}}, -1, NULL)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/attach_to_port.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/attach_to_port.sql
new file mode 100755
index 0000000..bb74477
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/attach_to_port.sql
@@ -0,0 +1,4 @@
+{### Attach the target to port for debugging ###}
+{% if port %}
+ SELECT * FROM pldbg_attach_to_port({{port}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/clear_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/clear_breakpoint.sql
new file mode 100755
index 0000000..c3d83f8
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/clear_breakpoint.sql
@@ -0,0 +1,4 @@
+{### Clear breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_drop_breakpoint({{session_id}}::int, {{foid}}::OID, {{line_number}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/continue.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/continue.sql
new file mode 100755
index 0000000..85dcd27
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/continue.sql
@@ -0,0 +1,11 @@
+{### Continue for debugging ###}
+{% if session_id %}
+ SELECT
+ p.func, p.targetName, p.linenumber,
+ pldbg_get_source({{session_id}}::INTEGER, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_continue({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/create_listener.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/create_listener.sql
new file mode 100755
index 0000000..1d47739
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/create_listener.sql
@@ -0,0 +1,2 @@
+{### Create listener for debugging ###}
+ SELECT * from pldbg_create_listener()
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_execute_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_execute_target.sql
new file mode 100755
index 0000000..62a8e32
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_execute_target.sql
@@ -0,0 +1,4 @@
+{### Debug execute target for plpgsql function ###}
+{% if function_oid %}
+ SELECT plpgsql_oid_debug({{function_oid}})
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_init.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_init.sql
new file mode 100755
index 0000000..e56ad98
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_plpgsql_init.sql
@@ -0,0 +1,4 @@
+{### Debug Initialization for plpgsql function ###}
+{% if packge_init_oid %}
+ SELECT plpgsql_oid_debug({{packge_init_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_execute_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_execute_target.sql
new file mode 100755
index 0000000..34e4229
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_execute_target.sql
@@ -0,0 +1,4 @@
+{### Debug execute target for EDB spl function ###}
+{% if function_oid %}
+ SELECT edb_oid_debug({{function_oid}})
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_init.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_init.sql
new file mode 100755
index 0000000..d16b1b1
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/debug_spl_init.sql
@@ -0,0 +1,4 @@
+{### Debug Initialization for EDB spl function ###}
+{% if packge_init_oid %}
+ SELECT edb_oid_debug({{packge_init_oid}}::OID)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/deposit_value.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/deposit_value.sql
new file mode 100755
index 0000000..6ee3259
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/deposit_value.sql
@@ -0,0 +1,4 @@
+{### Change the variable value and submit during debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_deposit_value({{session_id}}::int, {{var_name|qtLiteral }}, {{line_number}}, {{val|qtLiteral}})
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_breakpoints.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_breakpoints.sql
new file mode 100755
index 0000000..19da5db
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_breakpoints.sql
@@ -0,0 +1,4 @@
+{### Get the breakpoint information for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_get_breakpoints({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_function_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_function_info.sql
new file mode 100755
index 0000000..7989da7
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_function_info.sql
@@ -0,0 +1,66 @@
+{### To fetch debug function information ###}
+SELECT
+ p.proname AS name, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proallargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proallargtypes, 1)) AS s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT
+ pg_catalog.format_type(p.proargtypes[s.i], NULL)
+ FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(
+ p.proargtypes, 1)) AS s(i)), ',')
+ END AS proargtypenames,
+ CASE WHEN proallargtypes IS NOT NULL THEN
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proallargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')
+ ELSE
+ pg_catalog.array_to_string(ARRAY(
+ SELECT proargtypes[s.i] FROM
+ pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')
+ END AS proargtypes,
+ pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,
+ pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,
+
+ {% if is_ppas_database %}
+ CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,
+ CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,
+ CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,
+ CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,
+ CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,
+ NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,
+ {%else%}
+ 0 AS pkg,
+ '' AS pkgname,
+ 0 AS pkgconsoid,
+ n.oid AS schema,
+ n.nspname AS schemaname,
+ true AS isfunc,
+ {%endif%}
+ pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,
+
+ {% if hasFeatureFunctionDefaults %}
+ pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,
+ p.pronargdefaults
+ {%else%}
+ '' AS proargdefaults, 0 AS pronargdefaults
+ {%endif%}
+ FROM
+ pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid
+ LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid
+ LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid
+
+ {% if is_ppas_database %}
+ LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid
+ {% endif %}
+
+ {% if fid %}
+ WHERE p.oid = {{fid}}::int;
+ {% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_stack_info.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_stack_info.sql
new file mode 100755
index 0000000..05034c9
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_stack_info.sql
@@ -0,0 +1,4 @@
+{### Get the stack information for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_get_stack({{session_id}}::int) ORDER BY level
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_variables.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_variables.sql
new file mode 100755
index 0000000..47ebed5
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/get_variables.sql
@@ -0,0 +1,8 @@
+{### Get the variables information for debugging ###}
+{% if session_id %}
+ SELECT
+ name, varClass, value,
+ pg_catalog.format_type(dtype, NULL) as dtype, isconst
+ FROM pldbg_get_variables({{session_id}}::int)
+ ORDER BY varClass
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/select_frame.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/select_frame.sql
new file mode 100755
index 0000000..4a18839
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/select_frame.sql
@@ -0,0 +1,11 @@
+{### select the frame to debug the function ###}
+{% if session_id %}
+ SELECT
+ p.func AS func, p.targetName AS targetName, p.linenumber AS linenumber,
+ CASE WHEN p.func <> 0 THEN pldbg_get_source({{session_id}}::INTEGER, p.func) ELSE '<No source available>' END AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_select_frame({{session_id}}::INTEGER, {{frame_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/set_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/set_breakpoint.sql
new file mode 100755
index 0000000..a56d6f6
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/set_breakpoint.sql
@@ -0,0 +1,4 @@
+{### Set the breakpoints for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_set_breakpoint({{session_id}}::int, {{foid}}::OID,{{line_number}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_into.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_into.sql
new file mode 100755
index 0000000..cd05240
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_into.sql
@@ -0,0 +1,11 @@
+{### Step into function for debugging ###}
+{% if session_id %}
+ SELECT
+ p.func, p.targetName, p.linenumber,
+ pldbg_get_source({{session_id}}::INTEGER, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_step_into({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_over.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_over.sql
new file mode 100755
index 0000000..ed3153d
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/step_over.sql
@@ -0,0 +1,11 @@
+{### Step over function for debugging ###}
+{% if session_id %}
+ SELECT
+ p.func, p.targetName, p.linenumber,
+ pldbg_get_source({{session_id}}::INTEGER, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_step_over({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_breakpoint.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_breakpoint.sql
new file mode 100755
index 0000000..96efcb8
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_breakpoint.sql
@@ -0,0 +1,12 @@
+{### select the frame to debug the function ###}
+{% if session_id %}
+ SELECT
+ p.func AS func, p.targetName AS targetName,
+ p.linenumber AS linenumber,
+ pldbg_get_source({{session_id}}::INTEGER, p.func) AS src,
+ (SELECT
+ s.args
+ FROM pldbg_get_stack({{session_id}}::INTEGER) s
+ WHERE s.func = p.func) AS args
+ FROM pldbg_wait_for_breakpoint({{session_id}}::INTEGER) p
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_target.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_target.sql
new file mode 100755
index 0000000..c4f5634
--- /dev/null
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/v2/wait_for_target.sql
@@ -0,0 +1,4 @@
+{### Wait for the target for debugging ###}
+{% if session_id %}
+ SELECT * FROM pldbg_wait_for_target({{session_id}}::int)
+{% endif %}
\ No newline at end of file
diff --git a/web/setup.py b/web/setup.py
index 690744a..01e69a1 100644
--- a/web/setup.py
+++ b/web/setup.py
@@ -217,6 +217,21 @@ CREATE TABLE user_preferences (
FOREIGN KEY(uid) REFERENCES user (id)
)""")
+ if int(version.value) < 9:
+ db.engine.execute("""
+CREATE TABLE IF NOT EXISTS debugger_function_arguments (
+ server_id INTEGER ,
+ database_id INTEGER ,
+ schema_id INTEGER ,
+ function_id INTEGER ,
+ arg_id INTEGER ,
+ is_null INTEGER NOT NULL CHECK (is_null >= 0 AND is_null <= 1) ,
+ is_expression INTEGER NOT NULL CHECK (is_expression >= 0 AND is_expression <= 1) ,
+ use_default INTEGER NOT NULL CHECK (use_default >= 0 AND use_default <= 1) ,
+ value TEXT,
+ PRIMARY KEY (server_id, database_id, schema_id, function_id, arg_id)
+ )""")
+
# Finally, update the schema version
version.value = config.SETTINGS_SCHEMA_VERSION
db.session.merge(version)
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-14 12:52 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-04-14 20:39 ` Dave Page <[email protected]>
2016-04-18 08:55 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Dave Page @ 2016-04-14 20:39 UTC (permalink / raw)
To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers
Hi
On Thu, Apr 14, 2016 at 1:52 PM, Neel Patel <[email protected]> wrote:
> Hi,
>
> Please find attached v2 patch file of the debugger which fixes the below
> issues which was not present in the first patch.
> In this patch, we have added new table in sqlite database to store the
> functions arguments value user has given during debugging.
> After applying this patch, user needs to execute "setup.py" to create the
> new table in pgadmin4.db file.
>
> In direct debugging, when user debug the function then arguments values will
> be stored in the sqlite database so when user debug the same function again
> then previous values will be filled in the user input dialog.
> Once the execution is completed then user will be able to do the debug of
> the same function again by pressing the "Continue/Restart" button.
> User can debug the "procedure" which is supported in PPAS database.
> Replaced the "Glyphicon" with the "font-awesome" icons.
Very cool! Committed, understanding that there are still improvements
to be made.
> Below are the TODOs
>
> Validate the input arguments values changed by user while depositing the
> value during debugging.
> Need to implement the code folding in the codemirror editor area.
> As per the Ashesh's suggestion, need to add debug logs information so that
> we can get the state of the debug function. Also need to add "arrow" next to
> breakpoint in the gutters as per the pgadmin3.
> Need to add "Debug package initializer" in the user input dialog for the
> direct debugging.
> Last but not least "Review comments" :)
Here you go :-)
- Ensure all messages are gettext enabled.
- Constructs like the following won't work, because Jinja will
evaluate the string " + err.errormsg + " before it ever gets evaluated
as JS by the browser.
Alertify.alert("{{ _('" + err.errormsg + "') }}");
- Please adjust the button bar to use the same styling as the button
bar on the Properties tab.
- Let's make the stack pane tab part of the tabset at the bottom of
the debugger, and ensure docking etc. works so tabs can be split off
and arranged around the main source window.
- Stepping is quite slow. What's causing that? I wonder if we really
need to have all the one line SQL templates - they're probably quite
expensive to process.
- Is backend_running.sql required? I've removed both versions as I
can't find any references to them. Are any other templates not
required?
Will log any other issues that come up in further work.
> Below functionalities are implemented but testing are pending.
>
> Trigger functions need to test with the debugger.
> Functions are tested with data types (like text, integer etc.) but it needs
> to be tested with all the data types for direct debugging.
> Functions/Procedures need to test with PPAS 9.2 and earlier version where
> debugger version is different.
Thanks!
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-14 12:52 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-14 20:39 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
@ 2016-04-18 08:55 ` Neel Patel <[email protected]>
2016-04-18 12:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-18 08:55 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
Please find inline comments with all the fixes.
Attached is the updated patch file. Do review it and let me know for any
comments.
If you find any issues, let me know .Now, Working on the remaining TODOs as
mentioned in below email.
Thanks,
Neel Patel
On Fri, Apr 15, 2016 at 2:09 AM, Dave Page <[email protected]> wrote:
> Hi
>
> On Thu, Apr 14, 2016 at 1:52 PM, Neel Patel <[email protected]>
> wrote:
> > Hi,
> >
> > Please find attached v2 patch file of the debugger which fixes the below
> > issues which was not present in the first patch.
> > In this patch, we have added new table in sqlite database to store the
> > functions arguments value user has given during debugging.
> > After applying this patch, user needs to execute "setup.py" to create the
> > new table in pgadmin4.db file.
> >
> > In direct debugging, when user debug the function then arguments values
> will
> > be stored in the sqlite database so when user debug the same function
> again
> > then previous values will be filled in the user input dialog.
> > Once the execution is completed then user will be able to do the debug of
> > the same function again by pressing the "Continue/Restart" button.
> > User can debug the "procedure" which is supported in PPAS database.
> > Replaced the "Glyphicon" with the "font-awesome" icons.
>
> Very cool! Committed, understanding that there are still improvements
> to be made.
>
> > Below are the TODOs
> >
> > Validate the input arguments values changed by user while depositing the
> > value during debugging.
> > Need to implement the code folding in the codemirror editor area.
> > As per the Ashesh's suggestion, need to add debug logs information so
> that
> > we can get the state of the debug function. Also need to add "arrow"
> next to
> > breakpoint in the gutters as per the pgadmin3.
> > Need to add "Debug package initializer" in the user input dialog for the
> > direct debugging.
> > Last but not least "Review comments" :)
>
> Here you go :-)
>
> - Ensure all messages are gettext enabled.
>
Fixed.
>
> - Constructs like the following won't work, because Jinja will
> evaluate the string " + err.errormsg + " before it ever gets evaluated
> as JS by the browser.
>
> Alertify.alert("{{ _('" + err.errormsg + "') }}");
>
Fixed.
>
> - Please adjust the button bar to use the same styling as the button
> bar on the Properties tab.
>
Fixed
>
> - Let's make the stack pane tab part of the tabset at the bottom of
> the debugger, and ensure docking etc. works so tabs can be split off
> and arranged around the main source window.
>
Fixed. Now stack pane will be displayed along with another panel at bottom
and also docking has been introduced for all the panels so tabs will be
arranged around main debugger panel.
>
> - Stepping is quite slow. What's causing that? I wonder if we really
> need to have all the one line SQL templates - they're probably quite
> expensive to process.
>
Fixed. This is due to polling timeout was high (1 second) and we are
getting delay in getting the results. Now polling timeout has reduced to to
200ms.
>
> - Is backend_running.sql required? I've removed both versions as I
> can't find any references to them. Are any other templates not
> required?
>
Ok. No other templates.
>
> Will log any other issues that come up in further work.
>
> > Below functionalities are implemented but testing are pending.
> >
> > Trigger functions need to test with the debugger.
> > Functions are tested with data types (like text, integer etc.) but it
> needs
> > to be tested with all the data types for direct debugging.
> > Functions/Procedures need to test with PPAS 9.2 and earlier version where
> > debugger version is different.
>
> Thanks!
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] debugger_18_April_Fixes.patch (29.5K, 3-debugger_18_April_Fixes.patch)
download | inline diff:
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py
index 888e9a3..2690a75 100644
--- a/web/pgadmin/tools/debugger/__init__.py
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -171,7 +171,7 @@ def init_function(node_type, sid, did, scid, fid):
if user['is_superuser']:
status_in, rid_pre = conn.execute_scalar("SHOW shared_preload_libraries")
if not status_in:
- return internal_server_error("ERROR: Couldn't fetch debugger plugin information")
+ return internal_server_error(gettext("ERROR: Couldn't fetch debugger plugin information"))
# Need to check if plugin is really loaded or not with "plugin_debugger" string
if rid_pre:
@@ -181,7 +181,7 @@ def init_function(node_type, sid, did, scid, fid):
status_in, rid_tar = conn.execute_scalar("SELECT count(*) FROM pg_proc WHERE proname = 'pldbg_get_target_info'")
if not status_in:
current_app.logger.debug("ERROR: Couldn't fetch debugger target information")
- return internal_server_error("ERROR: Couldn't fetch debugger target information")
+ return internal_server_error(gettext("ERROR: Couldn't fetch debugger target information"))
if rid_tar == 0:
ret_status = False
@@ -191,7 +191,7 @@ def init_function(node_type, sid, did, scid, fid):
# Return the response that function can not be debug...
if not ret_status:
current_app.logger.debug("Error: Function/Procedure can not be debug")
- return internal_server_error("ERROR: Function/Procedure can not be debug.")
+ return internal_server_error(gettext("ERROR: Function/Procedure can not be debug."))
# Store the function information in session variable
if 'funcData' not in session:
@@ -199,25 +199,6 @@ def init_function(node_type, sid, did, scid, fid):
else:
function_data = session['funcData']
- function_data = {
- 'oid': fid,
- 'name': r_set['rows'][0]['name'],
- 'is_func': r_set['rows'][0]['isfunc'],
- 'is_callable': False,
- 'schema': r_set['rows'][0]['schemaname'],
- 'language': r_set['rows'][0]['lanname'],
- 'return_type': r_set['rows'][0]['rettype'],
- 'args_type': r_set['rows'][0]['proargtypenames'],
- 'args_name': r_set['rows'][0]['proargnames'],
- 'arg_mode': r_set['rows'][0]['proargmodes'],
- 'use_default': r_set['rows'][0]['pronargdefaults'],
- 'default_value': r_set['rows'][0]['proargdefaults'],
- 'pkg': r_set['rows'][0]['pkg'],
- 'args_value': ''
- }
-
- session['funcData'] = function_data;
-
data = {}
data['name'] = r_set['rows'][0]['proargnames']
data['type'] = r_set['rows'][0]['proargtypenames']
@@ -244,6 +225,26 @@ def init_function(node_type, sid, did, scid, fid):
r_set['rows'][0]['require_input'] = data['require_input']
+ function_data = {
+ 'oid': fid,
+ 'name': r_set['rows'][0]['name'],
+ 'is_func': r_set['rows'][0]['isfunc'],
+ 'is_callable': False,
+ 'schema': r_set['rows'][0]['schemaname'],
+ 'language': r_set['rows'][0]['lanname'],
+ 'return_type': r_set['rows'][0]['rettype'],
+ 'args_type': r_set['rows'][0]['proargtypenames'],
+ 'args_name': r_set['rows'][0]['proargnames'],
+ 'arg_mode': r_set['rows'][0]['proargmodes'],
+ 'use_default': r_set['rows'][0]['pronargdefaults'],
+ 'default_value': r_set['rows'][0]['proargdefaults'],
+ 'pkg': r_set['rows'][0]['pkg'],
+ 'require_input': data['require_input'],
+ 'args_value': ''
+ }
+
+ session['funcData'] = function_data;
+
return make_json_response(
data=r_set['rows'],
status=200
@@ -395,6 +396,7 @@ def initialize_target(debug_type, sid, did, scid, func_id):
'use_default': func_data['use_default'],
'default_value': func_data['default_value'],
'pkg': func_data['pkg'],
+ 'require_input': func_data['require_input'],
'args_value': func_data['args_value']
}
@@ -511,7 +513,8 @@ def restart_debugging(trans_id):
'proargtypenames': session['functionData'][str(trans_id)]['args_type'],
'pronargdefaults': session['functionData'][str(trans_id)]['use_default'],
'proargdefaults': session['functionData'][str(trans_id)]['default_value'],
- 'proargnames': session['functionData'][str(trans_id)]['args_name']
+ 'proargnames': session['functionData'][str(trans_id)]['args_name'],
+ 'require_input': session['functionData'][str(trans_id)]['require_input']
}
return make_json_response(data={'status': True, 'restart_debug': True, 'result': function_data})
@@ -762,7 +765,7 @@ def execute_debugger_query(trans_id, query_type):
if not status:
return internal_server_error(errormsg=result)
else:
- return make_json_response(info='Target Aborted.', data={'status': status, 'result': result})
+ return make_json_response(info=gettext('Target Aborted.'), data={'status': status, 'result': result})
else:
status, result = conn.execute_dict(sql)
if not status:
@@ -1267,14 +1270,19 @@ def poll_end_execution_result(trans_id):
return make_json_response(success=1, info=gettext("Execution Completed."),
data={'status': status, 'status_message': statusmsg})
if result:
- status = 'Success'
- data = {}
- for i in result:
- for k, v in i.items():
- data["name"] = k
- data.setdefault("value",[]).append(v)
+ if 'ERROR' in result:
+ status = 'ERROR'
+ return make_json_response(info=gettext("Execution completed with error"),
+ data={'status': status, 'status_message': result})
+ else:
+ status = 'Success'
+ data = {}
+ for i in result:
+ for k, v in i.items():
+ data["name"] = k
+ data.setdefault("value",[]).append(v)
- return make_json_response(success=1, info=gettext("Execution Completed."),
+ return make_json_response(success=1, info=gettext("Execution Completed."),
data={'status': status, 'result': data, 'status_message': statusmsg})
else:
status = 'Busy'
diff --git a/web/pgadmin/tools/debugger/static/css/debugger.css b/web/pgadmin/tools/debugger/static/css/debugger.css
index 2dceb53..dfeb331 100644
--- a/web/pgadmin/tools/debugger/static/css/debugger.css
+++ b/web/pgadmin/tools/debugger/static/css/debugger.css
@@ -1,17 +1,17 @@
-.navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom {
- background-image: linear-gradient(to bottom, #CCC 0%, #D2D2D2 100%);
-}
-
-.btn-default {
+.btn-step-into, .btn-step-over, .btn-continue, .btn-toggle-breakpoint, .btn-clear-breakpoint, .btn-stop {
background-color: #D2D2D2;
left: 0px;
right: 0px;
padding: 7px;
}
-.btn-toolbar {
- padding-top: 3px;
- padding-bottom: 3px;
+.debugger-toolbar {
+ background-color: #D2D2D2;
+ border: 2px solid #A9A9A9;
+ left: 0px;
+ right: 0px;
+ padding: 0px;
+ padding-left: 2px;
}
#container {
@@ -63,10 +63,10 @@
text-align: center;
}
-.top-container .breakpoints {
+.pg-debugger-panel .breakpoints {
width: 0.9em;
}
-.top-container .CodeMirror-activeline-background {
+.pg-debugger-panel .CodeMirror-activeline-background {
background: #50B0F0;
}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/direct.html b/web/pgadmin/tools/debugger/templates/debugger/direct.html
index 718ef2f..a72b90a 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/direct.html
+++ b/web/pgadmin/tools/debugger/templates/debugger/direct.html
@@ -19,7 +19,7 @@ try {
{% block body %}
<nav class="navbar-inverse navbar-fixed-top">
- <div class="container-fluid">
+ <div class="container-fluid debugger-toolbar">
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<div id="btn-toolbar" class="btn-toolbar" role="toolbar" aria-label="">
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js
index 6d24848..f16fa91 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/debugger.js
@@ -55,6 +55,7 @@ define(
width: 500,
isCloseable: true,
isPrivate: true,
+ icon: 'fa fa-arrow-circle-right',
url: 'about:blank'
});
@@ -153,8 +154,7 @@ define(
try {
var err = $.parseJSON(xhr.responseText);
if (err.success == 0) {
- msg = S('{{ _(' + err.errormsg + ')}}').value();
- Alertify.alert("{{ _('" + err.errormsg + "') }}");
+ Alertify.alert(err.errormsg);
}
} catch (e) {}
}
@@ -316,8 +316,7 @@ define(
try {
var err = $.parseJSON(xhr.responseText);
if (err.success == 0) {
- msg = S('{{ _(' + err.errormsg + ')}}').value();
- Alertify.alert("{{ _('" + err.errormsg + "') }}");
+ Alertify.alert(err.errormsg);
}
} catch (e) {}
}
@@ -326,4 +325,4 @@ define(
};
return pgAdmin.Tools.Debugger;
- });
\ No newline at end of file
+ });
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
index 8b58bf3..f345dc9 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
@@ -558,7 +558,6 @@ define(
method: 'POST',
data:{'data':JSON.stringify(args_value_list)},
success: function(res) {
- //TODO: Anything required ? .....
},
error: function(e) {
Alertify.alert(
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
index 01029c2..744914a 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
@@ -247,6 +247,18 @@ define(
// Make ajax call to listen the database message
var baseUrl = "{{ url_for('debugger.index') }}" + "poll_result/" + trans_id;
+ /*
+ During the execution we should poll the result in minimum seconds but once the execution is completed
+ and wait for the another debugging session then we should decrease the polling frequency.
+ */
+ if (pgTools.DirectDebug.direct_execution_completed) {
+ // poll the result after 1 second
+ var poll_timeout = 1000;
+ }
+ else {
+ // poll the result after 200 ms
+ var poll_timeout = 200;
+ }
setTimeout(
function() {
@@ -257,12 +269,11 @@ define(
if (res.data.status === 'Success') {
// If no result then poll again to wait for results.
if (res.data.result == null || res.data.result.length == 0) {
- // NEEL: THIS IS CONDITION IS ADDED
self.poll_result(trans_id);
}
else {
if (res.data.result[0].src != undefined || res.data.result[0].src != null) {
- pgTools.DirectDebug.docker.finishLoading(500);
+ pgTools.DirectDebug.docker.finishLoading(50);
pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
self.UpdateBreakpoint(trans_id);
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
@@ -272,9 +283,10 @@ define(
// Update the stack, local variables and parameters information
self.GetStackInformation(trans_id);
self.GetLocalVariables(trans_id);
+
}
else if (!pgTools.DirectDebug.debug_type && !pgTools.DirectDebug.first_time_indirect_debug) {
- pgTools.DirectDebug.docker.finishLoading(500);
+ pgTools.DirectDebug.docker.finishLoading(50);
if (self.active_line_no != undefined) {
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
}
@@ -283,7 +295,7 @@ define(
pgTools.DirectDebug.first_time_indirect_debug = true;
}
else {
- pgTools.DirectDebug.docker.finishLoading(500);
+ pgTools.DirectDebug.docker.finishLoading(50);
// If the source is really changed then only update the breakpoint information
if (res.data.result[0].src != pgTools.DirectDebug.editor.getValue()) {
pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
@@ -323,7 +335,6 @@ define(
self.poll_result(trans_id);
}
else {
- //TODO: NEEL ADDED
self.poll_result(trans_id);
}
}
@@ -339,7 +350,7 @@ define(
);
}
});
- }, 1000 );
+ }, poll_timeout );
},
@@ -353,6 +364,19 @@ define(
// Make ajax call to listen the database message
var baseUrl = "{{ url_for('debugger.index') }}" + "poll_end_execution_result/" + trans_id;
+ /*
+ During the execution we should poll the result in minimum seconds but once the execution is completed
+ and wait for the another debugging session then we should decrease the polling frequency.
+ */
+ if (pgTools.DirectDebug.direct_execution_completed) {
+ // poll the result to check that execution is completed or not after 1200 ms
+ var poll_end_timeout = 1200;
+ }
+ else {
+ // poll the result to check that execution is completed or not after 350 ms
+ var poll_end_timeout = 250;
+ }
+
setTimeout(
function() {
$.ajax({
@@ -392,7 +416,7 @@ define(
if (res.data.result.name != null) {
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
self.AddResults(res.data.result);
- pgTools.DirectDebug.paramsTabFrame.tab(3,true);
+ pgTools.DirectDebug.results_panel.focus();
pgTools.DirectDebug.direct_execution_completed = true;
//Set the alertify message to inform the user that execution is completed.
@@ -404,7 +428,7 @@ define(
);
// Update the message tab of the debugger
- pgTools.DirectDebug.dbmsMessages.$elem.text(res.data.status_message);
+ pgTools.DirectDebug.messages_panel.$container.find('.messages').text(res.data.status_message);
// Execution completed so disable the buttons other than "Continue/Start" button because user can still
// start the same execution again.
@@ -413,12 +437,6 @@ define(
self.enable('step_into', false);
self.enable('toggle_breakpoint', false);
self.enable('clear_all_breakpoints', false);
-
- //TODO: Continue button should be enable and user will be able to do next debugging with same function.
- //self.enable('continue', false);
-
- // NEEL: TODO: Added Execution is completed so again poll the result until user start the another debugging
- //self.poll_end_execution_result(pgTools.DirectDebug.trans_id);
}
}
}
@@ -432,6 +450,29 @@ define(
res.data.result
);
}
+ else if (res.data.status === 'ERROR') {
+ pgTools.DirectDebug.direct_execution_completed = true;
+ pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
+
+ //Set the alertify message to inform the user that execution is completed with error.
+ Alertify.notify(
+ res.info,
+ 'error',
+ 3,
+ function() { }
+ );
+
+ pgTools.DirectDebug.messages_panel.$container.find('.messages').text(res.data.status_message);
+ pgTools.DirectDebug.messages_panel.focus();
+
+ // Execution completed so disable the buttons other than "Continue/Start" button because user can still
+ // start the same execution again.
+ self.enable('stop', false);
+ self.enable('step_over', false);
+ self.enable('step_into', false);
+ self.enable('toggle_breakpoint', false);
+ self.enable('clear_all_breakpoints', false);
+ }
},
error: function(e) {
Alertify.alert(
@@ -440,7 +481,7 @@ define(
);
}
});
- }, 1200);
+ }, poll_end_timeout);
},
@@ -453,14 +494,38 @@ define(
success: function(res) {
// Restart the same function debugging with previous arguments
var restart_dbg = res.data.restart_debug ? 1 : 0;
- debug_function_again(res.data.result, restart_dbg);
+
+ /*
+ Need to check if restart debugging really require to open the input dialog ?
+ If yes then we will get the previous arguments from database and populate the input dialog
+ If no then we should directly start the listener.
+ */
+ if (res.data.result.require_input) {
+ var res_val = debug_function_again(res.data.result, restart_dbg);
+ }
+ else {
+ // Debugging of void function is started again so we need to start the listener again
+ var baseUrl = "{{ url_for('debugger.index') }}" + "start_listener/" + trans_id;
+
+ $.ajax({
+ url: baseUrl,
+ method: 'GET',
+ success: function(res) {
+ },
+ error: function(e) {
+ Alertify.alert(
+ 'Debugger listener starting error',
+ e.responseJSON.errormsg
+ );
+ }
+ });
+ }
},
error: function(xhr, status, error) {
try {
var err = $.parseJSON(xhr.responseText);
if (err.success == 0) {
- msg = S('{{ _(' + err.errormsg + ')}}').value();
- Alertify.alert("{{ _('" + err.errormsg + "') }}");
+ Alertify.alert(err.errormsg);
}
} catch (e) {}
}
@@ -473,9 +538,9 @@ define(
//Check first if previous execution was completed or not
if (pgTools.DirectDebug.direct_execution_completed) {
- pgTools.DirectDebug.direct_execution_completed = false;
// TODO: We need to get the arguments given by the user from sqlite database
self.Restart(trans_id);
+ pgTools.DirectDebug.direct_execution_completed = false;
}
else {
// Make ajax call to listen the database message
@@ -490,8 +555,6 @@ define(
if (pgTools.DirectDebug.debug_type) {
self.poll_end_execution_result(trans_id);
}
- //NEEL: ADDED
- //self.poll_end_execution_result(trans_id);
}
else {
Alertify.alert(
@@ -716,8 +779,6 @@ define(
self.stack_grid = null;
}
- pgTools.DirectDebug.stackTab.$elem.empty();
-
var DebuggerStackModel = Backbone.Model.extend({
defaults: {
name: undefined,
@@ -776,7 +837,10 @@ define(
});
stack_grid.render();
- pgTools.DirectDebug.stackTab.$elem.append(stack_grid.el);
+
+ // Render the stack grid into stack panel
+ pgTools.DirectDebug.stack_pane_panel.$container.find('.stack_pane').append(stack_grid.el);
+
},
AddResults: function(result) {
@@ -788,8 +852,6 @@ define(
self.result_grid = null;
}
- pgTools.DirectDebug.retResults.$elem.empty();
-
var DebuggerResultsModel = Backbone.Model.extend({
defaults: {
name: undefined
@@ -822,7 +884,10 @@ define(
});
result_grid.render();
- pgTools.DirectDebug.retResults.$elem.append(result_grid.el);
+
+ // Render the result grid into result panel
+ pgTools.DirectDebug.results_panel.$container.find('.debug_results').append(result_grid.el);
+
},
AddLocalVariables: function(result) {
@@ -834,8 +899,6 @@ define(
self.variable_grid = null;
}
- pgTools.DirectDebug.localVars.$elem.empty();
-
var DebuggerVariablesModel = Backbone.Model.extend({
defaults: {
name: undefined,
@@ -873,7 +936,10 @@ define(
});
variable_grid.render();
- pgTools.DirectDebug.localVars.$elem.append(variable_grid.el);
+
+ // Render the variables grid into local variables panel
+ pgTools.DirectDebug.local_variables_panel.$container.find('.local_variables').append(variable_grid.el);
+
},
AddParameters: function(result) {
@@ -885,8 +951,6 @@ define(
self.param_grid = null;
}
- pgTools.DirectDebug.paramsTab.$elem.empty();
-
var DebuggerParametersModel = Backbone.Model.extend({
defaults: {
name: undefined,
@@ -926,7 +990,9 @@ define(
});
param_grid.render();
- pgTools.DirectDebug.paramsTab.$elem.append(param_grid.el);
+
+ // Render the parameters grid into parameter panel
+ pgTools.DirectDebug.parameters_panel.$container.find('.parameters').append(param_grid.el);
},
deposit_parameter_value: function(model) {
@@ -1250,6 +1316,7 @@ define(
this.registerPanel(
'code', false, '100%', '100%',
function(panel) {
+
var container = panel.layout().scene().find('.pg-debugger-panel');
// Create the wcSplitter used by wcDocker to split the single panel.
@@ -1267,65 +1334,91 @@ define(
// By default, the splitter splits down the middle, we split the main panel by 80%.
hSplitter.pos(0.65);
- var $params = $('<div class="full-container params"></div>');
- hSplitter.right().addItem($params);
-
- // Add Local parameters tab to display function arguments value
- var paramsTabFrame = self.paramsTabFrame = new wcTabFrame($params, panel);
- var paramsTab = self.paramsTab = paramsTabFrame.addTab(
- '{{_('Parameters')}}', -1, wcDocker.LAYOUT.SIMPLE
- );
- paramsTab.addItem($('<div id=parameters class="info"></div>'));
-
- // Add Local variables tab
- var localVars = self.localVars = paramsTabFrame.addTab(
- '{{ _('Local variables') }}', -1, wcDocker.LAYOUT.SIMPLE
- );
- localVars.addItem($('<div id=local_variables class="info"></div>'));
-
- // Add DBMS messages tab
- var dbmsMessages = self.dbmsMessages = paramsTabFrame.addTab(
- '{{ _('Messages') }}', -1, wcDocker.LAYOUT.SIMPLE
- );
- dbmsMessages.addItem($('<div id=dbms_messages class="info"></div>'));
-
- // Add function return results tab
- var retResults = self.retResults = paramsTabFrame.addTab(
- '{{ _('Results') }}', -1, wcDocker.LAYOUT.SIMPLE
- );
- retResults.addItem($('<div id=ret_results class="info"></div>'));
-
- // Now create a second splitter to go inside the existing one.
- var $topContainer = $('<div class="debugger top-container"></div>');
- hSplitter.left().addItem($topContainer);
-
- // Create the wcSplitter used by wcDocker to split the single panel.
- var vSplitter = new wcSplitter(
- $topContainer, panel,
- wcDocker.ORIENTATION.HORIZONTAL
- );
-
- // Initialize this splitter with a layout in each pane.
- vSplitter.initLayouts(wcDocker.LAYOUT.SIMPLE, wcDocker.LAYOUT.SIMPLE);
-
- // Now create a tab widget and put that into one of the sub splits.
- var $stack = $('<div class="full-container stack">');
- vSplitter.bottom().addItem($stack);
-
- var stackFrame = new wcTabFrame($stack, panel);
- var stackTab = self.stackTab = stackFrame.addTab(
- '{{ _('Stack pane') }}', -1, wcDocker.LAYOUT.SIMPLE
- );
- stackTab.addItem(
- $('<div id="stack_pane" class="full-container-pane info"></div>'));
-
- // By default, the splitter splits down the middle, we split the main panel by 80%.
- vSplitter.pos(0.75);
+ var params = $('<div class="full-container params"></div>');
+ hSplitter.right().addItem(params);
+
+ // Create wcDocker for tab set.
+ var out_docker = new wcDocker(
+ '.full-container', {
+ allowContextMenu: false,
+ allowCollapse: false,
+ themePath: '{{ url_for('static', filename='css/wcDocker/Themes') }}',
+ theme: 'pgadmin'
+ });
+
+ // Create the parameters panel to display the arguments of the functions
+ var parameters = new pgAdmin.Browser.Panel({
+ name: 'parameters',
+ title: '{{ _('Parameters') }}',
+ width: '100%',
+ height:'100%',
+ isCloseable: false,
+ isPrivate: true,
+ content: '<div id ="parameters" class="parameters"></div>'
+ })
+
+ // Create the Local variables panel to display the local variables of the function.
+ var local_variables = new pgAdmin.Browser.Panel({
+ name: 'local_variables',
+ title: '{{ _('Local variables') }}',
+ width: '100%',
+ height:'100%',
+ isCloseable: false,
+ isPrivate: true,
+ content: '<div id ="local_variables" class="local_variables"></div>'
+ })
+
+ // Create the messages panel to display the message returned from the database server
+ var messages = new pgAdmin.Browser.Panel({
+ name: 'messages',
+ title: '{{ _('Messages') }}',
+ width: '100%',
+ height:'100%',
+ isCloseable: false,
+ isPrivate: true,
+ content: '<div id="messages" class="messages"></div>'
+ })
+
+ // Create the result panel to display the result after debugging the function
+ var results = new pgAdmin.Browser.Panel({
+ name: 'results',
+ title: '{{ _('Results') }}',
+ width: '100%',
+ height:'100%',
+ isCloseable: false,
+ isPrivate: true,
+ content: '<div id="debug_results" class="debug_results"></div>'
+ })
+
+ // Create the stack pane panel to display the debugging stack information.
+ var stack_pane = new pgAdmin.Browser.Panel({
+ name: 'stack_pane',
+ title: '{{ _('Stack Pane') }}',
+ width: '100%',
+ height:'100%',
+ isCloseable: false,
+ isPrivate: true,
+ content: '<div id="stack_pane" class="stack_pane"></div>'
+ })
+
+ // Load all the created panels
+ parameters.load(out_docker);
+ local_variables.load(out_docker);
+ messages.load(out_docker);
+ results.load(out_docker);
+ stack_pane.load(out_docker);
+
+ // Add all the panels to the docker
+ self.parameters_panel = out_docker.addPanel('parameters', wcDocker.DOCK.LEFT);
+ self.local_variables_panel = out_docker.addPanel('local_variables', wcDocker.DOCK.STACKED, self.parameters_panel);
+ self.messages_panel = out_docker.addPanel('messages', wcDocker.DOCK.STACKED, self.parameters_panel);
+ self.results_panel = out_docker.addPanel('results', wcDocker.DOCK.STACKED, self.parameters_panel);
+ self.stack_pane_panel = out_docker.addPanel('stack_pane', wcDocker.DOCK.STACKED, self.parameters_panel);
// Now create a tab widget and put that into one of the sub splits.
- var editor_pane = $('<div id="stack_pane" class="full-container-pane info"></div>');
+ var editor_pane = $('<div id="stack_editor_pane" class="full-container-pane info"></div>');
var code_editor_area = $('<textarea id="debugger-editor-textarea"></textarea>').append(editor_pane);
- vSplitter.top().addItem(code_editor_area);
+ hSplitter.left().addItem(code_editor_area);
// To show the line-number and set breakpoint marker details by user.
var editor = self.editor = CodeMirror.fromTextArea(
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-14 12:52 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-14 20:39 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-18 08:55 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-04-18 12:37 ` Dave Page <[email protected]>
2016-04-19 06:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Dave Page @ 2016-04-18 12:37 UTC (permalink / raw)
To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers
Hi
On Monday, April 18, 2016, Neel Patel <[email protected]> wrote:
> Hi Dave,
>
> Please find inline comments with all the fixes.
> Attached is the updated patch file. Do review it and let me know for any
> comments.
>
> If you find any issues, let me know .Now, Working on the remaining TODOs
> as mentioned in below email.
>
Thanks - committed with some minor tweaks. One problem partly still remains
though - you've partially copied the toolbar styling. Please use the actual
classes used by the Properties panel. I've already updated the query tool
in that way. Whilst your version looks much closer, it's missing the
minimum button widths, and duplicates CSS unnecessarily.
Thanks.
>
> On Fri, Apr 15, 2016 at 2:09 AM, Dave Page <[email protected]
> <javascript:_e(%7B%7D,'cvml','[email protected]');>> wrote:
>
>> Hi
>>
>> On Thu, Apr 14, 2016 at 1:52 PM, Neel Patel <[email protected]
>> <javascript:_e(%7B%7D,'cvml','[email protected]');>> wrote:
>> > Hi,
>> >
>> > Please find attached v2 patch file of the debugger which fixes the below
>> > issues which was not present in the first patch.
>> > In this patch, we have added new table in sqlite database to store the
>> > functions arguments value user has given during debugging.
>> > After applying this patch, user needs to execute "setup.py" to create
>> the
>> > new table in pgadmin4.db file.
>> >
>> > In direct debugging, when user debug the function then arguments values
>> will
>> > be stored in the sqlite database so when user debug the same function
>> again
>> > then previous values will be filled in the user input dialog.
>> > Once the execution is completed then user will be able to do the debug
>> of
>> > the same function again by pressing the "Continue/Restart" button.
>> > User can debug the "procedure" which is supported in PPAS database.
>> > Replaced the "Glyphicon" with the "font-awesome" icons.
>>
>> Very cool! Committed, understanding that there are still improvements
>> to be made.
>>
>> > Below are the TODOs
>> >
>> > Validate the input arguments values changed by user while depositing the
>> > value during debugging.
>> > Need to implement the code folding in the codemirror editor area.
>> > As per the Ashesh's suggestion, need to add debug logs information so
>> that
>> > we can get the state of the debug function. Also need to add "arrow"
>> next to
>> > breakpoint in the gutters as per the pgadmin3.
>> > Need to add "Debug package initializer" in the user input dialog for the
>> > direct debugging.
>> > Last but not least "Review comments" :)
>>
>> Here you go :-)
>>
>> - Ensure all messages are gettext enabled.
>>
>
> Fixed.
>
>>
>> - Constructs like the following won't work, because Jinja will
>> evaluate the string " + err.errormsg + " before it ever gets evaluated
>> as JS by the browser.
>>
>> Alertify.alert("{{ _('" + err.errormsg + "') }}");
>>
>
> Fixed.
>
>
>>
>> - Please adjust the button bar to use the same styling as the button
>> bar on the Properties tab.
>>
>
> Fixed
>
>>
>> - Let's make the stack pane tab part of the tabset at the bottom of
>> the debugger, and ensure docking etc. works so tabs can be split off
>> and arranged around the main source window.
>>
>
> Fixed. Now stack pane will be displayed along with another panel at bottom
> and also docking has been introduced for all the panels so tabs will be
> arranged around main debugger panel.
>
>
>>
>> - Stepping is quite slow. What's causing that? I wonder if we really
>> need to have all the one line SQL templates - they're probably quite
>> expensive to process.
>>
> Fixed. This is due to polling timeout was high (1 second) and we are
> getting delay in getting the results. Now polling timeout has reduced to to
> 200ms.
>
>>
>> - Is backend_running.sql required? I've removed both versions as I
>> can't find any references to them. Are any other templates not
>> required?
>>
> Ok. No other templates.
>
>>
>> Will log any other issues that come up in further work.
>>
>> > Below functionalities are implemented but testing are pending.
>> >
>> > Trigger functions need to test with the debugger.
>> > Functions are tested with data types (like text, integer etc.) but it
>> needs
>> > to be tested with all the data types for direct debugging.
>> > Functions/Procedures need to test with PPAS 9.2 and earlier version
>> where
>> > debugger version is different.
>>
>> Thanks!
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>
>
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-14 12:52 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-14 20:39 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-18 08:55 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-18 12:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
@ 2016-04-19 06:29 ` Neel Patel <[email protected]>
2016-04-20 13:02 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-19 06:29 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
Please find the attached patch file with below fix.
- Remove the duplicate CSS and used the actual class for the debugger
button toolbar.
- As per the Ashesh's comment, previously we used 2 wcDocker instance to
arrange the multiple tabs to main debugger panel. Now with this patch file,
we have used only 1 wcDocker instance.
Do review it and let us know for comments.
Thanks,
Neel Patel
On Mon, Apr 18, 2016 at 6:07 PM, Dave Page <[email protected]> wrote:
> Hi
>
> On Monday, April 18, 2016, Neel Patel <[email protected]> wrote:
>
>> Hi Dave,
>>
>> Please find inline comments with all the fixes.
>> Attached is the updated patch file. Do review it and let me know for any
>> comments.
>>
>> If you find any issues, let me know .Now, Working on the remaining TODOs
>> as mentioned in below email.
>>
>
> Thanks - committed with some minor tweaks. One problem partly still
> remains though - you've partially copied the toolbar styling. Please use
> the actual classes used by the Properties panel. I've already updated the
> query tool in that way. Whilst your version looks much closer, it's missing
> the minimum button widths, and duplicates CSS unnecessarily.
>
> Thanks.
>
>
>>
>> On Fri, Apr 15, 2016 at 2:09 AM, Dave Page <[email protected]> wrote:
>>
>>> Hi
>>>
>>> On Thu, Apr 14, 2016 at 1:52 PM, Neel Patel <[email protected]>
>>> wrote:
>>> > Hi,
>>> >
>>> > Please find attached v2 patch file of the debugger which fixes the
>>> below
>>> > issues which was not present in the first patch.
>>> > In this patch, we have added new table in sqlite database to store the
>>> > functions arguments value user has given during debugging.
>>> > After applying this patch, user needs to execute "setup.py" to create
>>> the
>>> > new table in pgadmin4.db file.
>>> >
>>> > In direct debugging, when user debug the function then arguments
>>> values will
>>> > be stored in the sqlite database so when user debug the same function
>>> again
>>> > then previous values will be filled in the user input dialog.
>>> > Once the execution is completed then user will be able to do the debug
>>> of
>>> > the same function again by pressing the "Continue/Restart" button.
>>> > User can debug the "procedure" which is supported in PPAS database.
>>> > Replaced the "Glyphicon" with the "font-awesome" icons.
>>>
>>> Very cool! Committed, understanding that there are still improvements
>>> to be made.
>>>
>>> > Below are the TODOs
>>> >
>>> > Validate the input arguments values changed by user while depositing
>>> the
>>> > value during debugging.
>>> > Need to implement the code folding in the codemirror editor area.
>>> > As per the Ashesh's suggestion, need to add debug logs information so
>>> that
>>> > we can get the state of the debug function. Also need to add "arrow"
>>> next to
>>> > breakpoint in the gutters as per the pgadmin3.
>>> > Need to add "Debug package initializer" in the user input dialog for
>>> the
>>> > direct debugging.
>>> > Last but not least "Review comments" :)
>>>
>>> Here you go :-)
>>>
>>> - Ensure all messages are gettext enabled.
>>>
>>
>> Fixed.
>>
>>>
>>> - Constructs like the following won't work, because Jinja will
>>> evaluate the string " + err.errormsg + " before it ever gets evaluated
>>> as JS by the browser.
>>>
>>> Alertify.alert("{{ _('" + err.errormsg + "') }}");
>>>
>>
>> Fixed.
>>
>>
>>>
>>> - Please adjust the button bar to use the same styling as the button
>>> bar on the Properties tab.
>>>
>>
>> Fixed
>>
>>>
>>> - Let's make the stack pane tab part of the tabset at the bottom of
>>> the debugger, and ensure docking etc. works so tabs can be split off
>>> and arranged around the main source window.
>>>
>>
>> Fixed. Now stack pane will be displayed along with another panel at
>> bottom and also docking has been introduced for all the panels so tabs will
>> be arranged around main debugger panel.
>>
>>
>>>
>>> - Stepping is quite slow. What's causing that? I wonder if we really
>>> need to have all the one line SQL templates - they're probably quite
>>> expensive to process.
>>>
>> Fixed. This is due to polling timeout was high (1 second) and we are
>> getting delay in getting the results. Now polling timeout has reduced to to
>> 200ms.
>>
>>>
>>> - Is backend_running.sql required? I've removed both versions as I
>>> can't find any references to them. Are any other templates not
>>> required?
>>>
>> Ok. No other templates.
>>
>>>
>>> Will log any other issues that come up in further work.
>>>
>>> > Below functionalities are implemented but testing are pending.
>>> >
>>> > Trigger functions need to test with the debugger.
>>> > Functions are tested with data types (like text, integer etc.) but it
>>> needs
>>> > to be tested with all the data types for direct debugging.
>>> > Functions/Procedures need to test with PPAS 9.2 and earlier version
>>> where
>>> > debugger version is different.
>>>
>>> Thanks!
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
>>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] debugger_19_April_Fixes.patch (10.3K, 3-debugger_19_April_Fixes.patch)
download | inline diff:
diff --git a/web/pgadmin/tools/debugger/static/css/debugger.css b/web/pgadmin/tools/debugger/static/css/debugger.css
index dfeb331..cc0c1a5 100644
--- a/web/pgadmin/tools/debugger/static/css/debugger.css
+++ b/web/pgadmin/tools/debugger/static/css/debugger.css
@@ -1,19 +1,3 @@
-.btn-step-into, .btn-step-over, .btn-continue, .btn-toggle-breakpoint, .btn-clear-breakpoint, .btn-stop {
- background-color: #D2D2D2;
- left: 0px;
- right: 0px;
- padding: 7px;
-}
-
-.debugger-toolbar {
- background-color: #D2D2D2;
- border: 2px solid #A9A9A9;
- left: 0px;
- right: 0px;
- padding: 0px;
- padding-left: 2px;
-}
-
#container {
position: absolute;
top: 44px;
@@ -63,10 +47,10 @@
text-align: center;
}
-.pg-debugger-panel .breakpoints {
+.debugger-container .breakpoints {
width: 0.9em;
}
-.pg-debugger-panel .CodeMirror-activeline-background {
+.debugger-container .CodeMirror-activeline-background {
background: #50B0F0;
}
\ No newline at end of file
diff --git a/web/pgadmin/tools/debugger/templates/debugger/direct.html b/web/pgadmin/tools/debugger/templates/debugger/direct.html
index a72b90a..e98e7d0 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/direct.html
+++ b/web/pgadmin/tools/debugger/templates/debugger/direct.html
@@ -19,40 +19,34 @@ try {
{% block body %}
<nav class="navbar-inverse navbar-fixed-top">
- <div class="container-fluid debugger-toolbar">
- <div class="collapse navbar-collapse">
- <ul class="nav navbar-nav">
- <div id="btn-toolbar" class="btn-toolbar" role="toolbar" aria-label="">
- <div class="btn-group" role="group" aria-label="">
- <button type="button" class="btn btn-default btn-step-into" title="{{ _('Step into') }}">
- <i class="fa fa-indent"></i>
- </button>
- <button type="button" class="btn btn-default btn-step-over" title="{{ _('Step over') }}">
- <i class="fa fa-outdent"></i>
- </button>
- <button type="button" class="btn btn-default btn-continue" title="{{ _('Continue/Start') }}">
- <i class="fa fa-play-circle"></i>
- </button>
- </div>
- <div class="btn-group" role="group" aria-label="">
- <button type="button" class="btn btn-default btn-toggle-breakpoint" title="{{ _('Toggle breakpoint') }}">
- <i class="fa fa-circle"></i>
- </button>
- <button type="button" class="btn btn-default btn-clear-breakpoint" title="{{ _('Clear all breakpoints') }}">
- <i class="fa fa-ban"></i>
- </button>
- </div>
- <div class="btn-group" role="group" aria-label="">
- <button type="button" class="btn btn-default btn-stop" title="{{ _('Stop') }}">
- <i class="fa fa-stop-circle"></i>
- </button>
- </div>
+ <div id="btn-toolbar" class="btn-toolbar pg-prop-btn-group" role="toolbar" aria-label="">
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-step-into" title="{{ _('Step into') }}">
+ <i class="fa fa-indent"></i>
+ </button>
+ <button type="button" class="btn btn-default btn-step-over" title="{{ _('Step over') }}">
+ <i class="fa fa-outdent"></i>
+ </button>
+ <button type="button" class="btn btn-default btn-continue" title="{{ _('Continue/Start') }}">
+ <i class="fa fa-play-circle"></i>
+ </button>
+ </div>
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-toggle-breakpoint" title="{{ _('Toggle breakpoint') }}">
+ <i class="fa fa-circle"></i>
+ </button>
+ <button type="button" class="btn btn-default btn-clear-breakpoint" title="{{ _('Clear all breakpoints') }}">
+ <i class="fa fa-ban"></i>
+ </button>
+ </div>
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-stop" title="{{ _('Stop') }}">
+ <i class="fa fa-stop-circle"></i>
+ </button>
</div>
- </ul>
</div>
- </div>
</nav>
-<div id="container"></div>
+<div id="container" class="debugger-container"></div>
{% endblock %}
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
index 04fab8c..d7f2d78 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
@@ -1317,35 +1317,6 @@ define(
'code', false, '100%', '100%',
function(panel) {
- var container = panel.layout().scene().find('.pg-debugger-panel');
-
- // Create the wcSplitter used by wcDocker to split the single panel.
- var hSplitter = new wcSplitter(
- container, panel,
- wcDocker.ORIENTATION.VERTICAL
- );
-
- hSplitter.scrollable(0, false, false);
- hSplitter.scrollable(1, true, true);
-
- // Initialize this splitter with a layout in each pane.
- hSplitter.initLayouts(wcDocker.LAYOUT.SIMPLE, wcDocker.LAYOUT.SIMPLE);
-
- // By default, the splitter splits down the middle, we split the main panel by 80%.
- hSplitter.pos(0.65);
-
- var params = $('<div class="full-container params"></div>');
- hSplitter.right().addItem(params);
-
- // Create wcDocker for tab set.
- var out_docker = new wcDocker(
- '.full-container', {
- allowContextMenu: false,
- allowCollapse: false,
- themePath: '{{ url_for('static', filename='css/wcDocker/Themes') }}',
- theme: 'pgadmin'
- });
-
// Create the parameters panel to display the arguments of the functions
var parameters = new pgAdmin.Browser.Panel({
name: 'parameters',
@@ -1402,49 +1373,50 @@ define(
})
// Load all the created panels
- parameters.load(out_docker);
- local_variables.load(out_docker);
- messages.load(out_docker);
- results.load(out_docker);
- stack_pane.load(out_docker);
-
- // Add all the panels to the docker
- self.parameters_panel = out_docker.addPanel('parameters', wcDocker.DOCK.LEFT);
- self.local_variables_panel = out_docker.addPanel('local_variables', wcDocker.DOCK.STACKED, self.parameters_panel);
- self.stack_pane_panel = out_docker.addPanel('stack_pane', wcDocker.DOCK.STACKED, self.parameters_panel);
- self.messages_panel = out_docker.addPanel('messages', wcDocker.DOCK.STACKED, self.parameters_panel);
- self.results_panel = out_docker.addPanel('results', wcDocker.DOCK.STACKED, self.parameters_panel);
-
- // Now create a tab widget and put that into one of the sub splits.
- var editor_pane = $('<div id="stack_editor_pane" class="full-container-pane info"></div>');
- var code_editor_area = $('<textarea id="debugger-editor-textarea"></textarea>').append(editor_pane);
- hSplitter.left().addItem(code_editor_area);
-
- // To show the line-number and set breakpoint marker details by user.
- var editor = self.editor = CodeMirror.fromTextArea(
- code_editor_area.get(0), {
- lineNumbers: true,
- gutters: ["note-gutter", "CodeMirror-linenumbers", "breakpoints"],
- mode: "text/x-sql",
- readOnly: true
- });
+ parameters.load(self.docker);
+ local_variables.load(self.docker);
+ messages.load(self.docker);
+ results.load(self.docker);
+ stack_pane.load(self.docker);
+ });
+
+ self.code_editor_panel = self.docker.addPanel('code', wcDocker.DOCK.TOP );
+
+ self.parameters_panel = self.docker.addPanel(
+ 'parameters', wcDocker.DOCK.BOTTOM, self.code_editor_panel);
+ self.local_variables_panel = self.docker.addPanel('local_variables', wcDocker.DOCK.STACKED, self.parameters_panel, {
+ tabOrientation: wcDocker.TAB.TOP
+ });
+ self.messages_panel = self.docker.addPanel('messages', wcDocker.DOCK.STACKED, self.parameters_panel);
+ self.results_panel = self.docker.addPanel(
+ 'results', wcDocker.DOCK.STACKED, self.parameters_panel);
+ self.stack_pane_panel = self.docker.addPanel(
+ 'stack_pane', wcDocker.DOCK.STACKED, self.parameters_panel);
+
+ var editor_pane = $('<div id="stack_editor_pane" class="full-container-pane info"></div>');
+ var code_editor_area = $('<textarea id="debugger-editor-textarea"></textarea>').append(editor_pane);
+ self.code_editor_panel.layout().addItem(code_editor_area);
+
+ // To show the line-number and set breakpoint marker details by user.
+ var editor = self.editor = CodeMirror.fromTextArea(
+ code_editor_area.get(0), {
+ lineNumbers: true,
+ gutters: ["note-gutter", "CodeMirror-linenumbers", "breakpoints"],
+ mode: "text/x-sql",
+ readOnly: true
});
// On loading the docker, register the callbacks
var onLoad = function() {
- self.docker.finishLoading(100);
- self.docker.off(wcDocker.EVENT.LOADED);
- // Register the callback when user set/clear the breakpoint on gutter area.
- self.editor.on("gutterClick", self.onBreakPoint.bind(self), self);
+ self.docker.finishLoading(100);
+ self.docker.off(wcDocker.EVENT.LOADED);
+ // Register the callback when user set/clear the breakpoint on gutter area.
+ self.editor.on("gutterClick", self.onBreakPoint.bind(self), self);
};
self.docker.startLoading('{{ _('Loading...') }}');
self.docker.on(wcDocker.EVENT.LOADED, onLoad);
- self.main_panel = self.docker.addPanel(
- 'code', wcDocker.DOCK.TOP, null, {h: '100%', w: '100%'}
- );
-
// Create the toolbar view for debugging the function
this.toolbarView = new DebuggerToolbarView();
},
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-14 12:52 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-14 20:39 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-18 08:55 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-18 12:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-19 06:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-04-20 13:02 ` Dave Page <[email protected]>
2016-04-25 13:48 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Dave Page @ 2016-04-20 13:02 UTC (permalink / raw)
To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers
Thanks - applied!
On Tuesday, April 19, 2016, Neel Patel <[email protected]> wrote:
> Hi Dave,
>
> Please find the attached patch file with below fix.
>
> - Remove the duplicate CSS and used the actual class for the debugger
> button toolbar.
> - As per the Ashesh's comment, previously we used 2 wcDocker instance
> to arrange the multiple tabs to main debugger panel. Now with this patch
> file, we have used only 1 wcDocker instance.
>
> Do review it and let us know for comments.
>
> Thanks,
> Neel Patel
>
> On Mon, Apr 18, 2016 at 6:07 PM, Dave Page <[email protected]
> <javascript:_e(%7B%7D,'cvml','[email protected]');>> wrote:
>
>> Hi
>>
>> On Monday, April 18, 2016, Neel Patel <[email protected]
>> <javascript:_e(%7B%7D,'cvml','[email protected]');>> wrote:
>>
>>> Hi Dave,
>>>
>>> Please find inline comments with all the fixes.
>>> Attached is the updated patch file. Do review it and let me know for any
>>> comments.
>>>
>>> If you find any issues, let me know .Now, Working on the remaining TODOs
>>> as mentioned in below email.
>>>
>>
>> Thanks - committed with some minor tweaks. One problem partly still
>> remains though - you've partially copied the toolbar styling. Please use
>> the actual classes used by the Properties panel. I've already updated the
>> query tool in that way. Whilst your version looks much closer, it's missing
>> the minimum button widths, and duplicates CSS unnecessarily.
>>
>> Thanks.
>>
>>
>>>
>>> On Fri, Apr 15, 2016 at 2:09 AM, Dave Page <[email protected]> wrote:
>>>
>>>> Hi
>>>>
>>>> On Thu, Apr 14, 2016 at 1:52 PM, Neel Patel <
>>>> [email protected]> wrote:
>>>> > Hi,
>>>> >
>>>> > Please find attached v2 patch file of the debugger which fixes the
>>>> below
>>>> > issues which was not present in the first patch.
>>>> > In this patch, we have added new table in sqlite database to store the
>>>> > functions arguments value user has given during debugging.
>>>> > After applying this patch, user needs to execute "setup.py" to create
>>>> the
>>>> > new table in pgadmin4.db file.
>>>> >
>>>> > In direct debugging, when user debug the function then arguments
>>>> values will
>>>> > be stored in the sqlite database so when user debug the same function
>>>> again
>>>> > then previous values will be filled in the user input dialog.
>>>> > Once the execution is completed then user will be able to do the
>>>> debug of
>>>> > the same function again by pressing the "Continue/Restart" button.
>>>> > User can debug the "procedure" which is supported in PPAS database.
>>>> > Replaced the "Glyphicon" with the "font-awesome" icons.
>>>>
>>>> Very cool! Committed, understanding that there are still improvements
>>>> to be made.
>>>>
>>>> > Below are the TODOs
>>>> >
>>>> > Validate the input arguments values changed by user while depositing
>>>> the
>>>> > value during debugging.
>>>> > Need to implement the code folding in the codemirror editor area.
>>>> > As per the Ashesh's suggestion, need to add debug logs information so
>>>> that
>>>> > we can get the state of the debug function. Also need to add "arrow"
>>>> next to
>>>> > breakpoint in the gutters as per the pgadmin3.
>>>> > Need to add "Debug package initializer" in the user input dialog for
>>>> the
>>>> > direct debugging.
>>>> > Last but not least "Review comments" :)
>>>>
>>>> Here you go :-)
>>>>
>>>> - Ensure all messages are gettext enabled.
>>>>
>>>
>>> Fixed.
>>>
>>>>
>>>> - Constructs like the following won't work, because Jinja will
>>>> evaluate the string " + err.errormsg + " before it ever gets evaluated
>>>> as JS by the browser.
>>>>
>>>> Alertify.alert("{{ _('" + err.errormsg + "') }}");
>>>>
>>>
>>> Fixed.
>>>
>>>
>>>>
>>>> - Please adjust the button bar to use the same styling as the button
>>>> bar on the Properties tab.
>>>>
>>>
>>> Fixed
>>>
>>>>
>>>> - Let's make the stack pane tab part of the tabset at the bottom of
>>>> the debugger, and ensure docking etc. works so tabs can be split off
>>>> and arranged around the main source window.
>>>>
>>>
>>> Fixed. Now stack pane will be displayed along with another panel at
>>> bottom and also docking has been introduced for all the panels so tabs will
>>> be arranged around main debugger panel.
>>>
>>>
>>>>
>>>> - Stepping is quite slow. What's causing that? I wonder if we really
>>>> need to have all the one line SQL templates - they're probably quite
>>>> expensive to process.
>>>>
>>> Fixed. This is due to polling timeout was high (1 second) and we are
>>> getting delay in getting the results. Now polling timeout has reduced to to
>>> 200ms.
>>>
>>>>
>>>> - Is backend_running.sql required? I've removed both versions as I
>>>> can't find any references to them. Are any other templates not
>>>> required?
>>>>
>>> Ok. No other templates.
>>>
>>>>
>>>> Will log any other issues that come up in further work.
>>>>
>>>> > Below functionalities are implemented but testing are pending.
>>>> >
>>>> > Trigger functions need to test with the debugger.
>>>> > Functions are tested with data types (like text, integer etc.) but
>>>> it needs
>>>> > to be tested with all the data types for direct debugging.
>>>> > Functions/Procedures need to test with PPAS 9.2 and earlier version
>>>> where
>>>> > debugger version is different.
>>>>
>>>> Thanks!
>>>>
>>>> --
>>>> Dave Page
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>
>>>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>>
>
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-14 12:52 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-14 20:39 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-18 08:55 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-18 12:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-19 06:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-20 13:02 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
@ 2016-04-25 13:48 ` Neel Patel <[email protected]>
2016-05-06 12:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Akshay Joshi <[email protected]>
0 siblings, 1 reply; 15+ messages in thread
From: Neel Patel @ 2016-04-25 13:48 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi,
Please find attached patch file with below fixes.
- Removed the "lineWrapping" option from the codemirror textarea because
it was creating issue in the code folding.
- Handle the values while depositing during debugging.
- Properly handle the Array values while saving it to sqlite database
and displayed in input dialog.
- SQL code folding was not supported in codemirror so added the same.
Currently we have added support for below keywords.
IF-END IF
LOOP-END LOOP
BEGIN - END
CASE- END CASE
We will add more SQL folding support later ( e.g. Fold multiline query
inside function, Folding of create function etc.)
As query tool also requires the use of code folding so below is
the usage for it.
- Define below require javascript files in modules.
'codemirror/addon/fold/foldgutter',
'codemirror/addon/fold/foldcode',
'codemirror/addon/fold/pgadmin-sqlfoldcode'
- Remove "lineWrapping" option from the codemirror textarea.
- Add the below options while creating the Codemirror
textarea.
*foldOptions*: {
widget: "\u2026"
},
*foldGutter*: {
rangeFinder:
CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder,
CodeMirror.pgadminIfRangeFinder,
CodeMirror.pgadminLoopRangeFinder, CodeMirror.pgadminCaseRangeFinder)
},
*gutters*: ["CodeMirror-foldgutter"]
Do review it and let us know for comments.
Thanks,
Neel Patel
On Wed, Apr 20, 2016 at 6:32 PM, Dave Page <[email protected]> wrote:
> Thanks - applied!
>
>
> On Tuesday, April 19, 2016, Neel Patel <[email protected]>
> wrote:
>
>> Hi Dave,
>>
>> Please find the attached patch file with below fix.
>>
>> - Remove the duplicate CSS and used the actual class for the debugger
>> button toolbar.
>> - As per the Ashesh's comment, previously we used 2 wcDocker instance
>> to arrange the multiple tabs to main debugger panel. Now with this patch
>> file, we have used only 1 wcDocker instance.
>>
>> Do review it and let us know for comments.
>>
>> Thanks,
>> Neel Patel
>>
>> On Mon, Apr 18, 2016 at 6:07 PM, Dave Page <[email protected]> wrote:
>>
>>> Hi
>>>
>>> On Monday, April 18, 2016, Neel Patel <[email protected]>
>>> wrote:
>>>
>>>> Hi Dave,
>>>>
>>>> Please find inline comments with all the fixes.
>>>> Attached is the updated patch file. Do review it and let me know for
>>>> any comments.
>>>>
>>>> If you find any issues, let me know .Now, Working on the remaining
>>>> TODOs as mentioned in below email.
>>>>
>>>
>>> Thanks - committed with some minor tweaks. One problem partly still
>>> remains though - you've partially copied the toolbar styling. Please use
>>> the actual classes used by the Properties panel. I've already updated the
>>> query tool in that way. Whilst your version looks much closer, it's missing
>>> the minimum button widths, and duplicates CSS unnecessarily.
>>>
>>> Thanks.
>>>
>>>
>>>>
>>>> On Fri, Apr 15, 2016 at 2:09 AM, Dave Page <[email protected]> wrote:
>>>>
>>>>> Hi
>>>>>
>>>>> On Thu, Apr 14, 2016 at 1:52 PM, Neel Patel <
>>>>> [email protected]> wrote:
>>>>> > Hi,
>>>>> >
>>>>> > Please find attached v2 patch file of the debugger which fixes the
>>>>> below
>>>>> > issues which was not present in the first patch.
>>>>> > In this patch, we have added new table in sqlite database to store
>>>>> the
>>>>> > functions arguments value user has given during debugging.
>>>>> > After applying this patch, user needs to execute "setup.py" to
>>>>> create the
>>>>> > new table in pgadmin4.db file.
>>>>> >
>>>>> > In direct debugging, when user debug the function then arguments
>>>>> values will
>>>>> > be stored in the sqlite database so when user debug the same
>>>>> function again
>>>>> > then previous values will be filled in the user input dialog.
>>>>> > Once the execution is completed then user will be able to do the
>>>>> debug of
>>>>> > the same function again by pressing the "Continue/Restart" button.
>>>>> > User can debug the "procedure" which is supported in PPAS database.
>>>>> > Replaced the "Glyphicon" with the "font-awesome" icons.
>>>>>
>>>>> Very cool! Committed, understanding that there are still improvements
>>>>> to be made.
>>>>>
>>>>> > Below are the TODOs
>>>>> >
>>>>> > Validate the input arguments values changed by user while depositing
>>>>> the
>>>>> > value during debugging.
>>>>> > Need to implement the code folding in the codemirror editor area.
>>>>> > As per the Ashesh's suggestion, need to add debug logs information
>>>>> so that
>>>>> > we can get the state of the debug function. Also need to add "arrow"
>>>>> next to
>>>>> > breakpoint in the gutters as per the pgadmin3.
>>>>> > Need to add "Debug package initializer" in the user input dialog for
>>>>> the
>>>>> > direct debugging.
>>>>> > Last but not least "Review comments" :)
>>>>>
>>>>> Here you go :-)
>>>>>
>>>>> - Ensure all messages are gettext enabled.
>>>>>
>>>>
>>>> Fixed.
>>>>
>>>>>
>>>>> - Constructs like the following won't work, because Jinja will
>>>>> evaluate the string " + err.errormsg + " before it ever gets evaluated
>>>>> as JS by the browser.
>>>>>
>>>>> Alertify.alert("{{ _('" + err.errormsg + "') }}");
>>>>>
>>>>
>>>> Fixed.
>>>>
>>>>
>>>>>
>>>>> - Please adjust the button bar to use the same styling as the button
>>>>> bar on the Properties tab.
>>>>>
>>>>
>>>> Fixed
>>>>
>>>>>
>>>>> - Let's make the stack pane tab part of the tabset at the bottom of
>>>>> the debugger, and ensure docking etc. works so tabs can be split off
>>>>> and arranged around the main source window.
>>>>>
>>>>
>>>> Fixed. Now stack pane will be displayed along with another panel at
>>>> bottom and also docking has been introduced for all the panels so tabs will
>>>> be arranged around main debugger panel.
>>>>
>>>>
>>>>>
>>>>> - Stepping is quite slow. What's causing that? I wonder if we really
>>>>> need to have all the one line SQL templates - they're probably quite
>>>>> expensive to process.
>>>>>
>>>> Fixed. This is due to polling timeout was high (1 second) and we are
>>>> getting delay in getting the results. Now polling timeout has reduced to to
>>>> 200ms.
>>>>
>>>>>
>>>>> - Is backend_running.sql required? I've removed both versions as I
>>>>> can't find any references to them. Are any other templates not
>>>>> required?
>>>>>
>>>> Ok. No other templates.
>>>>
>>>>>
>>>>> Will log any other issues that come up in further work.
>>>>>
>>>>> > Below functionalities are implemented but testing are pending.
>>>>> >
>>>>> > Trigger functions need to test with the debugger.
>>>>> > Functions are tested with data types (like text, integer etc.) but
>>>>> it needs
>>>>> > to be tested with all the data types for direct debugging.
>>>>> > Functions/Procedures need to test with PPAS 9.2 and earlier version
>>>>> where
>>>>> > debugger version is different.
>>>>>
>>>>> Thanks!
>>>>>
>>>>> --
>>>>> Dave Page
>>>>> Blog: http://pgsnake.blogspot.com
>>>>> Twitter: @pgsnake
>>>>>
>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>> The Enterprise PostgreSQL Company
>>>>>
>>>>
>>>>
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>>
>>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] debugger_25_April_Fixes.patch (25.3K, 3-debugger_25_April_Fixes.patch)
download | inline diff:
diff --git a/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js b/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
index 999ac7c..60f6b84 100644
--- a/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
+++ b/web/pgadmin/static/js/backgrid/backgrid.pgadmin.js
@@ -762,9 +762,10 @@
initialize: function() {
Backgrid.Cell.prototype.initialize.apply(this, arguments);
// set value to empty array.
+ var m = arguments[0].model;
if (_.isUndefined(this.collection)) {
this.collection = new (Backbone.Collection.extend({
- model: arrayCellModel}));
+ model: arrayCellModel}))(m.get('value'));
}
this.model.set(this.column.get('name'), this.collection);
@@ -784,9 +785,10 @@
initialize: function() {
Backgrid.Cell.prototype.initialize.apply(this, arguments);
// set value to empty array.
+ var m = arguments[0].model;
if (_.isUndefined(this.collection)) {
this.collection = new (Backbone.Collection.extend({
- model: arrayCellModel}));
+ model: arrayCellModel}))(m.get('value'));
}
diff --git a/web/pgadmin/static/js/codemirror/addon/fold/pgadmin-sqlfoldcode.js b/web/pgadmin/static/js/codemirror/addon/fold/pgadmin-sqlfoldcode.js
new file mode 100644
index 0000000..81f8393
--- /dev/null
+++ b/web/pgadmin/static/js/codemirror/addon/fold/pgadmin-sqlfoldcode.js
@@ -0,0 +1,83 @@
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.pgadminKeywordRangeFinder = function(cm, start, startTkn, endTkn) {
+ var line = start.line, lineText = cm.getLine(line);
+ var at = lineText.length, startChar, tokenType;
+ for (; at > 0;) {
+ var found = lineText.lastIndexOf(startTkn, at);
+ var startToken = startTkn;
+ var endToken = endTkn;
+ if (found < start.ch) {
+ var found = lineText.lastIndexOf("[", at);
+ if (found < start.ch) break;
+ var startToken = '[';
+ var endToken = ']';
+ }
+
+ tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type;
+ if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
+ at = found - 1;
+ }
+ if (startChar == null || lineText.lastIndexOf(startToken) > startChar) return;
+ var count = 1, lastLine = cm.lineCount(), end, endCh;
+ outer: for (var i = line + 1; i < lastLine; ++i) {
+ var text = cm.getLine(i), pos = 0;
+ for (;;) {
+ var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
+ if (nextOpen < 0) nextOpen = text.length;
+ if (nextClose < 0) nextClose = text.length;
+ pos = Math.min(nextOpen, nextClose);
+ if (pos == text.length) break;
+ if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) {
+ if (pos == nextOpen) ++count;
+ else if (!--count) {
+ end = i;
+ endCh = pos;
+ break outer;
+ }
+ }
+ ++pos;
+ }
+ }
+ if (end == null || end == line + 1) return;
+ return {from: CodeMirror.Pos(line, startChar + startTkn.length),
+ to: CodeMirror.Pos(end, endCh)};
+ };
+
+ CodeMirror.pgadminBeginRangeFinder = function(cm, start) {
+ var startToken = 'BEGIN';
+ var endToken = 'END;';
+ var fromToPos = CodeMirror.pgadminKeywordRangeFinder(cm, start, startToken, endToken);
+ return fromToPos;
+ };
+
+ CodeMirror.pgadminIfRangeFinder = function(cm, start) {
+ var startToken = 'IF';
+ var endToken = 'END IF';
+ var fromToPos = CodeMirror.pgadminKeywordRangeFinder(cm, start, startToken, endToken);
+ return fromToPos;
+ };
+
+ CodeMirror.pgadminLoopRangeFinder = function(cm, start) {
+ var startToken = 'LOOP';
+ var endToken = 'END LOOP';
+ var fromToPos = CodeMirror.pgadminKeywordRangeFinder(cm, start, startToken, endToken);
+ return fromToPos;
+ };
+
+ CodeMirror.pgadminCaseRangeFinder = function(cm, start) {
+ var startToken = 'CASE';
+ var endToken = 'END CASE';
+ var fromToPos = CodeMirror.pgadminKeywordRangeFinder(cm, start, startToken, endToken);
+ return fromToPos;
+ };
+
+});
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py
index 3976498..bc02ba4 100644
--- a/web/pgadmin/tools/debugger/__init__.py
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -447,13 +447,8 @@ def close(trans_id):
# Release the connection
if conn.connected():
- if obj['debug_type'] == 'indirect':
- # render the SQL template and send the query to server
- sql = render_template("/".join([template_path, 'abort_target.sql']),
- session_id=obj['session_id'])
- status, res = conn.execute_dict(sql)
- if not status:
- return internal_server_error(errormsg=res)
+ # on successful connection cancel the running transaction
+ status, result = conn.cancel_transaction(obj['conn_id'], obj['database_id'])
# Delete the existing debugger data in session variable
del session['debuggerData'][str(trans_id)]
@@ -1066,11 +1061,18 @@ def deposit_parameter_value(trans_id):
status, result = conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=result)
+
+ # Check if value deposited successfully or not and depending on the result, return the message information.
+ if result['rows'][0]['pldbg_deposit_value']:
+ info = gettext('Value deposited successfully')
+ else:
+ info = gettext('Error while setting the value')
+ return make_json_response(data={'status': status, 'info':info, 'result': result['rows'][0]['pldbg_deposit_value']})
else:
status = False
result = gettext('Not connected to server or connection with the server has been closed.')
- return make_json_response(data={'status': status, 'result': result['rows']})
+ return make_json_response(data={'status': status, 'result': result})
@blueprint.route('/select_frame/<int:trans_id>/<int:frame_id>', methods=['GET'])
@@ -1200,6 +1202,18 @@ def set_arguments_sqlite(sid, did, scid, func_id):
server_id=data[i]['server_id'], database_id=data[i]['database_id'], schema_id=data[i]['schema_id'],
function_id=data[i]['function_id'], arg_id=data[i]['arg_id']).count()
+ # handle the Array list sent from the client
+ array_string = ''
+ if data[i]['value'].__class__.__name__ in ('list') and data[i]['value']:
+ for k in range(0, len(data[i]['value'])):
+ array_string += data[i]['value'][k]['value']
+ if k != (len(data[i]['value']) - 1):
+ array_string += ','
+ elif data[i]['value'].__class__.__name__ in ('list') and not data[i]['value']:
+ array_string = ''
+ else:
+ array_string = data[i]['value']
+
# Check if data is already available in database then update the existing value otherwise add the new value
if DbgFuncArgsExists:
DbgFuncArgs = DebuggerFunctionArguments.query.filter_by(
@@ -1209,7 +1223,7 @@ def set_arguments_sqlite(sid, did, scid, func_id):
DbgFuncArgs.is_null = data[i]['is_null']
DbgFuncArgs.is_expression = data[i]['is_expression']
DbgFuncArgs.use_default = data[i]['use_default']
- DbgFuncArgs.value = data[i]['value']
+ DbgFuncArgs.value = array_string
else:
debugger_func_args = DebuggerFunctionArguments(
server_id = data[i]['server_id'],
@@ -1220,7 +1234,7 @@ def set_arguments_sqlite(sid, did, scid, func_id):
is_null = data[i]['is_null'],
is_expression = data[i]['is_expression'],
use_default = data[i]['use_default'],
- value = data[i]['value']
+ value = array_string
)
db.session.add(debugger_func_args)
diff --git a/web/pgadmin/tools/debugger/static/css/debugger.css b/web/pgadmin/tools/debugger/static/css/debugger.css
index cc0c1a5..40eb195 100644
--- a/web/pgadmin/tools/debugger/static/css/debugger.css
+++ b/web/pgadmin/tools/debugger/static/css/debugger.css
@@ -53,4 +53,33 @@
.debugger-container .CodeMirror-activeline-background {
background: #50B0F0;
-}
\ No newline at end of file
+}
+
+.CodeMirror-foldmarker {
+ color: blue;
+ text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
+ font-family: arial;
+ line-height: .3;
+ cursor: pointer;
+}
+
+.CodeMirror, .CodeMirror-gutters {
+ min-height: 100%;
+}
+
+.CodeMirror-foldgutter {
+ width: .9em;
+}
+
+.CodeMirror-foldgutter-open,
+.CodeMirror-foldgutter-folded {
+ cursor: pointer;
+}
+
+.CodeMirror-foldgutter-open:after {
+ content: "\25BC";
+}
+
+.CodeMirror-foldgutter-folded:after {
+ content: "\25B6";
+}
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
index f345dc9..846a539 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/debugger_ui.js
@@ -287,11 +287,25 @@ define(
}
// Need to update the func_obj variable from sqlite database if available
- // TODO: Need to check, how to update the value in Array fields....
if (func_args_data.length != 0) {
for (i = 0; i < func_args_data.length; i++) {
var index = func_args_data[i]['arg_id'];
- func_obj.push({ "name": argname[index], "type": argtype[index], "is_null": func_args_data[i]['is_null'] ? true: false, "expr": func_args_data[i]['is_expression']? true: false, "value": func_args_data[i]['value'], "use_default": func_args_data[i]['use_default']? true: false, "default_value": def_val_list[index]});
+ var values = [];
+ if (argtype[index].indexOf("[]") !=-1) {
+ var vals = func_args_data[i]['value'].split(",");
+ if (argtype[index].indexOf("integer") != -1) {
+ _.each(vals, function(val){
+ values.push({'value': parseInt(val)});
+ });
+ }
+ _.each(vals, function(val){
+ values.push({'value': val});
+ });
+ } else {
+ values = func_args_data[i]['value'];
+ }
+
+ func_obj.push({ "name": argname[index], "type": argtype[index], "is_null": func_args_data[i]['is_null'] ? true: false, "expr": func_args_data[i]['is_expression']? true: false, "value": values, "use_default": func_args_data[i]['use_default']? true: false, "default_value": def_val_list[index]});
}
}
}
@@ -358,11 +372,24 @@ define(
}
// Need to update the func_obj variable from sqlite database if available
- // TODO: Need to check, how to update the value in Array fields....
if (func_args_data.length != 0) {
for (i = 0; i < func_args_data.length; i++) {
var index = func_args_data[i]['arg_id'];
- func_obj.push({ "name": myargname[index], "type": argtype[index], "is_null": func_args_data[i]['is_null'] ? true: false, "expr": func_args_data[i]['is_expression']? true: false, "value": func_args_data[i]['value'], "use_default": func_args_data[i]['use_default']? true: false, "default_value": def_val_list[index]});
+ var values = [];
+ if (argtype[index].indexOf("[]") !=-1) {
+ var vals = func_args_data[i]['value'].split(",");
+ if (argtype[index].indexOf("integer") != -1) {
+ _.each(vals, function(val){
+ values.push({'value': parseInt(val)});
+ });
+ }
+ _.each(vals, function(val){
+ values.push({'value': val});
+ });
+ } else {
+ values = func_args_data[i]['value'];
+ }
+ func_obj.push({ "name": myargname[index], "type": argtype[index], "is_null": func_args_data[i]['is_null'] ? true: false, "expr": func_args_data[i]['is_expression']? true: false, "value": values, "use_default": func_args_data[i]['use_default']? true: false, "default_value": def_val_list[index]});
}
}
}
@@ -423,7 +450,6 @@ define(
this.grid.collection.each(function(m) {
- // TODO: Removed temporary for testing.....
// Check if value is set to NULL then we should ignore the value field
if (m.get('is_null')) {
args_value_list.push({ 'name': m.get('name'),
diff --git a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
index 05801ea..4574716 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
+++ b/web/pgadmin/tools/debugger/templates/debugger/js/direct.js
@@ -1,7 +1,8 @@
define(
['jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin','pgadmin.browser',
'backbone', 'backgrid', 'codemirror', 'backform','pgadmin.tools.debugger.ui',
- 'wcdocker', 'pgadmin.backform', 'pgadmin.backgrid', 'codemirror/addon/selection/active-line'],
+ 'wcdocker', 'pgadmin.backform', 'pgadmin.backgrid', 'codemirror/addon/selection/active-line',
+ 'codemirror/addon/fold/foldgutter', 'codemirror/addon/fold/foldcode', 'codemirror/addon/fold/pgadmin-sqlfoldcode'],
function($, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid, CodeMirror, Backform, debug_function_again) {
if (pgAdmin.Browser.tree != null) {
@@ -251,7 +252,7 @@ define(
During the execution we should poll the result in minimum seconds but once the execution is completed
and wait for the another debugging session then we should decrease the polling frequency.
*/
- if (pgTools.DirectDebug.direct_execution_completed) {
+ if (pgTools.DirectDebug.polling_timeout_idle) {
// poll the result after 1 second
var poll_timeout = 1000;
}
@@ -273,6 +274,7 @@ define(
}
else {
if (res.data.result[0].src != undefined || res.data.result[0].src != null) {
+ pgTools.DirectDebug.polling_timeout_idle = false;
pgTools.DirectDebug.docker.finishLoading(50);
pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
self.UpdateBreakpoint(trans_id);
@@ -293,8 +295,10 @@ define(
self.clear_all_breakpoint(trans_id);
self.execute_query(trans_id);
pgTools.DirectDebug.first_time_indirect_debug = true;
+ pgTools.DirectDebug.polling_timeout_idle = false;
}
else {
+ pgTools.DirectDebug.polling_timeout_idle = false;
pgTools.DirectDebug.docker.finishLoading(50);
// If the source is really changed then only update the breakpoint information
if (res.data.result[0].src != pgTools.DirectDebug.editor.getValue()) {
@@ -321,6 +325,7 @@ define(
}
}
else if (res.data.status === 'Busy') {
+ pgTools.DirectDebug.polling_timeout_idle = true;
// If status is Busy then poll the result by recursive call to the poll function
if (!pgTools.DirectDebug.debug_type) {
pgTools.DirectDebug.docker.startLoading('{{ _('Waiting for another session to invoke the target...') }}');
@@ -368,7 +373,7 @@ define(
During the execution we should poll the result in minimum seconds but once the execution is completed
and wait for the another debugging session then we should decrease the polling frequency.
*/
- if (pgTools.DirectDebug.direct_execution_completed) {
+ if (pgTools.DirectDebug.polling_timeout_idle) {
// poll the result to check that execution is completed or not after 1200 ms
var poll_end_timeout = 1200;
}
@@ -391,6 +396,7 @@ define(
*/
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap', 'CodeMirror-activeline-background');
pgTools.DirectDebug.direct_execution_completed = true;
+ pgTools.DirectDebug.polling_timeout_idle = true;
//Set the alertify message to inform the user that execution is completed.
Alertify.notify(
@@ -418,6 +424,7 @@ define(
self.AddResults(res.data.result);
pgTools.DirectDebug.results_panel.focus();
pgTools.DirectDebug.direct_execution_completed = true;
+ pgTools.DirectDebug.polling_timeout_idle = true;
//Set the alertify message to inform the user that execution is completed.
Alertify.notify(
@@ -537,10 +544,9 @@ define(
var self = this;
//Check first if previous execution was completed or not
- if (pgTools.DirectDebug.direct_execution_completed) {
- // TODO: We need to get the arguments given by the user from sqlite database
+ if (pgTools.DirectDebug.direct_execution_completed &&
+ pgTools.DirectDebug.direct_execution_completed == pgTools.DirectDebug.polling_timeout_idle) {
self.Restart(trans_id);
- pgTools.DirectDebug.direct_execution_completed = false;
}
else {
// Make ajax call to listen the database message
@@ -802,7 +808,6 @@ define(
if (result.length != 0)
{
for (i = 0; i < result.length; i++) {
- // TODO: change the my_func_test_2 with name of the function to be executed.
my_obj.push({ "name": result[i].targetname, "value": result[i].args, "line_no": result[i].linenumber });
}
}
@@ -871,7 +876,6 @@ define(
if (result.value.length != 0)
{
for (i = 0; i < result.value.length; i++) {
- // TODO: change the my_func_test_2 with name of the function to be executed.
my_obj.push({ "value": result.value[i]});
}
}
@@ -1014,6 +1018,13 @@ define(
if (res.data.status) {
// Get the updated variables value
self.GetLocalVariables(pgTools.DirectDebug.trans_id);
+ // Show the message to the user that deposit value is success or failure
+ Alertify.notify(
+ res.data.info,
+ res.data.result ? 'success': 'error',
+ 3,
+ function() { }
+ );
}
},
error: function(e) {
@@ -1187,6 +1198,7 @@ define(
this.debug_type = debug_type;
this.first_time_indirect_debug = false;
this.direct_execution_completed = false;
+ this.polling_timeout_idle = false;
var docker = this.docker = new wcDocker(
'#container', {
@@ -1268,14 +1280,14 @@ define(
}
else if (res.data.status === 'NotConnected') {
Alertify.alert(
- 'Data grid poll result error',
+ 'Not connected to server or connection with the server has been closed.',
res.data.result
);
}
},
error: function(e) {
Alertify.alert(
- 'Debugger listener starting error'
+ 'Debugger: Error fetching messages information'
);
}
});
@@ -1283,31 +1295,33 @@ define(
},
// Callback function when user click on gutters of codemirror to set/clear the breakpoint
- onBreakPoint: function(cm, m) {
+ onBreakPoint: function(cm, m, gutter) {
var self = this;
- // TODO::
- // We may want to check, if break-point is allowed at this moment or not
- var info = cm.lineInfo(m);
+ // If breakpoint gutter is clicked and execution is not completed then only set the breakpoint
+ if (gutter == "breakpoints" && !pgTools.DirectDebug.polling_timeout_idle ) {
+ // We may want to check, if break-point is allowed at this moment or not
+ var info = cm.lineInfo(m);
- // If gutterMarker is undefined that means there is no marker defined previously
- // So we need to set the breakpoint command here...
- if (info.gutterMarkers == undefined) {
- controller.set_breakpoint(self.trans_id,m+1,1); //set the breakpoint
- }
- else {
- controller.set_breakpoint(self.trans_id,m+1,0); //clear the breakpoint
- }
+ // If gutterMarker is undefined that means there is no marker defined previously
+ // So we need to set the breakpoint command here...
+ if (info.gutterMarkers == undefined) {
+ controller.set_breakpoint(self.trans_id,m+1,1); //set the breakpoint
+ }
+ else {
+ controller.set_breakpoint(self.trans_id,m+1,0); //clear the breakpoint
+ }
- cm.setGutterMarker(
- m, "breakpoints", info.gutterMarkers ? null : function() {
- var marker = document.createElement("div");
+ cm.setGutterMarker(
+ m, "breakpoints", info.gutterMarkers ? null : function() {
+ var marker = document.createElement("div");
- marker.style.color = "#822";
- marker.innerHTML = "●";
+ marker.style.color = "#822";
+ marker.innerHTML = "●";
- return marker;
+ return marker;
}());
+ }
},
// Create the debugger layout with splitter and display the appropriate data received from server.
@@ -1401,8 +1415,14 @@ define(
var editor = self.editor = CodeMirror.fromTextArea(
code_editor_area.get(0), {
lineNumbers: true,
- lineWrapping: true,
- gutters: ["note-gutter", "CodeMirror-linenumbers", "breakpoints"],
+ foldOptions: {
+ widget: "\u2026"
+ },
+ foldGutter: {
+ rangeFinder: CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder, CodeMirror.pgadminIfRangeFinder,
+ CodeMirror.pgadminLoopRangeFinder, CodeMirror.pgadminCaseRangeFinder)
+ },
+ gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "breakpoints"],
mode: "text/x-pgsql",
readOnly: true
});
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql
index d43f0e2..3c0cd78 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_edbspl.sql
@@ -34,7 +34,7 @@
{% set strParam = "p_param" ~ (loop.index - 1) %}
{% set str_declare = str_declare ~ "\t" ~ strParam ~ " " ~ arg_type[loop.index - 1] %}
{% if arg_mode == 'b' %}
-{### TODO: to check for Null parameters received from client ###}
+{### Handle Null parameters received from client ###}
{% if data[input_value_index]['type'] == 'text' and data[input_value_index]['value'] != 'NULL' %}
{% set tmp_val = data[input_value_index]['value']|qtLiteral %}
{% set str_declare = str_declare ~ " := " ~ strParam ~ " " ~ tmp_val ~ "::" ~ data[input_value_index]['type'] %}
@@ -61,7 +61,7 @@
{% set str_statement = str_statement ~ "VARIADIC " %}
{% endif %}
-{### TODO: to check for Null parameters received from client ###}
+{### Handle Null parameters received from client ###}
{% if data[input_value_index]['type'] == 'text' and data[input_value_index]['value'] != 'NULL' %}
{% set tmp_var = data[input_value_index]['value']|qtLiteral %}
{% set str_statement = str_statement ~ tmp_var ~ "::" ~ data[input_value_index]['type'] %}
@@ -101,4 +101,4 @@
{### Return final query formed with above condition ###}
{% if not inside_loop.value %}
{{ strQuery }}
-{% endif %}
\ No newline at end of file
+{% endif %}
diff --git a/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql
index ecd75f1..e096703 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql
+++ b/web/pgadmin/tools/debugger/templates/debugger/sql/execute_plpgsql.sql
@@ -11,7 +11,7 @@
{% if 'type' in dict_item and 'value' in dict_item %}
{% if dict_item['type'] == 'text' and dict_item['value'] != 'NULL' %}
{{ dict_item['value']|qtLiteral }}::{{ dict_item['type'] }}{% if not loop.last %}, {% endif %}
-{% elif dict_item['type'] == 'text' and dict_item['value'] == 'NULL' %}
+{% elif dict_item['value'] == 'NULL' %}
{{ dict_item['value'] }}::{{ dict_item['type'] }}{% if not loop.last %}, {% endif %}
{% else %}
{% if '[]' in dict_item['type'] %}
@@ -29,4 +29,4 @@
{% endif %}
{% endfor %}
{% endif %}
-)
\ No newline at end of file
+)
^ permalink raw reply [nested|flat] 15+ messages in thread
* Re: [pgAdmin4][Debugger]: Initial Patch
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-06 06:08 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:20 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 06:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 07:57 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-06 08:07 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-14 12:52 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-14 20:39 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-18 08:55 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-18 12:37 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-19 06:29 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-20 13:02 ` Re: [pgAdmin4][Debugger]: Initial Patch Dave Page <[email protected]>
2016-04-25 13:48 ` Re: [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
@ 2016-05-06 12:20 ` Akshay Joshi <[email protected]>
0 siblings, 0 replies; 15+ messages in thread
From: Akshay Joshi @ 2016-05-06 12:20 UTC (permalink / raw)
To: Neel Patel <[email protected]>; +Cc: Dave Page <[email protected]>; pgadmin-hackers
On Mon, Apr 25, 2016 at 7:18 PM, Neel Patel <[email protected]>
wrote:
> Hi,
>
> Please find attached patch file with below fixes.
>
> - Removed the "lineWrapping" option from the codemirror textarea
> because it was creating issue in the code folding.
> - Handle the values while depositing during debugging.
> - Properly handle the Array values while saving it to sqlite database
> and displayed in input dialog.
> - SQL code folding was not supported in codemirror so added the same.
> Currently we have added support for below keywords.
> IF-END IF
> LOOP-END LOOP
> BEGIN - END
> CASE- END CASE
> We will add more SQL folding support later ( e.g. Fold multiline query
> inside function, Folding of create function etc.)
>
> As query tool also requires the use of code folding so below is
> the usage for it.
> - Define below require javascript files in modules.
> 'codemirror/addon/fold/foldgutter',
> 'codemirror/addon/fold/foldcode',
> 'codemirror/addon/fold/pgadmin-sqlfoldcode'
>
> - Remove "lineWrapping" option from the codemirror textarea.
>
> - Add the below options while creating the Codemirror
> textarea.
>
> *foldOptions*: {
> widget: "\u2026"
> },
> *foldGutter*: {
> rangeFinder:
> CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder,
> CodeMirror.pgadminIfRangeFinder,
>
> CodeMirror.pgadminLoopRangeFinder, CodeMirror.pgadminCaseRangeFinder)
> },
> *gutters*: ["CodeMirror-foldgutter"]
>
>
> Do review it and let us know for comments.
>
Thanks patch applied.
>
> Thanks,
> Neel Patel
>
>
> On Wed, Apr 20, 2016 at 6:32 PM, Dave Page <[email protected]> wrote:
>
>> Thanks - applied!
>>
>>
>> On Tuesday, April 19, 2016, Neel Patel <[email protected]>
>> wrote:
>>
>>> Hi Dave,
>>>
>>> Please find the attached patch file with below fix.
>>>
>>> - Remove the duplicate CSS and used the actual class for the
>>> debugger button toolbar.
>>> - As per the Ashesh's comment, previously we used 2 wcDocker
>>> instance to arrange the multiple tabs to main debugger panel. Now with this
>>> patch file, we have used only 1 wcDocker instance.
>>>
>>> Do review it and let us know for comments.
>>>
>>> Thanks,
>>> Neel Patel
>>>
>>> On Mon, Apr 18, 2016 at 6:07 PM, Dave Page <[email protected]> wrote:
>>>
>>>> Hi
>>>>
>>>> On Monday, April 18, 2016, Neel Patel <[email protected]>
>>>> wrote:
>>>>
>>>>> Hi Dave,
>>>>>
>>>>> Please find inline comments with all the fixes.
>>>>> Attached is the updated patch file. Do review it and let me know for
>>>>> any comments.
>>>>>
>>>>> If you find any issues, let me know .Now, Working on the remaining
>>>>> TODOs as mentioned in below email.
>>>>>
>>>>
>>>> Thanks - committed with some minor tweaks. One problem partly still
>>>> remains though - you've partially copied the toolbar styling. Please use
>>>> the actual classes used by the Properties panel. I've already updated the
>>>> query tool in that way. Whilst your version looks much closer, it's missing
>>>> the minimum button widths, and duplicates CSS unnecessarily.
>>>>
>>>> Thanks.
>>>>
>>>>
>>>>>
>>>>> On Fri, Apr 15, 2016 at 2:09 AM, Dave Page <[email protected]> wrote:
>>>>>
>>>>>> Hi
>>>>>>
>>>>>> On Thu, Apr 14, 2016 at 1:52 PM, Neel Patel <
>>>>>> [email protected]> wrote:
>>>>>> > Hi,
>>>>>> >
>>>>>> > Please find attached v2 patch file of the debugger which fixes the
>>>>>> below
>>>>>> > issues which was not present in the first patch.
>>>>>> > In this patch, we have added new table in sqlite database to store
>>>>>> the
>>>>>> > functions arguments value user has given during debugging.
>>>>>> > After applying this patch, user needs to execute "setup.py" to
>>>>>> create the
>>>>>> > new table in pgadmin4.db file.
>>>>>> >
>>>>>> > In direct debugging, when user debug the function then arguments
>>>>>> values will
>>>>>> > be stored in the sqlite database so when user debug the same
>>>>>> function again
>>>>>> > then previous values will be filled in the user input dialog.
>>>>>> > Once the execution is completed then user will be able to do the
>>>>>> debug of
>>>>>> > the same function again by pressing the "Continue/Restart" button.
>>>>>> > User can debug the "procedure" which is supported in PPAS database.
>>>>>> > Replaced the "Glyphicon" with the "font-awesome" icons.
>>>>>>
>>>>>> Very cool! Committed, understanding that there are still improvements
>>>>>> to be made.
>>>>>>
>>>>>> > Below are the TODOs
>>>>>> >
>>>>>> > Validate the input arguments values changed by user while
>>>>>> depositing the
>>>>>> > value during debugging.
>>>>>> > Need to implement the code folding in the codemirror editor area.
>>>>>> > As per the Ashesh's suggestion, need to add debug logs information
>>>>>> so that
>>>>>> > we can get the state of the debug function. Also need to add
>>>>>> "arrow" next to
>>>>>> > breakpoint in the gutters as per the pgadmin3.
>>>>>> > Need to add "Debug package initializer" in the user input dialog
>>>>>> for the
>>>>>> > direct debugging.
>>>>>> > Last but not least "Review comments" :)
>>>>>>
>>>>>> Here you go :-)
>>>>>>
>>>>>> - Ensure all messages are gettext enabled.
>>>>>>
>>>>>
>>>>> Fixed.
>>>>>
>>>>>>
>>>>>> - Constructs like the following won't work, because Jinja will
>>>>>> evaluate the string " + err.errormsg + " before it ever gets evaluated
>>>>>> as JS by the browser.
>>>>>>
>>>>>> Alertify.alert("{{ _('" + err.errormsg + "') }}");
>>>>>>
>>>>>
>>>>> Fixed.
>>>>>
>>>>>
>>>>>>
>>>>>> - Please adjust the button bar to use the same styling as the button
>>>>>> bar on the Properties tab.
>>>>>>
>>>>>
>>>>> Fixed
>>>>>
>>>>>>
>>>>>> - Let's make the stack pane tab part of the tabset at the bottom of
>>>>>> the debugger, and ensure docking etc. works so tabs can be split off
>>>>>> and arranged around the main source window.
>>>>>>
>>>>>
>>>>> Fixed. Now stack pane will be displayed along with another panel at
>>>>> bottom and also docking has been introduced for all the panels so tabs will
>>>>> be arranged around main debugger panel.
>>>>>
>>>>>
>>>>>>
>>>>>> - Stepping is quite slow. What's causing that? I wonder if we really
>>>>>> need to have all the one line SQL templates - they're probably quite
>>>>>> expensive to process.
>>>>>>
>>>>> Fixed. This is due to polling timeout was high (1 second) and we are
>>>>> getting delay in getting the results. Now polling timeout has reduced to to
>>>>> 200ms.
>>>>>
>>>>>>
>>>>>> - Is backend_running.sql required? I've removed both versions as I
>>>>>> can't find any references to them. Are any other templates not
>>>>>> required?
>>>>>>
>>>>> Ok. No other templates.
>>>>>
>>>>>>
>>>>>> Will log any other issues that come up in further work.
>>>>>>
>>>>>> > Below functionalities are implemented but testing are pending.
>>>>>> >
>>>>>> > Trigger functions need to test with the debugger.
>>>>>> > Functions are tested with data types (like text, integer etc.) but
>>>>>> it needs
>>>>>> > to be tested with all the data types for direct debugging.
>>>>>> > Functions/Procedures need to test with PPAS 9.2 and earlier version
>>>>>> where
>>>>>> > debugger version is different.
>>>>>>
>>>>>> Thanks!
>>>>>>
>>>>>> --
>>>>>> Dave Page
>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>> Twitter: @pgsnake
>>>>>>
>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>> The Enterprise PostgreSQL Company
>>>>>>
>>>>>
>>>>>
>>>>
>>>> --
>>>> Dave Page
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>>
>>>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>>
>
>
> --
> Sent via pgadmin-hackers mailing list ([email protected])
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgadmin-hackers
>
>
--
*Akshay Joshi*
*Principal Software Engineer *
*Phone: +91 20-3058-9517Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 15+ messages in thread
end of thread, other threads:[~2016-05-06 12:20 UTC | newest]
Thread overview: 15+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2016-04-05 11:40 [pgAdmin4][Debugger]: Initial Patch Neel Patel <[email protected]>
2016-04-05 16:29 ` Dave Page <[email protected]>
2016-04-06 06:08 ` Neel Patel <[email protected]>
2016-04-06 06:20 ` Neel Patel <[email protected]>
2016-04-06 06:37 ` Neel Patel <[email protected]>
2016-04-06 07:57 ` Neel Patel <[email protected]>
2016-04-06 08:07 ` Dave Page <[email protected]>
2016-04-14 12:52 ` Neel Patel <[email protected]>
2016-04-14 20:39 ` Dave Page <[email protected]>
2016-04-18 08:55 ` Neel Patel <[email protected]>
2016-04-18 12:37 ` Dave Page <[email protected]>
2016-04-19 06:29 ` Neel Patel <[email protected]>
2016-04-20 13:02 ` Dave Page <[email protected]>
2016-04-25 13:48 ` Neel Patel <[email protected]>
2016-05-06 12:20 ` Akshay Joshi <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox