Clear the node cache, when an node is created/updated to make sure - we

will always have latest data related to that type of node. Also, fixed
the cache_level for different node types.

This commit also contains fixes for the following issue:
* In extension module - use the 'node-list-by-name' instead of using a
  custom 'node-ajax-options' control, and removed redundant template
  schemas from it.
* When we tries to destroy the select2 object from
  Select2Cell/Select2Control while releasing the properties view,
  sometimes select2 can not find the instance related it for some
  unknown reason. Hence - before removing it we will check for manual
  instance existance using $.data('select2').
* When we traverse through the browser tree nodes very quickly, it tries
  to remove the object before it gets created completely, and results
  into an exception.
* Icon in the select2 drop down list was not visible due to some CSS
  issues.

Apart of that, we will generate two new browser events -
'pgadmin-node:created:<NODE-TYPE>', 'pgadmin-node:updated:<NODE-TYPE>'
whenever a new node is created, or an existing node will be updated.
This commit is contained in:
Ashesh Vashi 2016-04-29 15:41:24 +05:30
parent dac514a4ae
commit 32e0a0d4b6
14 changed files with 148 additions and 66 deletions

View File

@ -173,27 +173,20 @@ function($, _, S, pgAdmin, pgBrowser) {
}, },
{ {
id: 'owner', label:'{{ _('Owner') }}', control: 'node-list-by-name', id: 'owner', label:'{{ _('Owner') }}', control: 'node-list-by-name',
mode: ['properties'], node: 'role', cell: 'string' mode: ['properties'], node: 'role', cell: 'string',
cache_level: 'server'
}, },
{ {
id: 'schema', label: '{{ _('Schema')}}', type: 'text', control: 'node-ajax-options', id: 'schema', label: '{{ _('Schema')}}', type: 'text',
mode: ['properties', 'create', 'edit'], group: '{{ _('Definition')}}', deps: ['relocatable'], control: 'node-list-by-name', group: '{{ _('Definition')}}',
url: 'schemas', first_empty: true, disabled: function(m) { mode: ['properties', 'create', 'edit'], deps: ['relocatable'],
node: 'schema', first_empty: true,
disabled: function(m) {
/* /*
* enable or disable schema field if model's relocatable * enable or disable schema field if model's relocatable
* attribute is True or False * attribute is True or False
*/ */
return (m.has('relocatable') ? !m.get('relocatable') : false); return (m.has('relocatable') ? !m.get('relocatable') : false);
},
transform: function(data) {
var res = [];
if (data && _.isArray(data)) {
_.each(data, function(d) {
res.push({label: d.schema, value: d.schema});
})
}
return res;
} }
}, },
{ {

View File

@ -1,3 +0,0 @@
{#===================fetch all schemas==========================#}
SELECT nspname As schema FROM pg_namespace
ORDER BY nspname

View File

@ -87,7 +87,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
},{ },{
id: 'owner', label:'{{ _('Owner') }}', cell: 'string', id: 'owner', label:'{{ _('Owner') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'], type: 'text', mode: ['properties', 'create', 'edit'],
disabled: 'inSchema', control: 'node-list-by-name', node: 'role' disabled: 'inSchema', control: 'node-list-by-name',
node: 'role'
},{ },{
id: 'schema', label:'{{ _('Schema') }}', cell: 'string', id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
type: 'text', mode: ['create', 'edit'], node: 'schema', type: 'text', mode: ['create', 'edit'], node: 'schema',

View File

@ -205,10 +205,12 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
id: 'description', label:'{{ _('Comment') }}', cell: 'string', id: 'description', label:'{{ _('Comment') }}', cell: 'string',
type: 'multiline' type: 'multiline'
},{ },{
id: 'basetype', label:'{{ _('Base type') }}', cell: 'string', control: 'node-ajax-options', id: 'basetype', label:'{{ _('Base type') }}', cell: 'string',
type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', control: 'node-ajax-options', type: 'text', url: 'get_types',
disabled: function(m) { return !m.isNew(); }, first_empty: true, mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}',
transform: function(d){ cache_level: 'database', cache_node: 'schema', disabled: function(m) {
return !m.isNew();
}, first_empty: true, transform: function(d) {
this.model.type_options = d; this.model.type_options = d;
return d; return d;
} }
@ -279,8 +281,10 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
'size': 'small' 'size': 'small'
} }
},{ },{
id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', id: 'collname', label:'{{ _('Collation') }}', cell: 'string',
type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { control: 'node-ajax-options', type: 'text', url: 'get_collations',
group: '{{ _('Definition') }}', cache_level: 'database',
cache_node: 'schema', disabled: function(m) {
return !m.isNew(); return !m.isNew();
} }
},{ },{

View File

@ -126,14 +126,15 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
},{ },{
id: 'schema', label: '{{ _('Schema')}}', cell: 'string', id: 'schema', label: '{{ _('Schema')}}', cell: 'string',
type: 'text', mode: ['create','edit'], node: 'schema', type: 'text', mode: ['create','edit'], node: 'schema',
control: 'node-list-by-id' cache_node: 'database', control: 'node-list-by-id'
},{ },{
id: 'description', label:'{{ _('Comment') }}', cell: 'string', id: 'description', label:'{{ _('Comment') }}', cell: 'string',
type: 'multiline', cellHeaderClasses: 'width_percent_50' type: 'multiline', cellHeaderClasses: 'width_percent_50'
},{ },{
id: 'template', label: '{{ _('Template')}}',type: 'text', id: 'template', label: '{{ _('Template')}}',type: 'text',
disabled: function(m) { return !m.isNew(); }, url: 'fetch_templates', disabled: function(m) { return !m.isNew(); }, url: 'fetch_templates',
group: '{{ _('Definition') }}',control: 'node-ajax-options' group: '{{ _('Definition') }}', control: 'node-ajax-options',
cache_node: 'database'
},{ },{
id: 'options', label: '{{ _('Option') }}', type: 'collection', id: 'options', label: '{{ _('Option') }}', type: 'collection',
group: '{{ _('Options') }}', control: 'unique-col-collection', group: '{{ _('Options') }}', control: 'unique-col-collection',

View File

@ -93,27 +93,33 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
id: 'prsstart', label: '{{ _('Start function')}}', id: 'prsstart', label: '{{ _('Start function')}}',
type: 'text', disabled: function(m) { return !m.isNew(); }, type: 'text', disabled: function(m) { return !m.isNew(); },
control: 'node-ajax-options', url: 'start_functions', control: 'node-ajax-options', url: 'start_functions',
group: '{{ _('Definition') }}' group: '{{ _('Definition') }}', cache_level: 'database',
cache_node: 'schema'
},{ },{
id: 'prstoken', label: '{{ _('Get next token function')}}', id: 'prstoken', label: '{{ _('Get next token function')}}',
type: 'text', disabled: function(m) { return !m.isNew(); }, type: 'text', disabled: function(m) { return !m.isNew(); },
control: 'node-ajax-options', url: 'token_functions', control: 'node-ajax-options', url: 'token_functions',
group: '{{ _('Definition') }}' group: '{{ _('Definition') }}', cache_level: 'database',
cache_node: 'schema'
},{ },{
id: 'prsend', label: '{{ _('End function')}}', id: 'prsend', label: '{{ _('End function')}}',
type: 'text', disabled: function(m) { return !m.isNew(); }, type: 'text', disabled: function(m) { return !m.isNew(); },
control: 'node-ajax-options', url: 'end_functions', control: 'node-ajax-options', url: 'end_functions',
group: '{{ _('Definition') }}' group: '{{ _('Definition') }}', cache_level: 'database',
cache_node: 'schema',
cache_node: 'schema'
},{ },{
id: 'prslextype', label: '{{ _('Lextypes function')}}', id: 'prslextype', label: '{{ _('Lextypes function')}}',
type: 'text', disabled: function(m) { return !m.isNew(); }, type: 'text', disabled: function(m) { return !m.isNew(); },
control: 'node-ajax-options', url: 'lextype_functions', control: 'node-ajax-options', url: 'lextype_functions',
group: '{{ _('Definition') }}' group: '{{ _('Definition') }}', cache_level: 'database',
cache_node: 'schema'
},{ },{
id: 'prsheadline', label: '{{ _('Headline function')}}', id: 'prsheadline', label: '{{ _('Headline function')}}',
type: 'text', disabled: function(m) { return !m.isNew(); }, type: 'text', disabled: function(m) { return !m.isNew(); },
control: 'node-ajax-options', url: 'headline_functions', control: 'node-ajax-options', url: 'headline_functions',
group: '{{ _('Definition') }}' group: '{{ _('Definition') }}', cache_level: 'database',
cache_node: 'schema'
}], }],
/* /*

View File

@ -83,13 +83,16 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
id: 'description', label:'{{ _('Comment') }}', cell: 'string', id: 'description', label:'{{ _('Comment') }}', cell: 'string',
type: 'multiline', cellHeaderClasses: 'width_percent_50' type: 'multiline', cellHeaderClasses: 'width_percent_50'
},{ },{
id: 'tmplinit', label: '{{ _('Init function')}}', group: '{{ _('Definition') }}', id: 'tmplinit', label: '{{ _('Init function')}}',
type: 'text', disabled: function(m) { return !m.isNew(); }, group: '{{ _('Definition') }}', type: 'text', disabled: function(m) {
control: 'node-ajax-options', url: 'get_init' return !m.isNew();
}, control: 'node-ajax-options', url: 'get_init',
cache_level: 'database', cache_node: 'schema'
},{ },{
id: 'tmpllexize', label: '{{ _('Lexize function')}}', group: '{{ _('Definition') }}', id: 'tmpllexize', label: '{{ _('Lexize function')}}', group: '{{ _('Definition') }}',
type: 'text', disabled: function(m) { return !m.isNew(); }, type: 'text', disabled: function(m) { return !m.isNew(); },
control: 'node-ajax-options', url: 'get_lexize' control: 'node-ajax-options', url: 'get_lexize', cache_level: 'database',
cache_node: 'schema'
}], }],
/* /*

View File

@ -257,12 +257,12 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) {
id: 'encoding', label: '{{ _('Encoding') }}', id: 'encoding', label: '{{ _('Encoding') }}',
editable: false, type: 'text', group: 'Definition', editable: false, type: 'text', group: 'Definition',
disabled: function(m) { return !m.isNew(); }, url: 'get_encodings', disabled: function(m) { return !m.isNew(); }, url: 'get_encodings',
control: 'node-ajax-options' control: 'node-ajax-options', cache_level: 'server'
},{ },{
id: 'template', label: '{{ _('Template') }}', id: 'template', label: '{{ _('Template') }}',
editable: false, type: 'text', group: 'Definition', editable: false, type: 'text', group: 'Definition',
disabled: function(m) { return !m.isNew(); }, disabled: function(m) { return !m.isNew(); },
control: 'node-list-by-name', node: 'database' control: 'node-list-by-name', node: 'database', cache_level: 'server'
},{ },{
id: 'spcname', label: '{{ _('Tablespace') }}', id: 'spcname', label: '{{ _('Tablespace') }}',
editable: false, type: 'text', group: 'Definition', editable: false, type: 'text', group: 'Definition',
@ -275,12 +275,12 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) {
id: 'datcollate', label: '{{ _('Collation') }}', id: 'datcollate', label: '{{ _('Collation') }}',
editable: false, type: 'text', group: 'Definition', editable: false, type: 'text', group: 'Definition',
disabled: function(m) { return !m.isNew(); }, url: 'get_ctypes', disabled: function(m) { return !m.isNew(); }, url: 'get_ctypes',
control: 'node-ajax-options' control: 'node-ajax-options', cache_level: 'server'
},{ },{
id: 'datctype', label: '{{ _('Character Type') }}', id: 'datctype', label: '{{ _('Character Type') }}',
editable: false, type: 'text', group: 'Definition', editable: false, type: 'text', group: 'Definition',
disabled: function(m) { return !m.isNew(); }, url: 'get_ctypes', disabled: function(m) { return !m.isNew(); }, url: 'get_ctypes',
control: 'node-ajax-options' control: 'node-ajax-options', cache_level: 'server'
},{ },{
id: 'datconnlimit', label: '{{ _('Connection Limit') }}', id: 'datconnlimit', label: '{{ _('Connection Limit') }}',
editable: false, type: 'int', group: 'Definition', min: -1 editable: false, type: 'int', group: 'Definition', min: -1

View File

@ -3,7 +3,7 @@ SELECT
has_database_privilege(db.oid, 'CREATE') as cancreate, datdba as owner has_database_privilege(db.oid, 'CREATE') as cancreate, datdba as owner
FROM FROM
pg_database db pg_database db
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace = ta.oid{% if did %} LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace = ta.oid
WHERE {% if did %} WHERE {% if did %}
db.oid = {{ did|qtLiteral }}::OID{% else %} db.oid = {{ did|qtLiteral }}::OID{% else %}
db.oid > {{ last_system_oid }}::OID db.oid > {{ last_system_oid }}::OID

View File

@ -79,22 +79,28 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) {
if (url) { if (url) {
var node = this.field.get('schema_node'), var node = this.field.get('schema_node'),
node_info = this.field.get('node_info'), node_info = this.field.get('node_info'),
with_id = this.field.get('url_with_id') || false,
full_url = node.generate_url.apply( full_url = node.generate_url.apply(
node, [ node, [
null, url, this.field.get('node_data'), null, url, this.field.get('node_data'), with_id, node_info
this.field.get('url_with_id') || false, node_info
]), ]),
cache_level = this.field.get('cache_level') || node.type, cache_level,
cache_node = this.field.get('cache_node'); cache_node = this.field.get('cache_node');
cache_node = (cache_node && pgAdmin.Browser.Nodes['cache_node']) || node; cache_node = (cache_node && pgAdmin.Browser.Nodes['cache_node']) || node;
if (this.field.has('cache_level')) {
cache_level = this.field.get('cache_level');
} else {
cache_level = cache_node.cache_level(node_info, with_id);
}
/* /*
* We needs to check, if we have already cached data for this url. * We needs to check, if we have already cached data for this url.
* If yes - use that, and do not bother about fetching it again, * If yes - use that, and do not bother about fetching it again,
* and use it. * and use it.
*/ */
var data = cache_node.cache(url, node_info, cache_level); var data = cache_node.cache(node.type + '#' + url, node_info, cache_level);
if (this.field.get('version_compatible') && if (this.field.get('version_compatible') &&
(_.isUndefined(data) || _.isNull(data))) { (_.isUndefined(data) || _.isNull(data))) {
@ -107,7 +113,7 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) {
* We will cache this data for short period of time for avoiding * We will cache this data for short period of time for avoiding
* same calls. * same calls.
*/ */
data = cache_node.cache(url, node_info, cache_level, res.data); data = cache_node.cache(node.type + '#' + url, node_info, cache_level, res.data);
}, },
error: function() { error: function() {
m.trigger('pgadmin:view:fetch:error', m, self.field); m.trigger('pgadmin:view:fetch:error', m, self.field);
@ -341,22 +347,28 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) {
eventHandler = m.top || m, eventHandler = m.top || m,
node = column.get('schema_node'), node = column.get('schema_node'),
node_info = column.get('node_info'), node_info = column.get('node_info'),
with_id = column.get('url_with_id') || false,
full_url = node.generate_url.apply( full_url = node.generate_url.apply(
node, [ node, [
null, url, column.get('node_data'), null, url, column.get('node_data'), with_id, node_info
column.get('url_with_id') || false, node_info
]), ]),
cache_level = column.get('cache_level') || node.type, cache_level,
cache_node = column.get('cache_node'); cache_node = column.get('cache_node');
cache_node = (cache_node && pgAdmin.Browser.Nodes['cache_node']) || node; cache_node = (cache_node && pgAdmin.Browser.Nodes['cache_node']) || node;
if (this.field.has('cache_level')) {
cache_level = this.field.get('cache_level');
} else {
cache_level = cache_node.cache_level(node_info, with_id);
}
/* /*
* We needs to check, if we have already cached data for this url. * We needs to check, if we have already cached data for this url.
* If yes - use that, and do not bother about fetching it again, * If yes - use that, and do not bother about fetching it again,
* and use it. * and use it.
*/ */
var data = cache_node.cache(url, node_info, cache_level); var data = cache_node.cache(node.type + '#' + url, node_info, cache_level);
if (column.get('version_compatible') && if (column.get('version_compatible') &&
(_.isUndefined(data) || _.isNull(data))) { (_.isUndefined(data) || _.isNull(data))) {
@ -369,7 +381,7 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) {
* We will cache this data for short period of time for avoiding * We will cache this data for short period of time for avoiding
* same calls. * same calls.
*/ */
data = cache_node.cache(url, node_info, cache_level, res.data); data = cache_node.cache(node.type + '#' + url, node_info, cache_level, res.data);
}, },
error: function() { error: function() {
eventHandler.trigger('pgadmin:view:fetch:error', m, column); eventHandler.trigger('pgadmin:view:fetch:error', m, column);

View File

@ -903,9 +903,12 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
// Closing this panel // Closing this panel
this.close(); this.close();
}.bind(panel), }.bind(panel),
updateTreeItem = function() { updateTreeItem = function(that) {
var panel = this; var panel = this;
// Clear the cache for this node now.
setTimeout(function() { that.clear_cache.apply(that, item); }, 0);
// Update the item lable (if label is modified.) // Update the item lable (if label is modified.)
if (view.model.tnode) { if (view.model.tnode) {
var itemData = tree.itemData(item), var itemData = tree.itemData(item),
@ -925,11 +928,18 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
tree.deselect(item); tree.deselect(item);
panel.$container.removeAttr('action-mode'); panel.$container.removeAttr('action-mode');
setTimeout(function() { closePanel(); }, 0); setTimeout(function() { closePanel(); }, 0);
setTimeout(function() { tree.select(item, {focus: true}); }, 10); setTimeout(function() { tree.select(item, {focus: true}); }, 10);
pgBrowser.Events.trigger(
'pgadmin-node:updated:' + that.type, item, that
);
}, },
saveNewNode = function() { saveNewNode = function(that) {
var panel = this; var panel = this;
// Clear the cache for this node now.
setTimeout(function() { that.clear_cache.apply(that, item); }, 0);
/* TODO:: Create new tree node for this */ /* TODO:: Create new tree node for this */
if (view.model.tnode && '_id' in view.model.tnode) { if (view.model.tnode && '_id' in view.model.tnode) {
var d = _.extend({}, view.model.tnode), var d = _.extend({}, view.model.tnode),
@ -938,6 +948,9 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
if (i) { if (i) {
tree.select(i, {focus: true}); tree.select(i, {focus: true});
} }
pgBrowser.Events.trigger(
'pgadmin-node:created:' + that.type, i, that
);
}, found = false; }, found = false;
delete view.model.tnode; delete view.model.tnode;
@ -1092,9 +1105,15 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
tree.open(item, { tree.open(item, {
success: function (item, options){ success: function (item, options){
setTimeout(function() {closePanel();}, 0); setTimeout(function() {closePanel();}, 0);
pgBrowser.Events.trigger(
'pgadmin-node:created:' + that.type, item, that
);
}, },
fail: function (item, options){ fail: function (item, options){
setTimeout(function() {closePanel();}, 0); setTimeout(function() {closePanel();}, 0);
pgBrowser.Events.trigger(
'pgadmin-node:created:' + that.type, item, that
);
}, },
unanimated: animation unanimated: animation
}); });
@ -1125,7 +1144,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
} }
setTimeout(function() {closePanel();}, 0); setTimeout(function() {closePanel();}, 0);
} }
}.bind(panel), }.bind(panel, that),
editInNewPanel = function() { editInNewPanel = function() {
// Open edit in separate panel // Open edit in separate panel
setTimeout(function() { setTimeout(function() {
@ -1136,7 +1155,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
}, 0); }, 0);
}, },
onCancelFunc = closePanel, onCancelFunc = closePanel,
onSaveFunc = updateTreeItem.bind(panel), onSaveFunc = updateTreeItem.bind(panel, that),
onEdit = editFunc.bind(panel); onEdit = editFunc.bind(panel);
if (action) { if (action) {
@ -1280,12 +1299,45 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
} }
if (_.isUndefined(data)) { if (_.isUndefined(data)) {
return cached[hash]; var res = cached[hash];
if (!_.isUndefined(res) &&
(res.at - Date.now() > 300000)) {
res = undefined;
}
return res;
} }
var res = cached[hash] = {data: data, at: Date(), level: level}; res = cached[hash] = {data: data, at: Date.now(), level: level};
return res; return res;
},
clear_cache: function(item) {
/*
* Reset the cache, when new node is created.
*
* FIXME:
* At the moment, we will clear all the cache for this node. But - we
* would like to clear the cache only this nodes parent, so that - it
* fetches the new data.
*/
this.cached = {};
},
cache_level: function(node_info, with_id) {
if (node_info) {
if (with_id && this.type in node_info) {
return this.type;
}
if (_.isArray(this.parent_type)) {
for (var parent in this.parent_type) {
if (parent in node_info) {
return parent;
}
}
return this.type;
}
return this.parent_type;
}
} }
}); });

View File

@ -947,6 +947,11 @@ ul.nav.nav-tabs {
padding-left: 20px; padding-left: 20px;
} }
.select2-results span.wcTabIcon {
padding-left: 20px;
background-position: 0px 2px;
}
.pgadmin-controls.SQL>.CodeMirror { .pgadmin-controls.SQL>.CodeMirror {
height: 500px!important; height: 500px!important;
} }

View File

@ -1993,13 +1993,15 @@
// Refresh SQL Field to refresh the control lazily after it renders // Refresh SQL Field to refresh the control lazily after it renders
setTimeout(function() { setTimeout(function() {
self.refreshTextArea.apply(self); self.refreshTextArea.apply(self);
}, 100); }, 0);
return self; return self;
}, },
refreshTextArea: function() { refreshTextArea: function() {
this.sqlCtrl.refresh(); if (this.sqlCtrl) {
this.sqlCtrl.refresh();
}
}, },
remove: function() { remove: function() {

View File

@ -450,7 +450,11 @@
this.undelegateEvents(); this.undelegateEvents();
if (this.$select) { if (this.$select) {
this.$select.select2('destroy'); if ( this.$select.data('select2')) {
this.$select.select2('destroy');
}
delete this.$select;
this.$select = null;
} }
this.$el.empty(); this.$el.empty();
@ -530,7 +534,9 @@
remove: function() { remove: function() {
this.$select.off('change', this.onSave); this.$select.off('change', this.onSave);
this.$select.select2('destroy'); if (this.$select.data('select2')) {
this.$select.select2('destroy');
}
this.$el.empty(); this.$el.empty();
Backgrid.SelectCell.prototype.remove.apply(this, arguments); Backgrid.SelectCell.prototype.remove.apply(this, arguments);
} }