1. Added new backup/restore options for PostgreSQL 11. Fixes #3503

2. Ensure backup should work with '--data-only' and '--schema-only' for any format. Fixes #3347
3. Added supported "Dump Options" for "pg_dumpall" which was not there since pgAdmin 3.
4. Fixed issue where "--clean" should not clubbed with "--data-only" while taking backup.
5. Fixed "--oids" should not clubbed with "--inserts" or "--column-inserts" while taking backup.
This commit is contained in:
Akshay Joshi 2018-08-22 12:17:50 +05:30
parent 013ad7446f
commit 0ab1305ddf
20 changed files with 561 additions and 540 deletions

View File

@ -58,13 +58,15 @@ Click the *Dump options* tab to continue. Use the box fields in the *Dump option
* Move switches in the **Do not save** field box to select the objects that will not be included in the backup.
* Move the switch next to *Owner* to the *Yes* position to include commands that set object ownership.
* Move the switch next to *Owner* to the *Yes* position to exclude commands that set object ownership.
* Move the switch next to *Privilege* to the *Yes* position to include commands that create access privileges.
* Move the switch next to *Privilege* to the *Yes* position to exclude commands that create access privileges.
* Move the switch next to *Tablespace* to the *Yes* position to include tablespaces.
* Move the switch next to *Tablespace* to the *Yes* position to exclude tablespaces.
* Move the switch next to *Unlogged table data* to the *Yes* position to include the contents of unlogged tables.
* Move the switch next to *Unlogged table data* to the *Yes* position to exclude the contents of unlogged tables.
* Move the switch next to *Comments* to the *Yes* position to exclude commands that set the comments. **Note:** This option is visible only for database server greater than or equal to 11.
.. image:: images/backup_queries.png
:alt: Queries option on backup dialog
@ -79,6 +81,8 @@ Click the *Dump options* tab to continue. Use the box fields in the *Dump option
* Move the switch next to *Include DROP DATABASE statement* to the *Yes* position to include a command in the backup that will drop any existing database object with the same name before recreating the object during a backup.
* Move the switch next to *Load Via Partition Root* to the *Yes* position, so when dumping a COPY or INSERT statement for a partitioned table, target the root of the partitioning hierarchy which contains it rather than the partition itself. **Note:** This option is visible only for database server greater than or equal to 11.
.. image:: images/backup_disable.png
:alt: Disable option on backup dialog

View File

@ -12,12 +12,66 @@ Use the *Backup Server* dialog to create a plain-text script that will recreate
Use the fields in the *General* tab to specify the following:
* Enter the name of the backup file in the *Filename* field. Optionally, select the *Browser* icon (ellipsis) to the right to navigate into a directory and select a file that will contain the archive.
* Use the *Encoding* drop-down listbox to select the character encoding method that should be used for the archive. **Note:** This option is visible only for database server greater than or equal to 11.
* Use the drop-down listbox next to *Role name* to specify a role with connection privileges on the selected server. The role will be used for authentication during the backup.
Move switches in the *Miscellaneous* box to specify the type of statements that should be included in the backup.
.. image:: images/backup_server_objects.png
:alt: Type of objects option on backup server dialog
* Move the *Verbose messages* switch to the *No* position to exclude status messages from the backup. The default is *Yes*.
* Move the *Force double quote on identifiers* switch to the *Yes* position to name identifiers without changing case. The default is *No*.
* Move switches in the **Type of objects** field box to specify details about the type of objects that will be backed up.
* Move the switch next to *Only data* to the *Yes* position to limit the back up to data.
* Move the switch next to *Only schema* to limit the back up to schema-level database objects.
.. image:: images/backup_server_do_not_save.png
:alt: Do not save option on backup server dialog
* Move switches in the **Do not save** field box to select the objects that will not be included in the backup.
* Move the switch next to *Owner* to the *Yes* position to exclude commands that set object ownership.
* Move the switch next to *Privilege* to the *Yes* position to exclude commands that create access privileges.
* Move the switch next to *Tablespace* to the *Yes* position to exclude tablespaces.
* Move the switch next to *Unlogged table data* to the *Yes* position to exclude the contents of unlogged tables.
* Move the switch next to *Comments* to the *Yes* position to exclude commands that set the comments. **Note:** This option is visible only for database server greater than or equal to 11.
.. image:: images/backup_server_queries.png
:alt: Queries option on backup server dialog
* Move switches in the **Queries** field box to specify the type of statements that should be included in the backup.
* Move the switch next to *Use Column Inserts* to the *Yes* position to dump the data in the form of INSERT statements and include explicit column names. Please note: this may make restoration from backup slow.
* Move the switch next to *Use Insert commands* to the *Yes* position to dump the data in the form of INSERT statements rather than using a COPY command. Please note: this may make restoration from backup slow.
* Move the switch next to *Include DROP DATABASE statement* to the *Yes* position to include a command in the backup that will drop any existing database object with the same name before recreating the object during a backup.
.. image:: images/backup_server_disable.png
:alt: Disable option on backup server dialog
* Move switches in the **Disable** field box to specify the type of statements that should be excluded from the backup.
* Move the switch next to *Trigger* (active when creating a data-only backup) to the *Yes* position to include commands that will disable triggers on the target table while the data is being loaded.
* Move the switch next to *$ quoting* to the *Yes* position to enable dollar quoting within function bodies; if disabled, the function body will be quoted using SQL standard string syntax.
.. image:: images/backup_server_miscellaneous.png
:alt: Miscellaneous option on backup server dialog
* Move switches in the **Miscellaneous** field box to specify miscellaneous backup options.
* Move the switch next to *With OIDs* to the *Yes* position to include object identifiers as part of the table data for each table.
* Move the switch next to *Verbose messages* to the *No* position to instruct *pg_dump* to exclude verbose messages.
* Move the switch next to *Force double quotes on identifiers* to the *Yes* position to force the quoting of all identifiers.
* Move the switch next to *Use SET SESSION AUTHORIZATION* to the *Yes* position to include a statement that will use a SET SESSION AUTHORIZATION command to determine object ownership (instead of an ALTER OWNER command).
Click the *Backup* button to build and execute a command based on your selections; click the *Cancel* button to exit without saving work.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 114 KiB

View File

@ -10,6 +10,7 @@ This release contains a number of features and fixes reported since the release
Features
********
| `Feature #3503 <https://redmine.postgresql.org/issues/3503>`_ - Added new backup/restore options for PostgreSQL 11. Added dump options for 'pg_dumpall'.
| `Feature #3553 <https://redmine.postgresql.org/issues/3553>`_ - Add a Spanish translation.
Bug fixes
@ -17,6 +18,7 @@ Bug fixes
| `Bug #3136 <https://redmine.postgresql.org/issues/3136>`_ - Stabilise feature tests for continuous running on CI systems.
| `Bug #3325 <https://redmine.postgresql.org/issues/3325>`_ - Fix sort/filter dialog issue where it incorrectly requires ASC/DESC.
| `Bug #3347 <https://redmine.postgresql.org/issues/3347>`_ - Ensure backup should work with '--data-only' and '--schema-only' for any format.
| `Bug #3407 <https://redmine.postgresql.org/issues/3407>`_ - Fix keyboard shortcuts layout in the preferences panel.
| `Bug #3461 <https://redmine.postgresql.org/issues/3461>`_ - Ensure that refreshing a node also updates the Property list.
| `Bug #3528 <https://redmine.postgresql.org/issues/3528>`_ - Handle connection errors properly in the query tool.

View File

@ -49,6 +49,8 @@ Click the *Restore options* tab to continue. Use the fields on the *Restore opti
* Move the switch next to *Owner* to the *Yes* position to exclude commands that set object ownership.
* Move the switch next to *Privilege* to the *Yes* position to exclude commands that create access privileges.
* Move the switch next to *Tablespace* to the *Yes* position to exclude tablespaces.
* Move the switch next to *Comments* to the *Yes* position to exclude commands that set the comments. **Note:** This option is visible only for database server greater than or equal to 11.
.. image:: images/restore_queries.png
:alt: Restore dialog queries section

View File

@ -259,117 +259,6 @@ def filename_with_file_manager_path(_file, create_file=True):
@blueprint.route(
'/job/<int:sid>', methods=['POST'], endpoint='create_server_job'
)
@login_required
def create_backup_job(sid):
"""
Args:
sid: Server ID
Creates a new job for backup task (Backup Server/Globals)
Returns:
None
"""
if request.form:
# Convert ImmutableDict to dict
data = dict(request.form)
data = json.loads(data['data'][0], encoding='utf-8')
else:
data = json.loads(request.data, encoding='utf-8')
try:
backup_file = filename_with_file_manager_path(data['file'])
except Exception as e:
return bad_request(errormsg=str(e))
# Fetch the server details like hostname, port, roles etc
server = Server.query.filter_by(
id=sid, user_id=current_user.id
).first()
if server is None:
return make_json_response(
success=0,
errormsg=_("Could not find the specified server.")
)
# To fetch MetaData for the server
from pgadmin.utils.driver import get_driver
driver = get_driver(PG_DEFAULT_DRIVER)
manager = driver.connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
if not connected:
return make_json_response(
success=0,
errormsg=_("Please connect to the server first.")
)
utility = manager.utility('backup_server')
args = [
'--file',
backup_file,
'--host',
manager.local_bind_host if manager.use_ssh_tunnel else server.host,
'--port',
str(manager.local_bind_port) if manager.use_ssh_tunnel
else str(server.port),
'--username',
server.username,
'--no-password',
'--database',
server.maintenance_db
]
if 'role' in data and data['role']:
args.append('--role')
args.append(data['role'])
if 'verbose' in data and data['verbose']:
args.append('--verbose')
if 'dqoute' in data and data['dqoute']:
args.append('--quote-all-identifiers')
if data['type'] == 'globals':
args.append('--globals-only')
try:
p = BatchProcess(
desc=BackupMessage(
BACKUP.SERVER if data['type'] != 'globals' else BACKUP.GLOBALS,
sid,
data['file'].encode('utf-8') if hasattr(
data['file'], 'encode'
) else data['file'],
*args
),
cmd=utility, args=args
)
manager.export_password_env(p.id)
# Check for connection timeout and if it is greater than 0 then
# set the environment variable PGCONNECT_TIMEOUT.
if manager.connect_timeout > 0:
env = dict()
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
p.set_env_variables(server, env=env)
else:
p.set_env_variables(server)
p.start()
jid = p.id
except Exception as e:
current_app.logger.exception(e)
return make_json_response(
status=410,
success=0,
errormsg=str(e)
)
# Return response
return make_json_response(
data={'job_id': jid, 'success': 1}
)
@blueprint.route(
'/job/<int:sid>/object', methods=['POST'], endpoint='create_object_job'
)
@ -392,12 +281,12 @@ def create_backup_objects_job(sid):
else:
data = json.loads(request.data, encoding='utf-8')
# Remove ratio from data in case of empty string
if 'ratio' in data and data['ratio'] == '':
data.pop("ratio")
backup_obj_type = 'objects'
if 'type' in data:
backup_obj_type = data['type']
try:
if data['format'] == 'directory':
if 'format' in data and data['format'] == 'directory':
backup_file = filename_with_file_manager_path(data['file'], False)
else:
backup_file = filename_with_file_manager_path(data['file'])
@ -428,7 +317,9 @@ def create_backup_objects_job(sid):
errormsg=_("Please connect to the server first.")
)
utility = manager.utility('backup')
utility = manager.utility('backup') if backup_obj_type == 'objects' \
else manager.utility('backup_server')
args = [
'--file',
backup_file,
@ -442,48 +333,53 @@ def create_backup_objects_job(sid):
'--no-password'
]
if backup_obj_type != 'objects':
args.append('--database')
args.append(server.maintenance_db)
if backup_obj_type == 'globals':
args.append('--globals-only')
def set_param(key, param):
if key in data and data[key]:
args.append(param)
def set_value(key, param, value):
if key in data:
if value:
if value is True and data[key]:
args.append(param)
args.append(data[key])
else:
args.append(param)
args.append(value)
def set_value(key, param, default_value=None):
if key in data and data[key] is not None and data[key] != '':
args.append(param)
args.append(data[key])
elif default_value is not None:
args.append(param)
args.append(default_value)
set_param('verbose', '--verbose')
set_param('dqoute', '--quote-all-identifiers')
set_value('role', '--role', True)
if data['format'] is not None:
set_value('role', '--role')
if backup_obj_type == 'objects' and \
'format' in data and data['format'] is not None:
if data['format'] == 'custom':
args.extend(['--format=c'])
set_param('blobs', '--blobs')
set_value('ratio', '--compress', True)
set_value('ratio', '--compress')
elif data['format'] == 'tar':
args.extend(['--format=t'])
set_param('blobs', '--blobs')
elif data['format'] == 'plain':
args.extend(['--format=p'])
if 'only_data' in data and data['only_data']:
args.append('--data-only')
set_param('disable_trigger', '--disable-triggers')
else:
set_param('only_schema', '--schema-only')
set_param('dns_owner', '--no-owner')
set_param('include_create_database', '--create')
set_param('include_drop_database', '--clean')
elif data['format'] == 'directory':
args.extend(['--format=d'])
if 'only_data' in data and data['only_data']:
set_param('only_data', '--data-only')
if 'format' in data and data['format'] == 'plain':
set_param('disable_trigger', '--disable-triggers')
elif 'only_schema' in data and data['only_schema']:
set_param('only_schema', '--schema-only')
set_param('dns_owner', '--no-owner')
set_param('include_create_database', '--create')
set_param('include_drop_database', '--clean')
set_param('pre_data', '--section=pre-data')
set_param('data', '--section=data')
set_param('post_data', '--section=post-data')
@ -496,31 +392,51 @@ def create_backup_objects_job(sid):
set_param('with_oids', '--oids')
set_param('use_set_session_auth', '--use-set-session-authorization')
set_value('encoding', '--encoding', True)
set_value('no_of_jobs', '--jobs', True)
if manager.version >= 110000:
set_param('no_comments', '--no-comments')
set_param('load_via_partition_root', '--load-via-partition-root')
for s in data['schemas']:
args.extend(['--schema', s])
set_value('encoding', '--encoding')
set_value('no_of_jobs', '--jobs')
for s, t in data['tables']:
args.extend([
'--table', driver.qtIdent(conn, s, t)
])
if 'schemas' in data:
for s in data['schemas']:
args.extend(['--schema', s])
args.append(data['database'])
if 'tables' in data:
for s, t in data['tables']:
args.extend([
'--table', driver.qtIdent(conn, s, t)
])
try:
p = BatchProcess(
desc=BackupMessage(
BACKUP.OBJECT, sid,
data['file'].encode('utf-8') if hasattr(
data['file'], 'encode'
) else data['file'],
*args,
database=data['database']
),
cmd=utility, args=args
)
if backup_obj_type == 'objects':
args.append(data['database'])
p = BatchProcess(
desc=BackupMessage(
BACKUP.OBJECT, sid,
data['file'].encode('utf-8') if hasattr(
data['file'], 'encode'
) else data['file'],
*args,
database=data['database']
),
cmd=utility, args=args
)
else:
p = BatchProcess(
desc=BackupMessage(
BACKUP.SERVER if backup_obj_type != 'globals'
else BACKUP.GLOBALS,
sid,
data['file'].encode('utf-8') if hasattr(
data['file'], 'encode'
) else data['file'],
*args
),
cmd=utility, args=args
)
manager.export_password_env(p.id)
# Check for connection timeout and if it is greater than 0 then
# set the environment variable PGCONNECT_TIMEOUT.

View File

@ -62,7 +62,7 @@ define([
dqoute: false,
verbose: true,
type: undefined,
/* global, server */
/* global */
},
schema: [{
id: 'file',
@ -97,22 +97,11 @@ define([
disabled: false,
group: gettext('Miscellaneous'),
}],
}, {
id: 'server_note',
label: gettext('Note'),
text: gettext('The backup format will be PLAIN'),
type: 'note',
visible: function(m) {
return m.get('type') === 'server';
},
}, {
id: 'globals_note',
label: gettext('Note'),
text: gettext('Only objects global to the entire database will be backed up in PLAIN format'),
type: 'note',
visible: function(m) {
return m.get('type') === 'globals';
},
}, {}],
validate: function() {
// TODO: HOW TO VALIDATE ???
@ -183,6 +172,13 @@ define([
value: 'directory',
},
],
visible: function(m) {
if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') {
setTimeout(function() { m.set('format', 'plain'); }, 10);
return false;
}
return true;
},
}, {
id: 'ratio',
label: gettext('Compression ratio'),
@ -190,6 +186,11 @@ define([
min: 0,
max: 9,
disabled: false,
visible: function(m) {
if (!_.isUndefined(m.get('type')) && m.get('type') === 'server')
return false;
return true;
},
}, {
id: 'encoding',
label: gettext('Encoding'),
@ -198,6 +199,15 @@ define([
node: 'database',
control: 'node-ajax-options',
url: 'get_encodings',
visible: function(m) {
if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') {
var t = pgBrowser.tree,
i = t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
return d.version >= 110000;
}
return true;
},
}, {
id: 'no_of_jobs',
label: gettext('Number of jobs'),
@ -206,6 +216,11 @@ define([
disabled: function(m) {
return !(m.get('format') === 'Directory');
},
visible: function(m) {
if (!_.isUndefined(m.get('type')) && m.get('type') === 'server')
return false;
return true;
},
}, {
id: 'role',
label: gettext('Role name'),
@ -214,6 +229,14 @@ define([
select2: {
allowClear: false,
},
}, {
id: 'server_note',
label: gettext('Note'),
text: gettext('The backup format will be PLAIN'),
type: 'note',
visible: function(m) {
return m.get('type') === 'server';
},
}, {
type: 'nested',
control: 'fieldset',
@ -250,6 +273,11 @@ define([
m.get('only_schema');
},
}],
visible: function(m) {
if (!_.isUndefined(m.get('type')) && m.get('type') === 'server')
return false;
return true;
},
}, {
type: 'nested',
control: 'fieldset',
@ -285,6 +313,13 @@ define([
control: Backform.CustomSwitchControl,
disabled: false,
group: gettext('Type of objects'),
visible: function(m) {
if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') {
setTimeout(function() { m.set('blobs', false); }, 10);
return false;
}
return true;
},
}],
}, {
type: 'nested',
@ -315,6 +350,20 @@ define([
control: Backform.CustomSwitchControl,
disabled: false,
group: gettext('Do not save'),
}, {
id: 'no_comments',
label: gettext('Comments'),
control: Backform.CustomSwitchControl,
disabled: false,
group: gettext('Do not save'),
visible: function() {
var t = pgBrowser.tree,
i = t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
s = pgBrowser.Nodes[d._type].getTreeNodeHierarchy(i)['server'];
return s.version >= 110000;
},
}],
}, {
type: 'nested',
@ -339,12 +388,41 @@ define([
control: Backform.CustomSwitchControl,
disabled: false,
group: gettext('Queries'),
visible: function(m) {
if (!_.isUndefined(m.get('type')) && m.get('type') === 'server')
return false;
return true;
},
}, {
id: 'include_drop_database',
label: gettext('Include DROP DATABASE statement'),
control: Backform.CustomSwitchControl,
group: gettext('Queries'),
deps: ['only_data'],
disabled: function(m) {
if (m.get('only_data')) {
setTimeout(function() { m.set('include_drop_database', false); }, 10);
return true;
}
return false;
},
}, {
id: 'load_via_partition_root',
label: gettext('Load Via Partition Root'),
control: Backform.CustomSwitchControl,
disabled: false,
group: gettext('Queries'),
visible: function(m) {
if (!_.isUndefined(m.get('type')) && m.get('type') === 'server')
return false;
var t = pgBrowser.tree,
i = t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
s = pgBrowser.Nodes[d._type].getTreeNodeHierarchy(i)['server'];
return s.version >= 110000;
},
}],
}, {
type: 'nested',
@ -376,8 +454,15 @@ define([
id: 'with_oids',
label: gettext('With OID(s)'),
control: Backform.CustomSwitchControl,
disabled: false,
deps: ['use_column_inserts', 'use_insert_commands'],
group: gettext('Miscellaneous'),
disabled: function(m) {
if (m.get('use_column_inserts') || m.get('use_insert_commands')) {
setTimeout(function() { m.set('with_oids', false); }, 10);
return true;
}
return false;
},
}, {
id: 'verbose',
label: gettext('Verbose messages'),
@ -483,33 +568,23 @@ define([
return this;
},
start_backup_global: function(action, item) {
var params = {
'globals': true,
};
this.start_backup_global_server.apply(
this, [action, item, params]
);
},
start_backup_server: function(action, item) {
var params = {
'server': true,
};
this.start_backup_global_server.apply(
this, [action, item, params]
);
},
// Callback to draw Backup Dialog for globals/server
start_backup_global_server: function(action, treeItem, params) {
let dialog = new globalBackupDialog.BackupDialog(
pgBrowser,
$,
alertify,
BackupModel
);
dialog.draw(action, treeItem, params);
dialog.draw(action, item, {'globals': true});
},
start_backup_server: function(action, item) {
let dialog = new globalBackupDialog.BackupDialog(
pgBrowser,
$,
alertify,
BackupObjectModel
);
dialog.draw(action, item, {'server': true});
},
// Callback to draw Backup Dialog for objects
backup_objects: function(action, treeItem) {
let dialog = new globalBackupDialog.BackupDialog(

View File

@ -156,7 +156,7 @@ class BackupCreateJobTest(BaseTestGenerator):
),
url='/backup/job/{0}/object',
expected_cmd_opts=['--verbose', '--format=p', '--data-only'],
not_expected_cmd_opts=['--schema-only', '--no-owner'],
not_expected_cmd_opts=['--schema-only'],
expected_exit_code=[0, None]
)),
('When backup the object with option only_schema',
@ -240,6 +240,34 @@ class BackupCreateJobTest(BaseTestGenerator):
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the object with option - Do not save comments,',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_file',
format='custom',
verbose=True,
schemas=[],
tables=[],
database='postgres',
no_comments=True,
),
url='/backup/job/{0}/object',
expected_cmd_opts=['--no-comments'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None],
server_min_version=110000,
message='Backup object with --no-comments are not supported '
'by EPAS/PG server less than 11.0'
)),
('When backup the object with option - all queries',
dict(
class_params=dict(
@ -269,6 +297,34 @@ class BackupCreateJobTest(BaseTestGenerator):
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the object with option - load via partition root',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_file',
format='plain',
verbose=True,
schemas=[],
tables=[],
database='postgres',
load_via_partition_root=True,
),
url='/backup/job/{0}/object',
expected_cmd_opts=['--load-via-partition-root'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None],
server_min_version=110000,
message='Backup object with --load-via-partition-root are not '
'supported by EPAS/PG server less than 11.0'
)),
('When backup the object with option - all queries and format custom',
dict(
class_params=dict(
@ -293,9 +349,9 @@ class BackupCreateJobTest(BaseTestGenerator):
include_drop_database=True
),
url='/backup/job/{0}/object',
expected_cmd_opts=['--inserts',
'--column-inserts'],
not_expected_cmd_opts=['--create', '--clean'],
expected_cmd_opts=['--inserts', '--clean',
'--column-inserts', '--create'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the object with option - miscellaneous',
@ -355,6 +411,7 @@ class BackupCreateJobTest(BaseTestGenerator):
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the server',
dict(
class_params=dict(
@ -377,6 +434,186 @@ class BackupCreateJobTest(BaseTestGenerator):
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the server with option only_data',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_server_file',
type='server',
verbose=True,
only_data=True,
only_schema=False
),
url='/backup/job/{0}',
expected_cmd_opts=['--verbose', '--data-only'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the server with option only_schema',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_server_file',
type='server',
format='plain',
verbose=True,
only_data=False,
only_schema=True
),
url='/backup/job/{0}',
expected_cmd_opts=['--verbose', '--schema-only'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the server with option - Do not save privilege,'
' tablespace, unlogged table data',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_server_file',
type='server',
format='plain',
verbose=True,
dns_privilege=True,
dns_unlogged_tbl_data=True,
dns_tablespace=True
),
url='/backup/job/{0}',
expected_cmd_opts=['--no-privileges',
'--no-tablespaces',
'--no-unlogged-table-data'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the server with option - Do not save comments,',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_server_file',
type='server',
format='plain',
verbose=True,
no_comments=True,
),
url='/backup/job/{0}',
expected_cmd_opts=['--no-comments'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None],
server_min_version=110000,
message='Backup server with --no-comments are not supported '
'by EPAS/PG server less than 11.0'
)),
('When backup the server with option - all queries',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_server_file',
type='server',
format='plain',
verbose=True,
use_column_inserts=True,
use_insert_commands=True,
include_drop_database=True
),
url='/backup/job/{0}',
expected_cmd_opts=['--clean', '--inserts',
'--column-inserts'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the server with option - miscellaneous',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_server_file',
type='server',
verbose=True,
disable_quoting=True,
use_set_session_auth=True,
with_oids=True,
dqoute=True
),
url='/backup/job/{0}',
expected_cmd_opts=['--verbose', '--quote-all-identifiers',
'--disable-dollar-quoting', '--oids',
'--use-set-session-authorization'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When backup the server with encoding',
dict(
class_params=dict(
sid=1,
name='test_backup_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_backup',
username='postgres'
),
params=dict(
file='test_backup_server_file',
dqoute=False,
verbose=True,
type='server',
encoding='UTF-8'
),
url='/backup/job/{0}',
expected_cmd_opts=['--encoding'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None],
server_min_version=110000,
message='Backup server with encoding are not supported '
'by EPAS/PG server less than 11.0'
)),
('When backup globals',
dict(
class_params=dict(
@ -454,6 +691,11 @@ class BackupCreateJobTest(BaseTestGenerator):
db_owner = server_response['data']['user']['name']
self.data = database_utils.get_db_data(db_owner)
if hasattr(self, 'server_min_version') and \
server_response["data"]["version"] < \
self.server_min_version:
self.skipTest(self.message)
url = self.url.format(self.server_id)
# Create the backup job

View File

@ -243,17 +243,13 @@ def create_restore_job(sid):
return True
return False
def set_value(key, param, value):
if key in data:
if value:
if value is True and data[key]:
args.append(param)
args.append(data[key])
else:
args.append(param)
args.append(value)
return True
return False
def set_value(key, param, default_value=None):
if key in data and data[key] is not None and data[key] != '':
args.append(param)
args.append(data[key])
elif default_value is not None:
args.append(param)
args.append(default_value)
def set_multiple(key, param, with_schema=True):
if key in data:
@ -293,8 +289,8 @@ def create_restore_job(sid):
'--username', server.username, '--no-password'
])
set_value('role', '--role', True)
set_value('database', '--dbname', True)
set_value('role', '--role')
set_value('database', '--dbname')
if data['format'] == 'directory':
args.extend(['--format=d'])
@ -318,7 +314,10 @@ def create_restore_job(sid):
set_param('use_set_session_auth', '--use-set-session-authorization')
set_param('exit_on_error', '--exit-on-error')
set_value('no_of_jobs', '--jobs', True)
if manager.version >= 110000:
set_param('no_comments', '--no-comments')
set_value('no_of_jobs', '--jobs')
set_param('verbose', '--verbose')
set_multiple('schemas', '--schema', False)

View File

@ -240,6 +240,20 @@ commonUtils, menuUtils, supportedNodes, restoreDialog
control: Backform.CustomSwitchControl,
disabled: false,
group: gettext('Do not save'),
}, {
id: 'no_comments',
label: gettext('Comments'),
control: Backform.CustomSwitchControl,
disabled: false,
group: gettext('Do not save'),
visible: function() {
var t = pgBrowser.tree,
i = t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
s = pgBrowser.Nodes[d._type].getTreeNodeHierarchy(i)['server'];
return s.version >= 110000;
},
}],
}, {
type: 'nested',

View File

@ -1,322 +0,0 @@
diff a/web/pgadmin/tools/restore/static/js/restore.js b/web/pgadmin/tools/restore/static/js/restore.js (rejected hunks)
@@ -391,318 +344,8 @@ commonUtils
},
// Callback to draw Backup Dialog for objects
restore_objects: function(action, treeItem) {
-
- var i = treeItem || pgBrowser.tree.selected(),
- server_data = null;
-
- while (i) {
- var node_data = pgBrowser.tree.itemData(i);
- if (node_data._type == 'server') {
- server_data = node_data;
- break;
- }
-
- if (pgBrowser.tree.hasParent(i)) {
- i = $(pgBrowser.tree.parent(i));
- } else {
- alertify.alert(
- gettext('Restore Error'),
- gettext('Please select server or child node from tree.')
- );
- break;
- }
- }
-
- if (!server_data) {
- return;
- }
-
- var module = 'paths',
- preference_name = 'pg_bin_dir',
- msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
-
- if ((server_data.type && server_data.type == 'ppas') ||
- server_data.server_type == 'ppas') {
- preference_name = 'ppas_bin_dir';
- msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
- }
-
- var preference = pgBrowser.get_preference(module, preference_name);
-
- if (preference) {
- if (!preference.value) {
- alertify.alert(gettext('Configuration required'), msg);
- return;
- }
- } else {
- alertify.alert(
- gettext('Restore Error'),
- S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value()
- );
- return;
- }
-
- var title = S(gettext('Restore (%s: %s)')),
- tree = pgBrowser.tree,
- item = treeItem || tree.selected(),
- data = item && item.length == 1 && tree.itemData(item),
- node = data && data._type && pgBrowser.Nodes[data._type];
-
- if (!node)
- return;
-
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [item]);
-
- if (treeInfo.database._label.indexOf('=') >= 0) {
- alertify.alert(
- gettext('Restore error'),
- gettext('Restore job creation failed. '+
- 'Databases with = symbols in the name cannot be restored using this utility.')
- );
- return;
- }
-
- title = title.sprintf(node.label, data.label).value();
-
- if (!alertify.pg_restore) {
- // Create Dialog title on the fly with node details
- alertify.dialog('pg_restore', function factory() {
- return {
- main: function(title, item, data, node) {
- this.set('title', title);
- this.setting('pg_node', node);
- this.setting('pg_item', item);
- this.setting('pg_item_data', data);
- },
- build: function() {
- alertify.pgDialogBuild.apply(this);
- },
- setup: function() {
- return {
- buttons: [{
- text: '',
- className: 'btn btn-default pull-left fa fa-lg fa-info',
- attrs: {
- name: 'object_help',
- type: 'button',
- url: 'backup.html',
- label: gettext('Restore'),
- },
- }, {
- text: '',
- key: 112,
- className: 'btn btn-default pull-left fa fa-lg fa-question',
- attrs: {
- name: 'dialog_help',
- type: 'button',
- label: gettext('Restore'),
- url: url_for('help.static', {
- 'filename': 'restore_dialog.html',
- }),
- },
- }, {
- text: gettext('Restore'),
- key: 13,
- className: 'btn btn-primary fa fa-upload pg-alertify-button',
- restore: true,
- 'data-btn-name': 'restore',
- }, {
- text: gettext('Cancel'),
- key: 27,
- className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
- restore: false,
- 'data-btn-name': 'cancel',
- }],
- // Set options for dialog
- options: {
- title: title,
- //disable both padding and overflow control.
- padding: !1,
- overflow: !1,
- model: 0,
- resizable: true,
- maximizable: true,
- pinnable: false,
- closableByDimmer: false,
- modal: false,
- },
- };
- },
- hooks: {
- // triggered when the dialog is closed
- onclose: function() {
- if (this.view) {
- this.view.remove({
- data: true,
- internal: true,
- silent: true,
- });
- }
- },
- },
- settings: {
- pg_node: null,
- pg_item: null,
- pg_item_data: null,
- },
- prepare: function() {
-
- var self = this;
- // Disable Backup button until user provides Filename
- this.__internal.buttons[2].element.disabled = true;
- var $container = $('<div class=\'restore_dialog\'></div>');
- var t = pgBrowser.tree,
- i = t.selected(),
- d = i && i.length == 1 ? t.itemData(i) : undefined,
- node = d && pgBrowser.Nodes[d._type];
-
- if (!d)
- return;
-
- var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
-
- var newModel = new RestoreObjectModel({
- node_data: node,
- }, {
- node_info: treeInfo,
- }),
- fields = Backform.generateViewSchema(
- treeInfo, newModel, 'create', node, treeInfo.server, true
- );
-
- var view = this.view = new Backform.Dialog({
- el: $container,
- model: newModel,
- schema: fields,
- });
-
- $(this.elements.body.childNodes[0]).addClass(
- 'alertify_tools_dialog_properties obj_properties'
- );
-
- view.render();
-
- this.elements.content.appendChild($container.get(0));
-
- view.$el.attr('tabindex', -1);
- // var dialogTabNavigator = pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
- pgBrowser.keyboardNavigation.getDialogTabNavigator(view);
- var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
- commonUtils.findAndSetFocus(container);
-
- // Listen to model & if filename is provided then enable Backup button
- this.view.model.on('change', function() {
- if (!_.isUndefined(this.get('file')) && this.get('file') !== '') {
- this.errorModel.clear();
- self.__internal.buttons[2].element.disabled = false;
- } else {
- self.__internal.buttons[2].element.disabled = true;
- this.errorModel.set('file', gettext('Please provide filename'));
- }
- });
-
- },
- // Callback functions when click on the buttons of the Alertify dialogs
- callback: function(e) {
- // Fetch current server id
- var t = pgBrowser.tree,
- i = this.settings['pg_item'] || t.selected(),
- d = this.settings['pg_item_data'] || (
- i && i.length == 1 ? t.itemData(i) : undefined
- ),
- node = this.settings['pg_node'] || (
- d && pgBrowser.Nodes[d._type]
- );
-
- if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') {
- e.cancel = true;
- pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
- node, i, e.button.element.getAttribute('label'));
- return;
- }
-
- if (e.button['data-btn-name'] === 'restore') {
- if (!d)
- return;
-
- var info = node.getTreeNodeHierarchy.apply(node, [i]),
- m = this.view.model;
- // Set current node info into model
- m.set('database', info.database._label);
- if (!m.get('custom')) {
- switch (d._type) {
- case 'schema':
- m.set('schemas', [d._label]);
- break;
- case 'table':
- m.set('schemas', [info.schema._label]);
- m.set('tables', [d._label]);
- break;
- case 'function':
- m.set('schemas', [info.schema._label]);
- m.set('functions', [d._label]);
- break;
- case 'index':
- m.set('schemas', [info.schema._label]);
- m.set('indexes', [d._label]);
- break;
- case 'trigger':
- m.set('schemas', [info.schema._label]);
- m.set('triggers', [d._label]);
- break;
- case 'trigger_func':
- m.set('schemas', [info.schema._label]);
- m.set('trigger_funcs', [d._label]);
- break;
- }
- } else {
- // TODO::
- // When we will implement the object selection in the
- // import dialog, we will need to select the objects from
- // the tree selection tab.
- }
-
- var self = this,
- baseUrl = url_for('restore.create_job', {
- 'sid': info.server._id,
- }),
- args = this.view.model.toJSON();
-
- $.ajax({
- url: baseUrl,
- method: 'POST',
- data: {
- 'data': JSON.stringify(args),
- },
- success: function(res) {
- if (res.success) {
- alertify.success(
- gettext('Restore job created.'), 5
- );
- pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
- } else {
- console.warn(res);
- }
- },
- error: function(xhr) {
- try {
- var err = $.parseJSON(xhr.responseText);
- alertify.alert(
- gettext('Restore failed.'),
- err.errormsg
- );
- } catch (e) {
- console.warn(e.stack || e);
- }
- },
- });
- }
- },
- };
- });
- }
-
- alertify.pg_restore(title, item, data, node).resizeTo('65%', '60%');
+ let dialog = new restoreDialog.RestoreDialog(pgBrowser, $, alertify, RestoreObjectModel);
+ dialog.draw(action, treeItem);
},
};
return pgBrowser.Restore;

View File

@ -174,6 +174,36 @@ class RestoreCreateJobTest(BaseTestGenerator):
not_expected_cmd_opts=[],
expected_exit_code=[0, None]
)),
('When restore object with option - Do not save comments',
dict(
class_params=dict(
sid=1,
name='test_restore_server',
port=5444,
host='localhost',
database='postgres',
bfile='test_restore',
username='postgres'
),
params=dict(
file='test_restore_file',
format='custom',
verbose=True,
custom=False,
schemas=[],
tables=[],
database='postgres',
no_comments=True,
only_data=False
),
url='/restore/job/{0}',
expected_cmd_opts=['--no-comments'],
not_expected_cmd_opts=[],
expected_exit_code=[0, None],
server_min_version=110000,
message='Restore object with --no-comments are not supported '
'by EPAS/PG server less than 11.0'
)),
('When restore object with option - Queries',
dict(
class_params=dict(
@ -315,6 +345,11 @@ class RestoreCreateJobTest(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)
url = self.url.format(self.server_id)
# Create the restore job