Skip to content

Commit c75bba0

Browse files
authored
1) Added 'failover' parameter support in CREATE and ALTER SUBSCRIPTION for PostgreSQL v17+. pgadmin-org#8932
2) Added 'two_phase' parameter support for ALTER SUBSCRIPTION for PostgreSQL v18+. 3) Updated versioned_template_loader.py to prioritize v18+ templates. 4) Updated the default value of the streaming parameter in CREATE SUBSCRIPTION to 'parallel' in PG v18 (previously false).
1 parent 93bbadb commit c75bba0

27 files changed

Lines changed: 609 additions & 11 deletions

.github/workflows/run-python-tests-epas.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,13 @@ jobs:
105105
run: |
106106
# Note: we use a custom port for PostgreSQL as the runner may already have a version of PostgreSQL installed
107107
sudo su -c "echo local all all trust > /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
108+
sudo su -c "echo host all all 127.0.0.1/32 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
109+
sudo su -c "echo host all all ::1/128 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
110+
sudo su -c "echo host replication postgres 127.0.0.1/32 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
111+
sudo su -c "echo host replication postgres ::1/128 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
108112
sudo sed -i "s/port = 544[0-9]/port = 58${{ matrix.pgver }}/g" /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
109113
sudo sed -i "s/shared_preload_libraries = '/shared_preload_libraries = '\$libdir\/plugin_debugger,/g" /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
114+
echo "wal_level = logical" | sudo tee -a /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
110115
sudo su - enterprisedb -c "mkdir -p /var/run/edb-as/${{ matrix.pgver }}-main.epas_stat_tmp"
111116
sudo systemctl restart edb-as@${{ matrix.pgver }}-main
112117
@@ -115,6 +120,16 @@ jobs:
115120
sleep 2
116121
done
117122
123+
- name: Start PostgreSQL on Windows
124+
if: ${{ matrix.os == 'windows-latest' }}
125+
run: |
126+
echo host replication postgres 127.0.0.1/32 trust >> "C:\EPAS\${{ matrix.pgver }}\data\pg_hba.conf"
127+
echo host replication postgres ::1/128 trust >> "C:\EPAS\${{ matrix.pgver }}\data\pg_hba.conf"
128+
echo wal_level = logical >> "C:\EPAS\${{ matrix.pgver }}\data\postgresql.conf"
129+
net stop epas-${{ matrix.pgver }}
130+
net start epas-${{ matrix.pgver }}
131+
shell: cmd
132+
118133
- name: Create pgagent extension on Linux
119134
if: ${{ matrix.os == 'ubuntu-22.04' && matrix.pgver <= 16 }}
120135
run: psql -U enterprisedb -d postgres -p 58${{ matrix.pgver }} -c 'CREATE EXTENSION IF NOT EXISTS pgagent;'

.github/workflows/run-python-tests-pg.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,13 @@ jobs:
116116
if: ${{ matrix.os == 'ubuntu-22.04' }}
117117
run: |
118118
sudo su -c "echo local all all trust > /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
119+
sudo su -c "echo host all all 127.0.0.1/32 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
120+
sudo su -c "echo host all all ::1/128 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
121+
sudo su -c "echo host replication postgres 127.0.0.1/32 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
122+
sudo su -c "echo host replication postgres ::1/128 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
119123
sudo sed -i "s/port = 543[0-9]/port = 59${{ matrix.pgver }}/g" /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
120124
sudo sed -i "s/#shared_preload_libraries = ''/shared_preload_libraries = '\$libdir\/plugin_debugger'/g" /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
125+
echo "wal_level = logical" | sudo tee -a /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
121126
sudo su - postgres -c "/usr/lib/postgresql/${{ matrix.pgver }}/bin/postgres -D /var/lib/postgresql/${{ matrix.pgver }}/main -c config_file=/etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf &"
122127
123128
until sudo runuser -l postgres -c "pg_isready -p 59${{ matrix.pgver }}" 2>/dev/null; do
@@ -133,7 +138,12 @@ jobs:
133138
if: ${{ matrix.os == 'macos-latest' }}
134139
run: |
135140
echo local all all trust > /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
141+
echo 'host all all 127.0.0.1/32 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
142+
echo 'host all all ::1/128 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
143+
echo 'host replication postgres 127.0.0.1/32 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
144+
echo 'host replication postgres ::1/128 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
136145
sed -i '' "s/#port = 543[0-9]/port = 59${{ matrix.pgver }}/g" /opt/homebrew/var/postgresql@${{ matrix.pgver }}/postgresql.conf
146+
echo "wal_level = logical" >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/postgresql.conf
137147
brew services restart postgresql@${{ matrix.pgver }}
138148
139149
until /opt/homebrew/opt/postgresql@${{ matrix.pgver }}/bin/pg_isready -p 59${{ matrix.pgver }} 2>/dev/null; do
@@ -143,6 +153,16 @@ jobs:
143153
144154
psql postgres -p 59${{ matrix.pgver }} -c 'CREATE ROLE postgres SUPERUSER LOGIN;'
145155
156+
- name: Start PostgreSQL on Windows
157+
if: ${{ matrix.os == 'windows-latest' }}
158+
run: |
159+
echo host replication postgres 127.0.0.1/32 trust >> "C:\PostgreSQL\${{ matrix.pgver }}\data\pg_hba.conf"
160+
echo host replication postgres ::1/128 trust >> "C:\PostgreSQL\${{ matrix.pgver }}\data\pg_hba.conf"
161+
echo wal_level = logical >> "C:\PostgreSQL\${{ matrix.pgver }}\data\postgresql.conf"
162+
net stop postgresql-x64-${{ matrix.pgver }}
163+
net start postgresql-x64-${{ matrix.pgver }}
164+
shell: cmd
165+
146166
- name: Install Python dependencies on Linux and macOS
147167
if: ${{ matrix.os == 'macos-latest' || matrix.os == 'ubuntu-22.04' }}
148168
run: make install-python-testing
29.5 KB
Loading

docs/en_US/subscription_dialog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ Use the *With* tab to define some parameters for a subscription:
106106
* Move the *Run as owner?* switch to *true* position to specify all replication actions are performed as the subscription owner. If *false*, replication workers will perform actions on each table as the owner of that table. The default is *false*. This option is available only on PostgreSQL 16 and above.
107107
* Use the *Password required?* to specify whether connections to the publisher made as a result of this subscription must use password authentication. This setting is ignored when the subscription is owned by a superuser. The default is true. Only superusers can set this value to *false*. This option is available only on PostgreSQL 16 and above.
108108
* Use the *Origin* to specify whether the subscription will request the publisher to only send changes that don't have an origin or send changes regardless of origin. The default is *any*. This option is available only on PostgreSQL 16 and above.
109+
* Use the *Failover* to specify whether the replication slots associated with the subscription are enabled to be synced to the standbys so that logical replication can be resumed from the new primary after failover. The default is false. This option is available only on PostgreSQL 17 and above.
109110

110111
Click the *SQL* tab to continue.
111112

web/pgadmin/browser/server_groups/servers/databases/subscriptions/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,14 @@ def get_sql(self, data, subid=None, operation=None):
652652
if len(res['rows']) == 0:
653653
return gone(self._NOT_FOUND_PUB_INFORMATION)
654654

655+
if self.manager.version >= 150000:
656+
res['rows'][0]['two_phase'] = \
657+
self.two_phase_mapping[res['rows'][0]['two_phase']]
658+
659+
if self.manager.version >= 160000:
660+
res['rows'][0]['streaming'] = \
661+
self.streaming_mapping[res['rows'][0]['streaming']]
662+
655663
old_data = res['rows'][0]
656664

657665
data, old_data = self.get_required_details(data, old_data)

web/pgadmin/browser/server_groups/servers/databases/subscriptions/static/js/subscription.ui.js

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ export default class SubscriptionSchema extends BaseUISchema{
2626
binary:false,
2727
two_phase:false,
2828
disable_on_error:false,
29-
streaming:false,
29+
streaming: (node_info?.node_info?.version >= 180000) ? 'parallel' : false,
3030
password_required:true,
3131
run_as_owner:false,
3232
origin:'any',
33+
failover:false,
3334
copy_data_after_refresh:false,
3435
sync:'off',
3536
refresh_pub: false,
@@ -72,13 +73,7 @@ export default class SubscriptionSchema extends BaseUISchema{
7273
(this.node_info['node_info'].host == 'localhost' || this.node_info['node_info'].host == '127.0.0.1')){
7374
host = this.node_info['node_info'].host;
7475
}
75-
if (host == this.node_info['node_info'].host && port == this.node_info['node_info'].port){
76-
state.create_slot = false;
77-
return true;
78-
} else {
79-
state.create_slot = true;
80-
}
81-
return false;
76+
return (host == this.node_info['node_info'].host && port == this.node_info['node_info'].port);
8277
}
8378
isAllConnectionDataEnter(state){
8479
let host = state.host,
@@ -317,6 +312,14 @@ export default class SubscriptionSchema extends BaseUISchema{
317312
readonly: obj.isConnect, deps :['connect', 'host', 'port'],
318313
helpMessage: gettext('Specifies whether the command should create the replication slot on the publisher.This field will be disabled and set to false if subscription connects to same database.Otherwise, the CREATE SUBSCRIPTION call will hang.'),
319314
helpMessageMode: ['edit', 'create'],
315+
depChange: (state) => {
316+
// Set create_slot to false if same DB, else true
317+
if(obj.isSameDB(state)) {
318+
state.create_slot = false;
319+
} else {
320+
state.create_slot = true;
321+
}
322+
},
320323
},
321324
{
322325
id: 'enabled', label: gettext('Enabled?'),
@@ -419,9 +422,18 @@ export default class SubscriptionSchema extends BaseUISchema{
419422
helpMessageMode: ['edit', 'create'],
420423
},
421424
{
422-
id: 'two_phase', label: gettext('Two phase?'),
423-
type: 'switch', mode: ['create', 'properties'],
425+
id: 'two_phase',
426+
label: gettext('Two phase?'),
427+
type: 'switch',
424428
group: gettext('With'),
429+
mode: (() => {
430+
if (obj.version >= 180000) {
431+
return ['create', 'edit', 'properties'];
432+
} else if (obj.version >= 150000 && obj.version < 180000) {
433+
return ['create', 'properties'];
434+
}
435+
return [];
436+
})(),
425437
min_version: 150000,
426438
helpMessage: gettext('Specifies whether two-phase commit is enabled for this subscription.'),
427439
helpMessageMode: ['edit', 'create'],
@@ -465,6 +477,14 @@ export default class SubscriptionSchema extends BaseUISchema{
465477
helpMessage: gettext('Specifies whether the subscription will request the publisher to only send changes that do not have an origin or send changes regardless of origin. Setting origin to none means that the subscription will request the publisher to only send changes that do not have an origin. Setting origin to any means that the publisher sends changes regardless of their origin.'),
466478
helpMessageMode: ['edit', 'create'],
467479
},
480+
{
481+
id: 'failover', label: gettext('Failover'),
482+
type: 'switch', mode: ['create', 'edit', 'properties'],
483+
group: gettext('With'),
484+
min_version: 170000,
485+
helpMessage: gettext('Specifies whether the replication slots associated with the subscription are enabled to be synced to the standbys so that logical replication can be resumed from the new primary after failover'),
486+
helpMessageMode: ['edit', 'create'],
487+
},
468488
];
469489
}
470490

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{% if data.copy_data is defined or data.create_slot is defined or data.slot_name is defined or data.sync is defined %}
2+
{% set add_semicolon_after_enabled = 'enabled' %}
3+
{% endif %}
4+
{% if data.create_slot is defined or data.slot_name is defined %}
5+
{% set add_semicolon_after_copy_data = 'copy_data' %}
6+
{% endif %}
7+
{% if data.slot_name is defined or data.sync is defined %}
8+
{% set add_semicolon_after_create_slot = 'create_slot' %}
9+
{% endif %}
10+
{% if data.sync is defined %}
11+
{% set add_semicolon_after_slot_name = 'slot_name' %}
12+
{% endif %}
13+
14+
CREATE SUBSCRIPTION {{ conn|qtIdent(data.name) }}
15+
{% 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%}
16+
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 %}'
17+
{% endif %}
18+
{% if data.pub %}
19+
PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %}
20+
{% endif %}
21+
22+
WITH ({% if data.connect is defined %}connect = {{ data.connect|lower}}, {% endif %}enabled = {{ data.enabled|lower}}, {% if data.copy_data is defined %}copy_data = {{ data.copy_data|lower}}{% if add_semicolon_after_copy_data == 'copy_data' %}, {% endif %}{% endif %}
23+
{% if data.create_slot is defined %}create_slot = {{ data.create_slot|lower }}{% if add_semicolon_after_create_slot == 'create_slot' %}, {% endif %}{% endif %}
24+
{% if data.slot_name is defined and data.slot_name != ''%}slot_name = {{ data.slot_name }}{% if add_semicolon_after_slot_name == 'slot_name' %}, {% endif %}{% endif %}{% if data.sync %}synchronous_commit = '{{ data.sync }}', {% endif %}binary = {{ data.binary|lower}}, streaming = '{{ data.streaming}}', two_phase = {{ data.two_phase|lower}}, disable_on_error = {{ data.disable_on_error|lower}}, run_as_owner = {{ data.run_as_owner|lower}}, password_required = {{ data.password_required|lower}}, origin = '{{ data.origin}}', failover = {{ data.failover|lower}});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
SELECT sub.oid as oid,
2+
subname as name,
3+
subpublications as pub,
4+
subpublications as proppub,
5+
sub.subsynccommit as sync,
6+
pga.rolname as subowner,
7+
subslotname as slot_name,
8+
subenabled as enabled,
9+
subbinary as binary,
10+
substream as streaming,
11+
subtwophasestate as two_phase,
12+
subdisableonerr as disable_on_error,
13+
subpasswordrequired as password_required,
14+
subrunasowner as run_as_owner,
15+
suborigin as origin,
16+
subfailover as failover,
17+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,' port',1), '=',2) as host,
18+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'port=',2), ' ',1) as port,
19+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'user=',2), ' ',1) as username,
20+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'dbname=',2), ' ',1) as db,
21+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'connect_timeout=',2), ' ',1) as connect_timeout,
22+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'passfile=',2), ' ',1) as passfile,
23+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslmode=',2), ' ',1) as sslmode,
24+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcompression=',2), ' ',1) as sslcompression,
25+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcert=',2), ' ',1) as sslcert,
26+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslkey=',2), ' ',1) as sslkey,
27+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslrootcert=',2), ' ',1) as sslrootcert,
28+
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcrl=',2), ' ',1) as sslcrl
29+
FROM pg_catalog.pg_subscription sub join pg_catalog.pg_roles pga on sub.subowner= pga.oid
30+
WHERE
31+
{% if subid %}
32+
sub.oid = {{ subid }};
33+
{% else %}
34+
sub.subdbid = {{ did }};
35+
{% endif %}

0 commit comments

Comments
 (0)