1) Added support for SYSTEM, CONCURRENTLY and TABLESPACE options in REINDEX. #6381

2) Added new/missing options to the VACUUM command. #6397
3) Added SKIP_LOCKED and BUFFER_USAGE_LIMIT option to Analyze command. #6415
This commit is contained in:
Akshay Joshi 2023-07-27 17:34:25 +05:30 committed by GitHub
parent e177344ae1
commit a460644ae8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1339 additions and 184 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

View File

@ -7,36 +7,109 @@
Use the *Maintenance* dialog to VACUUM, ANALYZE, REINDEX or CLUSTER a database
or selected database objects.
.. image:: images/maintenance.png
:alt: Maintenance dialog
:align: center
While this utility is useful for ad-hoc maintenance purposes, you are encouraged
to perform automatic VACUUM jobs on a regular schedule.
Select a button next to *Maintenance operation* to specify the type of
maintenance:
.. image:: images/maintenance_vacuum.png
:alt: Maintenance dialog
:align: center
* Click *VACUUM* to scan the selected database or table to reclaim storage used
by dead tuples.
* Move the *FULL* switch to the *Yes* position to compact tables by writing
a completely new version of the table file without dead space. The default
is *No*.
a completely new version of the table file without dead space.
* Move the *FREEZE* switch to the *Yes* position to freeze data in a table
when it will have no further updates. The default is *No*.
when it will have no further updates.
* Move the *ANALYZE* switch to the *Yes* position to issue ANALYZE commands
whenever the content of a table has changed sufficiently. The default is
*No*.
whenever the content of a table has changed sufficiently.
* Move the *DISABLE PAGE SKIPPING* switch to the *Yes* position to disables
all page-skipping behavior.
* Move the *SKIP LOCKED* switch to the *Yes* position to specifies that
VACUUM should not wait for any conflicting locks to be released when
beginning work on a relation. This option is available from v12 onwards.
* Move the *TRUNCATE* switch to the *Yes* position to specifies that VACUUM
should attempt to truncate off any empty pages at the end of the table and
allow the disk space for the truncated pages to be returned to the operating
system. This option is available from v12 onwards.
* Move the *PROCESS TOAST* switch to the *Yes* position to specifies that
VACUUM should attempt to process the corresponding TOAST table for each
relation, if one exists. This option is available from v14 onwards.
* Move the *PROCESS MAIN* switch to the *Yes* position to specifies that
VACUUM should attempt to process the main relation. This option is available
from v16 onwards.
* Move the *SKIP DATABASE STATS* switch to the *Yes* position to specifies
that VACUUM should skip updating the database-wide statistics about oldest
unfrozen XIDs. This option is available from v16 onwards.
* Move the *ONLY DATABASE STATS* switch to the *Yes* position to specifies
that VACUUM should do nothing except update the database-wide statistics
about oldest unfrozen XIDs . This option is available from v16 onwards.
* Use the *INDEX CLEANUP* field to force VACUUM to process indexes when there
are more than zero dead tuples.
* Use the *PARALLEL* field to specify index vacuum and index cleanup phases
of VACUUM in parallel using integer background workers. This option is
available from v13 onwards.
* Use the *BUFFER USAGE LIMIT* field to specifies the Buffer Access Strategy
ring buffer size for VACUUM. This size is used to calculate the number of
shared buffers which will be reused as part of this strategy. This option
is available from v16 onwards
.. image:: images/maintenance_analyze.png
:alt: Maintenance dialog
:align: center
* Click *ANALYZE* to update the stored statistics used by the query planner.
This enables the query optimizer to select the fastest query plan for optimal
performance.
* Move the *SKIP LOCKED* switch to the *Yes* position to specifies that
ANALYZE should not wait for any conflicting locks to be released when
beginning work on a relation. This option is available from v12 onwards.
* Use the *BUFFER USAGE LIMIT* field to specifies the Buffer Access Strategy
ring buffer size for ANALYZE. This size is used to calculate the number of
shared buffers which will be reused as part of this strategy. This option
is available from v16 onwards
.. image:: images/maintenance_reindex.png
:alt: Maintenance dialog
:align: center
* Click *REINDEX* to rebuild any index in case it has degenerated due to the
insertion of unusual data patterns. This happens, for example, if you insert
rows with increasing index values, and delete low index values.
* Move the *SYSTEM* switch to the *Yes* position to recreate all indexes
on system catalogs within the current database. This option is enabled
only when database object is selected.
* Move the *CONCURRENTLY* switch to the *Yes* position to rebuild the index
without taking any locks that prevent concurrent inserts, updates, or
deletes on the table. This option is available from v12 onwards.
* Use the *TABLESPACE* field to specifies that indexes will be rebuilt on
a new tablespace. This option is available from v14 onwards.
.. image:: images/maintenance_cluster.png
:alt: Maintenance dialog
:align: center
* Click *CLUSTER* to instruct PostgreSQL to cluster the selected table.
To exclude status messages from the process output, move the *Verbose Messages*
@ -47,4 +120,4 @@ to exit the dialog without performing maintenance operations, click *Cancel*.
pgAdmin will run the maintenance process in background. You can view all the background
process with there running status and logs on the :ref:`Processes <processes>`
tab
tab.

View File

@ -11,6 +11,7 @@ notes for it.
.. toctree::
:maxdepth: 1
release_notes_7_6
release_notes_7_5
release_notes_7_4
release_notes_7_3

View File

@ -0,0 +1,32 @@
***********
Version 7.6
***********
Release date: 2023-08-24
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v7.5.
Supported Database Servers
**************************
**PostgreSQL**: 11, 12, 13, 14 and 15
**EDB Advanced Server**: 11, 12, 13, 14 and 15
Bundled PostgreSQL Utilities
****************************
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 15.3
New features
************
| `Issue #6381 <https://github.com/pgadmin-org/pgadmin4/issues/6381>`_ - Added support for SYSTEM, CONCURRENTLY and TABLESPACE options in REINDEX.
| `Issue #6397 <https://github.com/pgadmin-org/pgadmin4/issues/6397>`_ - Added new/missing options to the VACUUM command.
| `Issue #6415 <https://github.com/pgadmin-org/pgadmin4/issues/6415>`_ - Added SKIP_LOCKED and BUFFER_USAGE_LIMIT option to Analyze command.
Housekeeping
************
Bug fixes
*********

View File

@ -470,19 +470,19 @@ STORAGE_DIR = os.path.join(DATA_DIR, 'storage')
##########################################################################
DEFAULT_BINARY_PATHS = {
"pg": "",
"pg-10": "",
"pg-11": "",
"pg-12": "",
"pg-13": "",
"pg-14": "",
"pg-15": "",
"pg-16": "",
"ppas": "",
"ppas-10": "",
"ppas-11": "",
"ppas-12": "",
"ppas-13": "",
"ppas-14": "",
"ppas-15": ""
"ppas-15": "",
"ppas-16": ""
}
##########################################################################

View File

@ -75,59 +75,50 @@ class Message(IProcessDesc):
return "{0} ({1}:{2})".format(s.name, host, port)
def get_op(self):
op = self._check_for_vacuum()
if self.data['op'] == "ANALYZE":
op = _('ANALYZE')
if self.data['verbose']:
op += '(' + _('VERBOSE') + ')'
if self.data['op'] == "REINDEX":
if 'schema' in self.data and self.data['schema']:
if 'primary_key' in self.data or \
'unique_constraint' in self.data or \
'index' in self.data:
return _('REINDEX INDEX')
else:
return _('REINDEX TABLE')
op = _('REINDEX')
if self.data['op'] == "CLUSTER":
op = _('CLUSTER')
return op
def get_object_msg(self):
msg = _("on database '{0}'").format(self.data['database'])
if 'primary_key' in self.data or 'unique_constraint' in self.data:
msg = _("on constraint '{0}/{1}/{2}/{3}'").format(
self.data['database'], self.data['schema'], self.data['table'],
self.data['primary_key'] if 'primary_key' in self.data else
self.data['unique_constraint'])
elif 'index' in self.data:
msg = _("on index '{0}/{1}/{2}/{3}'").format(
self.data['database'], self.data['schema'],
self.data['table'], self.data['index'])
elif 'table' in self.data:
msg = _("on table '{0}/{1}/{2}'").format(
self.data['database'], self.data['schema'], self.data['table'])
elif 'schema' in self.data:
msg = _("on schema '{0}/{1}'").format(self.data['database'],
self.data['schema'])
return msg
@property
def message(self):
res = _("{0} on database '{1}' of server {2}")
return res.format(
self.get_op(), self.data['database'], self.get_server_name())
op = _('VACUUM')
if self.data['op'] == "ANALYZE":
op = _('ANALYZE')
elif self.data['op'] == "REINDEX" and 'schema' not in self.data:
op = _('REINDEX')
elif self.data['op'] == "REINDEX" and 'schema' in self.data:
if 'primary_key' in self.data or 'unique_constraint' in self.data\
or 'index' in self.data:
op = _('REINDEX INDEX')
elif 'table' in self.data:
op = _('REINDEX TABLE')
else:
op = _('REINDEX SCHEMA')
elif self.data['op'] == "CLUSTER":
op = _('CLUSTER')
res = _("{0} {1} of server {2}")
return res.format(op, self.get_object_msg(), self.get_server_name())
@property
def type_desc(self):
return _("Maintenance")
def _check_for_vacuum(self):
"""
Check for VACUUM in data and return format response.
:return: response.
"""
res = None
if self.data['op'] == "VACUUM":
res = _('VACUUM ({0})')
opts = []
if 'vacuum_full' in self.data and self.data['vacuum_full']:
opts.append(_('FULL'))
if 'vacuum_freeze' in self.data and self.data['vacuum_freeze']:
opts.append(_('FREEZE'))
if self.data['verbose']:
opts.append(_('VERBOSE'))
res = res.format(', '.join(str(x) for x in opts))
return res
def details(self, cmd, args):
return {
"message": self.message,

View File

@ -11,6 +11,7 @@ import Notify from '../../../../static/js/helpers/Notifier';
import {getUtilityView} from '../../../../browser/static/js/utility_view';
import getApiInstance from 'sources/api_instance';
import MaintenanceSchema, {getVacuumSchema} from './maintenance.ui';
import { getNodeListByName } from '../../../../browser/static/js/node_ajax';
define([
'sources/gettext', 'sources/url_for', 'sources/pgadmin', 'pgadmin.browser',
@ -76,9 +77,15 @@ define([
},
getUISchema: function(treeItem) {
let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(treeItem);
const selectedNode = pgBrowser.tree.selected();
let itemNodeData = pgBrowser.tree.findNodeByDomElement(selectedNode).getData();
return new MaintenanceSchema(
()=>getVacuumSchema(),
()=>getVacuumSchema({
tablespace: ()=>getNodeListByName('tablespace', treeNodeInfo, itemNodeData, {}, (m)=>{
return (m.label != 'pg_global');
})
}),
{
nodeInfo: treeNodeInfo
}
@ -106,6 +113,15 @@ define([
if(treeInfo?.mview) {
extraData['table'] = treeInfo?.mview._label;
}
if(treeInfo?.primary_key) {
extraData['primary_key'] = treeInfo?.primary_key._label;
}
if(treeInfo?.unique_constraint) {
extraData['unique_constraint'] = treeInfo?.unique_constraint._label;
}
if(treeInfo?.index) {
extraData['index'] = treeInfo?.index._label;
}
extraData['save_btn_icon'] = 'done';
return extraData;
},
@ -174,7 +190,7 @@ define([
} else{
pgBrowser.Node.registerUtilityPanel();
let panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md),
let panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md, pgBrowser.stdH.lg),
j = panel.$container.find('.obj_properties').first();
let schema = that.getUISchema(item);
@ -194,7 +210,7 @@ define([
});
getUtilityView(
schema, treeInfo, 'select', 'dialog', j[0], panel, that.saveCallBack, extraData, 'OK', jobUrl, sqlHelpUrl, helpUrl);
schema, treeInfo, 'create', 'dialog', j[0], panel, that.saveCallBack, extraData, 'OK', jobUrl, sqlHelpUrl, helpUrl);
}
})
.catch(function() {

View File

@ -23,44 +23,296 @@ export class VacuumSchema extends BaseUISchema {
return 'op';
}
isDisabled(state) {
if(state?.op) {
return (state.op != 'VACUUM');
} else {
return false;
}
isApplicableForVacuum(state) {
return state?.op ? state.op == 'VACUUM' : false;
}
isApplicableForReindex(state) {
return state?.op ? state.op == 'REINDEX' : false;
}
get baseFields() {
let obj = this;
return [{
id: 'vacuum_full',
group: gettext('Vacuum'),
disabled: function(state) {
return obj.isDisabled(state);
},
deps: ['op'],
type: 'switch',
label: gettext('FULL'),
deps: ['op'],
inlineNext: true,
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
disabled: function(state) {
if (!obj.isApplicableForVacuum(state)) {
state.vacuum_full = false;
return true;
}
return false;
}
}, {
id: 'vacuum_freeze',
deps: ['op'],
disabled: function(state) {
return obj.isDisabled(state);
},
type: 'switch',
label: gettext('FREEZE'),
group: gettext('Vacuum'),
inlineNext: true,
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
disabled: function(state) {
if (!obj.isApplicableForVacuum(state)) {
state.vacuum_freeze = false;
return true;
}
return false;
}
}, {
id: 'vacuum_analyze',
deps: ['op'],
type: 'switch',
disabled: function(state) {
return obj.isDisabled(state);
},
label: gettext('ANALYZE'),
group: gettext('Vacuum'),
inlineNext: true,
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
disabled: function(state) {
if (!obj.isApplicableForVacuum(state)) {
state.vacuum_analyze = false;
return true;
}
return false;
}
}, {
id: 'vacuum_disable_page_skipping',
deps: ['op', 'vacuum_full'],
type: 'switch',
label: gettext('DISABLE PAGE SKIPPING'),
inlineNext: true,
disabled: function(state) {
if (!obj.isApplicableForVacuum(state) || state.vacuum_full) {
state.vacuum_disable_page_skipping = false;
return true;
}
return false;
},
visible: function(state) {
return obj.isApplicableForVacuum(state);
}
}, {
id: 'skip_locked',
deps: ['op'],
type: 'switch',
label: gettext('SKIP LOCKED'),
inlineNext: true,
visible: function(state) {
return state?.op ? (state.op == 'VACUUM' || state.op == 'ANALYZE') : false;
},
disabled: function(state) {
if (state?.op && state.op != 'VACUUM' && state.op != 'ANALYZE') {
state.skip_locked = false;
return true;
}
return false;
},
min_version: 120000,
}, {
id: 'vacuum_truncate',
deps: ['op', 'vacuum_full'],
type: 'switch',
label: gettext('TRUNCATE'),
inlineNext: true,
disabled: function(state) {
if (!obj.isApplicableForVacuum(state) || state.vacuum_full) {
state.vacuum_truncate = false;
return true;
}
return false;
},
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
min_version: 120000,
}, {
id: 'vacuum_process_toast',
deps: ['op'],
type: 'switch',
label: gettext('PROCESS TOAST'),
inlineNext: true,
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
disabled: function(state) {
if (!obj.isApplicableForVacuum(state)) {
state.vacuum_process_toast = false;
return true;
}
return false;
},
min_version: 140000,
}, {
id: 'vacuum_process_main',
deps: ['op'],
type: 'switch',
label: gettext('PROCESS MAIN'),
inlineNext: true,
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
disabled: function(state) {
if (!obj.isApplicableForVacuum(state)) {
state.vacuum_process_main = false;
return true;
}
return false;
},
min_version: 160000,
}, {
id: 'vacuum_skip_database_stats',
deps: ['op'],
type: 'switch',
label: gettext('SKIP DATABASE STATS'),
inlineNext: true,
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
disabled: function(state) {
if (!obj.isApplicableForVacuum(state)) {
state.vacuum_skip_database_stats = false;
return true;
}
return false;
},
min_version: 160000,
}, {
id: 'vacuum_only_database_stats',
deps: ['op'],
type: 'switch',
label: gettext('ONLY DATABASE STATS'),
inlineNext: true,
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
disabled: function(state) {
if (!obj.isApplicableForVacuum(state)) {
state.vacuum_only_database_stats = false;
return true;
}
return false;
},
min_version: 160000,
}, {
id: 'vacuum_index_cleanup',
deps: ['op', 'vacuum_full'],
type: 'select',
label: gettext('INDEX CLEANUP'),
controlProps: { allowClear: false, width: '100%' },
options: [
{
label: gettext('AUTO'),
value: 'AUTO',
},
{
label: gettext('ON'),
value: 'ON',
},
{
label: gettext('OFF'),
value: 'OFF',
}
],
disabled: function(state) {
if (!obj.isApplicableForVacuum(state) || state.vacuum_full) {
state.vacuum_index_cleanup = undefined;
return true;
}
return false;
},
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
min_version: 120000,
}, {
id: 'vacuum_parallel',
deps: ['op', 'vacuum_full'],
type: 'int',
label: gettext('PARALLEL'),
min:0, max:1024,
visible: function(state) {
return obj.isApplicableForVacuum(state);
},
disabled: function(state) {
if (!obj.isApplicableForVacuum(state) || state.vacuum_full) {
state.vacuum_parallel = undefined;
return true;
}
return false;
},
min_version: 130000,
}, {
id: 'buffer_usage_limit',
deps: ['op', 'vacuum_full', 'vacuum_analyze'],
type: 'text',
label: gettext('BUFFER USAGE LIMIT'),
visible: function(state) {
return state?.op ? (state.op == 'VACUUM' || state.op == 'ANALYZE') : false;
},
disabled: function(state) {
if (state?.op && ((state.op != 'VACUUM' && state.op != 'ANALYZE') || (state.op == 'VACUUM' && state.vacuum_full && !state.vacuum_analyze))) {
state.buffer_usage_limit = '';
return true;
}
return false;
},
helpMessage: gettext('Sizes should be specified as a string containing the numerical size followed by any one of the following memory units: kB (kilobytes), MB (megabytes), GB (gigabytes), or TB (terabytes)'),
min_version: 160000,
}, {
id: 'reindex_system',
deps: ['op'],
type: 'switch',
label: gettext('SYSTEM'),
visible: function(state) {
return obj.isApplicableForReindex(state);
},
disabled: function(state) {
if (!obj.isApplicableForReindex(state) || obj?._top?.nodeInfo?.schema) {
state.reindex_system = false;
return true;
}
return false;
},
helpMessage: gettext('This option is enabled only when the database is selected in the object explorer.'),
}, {
id: 'reindex_concurrently',
deps: ['op', 'reindex_system'],
type: 'switch',
label: gettext('CONCURRENTLY'),
visible: function(state) {
return obj.isApplicableForReindex(state);
},
disabled: function(state) {
if (!obj.isApplicableForReindex(state) || state.reindex_system) {
state.reindex_concurrently = false;
return true;
}
return false;
},
min_version: 120000,
}, {
id: 'reindex_tablespace',
label: gettext('TABLESPACE'),
deps: ['op', 'reindex_system'],
type: 'select',
options: this.fieldOptions.tablespace,
controlProps: { allowClear: true },
visible: function(state) {
return obj.isApplicableForReindex(state);
},
disabled: function(state) {
if (!obj.isApplicableForReindex(state) || state.reindex_system) {
state.reindex_tablespace = undefined;
return true;
}
return false;
},
min_version: 140000,
}];
}
}
@ -75,11 +327,11 @@ export default class MaintenanceSchema extends BaseUISchema {
constructor(vacuumSchema, fieldOptions = {}) {
super({
op: 'VACUUM',
verbose: true,
vacuum_full: false,
vacuum_freeze: false,
vacuum_analyze: false,
op: (fieldOptions.nodeInfo?.schema && !fieldOptions.nodeInfo?.table &&
!fieldOptions.nodeInfo?.primary_key && !fieldOptions.nodeInfo?.unique_constraint &&
!fieldOptions.nodeInfo?.index && !fieldOptions.nodeInfo?.partition &&
!fieldOptions.nodeInfo?.mview) ? 'REINDEX' : 'VACUUM',
verbose: true
});
this.fieldOptions = {
@ -95,6 +347,12 @@ export default class MaintenanceSchema extends BaseUISchema {
return 'id';
}
isSchemaNode() {
return this.nodeInfo?.schema && !this.nodeInfo?.table &&
!this.nodeInfo?.primary_key && !this.nodeInfo?.unique_constraint &&
!this.nodeInfo?.index && !this.nodeInfo?.partition;
}
get baseFields() {
let obj = this;
return [
@ -107,20 +365,22 @@ export default class MaintenanceSchema extends BaseUISchema {
{
'label': gettext('VACUUM'),
value: 'VACUUM',
disabled: (obj.isSchemaNode() && !obj.nodeInfo?.mview) ? true : false
},
{
'label': gettext('ANALYZE'),
value: 'ANALYZE',
disabled: (obj.isSchemaNode() && !obj.nodeInfo?.mview) ? true : false
},
{
'label': gettext('REINDEX'),
value: 'REINDEX',
disabled: obj.nodeInfo?.mview?true:false
disabled: obj.nodeInfo?.mview ? true : false
},
{
'label': gettext('CLUSTER'),
value: 'CLUSTER',
disabled: obj.nodeInfo?.mview?true:false
disabled: obj.nodeInfo?.mview ? true : obj.isSchemaNode() ? true : false
},
],
},
@ -129,6 +389,14 @@ export default class MaintenanceSchema extends BaseUISchema {
label: gettext('Type of objects'),
schema: obj.getVacuumSchema(),
group: gettext('Options'),
visible: function(state) {
if (state?.op == 'ANALYZE') {
return obj?.nodeInfo?.server?.version >= 120000;
} else if (state?.op == 'CLUSTER') {
return false;
}
return true;
}
},
{
id: 'verbose',
@ -136,21 +404,6 @@ export default class MaintenanceSchema extends BaseUISchema {
deps: ['op'],
type: 'switch',
label: gettext('Verbose Messages'),
disabled: function(state) {
let nodeInfo = this.nodeInfo;
if(state?.verbose) {
if ('primary_key' in nodeInfo || 'unique_constraint' in nodeInfo ||
'index' in nodeInfo) {
if (state.op == 'REINDEX') {
state.verbose = false;
return true;
}
}
return state.op == 'REINDEX';
} else {
return false;
}
},
},
];
}

View File

@ -8,6 +8,6 @@
//////////////////////////////////////////////////////////////
export const maintenanceSupportedNodes = [
'database', 'table', 'primary_key',
'unique_constraint', 'index', 'partition',
'database', 'schema', 'table', 'primary_key',
'unique_constraint', 'index', 'partition', 'mview'
];

View File

@ -1,17 +1,34 @@
{% set maintenance_options = [] %}
{% if data.verbose %}{{ maintenance_options.append('VERBOSE') or "" }}{% endif %}
{% if data.vacuum_full %}{{ maintenance_options.append('FULL') or "" }}{% endif %}
{% if data.vacuum_freeze %}{{ maintenance_options.append('FREEZE') or "" }}{% endif %}
{% if data.vacuum_analyze %}{{ maintenance_options.append('ANALYZE') or "" }}{% endif %}
{% if data.vacuum_disable_page_skipping %}{{ maintenance_options.append('DISABLE_PAGE_SKIPPING') or "" }}{% endif %}
{% if data.skip_locked %}{{ maintenance_options.append('SKIP_LOCKED') or "" }}{% endif %}
{% if data.vacuum_truncate %}{{ maintenance_options.append('TRUNCATE') or "" }}{% endif %}
{% if data.vacuum_process_toast %}{{ maintenance_options.append('PROCESS_TOAST') or "" }}{% endif %}
{% if data.vacuum_process_main %}{{ maintenance_options.append('PROCESS_MAIN') or "" }}{% endif %}
{% if data.vacuum_skip_database_stats %}{{ maintenance_options.append('SKIP_DATABASE_STATS') or "" }}{% endif %}
{% if data.vacuum_only_database_stats %}{{ maintenance_options.append('ONLY_DATABASE_STATS') or "" }}{% endif %}
{% if data.vacuum_index_cleanup %}{{ maintenance_options.append('INDEX_CLEANUP ' + data.vacuum_index_cleanup) or "" }}{% endif %}
{% if data.vacuum_parallel %}{{ maintenance_options.append('PARALLEL ' + data.vacuum_parallel) or "" }}{% endif %}
{% if data.buffer_usage_limit %}{{ maintenance_options.append('BUFFER_USAGE_LIMIT "' + data.buffer_usage_limit + '"') or "" }}{% endif %}
{% if data.reindex_tablespace %}{{ maintenance_options.append('TABLESPACE "' + data.reindex_tablespace + '"') or "" }}{% endif %}
{% if data.reindex_concurrently %}{{ maintenance_options.append('CONCURRENTLY') or "" }}{% endif %}
{% if data.op == "VACUUM" %}
VACUUM{% if data.vacuum_full %} FULL{% endif %}{% if data.vacuum_freeze %} FREEZE{% endif %}{% if data.verbose %} VERBOSE{% endif %}{% if data.vacuum_analyze %} ANALYZE{% endif %}{% if data.schema %} {{ conn|qtIdent(data.schema) }}.{{ conn|qtIdent(data.table) }}{% endif %};
VACUUM{% for option in maintenance_options %}{% if loop.first %} ({% endif %}{{ option }}{% if not loop.last %}, {% endif %}{% if loop.last %}){% endif %}{% endfor %}{% if data.schema %} {{ conn|qtIdent(data.schema) }}.{{ conn|qtIdent(data.table) }}{% endif %};
{% endif %}
{% if data.op == "ANALYZE" %}
ANALYZE{% if data.verbose %} VERBOSE{% endif %}{% if data.schema %} {{ conn|qtIdent(data.schema, data.table) }}{% endif %};
ANALYZE{% for option in maintenance_options %}{% if loop.first %} ({% endif %}{{ option }}{% if not loop.last %}, {% endif %}{% if loop.last %}){% endif %}{% endfor %}{% if data.schema %} {{ conn|qtIdent(data.schema, data.table) }}{% endif %};
{% endif %}
{% if data.op == "REINDEX" %}
{% if index_name %}
REINDEX INDEX {{ conn|qtIdent(data.schema, index_name) }};
REINDEX{% for option in maintenance_options %}{% if loop.first %} ({% endif %}{{ option }}{% if not loop.last %}, {% endif %}{% if loop.last %}){% endif %}{% endfor %} INDEX {{ conn|qtIdent(data.schema, index_name) }};
{% else %}
REINDEX{% if not data.schema %} DATABASE {{ conn|qtIdent(data.database) }}{% else %} TABLE {{ conn|qtIdent(data.schema, data.table) }}{% endif %};
REINDEX{% for option in maintenance_options %}{% if loop.first %} ({% endif %}{{ option }}{% if not loop.last %}, {% endif %}{% if loop.last %}){% endif %}{% endfor %}{% if not data.schema and not data.reindex_system %} DATABASE {{ conn|qtIdent(data.database) }}{% elif not data.schema and data.reindex_system%} SYSTEM {{ conn|qtIdent(data.database) }}{% elif data.schema and not data.table and not data.primary_key and not data.unique_constraint and not data.index and not data.mview %} SCHEMA {{ conn|qtIdent(data.schema) }}{% else %} TABLE {{ conn|qtIdent(data.schema, data.table) }}{% endif %};
{% endif %}
{% endif %}
{% if data.op == "CLUSTER" %}
CLUSTER{% if data.verbose %} VERBOSE {% endif %}{% if data.schema %} {{ conn|qtIdent(data.schema, data.table) }}{% endif %}{% if index_name %}
CLUSTER{% if data.verbose %} VERBOSE{% endif %}{% if data.schema %} {{ conn|qtIdent(data.schema, data.table) }}{% endif %}{% if index_name %}
USING {{ conn|qtIdent(index_name) }}{% endif %};
{% endif %}

View File

@ -53,8 +53,8 @@ class BatchProcessTest(BaseTestGenerator):
},
cmd="VACUUM VERBOSE;\n"
),
expected_msg="VACUUM (VERBOSE) on database "
"'postgres' of server " + SERVER_NAME,
expected_msg="VACUUM on database 'postgres' of server " +
SERVER_NAME,
expected_details_cmd='VACUUM VERBOSE;'
))
]

View File

@ -34,7 +34,7 @@ class MaintenanceJobTest(BaseTestGenerator):
cmd="VACUUM VERBOSE;\n"
),
url='/maintenance/job/{0}/{1}',
expected_cmd='VACUUM VERBOSE',
expected_cmd='VACUUM (VERBOSE)',
expected_exit_code=[0, None]
))
]

View File

@ -19,6 +19,8 @@ from unittest.mock import patch, MagicMock
from config import PG_DEFAULT_DRIVER
MAINTENANCE_URL = '/maintenance/job/{0}/{1}'
class MaintenanceCreateJobTest(BaseTestGenerator):
"""Test the BackupCreateJob class"""
@ -40,10 +42,11 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
vacuum_full=False,
verbose=True
),
url='/maintenance/job/{0}/{1}',
expected_cmd_opts=['VACUUM VERBOSE;\n'],
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE);\n'],
)),
('When maintaining object with VACUUM FULL',
('When maintaining object with VACUUM FULL, FREEZE, ANALYZE, '
'DISABLE_PAGE_SKIPPING',
dict(
class_params=dict(
sid=1,
@ -55,13 +58,130 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
params=dict(
database='postgres',
op='VACUUM',
vacuum_analyze=False,
vacuum_freeze=False,
vacuum_analyze=True,
vacuum_freeze=True,
vacuum_full=True,
vacuum_disable_page_skipping=True,
verbose=True
),
url='/maintenance/job/{0}/{1}',
expected_cmd_opts=['VACUUM FULL VERBOSE;\n'],
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, FULL, FREEZE, ANALYZE, '
'DISABLE_PAGE_SKIPPING);\n'],
)),
('When maintaining object with VACUUM SKIP LOCKED, TRUNCATE, '
'INDEX CLEANUP',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
skip_locked=True,
vacuum_truncate=True,
vacuum_index_cleanup='OFF',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, SKIP_LOCKED, TRUNCATE, '
'INDEX_CLEANUP OFF);\n'],
server_min_version=120000,
message='VACUUM SKIP_LOCKED, TRUNCATE and INDEX_CLEANUP is not '
'supported by EPAS/PG server less than 12.0'
)),
('When maintaining object with VACUUM PARALLEL',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
vacuum_parallel='15',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, PARALLEL 15);\n'],
server_min_version=130000,
message='VACUUM PARALLEL is not supported by EPAS/PG server '
'less than 13.0'
)),
('When maintaining object with VACUUM PROCESS TOAST',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
vacuum_process_toast=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, PROCESS_TOAST);\n'],
server_min_version=140000,
message='VACUUM PROCESS TOAST is not supported by EPAS/PG server '
'less than 14.0'
)),
('When maintaining object with VACUUM SKIP DATABASE STATS, '
'PROCESS MAIN, BUFFER USAGE LIMIT',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
vacuum_process_main=True,
vacuum_skip_database_stats=True,
buffer_usage_limit='1MB',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, PROCESS_MAIN, '
'SKIP_DATABASE_STATS, BUFFER_USAGE_LIMIT "1MB"'
');\n'],
server_min_version=160000,
message='VACUUM SKIP_DATABASE_STATS, PROCESS_MAIN and '
'BUFFER_USAGE_LIMIT is not supported by EPAS/PG server '
'less than 16.0'
)),
('When maintaining object with VACUUM ONLY DATABASE STATS',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
vacuum_only_database_stats=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, ONLY_DATABASE_STATS);\n'],
server_min_version=160000,
message='VACUUM ONLY DATABASE STATS is not supported by EPAS/PG '
'server less than 16.0'
)),
('When maintaining object with ANALYZE',
dict(
@ -75,13 +195,314 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
params=dict(
database='postgres',
op='ANALYZE',
vacuum_analyze=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['ANALYZE (VERBOSE);\n'],
)),
('When maintaining object with ANALYZE SKIP LOCKED',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='ANALYZE',
skip_locked=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['ANALYZE (VERBOSE, SKIP_LOCKED);\n'],
server_min_version=120000,
message='ANALYZE SKIP_LOCKED is not supported by EPAS/PG server '
'less than 12.0'
)),
('When maintaining object with ANALYZE BUFFER USAGE LIMIT',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='ANALYZE',
buffer_usage_limit='1MB',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['ANALYZE (VERBOSE, BUFFER_USAGE_LIMIT "1MB"'
');\n'],
server_min_version=160000,
message='ANALYZE BUFFER_USAGE_LIMIT is not supported by '
'EPAS/PG server less than 16.0'
)),
('When maintaining object with default options on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
schema='my_schema',
table='my_table',
vacuum_analyze=False,
vacuum_freeze=False,
vacuum_full=False,
verbose=True
),
url='/maintenance/job/{0}/{1}',
expected_cmd_opts=['ANALYZE VERBOSE;\n'],
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE) my_schema.my_table;\n'],
)),
('When maintaining object with VACUUM FULL, FREEZE, ANALYZE, '
'DISABLE_PAGE_SKIPPING on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
schema='my_schema',
table='my_table',
vacuum_analyze=True,
vacuum_freeze=True,
vacuum_full=True,
vacuum_disable_page_skipping=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, FULL, FREEZE, ANALYZE, '
'DISABLE_PAGE_SKIPPING) my_schema.my_table'
';\n'],
)),
('When maintaining object with VACUUM SKIP LOCKED, TRUNCATE, '
'INDEX CLEANUP on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
schema='my_schema',
table='my_table',
skip_locked=True,
vacuum_truncate=True,
vacuum_index_cleanup='OFF',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, SKIP_LOCKED, TRUNCATE, '
'INDEX_CLEANUP OFF) my_schema.my_table;\n'],
server_min_version=120000,
message='VACUUM SKIP_LOCKED, TRUNCATE and INDEX_CLEANUP is not '
'supported by EPAS/PG server less than 12.0'
)),
('When maintaining object with VACUUM PARALLEL on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
schema='my_schema',
table='my_table',
vacuum_parallel='15',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, PARALLEL 15) '
'my_schema.my_table;\n'],
server_min_version=130000,
message='VACUUM PARALLEL is not supported by EPAS/PG server '
'less than 13.0'
)),
('When maintaining object with VACUUM PROCESS TOAST on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
schema='my_schema',
table='my_table',
vacuum_process_toast=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, PROCESS_TOAST) '
'my_schema.my_table;\n'],
server_min_version=140000,
message='VACUUM PROCESS TOAST is not supported by EPAS/PG server '
'less than 14.0'
)),
('When maintaining object with VACUUM SKIP DATABASE STATS, '
'PROCESS MAIN, BUFFER USAGE LIMIT on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
schema='my_schema',
table='my_table',
vacuum_process_main=True,
vacuum_skip_database_stats=True,
buffer_usage_limit='1MB',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, PROCESS_MAIN, '
'SKIP_DATABASE_STATS, BUFFER_USAGE_LIMIT "1MB"'
') my_schema.my_table;\n'],
server_min_version=160000,
message='VACUUM SKIP_DATABASE_STATS, PROCESS_MAIN and '
'BUFFER_USAGE_LIMIT is not supported by EPAS/PG server '
'less than 16.0'
)),
('When maintaining object with VACUUM ONLY DATABASE STATS on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='VACUUM',
schema='my_schema',
table='my_table',
vacuum_only_database_stats=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['VACUUM (VERBOSE, ONLY_DATABASE_STATS) '
'my_schema.my_table;\n'],
server_min_version=160000,
message='VACUUM ONLY DATABASE STATS is not supported by EPAS/PG '
'server less than 16.0'
)),
('When maintaining object with ANALYZE on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='ANALYZE',
schema='my_schema',
table='my_table',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['ANALYZE (VERBOSE) my_schema.my_table;\n'],
)),
('When maintaining object with ANALYZE SKIP LOCKED on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='ANALYZE',
schema='my_schema',
table='my_table',
skip_locked=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['ANALYZE (VERBOSE, SKIP_LOCKED) '
'my_schema.my_table;\n'],
server_min_version=120000,
message='ANALYZE SKIP_LOCKED is not supported by EPAS/PG server '
'less than 12.0'
)),
('When maintaining object with ANALYZE BUFFER USAGE LIMIT on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='ANALYZE',
schema='my_schema',
table='my_table',
buffer_usage_limit='1MB',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['ANALYZE (VERBOSE, BUFFER_USAGE_LIMIT "1MB"'
') my_schema.my_table;\n'],
server_min_version=160000,
message='ANALYZE BUFFER_USAGE_LIMIT is not supported by '
'EPAS/PG server less than 16.0'
)),
('When maintenance the object with the REINDEX SYSTEM',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='REINDEX',
reindex_system=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX (VERBOSE) SYSTEM postgres;\n'],
)),
('When maintenance the object with the REINDEX',
dict(
@ -95,14 +516,207 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
params=dict(
database='postgres',
op='REINDEX',
vacuum_analyze=False,
vacuum_freeze=False,
vacuum_full=False,
verbose=False
),
url='/maintenance/job/{0}/{1}',
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX DATABASE postgres;\n'],
)),
('When maintenance the object with the REINDEX CONCURRENTLY',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='REINDEX',
reindex_concurrently=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX (VERBOSE, CONCURRENTLY) DATABASE '
'postgres;\n'],
server_min_version=120000,
message='REINDEX CONCURRENTLY is not supported by EPAS/PG server '
'less than 12.0'
)),
('When maintenance the object with the REINDEX TABLESPACE',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
op='REINDEX',
reindex_tablespace='pg_default',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX (VERBOSE, TABLESPACE "pg_default") '
'DATABASE postgres;\n'],
server_min_version=140000,
message='REINDEX TABLESPACE is not supported by EPAS/PG server '
'less than 14.0'
)),
('When maintenance the object with the REINDEX SCHEMA',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
op='REINDEX',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX (VERBOSE) SCHEMA my_schema;\n'],
)),
('When maintenance the object with the REINDEX TABLE',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
table='my_table',
op='REINDEX',
verbose=False
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX TABLE my_schema.my_table;\n'],
)),
('When maintenance the object with the REINDEX CONCURRENTLY TABLE',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
table='my_table',
op='REINDEX',
reindex_concurrently=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX (VERBOSE, CONCURRENTLY) TABLE '
'my_schema.my_table;\n'],
server_min_version=120000,
message='REINDEX CONCURRENTLY TABLE is not supported by '
'EPAS/PG server less than 12.0'
)),
('When maintenance the object with the REINDEX TABLESPACE TABLE',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
table='my_table',
op='REINDEX',
reindex_tablespace='pg_default',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX (VERBOSE, TABLESPACE "pg_default") '
'TABLE my_schema.my_table;\n'],
server_min_version=140000,
message='REINDEX TABLESPACE TABLE is not supported by '
'EPAS/PG server less than 14.0'
)),
('When maintenance the object with the REINDEX INDEX',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
index='my_index',
op='REINDEX',
verbose=False
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX INDEX my_schema.my_index;\n'],
)),
('When maintenance the object with the REINDEX CONCURRENTLY INDEX',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
index='my_index',
op='REINDEX',
reindex_concurrently=True,
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX (VERBOSE, CONCURRENTLY) INDEX '
'my_schema.my_index;\n'],
server_min_version=120000,
message='REINDEX CONCURRENTLY is not supported by EPAS/PG server '
'less than 12.0'
)),
('When maintenance the object with the REINDEX TABLESPACE INDEX',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
index='my_index',
op='REINDEX',
reindex_tablespace='pg_default',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['REINDEX (VERBOSE, TABLESPACE "pg_default") '
'INDEX my_schema.my_index;\n'],
server_min_version=140000,
message='REINDEX TABLESPACE is not supported by EPAS/PG server '
'less than 14.0'
)),
('When maintenance the object with the CLUSTER',
dict(
class_params=dict(
@ -115,13 +729,50 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
params=dict(
database='postgres',
op='CLUSTER',
vacuum_analyze=False,
vacuum_freeze=False,
vacuum_full=False,
verbose=False
verbose=True
),
url='/maintenance/job/{0}/{1}',
expected_cmd_opts=['CLUSTER;\n'],
url=MAINTENANCE_URL,
expected_cmd_opts=['CLUSTER VERBOSE;\n'],
)),
('When maintenance the object with the CLUSTER on table',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
table='my_table',
op='CLUSTER',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['CLUSTER VERBOSE my_schema.my_table;\n'],
)),
('When maintenance the object with the CLUSTER on table using index',
dict(
class_params=dict(
sid=1,
name='test_maintenance_server',
port=5444,
host='localhost',
username='postgres'
),
params=dict(
database='postgres',
schema='my_schema',
table='my_table',
index='my_index',
op='CLUSTER',
verbose=True
),
url=MAINTENANCE_URL,
expected_cmd_opts=['CLUSTER VERBOSE my_schema.my_table '
'USING my_index;\n'],
))
]
@ -142,9 +793,9 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
if os.name == 'nt':
binary_path = binary_path + '.exe'
retVal = does_utility_exist(binary_path)
if retVal is not None:
self.skipTest(retVal)
ret_val = does_utility_exist(binary_path)
if ret_val is not None:
self.skipTest(ret_val)
@patch('pgadmin.tools.maintenance.Server')
@patch('pgadmin.tools.maintenance.Message')
@ -189,6 +840,11 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
self.data = database_utils.get_db_data(db_owner)
self.db_name = self.data['name']
if hasattr(self, 'server_min_version') and \
server_response["data"]["version"] < \
self.server_min_version:
self.skipTest(self.message)
# Create the backup job
response = self.tester.post(url,
data=json.dumps(self.params),

View File

@ -17,7 +17,7 @@ class MaintenanceMessageTest(BaseTestGenerator):
SERVER_NAME = "server (host:port)"
scenarios = [
('When maintained the server',
('When maintained the server with VACUUM on database',
dict(
class_params=dict(
sid=1,
@ -31,67 +31,190 @@ class MaintenanceMessageTest(BaseTestGenerator):
},
cmd="VACUUM VERBOSE;\n"
),
expected_msg="VACUUM (VERBOSE) on database "
"'postgres' of server " + SERVER_NAME,
expected_details_cmd='VACUUM VERBOSE;'
expected_msg="VACUUM on database 'postgres' of server " +
SERVER_NAME
)),
('When maintained the server with FULL VERBOSE options',
('When maintained the server with VACUUM on table ',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'table': 'test_table',
'op': 'VACUUM',
'verbose': True
},
cmd="VACUUM FULL;\n"
),
expected_msg="VACUUM on table 'postgres/test_schema/test_table' "
"of server " + SERVER_NAME
)),
('When maintained the server with VACUUM on constraint ',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'table': 'test_table',
'primary_key': 'test_pkey',
'op': 'VACUUM',
'vacuum_analyze': False,
'vacuum_freeze': False,
'vacuum_full': True,
'verbose': True
},
cmd="VACUUM FULL VERBOSE;\n"
),
expected_msg="VACUUM (FULL, VERBOSE) on database "
"'postgres' of server " + SERVER_NAME,
expected_details_cmd='VACUUM FULL VERBOSE;'
expected_msg="VACUUM on constraint "
"'postgres/test_schema/test_table/test_pkey' "
"of server " + SERVER_NAME
)),
('When maintained the server with ANALYZE',
('When maintained the server with VACUUM on index ',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'table': 'test_table',
'index': 'test_idx',
'op': 'VACUUM',
'verbose': True
},
cmd="VACUUM FULL VERBOSE;\n"
),
expected_msg="VACUUM on index "
"'postgres/test_schema/test_table/test_idx' "
"of server " + SERVER_NAME
)),
('When maintained the server with ANALYZE on database',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'op': 'ANALYZE',
'vacuum_analyze': False,
'vacuum_freeze': False,
'vacuum_full': False,
'verbose': True
},
cmd="ANALYZE VERBOSE;\n"
),
expected_msg="ANALYZE(VERBOSE) on database "
"'postgres' of server " + SERVER_NAME,
expected_details_cmd='ANALYZE VERBOSE;'
expected_msg="ANALYZE on database 'postgres' of server " +
SERVER_NAME
)),
('When maintained the server with REINDEX',
('When maintained the server with ANALYZE on table ',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'table': 'test_table',
'op': 'ANALYZE',
'verbose': True
},
cmd="ANALYZE VERBOSE;\n"
),
expected_msg="ANALYZE on table 'postgres/test_schema/test_table' "
"of server " + SERVER_NAME
)),
('When maintained the server with ANALYZE on constraint ',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'table': 'test_table',
'primary_key': 'test_pkey',
'op': 'ANALYZE',
'verbose': True
},
cmd="ANALYZE FULL VERBOSE;\n"
),
expected_msg="ANALYZE on constraint "
"'postgres/test_schema/test_table/test_pkey' "
"of server " + SERVER_NAME
)),
('When maintained the server with ANALYZE on index ',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'table': 'test_table',
'index': 'test_idx',
'op': 'ANALYZE',
'verbose': True
},
cmd="ANALYZE;\n"
),
expected_msg="ANALYZE on index "
"'postgres/test_schema/test_table/test_idx' "
"of server " + SERVER_NAME
)),
('When maintained the server with REINDEX on database',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'op': 'REINDEX',
'vacuum_analyze': False,
'vacuum_freeze': False,
'vacuum_full': False,
'verbose': False
},
cmd="REINDEX (VERBOSE);\n"
),
expected_msg="REINDEX on database 'postgres' of server " +
SERVER_NAME
)),
('When maintained the server with REINDEX on schema',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'op': 'REINDEX',
'verbose': False
},
cmd="REINDEX (VERBOSE);\n"
),
expected_msg="REINDEX SCHEMA on schema 'postgres/test_schema' "
"of server " + SERVER_NAME
)),
('When maintained the server with REINDEX on table',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'table': 'test_table',
'op': 'REINDEX',
'verbose': False
},
cmd="REINDEX;\n"
),
expected_msg="REINDEX on database "
"'postgres' of server " + SERVER_NAME,
expected_details_cmd='REINDEX;'
expected_msg="REINDEX TABLE on table "
"'postgres/test_schema/test_table' of server " +
SERVER_NAME
)),
('When maintained the server with REINDEX on index',
dict(
class_params=dict(
sid=1,
data={
'database': 'postgres',
'schema': 'test_schema',
'table': 'test_table',
'primary_key': 'test_pkey',
'op': 'REINDEX',
'verbose': False
},
cmd="REINDEX;\n"
),
expected_msg="REINDEX INDEX on constraint "
"'postgres/test_schema/test_table/test_pkey' "
"of server " + SERVER_NAME
)),
('When maintained the server with CLUSTER',
dict(
@ -100,16 +223,12 @@ class MaintenanceMessageTest(BaseTestGenerator):
data={
'database': 'postgres',
'op': 'CLUSTER',
'vacuum_analyze': False,
'vacuum_freeze': False,
'vacuum_full': False,
'verbose': True
},
cmd="CLUSTER VERBOSE;\n"
),
expected_msg="CLUSTER on database "
"'postgres' of server " + SERVER_NAME,
expected_details_cmd='CLUSTER VERBOSE;'
expected_msg="CLUSTER on database 'postgres' of server " +
SERVER_NAME
)),
]
@ -125,7 +244,3 @@ class MaintenanceMessageTest(BaseTestGenerator):
# Check the expected message returned
self.assertEqual(maintenance_obj.message, self.expected_msg)
# Check the command
obj_details = maintenance_obj.details(self.class_params['cmd'], None)
self.assertIn(self.expected_details_cmd, obj_details['query'])

View File

@ -71,9 +71,6 @@ SUPPORTED_AUTH_SOURCES = [INTERNAL,
BINARY_PATHS = {
"as_bin_paths": [
{"version": "100000", "next_major_version": "110000",
"serverType": gettext("EDB Advanced Server 10"), "binaryPath": None,
"isDefault": False},
{"version": "110000", "next_major_version": "120000",
"serverType": gettext("EDB Advanced Server 11"), "binaryPath": None,
"isDefault": False},
@ -88,12 +85,12 @@ BINARY_PATHS = {
"isDefault": False},
{"version": "150000", "next_major_version": "160000",
"serverType": gettext("EDB Advanced Server 15"), "binaryPath": None,
"isDefault": False},
{"version": "160000", "next_major_version": "170000",
"serverType": gettext("EDB Advanced Server 16"), "binaryPath": None,
"isDefault": False}
],
"pg_bin_paths": [
{"version": "100000", "next_major_version": "110000",
"serverType": gettext("PostgreSQL 10"), "binaryPath": None,
"isDefault": False},
{"version": "110000", "next_major_version": "120000",
"serverType": gettext("PostgreSQL 11"), "binaryPath": None,
"isDefault": False},
@ -108,6 +105,9 @@ BINARY_PATHS = {
"isDefault": False},
{"version": "150000", "next_major_version": "160000",
"serverType": gettext("PostgreSQL 15"), "binaryPath": None,
"isDefault": False},
{"version": "160000", "next_major_version": "170000",
"serverType": gettext("PostgreSQL 16"), "binaryPath": None,
"isDefault": False}
]
}

View File

@ -76,7 +76,8 @@ def get_version_mapping_directories():
:param server_type:
:return:
"""
return ({'name': "15_plus", 'number': 150000},
return ({'name': "16_plus", 'number': 160000},
{'name': "15_plus", 'number': 150000},
{'name': "14_plus", 'number': 140000},
{'name': "13_plus", 'number': 130000},
{'name': "12_plus", 'number': 120000},

View File

@ -281,8 +281,8 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
default_binary_path = self.server['default_binary_paths']
if default_binary_path is not None:
def get_server_version_string():
server_version = {150000: '15', 140000: '14', 130000: '13',
120000: '12', 110000: '11', 100000: '10'}
server_version = {160000: '16', 150000: '15', 140000: '14',
130000: '13', 120000: '12', 110000: '11'}
for k, v in server_version.items():
if k <= self.server_information['server_version']:
return v