1) Added SSL support for creating a subscription. Fixes #6201

2) Fixed an issue where the user is not able to create the subscription. Fixes #6230
3) Fixed a couple of issues raised during testing of logical replication.
This commit is contained in:
Pradip Parkale 2021-02-19 14:56:45 +05:30 committed by Akshay Joshi
parent 32197a8405
commit 731ba32e81
16 changed files with 277 additions and 40 deletions

View File

@ -43,4 +43,6 @@ Bug fixes
| `Issue #6187 <https://redmine.postgresql.org/issues/6187>`_ - Limit the upgrade check to run once per day.
| `Issue #6193 <https://redmine.postgresql.org/issues/6193>`_ - Ensure that ERD throws a warning before closing unsaved changes if open in a new tab.
| `Issue #6197 <https://redmine.postgresql.org/issues/6197>`_ - Fixed an issue where the ERD image is not properly downloaded.
| `Issue #6201 <https://redmine.postgresql.org/issues/6201>`_ - Added SSL support for creating a subscription.
| `Issue #6208 <https://redmine.postgresql.org/issues/6208>`_ - Fixed an issue where utility(Backup, Maintenance, ...) jobs are failing when the log level is set to DEBUG.
| `Issue #6230 <https://redmine.postgresql.org/issues/6230>`_ - Fixed an issue where the user is not able to create the subscription.

View File

@ -244,6 +244,7 @@ class ServerModule(sg.ServerGroupPluginModule):
user=user_info,
in_recovery=in_recovery,
wal_pause=wal_paused,
host=server.host,
is_password_saved=bool(server.save_password),
is_tunnel_password_saved=True
if server.tunnel_password is not None else False,
@ -535,6 +536,7 @@ class ServerNode(PGChildNodeView):
server_type=server_type,
version=manager.version,
db=manager.db,
host=server.host,
user=manager.user_info if connected else None,
in_recovery=in_recovery,
wal_pause=wal_paused,
@ -604,6 +606,7 @@ class ServerNode(PGChildNodeView):
user=manager.user_info if connected else None,
in_recovery=in_recovery,
wal_pause=wal_paused,
host=server.host,
is_password_saved=bool(server.save_password),
is_tunnel_password_saved=True
if server.tunnel_password is not None else False,

View File

@ -80,6 +80,7 @@ define('pgadmin.node.publication', [
evnt_update:true,
evnt_truncate:true,
only_table: undefined,
publish_via_partition_root: false,
},
// Default values!
@ -177,6 +178,20 @@ define('pgadmin.node.publication', [
return false;
},
},{
id: 'publish_via_partition_root', label: gettext('Publish via root?'),
type: 'switch', group: gettext('With'),
extraToggleClasses: 'pg-el-sm-6',
controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12',
controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12',
visible: function(m) {
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
&& !_.isUndefined(m.node_info.server.version) &&
m.node_info.server.version >= 130000)
return true;
return false;
},
}],
},
],

View File

@ -2,7 +2,7 @@ SELECT c.oid AS oid, c.pubname AS name,
pubinsert AS evnt_insert, pubupdate AS evnt_update, pubdelete AS evnt_delete, pubtruncate AS evnt_truncate,
puballtables AS all_table,
pga.rolname AS pubowner FROM pg_publication c
JOIN pg_authid pga ON c.pubowner= pga.oid
JOIN pg_roles pga ON c.pubowner= pga.oid
{% if pbid %}
WHERE c.oid = {{ pbid }}
{% endif %}

View File

@ -0,0 +1,23 @@
{% if data.evnt_delete or data.evnt_update or data.evnt_truncate %}
{% set add_comma_after_insert = 'insert' %}
{% endif %}
{% if data.evnt_truncate %}
{% set add_comma_after_delete = 'delete' %}
{% endif %}
{% if data.evnt_delete or data.evnt_truncate%}
{% set add_comma_after_update = 'update' %}
{% endif %}
{% if data.publish_via_partition_root%}
{% set add_comma_after_truncate = 'truncate' %}
{% endif %}
{### Create PUBLICATION ###}
CREATE PUBLICATION {{ conn|qtIdent(data.name) }}
{% if data.all_table %}
FOR ALL TABLES
{% elif data.pubtable %}
FOR TABLE {% if data.only_table%}ONLY {% endif %}{% for pub_table in data.pubtable %}{% if loop.index != 1 %}, {% endif %}{{ pub_table }}{% endfor %}
{% endif %}
{% if data.evnt_insert or data.evnt_update or data.evnt_delete or data.evnt_truncate %}
WITH (publish = '{% if data.evnt_insert %}insert{% if add_comma_after_insert == 'insert' %}, {% endif %}{% endif %}{% if data.evnt_update %}update{% if add_comma_after_update == 'update' %}, {% endif %}{% endif %}{% if data.evnt_delete %}delete{% if add_comma_after_delete == 'delete' %}, {% endif %}{% endif %}{% if data.evnt_truncate %}truncate{% endif %}', publish_via_partition_root = {{ data.publish_via_partition_root|lower }});
{% endif %}

View File

@ -0,0 +1,9 @@
SELECT c.oid AS oid, c.pubname AS name,
pubinsert AS evnt_insert, pubupdate AS evnt_update, pubdelete AS evnt_delete, pubtruncate AS evnt_truncate,
puballtables AS all_table,
pubviaroot AS publish_via_partition_root,
pga.rolname AS pubowner FROM pg_publication c
JOIN pg_roles pga ON c.pubowner= pga.oid
{% if pbid %}
WHERE c.oid = {{ pbid }}
{% endif %}

View File

@ -0,0 +1,48 @@
{% if data.evnt_delete or data.evnt_update or data.evnt_truncate %}
{% set add_comma_after_insert = 'insert' %}
{% endif %}
{% if data.evnt_truncate %}
{% set add_comma_after_delete = 'delete' %}
{% endif %}
{% if data.evnt_delete or data.evnt_truncate%}
{% set add_comma_after_update = 'update' %}
{% endif %}
{### Alter publication owner ###}
{% if data.pubowner %}
ALTER PUBLICATION {{ conn|qtIdent(o_data.name) }}
OWNER TO {{ data.pubowner }};
{% endif %}
{### Alter publication event ###}
{% if (data.evnt_insert is defined and data.evnt_insert != o_data.evnt_insert) or (data.evnt_update is defined and data.evnt_update != o_data.evnt_update) or (data.evnt_delete is defined and data.evnt_delete != o_data.evnt_delete) or (data.evnt_truncate is defined and data.evnt_truncate != o_data.evnt_truncate) %}
ALTER PUBLICATION {{ conn|qtIdent(o_data.name) }} SET
(publish = '{% if data.evnt_insert %}insert{% if add_comma_after_insert == 'insert' %}, {% endif %}{% endif %}{% if data.evnt_update %}update{% if add_comma_after_update == 'update' %}, {% endif %}{% endif %}{% if data.evnt_delete %}delete{% if add_comma_after_delete == 'delete' %}, {% endif %}{% endif %}{% if data.evnt_truncate %}truncate{% endif %}');
{% endif %}
{### Alter publication partition root ###}
{% if data.publish_via_partition_root is defined and data.publish_via_partition_root != o_data.publish_via_partition_root%}
ALTER PUBLICATION {{ conn|qtIdent(o_data.name) }} SET
(publish_via_partition_root = {{ data.publish_via_partition_root|lower }});
{% endif %}
{### Alter drop publication table ###}
{% if drop_table %}
ALTER PUBLICATION {{ conn|qtIdent(o_data.name) }}
DROP TABLE {% if data.only_table%}ONLY {% endif %}{% for pub_table in drop_table_data %}{% if loop.index != 1 %}, {% endif %}{{ pub_table }}{% endfor %};
{% endif %}
{### Alter publication table ###}
{% if add_table %}
ALTER PUBLICATION {{ conn|qtIdent(o_data.name) }}
ADD TABLE {% if data.only_table%}ONLY {% endif %}{% for pub_table in add_table_data %}{% if loop.index != 1 %}, {% endif %}{{ pub_table }}{% endfor %};
{% endif %}
{### Alter publication name ###}
{% if data.name != o_data.name %}
ALTER PUBLICATION {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}

View File

@ -3,4 +3,5 @@ FROM information_schema.tables c
WHERE c.table_type = 'BASE TABLE'
AND c.table_schema NOT LIKE 'pg\_%'
AND c.table_schema NOT LIKE 'pgagent'
AND c.table_schema NOT LIKE 'sys'
AND c.table_schema NOT IN ('information_schema') ORDER BY 1;

View File

@ -2,7 +2,7 @@ SELECT c.oid AS oid, c.pubname AS name,
pubinsert AS evnt_insert, pubupdate AS evnt_update, pubdelete AS evnt_delete,
puballtables AS all_table,
pga.rolname AS pubowner FROM pg_publication c
JOIN pg_authid pga ON c.pubowner= pga.oid
JOIN pg_roles pga ON c.pubowner= pga.oid
{% if pbid %}
where c.oid = {{ pbid }}
{% endif %}

View File

@ -261,8 +261,14 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
self._PROPERTIES_SQL]), did=did)
status, res = self.conn.execute_dict(sql)
# Check for permission denied message
if 'permission denied' in res:
return internal_server_error(
errormsg="Access is revoked for normal users")
if not status:
return internal_server_error(errormsg=res)
return ajax_response(
response=res['rows'],
status=200
@ -680,14 +686,21 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
host=connection_details['host'],
database=connection_details['db'],
user=connection_details['username'],
password=connection_details['password'] if
connection_details['password'] else None,
password=connection_details[
'password'] if 'password' in connection_details else None,
port=connection_details['port'] if
connection_details['port'] else None,
passfile=get_complete_file_path(passfile),
connect_timeout=connection_details['connect_timeout'] if
'connect_timeout' in connection_details and
connection_details['connect_timeout'] else 0
connection_details['connect_timeout'] else 0,
sslmode=connection_details['sslmode'],
sslcert=get_complete_file_path(connection_details['sslcert']),
sslkey=get_complete_file_path(connection_details['sslkey']),
sslrootcert=get_complete_file_path(
connection_details['sslrootcert']),
sslcompression=True if connection_details[
'sslcompression'] else False,
)
# create a cursor
cur = conn.cursor()
@ -715,7 +728,9 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
url_params = {k: v for k, v in request.args.items()}
required_connection_args = ['host', 'port', 'username', 'db',
'connect_timeout', 'passfile']
'connect_timeout', 'passfile', 'sslmode',
'sslcompression', 'sslcert', 'sslkey',
'sslrootcert', 'sslcrl']
if 'oid' in url_params:
status, params = self._fetch_properties(did, url_params['oid'])

View File

@ -12,6 +12,7 @@ define('pgadmin.node.subscription', [
'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform',
'sources/browser/server_groups/servers/model_validation', 'pgadmin.alertifyjs', 'pgadmin.browser.collection',
], function(gettext, url_for, $, _, pgAdmin, pgBrowser, Backform, modelValidation, Alertify) {
var SSL_MODES = ['prefer', 'require', 'verify-ca', 'verify-full'];
// Extend the browser's collection class for subscriptions collection
if (!pgBrowser.Nodes['coll-subscription']) {
@ -77,7 +78,7 @@ define('pgadmin.node.subscription', [
name: undefined,
subowner: undefined,
pubtable: undefined,
connect_timeout: undefined,
connect_timeout: 10,
pub:[],
enabled:true,
create_slot: true,
@ -85,7 +86,18 @@ define('pgadmin.node.subscription', [
connect:true,
copy_data_after_refresh:false,
sync:'off',
refresh_pub: undefined,
refresh_pub: false,
password: '',
sslmode: 'prefer',
sslcompression: false,
sslcert: '',
sslkey: '',
sslrootcert: '',
sslcrl: '',
host: '',
hostaddr: '',
port: 5432,
db: 'postgres',
},
// Default values!
@ -189,7 +201,7 @@ define('pgadmin.node.subscription', [
id: 'pub', label: gettext('Publication'), type: 'array', select2: { allowClear: true, multiple: true, width: '92%'},
group: gettext('Connection'), mode: ['create', 'edit'], controlsClassName: 'pgadmin-controls pg-el-sm-11 pg-el-12',
deps: ['all_table', 'host', 'port', 'username', 'db', 'password'], disabled: 'isAllConnectionDataEnter',
helpMessage: gettext('Click the refresh button to get the publications"'),
helpMessage: gettext('Click the refresh button to get the publications'),
control: Backform.Select2Control.extend({
defaults: _.extend(Backform.Select2Control.prototype.defaults, {
select2: {
@ -280,6 +292,91 @@ define('pgadmin.node.subscription', [
},
}),
},
{
id: 'sslmode', label: gettext('SSL mode'), control: 'select2', group: gettext('SSL'),
select2: {
allowClear: false,
minimumResultsForSearch: Infinity,
},
mode: ['properties', 'edit', 'create'],
'options': [
{label: gettext('Allow'), value: 'allow'},
{label: gettext('Prefer'), value: 'prefer'},
{label: gettext('Require'), value: 'require'},
{label: gettext('Disable'), value: 'disable'},
{label: gettext('Verify-CA'), value: 'verify-ca'},
{label: gettext('Verify-Full'), value: 'verify-full'},
],
},{
id: 'sslcert', label: gettext('Client certificate'), type: 'text',
group: gettext('SSL'), mode: ['edit', 'create'],
disabled: 'isSSL', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
deps: ['sslmode'],
},{
id: 'sslkey', label: gettext('Client certificate key'), type: 'text',
group: gettext('SSL'), mode: ['edit', 'create'],
disabled: 'isSSL', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
deps: ['sslmode'],
},{
id: 'sslrootcert', label: gettext('Root certificate'), type: 'text',
group: gettext('SSL'), mode: ['edit', 'create'],
disabled: 'isSSL', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
deps: ['sslmode'],
},{
id: 'sslcrl', label: gettext('Certificate revocation list'), type: 'text',
group: gettext('SSL'), mode: ['edit', 'create'],
disabled: 'isSSL', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
deps: ['sslmode'],
},{
id: 'sslcompression', label: gettext('SSL compression?'), type: 'switch',
mode: ['edit', 'create'], group: gettext('SSL'),
'options': {'size': 'mini'},
deps: ['sslmode'], disabled: 'isSSL',
},{
id: 'sslcert', label: gettext('Client certificate'), type: 'text',
group: gettext('SSL'), mode: ['properties'],
deps: ['sslmode'],
visible: function(model) {
var sslcert = model.get('sslcert');
return !_.isUndefined(sslcert) && !_.isNull(sslcert);
},
},{
id: 'sslkey', label: gettext('Client certificate key'), type: 'text',
group: gettext('SSL'), mode: ['properties'],
deps: ['sslmode'],
visible: function(model) {
var sslkey = model.get('sslkey');
return !_.isUndefined(sslkey) && !_.isNull(sslkey);
},
},{
id: 'sslrootcert', label: gettext('Root certificate'), type: 'text',
group: gettext('SSL'), mode: ['properties'],
deps: ['sslmode'],
visible: function(model) {
var sslrootcert = model.get('sslrootcert');
return !_.isUndefined(sslrootcert) && !_.isNull(sslrootcert);
},
},{
id: 'sslcrl', label: gettext('Certificate revocation list'), type: 'text',
group: gettext('SSL'), mode: ['properties'],
deps: ['sslmode'],
visible: function(model) {
var sslcrl = model.get('sslcrl');
return !_.isUndefined(sslcrl) && !_.isNull(sslcrl);
},
},{
id: 'sslcompression', label: gettext('SSL compression?'), type: 'switch',
mode: ['properties'], group: gettext('SSL'),
'options': {'size': 'mini'},
deps: ['sslmode'], visible: function(model) {
var sslmode = model.get('sslmode');
return _.indexOf(SSL_MODES, sslmode) != -1;
},
},
{
id: 'copy_data_after_refresh', label: gettext('Copy data?'),
type: 'switch', mode: ['edit'],
@ -298,8 +395,8 @@ define('pgadmin.node.subscription', [
id: 'create_slot', label: gettext('Create slot?'),
type: 'switch', mode: ['create'],
group: gettext('With'),
disabled: 'isDisable',
readonly: 'isConnect', deps :['connect'],
disabled: 'isSameDB',
readonly: 'isConnect', deps :['connect', 'host'],
helpMessage: gettext('Specifies whether the command should create the replication slot on the publisher.'),
},
@ -358,13 +455,21 @@ define('pgadmin.node.subscription', [
return false;
return true;
},
isSameDB:function(m){
if (m.attributes['host'] == m.node_info.server.host){
setTimeout( function() {
m.set('create_slot', false);
}, 10);
return true;
}
return false;
},
isAllConnectionDataEnter: function(m){
let host = m.get('host'),
db = m.get('db'),
port = m.get('port'),
username = m.get('username'),
password = m.get('password');
if ((!_.isUndefined(host) && host) && (!_.isUndefined(db) && db) && (!_.isUndefined(port) && port) && (!_.isUndefined(username) && username) && (!_.isUndefined(password) && password))
username = m.get('username');
if ((!_.isUndefined(host) && host) && (!_.isUndefined(db) && db) && (!_.isUndefined(port) && port) && (!_.isUndefined(username) && username))
return false;
return true;
},
@ -388,8 +493,12 @@ define('pgadmin.node.subscription', [
}
return false;
},
isSSL: function(model) {
var ssl_mode = model.get('sslmode');
return _.indexOf(SSL_MODES, ssl_mode) == -1;
},
sessChanged: function() {
if (_.isEqual(_.omit(this.attributes, ['refresh_pub']), _.omit(this.origSessAttrs, ['refresh_pub'])) && !this.isNew())
if (!this.isNew() && _.isUndefined(this.attributes['refresh_pub']))
return false;
return pgBrowser.DataModel.prototype.sessChanged.apply(this);
},
@ -400,7 +509,8 @@ define('pgadmin.node.subscription', [
validate: function() {
var msg;
this.errorModel.clear();
var name = this.get('name');
var name = this.get('name'),
slot_name = this.get('slot_name');
if (_.isUndefined(name) || _.isNull(name) ||
String(name).replace(/^\s+|\s+$/g, '') == '') {
@ -409,6 +519,14 @@ define('pgadmin.node.subscription', [
return msg;
}
if (!_.isUndefined(slot_name) && !_.isNull(slot_name)){
if(/^[a-zA-Z0-9_]+$/.test(slot_name) == false){
msg = gettext('Replication slot name may only contain lower case letters, numbers, and the underscore character.');
this.errorModel.set('name', msg);
return msg;
}
}
const validateModel = new modelValidation.ModelValidation(this);
return validateModel.validate();
},

View File

@ -12,8 +12,8 @@
{% endif %}
CREATE SUBSCRIPTION {{ conn|qtIdent(data.name) }}
{% if data.host or data.port or data.username or data.password or data.db %}
CONNECTION '{% if data.host %}host={{data.host}}{% endif %}{% if data.port %} port={{ data.port }}{% endif %}{% if data.username %} user={{ data.username }}{% endif %}{% if data.db %} dbname={{ data.db }}{% endif %}{% if data.connect_timeout %} connect_timeout={{ data.connect_timeout }}{% endif %}{% if data.passfile %} passfile={{ data.passfile }}{% endif %}{% if data.password %} {% if dummy %}password=xxxxxx{% else %}password={{ data.password}}{% endif %}{% endif %}'
{% if data.host or data.port or data.username or data.password or data.db or data.connect_timeout or data.passfile or data.sslmode or data.sslcompression or data.sslcert or data.sslkey or data.sslrootcert or data.sslcrl%}
CONNECTION '{% if data.host %}host={{data.host}}{% endif %}{% if data.port %} port={{ data.port }}{% endif %}{% if data.username %} user={{ data.username }}{% endif %}{% if data.db %} dbname={{ data.db }}{% endif %}{% if data.connect_timeout %} connect_timeout={{ data.connect_timeout }}{% endif %}{% if data.passfile %} passfile={{ data.passfile }}{% endif %}{% if data.password %} {% if dummy %}password=xxxxxx{% else %}password={{ data.password}}{% endif %}{% endif %}{% if data.sslmode %} sslmode={{ data.sslmode }}{% endif %}{% if data.sslcompression %} sslcompression={{ data.sslcompression }}{% endif %}{% if data.sslcert %} sslcert={{ data.sslcert }}{% endif %}{% if data.sslkey %} sslkey={{ data.sslkey }}{% endif %}{% if data.sslrootcert %} sslrootcert={{ data.sslrootcert }}{% endif %}{% if data.sslcrl %} sslcrl={{ data.sslcrl }}{% endif %}'
{% endif %}
{% if data.pub %}
PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %}

View File

@ -1,18 +1,24 @@
SELECT sub.oid AS oid,
subname AS name,
subpublications AS pub,
sub.subsynccommit AS sync,
subpublications AS cur_pub,
pga.rolname AS subowner,
subslotname AS slot_name,
subenabled AS enabled,
SPLIT_PART(SPLIT_PART(subconninfo,' port',1), '=',2) AS host,
SPLIT_PART(SPLIT_PART(subconninfo,'port=',2), ' ',1) AS port,
SPLIT_PART(SPLIT_PART(subconninfo,'user=',2), ' ',1) AS username,
SPLIT_PART(SPLIT_PART(subconninfo,'dbname=',2), ' ',1) AS db,
SPLIT_PART(SPLIT_PART(subconninfo,'connect_timeout=',2), ' ',1) AS connect_timeout,
SPLIT_PART(SPLIT_PART(subconninfo,'passfile=',2), ' ',1) AS passfile
FROM pg_subscription sub JOIN pg_authid pga ON sub.subowner= pga.oid
SELECT sub.oid as oid,
subname as name,
subpublications as pub,
sub.subsynccommit as sync,
subpublications as cur_pub,
pga.rolname as subowner,
subslotname as slot_name,
subenabled as enabled,
SPLIT_PART(SPLIT_PART(subconninfo,' port',1), '=',2) as host,
SPLIT_PART(SPLIT_PART(subconninfo,'port=',2), ' ',1) as port,
SPLIT_PART(SPLIT_PART(subconninfo,'user=',2), ' ',1) as username,
SPLIT_PART(SPLIT_PART(subconninfo,'dbname=',2), ' ',1) as db,
SPLIT_PART(SPLIT_PART(subconninfo,'connect_timeout=',2), ' ',1) as connect_timeout,
SPLIT_PART(SPLIT_PART(subconninfo,'passfile=',2), ' ',1) as passfile,
SPLIT_PART(SPLIT_PART(subconninfo,'sslmode=',2), ' ',1) as sslmode,
SPLIT_PART(SPLIT_PART(subconninfo,'sslcompression=',2), ' ',1) as sslcompression,
SPLIT_PART(SPLIT_PART(subconninfo,'sslcert=',2), ' ',1) as sslcert,
SPLIT_PART(SPLIT_PART(subconninfo,'sslkey=',2), ' ',1) as sslkey,
SPLIT_PART(SPLIT_PART(subconninfo,'sslrootcert=',2), ' ',1) as sslrootcert,
SPLIT_PART(SPLIT_PART(subconninfo,'sslcrl=',2), ' ',1) as sslcrl
FROM pg_subscription sub join pg_roles pga on sub.subowner= pga.oid
WHERE
{% if subid %}
sub.oid = {{ subid }};

View File

@ -47,9 +47,9 @@ ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
{% endif %}
{### Alter subscription connection info ###}
{% if data.host or data.port or data.username or data.password or data.db or data.connect_timeout or data.passfile %}
{% if data.host or data.port or data.username or data.password or data.db or data.connect_timeout or data.passfile or data.sslmode or data.sslcompression or data.sslcert or data.sslkey or data.sslrootcert or data.sslcrl %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
CONNECTION 'host={{ o_data.host}} port={{ o_data.port }} user={{ o_data.username }} dbname={{ o_data.db }}{% if data.connect_timeout %} connect_timeout={{ o_data.connect_timeout }}{% endif %}{% if data.passfile %} passfile={{ o_data.passfile }}{% endif %}{% if data.password %} {% if dummy %}password=xxxxxx{% else %} password={{ data.password}}{% endif %}{% endif %}';
CONNECTION 'host={{ o_data.host}} port={{ o_data.port }} user={{ o_data.username }} dbname={{ o_data.db }}{% if data.connect_timeout %} connect_timeout={{ o_data.connect_timeout }}{% endif %}{% if data.passfile %} passfile={{ o_data.passfile }}{% endif %}{% if data.password %} {% if dummy %}password=xxxxxx{% else %} password={{ data.password}}{% endif %}{% endif %}{% if data.sslmode %} sslmode={{ data.sslmode }}{% endif %}{% if data.sslcompression %} sslcompression={{ data.sslcompression }}{% endif %}{% if data.sslcert %} sslcert={{ data.sslcert }}{% endif %}{% if data.sslkey %} sslkey={{ data.sslkey }}{% endif %}{% if data.sslrootcert %} sslrootcert={{ data.sslrootcert }}{% endif %}{% if data.sslcrl %} sslcrl={{ data.sslcrl }}{% endif %}';
{% endif %}
{### Alter subscription name ###}
{% if data.name and data.name != o_data.name %}

View File

@ -40,15 +40,11 @@ export class ModelValidation {
this.checkForEmpty('username', gettext('Username must be specified.'));
this.checkForEmpty('port', gettext('Port must be specified.'));
if(!_.isUndefined(pub)){
if (this.model.isNew())
this.checkForEmpty('password', gettext('Password must be specified.'));
this.checkForEmpty('pub', gettext('Publication must be specified.'));
}
} else {
this.checkForEmpty('db', gettext('Maintenance database must be specified.'));
if(!_.isUndefined(pub)){
if (this.model.isNew())
this.checkForEmpty('password', gettext('Password must be specified.'));
this.checkForEmpty('pub', gettext('Publication must be specified.'));
}
this.clearHostAddressAndDbErrors();

View File

@ -84,7 +84,8 @@ def get_version_mapping_directories(server_type):
{'name': "default", 'number': 0}
)
return ({'name': "12_plus", 'number': 120000},
return ({'name': "13_plus", 'number': 130000},
{'name': "12_plus", 'number': 120000},
{'name': "11_plus", 'number': 110000},
{'name': "10_plus", 'number': 100000},
{'name': "9.6_plus", 'number': 90600},