Initial version for generationg the CREATE/SELECT/EXEC scripts for the

selected node.
pull/3/head
Murtuza Zabuawala 2016-05-16 00:25:31 +05:30 committed by Ashesh Vashi
parent c34e62207a
commit 5ca63fba48
8 changed files with 261 additions and 19 deletions

View File

@ -173,6 +173,12 @@ class FunctionView(PGChildNodeView, DataTypeReader):
* types(gid, sid, did, scid, fnid=None):
- Returns Data Types.
* select_sql(gid, sid, did, scid, fnid):
- Returns sql for Script
* exec_sql(gid, sid, did, scid, fnid):
- Returns sql for Script
"""
node_type = blueprint.node_type
@ -203,7 +209,9 @@ class FunctionView(PGChildNodeView, DataTypeReader):
'module.js': [{}, {}, {'get': 'module_js'}],
'get_types': [{'get': 'types'}, {'get': 'types'}],
'get_languages': [{'get': 'get_languages'}, {'get': 'get_languages'}],
'vopts': [{}, {'get': 'variable_options'}]
'vopts': [{}, {'get': 'variable_options'}],
'select_sql': [{'get': 'select_sql'}],
'exec_sql': [{'get': 'exec_sql'}]
})
@property
@ -1198,6 +1206,74 @@ It may have been removed by another user or moved to another schema.
status=200
)
@check_precondition
def select_sql(self, gid, sid, did, scid, fnid):
"""
This function returns sql for select script call.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Function Id
"""
# Fetch the function definition.
SQL = render_template("/".join([self.sql_template_path,
'get_definition.sql']), fnid=fnid, scid=scid)
status, res = self.conn.execute_2darray(SQL)
if not status:
return internal_server_error(errormsg=res)
func_def, name = res['rows'][0]
# Fetch only arguments
args = name[name.rfind('('):].strip('(').strip(')').split(',')
# Remove unwanted spaces from arguments
args = [arg.strip(' ') for arg in args]
# Remove duplicate and then format arguments
for arg in list(set(args)):
formatted_arg = '\n\t<' + arg + '>'
name = name.replace(arg, formatted_arg)
name = name.replace(')', '\n)')
sql = "SELECT {0}".format(name)
return ajax_response(response=sql)
@check_precondition
def exec_sql(self, gid, sid, did, scid, fnid):
"""
This function returns sql for exec script call.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Function Id
"""
resp_data = self._fetch_properties(gid, sid, did, scid, fnid)
name = resp_data['pronamespace'] + "." + resp_data['name_with_args']
# Fetch only arguments
args = name[name.rfind('('):].strip('(').strip(')').split(',')
# Remove unwanted spaces from arguments
args = [arg.strip(' ') for arg in args]
# Remove duplicate and then format arguments
for arg in list(set(args)):
formatted_arg = '\n\t<' + arg + '>'
name = name.replace(arg, formatted_arg)
name = name.replace(')', '\n)')
sql = "EXEC {0}".format(name)
return ajax_response(response=sql)
FunctionView.register_node_view(blueprint)

View File

@ -108,6 +108,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
collection_type: 'coll-function',
hasSQL: true,
hasDepends: true,
hasScriptTypes: ['create', 'select'],
parent_type: ['schema', 'catalog'],
Init: function(args) {
/* Avoid mulitple registration of menus */

View File

@ -28,6 +28,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Function) {
collection_type: 'coll-procedure',
hasSQL: true,
hasDepends: true,
hasScriptTypes: ['create', 'exec'],
parent_type: ['schema'],
Init: function() {
/* Avoid mulitple registration of menus */

View File

@ -105,6 +105,60 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
}]);
}
}
// This will add options of scripts eg:'CREATE Script'
if (self.hasScriptTypes && _.isArray(self.hasScriptTypes)
&& self.hasScriptTypes.length > 0) {
// For each script type create menu
_.each(self.hasScriptTypes, function(stype) {
var type_label = S(
"{{ _("%%s Script") }}"
).sprintf(stype.toUpperCase()).value(),
stype = stype.toLowerCase();
// Adding menu for each script type
pgAdmin.Browser.add_menus([{
name: 'show_script_' + stype, node: self.type, module: self,
applies: ['object', 'context'], callback: 'show_script',
priority: 3, label: type_label, category: 'Scripts',
data: {'script': stype}, icon: 'fa fa-pencil',
enable: this.check_user_permission
}]);
});
// If node has hasSQL then provide CREATE Script by default
} else if(self.hasSQL) {
pgAdmin.Browser.add_menus([{
name: 'show_script_create', node: self.type, module: self,
applies: ['object', 'context'], callback: 'show_script',
priority: 3, label: 'CREATE Script', category: 'Scripts',
data: {'script': 'create'}, icon: 'fa fa-pencil',
enable: this.check_user_permission
}]);
}
},
///////
// Checks if Script Type is allowed to user
// First check if role node & create role allowed
// Otherwise test rest of database objects
// if no permission matched then do not allow create script
///////
check_user_permission: function(itemData, item, data) {
var node = pgBrowser.Nodes[itemData._type],
parentData = node.getTreeNodeHierarchy(item);
if ( _.indexOf(['create','insert','update', 'delete'], data.script) != -1) {
if (itemData.type == 'role' &&
parentData.server.user.can_create_role) {
return true;
} else if (parentData.server.user.is_superuser ||
parentData.server.user.can_create_db ||
(parentData.schema && parentData.schema.can_create)) {
return true;
} else {
return false;
}
} else {
return true;
}
},
///////
// Generate a Backform view using the node's model type
@ -499,6 +553,43 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
},
null).show()
},
// Callback for creating script(s) & opening them in Query editor
show_script: function(args, item) {
var scriptType = args.script,
obj = this,
t = pgBrowser.tree,
i = item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return;
/*
* Make sure - we're using the correct version of node
*/
obj = pgBrowser.Nodes[d._type];
var objName = d.label;
// URL for script type
if(scriptType == 'insert') {
sql_url = 'insert_sql';
} else if(scriptType == 'update') {
sql_url = 'update_sql';
} else if(scriptType == 'delete') {
sql_url = 'delete_sql';
} else if(scriptType == 'select') {
sql_url = 'select_sql';
} else if(scriptType == 'exec') {
sql_url = 'exec_sql';
} else {
// By Default get CREATE SQL
sql_url = 'sql';
}
// Open data grid & pass the URL for fetching
pgAdmin.DataGrid.show_query_tool.apply(
this, [obj.generate_url(i, sql_url, d, true), i]
);
},
// Callback called - when a node is selected in browser tree.
selected: function(item, data, browser) {
// Show the information about the selected node in the below panels,

View File

@ -18,8 +18,9 @@ from flask.ext.babel import gettext
from flask.ext.security import login_required
from pgadmin.tools.sqleditor.command import *
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import make_json_response, bad_request, internal_server_error
from pgadmin.utils.ajax import make_json_response, bad_request,\
internal_server_error
from config import PG_DEFAULT_DRIVER
class DataGridModule(PgAdminModule):
"""
@ -143,17 +144,26 @@ def panel(trans_id, is_query_tool, editor_title):
is_query_tool: True if panel calls when query tool menu is clicked.
editor_title: Title of the editor
"""
# Let's fetch Script type URL from request
if request.args and request.args['query_url'] != '':
sURL = request.args['query_url']
else:
sURL = None
return render_template("datagrid/index.html", _=gettext, uniqueId=trans_id,
is_query_tool=is_query_tool, editor_title=editor_title)
is_query_tool=is_query_tool, editor_title=editor_title,
script_type_url=sURL)
@blueprint.route(
'/initialize/query_tool/<int:sid>/<int:did>',
methods=["PUT", "POST"]
methods=["POST"]
)
@blueprint.route(
'/initialize/query_tool/<int:sid>',
methods=["POST"]
)
@login_required
def initialize_query_tool(sid, did):
def initialize_query_tool(sid, did=None):
"""
This method is responsible for instantiating and initializing
the query tool object. It will also create a unique
@ -164,6 +174,21 @@ def initialize_query_tool(sid, did):
did: Database Id
"""
if did is None:
# Use Maintenance database OID
from pgadmin.utils.driver import get_driver
driver = get_driver(PG_DEFAULT_DRIVER)
manager = driver.connection_manager(sid)
conn = manager.connection()
if conn.connected():
did = manager.did
else:
internal_server_error(
errormsg=gettext(
'Server disconnected, Please connect and try again'
)
)
try:
command_obj = ObjectRegistry.get_object('query_tool', sid=sid, did=did)
except Exception as e:

View File

@ -156,8 +156,36 @@ try {
}
});
// Fetch the SQL for Scripts (eg: CREATE/UPDATE/DELETE/SELECT)
var script_sql = '';
{% if script_type_url%}
// Call AJAX only if script type url is present
$.ajax({
url: '{{ script_type_url }}',
type:'GET',
async: false,
success: function(res) {
script_sql = res;
},
error: function(jqx) {
var msg = jqx.responseText;
/* Error from the server */
if (jqx.status == 410 || jqx.status == 500) {
try {
var data = $.parseJSON(jqx.responseText);
msg = data.errormsg;
} catch (e) {}
}
pgBrowser.report_error(
S('{{ _('Error fetching sql for script: "%%s"') }}')
.sprintf(msg)
.value(), msg);
}
});
{% endif %}
// Start the query tool.
sqlEditorController.start({{ is_query_tool }}, "{{ editor_title }}");
sqlEditorController.start({{ is_query_tool }}, "{{ editor_title }}", script_sql);
});
} catch (err) {
/* Show proper error dialog */

View File

@ -346,9 +346,10 @@ define(
},
// This is a callback function to show query tool when user click on menu item.
show_query_tool: function(data, i) {
show_query_tool: function(url, i) {
var self = this,
d = pgAdmin.Browser.tree.itemData(i);
sURL = url || '',
d = pgAdmin.Browser.tree.itemData(i);
if (d === undefined) {
alertify.alert(
'Query tool Error',
@ -362,17 +363,32 @@ define(
parentData = node.getTreeNodeHierarchy(i);
// If server, database is undefined then return from the function.
if (parentData.server === undefined || parentData.database === undefined) {
if (parentData.server === undefined) {
return;
}
var baseUrl = "{{ url_for('datagrid.index') }}" + "initialize/query_tool/" + parentData.server._id +
"/" + parentData.database._id;
var grid_title = parentData.database.label + ' on ' + parentData.server.user.name + '@' +
var baseUrl = "{{ url_for('datagrid.index') }}" + "initialize/query_tool/" + parentData.server._id;
// If database not present then use Maintenance database
// We will handle this at server side
if (parentData.database) {
baseUrl += "/" + parentData.database._id;
}
// If Database is not available then use default db
var db_label = parentData.database ? parentData.database.label
: parentData.server.db;
var grid_title = db_label + ' on ' + parentData.server.user.name + '@' +
parentData.server.label ;
var panel_title = ' Query-' + self.title_index;
self.title_index += 1;
var panel_title = ' Query-';
if (!_.isUndefined(self.title_index) && !isNaN(self.title_index)) {
panel_title += self.title_index;
self.title_index += 1;
} else {
panel_title += "{{ _(' untitled') }}";
}
$.ajax({
url: baseUrl,
@ -399,7 +415,8 @@ define(
});
// Open the panel if frame is initialized
baseUrl = "{{ url_for('datagrid.index') }}" + "panel/" + res.data.gridTransId + "/true/" + grid_title;
baseUrl = "{{ url_for('datagrid.index') }}" + "panel/" + res.data.gridTransId + "/true/"
+ grid_title + '?' + "query_url=" + encodeURI(sURL);
var openQueryToolURL = function(j) {
setTimeout(function() {
var frameInitialized = j.data('frameInitialized');
@ -417,7 +434,7 @@ define(
},
error: function(e) {
alertify.alert(
'Query Tool Initialize Error',
"{{ _('Query Tool Initialize Error') }}",
e.responseJSON.errormsg
);
}

View File

@ -678,7 +678,7 @@ define(
* call the render method of the grid view to render the backgrid
* header and loading icon and start execution of the sql query.
*/
start: function(is_query_tool, editor_title) {
start: function(is_query_tool, editor_title, script_sql) {
var self = this;
self.is_query_tool = is_query_tool;
@ -731,6 +731,9 @@ define(
if (self.is_query_tool) {
self.gridView.query_tool_obj.refresh();
if(script_sql && script_sql !== '') {
self.gridView.query_tool_obj.setValue(script_sql);
}
}
else {
self.gridView.query_tool_obj.setOption("readOnly",true);