public inbox for [email protected]
help / color / mirror / Atom feedFrom: Neel Patel <[email protected]>
To: pgadmin-hackers <[email protected]>
Subject: [pgAdmin4][Debugger]: Initial Patch
Date: Tue, 5 Apr 2016 17:10:50 +0530
Message-ID: <CACCA4P1PzVEELk_=8NbpKtgOwVSypG--jRssRLtb8i_SWCcHtg@mail.gmail.com> (raw)
List-Unsubscribe: <mailto:[email protected]?body=unsub%20pgadmin-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
view thread (15+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [pgAdmin4][Debugger]: Initial Patch
In-Reply-To: <CACCA4P1PzVEELk_=8NbpKtgOwVSypG--jRssRLtb8i_SWCcHtg@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox