diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js index 219859985..e2816761c 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js @@ -7,6 +7,8 @@ // ////////////////////////////////////////////////////////////// +import { getNodePartitionTableSchema } from './partition.ui'; + define([ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.browser', @@ -314,36 +316,14 @@ function( ); }, }, + getSchema: function(treeNodeInfo, itemNodeData) { + return getNodePartitionTableSchema(treeNodeInfo, itemNodeData, pgBrowser); + }, model: pgBrowser.Node.Model.extend({ defaults: { name: undefined, oid: undefined, - spcoid: undefined, - spcname: undefined, - relowner: undefined, - relacl: undefined, - relhasoids: undefined, - relhassubclass: undefined, - reltuples: undefined, description: undefined, - conname: undefined, - conkey: undefined, - isrepl: undefined, - triggercount: undefined, - relpersistence: undefined, - fillfactor: undefined, - reloftype: undefined, - typname: undefined, - labels: undefined, - providers: undefined, - is_sys_table: undefined, - coll_inherits: [], - hastoasttable: true, - toast_autovacuum_enabled: 'x', - autovacuum_enabled: 'x', - primary_key: [], - partitions: [], - partition_type: 'range', is_partitioned: false, partition_value: undefined, }, @@ -364,851 +344,22 @@ function( }, schema: [{ id: 'name', label: gettext('Name'), type: 'text', - mode: ['properties', 'create', 'edit'], disabled: 'inSchema', + mode: ['properties', 'create', 'edit'], },{ id: 'oid', label: gettext('OID'), type: 'text', mode: ['properties'], - },{ - id: 'relowner', label: gettext('Owner'), type: 'text', node: 'role', - mode: ['properties', 'create', 'edit'], select2: {allowClear: false}, - disabled: 'inSchema', control: 'node-list-by-name', },{ id: 'schema', label: gettext('Schema'), type: 'text', node: 'schema', - control: 'node-list-by-name', mode: ['create', 'edit', 'properties'], - disabled: 'inSchema', filter: function(d) { - // If schema name start with pg_* then we need to exclude them - if(d && d.label.match(/^pg_/)) - { - return false; - } - return true; - }, cache_node: 'database', cache_level: 'database', - },{ - id: 'spcname', label: gettext('Tablespace'), node: 'tablespace', - type: 'text', control: 'node-list-by-name', disabled: 'inSchema', - mode: ['properties', 'create', 'edit'], - filter: function(d) { - // If tablespace name is not "pg_global" then we need to exclude them - 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; - }, + mode: ['create', 'edit', 'properties'], },{ 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; - }, - readonly: function(m) { - if (!m.isNew()) - return true; - return false; - }, },{ - id: 'description', label: gettext('Comment'), type: 'multiline', - mode: ['properties', 'create', 'edit'], disabled: 'inSchema', - }, - { id: 'partition_value', label:gettext('Partition Scheme'), type: 'text', visible: false, },{ - id: 'coll_inherits', label: gettext('Inherited from table(s)'), - type: 'text', group: gettext('Advanced'), mode: ['properties'], - },{ - id: 'Columns', type: 'group', label: gettext('Columns'), - mode: ['edit', 'create'], min_version: 100000, - // Always hide in case of partition table. - visible: function() { return false; }, - },{ - // Tab control for columns - id: 'columns', label: gettext('Columns'), type: 'collection', - group: gettext('Columns'), - model: pgBrowser.Nodes['column'].model, - subnode: pgBrowser.Nodes['column'].model, - mode: ['create', 'edit'], - 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', - canEdit: true, canDelete: true, - // For each row edit/delete button enable/disable - canEditRow: 'check_grid_row_edit_delete', - canDeleteRow: 'check_grid_row_edit_delete', - uniqueCol : ['name'], - columns : ['name' , 'cltype', 'attlen', 'attprecision', 'attnotnull', 'is_primary_key'], - control: Backform.UniqueColCollectionControl.extend({ - initialize: function() { - Backform.UniqueColCollectionControl.prototype.initialize.apply(this, arguments); - var self = this, - collection = self.model.get(self.field.get('name')); - - collection.on('change:is_primary_key', function(local_model) { - var primary_key_coll = self.model.get('primary_key'), - column_name = local_model.get('name'), - primary_key, primary_key_column_coll; - - if(local_model.get('is_primary_key')) { - // Add column to primary key. - if (primary_key_coll.length < 1) { - primary_key = new (primary_key_coll.model)({}, { - top: self.model, - collection: primary_key_coll, - handler: primary_key_coll, - }); - primary_key_coll.add(primary_key); - } else { - primary_key = primary_key_coll.first(); - } - // Do not alter existing primary key columns. - if (_.isUndefined(primary_key.get('oid'))) { - primary_key_column_coll = primary_key.get('columns'); - var primary_key_column_exist = primary_key_column_coll.where({column:column_name}); - - if (primary_key_column_exist.length == 0) { - var primary_key_column = new (primary_key_column_coll.model)( - {column: column_name}, { silent: true, - top: self.model, - collection: primary_key_coll, - handler: primary_key_coll, - }); - - primary_key_column_coll.add(primary_key_column); - } - - primary_key_column_coll.trigger( - 'pgadmin:multicolumn:updated', primary_key_column_coll - ); - } - - } else { - // remove column from primary key. - if (primary_key_coll.length > 0) { - primary_key = primary_key_coll.first(); - // Do not alter existing primary key columns. - if (!_.isUndefined(primary_key.get('oid'))) { - return; - } - - primary_key_column_coll = primary_key.get('columns'); - var removedCols = primary_key_column_coll.where({ - column: column_name, - }); - if (removedCols.length > 0) { - primary_key_column_coll.remove(removedCols); - _.each(removedCols, function(m) { - m.destroy(); - }); - if (primary_key_column_coll.length == 0) { - setTimeout(function () { - // There will be only on primary key so remove the first one. - primary_key_coll.remove(primary_key_coll.first()); - /* Ideally above line of code should be "primary_key_coll.reset()". - * But our custom DataCollection (extended from Backbone collection in datamodel.js) - * does not respond to reset event, it only supports add, remove, change events. - * And hence no custom event listeners/validators get called for reset event. - */ - }, 10); - } - } - primary_key_column_coll.trigger('pgadmin:multicolumn:updated', primary_key_column_coll); - } - } - }); - }, - remove: function() { - var collection = this.model.get(this.field.get('name')); - if (collection) { - collection.off('change:is_primary_key'); - } - - Backform.UniqueColCollectionControl.prototype.remove.apply(this, arguments); - }, - }), - allowMultipleEmptyRow: false, - },{ - id: 'inherited_tables_cnt', label: gettext('Inherited tables count'), - type: 'text', mode: ['properties'], group: gettext('Advanced'), - disabled: 'inSchema', - },{ - // Here we will create tab control for constraints - type: 'nested', control: 'tab', group: gettext('Constraints'), - mode: ['edit', 'create'], - schema: [{ - id: 'primary_key', label: gettext('Primary key'), - model: pgBrowser.Nodes['primary_key'].model, - subnode: pgBrowser.Nodes['primary_key'].model, - editable: false, type: 'collection', - group: gettext('Primary Key'), mode: ['edit', 'create'], - canEdit: true, canDelete: true, deps:['is_partitioned'], - control: 'unique-col-collection', - columns : ['name', 'columns'], - canAdd: function(m) { - if (m.get('is_partitioned') && !_.isUndefined(m.top.node_info) && !_.isUndefined(m.top.node_info.server) - && !_.isUndefined(m.top.node_info.server.version) && - m.top.node_info.server.version < 110000) { - setTimeout(function() { - var coll = m.get('primary_key'); - coll.remove(coll.filter(function() { return true; })); - }, 10); - return false; - } - - return true; - }, - canAddRow: function(m) { - // User can only add one primary key - var columns = m.get('columns'); - - return (m.get('primary_key') && - m.get('primary_key').length < 1 && - _.some(columns.pluck('name'))); - }, - },{ - id: 'foreign_key', label: gettext('Foreign key'), - model: pgBrowser.Nodes['foreign_key'].model, - subnode: pgBrowser.Nodes['foreign_key'].model, - editable: false, type: 'collection', - group: gettext('Foreign Key'), mode: ['edit', 'create'], - canEdit: true, canDelete: true, deps:['is_partitioned'], - control: 'unique-col-collection', - canAdd: function(m) { - if (m.get('is_partitioned')) { - setTimeout(function() { - var coll = m.get('foreign_key'); - coll.remove(coll.filter(function() { return true; })); - }, 10); - return false; - } - - return true; - }, - columns : ['name', 'columns'], - canAddRow: function(m) { - // User can only add if there is at least one column with name. - var columns = m.get('columns'); - return _.some(columns.pluck('name')); - }, - },{ - id: 'check_constraint', label: gettext('Check constraint'), - model: pgBrowser.Nodes['check_constraint'].model, - subnode: pgBrowser.Nodes['check_constraint'].model, - editable: false, type: 'collection', - group: gettext('Check'), mode: ['edit', 'create'], - canEdit: true, canDelete: true, deps:['is_partitioned'], - control: 'unique-col-collection', - canAdd: true, - columns : ['name', 'consrc'], - },{ - id: 'unique_constraint', label: gettext('Unique Constraint'), - model: pgBrowser.Nodes['unique_constraint'].model, - subnode: pgBrowser.Nodes['unique_constraint'].model, - editable: false, type: 'collection', - group: gettext('Unique'), mode: ['edit', 'create'], - canEdit: true, canDelete: true, deps:['is_partitioned'], - control: 'unique-col-collection', - columns : ['name', 'columns'], - canAdd: function(m) { - if (m.get('is_partitioned')) { - setTimeout(function() { - var coll = m.get('unique_constraint'); - coll.remove(coll.filter(function() { return true; })); - }, 10); - return false; - } - - return true; - }, - canAddRow: function(m) { - // User can only add if there is at least one column with name. - var columns = m.get('columns'); - return _.some(columns.pluck('name')); - }, - },{ - id: 'exclude_constraint', label: gettext('Exclude constraint'), - model: pgBrowser.Nodes['exclusion_constraint'].model, - subnode: pgBrowser.Nodes['exclusion_constraint'].model, - editable: false, type: 'collection', - group: gettext('Exclude'), mode: ['edit', 'create'], - canEdit: true, canDelete: true, deps:['is_partitioned'], - control: 'unique-col-collection', - columns : ['name', 'columns', 'constraint'], - canAdd: function(m) { - if (m.get('is_partitioned')) { - setTimeout(function() { - var coll = m.get('exclude_constraint'); - coll.remove(coll.filter(function() { return true; })); - }, 10); - return false; - } - - return true; - }, - canAddRow: function(m) { - // User can only add if there is at least one column with name. - var columns = m.get('columns'); - return _.some(columns.pluck('name')); - }, - }], - },{ - id: 'typname', label: gettext('Of type'), type: 'text', - mode: ['properties', 'create', 'edit'], group: gettext('Advanced'), - disabled: 'checkOfType', url: 'get_oftype', - deps: ['coll_inherits', 'is_partitioned'], - transform: function(data, cell) { - var control = cell || this, - m = control.model; - m.of_types_tables = data; - return data; - }, - control: Backform.NodeAjaxOptionsControl.extend({ - // When of_types changes we need to clear columns collection - onChange: function() { - Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments); - var self = this, - tbl_name = self.model.get('typname'), - data = undefined, - arg = undefined, - column_collection = self.model.get('columns'); - - if (!_.isUndefined(tbl_name) && - tbl_name !== '' && column_collection.length !== 0) { - var msg = gettext('Changing of table type will clear columns collection.'); - Alertify.confirm(msg, function (e) { - if (e) { - // User clicks Ok, lets clear columns collection - column_collection.reset(); - } else { - return this; - } - }); - } else if (!_.isUndefined(tbl_name) && tbl_name === '') { - column_collection.reset(); - } - - // Run Ajax now to fetch columns - if (!_.isUndefined(tbl_name) && tbl_name !== '') { - arg = { 'tname': tbl_name }; - data = self.model.fetch_columns_ajax.apply(self, [arg]); - // Add into column collection - column_collection.set(data, { merge:false,remove:false }); - } - }, - }), - },{ - id: 'fillfactor', label: gettext('Fill factor'), type: 'int', - mode: ['create', 'edit'], min: 10, max: 100, - group: gettext('Advanced'), - disabled: function(m) { - if(m.get('is_partitioned')) { - return true; - } - return m.inSchema(); - }, - },{ - id: 'relhasoids', label: gettext('Has OIDs?'), cell: 'switch', - type: 'switch', mode: ['properties', 'create', 'edit'], - disabled: true, group: gettext('Advanced'), - },{ - id: 'relpersistence', label: gettext('Unlogged?'), cell: 'switch', - type: 'switch', mode: ['properties', 'create', 'edit'], - disabled: 'inSchemaWithModelCheck', - group: gettext('Advanced'), - },{ - id: 'conname', label: gettext('Primary key'), cell: 'string', - type: 'text', mode: ['properties'], group: gettext('Advanced'), - disabled: 'inSchema', - },{ - id: 'reltuples', label: gettext('Rows (estimated)'), cell: 'string', - type: 'text', mode: ['properties'], group: gettext('Advanced'), - disabled: 'inSchema', - },{ - id: 'rows_cnt', label: gettext('Rows (counted)'), cell: 'string', - type: 'text', mode: ['properties'], group: gettext('Advanced'), - disabled: 'inSchema', - },{ - id: 'relhassubclass', label: gettext('Inherits tables?'), cell: 'switch', - type: 'switch', mode: ['properties'], group: gettext('Advanced'), - disabled: 'inSchema', - },{ - id: 'is_sys_table', label: gettext('System table?'), cell: 'switch', - type: 'switch', mode: ['properties'], - disabled: 'inSchema', - },{ - type: 'nested', control: 'fieldset', label: gettext('Like'), - group: gettext('Advanced'), - schema:[{ - id: 'like_relation', label: gettext('Relation'), - type: 'text', mode: ['create', 'edit'], deps: ['typname'], - control: 'node-ajax-options', url: 'get_relations', - disabled: 'isLikeDisable', group: gettext('Like'), - },{ - id: 'like_default_value', label: gettext('With default values?'), - type: 'switch', mode: ['create', 'edit'], deps: ['typname'], - disabled: 'isLikeDisable', group: gettext('Like'), - },{ - id: 'like_constraints', label: gettext('With constraints?'), - type: 'switch', mode: ['create', 'edit'], deps: ['typname'], - disabled: 'isLikeDisable', group: gettext('Like'), - },{ - id: 'like_indexes', label: gettext('With indexes?'), - type: 'switch', mode: ['create', 'edit'], deps: ['typname'], - disabled: 'isLikeDisable', group: gettext('Like'), - },{ - id: 'like_storage', label: gettext('With storage?'), - type: 'switch', mode: ['create', 'edit'], deps: ['typname'], - disabled: 'isLikeDisable', group: gettext('Like'), - },{ - id: 'like_comments', label: gettext('With comments?'), - type: 'switch', mode: ['create', 'edit'], deps: ['typname'], - 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: gettext('Range'), value: 'range', - },{ - label: gettext('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; - }, - readonly: function(m) { - return !m.isNew(); - }, - disabled: function(m) { - return !m.get('is_partitioned'); - }, - },{ - 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() { 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: '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: true, canDelete: true, - customDeleteTitle: gettext('Detach Partition'), - customDeleteMsg: gettext('Are you sure you wish to detach this partition?'), - columns:['is_attach', 'partition_name', 'is_default', 'values_from', 'values_to', 'values_in', 'values_modulus', 'values_remainder'], - 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, - }) - .done(function(res) { - if (res.success == 1) { - self.model.table_options = res.data; - } - else { - Alertify.alert( - gettext('Error fetching tables to be attached'), res.data.result - ); - } - }) - .fail(function(xhr, status, error) { - Alertify.pgRespErrorNotify(xhr, error, gettext('Error fetching tables to be attached')); - }); - } - }, - } - ), - 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() { return true; })); - }, 10); - } - }, - },{ - id: 'partition_note', label: gettext('Partition'), - type: 'note', group: 'partition', - text: [ - '', - ].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 - // (auto vacuum). - type: 'nested', control: 'tab', group: gettext('Parameter'), - mode: ['edit', 'create'], deps: ['is_partitioned'], - schema: Backform.VacuumSettingsSchema, - },{ - id: 'relacl_str', label: gettext('Privileges'), disabled: 'inSchema', - type: 'text', mode: ['properties'], group: gettext('Security'), - }, pgBrowser.SecurityGroupSchema,{ - id: 'relacl', label: gettext('Privileges'), type: 'collection', - group: 'security', control: 'unique-col-collection', - model: pgBrowser.Node.PrivilegeRoleModel.extend({ - privileges: ['a','r','w','d','D','x','t']}), - mode: ['edit', 'create'], canAdd: true, canDelete: true, - uniqueCol : ['grantee'], - },{ - id: 'seclabels', label: gettext('Security labels'), canEdit: false, - model: pgBrowser.SecLabelModel, editable: false, canAdd: true, - type: 'collection', min_version: 90100, mode: ['edit', 'create'], - group: 'security', canDelete: true, control: 'unique-col-collection', - },{ - id: 'vacuum_settings_str', label: gettext('Storage settings'), - type: 'multiline', group: gettext('Advanced'), mode: ['properties'], + id: 'description', label: gettext('Comment'), type: 'multiline', + mode: ['properties', 'create', 'edit'], }], - sessChanged: function() { - /* If only custom autovacuum option is enabled then check if the options table is also changed. */ - if(_.size(this.sessAttrs) == 2 && this.sessAttrs['autovacuum_custom'] && this.sessAttrs['toast_autovacuum']) { - return this.get('vacuum_table').sessChanged() || this.get('vacuum_toast').sessChanged(); - } - if(_.size(this.sessAttrs) == 1 && (this.sessAttrs['autovacuum_custom'] || this.sessAttrs['toast_autovacuum'])) { - return this.get('vacuum_table').sessChanged() || this.get('vacuum_toast').sessChanged(); - } - return pgBrowser.DataModel.prototype.sessChanged.apply(this); - }, - validate: function(keys) { - var msg, - name = this.get('name'), - schema = this.get('schema'), - relowner = this.get('relowner'), - is_partitioned = this.get('is_partitioned'), - partition_keys = this.get('partition_keys'); - - // If nothing to validate or VacuumSetting keys then - // return from here - if ( keys && (keys.length == 0 - || _.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.'); - this.errorModel.set('name', msg); - return msg; - } else if (_.isUndefined(schema) || _.isNull(schema) || - String(schema).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Table schema cannot be empty.'); - this.errorModel.set('schema', msg); - return msg; - } else if (_.isUndefined(relowner) || _.isNull(relowner) || - String(relowner).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Table owner cannot be empty.'); - this.errorModel.set('relowner', msg); - return msg; - } else 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; - } - return null; - }, - // We will disable everything if we are under catalog node - inSchema: function() { - if(this.node_info && 'catalog' in this.node_info) - { - return true; - } - return false; - }, - isInheritedTable: function(m) { - if(!m.inSchema.apply(this, [m])) { - // Either of_types or coll_inherits has value - return ( - (_.isUndefined(m.get('coll_inherits')) || m.get('coll_inherits').length == 0) - && - (_.isUndefined(m.get('typname')) || String(m.get('typname')).replace(/^\s+|\s+$/g, '') === '') - ); - } - return false; - }, - // Oftype is defined? - checkInheritance: function(m) { - // 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]) && - ( _.isUndefined(m.get('typname')) || - _.isNull(m.get('typname')) || - String(m.get('typname')).replace(/^\s+|\s+$/g, '') == '')) { - return false; - } - return true; - }, - // We will disable Like if ofType is defined - isLikeDisable: function(m) { - if(!m.inSchemaWithModelCheck.apply(this, [m]) && - ( _.isUndefined(m.get('typname')) || - _.isNull(m.get('typname')) || - String(m.get('typname')).replace(/^\s+|\s+$/g, '') == '')) { - return false; - } - return true; - }, - // Check for column grid when to Add - check_grid_add_condition: function(m) { - var enable_flag = true; - if(!m.inSchema.apply(this, [m])) { - // if of_type then disable add in grid - if (!_.isUndefined(m.get('typname')) && - !_.isNull(m.get('typname')) && - m.get('typname') !== '') { - enable_flag = false; - } - } - return enable_flag; - }, - // Check for column grid when to edit/delete (for each row) - check_grid_row_edit_delete: function(m) { - var flag = true; - if(!_.isUndefined(m.get('inheritedfrom')) && - !_.isNull(m.get('inheritedfrom')) && - String(m.get('inheritedfrom')).replace(/^\s+|\s+$/g, '') !== '') { - flag = false; - } - return flag; - }, - // We will disable it if Inheritance is defined - checkOfType: function(m) { - // 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]) && - (_.isUndefined(m.get('coll_inherits')) || - _.isNull(m.get('coll_inherits')) || - String(m.get('coll_inherits')).replace(/^\s+|\s+$/g, '') == '')) { - return false; - } - return true; - }, - // We will check if we are under schema node & in 'create' mode - inSchemaWithModelCheck: function(m) { - if(this.node_info && 'schema' in this.node_info) - { - // We will disbale control if it's in 'edit' mode - return !m.isNew(); - } - return true; - }, - isTableAutoVacuumEnable: function(m) { - // We need to check additional condition to toggle enable/disable - // for table auto-vacuum - if(!m.inSchema.apply(this, [m]) && - m.get('autovacuum_enabled') === true) { - return false; - } - return true; - }, - isToastTableAutoVacuumEnable: function(m) { - // We need to check additional condition to toggle enable/disable - // for toast table auto-vacuum - if(!m.inSchemaWithModelCheck.apply(this, [m]) && - m.get('toast_autovacuum_enabled') == true) { - return false; - } - return true; - }, - fetch_columns_ajax: function(arg) { - var self = this, - url = 'get_columns', - m = self.model.top || self.model, - data = undefined, - node = this.field.get('schema_node'), - node_info = this.field.get('node_info'), - full_url = node.generate_url.apply( - node, [ - null, url, this.field.get('node_data'), - this.field.get('url_with_id') || false, node_info, - ] - ), - cache_level = this.field.get('cache_level') || node.type, - cache_node = this.field.get('cache_node'); - - cache_node = (cache_node && pgBrowser.Nodes['cache_node']) || node; - - m.trigger('pgadmin:view:fetching', m, self.field); - // Fetching Columns data for the selected table. - $.ajax({ - async: false, - url: full_url, - data: arg, - }) - .done(function(res) { - data = cache_node.cache(url, node_info, cache_level, res.data); - }) - .fail(function() { - m.trigger('pgadmin:view:fetch:error', m, self.field); - }); - m.trigger('pgadmin:view:fetched', m, self.field); - data = (data && data.data) || []; - return data; - }, }), canCreate: SchemaChildTreeNode.isTreeItemOfChildOfSchema, // Check to whether table has disable trigger(s) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.ui.js new file mode 100644 index 000000000..5287ec5c4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.ui.js @@ -0,0 +1,434 @@ +import gettext from 'sources/gettext'; +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import SecLabelSchema from 'top/browser/server_groups/servers/static/js/sec_label.ui'; +import _ from 'lodash'; +import { ConstraintsSchema } from '../../../static/js/table.ui'; +import { PartitionKeysSchema, PartitionsSchema } from '../../../static/js/partition.utils.ui'; +import { getNodePrivilegeRoleSchema } from '../../../../../../static/js/privilege.ui'; +import { getNodeAjaxOptions, getNodeListByName } from '../../../../../../../../static/js/node_ajax'; +import { getNodeVacuumSettingsSchema } from '../../../../../../static/js/vacuum.ui'; +import { getNodeForeignKeySchema } from '../../../constraints/foreign_key/static/js/foreign_key.ui'; +import { getNodeExclusionConstraintSchema } from '../../../constraints/exclusion_constraint/static/js/exclusion_constraint.ui'; + +export function getNodePartitionTableSchema(treeNodeInfo, itemNodeData, pgBrowser) { + const spcname = ()=>getNodeListByName('tablespace', treeNodeInfo, itemNodeData, {}, (m)=>{ + return (m.label != 'pg_global'); + }); + + let tableNode = pgBrowser.Nodes['table']; + + return new PartitionTableSchema( + { + relowner: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData), + schema: ()=>getNodeListByName('schema', treeNodeInfo, itemNodeData, { + cacheLevel: 'database', + cacheNode: 'database', + }, (d)=>{ + // If schema name start with pg_* then we need to exclude them + if(d && d.label.match(/^pg_/)) + { + return false; + } + return true; + }), + spcname: spcname, + coll_inherits: ()=>getNodeAjaxOptions('get_inherits', tableNode, treeNodeInfo, itemNodeData), + typname: ()=>getNodeAjaxOptions('get_oftype', tableNode, treeNodeInfo, itemNodeData), + like_relation: ()=>getNodeAjaxOptions('get_relations', tableNode, treeNodeInfo, itemNodeData), + }, + treeNodeInfo, + { + vacuum_settings: ()=>getNodeVacuumSettingsSchema(tableNode, treeNodeInfo, itemNodeData), + constraints: ()=>new ConstraintsSchema( + treeNodeInfo, + ()=>getNodeForeignKeySchema(treeNodeInfo, itemNodeData, pgBrowser, true), + ()=>getNodeExclusionConstraintSchema(treeNodeInfo, itemNodeData, pgBrowser, true), + {spcname: spcname}, + ), + }, + (privileges)=>getNodePrivilegeRoleSchema(tableNode, treeNodeInfo, itemNodeData, privileges), + (params)=>{ + return getNodeAjaxOptions('get_columns', tableNode, treeNodeInfo, itemNodeData, {urlParams: params, useCache:false}); + }, + { + relowner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name, + schema: treeNodeInfo.schema._label, + } + ); +} + +export default class PartitionTableSchema extends BaseUISchema { + constructor(fieldOptions={}, nodeInfo, schemas, getPrivilegeRoleSchema, getColumns, initValues) { + super({ + name: undefined, + oid: undefined, + spcoid: undefined, + spcname: undefined, + relowner: undefined, + relacl: undefined, + relhasoids: undefined, + relhassubclass: undefined, + reltuples: undefined, + description: undefined, + conname: undefined, + conkey: undefined, + isrepl: undefined, + triggercount: undefined, + relpersistence: undefined, + fillfactor: undefined, + reloftype: undefined, + typname: undefined, + labels: undefined, + providers: undefined, + is_sys_table: undefined, + coll_inherits: [], + hastoasttable: true, + toast_autovacuum_enabled: 'x', + autovacuum_enabled: 'x', + primary_key: [], + partitions: [], + partition_type: 'range', + is_partitioned: false, + partition_value: undefined, + ...initValues, + }); + + this.fieldOptions = fieldOptions; + this.schemas = schemas; + this.getPrivilegeRoleSchema = getPrivilegeRoleSchema; + this.nodeInfo = nodeInfo; + this.getColumns = getColumns; + + this.partitionKeysObj = new PartitionKeysSchema(); + this.partitionsObj = new PartitionsSchema(this.nodeInfo); + this.constraintsObj = this.schemas.constraints(); + this.vacuumSettingsSchema = this.schemas.vacuum_settings(); + } + + get idAttribute() { + return 'oid'; + } + + initialise(state) { + this.changeColumnOptions(state.columns); + } + + inSchemaWithModelCheck(state) { + if(this.nodeInfo && 'schema' in this.nodeInfo) { + return !this.isNew(state); + } + return false; + } + + isPartitioned(state) { + if(state.is_partitioned) { + return true; + } + return this.inCatalog(); + } + + changeColumnOptions(columns) { + let colOptions = (columns||[]).map((c)=>({label: c.name, value: c.name, image:'icon-column'})); + this.constraintsObj.changeColumnOptions(colOptions); + this.partitionKeysObj.changeColumnOptions(colOptions); + this.partitionsObj.changeColumnOptions(colOptions); + } + + get baseFields() { + let obj = this; + + return [{ + id: 'name', label: gettext('Name'), type: 'text', noEmpty: true, + mode: ['properties', 'create', 'edit'], readonly: this.inCatalog, + },{ + id: 'oid', label: gettext('OID'), type: 'text', mode: ['properties'], + },{ + id: 'relowner', label: gettext('Owner'), type: 'select', + options: this.fieldOptions.relowner, noEmpty: true, + mode: ['properties', 'create', 'edit'], controlProps: {allowClear: false}, + readonly: this.inCatalog, + },{ + id: 'schema', label: gettext('Schema'), type: 'select', + options: this.fieldOptions.schema, noEmpty: true, + mode: ['create', 'edit'], + readonly: this.inCatalog, + },{ + id: 'spcname', label: gettext('Tablespace'), + type: 'select', options: this.fieldOptions.spcname, + mode: ['properties', 'create', 'edit'], deps: ['is_partitioned'], + readonly: this.inCatalog, + },{ + id: 'partition', type: 'group', label: gettext('Partitions'), + mode: ['edit', 'create'], min_version: 100000, + visible: function(state) { + // Always show in case of create mode + if (obj.isNew(state) || state.is_partitioned) + return true; + return false; + }, + },{ + id: 'is_partitioned', label:gettext('Partitioned table?'), cell: 'switch', + type: 'switch', mode: ['properties', 'create', 'edit'], + min_version: 100000, + readonly: function(state) { + if (!obj.isNew(state)) + return true; + return false; + }, + },{ + id: 'is_sys_table', label: gettext('System table?'), cell: 'switch', + type: 'switch', mode: ['properties'], + disabled: this.inCatalog, + },{ + id: 'description', label: gettext('Comment'), type: 'multiline', + mode: ['properties', 'create', 'edit'], disabled: this.inCatalog, + },{ + id: 'advanced', label: gettext('Advanced'), type: 'group', + // visible: ShowAdvancedTab.show_advanced_tab, + visible: true, + },{ + id: 'coll_inherits', label: gettext('Inherited from table(s)'), + type: 'text', group: gettext('Advanced'), mode: ['properties'], + }, + { + id: 'inherited_tables_cnt', label: gettext('Inherited tables count'), + type: 'text', mode: ['properties'], group: 'advanced', + disabled: this.inCatalog, + },{ + // Here we will create tab control for constraints + type: 'nested-tab', group: gettext('Constraints'), + mode: ['edit', 'create'], + schema: obj.constraintsObj, + },{ + id: 'fillfactor', label: gettext('Fill factor'), type: 'int', + mode: ['create', 'edit'], min: 10, max: 100, + group: 'advanced', + disabled: obj.isPartitioned, + },{ + id: 'toast_tuple_target', label: gettext('Toast tuple target'), type: 'int', + mode: ['create', 'edit'], min: 128, min_version: 110000, + group: 'advanced', + disabled: obj.isPartitioned, + },{ + id: 'parallel_workers', label: gettext('Parallel workers'), type: 'int', + mode: ['create', 'edit'], group: 'advanced', min_version: 90600, + disabled: obj.isPartitioned, + }, + { + id: 'relhasoids', label: gettext('Has OIDs?'), cell: 'switch', + type: 'switch', mode: ['properties', 'create', 'edit'], + group: 'advanced', readonly: true, + disabled: function() { + if(obj.getServerVersion() >= 120000) { + return true; + } + return obj.inCatalog(); + }, + },{ + id: 'relpersistence', label: gettext('Unlogged?'), cell: 'switch', + type: 'switch', mode: ['properties', 'create', 'edit'], + disabled: obj.inSchemaWithModelCheck, + group: 'advanced', + },{ + id: 'conname', label: gettext('Primary key'), cell: 'text', + type: 'text', mode: ['properties'], group: 'advanced', + disabled: this.inCatalog, + },{ + id: 'reltuples', label: gettext('Rows (estimated)'), cell: 'text', + type: 'text', mode: ['properties'], group: 'advanced', + disabled: this.inCatalog, + },{ + id: 'rows_cnt', label: gettext('Rows (counted)'), cell: 'text', + type: 'text', mode: ['properties'], group: 'advanced', + disabled: this.inCatalog, + formatter: { + fromRaw: ()=>{ + return 0; + }, + toRaw: (backendVal)=>{ + return backendVal; + }, + }, + },{ + id: 'relhassubclass', label: gettext('Inherits tables?'), cell: 'switch', + type: 'switch', mode: ['properties'], group: 'advanced', + disabled: this.inCatalog, + },{ + id: 'partition_type', label:gettext('Partition Type'), + editable: false, type: 'select', controlProps: {allowClear: false}, + group: 'partition', deps: ['is_partitioned'], + options: function() { + var options = [{ + label: gettext('Range'), value: 'range', + },{ + label: gettext('List'), value: 'list', + }]; + + if(obj.getServerVersion() >= 110000) { + options.push({ + label: gettext('Hash'), value: 'hash', + }); + } + return Promise.resolve(options); + }, + mode:['create'], + min_version: 100000, + disabled: function(state) { + if (!state.is_partitioned) + return true; + return false; + }, + readonly: function(state) {return !obj.isNew(state);}, + }, + { + id: 'partition_keys', label:gettext('Partition Keys'), + schema: obj.partitionKeysObj, + editable: true, type: 'collection', + group: 'partition', mode: ['create'], + deps: ['is_partitioned', 'partition_type', 'typname'], + canEdit: false, canDelete: true, + canAdd: function(state) { + if (obj.isNew(state) && state.is_partitioned) + return true; + return false; + }, + canAddRow: function(state) { + let columnsExist = false; + + var maxRowCount = 1000; + if (state.partition_type && state.partition_type == 'list') + maxRowCount = 1; + + if (state.columns?.length > 0) { + columnsExist = _.some(_.map(state.columns, 'name')); + } + + if(state.partition_keys) { + return state.partition_keys.length < maxRowCount && columnsExist; + } + + return true; + }, min_version: 100000, + depChange: (state, source, topState, actionObj)=>{ + if(state.typname != actionObj.oldState.typname) { + return { + partition_keys: [], + }; + } + } + }, + { + id: 'partition_scheme', label: gettext('Partition Scheme'), + group: 'partition', mode: ['edit'], + type: (state)=>({ + type: 'note', + text: state.partition_scheme || '', + }), + min_version: 100000, + }, + { + id: 'partition_key_note', label: gettext('Partition Keys'), + type: 'note', group: 'partition', mode: ['create'], + text: [ + '', + ].join(''), + min_version: 100000, + }, + { + id: 'partitions', label: gettext('Partitions'), + schema: this.partitionsObj, + editable: true, type: 'collection', + group: 'partition', mode: ['edit', 'create'], + deps: ['is_partitioned', 'partition_type', 'typname'], + depChange: (state, source)=>{ + if(['is_partitioned', 'partition_type', 'typname'].indexOf(source[0]) >= 0 && obj.isNew(state)){ + return {'partitions': []}; + } + }, + canEdit: true, canDelete: true, + customDeleteTitle: gettext('Detach Partition'), + customDeleteMsg: gettext('Are you sure you wish to detach this partition?'), + columns:['is_attach', 'partition_name', 'is_default', 'values_from', 'values_to', 'values_in', 'values_modulus', 'values_remainder'], + canAdd: function(state) { + if (state.is_partitioned) + return true; + return false; + }, + min_version: 100000, + }, + { + id: 'partition_note', label: gettext('Partitions'), + type: 'note', group: 'partition', mode: ['create'], + text: [ + '', + ].join(''), + min_version: 100000, + }, + { + // Here - we will create tab control for storage parameters + // (auto vacuum). + type: 'nested-tab', group: gettext('Parameters'), + mode: ['edit', 'create'], deps: ['is_partitioned'], + schema: this.vacuumSettingsSchema, + }, + { + id: 'relacl_str', label: gettext('Privileges'), disabled: this.inCatalog, + type: 'text', mode: ['properties'], group: gettext('Security'), + }, + { + id: 'relacl', label: gettext('Privileges'), type: 'collection', + group: gettext('Security'), schema: this.getPrivilegeRoleSchema(['a','r','w','d','D','x','t']), + mode: ['edit', 'create'], canAdd: true, canDelete: true, + uniqueCol : ['grantee'], + },{ + id: 'seclabels', label: gettext('Security labels'), canEdit: false, + schema: new SecLabelSchema(), editable: false, canAdd: true, + type: 'collection', min_version: 90100, mode: ['edit', 'create'], + group: gettext('Security'), canDelete: true, control: 'unique-col-collection', + },{ + id: 'vacuum_settings_str', label: gettext('Storage settings'), + type: 'multiline', group: 'advanced', mode: ['properties'], + }]; + } + + validate(state, setError) { + if (state.is_partitioned && this.isNew(state) && + (!state.partition_keys || state.partition_keys && state.partition_keys.length <= 0)) { + setError('partition_keys', gettext('Please specify at least one key for partitioned table.')); + return true; + } + return false; + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.ui.js index 0c90010b3..51ad71d7c 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.ui.js @@ -118,17 +118,17 @@ export class PartitionsSchema extends BaseUISchema { mode: ['properties'], },{ id: 'is_attach', label:gettext('Operation'), cell: 'select', type: 'select', - minWidth: 75, options: [ + minWidth: 120, options: [ {label: gettext('Attach'), value: true}, {label: gettext('Create'), value: false}, - ], + ], controlProps: {allowClear: false}, editable: function(state) { if(obj.isNew(state) && !obj.top.isNew()) { return true; } return false; }, - disabled: function(state) { + readonly: function(state) { if(obj.isNew(state) && !obj.top.isNew()) { return false; } @@ -142,7 +142,7 @@ export class PartitionsSchema extends BaseUISchema { } return false; }, - disabled: function(state) { + readonly: function(state) { if(obj.isNew(state)) { return false; } @@ -159,7 +159,7 @@ export class PartitionsSchema extends BaseUISchema { } return false; }, - disabled: function(state) { + readonly: function(state) { if(obj.top && (obj.top.sessData.partition_type == 'range' || obj.top.sessData.partition_type == 'list') && obj.isNew(state) && obj.getServerVersion() >= 110000) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js index 30a3870f5..2ac2ada58 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js @@ -535,7 +535,7 @@ export default class TableSchema extends BaseUISchema { }, { id: 'replica_identity', label: gettext('Replica Identity'), - group: gettext('advanced'), type: 'text',mode: ['edit', 'properties'], + group: 'advanced', type: 'text',mode: ['edit', 'properties'], }, { id: 'coll_inherits', label: gettext('Inherited from table(s)'), type: 'text', group: 'advanced', mode: ['properties'], diff --git a/web/pgadmin/browser/server_groups/servers/static/js/vacuum.ui.js b/web/pgadmin/browser/server_groups/servers/static/js/vacuum.ui.js index 9e7019f1c..622c0d3fc 100644 --- a/web/pgadmin/browser/server_groups/servers/static/js/vacuum.ui.js +++ b/web/pgadmin/browser/server_groups/servers/static/js/vacuum.ui.js @@ -60,14 +60,6 @@ export default class VacuumSettingsSchema extends BaseUISchema { this.vacuumToastTableObj = new VacuumTableSchema('toast_autovacuum'); } - inSchemaCheck() { - if(this.nodeInfo && 'catalog' in this.nodeInfo) - { - return true; - } - return false; - } - get baseFields() { var obj = this; return [{ @@ -79,13 +71,10 @@ export default class VacuumSettingsSchema extends BaseUISchema { } // If table is partitioned table then disabled it. if(state.top && state.is_partitioned) { - // We also need to unset rest of all - state.autovacuum_custom = false; - return true; } - if(obj.inSchemaCheck) + if(obj.inCatalog) { return false; } @@ -107,13 +96,13 @@ export default class VacuumSettingsSchema extends BaseUISchema { ], deps: ['autovacuum_custom'], disabled: function(state) { - if(obj.inSchemaCheck && state.autovacuum_custom) { + if(obj.inCatalog && state.autovacuum_custom) { return false; } return true; }, depChange: function(state) { - if(obj.inSchemaCheck && state.autovacuum_custom) { + if(obj.inCatalog && state.autovacuum_custom) { return; } return {autovacuum_enabled: 'x'}; @@ -133,7 +122,7 @@ export default class VacuumSettingsSchema extends BaseUISchema { disabled: function(state) { // We need to check additional condition to toggle enable/disable // for table auto-vacuum - if(obj.inSchemaCheck && (obj.isNew() || (state.toast_autovacuum_enabled || state.hastoasttable))) { + if(obj.inCatalog && state.hastoasttable) { return false; } return true; @@ -150,13 +139,13 @@ export default class VacuumSettingsSchema extends BaseUISchema { ], deps:['toast_autovacuum'], disabled: function(state) { - if(obj.inSchemaCheck && state.toast_autovacuum) { + if(obj.inCatalog && state.toast_autovacuum) { return false; } return true; }, depChange: function(state) { - if(obj.inSchemaCheck && state.toast_autovacuum) { + if(obj.inCatalog && state.toast_autovacuum) { return; } if(obj.isNew() || state.hastoasttable) { diff --git a/web/regression/javascript/schema_ui_files/partition.ui.spec.js b/web/regression/javascript/schema_ui_files/partition.ui.spec.js new file mode 100644 index 000000000..9e55da6fb --- /dev/null +++ b/web/regression/javascript/schema_ui_files/partition.ui.spec.js @@ -0,0 +1,153 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import jasmineEnzyme from 'jasmine-enzyme'; +import React from 'react'; +import '../helper/enzyme.helper'; +import { createMount } from '@material-ui/core/test-utils'; +import pgAdmin from 'sources/pgadmin'; +import {messages} from '../fake_messages'; +import SchemaView from '../../../pgadmin/static/js/SchemaView'; +import _ from 'lodash'; +import * as nodeAjax from '../../../pgadmin/browser/static/js/node_ajax'; +import { getNodePartitionTableSchema } from '../../../pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.ui'; + +describe('PartitionTableSchema', ()=>{ + let mount; + let schemaObj; + let getInitData = ()=>Promise.resolve({}); + + /* Use createMount so that material ui components gets the required context */ + /* https://material-ui.com/guides/testing/#api */ + beforeAll(()=>{ + mount = createMount(); + spyOn(nodeAjax, 'getNodeAjaxOptions').and.returnValue(Promise.resolve([])); + spyOn(nodeAjax, 'getNodeListByName').and.returnValue(Promise.resolve([])); + schemaObj = getNodePartitionTableSchema({ + server: { + _id: 1, + }, + schema: { + _label: 'public', + } + }, {}, { + Nodes: {table: {}}, + serverInfo: { + 1: { + user: { + name: 'Postgres', + } + } + } + }); + }); + + afterAll(() => { + mount.cleanUp(); + }); + + beforeEach(()=>{ + jasmineEnzyme(); + /* messages used by validators */ + pgAdmin.Browser = pgAdmin.Browser || {}; + pgAdmin.Browser.messages = pgAdmin.Browser.messages || messages; + pgAdmin.Browser.utils = pgAdmin.Browser.utils || {}; + }); + + it('create', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + it('edit', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + it('properties', ()=>{ + mount({}} + onEdit={()=>{}} + />); + }); + + it('depChange', ()=>{ + let state = {typname: 'newtype'}; + let partKeyField = _.find(schemaObj.fields, (f)=>f.id=='partition_keys'); + expect(partKeyField.depChange(state, null, null, { + oldState: { + typname: 'oldtype', + } + })).toEqual({ + partition_keys: [] + }); + + state = { + partition_type: 'list', + columns: [{name: 'id'}], + partition_keys: [], + }; + expect(partKeyField.canAddRow(state)).toBe(true); + + state = {is_partitioned: true}; + expect(partKeyField.canAdd(state)).toBe(true); + + expect(_.find(schemaObj.fields, (f)=>f.id=='partitions').depChange(state, ['is_partitioned'])) + .toEqual({ + partitions: [] + }); + }); + + it('validate', ()=>{ + let state = {is_partitioned: true}; + let setError = jasmine.createSpy('setError'); + + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('partition_keys', 'Please specify at least one key for partitioned table.'); + + state.partition_keys = [{key: 'id'}]; + expect(schemaObj.validate(state, setError)).toBe(false); + }); +}); +