Set break point option should present in pgAdmin4 for trigger. Fixes #1226

pull/3/head
Neel Patel 2016-08-29 13:43:43 +05:30 committed by Akshay Joshi
parent 2d75eac117
commit f5249f2e18
3 changed files with 76 additions and 8 deletions

View File

@ -111,10 +111,11 @@ def update_session_function_transaction(trans_id, data):
@blueprint.route('/init/<node_type>/<int:sid>/<int:did>/<int:scid>/<int:fid>', methods=['GET']) @blueprint.route('/init/<node_type>/<int:sid>/<int:did>/<int:scid>/<int:fid>', methods=['GET'])
@blueprint.route('/init/<node_type>/<int:sid>/<int:did>/<int:scid>/<int:fid>/<int:trid>', methods=['GET'])
@login_required @login_required
def init_function(node_type, sid, did, scid, fid): def init_function(node_type, sid, did, scid, fid, trid=None):
""" """
init_function(node_type, sid, did, scid, fid) init_function(node_type, sid, did, scid, fid, trid)
This method is responsible to initialize the function required for debugging. 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 method is also responsible for storing the all functions data to session variable.
@ -132,6 +133,8 @@ def init_function(node_type, sid, did, scid, fid):
- Schema Id - Schema Id
fid fid
- Function Id - Function Id
trid
- Trigger Function Id
""" """
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection(did=did) conn = manager.connection(did=did)
@ -149,6 +152,18 @@ def init_function(node_type, sid, did, scid, fid):
# Set the template path required to read the sql files # Set the template path required to read the sql files
template_path = 'debugger/sql' template_path = 'debugger/sql'
if node_type == 'trigger':
# Find trigger function id from trigger id
sql = render_template("/".join([template_path, 'get_trigger_function_info.sql']), table_id=fid, trigger_id=trid)
status, tr_set = conn.execute_dict(sql)
if not status:
current_app.logger.debug("Error retrieving trigger function information from database")
return internal_server_error(errormsg=tr_set)
fid = tr_set['rows'][0]['tgfoid']
sql = ''
sql = render_template("/".join([template_path, 'get_function_debug_info.sql']), is_ppas_database=ppas_server, sql = render_template("/".join([template_path, 'get_function_debug_info.sql']), is_ppas_database=ppas_server,
hasFeatureFunctionDefaults=True, fid=fid) hasFeatureFunctionDefaults=True, fid=fid)
status, r_set = conn.execute_dict(sql) status, r_set = conn.execute_dict(sql)
@ -166,9 +181,6 @@ def init_function(node_type, sid, did, scid, fid):
if ":" in r_set['rows'][0]['name']: if ":" in r_set['rows'][0]['name']:
ret_status = False ret_status = False
msg = gettext("Functions with a colon in the name cannot be debugged.") msg = gettext("Functions with a colon in the name cannot be debugged.")
elif node_type != 'trigger' and r_set['rows'][0]['rettype'] == 'trigger':
ret_status = False
msg = gettext("Functions with return type of 'trigger' cannot be debugged.")
elif ppas_server and r_set['rows'][0]['prosrc'].lstrip().startswith('$__EDBwrapped__$'): elif ppas_server and r_set['rows'][0]['prosrc'].lstrip().startswith('$__EDBwrapped__$'):
ret_status = False ret_status = False
msg = gettext("EDB Advanced Server wrapped functions cannot be debugged.") msg = gettext("EDB Advanced Server wrapped functions cannot be debugged.")
@ -304,10 +316,12 @@ def direct_new(trans_id):
@blueprint.route('/initialize_target/<debug_type>/<int:sid>/<int:did>/<int:scid>/<int:func_id>', @blueprint.route('/initialize_target/<debug_type>/<int:sid>/<int:did>/<int:scid>/<int:func_id>',
methods=['GET', 'POST']) methods=['GET', 'POST'])
@blueprint.route('/initialize_target/<debug_type>/<int:sid>/<int:did>/<int:scid>/<int:func_id>/<int:tri_id>',
methods=['GET', 'POST'])
@login_required @login_required
def initialize_target(debug_type, sid, did, scid, func_id): def initialize_target(debug_type, sid, did, scid, func_id, tri_id=None):
""" """
initialize_target(debug_type, sid, did, scid, func_id) initialize_target(debug_type, sid, did, scid, func_id, tri_id)
This method is responsible for creating an asynchronous connection. This method is responsible for creating an asynchronous connection.
It will also create a unique transaction id and store the information It will also create a unique transaction id and store the information
@ -324,6 +338,8 @@ def initialize_target(debug_type, sid, did, scid, func_id):
- Schema Id - Schema Id
func_id func_id
- Function Id - Function Id
tri_id
- Trigger Function Id
""" """
# Create asynchronous connection using random connection id. # Create asynchronous connection using random connection id.
@ -339,6 +355,20 @@ def initialize_target(debug_type, sid, did, scid, func_id):
if not status: if not status:
return internal_server_error(errormsg=str(msg)) return internal_server_error(errormsg=str(msg))
# Set the template path required to read the sql files
template_path = 'debugger/sql'
if tri_id is not None:
# Find trigger function id from trigger id
sql = render_template("/".join([template_path, 'get_trigger_function_info.sql']), table_id=func_id, trigger_id=tri_id)
status, tr_set = conn.execute_dict(sql)
if not status:
current_app.logger.debug("Error retrieving trigger function information from database")
return internal_server_error(errormsg=tr_set)
func_id = tr_set['rows'][0]['tgfoid']
# Create a unique id for the transaction # Create a unique id for the transaction
trans_id = str(random.randint(1, 9999999)) trans_id = str(random.randint(1, 9999999))

View File

@ -46,6 +46,18 @@ define(
category: 'Debugging', priority: 10, label: '{{ _('Set breakpoint') }}', category: 'Debugging', priority: 10, label: '{{ _('Set breakpoint') }}',
data: {object: 'procedure'}, icon: 'fa fa-arrow-circle-right', data: {object: 'procedure'}, icon: 'fa fa-arrow-circle-right',
enable: 'can_debug' enable: 'can_debug'
}, {
name: 'trigger_function_indirect_debugger', node: 'trigger_function', module: this,
applies: ['object', 'context'], callback: 'check_func_debuggable',
priority: 10, label: '{{ _('Set breakpoint') }}', category: 'Debugging',
icon: 'fa fa-arrow-circle-right', data: {object:'trigger_function'},
enable: 'can_debug'
}, {
name: 'trigger_indirect_debugger', node: 'trigger', module: this,
applies: ['object', 'context'], callback: 'check_func_debuggable',
priority: 10, label: '{{ _('Set breakpoint') }}', category: 'Debugging',
icon: 'fa fa-arrow-circle-right', data: {object:'trigger'},
enable: 'can_debug'
}]); }]);
// Create and load the new frame required for debugger panel // Create and load the new frame required for debugger panel
@ -89,6 +101,11 @@ define(
if (!(treeInfo.server.user.is_superuser || treeInfo.function.funcowner == treeInfo.server.user.name)) if (!(treeInfo.server.user.is_superuser || treeInfo.function.funcowner == treeInfo.server.user.name))
return false; return false;
// For trigger node, language will be undefined - we should allow indirect debugging for trigger node
if (d_.language == undefined && d_._type == 'trigger') {
return true;
}
if (d_.language != 'plpgsql' && d_.language != 'edbspl') { if (d_.language != 'plpgsql' && d_.language != 'edbspl') {
return false; return false;
} }
@ -178,10 +195,19 @@ define(
var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id + var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id +
"/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id; "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.function._id;
} }
else { else if (d._type == "procedure") {
var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id + var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id +
"/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.procedure._id; "/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.procedure._id;
} }
else if (d._type == "trigger_function") {
var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id +
"/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.trigger_function._id;
}
else if (d._type == "trigger") {
var baseUrl = "{{ url_for('debugger.index') }}" + "initialize_target/" + "indirect/" + treeInfo.server._id +
"/" + treeInfo.database._id + "/" + treeInfo.schema._id + "/" + treeInfo.table._id +
"/" + treeInfo.trigger._id;
}
$.ajax({ $.ajax({
url: baseUrl, url: baseUrl,

View File

@ -0,0 +1,12 @@
{### To fetch trigger function information ###}
SELECT t.oid, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, nspname, des.description, l.lanname, p.prosrc,
COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'), substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause
FROM pg_trigger t
JOIN pg_class cl ON cl.oid=tgrelid
JOIN pg_namespace na ON na.oid=relnamespace
LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_trigger'::regclass)
LEFT OUTER JOIN pg_proc p ON p.oid=t.tgfoid
LEFT OUTER JOIN pg_language l ON l.oid=p.prolang
WHERE NOT tgisinternal
AND tgrelid = {{table_id}}::oid AND t.oid = {{trigger_id}}::oid
ORDER BY tgname