mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Added support of Declarative Partitioning (Range, List) for PostgreSQL 10.
This allows the user to create partitioned table and it's partitions. - Edit partitions, Create/Listing of constraints, rules, triggers under partitions. (Implemented by Harshal) - Updating browser tree node when create/attach/detach partitions from table dialog.(Implemented by Ashesh)
This commit is contained in:
parent
ca7fe9a520
commit
c2fb0394bf
@ -145,6 +145,16 @@ define('pgadmin.node.schema', [
|
|||||||
group: gettext('Table'), mode: ['edit', 'create'],
|
group: gettext('Table'), mode: ['edit', 'create'],
|
||||||
type: 'switch',
|
type: 'switch',
|
||||||
disabled: function(m) {
|
disabled: function(m) {
|
||||||
|
// If table is partitioned table then disabled it.
|
||||||
|
if (m.top && m.top.get('is_partitioned')) {
|
||||||
|
// We also need to unset rest of all
|
||||||
|
setTimeout(function() {
|
||||||
|
m.set('autovacuum_custom', false);
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if(!m.top.inSchema.apply(this, [m])) {
|
if(!m.top.inSchema.apply(this, [m])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -459,6 +469,32 @@ define('pgadmin.node.schema', [
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pgBrowser.tableChildTreeNodeHierarchy = function(i) {
|
||||||
|
var idx = 0,
|
||||||
|
res = {},
|
||||||
|
t = pgBrowser.tree;
|
||||||
|
|
||||||
|
do {
|
||||||
|
d = t.itemData(i);
|
||||||
|
if (
|
||||||
|
d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId
|
||||||
|
) {
|
||||||
|
if (d._type === 'partition' || d._type === 'table') {
|
||||||
|
if (!('table' in res)) {
|
||||||
|
res['table'] = _.extend({}, d, {'priority': idx});
|
||||||
|
idx -= 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res[d._type] = _.extend({}, d, {'priority': idx});
|
||||||
|
idx -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = t.hasParent(i) ? t.parent(i) : null;
|
||||||
|
} while (i);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch Cell with Deps (specifically for table children)
|
// Switch Cell with Deps (specifically for table children)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -86,6 +86,7 @@ define('pgadmin.node.column', [
|
|||||||
|
|
||||||
if (!pgBrowser.Nodes['column']) {
|
if (!pgBrowser.Nodes['column']) {
|
||||||
pgBrowser.Nodes['column'] = pgBrowser.Node.extend({
|
pgBrowser.Nodes['column'] = pgBrowser.Node.extend({
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
parent_type: ['table', 'view', 'mview'],
|
parent_type: ['table', 'view', 'mview'],
|
||||||
collection_type: ['coll-table', 'coll-view', 'coll-mview'],
|
collection_type: ['coll-table', 'coll-view', 'coll-mview'],
|
||||||
type: 'column',
|
type: 'column',
|
||||||
@ -197,11 +198,17 @@ define('pgadmin.node.column', [
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
disabled: function(m){
|
disabled: function(m){
|
||||||
// If primary key already exist then disable.
|
// Disable it, when one of this:
|
||||||
|
// - Primary key already exist
|
||||||
|
// - Table is a partitioned table
|
||||||
if (
|
if (
|
||||||
m.top && !_.isUndefined(m.top.get('oid')) &&
|
m.top && ((
|
||||||
m.top.get('primary_key').length > 0 &&
|
!_.isUndefined(m.top.get('oid')) &&
|
||||||
!_.isUndefined(m.top.get('primary_key').first().get('oid'))
|
m.top.get('primary_key').length > 0 &&
|
||||||
|
!_.isUndefined(m.top.get('primary_key').first().get('oid'))
|
||||||
|
) || (
|
||||||
|
m.top.has('is_partitioned') && m.top.get('is_partitioned')
|
||||||
|
))
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -228,6 +235,17 @@ define('pgadmin.node.column', [
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If table is partitioned table then disable
|
||||||
|
if (m.top && !_.isUndefined(m.top.get('is_partitioned')) &&
|
||||||
|
m.top.get('is_partitioned'))
|
||||||
|
{
|
||||||
|
setTimeout(function () {
|
||||||
|
m.set('is_primary_key', false);
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!m.inSchemaWithColumnCheck.apply(this, [m]) &&
|
if(!m.inSchemaWithColumnCheck.apply(this, [m]) &&
|
||||||
!_.isUndefined(name) && !_.isNull(name) && name !== '') {
|
!_.isUndefined(name) && !_.isNull(name) && name !== '') {
|
||||||
return true;
|
return true;
|
||||||
|
@ -24,7 +24,7 @@ from . import utils as columns_utils
|
|||||||
class ColumnGetTestCase(BaseTestGenerator):
|
class ColumnGetTestCase(BaseTestGenerator):
|
||||||
"""This class will get column under table node."""
|
"""This class will get column under table node."""
|
||||||
scenarios = [
|
scenarios = [
|
||||||
('Fetch table Node URL', dict(url='/browser/column/obj/'))
|
('Fetch columns under table node', dict(url='/browser/column/obj/'))
|
||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -10,6 +10,7 @@ define('pgadmin.node.check_constraints', [
|
|||||||
// Check Constraint Node
|
// Check Constraint Node
|
||||||
if (!pgBrowser.Nodes['check_constraints']) {
|
if (!pgBrowser.Nodes['check_constraints']) {
|
||||||
pgAdmin.Browser.Nodes['check_constraints'] = pgBrowser.Node.extend({
|
pgAdmin.Browser.Nodes['check_constraints'] = pgBrowser.Node.extend({
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
type: 'check_constraints',
|
type: 'check_constraints',
|
||||||
label: gettext('Check'),
|
label: gettext('Check'),
|
||||||
collection_type: 'coll-constraints',
|
collection_type: 'coll-constraints',
|
||||||
@ -18,7 +19,7 @@ define('pgadmin.node.check_constraints', [
|
|||||||
dialogHelp: url_for('help.static', {'filename': 'check_dialog.html'}),
|
dialogHelp: url_for('help.static', {'filename': 'check_dialog.html'}),
|
||||||
hasSQL: true,
|
hasSQL: true,
|
||||||
hasDepends: true,
|
hasDepends: true,
|
||||||
parent_type: ['table'],
|
parent_type: ['table','partition'],
|
||||||
Init: function() {
|
Init: function() {
|
||||||
// Avoid mulitple registration of menus
|
// Avoid mulitple registration of menus
|
||||||
if (this.initialized)
|
if (this.initialized)
|
||||||
@ -137,6 +138,18 @@ define('pgadmin.node.check_constraints', [
|
|||||||
'switch', cell: 'boolean', group: gettext('Definition'), mode:
|
'switch', cell: 'boolean', group: gettext('Definition'), mode:
|
||||||
['properties', 'create', 'edit'], min_version: 90200,
|
['properties', 'create', 'edit'], min_version: 90200,
|
||||||
disabled: function(m) {
|
disabled: function(m) {
|
||||||
|
// Disabled if table is a partitioned table.
|
||||||
|
if ((_.has(m , 'top') && !_.isUndefined(m.top) && m.top.get('is_partitioned')) ||
|
||||||
|
(_.has(m, 'node_info') && _.has(m.node_info, 'table') &&
|
||||||
|
_.has(m.node_info.table, 'is_partitioned') && m.node_info.table.is_partitioned)
|
||||||
|
){
|
||||||
|
setTimeout(function(){
|
||||||
|
m.set('connoinherit', false);
|
||||||
|
},10);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return ((_.has(m, 'handler') &&
|
return ((_.has(m, 'handler') &&
|
||||||
!_.isUndefined(m.handler) &&
|
!_.isUndefined(m.handler) &&
|
||||||
!_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
|
!_.isUndefined(m.get('oid'))) || (_.isFunction(m.isNew) && !m.isNew()));
|
||||||
|
@ -602,6 +602,7 @@ define('pgadmin.node.exclusion_constraint', [
|
|||||||
// Extend the browser's node class for exclusion constraint node
|
// Extend the browser's node class for exclusion constraint node
|
||||||
if (!pgBrowser.Nodes['exclusion_constraint']) {
|
if (!pgBrowser.Nodes['exclusion_constraint']) {
|
||||||
pgAdmin.Browser.Nodes['exclusion_constraint'] = pgBrowser.Node.extend({
|
pgAdmin.Browser.Nodes['exclusion_constraint'] = pgBrowser.Node.extend({
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
type: 'exclusion_constraint',
|
type: 'exclusion_constraint',
|
||||||
label: gettext('Exclusion constraint'),
|
label: gettext('Exclusion constraint'),
|
||||||
collection_type: 'coll-constraints',
|
collection_type: 'coll-constraints',
|
||||||
@ -609,7 +610,7 @@ define('pgadmin.node.exclusion_constraint', [
|
|||||||
sqlCreateHelp: 'ddl-constraints.html',
|
sqlCreateHelp: 'ddl-constraints.html',
|
||||||
dialogHelp: url_for('help.static', {'filename': 'exclusion_constraint_dialog.html'}),
|
dialogHelp: url_for('help.static', {'filename': 'exclusion_constraint_dialog.html'}),
|
||||||
hasSQL: true,
|
hasSQL: true,
|
||||||
parent_type: 'table',
|
parent_type: ['table','partition'],
|
||||||
canDrop: true,
|
canDrop: true,
|
||||||
canDropCascade: true,
|
canDropCascade: true,
|
||||||
hasDepends: true,
|
hasDepends: true,
|
||||||
@ -916,12 +917,22 @@ define('pgadmin.node.exclusion_constraint', [
|
|||||||
if (data && data.check == false)
|
if (data && data.check == false)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var t = pgBrowser.tree, i = item, d = itemData, parents = [];
|
var t = pgBrowser.tree, i = item, d = itemData, parents = [],
|
||||||
|
immediate_parent_table_found = false,
|
||||||
|
is_immediate_parent_table_partitioned = false;
|
||||||
// To iterate over tree to check parent node
|
// To iterate over tree to check parent node
|
||||||
while (i) {
|
while (i) {
|
||||||
|
// If table is partitioned table then return false
|
||||||
|
if (!immediate_parent_table_found && (d._type == 'table' || d._type == 'partition')) {
|
||||||
|
immediate_parent_table_found = true;
|
||||||
|
if ('is_partitioned' in d && d.is_partitioned) {
|
||||||
|
is_immediate_parent_table_partitioned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If it is schema then allow user to create table
|
// If it is schema then allow user to create table
|
||||||
if (_.indexOf(['schema'], d._type) > -1)
|
if (_.indexOf(['schema'], d._type) > -1)
|
||||||
return true;
|
return !is_immediate_parent_table_partitioned;
|
||||||
parents.push(d._type);
|
parents.push(d._type);
|
||||||
i = t.hasParent(i) ? t.parent(i) : null;
|
i = t.hasParent(i) ? t.parent(i) : null;
|
||||||
d = i ? t.itemData(i) : null;
|
d = i ? t.itemData(i) : null;
|
||||||
@ -930,7 +941,7 @@ define('pgadmin.node.exclusion_constraint', [
|
|||||||
if (_.indexOf(parents, 'catalog') > -1) {
|
if (_.indexOf(parents, 'catalog') > -1) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return !is_immediate_parent_table_partitioned;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -602,6 +602,7 @@ define('pgadmin.node.foreign_key', [
|
|||||||
// Extend the browser's node class for foreign key node
|
// Extend the browser's node class for foreign key node
|
||||||
if (!pgBrowser.Nodes['foreign_key']) {
|
if (!pgBrowser.Nodes['foreign_key']) {
|
||||||
pgAdmin.Browser.Nodes['foreign_key'] = pgBrowser.Node.extend({
|
pgAdmin.Browser.Nodes['foreign_key'] = pgBrowser.Node.extend({
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
type: 'foreign_key',
|
type: 'foreign_key',
|
||||||
label: gettext('Foreign key'),
|
label: gettext('Foreign key'),
|
||||||
collection_type: 'coll-constraints',
|
collection_type: 'coll-constraints',
|
||||||
@ -610,7 +611,7 @@ define('pgadmin.node.foreign_key', [
|
|||||||
dialogHelp: url_for('help.static', {'filename': 'foreign_key_dialog.html'}),
|
dialogHelp: url_for('help.static', {'filename': 'foreign_key_dialog.html'}),
|
||||||
hasSQL: true,
|
hasSQL: true,
|
||||||
hasDepends: false,
|
hasDepends: false,
|
||||||
parent_type: 'table',
|
parent_type: ['table','partition'],
|
||||||
canDrop: true,
|
canDrop: true,
|
||||||
canDropCascade: true,
|
canDropCascade: true,
|
||||||
hasDepends: true,
|
hasDepends: true,
|
||||||
@ -1068,12 +1069,22 @@ define('pgadmin.node.foreign_key', [
|
|||||||
if (data && data.check == false)
|
if (data && data.check == false)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var t = pgBrowser.tree, i = item, d = itemData, parents = [];
|
var t = pgBrowser.tree, i = item, d = itemData, parents = [],
|
||||||
|
immediate_parent_table_found = false,
|
||||||
|
is_immediate_parent_table_partitioned = false;
|
||||||
// To iterate over tree to check parent node
|
// To iterate over tree to check parent node
|
||||||
while (i) {
|
while (i) {
|
||||||
|
// If table is partitioned table then return false
|
||||||
|
if (!immediate_parent_table_found && (d._type == 'table' || d._type == 'partition')) {
|
||||||
|
immediate_parent_table_found = true;
|
||||||
|
if ('is_partitioned' in d && d.is_partitioned) {
|
||||||
|
is_immediate_parent_table_partitioned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If it is schema then allow user to c reate table
|
// If it is schema then allow user to c reate table
|
||||||
if (_.indexOf(['schema'], d._type) > -1)
|
if (_.indexOf(['schema'], d._type) > -1)
|
||||||
return true;
|
return !is_immediate_parent_table_partitioned;
|
||||||
parents.push(d._type);
|
parents.push(d._type);
|
||||||
i = t.hasParent(i) ? t.parent(i) : null;
|
i = t.hasParent(i) ? t.parent(i) : null;
|
||||||
d = i ? t.itemData(i) : null;
|
d = i ? t.itemData(i) : null;
|
||||||
@ -1082,7 +1093,7 @@ define('pgadmin.node.foreign_key', [
|
|||||||
if (_.indexOf(parents, 'catalog') > -1) {
|
if (_.indexOf(parents, 'catalog') > -1) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return !is_immediate_parent_table_partitioned;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -6,6 +6,7 @@ define('pgadmin.node.{{node_type}}', [
|
|||||||
// Extend the browser's node class for index constraint node
|
// Extend the browser's node class for index constraint node
|
||||||
if (!pgBrowser.Nodes['{{node_type}}']) {
|
if (!pgBrowser.Nodes['{{node_type}}']) {
|
||||||
pgAdmin.Browser.Nodes['{{node_type}}'] = pgBrowser.Node.extend({
|
pgAdmin.Browser.Nodes['{{node_type}}'] = pgBrowser.Node.extend({
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
type: '{{node_type}}',
|
type: '{{node_type}}',
|
||||||
label: '{{ node_label }}',
|
label: '{{ node_label }}',
|
||||||
collection_type: 'coll-constraints',
|
collection_type: 'coll-constraints',
|
||||||
@ -20,7 +21,7 @@ define('pgadmin.node.{{node_type}}', [
|
|||||||
hasDepends: true,
|
hasDepends: true,
|
||||||
hasStatistics: true,
|
hasStatistics: true,
|
||||||
statsPrettifyFields: ['Index size'],
|
statsPrettifyFields: ['Index size'],
|
||||||
parent_type: 'table',
|
parent_type: ['table','partition'],
|
||||||
canDrop: true,
|
canDrop: true,
|
||||||
canDropCascade: true,
|
canDropCascade: true,
|
||||||
Init: function() {
|
Init: function() {
|
||||||
@ -45,12 +46,28 @@ define('pgadmin.node.{{node_type}}', [
|
|||||||
if (data && data.check == false)
|
if (data && data.check == false)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var t = pgBrowser.tree, i = item, d = itemData, parents = [];
|
var t = pgBrowser.tree, i = item, d = itemData, parents = [],
|
||||||
|
immediate_parent_table_found = false,
|
||||||
|
is_immediate_parent_table_partitioned = false;
|
||||||
|
|
||||||
// To iterate over tree to check parent node
|
// To iterate over tree to check parent node
|
||||||
while (i) {
|
while (i) {
|
||||||
// If it is schema then allow user to c reate table
|
// If table is partitioned table then return false
|
||||||
|
if (!immediate_parent_table_found && (d._type == 'table' || d._type == 'partition')) {
|
||||||
|
immediate_parent_table_found = true;
|
||||||
|
if ('is_partitioned' in d && d.is_partitioned) {
|
||||||
|
is_immediate_parent_table_partitioned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it is schema then allow user to create table
|
||||||
if (_.indexOf(['schema'], d._type) > -1) {
|
if (_.indexOf(['schema'], d._type) > -1) {
|
||||||
{% if node_type == 'primary_key' %}
|
{% if node_type == 'primary_key' %}
|
||||||
|
|
||||||
|
if (is_immediate_parent_table_partitioned) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// There should be only one primary key per table.
|
// There should be only one primary key per table.
|
||||||
var children = t.children(arguments[1], false),
|
var children = t.children(arguments[1], false),
|
||||||
primary_key_found = false;
|
primary_key_found = false;
|
||||||
@ -63,7 +80,7 @@ define('pgadmin.node.{{node_type}}', [
|
|||||||
});
|
});
|
||||||
return !primary_key_found;
|
return !primary_key_found;
|
||||||
{% else %}
|
{% else %}
|
||||||
return true;
|
return !is_immediate_parent_table_partitioned;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
}
|
}
|
||||||
parents.push(d._type);
|
parents.push(d._type);
|
||||||
@ -74,7 +91,7 @@ define('pgadmin.node.{{node_type}}', [
|
|||||||
if (_.indexOf(parents, 'catalog') > -1) {
|
if (_.indexOf(parents, 'catalog') > -1) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return !is_immediate_parent_table_partitioned;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -9,16 +9,18 @@ define('pgadmin.node.constraints', [
|
|||||||
node: 'constraints',
|
node: 'constraints',
|
||||||
label: gettext('Constraints'),
|
label: gettext('Constraints'),
|
||||||
type: 'coll-constraints',
|
type: 'coll-constraints',
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
columns: ['name', 'comment']
|
columns: ['name', 'comment']
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!pgBrowser.Nodes['constraints']) {
|
if (!pgBrowser.Nodes['constraints']) {
|
||||||
pgAdmin.Browser.Nodes['constraints'] = pgBrowser.Node.extend({
|
pgAdmin.Browser.Nodes['constraints'] = pgBrowser.Node.extend({
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
type: 'constraints',
|
type: 'constraints',
|
||||||
label: gettext('Constraints'),
|
label: gettext('Constraints'),
|
||||||
collection_type: 'coll-constraints',
|
collection_type: 'coll-constraints',
|
||||||
parent_type: ['table'],
|
parent_type: ['table','partition'],
|
||||||
Init: function() {
|
Init: function() {
|
||||||
/* Avoid mulitple registration of menus */
|
/* Avoid mulitple registration of menus */
|
||||||
if (this.initialized)
|
if (this.initialized)
|
||||||
|
@ -72,12 +72,28 @@ class IndexesModule(CollectionNodeModule):
|
|||||||
if super(IndexesModule, self).BackendSupported(manager, **kwargs):
|
if super(IndexesModule, self).BackendSupported(manager, **kwargs):
|
||||||
conn = manager.connection(did=kwargs['did'])
|
conn = manager.connection(did=kwargs['did'])
|
||||||
|
|
||||||
|
# In case of partitioned table return false.
|
||||||
|
if 'tid' in kwargs and manager.version >= 100000:
|
||||||
|
partition_path = 'partition/sql/#{0}#'.format(manager.version)
|
||||||
|
SQL = render_template(
|
||||||
|
"/".join([partition_path, 'backend_support.sql']),
|
||||||
|
tid=kwargs['tid']
|
||||||
|
)
|
||||||
|
status, res = conn.execute_scalar(SQL)
|
||||||
|
|
||||||
|
# check if any errors
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
return not res
|
||||||
|
|
||||||
if 'vid' not in kwargs:
|
if 'vid' not in kwargs:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
template_path = 'index/sql/#{0}#'.format(manager.version)
|
template_path = 'index/sql/#{0}#'.format(manager.version)
|
||||||
SQL = render_template("/".join(
|
SQL = render_template(
|
||||||
[template_path, 'backend_support.sql']), vid=kwargs['vid'])
|
"/".join([template_path, 'backend_support.sql']),
|
||||||
|
vid=kwargs['vid']
|
||||||
|
)
|
||||||
status, res = conn.execute_scalar(SQL)
|
status, res = conn.execute_scalar(SQL)
|
||||||
|
|
||||||
# check if any errors
|
# check if any errors
|
||||||
@ -239,9 +255,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
# We need parent's name eg table name and schema name
|
# We need parent's name eg table name and schema name
|
||||||
# when we create new index in update we can fetch it using
|
# when we create new index in update we can fetch it using
|
||||||
# property sql
|
# property sql
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'get_parent.sql']),
|
"/".join([self.template_path, 'get_parent.sql']),
|
||||||
tid=kwargs['tid'])
|
tid=kwargs['tid']
|
||||||
|
)
|
||||||
status, rset = self.conn.execute_2darray(SQL)
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=rset)
|
return internal_server_error(errormsg=rset)
|
||||||
@ -262,8 +279,9 @@ class IndexesView(PGChildNodeView):
|
|||||||
"""
|
"""
|
||||||
res = [{'label': '', 'value': ''}]
|
res = [{'label': '', 'value': ''}]
|
||||||
try:
|
try:
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'get_collations.sql']))
|
"/".join([self.template_path, 'get_collations.sql'])
|
||||||
|
)
|
||||||
status, rset = self.conn.execute_2darray(SQL)
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
@ -289,8 +307,7 @@ class IndexesView(PGChildNodeView):
|
|||||||
"""
|
"""
|
||||||
res = [{'label': '', 'value': ''}]
|
res = [{'label': '', 'value': ''}]
|
||||||
try:
|
try:
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template("/".join([self.template_path, 'get_am.sql']))
|
||||||
'get_am.sql']))
|
|
||||||
status, rset = self.conn.execute_2darray(SQL)
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
@ -317,17 +334,17 @@ class IndexesView(PGChildNodeView):
|
|||||||
res = dict()
|
res = dict()
|
||||||
try:
|
try:
|
||||||
# Fetching all the access methods
|
# Fetching all the access methods
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template("/".join([self.template_path, 'get_am.sql']))
|
||||||
'get_am.sql']))
|
|
||||||
status, rset = self.conn.execute_2darray(SQL)
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
for row in rset['rows']:
|
for row in rset['rows']:
|
||||||
# Fetching all the op_classes for each access method
|
# Fetching all the op_classes for each access method
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'get_op_class.sql']),
|
"/".join([self.template_path, 'get_op_class.sql']),
|
||||||
oid=row['oid'])
|
oid=row['oid']
|
||||||
|
)
|
||||||
status, result = self.conn.execute_2darray(SQL)
|
status, result = self.conn.execute_2darray(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
@ -365,8 +382,9 @@ class IndexesView(PGChildNodeView):
|
|||||||
JSON of available schema nodes
|
JSON of available schema nodes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'nodes.sql']), tid=tid)
|
"/".join([self.template_path, 'nodes.sql']), tid=tid
|
||||||
|
)
|
||||||
status, res = self.conn.execute_dict(SQL)
|
status, res = self.conn.execute_dict(SQL)
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
@ -393,10 +411,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
Returns:
|
Returns:
|
||||||
JSON of available schema child nodes
|
JSON of available schema child nodes
|
||||||
"""
|
"""
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'nodes.sql']),
|
"/".join([self.template_path, 'nodes.sql']),
|
||||||
tid=tid,
|
tid=tid, idx=idx
|
||||||
idx=idx)
|
)
|
||||||
status, rset = self.conn.execute_2darray(SQL)
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=rset)
|
return internal_server_error(errormsg=rset)
|
||||||
@ -433,8 +451,9 @@ class IndexesView(PGChildNodeView):
|
|||||||
JSON of available schema child nodes
|
JSON of available schema child nodes
|
||||||
"""
|
"""
|
||||||
res = []
|
res = []
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'nodes.sql']), tid=tid)
|
"/".join([self.template_path, 'nodes.sql']), tid=tid
|
||||||
|
)
|
||||||
status, rset = self.conn.execute_2darray(SQL)
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=rset)
|
return internal_server_error(errormsg=rset)
|
||||||
@ -465,8 +484,9 @@ class IndexesView(PGChildNodeView):
|
|||||||
Updated properties data with column details
|
Updated properties data with column details
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'column_details.sql']), idx=idx)
|
"/".join([self.template_path, 'column_details.sql']), idx=idx
|
||||||
|
)
|
||||||
status, rset = self.conn.execute_2darray(SQL)
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=rset)
|
return internal_server_error(errormsg=rset)
|
||||||
@ -521,10 +541,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
JSON of selected schema node
|
JSON of selected schema node
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'properties.sql']),
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
did=did, tid=tid, idx=idx,
|
did=did, tid=tid, idx=idx, datlastsysoid=self.datlastsysoid
|
||||||
datlastsysoid=self.datlastsysoid)
|
)
|
||||||
|
|
||||||
status, res = self.conn.execute_dict(SQL)
|
status, res = self.conn.execute_dict(SQL)
|
||||||
|
|
||||||
@ -595,9 +615,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
try:
|
try:
|
||||||
# Start transaction.
|
# Start transaction.
|
||||||
self.conn.execute_scalar("BEGIN;")
|
self.conn.execute_scalar("BEGIN;")
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'create.sql']),
|
"/".join([self.template_path, 'create.sql']),
|
||||||
data=data, conn=self.conn, mode='create')
|
data=data, conn=self.conn, mode='create'
|
||||||
|
)
|
||||||
status, res = self.conn.execute_scalar(SQL)
|
status, res = self.conn.execute_scalar(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
# End transaction.
|
# End transaction.
|
||||||
@ -606,9 +627,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
|
|
||||||
# If user chooses concurrent index then we cannot run it along
|
# If user chooses concurrent index then we cannot run it along
|
||||||
# with other alter statements so we will separate alter index part
|
# with other alter statements so we will separate alter index part
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'alter.sql']),
|
"/".join([self.template_path, 'alter.sql']),
|
||||||
data=data, conn=self.conn)
|
data=data, conn=self.conn
|
||||||
|
)
|
||||||
SQL = SQL.strip('\n').strip(' ')
|
SQL = SQL.strip('\n').strip(' ')
|
||||||
if SQL != '':
|
if SQL != '':
|
||||||
status, res = self.conn.execute_scalar(SQL)
|
status, res = self.conn.execute_scalar(SQL)
|
||||||
@ -618,9 +640,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
# we need oid to to add object in tree at browser
|
# we need oid to to add object in tree at browser
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'get_oid.sql']),
|
"/".join([self.template_path, 'get_oid.sql']),
|
||||||
tid=tid, data=data)
|
tid=tid, data=data
|
||||||
|
)
|
||||||
status, idx = self.conn.execute_scalar(SQL)
|
status, idx = self.conn.execute_scalar(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
# End transaction.
|
# End transaction.
|
||||||
@ -665,10 +688,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
try:
|
try:
|
||||||
# We will first fetch the index name for current request
|
# We will first fetch the index name for current request
|
||||||
# so that we create template for dropping index
|
# so that we create template for dropping index
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'properties.sql']),
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
did=did, tid=tid, idx=idx,
|
did=did, tid=tid, idx=idx, datlastsysoid=self.datlastsysoid
|
||||||
datlastsysoid=self.datlastsysoid)
|
)
|
||||||
|
|
||||||
status, res = self.conn.execute_dict(SQL)
|
status, res = self.conn.execute_dict(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
@ -687,9 +710,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
|
|
||||||
data = dict(res['rows'][0])
|
data = dict(res['rows'][0])
|
||||||
|
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'delete.sql']),
|
"/".join([self.template_path, 'delete.sql']),
|
||||||
data=data, conn=self.conn, cascade=cascade)
|
data=data, conn=self.conn, cascade=cascade
|
||||||
|
)
|
||||||
status, res = self.conn.execute_scalar(SQL)
|
status, res = self.conn.execute_scalar(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
@ -787,10 +811,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
This function will genrate sql from model data
|
This function will genrate sql from model data
|
||||||
"""
|
"""
|
||||||
if idx is not None:
|
if idx is not None:
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'properties.sql']),
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
did=did, tid=tid, idx=idx,
|
did=did, tid=tid, idx=idx, datlastsysoid=self.datlastsysoid
|
||||||
datlastsysoid=self.datlastsysoid)
|
)
|
||||||
|
|
||||||
status, res = self.conn.execute_dict(SQL)
|
status, res = self.conn.execute_dict(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
@ -826,11 +850,15 @@ class IndexesView(PGChildNodeView):
|
|||||||
return gettext('-- definition incomplete')
|
return gettext('-- definition incomplete')
|
||||||
|
|
||||||
# If the request for new object which do not have did
|
# If the request for new object which do not have did
|
||||||
SQL = render_template("/".join([self.template_path, 'create.sql']),
|
SQL = render_template(
|
||||||
data=data, conn=self.conn, mode=mode)
|
"/".join([self.template_path, 'create.sql']),
|
||||||
|
data=data, conn=self.conn, mode=mode
|
||||||
|
)
|
||||||
SQL += "\n"
|
SQL += "\n"
|
||||||
SQL += render_template("/".join([self.template_path, 'alter.sql']),
|
SQL += render_template(
|
||||||
data=data, conn=self.conn)
|
"/".join([self.template_path, 'alter.sql']),
|
||||||
|
data=data, conn=self.conn
|
||||||
|
)
|
||||||
|
|
||||||
return SQL, data['name'] if 'name' in data else old_data['name']
|
return SQL, data['name'] if 'name' in data else old_data['name']
|
||||||
|
|
||||||
@ -848,10 +876,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
idx: Index ID
|
idx: Index ID
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'properties.sql']),
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
did=did, tid=tid, idx=idx,
|
did=did, tid=tid, idx=idx, datlastsysoid=self.datlastsysoid
|
||||||
datlastsysoid=self.datlastsysoid)
|
)
|
||||||
|
|
||||||
status, res = self.conn.execute_dict(SQL)
|
status, res = self.conn.execute_dict(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
@ -872,9 +900,10 @@ class IndexesView(PGChildNodeView):
|
|||||||
return SQL
|
return SQL
|
||||||
sql_header = u"-- Index: {0}\n\n-- ".format(data['name'])
|
sql_header = u"-- Index: {0}\n\n-- ".format(data['name'])
|
||||||
|
|
||||||
sql_header += render_template("/".join([self.template_path,
|
sql_header += render_template(
|
||||||
'delete.sql']),
|
"/".join([self.template_path, 'delete.sql']),
|
||||||
data=data, conn=self.conn)
|
data=data, conn=self.conn
|
||||||
|
)
|
||||||
|
|
||||||
SQL = sql_header + '\n\n' + SQL
|
SQL = sql_header + '\n\n' + SQL
|
||||||
|
|
||||||
@ -959,10 +988,11 @@ class IndexesView(PGChildNodeView):
|
|||||||
|
|
||||||
if is_pgstattuple:
|
if is_pgstattuple:
|
||||||
# Fetch index details only if extended stats available
|
# Fetch index details only if extended stats available
|
||||||
SQL = render_template("/".join([self.template_path,
|
SQL = render_template(
|
||||||
'properties.sql']),
|
"/".join([self.template_path, 'properties.sql']),
|
||||||
did=did, tid=tid, idx=idx,
|
did=did, tid=tid, idx=idx,
|
||||||
datlastsysoid=self.datlastsysoid)
|
datlastsysoid=self.datlastsysoid
|
||||||
|
)
|
||||||
status, res = self.conn.execute_dict(SQL)
|
status, res = self.conn.execute_dict(SQL)
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
|
@ -10,6 +10,7 @@ define('pgadmin.node.index', [
|
|||||||
node: 'index',
|
node: 'index',
|
||||||
label: gettext('Indexes'),
|
label: gettext('Indexes'),
|
||||||
type: 'coll-index',
|
type: 'coll-index',
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
sqlAlterHelp: 'sql-alterindex.html',
|
sqlAlterHelp: 'sql-alterindex.html',
|
||||||
sqlCreateHelp: 'sql-createindex.html',
|
sqlCreateHelp: 'sql-createindex.html',
|
||||||
dialogHelp: url_for('help.static', {'filename': 'index_dialog.html'}),
|
dialogHelp: url_for('help.static', {'filename': 'index_dialog.html'}),
|
||||||
@ -210,8 +211,9 @@ define('pgadmin.node.index', [
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!pgBrowser.Nodes['index']) {
|
if (!pgBrowser.Nodes['index']) {
|
||||||
pgAdmin.Browser.Nodes['index'] = pgAdmin.Browser.Node.extend({
|
pgAdmin.Browser.Nodes['index'] = pgBrowser.Node.extend({
|
||||||
parent_type: ['table', 'view', 'mview'],
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
|
parent_type: ['table', 'view', 'mview', 'partition'],
|
||||||
collection_type: ['coll-table', 'coll-view'],
|
collection_type: ['coll-table', 'coll-view'],
|
||||||
sqlAlterHelp: 'sql-alterindex.html',
|
sqlAlterHelp: 'sql-alterindex.html',
|
||||||
sqlCreateHelp: 'sql-createindex.html',
|
sqlCreateHelp: 'sql-createindex.html',
|
||||||
@ -246,6 +248,12 @@ define('pgadmin.node.index', [
|
|||||||
category: 'create', priority: 4, label: gettext('Index...'),
|
category: 'create', priority: 4, label: gettext('Index...'),
|
||||||
icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
|
icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
|
||||||
enable: 'canCreate'
|
enable: 'canCreate'
|
||||||
|
},{
|
||||||
|
name: 'create_index_onPartition', node: 'partition', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: gettext('Index...'),
|
||||||
|
icon: 'wcTabIcon icon-index', data: {action: 'create', check: true},
|
||||||
|
enable: 'canCreate'
|
||||||
},{
|
},{
|
||||||
name: 'create_index_onMatView', node: 'mview', module: this,
|
name: 'create_index_onMatView', node: 'mview', module: this,
|
||||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
@ -472,12 +480,23 @@ define('pgadmin.node.index', [
|
|||||||
if (data && data.check == false)
|
if (data && data.check == false)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var t = pgBrowser.tree, i = item, d = itemData, parents = [];
|
var t = pgBrowser.tree, i = item, d = itemData, parents = [],
|
||||||
|
immediate_parent_table_found = false,
|
||||||
|
is_immediate_parent_table_partitioned = false;
|
||||||
// To iterate over tree to check parent node
|
// To iterate over tree to check parent node
|
||||||
while (i) {
|
while (i) {
|
||||||
// If it is schema then allow user to c reate table
|
// Do not allow creating index on partitioned tables.
|
||||||
|
if (!immediate_parent_table_found &&
|
||||||
|
_.indexOf(['table', 'partition'], d._type) > -1) {
|
||||||
|
immediate_parent_table_found = true;
|
||||||
|
if ('is_partitioned' in d && d.is_partitioned) {
|
||||||
|
is_immediate_parent_table_partitioned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it is schema then allow user to create index
|
||||||
if (_.indexOf(['schema'], d._type) > -1)
|
if (_.indexOf(['schema'], d._type) > -1)
|
||||||
return true;
|
return !is_immediate_parent_table_partitioned;
|
||||||
parents.push(d._type);
|
parents.push(d._type);
|
||||||
i = t.hasParent(i) ? t.parent(i) : null;
|
i = t.hasParent(i) ? t.parent(i) : null;
|
||||||
d = i ? t.itemData(i) : null;
|
d = i ? t.itemData(i) : null;
|
||||||
@ -486,7 +505,7 @@ define('pgadmin.node.index', [
|
|||||||
if (_.indexOf(parents, 'catalog') > -1) {
|
if (_.indexOf(parents, 'catalog') > -1) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return !is_immediate_parent_table_partitioned;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,583 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
""" Implements Partitions Node """
|
||||||
|
|
||||||
|
import re
|
||||||
|
import simplejson as json
|
||||||
|
import pgadmin.browser.server_groups.servers.databases.schemas as schema
|
||||||
|
from flask import render_template, request
|
||||||
|
from flask_babel import gettext
|
||||||
|
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
|
||||||
|
import DataTypeReader, VacuumSettings
|
||||||
|
from pgadmin.utils.ajax import internal_server_error, \
|
||||||
|
make_response as ajax_response, gone
|
||||||
|
from pgadmin.browser.server_groups.servers.databases.schemas.tables.utils \
|
||||||
|
import BaseTableView
|
||||||
|
from pgadmin.browser.collection import CollectionNodeModule
|
||||||
|
from pgadmin.utils.ajax import make_json_response, precondition_required
|
||||||
|
from config import PG_DEFAULT_DRIVER
|
||||||
|
from pgadmin.browser.utils import PGChildModule
|
||||||
|
|
||||||
|
|
||||||
|
class PartitionsModule(CollectionNodeModule):
|
||||||
|
"""
|
||||||
|
class PartitionsModule(CollectionNodeModule)
|
||||||
|
|
||||||
|
A module class for Partition node derived from CollectionNodeModule.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
-------
|
||||||
|
* __init__(*args, **kwargs)
|
||||||
|
- Method is used to initialize the Partition and it's base module.
|
||||||
|
|
||||||
|
* get_nodes(gid, sid, did, scid, tid)
|
||||||
|
- Method is used to generate the browser collection node.
|
||||||
|
|
||||||
|
* node_inode()
|
||||||
|
- Method is overridden from its base class to make the node as leaf node.
|
||||||
|
|
||||||
|
* script_load()
|
||||||
|
- Load the module script for schema, when any of the server node is
|
||||||
|
initialized.
|
||||||
|
"""
|
||||||
|
|
||||||
|
NODE_TYPE = 'partition'
|
||||||
|
COLLECTION_LABEL = gettext("Partitions")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Method is used to initialize the PartitionsModule and it's base module.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
*args:
|
||||||
|
**kwargs:
|
||||||
|
"""
|
||||||
|
super(PartitionsModule, self).__init__(*args, **kwargs)
|
||||||
|
self.min_ver = 100000
|
||||||
|
self.max_ver = None
|
||||||
|
|
||||||
|
def get_nodes(self, gid, sid, did, scid, **kwargs):
|
||||||
|
"""
|
||||||
|
Generate the collection node
|
||||||
|
"""
|
||||||
|
yield self.generate_browser_collection_node(kwargs['tid'])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def script_load(self):
|
||||||
|
"""
|
||||||
|
Load the module script for server, when any of the server-group node is
|
||||||
|
initialized.
|
||||||
|
"""
|
||||||
|
return schema.SchemaModule.NODE_TYPE
|
||||||
|
|
||||||
|
@property
|
||||||
|
def node_inode(self):
|
||||||
|
"""
|
||||||
|
Load the module node as a leaf node
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def BackendSupported(self, manager, **kwargs):
|
||||||
|
"""
|
||||||
|
Load this module if it is a partition table
|
||||||
|
"""
|
||||||
|
if 'tid' in kwargs and CollectionNodeModule.BackendSupported(self, manager, **kwargs):
|
||||||
|
conn = manager.connection(did=kwargs['did'])
|
||||||
|
|
||||||
|
template_path = 'partition/sql/#{0}#'.format(manager.version)
|
||||||
|
SQL = render_template("/".join(
|
||||||
|
[template_path, 'backend_support.sql']), tid=kwargs['tid'])
|
||||||
|
status, res = conn.execute_scalar(SQL)
|
||||||
|
|
||||||
|
# check if any errors
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def register(self, app, options, first_registration=False):
|
||||||
|
"""
|
||||||
|
Override the default register function to automatically register
|
||||||
|
sub-modules of table node under partition table node.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if first_registration:
|
||||||
|
self.submodules = list(app.find_submodules(self.import_name))
|
||||||
|
|
||||||
|
super(CollectionNodeModule, self).register(app, options, first_registration)
|
||||||
|
|
||||||
|
for module in self.submodules:
|
||||||
|
if first_registration:
|
||||||
|
module.parentmodules.append(self)
|
||||||
|
app.register_blueprint(module)
|
||||||
|
|
||||||
|
# Now add sub modules of table node to partition table node.
|
||||||
|
if first_registration:
|
||||||
|
# Exclude 'partition' module for now to avoid cyclic import issue.
|
||||||
|
modules_to_skip = ['partition', 'column']
|
||||||
|
for parent in self.parentmodules:
|
||||||
|
if parent.NODE_TYPE == 'table':
|
||||||
|
self.submodules += [
|
||||||
|
submodule for submodule in parent.submodules
|
||||||
|
if submodule.NODE_TYPE not in modules_to_skip
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def module_use_template_javascript(self):
|
||||||
|
"""
|
||||||
|
Returns whether Jinja2 template is used for generating the javascript
|
||||||
|
module.
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
blueprint = PartitionsModule(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings):
|
||||||
|
"""
|
||||||
|
This class is responsible for generating routes for Partition node
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
-------
|
||||||
|
|
||||||
|
* list()
|
||||||
|
- This function is used to list all the Partition nodes within that
|
||||||
|
collection.
|
||||||
|
|
||||||
|
* nodes()
|
||||||
|
- This function will used to create all the child node within that
|
||||||
|
collection, Here it will create all the Partition node.
|
||||||
|
|
||||||
|
* properties(gid, sid, did, scid, tid, ptid)
|
||||||
|
- This function will show the properties of the selected Partition node
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
node_type = blueprint.node_type
|
||||||
|
|
||||||
|
parent_ids = [
|
||||||
|
{'type': 'int', 'id': 'gid'},
|
||||||
|
{'type': 'int', 'id': 'sid'},
|
||||||
|
{'type': 'int', 'id': 'did'},
|
||||||
|
{'type': 'int', 'id': 'scid'},
|
||||||
|
{'type': 'int', 'id': 'tid'}
|
||||||
|
]
|
||||||
|
ids = [
|
||||||
|
{'type': 'int', 'id': 'ptid'}
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = dict({
|
||||||
|
'obj': [
|
||||||
|
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||||
|
{'get': 'list', 'post': 'create'}
|
||||||
|
],
|
||||||
|
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],
|
||||||
|
'children': [{'get': 'children'}],
|
||||||
|
'sql': [{'get': 'sql'}],
|
||||||
|
'msql': [{'get': 'msql'}, {}],
|
||||||
|
'detach': [{'put': 'detach'}],
|
||||||
|
'truncate': [{'put': 'truncate'}]
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
def children(self, **kwargs):
|
||||||
|
"""Build a list of treeview nodes from the child nodes."""
|
||||||
|
|
||||||
|
if 'sid' not in kwargs:
|
||||||
|
return precondition_required(
|
||||||
|
gettext('Required properties are missing.')
|
||||||
|
)
|
||||||
|
|
||||||
|
from pgadmin.utils.driver import get_driver
|
||||||
|
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
|
||||||
|
sid=kwargs['sid']
|
||||||
|
)
|
||||||
|
|
||||||
|
did = None
|
||||||
|
if 'did' in kwargs:
|
||||||
|
did = kwargs['did']
|
||||||
|
|
||||||
|
conn = manager.connection(did=did)
|
||||||
|
|
||||||
|
if not conn.connected():
|
||||||
|
return precondition_required(
|
||||||
|
gettext(
|
||||||
|
"Connection to the server has been lost."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
nodes = []
|
||||||
|
for module in self.blueprint.submodules:
|
||||||
|
if isinstance(module, PGChildModule):
|
||||||
|
if manager is not None and \
|
||||||
|
module.BackendSupported(manager, **kwargs):
|
||||||
|
# treat partition table as normal table.
|
||||||
|
# replace tid with ptid and pop ptid from kwargs
|
||||||
|
if 'ptid' in kwargs:
|
||||||
|
ptid = kwargs.pop('ptid')
|
||||||
|
kwargs['tid'] = ptid
|
||||||
|
nodes.extend(module.get_nodes(**kwargs))
|
||||||
|
else:
|
||||||
|
nodes.extend(module.get_nodes(**kwargs))
|
||||||
|
|
||||||
|
# Explicitly include 'partition' module as we had excluded it during
|
||||||
|
# registration.
|
||||||
|
nodes.extend(self.blueprint.get_nodes(**kwargs))
|
||||||
|
|
||||||
|
# Return sorted nodes based on label
|
||||||
|
return make_json_response(
|
||||||
|
data=sorted(
|
||||||
|
nodes, key=lambda c: c['label']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@BaseTableView.check_precondition
|
||||||
|
def list(self, gid, sid, did, scid, tid):
|
||||||
|
"""
|
||||||
|
This function is used to list all the table nodes within that
|
||||||
|
collection.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: Table ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON of available table nodes
|
||||||
|
"""
|
||||||
|
SQL = render_template("/".join([self.partition_template_path,
|
||||||
|
'properties.sql']),
|
||||||
|
did=did, scid=scid, tid=tid,
|
||||||
|
datlastsysoid=self.datlastsysoid)
|
||||||
|
status, res = self.conn.execute_dict(SQL)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
return ajax_response(
|
||||||
|
response=res['rows'],
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@BaseTableView.check_precondition
|
||||||
|
def nodes(self, gid, sid, did, scid, tid, ptid=None):
|
||||||
|
"""
|
||||||
|
This function is used to list all the table nodes within that
|
||||||
|
collection.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: Parent Table ID
|
||||||
|
ptid: Partition Table ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON of available table nodes
|
||||||
|
"""
|
||||||
|
SQL = render_template(
|
||||||
|
"/".join([self.partition_template_path, 'nodes.sql']),
|
||||||
|
scid=scid, tid=tid
|
||||||
|
)
|
||||||
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=rset)
|
||||||
|
|
||||||
|
def browser_node(row):
|
||||||
|
return self.blueprint.generate_browser_node(
|
||||||
|
row['oid'],
|
||||||
|
tid,
|
||||||
|
row['name'],
|
||||||
|
icon="icon-partition",
|
||||||
|
tigger_count=row['triggercount'],
|
||||||
|
has_enable_triggers=row['has_enable_triggers'],
|
||||||
|
is_partitioned=row['is_partitioned'],
|
||||||
|
parent_schema_id=scid,
|
||||||
|
schema_id=row['schema_id'],
|
||||||
|
schema_name=row['schema_name']
|
||||||
|
)
|
||||||
|
|
||||||
|
if ptid is not None:
|
||||||
|
if len(rset['rows']) == 0:
|
||||||
|
return gone(gettext(
|
||||||
|
"The specified partitioned table could not be found."
|
||||||
|
))
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
data=browser_node(rset['rows'][0]), status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
res = []
|
||||||
|
for row in rset['rows']:
|
||||||
|
res.append(browser_node(row))
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@BaseTableView.check_precondition
|
||||||
|
def properties(self, gid, sid, did, scid, tid, ptid):
|
||||||
|
"""
|
||||||
|
This function will show the properties of the selected table node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: Table ID
|
||||||
|
ptid: Partition Table ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON of selected table node
|
||||||
|
"""
|
||||||
|
|
||||||
|
SQL = render_template("/".join([self.partition_template_path,
|
||||||
|
'properties.sql']),
|
||||||
|
did=did, scid=scid, tid=tid,
|
||||||
|
ptid=ptid, datlastsysoid=self.datlastsysoid)
|
||||||
|
status, res = self.conn.execute_dict(SQL)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
if len(res['rows']) == 0:
|
||||||
|
return gone(gettext(
|
||||||
|
"The specified partitioned table could not be found."))
|
||||||
|
|
||||||
|
return super(PartitionsView, self).properties(
|
||||||
|
gid, sid, did, scid, ptid, res)
|
||||||
|
|
||||||
|
@BaseTableView.check_precondition
|
||||||
|
def sql(self, gid, sid, did, scid, tid, ptid):
|
||||||
|
"""
|
||||||
|
This function will creates reverse engineered sql for
|
||||||
|
the table object
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: Table ID
|
||||||
|
ptid: Partition Table ID
|
||||||
|
"""
|
||||||
|
main_sql = []
|
||||||
|
|
||||||
|
SQL = render_template("/".join([self.partition_template_path,
|
||||||
|
'properties.sql']),
|
||||||
|
did=did, scid=scid, tid=tid,
|
||||||
|
ptid=ptid, datlastsysoid=self.datlastsysoid)
|
||||||
|
status, res = self.conn.execute_dict(SQL)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
if len(res['rows']) == 0:
|
||||||
|
return gone(gettext(
|
||||||
|
"The specified partitioned table could not be found."))
|
||||||
|
|
||||||
|
data = res['rows'][0]
|
||||||
|
|
||||||
|
return BaseTableView.get_reverse_engineered_sql(self, did, scid, ptid,
|
||||||
|
main_sql, data)
|
||||||
|
|
||||||
|
@BaseTableView.check_precondition
|
||||||
|
def detach(self, gid, sid, did, scid, tid, ptid):
|
||||||
|
"""
|
||||||
|
This function will reset statistics of table
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: Table ID
|
||||||
|
ptid: Partition Table ID
|
||||||
|
"""
|
||||||
|
# Fetch schema name
|
||||||
|
status, parent_schema = self.conn.execute_scalar(
|
||||||
|
render_template(
|
||||||
|
"/".join([self.table_template_path, 'get_schema.sql']),
|
||||||
|
conn=self.conn, scid=scid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=parent_schema)
|
||||||
|
|
||||||
|
# Fetch Parent Table name
|
||||||
|
status, partitioned_table_name = self.conn.execute_scalar(
|
||||||
|
render_template(
|
||||||
|
"/".join([self.table_template_path, 'get_table.sql']),
|
||||||
|
conn=self.conn, scid=scid, tid=tid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=partitioned_table_name)
|
||||||
|
|
||||||
|
# Get schema oid of partition
|
||||||
|
status, pscid = self.conn.execute_scalar(
|
||||||
|
render_template("/".join([self.table_template_path,
|
||||||
|
'get_schema_oid.sql']), tid=ptid))
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=scid)
|
||||||
|
|
||||||
|
# Fetch schema name
|
||||||
|
status, partition_schema = self.conn.execute_scalar(
|
||||||
|
render_template("/".join([self.table_template_path,
|
||||||
|
'get_schema.sql']), conn=self.conn,
|
||||||
|
scid=pscid)
|
||||||
|
)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=partition_schema)
|
||||||
|
|
||||||
|
# Fetch Partition Table name
|
||||||
|
status, partition_name = self.conn.execute_scalar(
|
||||||
|
render_template(
|
||||||
|
"/".join([self.table_template_path, 'get_table.sql']),
|
||||||
|
conn=self.conn, scid=pscid, tid=ptid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=partition_name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
temp_data = dict()
|
||||||
|
temp_data['parent_schema'] = parent_schema
|
||||||
|
temp_data['partitioned_table_name'] = partitioned_table_name
|
||||||
|
temp_data['schema'] = partition_schema
|
||||||
|
temp_data['name'] = partition_name
|
||||||
|
|
||||||
|
SQL = render_template("/".join(
|
||||||
|
[self.partition_template_path, 'detach.sql']),
|
||||||
|
data=temp_data, conn=self.conn)
|
||||||
|
|
||||||
|
status, res = self.conn.execute_scalar(SQL)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
success=1,
|
||||||
|
info=gettext("Partition detached."),
|
||||||
|
data={
|
||||||
|
'id': ptid,
|
||||||
|
'scid': scid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@BaseTableView.check_precondition
|
||||||
|
def msql(self, gid, sid, did, scid, tid, ptid=None):
|
||||||
|
"""
|
||||||
|
This function will create modified sql for table object
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: Table ID
|
||||||
|
"""
|
||||||
|
data = dict()
|
||||||
|
for k, v in request.args.items():
|
||||||
|
try:
|
||||||
|
data[k] = json.loads(v, encoding='utf-8')
|
||||||
|
except (ValueError, TypeError, KeyError):
|
||||||
|
data[k] = v
|
||||||
|
|
||||||
|
if ptid is not None:
|
||||||
|
SQL = render_template("/".join([self.partition_template_path,
|
||||||
|
'properties.sql']),
|
||||||
|
did=did, scid=scid, tid=tid,
|
||||||
|
ptid=ptid, datlastsysoid=self.datlastsysoid)
|
||||||
|
status, res = self.conn.execute_dict(SQL)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
SQL, name = self.get_sql(did, scid, ptid, data, res)
|
||||||
|
SQL = re.sub('\n{2,}', '\n\n', SQL)
|
||||||
|
SQL = SQL.strip('\n')
|
||||||
|
if SQL == '':
|
||||||
|
SQL = "--modified SQL"
|
||||||
|
return make_json_response(
|
||||||
|
data=SQL,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@BaseTableView.check_precondition
|
||||||
|
def update(self, gid, sid, did, scid, tid, ptid):
|
||||||
|
"""
|
||||||
|
This function will update an existing table object
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: Table ID
|
||||||
|
ptid: Partition Table ID
|
||||||
|
"""
|
||||||
|
data = request.form if request.form else json.loads(
|
||||||
|
request.data, encoding='utf-8'
|
||||||
|
)
|
||||||
|
|
||||||
|
for k, v in data.items():
|
||||||
|
try:
|
||||||
|
data[k] = json.loads(v, encoding='utf-8')
|
||||||
|
except (ValueError, TypeError, KeyError):
|
||||||
|
data[k] = v
|
||||||
|
|
||||||
|
try:
|
||||||
|
SQL = render_template("/".join([self.partition_template_path,
|
||||||
|
'properties.sql']),
|
||||||
|
did=did, scid=scid, tid=tid,
|
||||||
|
ptid=ptid, datlastsysoid=self.datlastsysoid)
|
||||||
|
status, res = self.conn.execute_dict(SQL)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
return super(PartitionsView, self).update(
|
||||||
|
gid, sid, did, scid, ptid, data, res, parent_id=tid)
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@BaseTableView.check_precondition
|
||||||
|
def truncate(self, gid, sid, did, scid, tid, ptid):
|
||||||
|
"""
|
||||||
|
This function will truncate the table object
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
did: Database ID
|
||||||
|
scid: Schema ID
|
||||||
|
tid: Table ID
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
SQL = render_template("/".join([self.partition_template_path,
|
||||||
|
'properties.sql']),
|
||||||
|
did=did, scid=scid, tid=tid,
|
||||||
|
ptid=ptid, datlastsysoid=self.datlastsysoid)
|
||||||
|
status, res = self.conn.execute_dict(SQL)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
return super(PartitionsView, self).truncate(gid, sid, did, scid, ptid, res)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
PartitionsView.register_node_view(blueprint)
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@ define('pgadmin.node.rule', [
|
|||||||
node: 'rule',
|
node: 'rule',
|
||||||
label: gettext('Rules'),
|
label: gettext('Rules'),
|
||||||
type: 'coll-rule',
|
type: 'coll-rule',
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
columns: ["name", "owner", "comment"]
|
columns: ["name", "owner", "comment"]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -33,8 +34,9 @@ define('pgadmin.node.rule', [
|
|||||||
rule option in the context menu
|
rule option in the context menu
|
||||||
*/
|
*/
|
||||||
if (!pgBrowser.Nodes['rule']) {
|
if (!pgBrowser.Nodes['rule']) {
|
||||||
pgAdmin.Browser.Nodes['rule'] = pgAdmin.Browser.Node.extend({
|
pgAdmin.Browser.Nodes['rule'] = pgBrowser.Node.extend({
|
||||||
parent_type: ['table','view'],
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
|
parent_type: ['table','view', 'partition'],
|
||||||
type: 'rule',
|
type: 'rule',
|
||||||
sqlAlterHelp: 'sql-alterrule.html',
|
sqlAlterHelp: 'sql-alterrule.html',
|
||||||
sqlCreateHelp: 'sql-createrule.html',
|
sqlCreateHelp: 'sql-createrule.html',
|
||||||
@ -98,6 +100,12 @@ define('pgadmin.node.rule', [
|
|||||||
category: 'create', priority: 4, label: gettext('Rule...'),
|
category: 'create', priority: 4, label: gettext('Rule...'),
|
||||||
icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
|
icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
|
||||||
enable: 'canCreate'
|
enable: 'canCreate'
|
||||||
|
},{
|
||||||
|
name: 'create_rule', node: 'partition', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: gettext('Rule...'),
|
||||||
|
icon: 'wcTabIcon icon-rule', data: {action: 'create', check: true},
|
||||||
|
enable: 'canCreate'
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,323 @@
|
|||||||
|
define(
|
||||||
|
['sources/gettext', 'jquery', 'underscore', 'pgadmin.browser',
|
||||||
|
'backform','backgrid', 'pgadmin.browser.collection'],
|
||||||
|
function(gettext, $, _, pgBrowser, Backform, Backgrid) {
|
||||||
|
|
||||||
|
Backgrid.PartitionRow = Backgrid.Row.extend({
|
||||||
|
modelDuplicateColor: "lightYellow",
|
||||||
|
|
||||||
|
modelUniqueColor: "#fff",
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
Backgrid.Row.prototype.initialize.apply(this, arguments);
|
||||||
|
var self = this;
|
||||||
|
self.model.on("change:is_attach", function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
self.columns.each(function(col) {
|
||||||
|
if (col.get('name') == 'partition_name') {
|
||||||
|
var idx = self.columns.indexOf(col),
|
||||||
|
cf = col.get("cellFunction"),
|
||||||
|
cell = new (cf.apply(col, [self.model]))({
|
||||||
|
column: col,
|
||||||
|
model: self.model
|
||||||
|
}),
|
||||||
|
oldCell = self.cells[idx];
|
||||||
|
oldCell.remove();
|
||||||
|
self.cells[idx] = cell;
|
||||||
|
self.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
self.listenTo(self.model, 'pgadmin-session:model:duplicate', self.modelDuplicate);
|
||||||
|
self.listenTo(self.model, 'pgadmin-session:model:unique', self.modelUnique);
|
||||||
|
},
|
||||||
|
modelDuplicate: function() {
|
||||||
|
$(this.el).removeClass("new");
|
||||||
|
this.el.style.backgroundColor = this.modelDuplicateColor;
|
||||||
|
},
|
||||||
|
modelUnique: function() {
|
||||||
|
this.el.style.backgroundColor = this.modelUniqueColor;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var getPartitionCell = function(model) {
|
||||||
|
var is_attach = model.get("is_attach");
|
||||||
|
if (is_attach) {
|
||||||
|
var options = [];
|
||||||
|
model.set({'partition_name': undefined}, {silent:true});
|
||||||
|
_.each(model.top.table_options, function(t) {
|
||||||
|
options.push([t.label, t.value]);
|
||||||
|
});
|
||||||
|
return Backgrid.Extension.Select2Cell.extend({optionValues: options});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return Backgrid.StringCell;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Backform.PartitionKeyModel = pgBrowser.Node.Model.extend({
|
||||||
|
defaults: {
|
||||||
|
key_type: 'column',
|
||||||
|
pt_column: undefined,
|
||||||
|
expression: undefined
|
||||||
|
},
|
||||||
|
keys:['pt_column'],
|
||||||
|
schema: [{
|
||||||
|
id: 'key_type', label:'Key type', type:'select2', editable: true,
|
||||||
|
cell:'select2', cellHeaderClasses: 'width_percent_25',
|
||||||
|
select2: {allowClear: false},
|
||||||
|
options:[{
|
||||||
|
label: 'Column', value: 'column'
|
||||||
|
},{
|
||||||
|
label: 'Expression', value: 'expression'
|
||||||
|
}]
|
||||||
|
},{
|
||||||
|
id: 'pt_column', label: gettext('Column'), type:'text',
|
||||||
|
cell: Backgrid.Extension.Select2DepCell.extend({
|
||||||
|
keyPathAccessor: function(obj, path) {
|
||||||
|
var res = obj;
|
||||||
|
if(_.isArray(res)) {
|
||||||
|
return _.map(res, function(o) { return o['pt_column']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
path = path.split('.');
|
||||||
|
for (var i = 0; i < path.length; i++) {
|
||||||
|
if (_.isNull(res)) return null;
|
||||||
|
if (_.isEmpty(path[i])) continue;
|
||||||
|
if (!_.isUndefined(res[path[i]])) res = res[path[i]];
|
||||||
|
}
|
||||||
|
return _.isObject(res) && !_.isArray(res) ? null : res;
|
||||||
|
},
|
||||||
|
initialize: function() {
|
||||||
|
// Here we will decide if we need to call URL
|
||||||
|
// Or fetch the data from parent columns collection
|
||||||
|
var self = this;
|
||||||
|
if(this.model.handler) {
|
||||||
|
Backgrid.Extension.Select2DepCell.prototype.initialize.apply(this, arguments);
|
||||||
|
// Do not listen for any event(s) for existing constraint.
|
||||||
|
if (_.isUndefined(self.model.get('oid'))) {
|
||||||
|
var tableCols = self.model.top.get('columns');
|
||||||
|
self.listenTo(tableCols, 'remove' , self.resetColOptions);
|
||||||
|
self.listenTo(tableCols, 'change:name', self.resetColOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.custom_options();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetColOptions: function(m) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
self.custom_options();
|
||||||
|
self.render.apply(self);
|
||||||
|
}, 50);
|
||||||
|
},
|
||||||
|
custom_options: function() {
|
||||||
|
// We will add all the columns entered by user in table model
|
||||||
|
var columns = this.model.top.get('columns'),
|
||||||
|
added_columns_from_tables = [];
|
||||||
|
|
||||||
|
if (columns.length > 0) {
|
||||||
|
_.each(columns.models, function(m) {
|
||||||
|
var col = m.get('name');
|
||||||
|
if(!_.isUndefined(col) && !_.isNull(col)) {
|
||||||
|
added_columns_from_tables.push(
|
||||||
|
{label: col, value: col, image:'icon-column'}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Set the values in to options so that user can select
|
||||||
|
this.column.set('options', added_columns_from_tables);
|
||||||
|
},
|
||||||
|
remove: function() {
|
||||||
|
if(this.model.handler) {
|
||||||
|
var self = this,
|
||||||
|
tableCols = self.model.top.get('columns');
|
||||||
|
self.stopListening(tableCols, 'remove' , self.resetColOptions);
|
||||||
|
self.stopListening(tableCols, 'change:name' , self.resetColOptions);
|
||||||
|
Backgrid.Extension.Select2DepCell.prototype.remove.apply(this, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
deps: ['key_type'],
|
||||||
|
cellHeaderClasses: 'width_percent_30',
|
||||||
|
transform : function(data){
|
||||||
|
var res = [];
|
||||||
|
if (data && _.isArray(data)) {
|
||||||
|
_.each(data, function(d) {
|
||||||
|
res.push({label: d.label, value: d.label, image:'icon-column'});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
select2:{allowClear:false},
|
||||||
|
editable: function(m) {
|
||||||
|
if (m.get('key_type') == 'expression') {
|
||||||
|
setTimeout( function() {
|
||||||
|
m.set('pt_column', undefined);
|
||||||
|
}, 10);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'expression', label:'Expression', type:'text',
|
||||||
|
cell:Backgrid.Extension.StringDepCell,
|
||||||
|
cellHeaderClasses: 'width_percent_45',
|
||||||
|
deps: ['key_type'],
|
||||||
|
editable: function(m) {
|
||||||
|
if (m.get('key_type') == 'column') {
|
||||||
|
setTimeout( function() {
|
||||||
|
m.set('expression', undefined);
|
||||||
|
}, 10);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
validate: function(keys) {
|
||||||
|
var col_type = this.get('key_type'),
|
||||||
|
pt_column = this.get('pt_column'),
|
||||||
|
expression = this.get('expression');
|
||||||
|
|
||||||
|
// Have to clear existing validation before initiating current state
|
||||||
|
// validation only
|
||||||
|
this.errorModel.clear();
|
||||||
|
|
||||||
|
if (_.isUndefined(col_type) || _.isNull(col_type) ||
|
||||||
|
String(col_type).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = gettext('Partition key type cannot be empty.');
|
||||||
|
this.errorModel.set('key_type', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
else if (col_type == 'column' &&
|
||||||
|
_.isUndefined(pt_column) || _.isNull(pt_column) ||
|
||||||
|
String(pt_column).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = gettext('Partition key column cannot be empty.');
|
||||||
|
this.errorModel.set('pt_column', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
else if (col_type == 'expression' &&
|
||||||
|
_.isUndefined(expression) || _.isNull(expression) ||
|
||||||
|
String(expression).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = gettext('Partition key expression cannot be empty.');
|
||||||
|
this.errorModel.set('expression', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Backform.PartitionsModel = pgBrowser.Node.Model.extend({
|
||||||
|
defaults: {
|
||||||
|
oid: undefined,
|
||||||
|
is_attach: false,
|
||||||
|
partition_name: undefined,
|
||||||
|
values_from: undefined,
|
||||||
|
values_to: undefined,
|
||||||
|
values_in: undefined
|
||||||
|
},
|
||||||
|
keys:['partition_name'],
|
||||||
|
schema: [{
|
||||||
|
id: 'oid', label: gettext('OID'), type: 'text'
|
||||||
|
},{
|
||||||
|
id: 'is_attach', label:gettext('Operation'), cell: 'switch',
|
||||||
|
type: 'switch', options: { 'onText': 'Attach', 'offText': 'Create'},
|
||||||
|
cellHeaderClasses: 'width_percent_5',
|
||||||
|
editable: function(m) {
|
||||||
|
if (m instanceof Backbone.Model && m.isNew() && !m.top.isNew())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'partition_name', label: gettext('Name'), type: 'text', cell:'string',
|
||||||
|
cellHeaderClasses: 'width_percent_25',
|
||||||
|
editable: function(m) {
|
||||||
|
if (m instanceof Backbone.Model && m.isNew())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}, cellFunction: getPartitionCell
|
||||||
|
},{
|
||||||
|
id: 'values_from', label:'From', type:'text',
|
||||||
|
cell:Backgrid.Extension.StringDepCell,
|
||||||
|
cellHeaderClasses: 'width_percent_20',
|
||||||
|
editable: function(m) {
|
||||||
|
if(m.handler && m.handler.top &&
|
||||||
|
m.handler.top.attributes &&
|
||||||
|
m.handler.top.attributes.partition_type == 'range' &&
|
||||||
|
m instanceof Backbone.Model && m.isNew())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'values_to', label:'To', type:'text',
|
||||||
|
cell:Backgrid.Extension.StringDepCell,
|
||||||
|
cellHeaderClasses: 'width_percent_20',
|
||||||
|
editable: function(m) {
|
||||||
|
if(m.handler && m.handler.top &&
|
||||||
|
m.handler.top.attributes &&
|
||||||
|
m.handler.top.attributes.partition_type == 'range' &&
|
||||||
|
m instanceof Backbone.Model && m.isNew())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'values_in', label:'In', type:'text',
|
||||||
|
cell:Backgrid.Extension.StringDepCell,
|
||||||
|
cellHeaderClasses: 'width_percent_25',
|
||||||
|
editable: function(m) {
|
||||||
|
if(m.handler && m.handler.top &&
|
||||||
|
m.handler.top.attributes &&
|
||||||
|
m.handler.top.attributes.partition_type == 'list' &&
|
||||||
|
m instanceof Backbone.Model && m.isNew())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
validate: function(keys) {
|
||||||
|
var partition_name = this.get('partition_name'),
|
||||||
|
values_from = this.get('values_from'),
|
||||||
|
values_to = this.get('values_to'),
|
||||||
|
values_in = this.get('values_in');
|
||||||
|
|
||||||
|
// Have to clear existing validation before initiating current state
|
||||||
|
// validation only
|
||||||
|
this.errorModel.clear();
|
||||||
|
|
||||||
|
if (_.isUndefined(partition_name) || _.isNull(partition_name) ||
|
||||||
|
String(partition_name).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = gettext('Partition name cannot be empty.');
|
||||||
|
this.errorModel.set('partition_name', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.top.get('partition_type') == 'range') {
|
||||||
|
if (_.isUndefined(values_from) || _.isNull(values_from) ||
|
||||||
|
String(values_from).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = gettext('For range partition From field cannot be empty.');
|
||||||
|
this.errorModel.set('values_from', msg);
|
||||||
|
return msg;
|
||||||
|
} else if (_.isUndefined(values_to) || _.isNull(values_to) ||
|
||||||
|
String(values_to).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = gettext('For range partition To field cannot be empty.');
|
||||||
|
this.errorModel.set('values_to', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
} else if (this.top.get('partition_type') == 'list') {
|
||||||
|
if (_.isUndefined(values_in) || _.isNull(values_in) ||
|
||||||
|
String(values_in).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
msg = gettext('For list partition In field cannot be empty.');
|
||||||
|
this.errorModel.set('values_in', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -3,8 +3,10 @@ define('pgadmin.node.table', [
|
|||||||
'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
|
'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify',
|
||||||
'sources/alerts/alertify_wrapper',
|
'sources/alerts/alertify_wrapper',
|
||||||
'pgadmin.browser.collection', 'pgadmin.node.column',
|
'pgadmin.browser.collection', 'pgadmin.node.column',
|
||||||
'pgadmin.node.constraints'
|
'pgadmin.node.constraints', 'pgadmin.browser.table.partition.utils'
|
||||||
], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify, AlertifyWrapper) {
|
], function(
|
||||||
|
gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify, AlertifyWrapper
|
||||||
|
) {
|
||||||
|
|
||||||
if (!pgBrowser.Nodes['coll-table']) {
|
if (!pgBrowser.Nodes['coll-table']) {
|
||||||
var databases = pgBrowser.Nodes['coll-table'] =
|
var databases = pgBrowser.Nodes['coll-table'] =
|
||||||
@ -12,7 +14,7 @@ define('pgadmin.node.table', [
|
|||||||
node: 'table',
|
node: 'table',
|
||||||
label: gettext('Tables'),
|
label: gettext('Tables'),
|
||||||
type: 'coll-table',
|
type: 'coll-table',
|
||||||
columns: ['name', 'relowner', 'description'],
|
columns: ['name', 'relowner', 'is_partitioned', 'description'],
|
||||||
hasStatistics: true,
|
hasStatistics: true,
|
||||||
statsPrettifyFields: ['Size', 'Indexes size', 'Table size',
|
statsPrettifyFields: ['Size', 'Indexes size', 'Table size',
|
||||||
'Toast table size', 'Tuple length',
|
'Toast table size', 'Tuple length',
|
||||||
@ -22,6 +24,7 @@ define('pgadmin.node.table', [
|
|||||||
|
|
||||||
if (!pgBrowser.Nodes['table']) {
|
if (!pgBrowser.Nodes['table']) {
|
||||||
pgBrowser.Nodes['table'] = pgBrowser.Node.extend({
|
pgBrowser.Nodes['table'] = pgBrowser.Node.extend({
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
type: 'table',
|
type: 'table',
|
||||||
label: gettext('Table'),
|
label: gettext('Table'),
|
||||||
collection_type: 'coll-table',
|
collection_type: 'coll-table',
|
||||||
@ -39,7 +42,7 @@ define('pgadmin.node.table', [
|
|||||||
height: '95%',
|
height: '95%',
|
||||||
width: '85%',
|
width: '85%',
|
||||||
Init: function() {
|
Init: function() {
|
||||||
/* Avoid mulitple registration of menus */
|
/* Avoid multiple registration of menus */
|
||||||
if (this.initialized)
|
if (this.initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -91,6 +94,9 @@ define('pgadmin.node.table', [
|
|||||||
icon: 'fa fa-bar-chart', enable : 'canCreate'
|
icon: 'fa fa-bar-chart', enable : 'canCreate'
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
pgBrowser.Events.on(
|
||||||
|
'pgadmin:browser:node:table:updated', this.onTableUpdated, this
|
||||||
|
);
|
||||||
},
|
},
|
||||||
canDrop: pgBrowser.Nodes['schema'].canChildDrop,
|
canDrop: pgBrowser.Nodes['schema'].canChildDrop,
|
||||||
canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
|
canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
|
||||||
@ -284,7 +290,10 @@ define('pgadmin.node.table', [
|
|||||||
hastoasttable: true,
|
hastoasttable: true,
|
||||||
toast_autovacuum_enabled: false,
|
toast_autovacuum_enabled: false,
|
||||||
autovacuum_enabled: false,
|
autovacuum_enabled: false,
|
||||||
primary_key: []
|
primary_key: [],
|
||||||
|
partitions: [],
|
||||||
|
partition_type: 'range',
|
||||||
|
is_partitioned: false
|
||||||
},
|
},
|
||||||
// Default values!
|
// Default values!
|
||||||
initialize: function(attrs, args) {
|
initialize: function(attrs, args) {
|
||||||
@ -329,13 +338,38 @@ define('pgadmin.node.table', [
|
|||||||
// If tablespace name is not "pg_global" then we need to exclude them
|
// If tablespace name is not "pg_global" then we need to exclude them
|
||||||
return (!(d && d.label.match(/pg_global/)))
|
return (!(d && d.label.match(/pg_global/)))
|
||||||
}
|
}
|
||||||
|
},{
|
||||||
|
id: 'partition', type: 'group', label: gettext('Partition'),
|
||||||
|
mode: ['edit', 'create'], min_version: 100000,
|
||||||
|
visible: function(m) {
|
||||||
|
// Always show in case of create mode
|
||||||
|
if (m.isNew() || m.get('is_partitioned'))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'is_partitioned', label:gettext('Partitioned Table?'), cell: 'switch',
|
||||||
|
type: 'switch', mode: ['properties', 'create', 'edit'],
|
||||||
|
visible: function(m) {
|
||||||
|
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||||
|
&& !_.isUndefined(m.node_info.server.version) &&
|
||||||
|
m.node_info.server.version >= 100000)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
disabled: function(m) {
|
||||||
|
if (!m.isNew())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},{
|
},{
|
||||||
id: 'description', label: gettext('Comment'), type: 'multiline',
|
id: 'description', label: gettext('Comment'), type: 'multiline',
|
||||||
mode: ['properties', 'create', 'edit'], disabled: 'inSchema'
|
mode: ['properties', 'create', 'edit'], disabled: 'inSchema'
|
||||||
},{
|
},{
|
||||||
id: 'coll_inherits', label: gettext('Inherited from table(s)'),
|
id: 'coll_inherits', label: gettext('Inherited from table(s)'),
|
||||||
url: 'get_inherits', type: 'array', group: gettext('Columns'),
|
url: 'get_inherits', type: 'array', group: gettext('Columns'),
|
||||||
disabled: 'checkInheritance', deps: ['typname'],
|
disabled: 'checkInheritance', deps: ['typname', 'is_partitioned'],
|
||||||
mode: ['create', 'edit'],
|
mode: ['create', 'edit'],
|
||||||
select2: { multiple: true, allowClear: true,
|
select2: { multiple: true, allowClear: true,
|
||||||
placeholder: gettext('Select to inherit from...')},
|
placeholder: gettext('Select to inherit from...')},
|
||||||
@ -432,7 +466,26 @@ define('pgadmin.node.table', [
|
|||||||
model: pgBrowser.Nodes['column'].model,
|
model: pgBrowser.Nodes['column'].model,
|
||||||
subnode: pgBrowser.Nodes['column'].model,
|
subnode: pgBrowser.Nodes['column'].model,
|
||||||
mode: ['create', 'edit'],
|
mode: ['create', 'edit'],
|
||||||
disabled: 'inSchema', deps: ['typname'],
|
disabled: function(m) {
|
||||||
|
// In case of partitioned table remove inherited columns
|
||||||
|
if (m.isNew() && m.get('is_partitioned')) {
|
||||||
|
setTimeout(function() {
|
||||||
|
var coll = m.get('columns');
|
||||||
|
coll.remove(coll.filter(function(model) {
|
||||||
|
if (_.isUndefined(model.get('inheritedfrom')))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.node_info && 'catalog' in this.node_info)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
deps: ['typname', 'is_partitioned'],
|
||||||
canAdd: 'check_grid_add_condition',
|
canAdd: 'check_grid_add_condition',
|
||||||
canEdit: true, canDelete: true,
|
canEdit: true, canDelete: true,
|
||||||
// For each row edit/delete button enable/disable
|
// For each row edit/delete button enable/disable
|
||||||
@ -535,10 +588,22 @@ define('pgadmin.node.table', [
|
|||||||
subnode: pgBrowser.Nodes['primary_key'].model,
|
subnode: pgBrowser.Nodes['primary_key'].model,
|
||||||
editable: false, type: 'collection',
|
editable: false, type: 'collection',
|
||||||
group: gettext('Primary Key'), mode: ['edit', 'create'],
|
group: gettext('Primary Key'), mode: ['edit', 'create'],
|
||||||
canEdit: true, canDelete: true,
|
canEdit: true, canDelete: true, deps:['is_partitioned'],
|
||||||
control: 'unique-col-collection',
|
control: 'unique-col-collection',
|
||||||
columns : ['name', 'columns'],
|
columns : ['name', 'columns'],
|
||||||
canAdd: true,
|
canAdd: function(m) {
|
||||||
|
if (m.get('is_partitioned')) {
|
||||||
|
setTimeout(function() {
|
||||||
|
var coll = m.get('primary_key');
|
||||||
|
coll.remove(coll.filter(function(model) {
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}, 10);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
canAddRow: function(m) {
|
canAddRow: function(m) {
|
||||||
// User can only add one primary key
|
// User can only add one primary key
|
||||||
var columns = m.get('columns');
|
var columns = m.get('columns');
|
||||||
@ -553,9 +618,21 @@ define('pgadmin.node.table', [
|
|||||||
subnode: pgBrowser.Nodes['foreign_key'].model,
|
subnode: pgBrowser.Nodes['foreign_key'].model,
|
||||||
editable: false, type: 'collection',
|
editable: false, type: 'collection',
|
||||||
group: gettext('Foreign Key'), mode: ['edit', 'create'],
|
group: gettext('Foreign Key'), mode: ['edit', 'create'],
|
||||||
canEdit: true, canDelete: true,
|
canEdit: true, canDelete: true, deps:['is_partitioned'],
|
||||||
control: 'unique-col-collection',
|
control: 'unique-col-collection',
|
||||||
canAdd: true,
|
canAdd: function(m) {
|
||||||
|
if (m.get('is_partitioned')) {
|
||||||
|
setTimeout(function() {
|
||||||
|
var coll = m.get('foreign_key');
|
||||||
|
coll.remove(coll.filter(function(model) {
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}, 10);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
columns : ['name', 'columns'],
|
columns : ['name', 'columns'],
|
||||||
canAddRow: function(m) {
|
canAddRow: function(m) {
|
||||||
// User can only add if there is at least one column with name.
|
// User can only add if there is at least one column with name.
|
||||||
@ -568,7 +645,7 @@ define('pgadmin.node.table', [
|
|||||||
subnode: pgBrowser.Nodes['check_constraints'].model,
|
subnode: pgBrowser.Nodes['check_constraints'].model,
|
||||||
editable: false, type: 'collection',
|
editable: false, type: 'collection',
|
||||||
group: gettext('Check'), mode: ['edit', 'create'],
|
group: gettext('Check'), mode: ['edit', 'create'],
|
||||||
canEdit: true, canDelete: true,
|
canEdit: true, canDelete: true, deps:['is_partitioned'],
|
||||||
control: 'unique-col-collection',
|
control: 'unique-col-collection',
|
||||||
canAdd: true,
|
canAdd: true,
|
||||||
columns : ['name', 'consrc']
|
columns : ['name', 'consrc']
|
||||||
@ -578,10 +655,22 @@ define('pgadmin.node.table', [
|
|||||||
subnode: pgBrowser.Nodes['unique_constraint'].model,
|
subnode: pgBrowser.Nodes['unique_constraint'].model,
|
||||||
editable: false, type: 'collection',
|
editable: false, type: 'collection',
|
||||||
group: gettext('Unique'), mode: ['edit', 'create'],
|
group: gettext('Unique'), mode: ['edit', 'create'],
|
||||||
canEdit: true, canDelete: true,
|
canEdit: true, canDelete: true, deps:['is_partitioned'],
|
||||||
control: 'unique-col-collection',
|
control: 'unique-col-collection',
|
||||||
columns : ['name', 'columns'],
|
columns : ['name', 'columns'],
|
||||||
canAdd: true,
|
canAdd: function(m) {
|
||||||
|
if (m.get('is_partitioned')) {
|
||||||
|
setTimeout(function() {
|
||||||
|
var coll = m.get('unique_constraint');
|
||||||
|
coll.remove(coll.filter(function(model) {
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}, 10);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
canAddRow: function(m) {
|
canAddRow: function(m) {
|
||||||
// User can only add if there is at least one column with name.
|
// User can only add if there is at least one column with name.
|
||||||
var columns = m.get('columns');
|
var columns = m.get('columns');
|
||||||
@ -593,10 +682,22 @@ define('pgadmin.node.table', [
|
|||||||
subnode: pgBrowser.Nodes['exclusion_constraint'].model,
|
subnode: pgBrowser.Nodes['exclusion_constraint'].model,
|
||||||
editable: false, type: 'collection',
|
editable: false, type: 'collection',
|
||||||
group: gettext('Exclude'), mode: ['edit', 'create'],
|
group: gettext('Exclude'), mode: ['edit', 'create'],
|
||||||
canEdit: true, canDelete: true,
|
canEdit: true, canDelete: true, deps:['is_partitioned'],
|
||||||
control: 'unique-col-collection',
|
control: 'unique-col-collection',
|
||||||
columns : ['name', 'columns', 'constraint'],
|
columns : ['name', 'columns', 'constraint'],
|
||||||
canAdd: true,
|
canAdd: function(m) {
|
||||||
|
if (m.get('is_partitioned')) {
|
||||||
|
setTimeout(function() {
|
||||||
|
var coll = m.get('exclude_constraint');
|
||||||
|
coll.remove(coll.filter(function(model) {
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}, 10);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
canAddRow: function(m) {
|
canAddRow: function(m) {
|
||||||
// User can only add if there is at least one column with name.
|
// User can only add if there is at least one column with name.
|
||||||
var columns = m.get('columns');
|
var columns = m.get('columns');
|
||||||
@ -607,7 +708,7 @@ define('pgadmin.node.table', [
|
|||||||
id: 'typname', label: gettext('Of type'), type: 'text',
|
id: 'typname', label: gettext('Of type'), type: 'text',
|
||||||
control: 'node-ajax-options', mode: ['properties', 'create', 'edit'],
|
control: 'node-ajax-options', mode: ['properties', 'create', 'edit'],
|
||||||
disabled: 'checkOfType', url: 'get_oftype', group: gettext('Advanced'),
|
disabled: 'checkOfType', url: 'get_oftype', group: gettext('Advanced'),
|
||||||
deps: ['coll_inherits'], transform: function(data, cell) {
|
deps: ['coll_inherits', 'is_partitioned'], transform: function(data, cell) {
|
||||||
var control = cell || this,
|
var control = cell || this,
|
||||||
m = control.model;
|
m = control.model;
|
||||||
m.of_types_tables = data;
|
m.of_types_tables = data;
|
||||||
@ -710,11 +811,217 @@ define('pgadmin.node.table', [
|
|||||||
type: 'switch', mode: ['create', 'edit'], deps: ['typname'],
|
type: 'switch', mode: ['create', 'edit'], deps: ['typname'],
|
||||||
disabled: 'isLikeDisable', group: gettext('Like')
|
disabled: 'isLikeDisable', group: gettext('Like')
|
||||||
}]
|
}]
|
||||||
|
},{
|
||||||
|
id: 'partition_type', label:gettext('Partition Type'),
|
||||||
|
editable: false, type: 'select2', select2: {allowClear: false},
|
||||||
|
group: 'partition', deps: ['is_partitioned'],
|
||||||
|
options:[{
|
||||||
|
label: 'Range', value: 'range'
|
||||||
|
},{
|
||||||
|
label: 'List', value: 'list'
|
||||||
|
}],
|
||||||
|
mode:['create'],
|
||||||
|
visible: function(m) {
|
||||||
|
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||||
|
&& !_.isUndefined(m.node_info.server.version) &&
|
||||||
|
m.node_info.server.version >= 100000)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
disabled: function(m) {
|
||||||
|
if (!m.isNew() || !m.get('is_partitioned'))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'partition_keys', label:gettext('Partition Keys'),
|
||||||
|
model: Backform.PartitionKeyModel,
|
||||||
|
subnode: Backform.PartitionKeyModel,
|
||||||
|
editable: true, type: 'collection',
|
||||||
|
group: 'partition', mode: ['create'],
|
||||||
|
deps: ['is_partitioned', 'partition_type'],
|
||||||
|
canEdit: false, canDelete: true,
|
||||||
|
control: 'sub-node-collection',
|
||||||
|
canAdd: function(m) {
|
||||||
|
if (m.isNew() && m.get('is_partitioned'))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
canAddRow: function(m) {
|
||||||
|
var columns = m.get('columns');
|
||||||
|
var max_row_count = 1000;
|
||||||
|
|
||||||
|
if (m.get('partition_type') && m.get('partition_type') == 'list')
|
||||||
|
max_row_count = 1;
|
||||||
|
|
||||||
|
return (m.get('partition_keys') &&
|
||||||
|
m.get('partition_keys').length < max_row_count &&
|
||||||
|
_.some(columns.pluck('name'))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
visible: function(m) {
|
||||||
|
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||||
|
&& !_.isUndefined(m.node_info.server.version) &&
|
||||||
|
m.node_info.server.version >= 100000)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
disabled: function(m) {
|
||||||
|
if (m.get('partition_keys') && m.get('partition_keys').models.length > 0) {
|
||||||
|
setTimeout(function () {
|
||||||
|
var coll = m.get('partition_keys');
|
||||||
|
coll.remove(coll.filter(function(model) {
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'partition_scheme', label: gettext('Partition Scheme'),
|
||||||
|
type: 'note', group: 'partition', mode: ['edit'],
|
||||||
|
visible: function(m) {
|
||||||
|
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||||
|
&& !_.isUndefined(m.node_info.server.version) &&
|
||||||
|
m.node_info.server.version >= 100000)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
disabled: function(m) {
|
||||||
|
if (!m.isNew()) {
|
||||||
|
this.text = m.get('partition_scheme');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'partition_key_note', label: gettext('Partition Keys'),
|
||||||
|
type: 'note', group: 'partition', mode: ['create'],
|
||||||
|
text: [
|
||||||
|
'<br> ',
|
||||||
|
gettext('Partition table supports two types of keys:'),
|
||||||
|
'<br><ul><li>',
|
||||||
|
gettext('Column: User can select any column from the list of available columns.'),
|
||||||
|
'</li><li>',
|
||||||
|
gettext('Expression: User can specify expression to create partition key.'),
|
||||||
|
'<br><p>',
|
||||||
|
gettext('Example'),
|
||||||
|
':',
|
||||||
|
gettext("Let's say, we want to create a partition table based per year for the column 'saledate', having datatype 'date/timestamp', then we need to specify the expression as 'extract(YEAR from saledate)' as partition key."),
|
||||||
|
'</p></li></ul>'
|
||||||
|
].join(''),
|
||||||
|
visible: function(m) {
|
||||||
|
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||||
|
&& !_.isUndefined(m.node_info.server.version) &&
|
||||||
|
m.node_info.server.version >= 100000)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: 'partitions', label:gettext('Partitions'),
|
||||||
|
model: Backform.PartitionsModel,
|
||||||
|
subnode: Backform.PartitionsModel,
|
||||||
|
editable: true, type: 'collection',
|
||||||
|
group: 'partition', mode: ['edit', 'create'],
|
||||||
|
deps: ['is_partitioned', 'partition_type'],
|
||||||
|
canEdit: false, canDelete: true,
|
||||||
|
customDeleteTitle: gettext('Detach Partition'),
|
||||||
|
customDeleteMsg: gettext('Are you sure you wish to detach this partition?'),
|
||||||
|
columns:['is_attach', 'partition_name', 'values_from', 'values_to', 'values_in'],
|
||||||
|
control: Backform.SubNodeCollectionControl.extend({
|
||||||
|
row: Backgrid.PartitionRow,
|
||||||
|
initialize: function() {
|
||||||
|
Backform.SubNodeCollectionControl.prototype.initialize.apply(this, arguments);
|
||||||
|
var self = this;
|
||||||
|
if (!this.model.isNew()) {
|
||||||
|
var node = this.field.get('schema_node'),
|
||||||
|
node_info = this.field.get('node_info');
|
||||||
|
|
||||||
|
// Make ajax call to get the tables to be attached
|
||||||
|
$.ajax({
|
||||||
|
url: node.generate_url.apply(
|
||||||
|
node, [
|
||||||
|
null, 'get_attach_tables', this.field.get('node_data'),
|
||||||
|
true, node_info
|
||||||
|
]),
|
||||||
|
|
||||||
|
type: 'GET',
|
||||||
|
async: false,
|
||||||
|
success: function(res) {
|
||||||
|
if (res.success == 1) {
|
||||||
|
self.model.table_options = res.data;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alertify.alert(
|
||||||
|
'Error fetching tables to be attached', res.data.result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
var errmsg = $.parseJSON(e.responseText);
|
||||||
|
alertify.alert('Error fetching tables to be attached.', errmsg.errormsg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
canAdd: function(m) {
|
||||||
|
if (m.get('is_partitioned'))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
visible: function(m) {
|
||||||
|
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||||
|
&& !_.isUndefined(m.node_info.server.version) &&
|
||||||
|
m.node_info.server.version >= 100000)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
disabled: function(m) {
|
||||||
|
if (m.isNew() && m.get('partitions') && m.get('partitions').models.length > 0) {
|
||||||
|
setTimeout(function () {
|
||||||
|
var coll = m.get('partitions');
|
||||||
|
coll.remove(coll.filter(function(model) {
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
id: 'partition_note', label: gettext('Partitions'),
|
||||||
|
type: 'note', group: 'partition',
|
||||||
|
text: [
|
||||||
|
'<ul>',
|
||||||
|
' <li>',
|
||||||
|
gettext('Create a table: User can create multiple partitions while creating new partitioned table. Operation switch is disabled in this scenario.'),
|
||||||
|
'</li><li>',
|
||||||
|
gettext('Edit existing table: User can create/attach/detach multiple partitions. In attach operation user can select table from the list of suitable tables to be attached.'),
|
||||||
|
'</li><li>',
|
||||||
|
gettext('From/To/In input: Values for these fields must be quoted with single quote. For more than one partition key values must be comma(,) separated.'),
|
||||||
|
'<br>',
|
||||||
|
gettext('Example'),
|
||||||
|
':<ul><li>',
|
||||||
|
gettext("From/To: Enabled for range partition. Consider partitioned table with multiple keys of type Integer, then values should be specified like '100','200'."),
|
||||||
|
'</li><li> ',
|
||||||
|
gettext('In: Enabled for list partition. Values must be comma(,) separated and quoted with single quote.'),
|
||||||
|
'</li></ul></li></ul>'
|
||||||
|
].join(''),
|
||||||
|
visible: function(m) {
|
||||||
|
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
|
||||||
|
&& !_.isUndefined(m.node_info.server.version) &&
|
||||||
|
m.node_info.server.version >= 100000)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},{
|
},{
|
||||||
// Here - we will create tab control for storage parameters
|
// Here - we will create tab control for storage parameters
|
||||||
// (auto vacuum).
|
// (auto vacuum).
|
||||||
type: 'nested', control: 'tab', group: gettext('Parameter'),
|
type: 'nested', control: 'tab', group: gettext('Parameter'),
|
||||||
mode: ['edit', 'create'],
|
mode: ['edit', 'create'], deps: ['is_partitioned'],
|
||||||
schema: Backform.VacuumSettingsSchema
|
schema: Backform.VacuumSettingsSchema
|
||||||
},{
|
},{
|
||||||
id: 'relacl_str', label: gettext('Privileges'), disabled: 'inSchema',
|
id: 'relacl_str', label: gettext('Privileges'), disabled: 'inSchema',
|
||||||
@ -741,35 +1048,46 @@ define('pgadmin.node.table', [
|
|||||||
msg = undefined,
|
msg = undefined,
|
||||||
name = this.get('name'),
|
name = this.get('name'),
|
||||||
schema = this.get('schema'),
|
schema = this.get('schema'),
|
||||||
relowner = this.get('relowner');
|
relowner = this.get('relowner'),
|
||||||
|
is_partitioned = this.get('is_partitioned'),
|
||||||
|
partition_keys = this.get('partition_keys');
|
||||||
|
|
||||||
// If nothing to validate or VacuumSetting keys then
|
if (
|
||||||
// return from here
|
_.isUndefined(name) || _.isNull(name) ||
|
||||||
if ( keys && (keys.length == 0
|
String(name).replace(/^\s+|\s+$/g, '') == ''
|
||||||
|| _.indexOf(keys, 'autovacuum_enabled') != -1
|
) {
|
||||||
|| _.indexOf(keys, 'toast_autovacuum_enabled') != -1) ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Have to clear existing validation before initiating current state validation only
|
|
||||||
this.errorModel.clear();
|
|
||||||
|
|
||||||
if (_.isUndefined(name) || _.isNull(name) ||
|
|
||||||
String(name).replace(/^\s+|\s+$/g, '') == '') {
|
|
||||||
msg = gettext('Table name cannot be empty.');
|
msg = gettext('Table name cannot be empty.');
|
||||||
this.errorModel.set('name', msg);
|
this.errorModel.set('name', msg);
|
||||||
return msg;
|
return msg;
|
||||||
} else if (_.isUndefined(schema) || _.isNull(schema) ||
|
}
|
||||||
String(schema).replace(/^\s+|\s+$/g, '') == '') {
|
this.errorModel.unset('name');
|
||||||
|
if (
|
||||||
|
_.isUndefined(schema) || _.isNull(schema) ||
|
||||||
|
String(schema).replace(/^\s+|\s+$/g, '') == ''
|
||||||
|
) {
|
||||||
msg = gettext('Table schema cannot be empty.');
|
msg = gettext('Table schema cannot be empty.');
|
||||||
this.errorModel.set('schema', msg);
|
this.errorModel.set('schema', msg);
|
||||||
return msg;
|
return msg;
|
||||||
} else if (_.isUndefined(relowner) || _.isNull(relowner) ||
|
}
|
||||||
String(relowner).replace(/^\s+|\s+$/g, '') == '') {
|
this.errorModel.unset('schema');
|
||||||
|
if (
|
||||||
|
_.isUndefined(relowner) || _.isNull(relowner) ||
|
||||||
|
String(relowner).replace(/^\s+|\s+$/g, '') == ''
|
||||||
|
) {
|
||||||
msg = gettext('Table owner cannot be empty.');
|
msg = gettext('Table owner cannot be empty.');
|
||||||
this.errorModel.set('relowner', msg);
|
this.errorModel.set('relowner', msg);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
this.errorModel.unset('relowner');
|
||||||
|
if (
|
||||||
|
is_partitioned && this.isNew() &&
|
||||||
|
!_.isNull(partition_keys) && partition_keys.length <= 0
|
||||||
|
) {
|
||||||
|
msg = gettext('Please specify at least one key for partitioned table.');
|
||||||
|
this.errorModel.set('partition_keys', msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
this.errorModel.unset('partition_keys');
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
// We will disable everything if we are under catalog node
|
// We will disable everything if we are under catalog node
|
||||||
@ -797,7 +1115,15 @@ define('pgadmin.node.table', [
|
|||||||
},
|
},
|
||||||
// Oftype is defined?
|
// Oftype is defined?
|
||||||
checkInheritance: function(m) {
|
checkInheritance: function(m) {
|
||||||
// coll_inherits || typname
|
// Disabled if it is partitioned table
|
||||||
|
if (m.get('is_partitioned')) {
|
||||||
|
setTimeout( function() {
|
||||||
|
m.set('coll_inherits', []);
|
||||||
|
}, 10);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// coll_inherits || typname
|
||||||
if(!m.inSchema.apply(this, [m]) &&
|
if(!m.inSchema.apply(this, [m]) &&
|
||||||
( _.isUndefined(m.get('typname')) ||
|
( _.isUndefined(m.get('typname')) ||
|
||||||
_.isNull(m.get('typname')) ||
|
_.isNull(m.get('typname')) ||
|
||||||
@ -841,7 +1167,15 @@ define('pgadmin.node.table', [
|
|||||||
},
|
},
|
||||||
// We will disable it if Inheritance is defined
|
// We will disable it if Inheritance is defined
|
||||||
checkOfType: function(m) {
|
checkOfType: function(m) {
|
||||||
//coll_inherits || typname
|
// Disabled if it is partitioned table
|
||||||
|
if (m.get('is_partitioned')) {
|
||||||
|
setTimeout( function() {
|
||||||
|
m.set('typname', undefined);
|
||||||
|
}, 10);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//coll_inherits || typname
|
||||||
if(!m.inSchemaWithModelCheck.apply(this, [m]) &&
|
if(!m.inSchemaWithModelCheck.apply(this, [m]) &&
|
||||||
(_.isUndefined(m.get('coll_inherits')) ||
|
(_.isUndefined(m.get('coll_inherits')) ||
|
||||||
_.isNull(m.get('coll_inherits')) ||
|
_.isNull(m.get('coll_inherits')) ||
|
||||||
@ -967,6 +1301,116 @@ define('pgadmin.node.table', [
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onTableUpdated: function(_node, _oldNodeData, _newNodeData) {
|
||||||
|
if (
|
||||||
|
_newNodeData.is_partitioned && 'affected_partitions' in _newNodeData
|
||||||
|
) {
|
||||||
|
var partitions = _newNodeData.affected_partitions,
|
||||||
|
idx, node_info, self = this,
|
||||||
|
newPartitionsIDs = [],
|
||||||
|
insertChildTreeNodes = [],
|
||||||
|
insertChildrenNodes = function() {
|
||||||
|
if (!insertChildTreeNodes.length)
|
||||||
|
return;
|
||||||
|
var option = insertChildTreeNodes.pop();
|
||||||
|
pgBrowser.addChildTreeNodes(
|
||||||
|
option.treeHierarchy, option.parent, option.type,
|
||||||
|
option.childrenIDs, insertChildrenNodes
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ('detached' in partitions && partitions.detached.length > 0) {
|
||||||
|
// Remove it from the partition collections node first
|
||||||
|
pgBrowser.removeChildTreeNodesById(
|
||||||
|
_node, 'coll-partition', _.map(
|
||||||
|
partitions.detached, function(_d) { return parseInt(_d.oid); }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var schemaNode = pgBrowser.findParentTreeNodeByType(
|
||||||
|
_node, 'schema'
|
||||||
|
),
|
||||||
|
detachedBySchema = _.groupBy(
|
||||||
|
partitions.detached,
|
||||||
|
function(_d) { return parseInt(_d.schema_id); }
|
||||||
|
), childIDs;
|
||||||
|
|
||||||
|
for (var key in detachedBySchema) {
|
||||||
|
schemaNode = pgBrowser.findSiblingTreeNode(schemaNode, key);
|
||||||
|
|
||||||
|
if (schemaNode) {
|
||||||
|
childIDs = _.map(
|
||||||
|
detachedBySchema[key],
|
||||||
|
function(_d) { return parseInt(_d.oid); }
|
||||||
|
);
|
||||||
|
|
||||||
|
var tablesCollNode = pgBrowser.findChildCollectionTreeNode(
|
||||||
|
schemaNode, 'coll-table'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tablesCollNode) {
|
||||||
|
insertChildTreeNodes.push({
|
||||||
|
'parent': tablesCollNode,
|
||||||
|
'type': 'table',
|
||||||
|
'treeHierarchy': pgAdmin.Browser.Nodes.schema.getTreeNodeHierarchy(schemaNode),
|
||||||
|
'childrenIDs': _.clone(childIDs)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('attached' in partitions && partitions.attached.length > 0) {
|
||||||
|
var schemaNode = pgBrowser.findParentTreeNodeByType(
|
||||||
|
_node, 'schema'
|
||||||
|
),
|
||||||
|
attachedBySchema = _.groupBy(
|
||||||
|
partitions.attached,
|
||||||
|
function(_d) { return parseInt(_d.schema_id); }
|
||||||
|
), childIDs;
|
||||||
|
|
||||||
|
for (var key in attachedBySchema) {
|
||||||
|
schemaNode = pgBrowser.findSiblingTreeNode(schemaNode, key);
|
||||||
|
|
||||||
|
if (schemaNode) {
|
||||||
|
childIDs = _.map(
|
||||||
|
attachedBySchema[key],
|
||||||
|
function(_d) { return parseInt(_d.oid); }
|
||||||
|
);
|
||||||
|
// Remove it from the table collections node first
|
||||||
|
pgBrowser.removeChildTreeNodesById(
|
||||||
|
schemaNode, 'coll-table', childIDs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
newPartitionsIDs = newPartitionsIDs.concat(childIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('created' in partitions && partitions.created.length > 0) {
|
||||||
|
_.each(partitions.created, function(_data) {
|
||||||
|
newPartitionsIDs.push(_data.oid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPartitionsIDs.length) {
|
||||||
|
node_info = self.getTreeNodeHierarchy(_node);
|
||||||
|
|
||||||
|
var partitionsCollNode = pgBrowser.findChildCollectionTreeNode(
|
||||||
|
_node, 'coll-partition'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (partitionsCollNode) {
|
||||||
|
insertChildTreeNodes.push({
|
||||||
|
'parent': partitionsCollNode,
|
||||||
|
'type': 'partition',
|
||||||
|
'treeHierarchy': self.getTreeNodeHierarchy(_node),
|
||||||
|
'childrenIDs': newPartitionsIDs
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertChildrenNodes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE {{conn|qtIdent(data.parent_schema, data.partitioned_table_name)}} ATTACH PARTITION {{conn|qtIdent(data.schema, data.name)}}
|
||||||
|
{{ data.partition_value }};
|
@ -0,0 +1,9 @@
|
|||||||
|
{#=============Checks if it is partitioned table========#}
|
||||||
|
{% if tid %}
|
||||||
|
SELECT
|
||||||
|
CASE WHEN c.relkind = 'p' THEN True ELSE False END As ptable
|
||||||
|
FROM
|
||||||
|
pg_class c
|
||||||
|
WHERE
|
||||||
|
c.oid = {{ tid }}::oid
|
||||||
|
{% endif %}
|
@ -0,0 +1,30 @@
|
|||||||
|
{% import 'table/sql/macros/constraints.macro' as CONSTRAINTS %}
|
||||||
|
{#===========================================#}
|
||||||
|
{#====== MAIN TABLE TEMPLATE STARTS HERE ======#}
|
||||||
|
{#===========================================#}
|
||||||
|
{### CREATE TABLE STATEMENT FOR partitions ###}
|
||||||
|
CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data.schema, data.name)}}{% if data.relispartition is defined and data.relispartition %} PARTITION OF {{conn|qtIdent(data.parent_schema, data.partitioned_table_name)}}{% endif %}
|
||||||
|
|
||||||
|
{# Macro to render for constraints #}
|
||||||
|
{% if data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 or data.exclude_constraint|length > 0 %}
|
||||||
|
( {% endif %}
|
||||||
|
{% if data.primary_key|length > 0 %}{{CONSTRAINTS.PRIMARY_KEY(conn, data.primary_key[0])}}{% endif %}{% if data.unique_constraint|length > 0 %}{% if data.primary_key|length > 0 %},{% endif %}
|
||||||
|
{{CONSTRAINTS.UNIQUE(conn, data.unique_constraint)}}{% endif %}{% if data.foreign_key|length > 0 %}{% if data.primary_key|length > 0 or data.unique_constraint|length > 0 %},{% endif %}
|
||||||
|
{{CONSTRAINTS.FOREIGN_KEY(conn, data.foreign_key)}}{% endif %}{% if data.check_constraint|length > 0 %}{% if data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 %},{% endif %}
|
||||||
|
{{CONSTRAINTS.CHECK(conn, data.check_constraint)}}{% endif %}{% if data.exclude_constraint|length > 0 %}{% if data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 %},{% endif %}
|
||||||
|
{{CONSTRAINTS.EXCLUDE(conn, data.exclude_constraint)}}{% endif %}
|
||||||
|
{% if data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 or data.exclude_constraint|length > 0 %}
|
||||||
|
|
||||||
|
)
|
||||||
|
{% endif %}
|
||||||
|
{{ data.partition_value }}{% if data.is_partitioned is defined and data.is_partitioned %}
|
||||||
|
|
||||||
|
PARTITION BY {{ data.partition_scheme }}{% endif %};
|
||||||
|
|
||||||
|
|
||||||
|
{### Alter SQL for Owner ###}
|
||||||
|
{% if data.relowner %}
|
||||||
|
|
||||||
|
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
|
||||||
|
OWNER to {{conn|qtIdent(data.relowner)}};
|
||||||
|
{% endif %}
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE {{conn|qtIdent(data.parent_schema, data.partitioned_table_name)}} DETACH PARTITION {{conn|qtIdent(data.schema, data.name)}};
|
@ -0,0 +1,23 @@
|
|||||||
|
SELECT oid, quote_ident(nspname)||'.'||quote_ident(relname) AS table_name FROM
|
||||||
|
(SELECT
|
||||||
|
r.oid, r.relname, n.nspname, array_agg(a.attname) attnames, array_agg(a.atttypid) atttypes
|
||||||
|
FROM
|
||||||
|
(SELECT oid, relname, relnamespace FROM pg_catalog.pg_class
|
||||||
|
WHERE relkind in ('r', 'p') AND NOT relispartition) r
|
||||||
|
JOIN (SELECT oid AS nspoid, nspname FROM
|
||||||
|
pg_catalog.pg_namespace WHERE nspname NOT LIKE E'pg\_%') n
|
||||||
|
ON (r.relnamespace = n.nspoid)
|
||||||
|
JOIN (SELECT attrelid, attname, atttypid FROM
|
||||||
|
pg_catalog.pg_attribute WHERE attnum > 0 ORDER BY attrelid, attnum) a
|
||||||
|
ON (r.oid = a.attrelid)
|
||||||
|
GROUP BY r.oid, r.relname, r.relnamespace, n.nspname) all_tables
|
||||||
|
JOIN
|
||||||
|
(SELECT
|
||||||
|
attrelid, array_agg(attname) attnames, array_agg(atttypid) atttypes
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_catalog.pg_attribute
|
||||||
|
WHERE attrelid = {{ tid }} AND attnum > 0
|
||||||
|
ORDER BY attrelid, attnum) attributes
|
||||||
|
GROUP BY attrelid) current_table ON current_table.attrelid != all_tables.oid
|
||||||
|
AND current_table.attnames = all_tables.attnames
|
||||||
|
AND current_table.atttypes = all_tables.atttypes
|
@ -0,0 +1,15 @@
|
|||||||
|
SELECT rel.oid, rel.relname AS name,
|
||||||
|
(SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount,
|
||||||
|
(SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE AND tgenabled = 'O') AS has_enable_triggers,
|
||||||
|
pg_get_expr(rel.relpartbound, rel.oid) AS partition_value,
|
||||||
|
rel.relnamespace AS schema_id,
|
||||||
|
nsp.nspname AS schema_name,
|
||||||
|
(CASE WHEN rel.relkind = 'p' THEN true ELSE false END) AS is_partitioned,
|
||||||
|
(CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef(rel.oid::oid) ELSE '' END) AS partition_scheme
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_inherits WHERE inhparent = {{ tid }}::oid) inh
|
||||||
|
LEFT JOIN pg_class rel ON inh.inhrelid = rel.oid
|
||||||
|
LEFT JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid
|
||||||
|
WHERE rel.relispartition
|
||||||
|
{% if ptid %} AND rel.oid = {{ ptid }}::OID {% endif %}
|
||||||
|
ORDER BY rel.relname;
|
@ -0,0 +1,82 @@
|
|||||||
|
SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS relacl_str,
|
||||||
|
(CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE
|
||||||
|
(SELECT sp.spcname FROM pg_database dtb
|
||||||
|
JOIN pg_tablespace sp ON dtb.dattablespace=sp.oid
|
||||||
|
WHERE dtb.oid = {{ did }}::oid)
|
||||||
|
END) as spcname,
|
||||||
|
(select nspname FROM pg_namespace WHERE oid = {{scid}}::oid ) as parent_schema,
|
||||||
|
nsp.nspname as schema,
|
||||||
|
pg_get_userbyid(rel.relowner) AS relowner, rel.relhasoids, rel.relispartition,
|
||||||
|
rel.relhassubclass, rel.reltuples, des.description, con.conname, con.conkey,
|
||||||
|
EXISTS(select 1 FROM pg_trigger
|
||||||
|
JOIN pg_proc pt ON pt.oid=tgfoid AND pt.proname='logtrigger'
|
||||||
|
JOIN pg_proc pc ON pc.pronamespace=pt.pronamespace AND pc.proname='slonyversion'
|
||||||
|
WHERE tgrelid=rel.oid) AS isrepl,
|
||||||
|
(SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount,
|
||||||
|
(SELECT ARRAY(SELECT CASE WHEN (nspname NOT LIKE E'pg\_%') THEN
|
||||||
|
quote_ident(nspname)||'.'||quote_ident(c.relname)
|
||||||
|
ELSE quote_ident(c.relname) END AS inherited_tables
|
||||||
|
FROM pg_inherits i
|
||||||
|
JOIN pg_class c ON c.oid = i.inhparent
|
||||||
|
JOIN pg_namespace n ON n.oid=c.relnamespace
|
||||||
|
WHERE i.inhrelid = rel.oid ORDER BY inhseqno)) AS coll_inherits,
|
||||||
|
(SELECT count(*)
|
||||||
|
FROM pg_inherits i
|
||||||
|
JOIN pg_class c ON c.oid = i.inhparent
|
||||||
|
JOIN pg_namespace n ON n.oid=c.relnamespace
|
||||||
|
WHERE i.inhrelid = rel.oid) AS inherited_tables_cnt,
|
||||||
|
(CASE WHEN rel.relpersistence = 'u' THEN true ELSE false END) AS relpersistence,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor,
|
||||||
|
(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')
|
||||||
|
THEN true ELSE false END) AS autovacuum_enabled,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age,
|
||||||
|
(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')
|
||||||
|
THEN true ELSE false END) AS toast_autovacuum_enabled,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age,
|
||||||
|
array_to_string(rel.reloptions, ',') AS table_vacuum_settings_str,
|
||||||
|
array_to_string(tst.reloptions, ',') AS toast_table_vacuum_settings_str,
|
||||||
|
rel.reloptions AS reloptions, tst.reloptions AS toast_reloptions, rel.reloftype, typ.typname,
|
||||||
|
(CASE WHEN rel.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable,
|
||||||
|
-- Added for pgAdmin4
|
||||||
|
(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean THEN true ELSE false END) AS autovacuum_custom,
|
||||||
|
(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean AND rel.reltoastrelid != 0 THEN true ELSE false END) AS toast_autovacuum,
|
||||||
|
|
||||||
|
(SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=rel.oid AND sl1.objsubid=0) AS seclabels,
|
||||||
|
(CASE WHEN rel.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_table,
|
||||||
|
-- Added for partition table
|
||||||
|
(CASE WHEN rel.relkind = 'p' THEN true ELSE false END) AS is_partitioned,
|
||||||
|
(CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef(rel.oid::oid) ELSE '' END) AS partition_scheme,
|
||||||
|
{% if ptid %}
|
||||||
|
(CASE WHEN rel.relispartition THEN pg_get_expr(rel.relpartbound, {{ ptid }}::oid) ELSE '' END) AS partition_value,
|
||||||
|
(SELECT relname FROM pg_class WHERE oid = {{ tid }}::oid) AS partitioned_table_name
|
||||||
|
{% else %}
|
||||||
|
pg_get_expr(rel.relpartbound, rel.oid) AS partition_value
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
FROM pg_class rel
|
||||||
|
LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
|
||||||
|
LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass)
|
||||||
|
LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
|
||||||
|
LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
|
||||||
|
LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
|
||||||
|
LEFT JOIN pg_inherits inh ON inh.inhrelid = rel.oid
|
||||||
|
LEFT JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid
|
||||||
|
WHERE rel.relispartition AND inh.inhparent = {{ tid }}::oid
|
||||||
|
{% if ptid %} AND rel.oid = {{ ptid }}::oid {% endif %}
|
||||||
|
ORDER BY rel.relname;
|
@ -0,0 +1,46 @@
|
|||||||
|
{### SQL to fetch privileges for tablespace ###}
|
||||||
|
SELECT 'relacl' as deftype, COALESCE(gt.rolname, 'PUBLIC') grantee, g.rolname grantor,
|
||||||
|
array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||||
|
FROM
|
||||||
|
(SELECT
|
||||||
|
d.grantee, d.grantor, d.is_grantable,
|
||||||
|
CASE d.privilege_type
|
||||||
|
WHEN 'CONNECT' THEN 'c'
|
||||||
|
WHEN 'CREATE' THEN 'C'
|
||||||
|
WHEN 'DELETE' THEN 'd'
|
||||||
|
WHEN 'EXECUTE' THEN 'X'
|
||||||
|
WHEN 'INSERT' THEN 'a'
|
||||||
|
WHEN 'REFERENCES' THEN 'x'
|
||||||
|
WHEN 'SELECT' THEN 'r'
|
||||||
|
WHEN 'TEMPORARY' THEN 'T'
|
||||||
|
WHEN 'TRIGGER' THEN 't'
|
||||||
|
WHEN 'TRUNCATE' THEN 'D'
|
||||||
|
WHEN 'UPDATE' THEN 'w'
|
||||||
|
WHEN 'USAGE' THEN 'U'
|
||||||
|
ELSE 'UNKNOWN'
|
||||||
|
END AS privilege_type
|
||||||
|
FROM
|
||||||
|
(SELECT rel.relacl
|
||||||
|
FROM pg_class rel
|
||||||
|
LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
|
||||||
|
LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
|
||||||
|
LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
|
||||||
|
LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
|
||||||
|
WHERE rel.relkind IN ('r','s','t','p') AND rel.relnamespace = {{ scid }}::oid
|
||||||
|
AND rel.oid = {{ tid }}::oid
|
||||||
|
) acl,
|
||||||
|
(SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
|
||||||
|
AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
|
||||||
|
aclexplode(rel.relacl) as d
|
||||||
|
FROM pg_class rel
|
||||||
|
LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
|
||||||
|
LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
|
||||||
|
LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
|
||||||
|
LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
|
||||||
|
WHERE rel.relkind IN ('r','s','t','p') AND rel.relnamespace = {{ scid }}::oid
|
||||||
|
AND rel.oid = {{ tid }}::oid
|
||||||
|
) a) d
|
||||||
|
) d
|
||||||
|
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
|
||||||
|
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
|
||||||
|
GROUP BY g.rolname, gt.rolname
|
@ -0,0 +1,17 @@
|
|||||||
|
{% import 'table/sql/macros/db_catalogs.macro' as CATALOG %}
|
||||||
|
SELECT c.oid, c.relname , nspname,
|
||||||
|
CASE WHEN nspname NOT LIKE E'pg\_%' THEN
|
||||||
|
quote_ident(nspname)||'.'||quote_ident(c.relname)
|
||||||
|
ELSE quote_ident(c.relname)
|
||||||
|
END AS inherits
|
||||||
|
FROM pg_class c
|
||||||
|
JOIN pg_namespace n
|
||||||
|
ON n.oid=c.relnamespace
|
||||||
|
WHERE relkind='r' AND NOT relispartition
|
||||||
|
{% if not show_system_objects %}
|
||||||
|
{{ CATALOG.VALID_CATALOGS(server_type) }}
|
||||||
|
{% endif %}
|
||||||
|
{% if tid %}
|
||||||
|
AND c.oid != tid
|
||||||
|
{% endif %}
|
||||||
|
ORDER BY relnamespace, c.relname
|
@ -0,0 +1,5 @@
|
|||||||
|
SELECT rel.oid as tid
|
||||||
|
FROM pg_class rel
|
||||||
|
WHERE rel.relkind IN ('r','s','t','p')
|
||||||
|
AND rel.relnamespace = {{ scid }}::oid
|
||||||
|
AND rel.relname = {{data.name|qtLiteral}}
|
@ -0,0 +1,8 @@
|
|||||||
|
SELECT
|
||||||
|
rel.relname AS name
|
||||||
|
FROM
|
||||||
|
pg_class rel
|
||||||
|
WHERE
|
||||||
|
rel.relkind IN ('r','s','t','p')
|
||||||
|
AND rel.relnamespace = {{ scid }}::oid
|
||||||
|
AND rel.oid = {{ tid }}::oid;
|
@ -0,0 +1,9 @@
|
|||||||
|
SELECT rel.oid, rel.relname AS name,
|
||||||
|
(SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount,
|
||||||
|
(SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE AND tgenabled = 'O') AS has_enable_triggers,
|
||||||
|
(CASE WHEN rel.relkind = 'p' THEN true ELSE false END) AS is_partitioned
|
||||||
|
FROM pg_class rel
|
||||||
|
WHERE rel.relkind IN ('r','s','t','p') AND rel.relnamespace = {{ scid }}::oid
|
||||||
|
AND NOT rel.relispartition
|
||||||
|
{% if tid %} AND rel.oid = {{tid}}::OID {% endif %}
|
||||||
|
ORDER BY rel.relname;
|
@ -0,0 +1,73 @@
|
|||||||
|
SELECT rel.oid, rel.relname AS name, rel.reltablespace AS spcoid,rel.relacl AS relacl_str,
|
||||||
|
(CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE
|
||||||
|
(SELECT sp.spcname FROM pg_database dtb
|
||||||
|
JOIN pg_tablespace sp ON dtb.dattablespace=sp.oid
|
||||||
|
WHERE dtb.oid = {{ did }}::oid)
|
||||||
|
END) as spcname,
|
||||||
|
(select nspname FROM pg_namespace WHERE oid = {{scid}}::oid ) as schema,
|
||||||
|
pg_get_userbyid(rel.relowner) AS relowner, rel.relhasoids, rel.relkind,
|
||||||
|
(CASE WHEN rel.relkind = 'p' THEN true ELSE false END) AS is_partitioned,
|
||||||
|
rel.relhassubclass, rel.reltuples, des.description, con.conname, con.conkey,
|
||||||
|
EXISTS(select 1 FROM pg_trigger
|
||||||
|
JOIN pg_proc pt ON pt.oid=tgfoid AND pt.proname='logtrigger'
|
||||||
|
JOIN pg_proc pc ON pc.pronamespace=pt.pronamespace AND pc.proname='slonyversion'
|
||||||
|
WHERE tgrelid=rel.oid) AS isrepl,
|
||||||
|
(SELECT count(*) FROM pg_trigger WHERE tgrelid=rel.oid AND tgisinternal = FALSE) AS triggercount,
|
||||||
|
(SELECT ARRAY(SELECT CASE WHEN (nspname NOT LIKE E'pg\_%') THEN
|
||||||
|
quote_ident(nspname)||'.'||quote_ident(c.relname)
|
||||||
|
ELSE quote_ident(c.relname) END AS inherited_tables
|
||||||
|
FROM pg_inherits i
|
||||||
|
JOIN pg_class c ON c.oid = i.inhparent
|
||||||
|
JOIN pg_namespace n ON n.oid=c.relnamespace
|
||||||
|
WHERE i.inhrelid = rel.oid ORDER BY inhseqno)) AS coll_inherits,
|
||||||
|
(SELECT count(*)
|
||||||
|
FROM pg_inherits i
|
||||||
|
JOIN pg_class c ON c.oid = i.inhparent
|
||||||
|
JOIN pg_namespace n ON n.oid=c.relnamespace
|
||||||
|
WHERE i.inhrelid = rel.oid) AS inherited_tables_cnt,
|
||||||
|
(CASE WHEN rel.relpersistence = 'u' THEN true ELSE false END) AS relpersistence,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor,
|
||||||
|
(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')
|
||||||
|
THEN true ELSE false END) AS autovacuum_enabled,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age,
|
||||||
|
substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age,
|
||||||
|
(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') = 'true')
|
||||||
|
THEN true ELSE false END) AS toast_autovacuum_enabled,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age,
|
||||||
|
substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age,
|
||||||
|
array_to_string(rel.reloptions, ',') AS table_vacuum_settings_str,
|
||||||
|
array_to_string(tst.reloptions, ',') AS toast_table_vacuum_settings_str,
|
||||||
|
rel.reloptions AS reloptions, tst.reloptions AS toast_reloptions, rel.reloftype, typ.typname,
|
||||||
|
(CASE WHEN rel.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable,
|
||||||
|
-- Added for pgAdmin4
|
||||||
|
(CASE WHEN (substring(array_to_string(rel.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean THEN true ELSE false END) AS autovacuum_custom,
|
||||||
|
(CASE WHEN (substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean AND rel.reltoastrelid != 0 THEN true ELSE false END) AS toast_autovacuum,
|
||||||
|
|
||||||
|
(SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=rel.oid AND sl1.objsubid=0) AS seclabels,
|
||||||
|
(CASE WHEN rel.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_table
|
||||||
|
-- Added for partition table
|
||||||
|
{% if tid %}, (CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef({{ tid }}::oid) ELSE '' END) AS partition_scheme {% endif %}
|
||||||
|
FROM pg_class rel
|
||||||
|
LEFT OUTER JOIN pg_tablespace spc on spc.oid=rel.reltablespace
|
||||||
|
LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass)
|
||||||
|
LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p'
|
||||||
|
LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid
|
||||||
|
LEFT JOIN pg_type typ ON rel.reloftype=typ.oid
|
||||||
|
WHERE rel.relkind IN ('r','s','t','p') AND rel.relnamespace = {{ scid }}::oid
|
||||||
|
AND NOT rel.relispartition
|
||||||
|
{% if tid %} AND rel.oid = {{ tid }}::oid {% endif %}
|
||||||
|
ORDER BY rel.relname;
|
@ -57,7 +57,8 @@ CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data
|
|||||||
{{CONSTRAINTS.EXCLUDE(conn, data.exclude_constraint)}}{% endif %}
|
{{CONSTRAINTS.EXCLUDE(conn, data.exclude_constraint)}}{% endif %}
|
||||||
{% if data.like_relation or data.coll_inherits or data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 or data.exclude_constraint|length > 0 %}
|
{% if data.like_relation or data.coll_inherits or data.columns|length > 0 or data.primary_key|length > 0 or data.unique_constraint|length > 0 or data.foreign_key|length > 0 or data.check_constraint|length > 0 or data.exclude_constraint|length > 0 %}
|
||||||
|
|
||||||
)
|
){% if data.relkind is defined and data.relkind == 'p' %} PARTITION BY {{ data.partition_scheme }} {% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{### If we are inheriting it from another table(s) ###}
|
{### If we are inheriting it from another table(s) ###}
|
||||||
{% if data.coll_inherits %}
|
{% if data.coll_inherits %}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
{# ===== fetch new assigned schema oid ===== #}
|
{# ===== fetch new assigned schema oid ===== #}
|
||||||
SELECT
|
SELECT
|
||||||
c.relnamespace as scid
|
c.relnamespace as scid, nsp.nspname as nspname
|
||||||
FROM
|
FROM
|
||||||
pg_class c
|
pg_class c
|
||||||
|
LEFT JOIN pg_namespace nsp ON nsp.oid = c.relnamespace
|
||||||
WHERE
|
WHERE
|
||||||
{% if tid %}
|
{% if tid %}
|
||||||
c.oid = {{tid}}::oid;
|
c.oid = {{tid}}::oid;
|
||||||
|
@ -14,6 +14,7 @@ from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
|||||||
utils as schema_utils
|
utils as schema_utils
|
||||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||||
database_utils
|
database_utils
|
||||||
|
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
from regression import parent_node_dict
|
from regression import parent_node_dict
|
||||||
from regression.python_test_utils import test_utils as utils
|
from regression.python_test_utils import test_utils as utils
|
||||||
@ -23,7 +24,19 @@ class TableAddTestCase(BaseTestGenerator):
|
|||||||
""" This class will add new collation under schema node. """
|
""" This class will add new collation under schema node. """
|
||||||
scenarios = [
|
scenarios = [
|
||||||
# Fetching default URL for table node.
|
# Fetching default URL for table node.
|
||||||
('Fetch table Node URL', dict(url='/browser/table/obj/'))
|
('Create Table', dict(url='/browser/table/obj/')),
|
||||||
|
('Create Range partitioned table with 2 partitions',
|
||||||
|
dict(url='/browser/table/obj/',
|
||||||
|
server_min_version=100000,
|
||||||
|
partition_type='range'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
('Create List partitioned table with 2 partitions',
|
||||||
|
dict(url='/browser/table/obj/',
|
||||||
|
server_min_version=100000,
|
||||||
|
partition_type='list'
|
||||||
|
)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -43,6 +56,19 @@ class TableAddTestCase(BaseTestGenerator):
|
|||||||
if not schema_response:
|
if not schema_response:
|
||||||
raise Exception("Could not find the schema to add a table.")
|
raise Exception("Could not find the schema to add a table.")
|
||||||
|
|
||||||
|
self.is_partition = False
|
||||||
|
if hasattr(self, 'server_min_version'):
|
||||||
|
server_con = server_utils.connect_server(self, self.server_id)
|
||||||
|
if not server_con["info"] == "Server connected.":
|
||||||
|
raise Exception("Could not connect to server to add "
|
||||||
|
"partitioned table.")
|
||||||
|
if server_con["data"]["version"] < self.server_min_version:
|
||||||
|
message = "Partitioned table are not supported by " \
|
||||||
|
"PPAS/PG 10.0 and below."
|
||||||
|
self.skipTest(message)
|
||||||
|
else:
|
||||||
|
self.is_partition = True
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
""" This function will add table under schema node. """
|
""" This function will add table under schema node. """
|
||||||
db_user = self.server["username"]
|
db_user = self.server["username"]
|
||||||
@ -68,7 +94,7 @@ class TableAddTestCase(BaseTestGenerator):
|
|||||||
"seclabels": []
|
"seclabels": []
|
||||||
},
|
},
|
||||||
{"name": "DOJ",
|
{"name": "DOJ",
|
||||||
"cltype": "date[]",
|
"cltype": "date",
|
||||||
"attacl": [],
|
"attacl": [],
|
||||||
"is_primary_key": False,
|
"is_primary_key": False,
|
||||||
"attoptions": [],
|
"attoptions": [],
|
||||||
@ -76,7 +102,7 @@ class TableAddTestCase(BaseTestGenerator):
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"exclude_constraint": [],
|
"exclude_constraint": [],
|
||||||
"fillfactor": "11",
|
"fillfactor": "",
|
||||||
"hastoasttable": True,
|
"hastoasttable": True,
|
||||||
"like_constraints": True,
|
"like_constraints": True,
|
||||||
"like_default_value": True,
|
"like_default_value": True,
|
||||||
@ -166,6 +192,35 @@ class TableAddTestCase(BaseTestGenerator):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.is_partition:
|
||||||
|
data['partition_type'] = self.partition_type
|
||||||
|
data['is_partitioned'] = True
|
||||||
|
if self.partition_type == 'range':
|
||||||
|
data['partitions'] = \
|
||||||
|
[{'values_from': "'2010-01-01'",
|
||||||
|
'values_to': "'2010-12-31'",
|
||||||
|
'is_attach': False,
|
||||||
|
'partition_name': 'emp_2010'
|
||||||
|
},
|
||||||
|
{'values_from': "'2011-01-01'",
|
||||||
|
'values_to': "'2011-12-31'",
|
||||||
|
'is_attach': False,
|
||||||
|
'partition_name': 'emp_2011'
|
||||||
|
}]
|
||||||
|
else:
|
||||||
|
data['partitions'] = \
|
||||||
|
[{'values_in': "'2012-01-01', '2012-12-31'",
|
||||||
|
'is_attach': False,
|
||||||
|
'partition_name': 'emp_2012'
|
||||||
|
},
|
||||||
|
{'values_in': "'2013-01-01', '2013-12-31'",
|
||||||
|
'is_attach': False,
|
||||||
|
'partition_name': 'emp_2013'
|
||||||
|
}]
|
||||||
|
data['partition_keys'] = \
|
||||||
|
[{'key_type': 'column', 'pt_column': 'DOJ'}]
|
||||||
|
|
||||||
# Add table
|
# Add table
|
||||||
response = self.tester.post(
|
response = self.tester.post(
|
||||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||||
|
@ -23,7 +23,7 @@ class TableDeleteTestCase(BaseTestGenerator):
|
|||||||
"""This class will delete new table under schema node."""
|
"""This class will delete new table under schema node."""
|
||||||
scenarios = [
|
scenarios = [
|
||||||
# Fetching default URL for table node.
|
# Fetching default URL for table node.
|
||||||
('Fetch table Node URL', dict(url='/browser/table/obj/'))
|
('Delete Table', dict(url='/browser/table/obj/'))
|
||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -14,6 +14,7 @@ from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
|||||||
utils as schema_utils
|
utils as schema_utils
|
||||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||||
database_utils
|
database_utils
|
||||||
|
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
from regression import parent_node_dict
|
from regression import parent_node_dict
|
||||||
from regression.python_test_utils import test_utils as utils
|
from regression.python_test_utils import test_utils as utils
|
||||||
@ -24,7 +25,49 @@ class TableUpdateTestCase(BaseTestGenerator):
|
|||||||
"""This class will add new collation under schema node."""
|
"""This class will add new collation under schema node."""
|
||||||
scenarios = [
|
scenarios = [
|
||||||
# Fetching default URL for table node.
|
# Fetching default URL for table node.
|
||||||
('Fetch table Node URL', dict(url='/browser/table/obj/'))
|
('Update Table', dict(url='/browser/table/obj/')),
|
||||||
|
('Create partitions of existing range partitioned table',
|
||||||
|
dict(url='/browser/table/obj/',
|
||||||
|
server_min_version=100000,
|
||||||
|
partition_type='range',
|
||||||
|
mode='create'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
('Create partitions of existing list partitioned table',
|
||||||
|
dict(url='/browser/table/obj/',
|
||||||
|
server_min_version=100000,
|
||||||
|
partition_type='list',
|
||||||
|
mode='create'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
('Detach partition from existing range partitioned table',
|
||||||
|
dict(url='/browser/table/obj/',
|
||||||
|
server_min_version=100000,
|
||||||
|
partition_type='range',
|
||||||
|
mode='detach'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
('Detach partition from existing list partitioned table',
|
||||||
|
dict(url='/browser/table/obj/',
|
||||||
|
server_min_version=100000,
|
||||||
|
partition_type='list',
|
||||||
|
mode='detach'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
('Attach partition to existing range partitioned table',
|
||||||
|
dict(url='/browser/table/obj/',
|
||||||
|
server_min_version=100000,
|
||||||
|
partition_type='range',
|
||||||
|
mode='attach'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
('Attach partition to existing list partitioned table',
|
||||||
|
dict(url='/browser/table/obj/',
|
||||||
|
server_min_version=100000,
|
||||||
|
partition_type='list',
|
||||||
|
mode='attach'
|
||||||
|
)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -44,9 +87,31 @@ class TableUpdateTestCase(BaseTestGenerator):
|
|||||||
if not schema_response:
|
if not schema_response:
|
||||||
raise Exception("Could not find the schema to add a table.")
|
raise Exception("Could not find the schema to add a table.")
|
||||||
self.table_name = "test_table_put_%s" % (str(uuid.uuid4())[1:6])
|
self.table_name = "test_table_put_%s" % (str(uuid.uuid4())[1:6])
|
||||||
self.table_id = tables_utils.create_table(self.server, self.db_name,
|
|
||||||
self.schema_name,
|
self.is_partition = False
|
||||||
self.table_name)
|
if hasattr(self, 'server_min_version'):
|
||||||
|
server_con = server_utils.connect_server(self, self.server_id)
|
||||||
|
if not server_con["info"] == "Server connected.":
|
||||||
|
raise Exception("Could not connect to server to add "
|
||||||
|
"partitioned table.")
|
||||||
|
if server_con["data"]["version"] < self.server_min_version:
|
||||||
|
message = "Partitioned table are not supported by " \
|
||||||
|
"PPAS/PG 10.0 and below."
|
||||||
|
self.skipTest(message)
|
||||||
|
else:
|
||||||
|
self.is_partition = True
|
||||||
|
|
||||||
|
self.table_id = tables_utils.create_table_for_partition(
|
||||||
|
self.server,
|
||||||
|
self.db_name,
|
||||||
|
self.schema_name,
|
||||||
|
self.table_name,
|
||||||
|
'partitioned',
|
||||||
|
self.partition_type)
|
||||||
|
else:
|
||||||
|
self.table_id = tables_utils.create_table(self.server, self.db_name,
|
||||||
|
self.schema_name,
|
||||||
|
self.table_name)
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
"""This function will fetch added table under schema node."""
|
"""This function will fetch added table under schema node."""
|
||||||
@ -54,10 +119,18 @@ class TableUpdateTestCase(BaseTestGenerator):
|
|||||||
self.table_id)
|
self.table_id)
|
||||||
if not table_response:
|
if not table_response:
|
||||||
raise Exception("Could not find the table to update.")
|
raise Exception("Could not find the table to update.")
|
||||||
data = {
|
|
||||||
"description": "This is test comment for table",
|
if self.is_partition:
|
||||||
"id": self.table_id
|
data = {"id": self.table_id}
|
||||||
}
|
tables_utils.set_partition_data(
|
||||||
|
self.server, self.db_name, self.schema_name, self.table_name,
|
||||||
|
self.partition_type, data, self.mode)
|
||||||
|
else:
|
||||||
|
data = {
|
||||||
|
"description": "This is test comment for table",
|
||||||
|
"id": self.table_id
|
||||||
|
}
|
||||||
|
|
||||||
response = self.tester.put(
|
response = self.tester.put(
|
||||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||||
str(self.server_id) + '/' + str(self.db_id) + '/' +
|
str(self.server_id) + '/' + str(self.db_id) + '/' +
|
||||||
|
@ -85,3 +85,153 @@ def verify_table(server, db_name, table_id):
|
|||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def create_table_for_partition(server, db_name, schema_name, table_name,
|
||||||
|
table_type, partition_type, partition_name=None):
|
||||||
|
"""
|
||||||
|
This function creates partitioned/partition/regular table
|
||||||
|
under provided schema.
|
||||||
|
|
||||||
|
:param server: server details
|
||||||
|
:param db_name: database name
|
||||||
|
:param schema_name: schema name
|
||||||
|
:param table_name: table name
|
||||||
|
:param table_type: regular/partitioned/partition
|
||||||
|
:param partition_type: partition table type (range/list)
|
||||||
|
:param partition_name: Partition Name
|
||||||
|
:return table_id: table id
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
connection = utils.get_db_connection(db_name,
|
||||||
|
server['username'],
|
||||||
|
server['db_password'],
|
||||||
|
server['host'],
|
||||||
|
server['port'])
|
||||||
|
old_isolation_level = connection.isolation_level
|
||||||
|
connection.set_isolation_level(0)
|
||||||
|
pg_cursor = connection.cursor()
|
||||||
|
|
||||||
|
query = ''
|
||||||
|
if table_type == 'partitioned':
|
||||||
|
if partition_type == 'range':
|
||||||
|
query = "CREATE TABLE %s.%s(country text, sales bigint, " \
|
||||||
|
"saledate date) PARTITION BY RANGE(saledate)" % \
|
||||||
|
(schema_name, table_name)
|
||||||
|
else:
|
||||||
|
query = "CREATE TABLE %s.%s(country text, sales bigint, " \
|
||||||
|
"saledate date) PARTITION BY LIST(saledate)" % \
|
||||||
|
(schema_name, table_name)
|
||||||
|
elif table_type == 'partition':
|
||||||
|
if partition_type == 'range':
|
||||||
|
query = "CREATE TABLE %s.%s PARTITION OF %s.%s " \
|
||||||
|
"FOR VALUES FROM ('2012-01-01') TO ('2012-12-31')" % \
|
||||||
|
(schema_name, partition_name, schema_name, table_name)
|
||||||
|
else:
|
||||||
|
query = "CREATE TABLE %s.%s PARTITION OF %s.%s " \
|
||||||
|
"FOR VALUES IN ('2013-01-01')" % \
|
||||||
|
(schema_name, partition_name, schema_name, table_name)
|
||||||
|
|
||||||
|
# To fetch OID table name is actually partition name
|
||||||
|
table_name = partition_name
|
||||||
|
elif table_type == 'regular':
|
||||||
|
query = "CREATE TABLE %s.%s(country text, sales bigint," \
|
||||||
|
"saledate date NOT NULL)" % (schema_name, table_name)
|
||||||
|
|
||||||
|
pg_cursor.execute(query)
|
||||||
|
connection.set_isolation_level(old_isolation_level)
|
||||||
|
connection.commit()
|
||||||
|
# Get 'oid' from newly created table
|
||||||
|
pg_cursor.execute("select oid from pg_class where relname='%s'" %
|
||||||
|
table_name)
|
||||||
|
table = pg_cursor.fetchone()
|
||||||
|
table_id = ''
|
||||||
|
if table:
|
||||||
|
table_id = table[0]
|
||||||
|
connection.close()
|
||||||
|
return table_id
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def set_partition_data(server, db_name, schema_name, table_name,
|
||||||
|
partition_type, data, mode):
|
||||||
|
"""
|
||||||
|
This function is used to set the partitions data on the basis of
|
||||||
|
partition type and action.
|
||||||
|
|
||||||
|
:param server: server details
|
||||||
|
:param db_name: Database Name
|
||||||
|
:param schema_name: Schema Name
|
||||||
|
:param table_name: Table Name
|
||||||
|
:param partition_type: range or list
|
||||||
|
:param data: Data
|
||||||
|
:param mode: create/detach
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
data['partitions'] = dict()
|
||||||
|
if partition_type == 'range' and mode == 'create':
|
||||||
|
data['partitions'].update(
|
||||||
|
{'added': [{'values_from': "'2014-01-01'",
|
||||||
|
'values_to': "'2014-12-31'",
|
||||||
|
'is_attach': False,
|
||||||
|
'partition_name': 'sale_2014'},
|
||||||
|
{'values_from': "'2015-01-01'",
|
||||||
|
'values_to': "'2015-12-31'",
|
||||||
|
'is_attach': False,
|
||||||
|
'partition_name': 'sale_2015'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif partition_type == 'list' and mode == 'create':
|
||||||
|
data['partitions'].update(
|
||||||
|
{'added': [{'values_in': "'2016-01-01', '2016-12-31'",
|
||||||
|
'is_attach': False,
|
||||||
|
'partition_name': 'sale_2016'},
|
||||||
|
{'values_in': "'2017-01-01', '2017-12-31'",
|
||||||
|
'is_attach': False,
|
||||||
|
'partition_name': 'sale_2017'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif partition_type == 'range' and mode == 'detach':
|
||||||
|
partition_id = create_table_for_partition(server, db_name, schema_name,
|
||||||
|
table_name, 'partition',
|
||||||
|
partition_type, 'sale_2012')
|
||||||
|
data['partitions'].update(
|
||||||
|
{'deleted': [{'oid': partition_id}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif partition_type == 'list' and mode == 'detach':
|
||||||
|
partition_id = create_table_for_partition(server, db_name, schema_name,
|
||||||
|
table_name, 'partition',
|
||||||
|
partition_type, 'sale_2013')
|
||||||
|
data['partitions'].update(
|
||||||
|
{'deleted': [{'oid': partition_id}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif partition_type == 'range' and mode == 'attach':
|
||||||
|
partition_id = create_table_for_partition(server, db_name, schema_name,
|
||||||
|
'attach_sale_2010', 'regular',
|
||||||
|
partition_type)
|
||||||
|
data['partitions'].update(
|
||||||
|
{'added': [{'values_from': "'2010-01-01'",
|
||||||
|
'values_to': "'2010-12-31'",
|
||||||
|
'is_attach': True,
|
||||||
|
'partition_name': partition_id
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif partition_type == 'list' and mode == 'attach':
|
||||||
|
partition_id = create_table_for_partition(server, db_name, schema_name,
|
||||||
|
'attach_sale_2011', 'regular',
|
||||||
|
partition_type)
|
||||||
|
data['partitions'].update(
|
||||||
|
{'added': [{'values_in': "'2011-01-01'",
|
||||||
|
'is_attach': True,
|
||||||
|
'partition_name': partition_id
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -30,13 +30,15 @@ define('pgadmin.node.trigger', [
|
|||||||
node: 'trigger',
|
node: 'trigger',
|
||||||
label: gettext('Triggers'),
|
label: gettext('Triggers'),
|
||||||
type: 'coll-trigger',
|
type: 'coll-trigger',
|
||||||
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
columns: ['name', 'description']
|
columns: ['name', 'description']
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!pgBrowser.Nodes['trigger']) {
|
if (!pgBrowser.Nodes['trigger']) {
|
||||||
pgAdmin.Browser.Nodes['trigger'] = pgAdmin.Browser.Node.extend({
|
pgAdmin.Browser.Nodes['trigger'] = pgBrowser.Node.extend({
|
||||||
parent_type: ['table', 'view'],
|
getTreeNodeHierarchy: pgBrowser.tableChildTreeNodeHierarchy,
|
||||||
|
parent_type: ['table', 'view', 'partition'],
|
||||||
collection_type: ['coll-table', 'coll-view'],
|
collection_type: ['coll-table', 'coll-view'],
|
||||||
type: 'trigger',
|
type: 'trigger',
|
||||||
label: gettext('Trigger'),
|
label: gettext('Trigger'),
|
||||||
@ -71,6 +73,12 @@ define('pgadmin.node.trigger', [
|
|||||||
category: 'create', priority: 4, label: gettext('Trigger...'),
|
category: 'create', priority: 4, label: gettext('Trigger...'),
|
||||||
icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
|
icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
|
||||||
enable: 'canCreate'
|
enable: 'canCreate'
|
||||||
|
},{
|
||||||
|
name: 'create_trigger_onPartition', node: 'partition', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: gettext('Trigger...'),
|
||||||
|
icon: 'wcTabIcon icon-trigger', data: {action: 'create', check: true},
|
||||||
|
enable: 'canCreate'
|
||||||
},{
|
},{
|
||||||
name: 'enable_trigger', node: 'trigger', module: this,
|
name: 'enable_trigger', node: 'trigger', module: this,
|
||||||
applies: ['object', 'context'], callback: 'enable_trigger',
|
applies: ['object', 'context'], callback: 'enable_trigger',
|
||||||
@ -206,6 +214,17 @@ define('pgadmin.node.trigger', [
|
|||||||
mode: ['create','edit', 'properties'],
|
mode: ['create','edit', 'properties'],
|
||||||
deps: ['is_constraint_trigger'],
|
deps: ['is_constraint_trigger'],
|
||||||
disabled: function(m) {
|
disabled: function(m) {
|
||||||
|
// Disabled if table is a partitioned table.
|
||||||
|
if (_.has(m, 'node_info') && _.has(m.node_info, 'table') &&
|
||||||
|
_.has(m.node_info.table, 'is_partitioned') && m.node_info.table.is_partitioned)
|
||||||
|
{
|
||||||
|
setTimeout(function(){
|
||||||
|
m.set('is_row_trigger', false);
|
||||||
|
},10);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// If constraint trigger is set to True then row trigger will
|
// If constraint trigger is set to True then row trigger will
|
||||||
// automatically set to True and becomes disable
|
// automatically set to True and becomes disable
|
||||||
var is_constraint_trigger = m.get('is_constraint_trigger');
|
var is_constraint_trigger = m.get('is_constraint_trigger');
|
||||||
@ -232,7 +251,19 @@ define('pgadmin.node.trigger', [
|
|||||||
id: 'is_constraint_trigger', label: gettext('Constraint trigger?'),
|
id: 'is_constraint_trigger', label: gettext('Constraint trigger?'),
|
||||||
type: 'switch', disabled: 'inSchemaWithModelCheck',
|
type: 'switch', disabled: 'inSchemaWithModelCheck',
|
||||||
mode: ['create','edit', 'properties'],
|
mode: ['create','edit', 'properties'],
|
||||||
group: gettext('Definition')
|
group: gettext('Definition'),
|
||||||
|
disabled: function(m) {
|
||||||
|
// Disabled if table is a partitioned table.
|
||||||
|
if (_.has(m, 'node_info') && _.has(m.node_info, 'table') &&
|
||||||
|
_.has(m.node_info.table, 'is_partitioned') && m.node_info.table.is_partitioned)
|
||||||
|
{
|
||||||
|
setTimeout(function(){
|
||||||
|
m.set('is_constraint_trigger', false);
|
||||||
|
},10);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
},{
|
},{
|
||||||
id: 'tgdeferrable', label: gettext('Deferrable?'),
|
id: 'tgdeferrable', label: gettext('Deferrable?'),
|
||||||
type: 'switch', group: gettext('Definition'),
|
type: 'switch', group: gettext('Definition'),
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,7 @@ define(
|
|||||||
$ = $ || window.jQuery || window.$;
|
$ = $ || window.jQuery || window.$;
|
||||||
Bootstrap = Bootstrap || window.Bootstrap;
|
Bootstrap = Bootstrap || window.Bootstrap;
|
||||||
|
|
||||||
pgAdmin.Browser = pgAdmin.Browser || {};
|
var pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||||
|
|
||||||
var panelEvents = {};
|
var panelEvents = {};
|
||||||
panelEvents[wcDocker.EVENT.VISIBILITY_CHANGED] = function() {
|
panelEvents[wcDocker.EVENT.VISIBILITY_CHANGED] = function() {
|
||||||
@ -1756,6 +1756,201 @@ define(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
removeChildTreeNodesById: function(_parentNode, _collType, _childIds) {
|
||||||
|
var tree = pgBrowser.tree;
|
||||||
|
if(_parentNode && _collType) {
|
||||||
|
var children = tree.children(_parentNode),
|
||||||
|
idx = 0, size = children.length,
|
||||||
|
childNode, childNodeData;
|
||||||
|
|
||||||
|
_parentNode = null;
|
||||||
|
|
||||||
|
for (; idx < size; idx++) {
|
||||||
|
childNode = children.eq(idx);
|
||||||
|
childNodeData = tree.itemData(childNode);
|
||||||
|
|
||||||
|
if (childNodeData._type == _collType) {
|
||||||
|
_parentNode = childNode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_parentNode) {
|
||||||
|
var children = tree.children(_parentNode),
|
||||||
|
idx = 0, size = children.length,
|
||||||
|
childNode, childNodeData,
|
||||||
|
prevChildNode;
|
||||||
|
|
||||||
|
for (; idx < size; idx++) {
|
||||||
|
childNode = children.eq(idx);
|
||||||
|
childNodeData = tree.itemData(childNode);
|
||||||
|
|
||||||
|
if (_childIds.indexOf(childNodeData._id) != -1) {
|
||||||
|
pgBrowser.removeTreeNode(childNode, false, _parentNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeTreeNode: function(_node, _selectNext, _parentNode) {
|
||||||
|
var tree = pgBrowser.tree,
|
||||||
|
nodeToSelect = null;
|
||||||
|
|
||||||
|
if (!_node)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_selectNext) {
|
||||||
|
nodeToSelect = tree.next(_node);
|
||||||
|
if (!nodeToSelect || !nodeToSelect.length) {
|
||||||
|
nodeToSelect = tree.prev(_node);
|
||||||
|
|
||||||
|
if (!nodeToSelect || !nodeToSelect.length) {
|
||||||
|
if (!_parentNode) {
|
||||||
|
nodeToSelect = tree.parent(_node);
|
||||||
|
} else {
|
||||||
|
nodeToSelect = _parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nodeToSelect)
|
||||||
|
tree.select(nodeToSelect);
|
||||||
|
}
|
||||||
|
tree.remove(_node);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
findSiblingTreeNode: function(_node, _id) {
|
||||||
|
var tree = pgBrowser.tree,
|
||||||
|
parentNode = tree.parent(_node),
|
||||||
|
siblings = tree.children(parentNode),
|
||||||
|
idx = 0, nodeData, node;
|
||||||
|
|
||||||
|
for(; idx < siblings.length; idx++) {
|
||||||
|
node = siblings.eq(idx);
|
||||||
|
nodeData = tree.itemData(node);
|
||||||
|
|
||||||
|
if (nodeData && nodeData._id == _id)
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
findParentTreeNodeByType: function(_node, _parentType) {
|
||||||
|
var tree = pgBrowser.tree,
|
||||||
|
nodeData,
|
||||||
|
node = _node;
|
||||||
|
|
||||||
|
do {
|
||||||
|
nodeData = tree.itemData(node);
|
||||||
|
if (nodeData && nodeData._type == _parentType)
|
||||||
|
return node;
|
||||||
|
node = tree.hasParent(node) ? tree.parent(node) : null;
|
||||||
|
} while (node);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
findChildCollectionTreeNode: function(_node, _collType) {
|
||||||
|
var tree = pgBrowser.tree,
|
||||||
|
nodeData, idx = 0,
|
||||||
|
node = _node,
|
||||||
|
children = _node && tree.children(_node);
|
||||||
|
|
||||||
|
if (!children || !children.length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for(; idx < children.length; idx++) {
|
||||||
|
node = children.eq(idx);
|
||||||
|
nodeData = tree.itemData(node);
|
||||||
|
|
||||||
|
if (nodeData && nodeData._type == _collType)
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
addChildTreeNodes: function(_treeHierarchy, _node, _type, _arrayIds, _callback) {
|
||||||
|
var module = _type in pgBrowser.Nodes && pgBrowser.Nodes[_type],
|
||||||
|
childTreeInfo = _arrayIds.length && _.extend(
|
||||||
|
{}, _.mapObject(
|
||||||
|
_treeHierarchy, function(_val, _key) {
|
||||||
|
_val.priority -= 1; return _val;
|
||||||
|
})
|
||||||
|
),
|
||||||
|
arrayChildNodeData = [],
|
||||||
|
fetchNodeInfo = function(_callback) {
|
||||||
|
if (!_arrayIds.length) {
|
||||||
|
if (_callback) {
|
||||||
|
_callback();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childDummyInfo = {
|
||||||
|
'_id': _arrayIds.pop(), '_type': _type, 'priority': 0
|
||||||
|
},
|
||||||
|
childNodeUrl;
|
||||||
|
childTreeInfo[_type] = childDummyInfo;
|
||||||
|
|
||||||
|
childNodeUrl = module.generate_url(
|
||||||
|
null, 'nodes', childDummyInfo, true, childTreeInfo
|
||||||
|
);
|
||||||
|
console.debug("Fetching node information using: ", childNodeUrl);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: childNodeUrl,
|
||||||
|
dataType: "json",
|
||||||
|
success: function(res) {
|
||||||
|
if (res.success) {
|
||||||
|
arrayChildNodeData.push(res.data);
|
||||||
|
}
|
||||||
|
fetchNodeInfo(_callback);
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
try {
|
||||||
|
var err = $.parseJSON(xhr.responseText);
|
||||||
|
if (err.success == 0) {
|
||||||
|
var alertifyWrapper = new AlertifyWrapper();
|
||||||
|
alertifyWrapper.error(err.errormsg);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
fetchNodeInfo(_callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (!module) {
|
||||||
|
console.warning(
|
||||||
|
"Developer: Couldn't find the module for the given child: ",
|
||||||
|
_.clone(arguments)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pgBrowser.tree.wasLoad(_node) || pgBrowser.tree.isLeaf(_node)) {
|
||||||
|
fetchNodeInfo(function() {
|
||||||
|
console.log('Append this nodes:', arrayChildNodeData);
|
||||||
|
_.each(arrayChildNodeData, function(_nodData) {
|
||||||
|
pgBrowser.Events.trigger(
|
||||||
|
'pgadmin:browser:tree:add', _nodData, _treeHierarchy
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_callback) {
|
||||||
|
_callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (_callback) {
|
||||||
|
_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_refreshNode: function(_ctx, _d) {
|
_refreshNode: function(_ctx, _d) {
|
||||||
var traverseNodes = function(_d) {
|
var traverseNodes = function(_d) {
|
||||||
var _ctx = this, idx = 0, ctx, d,
|
var _ctx = this, idx = 0, ctx, d,
|
||||||
|
@ -652,18 +652,7 @@ define([
|
|||||||
if (res.success == 0) {
|
if (res.success == 0) {
|
||||||
pgBrowser.report_error(res.errormsg, res.info);
|
pgBrowser.report_error(res.errormsg, res.info);
|
||||||
} else {
|
} else {
|
||||||
var n = t.next(i);
|
pgBrowser.removeTreeNode(i, true);
|
||||||
if (!n || !n.length) {
|
|
||||||
n = t.prev(i);
|
|
||||||
if (!n || !n.length) {
|
|
||||||
n = t.parent(i);
|
|
||||||
t.setInode(n, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.remove(i);
|
|
||||||
if (n.length) {
|
|
||||||
t.select(n);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -1320,9 +1309,14 @@ define([
|
|||||||
pgBrowser.Events.trigger(
|
pgBrowser.Events.trigger(
|
||||||
'pgadmin:browser:tree:update',
|
'pgadmin:browser:tree:update',
|
||||||
_old, _new, info, {
|
_old, _new, info, {
|
||||||
success: function() {
|
success: function(_item, _newNodeData, _oldNodeData) {
|
||||||
pgBrowser.Events.trigger(
|
pgBrowser.Events.trigger(
|
||||||
'pgadmin:browser:node:updated', _new
|
'pgadmin:browser:node:updated', _item, _newNodeData,
|
||||||
|
_oldNodeData
|
||||||
|
);
|
||||||
|
pgBrowser.Events.trigger(
|
||||||
|
'pgadmin:browser:node:' + _newNodeData._type + ':updated',
|
||||||
|
_item, _newNodeData, _oldNodeData
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,7 @@ TODO LIST FOR BACKUP:
|
|||||||
|
|
||||||
// Define list of nodes on which backup context menu option appears
|
// Define list of nodes on which backup context menu option appears
|
||||||
var backup_supported_nodes = [
|
var backup_supported_nodes = [
|
||||||
'database', 'schema', 'table'
|
'database', 'schema', 'table', 'partition'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,6 +127,10 @@ def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id):
|
|||||||
return internal_server_error(errormsg=str(msg))
|
return internal_server_error(errormsg=str(msg))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# if object type is partition then it is nothing but a table.
|
||||||
|
if obj_type == 'partition':
|
||||||
|
obj_type = 'table'
|
||||||
|
|
||||||
# Get the object as per the object type
|
# Get the object as per the object type
|
||||||
command_obj = ObjectRegistry.get_object(obj_type, conn_id=conn_id, sid=sid,
|
command_obj = ObjectRegistry.get_object(obj_type, conn_id=conn_id, sid=sid,
|
||||||
did=did, obj_id=obj_id, cmd_type=cmd_type,
|
did=did, obj_id=obj_id, cmd_type=cmd_type,
|
||||||
@ -201,12 +205,14 @@ def panel(trans_id, is_query_tool, editor_title):
|
|||||||
else:
|
else:
|
||||||
new_browser_tab = 'false'
|
new_browser_tab = 'false'
|
||||||
|
|
||||||
return render_template("datagrid/index.html", _=gettext, uniqueId=trans_id,
|
return render_template(
|
||||||
is_query_tool=is_query_tool,
|
"datagrid/index.html", _=gettext, uniqueId=trans_id,
|
||||||
editor_title=editor_title, script_type_url=sURL,
|
is_query_tool=is_query_tool,
|
||||||
is_desktop_mode=app.PGADMIN_RUNTIME,
|
editor_title=editor_title, script_type_url=sURL,
|
||||||
is_linux=is_linux_platform,
|
is_desktop_mode=app.PGADMIN_RUNTIME,
|
||||||
is_new_browser_tab=new_browser_tab)
|
is_linux=is_linux_platform,
|
||||||
|
is_new_browser_tab=new_browser_tab
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route(
|
@blueprint.route(
|
||||||
@ -346,6 +352,8 @@ def validate_filter(sid, did, obj_id):
|
|||||||
@login_required
|
@login_required
|
||||||
def script():
|
def script():
|
||||||
"""render the required javascript"""
|
"""render the required javascript"""
|
||||||
return Response(response=render_template("datagrid/js/datagrid.js", _=gettext),
|
return Response(
|
||||||
status=200,
|
response=render_template("datagrid/js/datagrid.js", _=gettext),
|
||||||
mimetype="application/javascript")
|
status=200, mimetype="application/javascript"
|
||||||
|
)
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ define([
|
|||||||
// Define list of nodes on which view data option appears
|
// Define list of nodes on which view data option appears
|
||||||
var supported_nodes = [
|
var supported_nodes = [
|
||||||
'table', 'view', 'mview',
|
'table', 'view', 'mview',
|
||||||
'foreign-table', 'catalog_object'
|
'foreign-table', 'catalog_object', 'partition'
|
||||||
],
|
],
|
||||||
|
|
||||||
/* Enable/disable View data menu in tools based
|
/* Enable/disable View data menu in tools based
|
||||||
|
@ -150,7 +150,7 @@ define([
|
|||||||
|
|
||||||
var maintenance_supported_nodes = [
|
var maintenance_supported_nodes = [
|
||||||
'database', 'table', 'primary_key',
|
'database', 'table', 'primary_key',
|
||||||
'unique_constraint', 'index'
|
'unique_constraint', 'index', 'partition'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,7 +180,7 @@ define([
|
|||||||
|
|
||||||
var menus = [{
|
var menus = [{
|
||||||
name: 'maintenance', module: this,
|
name: 'maintenance', module: this,
|
||||||
applies: ['tools'], callback: 'callback_maintenace',
|
applies: ['tools'], callback: 'callback_maintenance',
|
||||||
priority: 10, label: gettext('Maintenance...'),
|
priority: 10, label: gettext('Maintenance...'),
|
||||||
icon: 'fa fa-wrench', enable: menu_enabled
|
icon: 'fa fa-wrench', enable: menu_enabled
|
||||||
}];
|
}];
|
||||||
@ -190,7 +190,7 @@ define([
|
|||||||
menus.push({
|
menus.push({
|
||||||
name: 'maintenance_context_' + maintenance_supported_nodes[idx],
|
name: 'maintenance_context_' + maintenance_supported_nodes[idx],
|
||||||
node: maintenance_supported_nodes[idx], module: this,
|
node: maintenance_supported_nodes[idx], module: this,
|
||||||
applies: ['context'], callback: 'callback_maintenace',
|
applies: ['context'], callback: 'callback_maintenance',
|
||||||
priority: 10, label: gettext('Maintenance...'),
|
priority: 10, label: gettext('Maintenance...'),
|
||||||
icon: 'fa fa-wrench', enable: menu_enabled
|
icon: 'fa fa-wrench', enable: menu_enabled
|
||||||
});
|
});
|
||||||
@ -201,7 +201,7 @@ define([
|
|||||||
/*
|
/*
|
||||||
Open the dialog for the maintenance functionality
|
Open the dialog for the maintenance functionality
|
||||||
*/
|
*/
|
||||||
callback_maintenace: function(args, item) {
|
callback_maintenance: function(args, item) {
|
||||||
var i = item || pgBrowser.tree.selected(),
|
var i = item || pgBrowser.tree.selected(),
|
||||||
server_data = null;
|
server_data = null;
|
||||||
|
|
||||||
@ -320,7 +320,10 @@ define([
|
|||||||
if (treeInfo.schema != undefined) {
|
if (treeInfo.schema != undefined) {
|
||||||
schema = treeInfo.schema._label;
|
schema = treeInfo.schema._label;
|
||||||
}
|
}
|
||||||
if (treeInfo.table != undefined) {
|
|
||||||
|
if (treeInfo.partition != undefined) {
|
||||||
|
table = treeInfo.partition._label;
|
||||||
|
} else if (treeInfo.table != undefined) {
|
||||||
table = treeInfo.table._label;
|
table = treeInfo.table._label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +228,8 @@ define([
|
|||||||
var restore_supported_nodes = [
|
var restore_supported_nodes = [
|
||||||
'database', 'schema',
|
'database', 'schema',
|
||||||
'table', 'function',
|
'table', 'function',
|
||||||
'trigger', 'index'
|
'trigger', 'index',
|
||||||
|
'partition'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,6 +29,7 @@ class PgAdminModule(Blueprint):
|
|||||||
kwargs.setdefault('template_folder', 'templates')
|
kwargs.setdefault('template_folder', 'templates')
|
||||||
kwargs.setdefault('static_folder', 'static')
|
kwargs.setdefault('static_folder', 'static')
|
||||||
self.submodules = []
|
self.submodules = []
|
||||||
|
self.parentmodules = []
|
||||||
|
|
||||||
super(PgAdminModule, self).__init__(name, import_name, **kwargs)
|
super(PgAdminModule, self).__init__(name, import_name, **kwargs)
|
||||||
|
|
||||||
@ -59,6 +60,8 @@ class PgAdminModule(Blueprint):
|
|||||||
super(PgAdminModule, self).register(app, options, first_registration)
|
super(PgAdminModule, self).register(app, options, first_registration)
|
||||||
|
|
||||||
for module in self.submodules:
|
for module in self.submodules:
|
||||||
|
if first_registration:
|
||||||
|
module.parentmodules.append(self)
|
||||||
app.register_blueprint(module)
|
app.register_blueprint(module)
|
||||||
|
|
||||||
def get_own_stylesheets(self):
|
def get_own_stylesheets(self):
|
||||||
|
@ -40,3 +40,11 @@ class ConnectionLost(HTTPException):
|
|||||||
'conn_id': self.conn_id
|
'conn_id': self.conn_id
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Connection (id #{2}) lost for the server (#{0}) on " \
|
||||||
|
"database ({1})".format(self.sid, self.db, self.conn_id)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Connection (id #{2}) lost for the server (#{0}) on " \
|
||||||
|
"database ({1})".format(self.sid, self.db, self.conn_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user