1) Added 'failover' parameter support in CREATE and ALTER SUBSCRIPTION for PostgreSQL v17+. #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).
This commit is contained in:
Anil Sahoo authored and GitHub committed 2025-09-29 12:52:47 +05:30
1 parent 93bbadb994
commit c75bba03c2
27 files changed
+609 -11

No files matched your search

@@ -105,8 +105,13 @@ jobs:
run: |
# Note: we use a custom port for PostgreSQL as the runner may already have a version of PostgreSQL installed
sudo su -c "echo local all all trust > /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host all all 127.0.0.1/32 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host all all ::1/128 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host replication postgres 127.0.0.1/32 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host replication postgres ::1/128 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo sed -i "s/port = 544[0-9]/port = 58${{ matrix.pgver }}/g" /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
sudo sed -i "s/shared_preload_libraries = '/shared_preload_libraries = '\$libdir\/plugin_debugger,/g" /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
echo "wal_level = logical" | sudo tee -a /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
sudo su - enterprisedb -c "mkdir -p /var/run/edb-as/${{ matrix.pgver }}-main.epas_stat_tmp"
sudo systemctl restart edb-as@${{ matrix.pgver }}-main
@@ -115,6 +120,16 @@ jobs:
sleep 2
done
- name: Start PostgreSQL on Windows
if: ${{ matrix.os == 'windows-latest' }}
run: |
echo host replication postgres 127.0.0.1/32 trust >> "C:\EPAS\${{ matrix.pgver }}\data\pg_hba.conf"
echo host replication postgres ::1/128 trust >> "C:\EPAS\${{ matrix.pgver }}\data\pg_hba.conf"
echo wal_level = logical >> "C:\EPAS\${{ matrix.pgver }}\data\postgresql.conf"
net stop epas-${{ matrix.pgver }}
net start epas-${{ matrix.pgver }}
shell: cmd
- name: Create pgagent extension on Linux
if: ${{ matrix.os == 'ubuntu-22.04' && matrix.pgver <= 16 }}
run: psql -U enterprisedb -d postgres -p 58${{ matrix.pgver }} -c 'CREATE EXTENSION IF NOT EXISTS pgagent;'
+20
View File
@@ -116,8 +116,13 @@ jobs:
if: ${{ matrix.os == 'ubuntu-22.04' }}
run: |
sudo su -c "echo local all all trust > /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host all all 127.0.0.1/32 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host all all ::1/128 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host replication postgres 127.0.0.1/32 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host replication postgres ::1/128 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo sed -i "s/port = 543[0-9]/port = 59${{ matrix.pgver }}/g" /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
sudo sed -i "s/#shared_preload_libraries = ''/shared_preload_libraries = '\$libdir\/plugin_debugger'/g" /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
echo "wal_level = logical" | sudo tee -a /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
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 &"
until sudo runuser -l postgres -c "pg_isready -p 59${{ matrix.pgver }}" 2>/dev/null; do
@@ -133,7 +138,12 @@ jobs:
if: ${{ matrix.os == 'macos-latest' }}
run: |
echo local all all trust > /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
echo 'host all all 127.0.0.1/32 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
echo 'host all all ::1/128 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
echo 'host replication postgres 127.0.0.1/32 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
echo 'host replication postgres ::1/128 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
sed -i '' "s/#port = 543[0-9]/port = 59${{ matrix.pgver }}/g" /opt/homebrew/var/postgresql@${{ matrix.pgver }}/postgresql.conf
echo "wal_level = logical" >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/postgresql.conf
brew services restart postgresql@${{ matrix.pgver }}
until /opt/homebrew/opt/postgresql@${{ matrix.pgver }}/bin/pg_isready -p 59${{ matrix.pgver }} 2>/dev/null; do
@@ -143,6 +153,16 @@ jobs:
psql postgres -p 59${{ matrix.pgver }} -c 'CREATE ROLE postgres SUPERUSER LOGIN;'
- name: Start PostgreSQL on Windows
if: ${{ matrix.os == 'windows-latest' }}
run: |
echo host replication postgres 127.0.0.1/32 trust >> "C:\PostgreSQL\${{ matrix.pgver }}\data\pg_hba.conf"
echo host replication postgres ::1/128 trust >> "C:\PostgreSQL\${{ matrix.pgver }}\data\pg_hba.conf"
echo wal_level = logical >> "C:\PostgreSQL\${{ matrix.pgver }}\data\postgresql.conf"
net stop postgresql-x64-${{ matrix.pgver }}
net start postgresql-x64-${{ matrix.pgver }}
shell: cmd
- name: Install Python dependencies on Linux and macOS
if: ${{ matrix.os == 'macos-latest' || matrix.os == 'ubuntu-22.04' }}
run: make install-python-testing
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 64 KiB

+1
View File
@@ -106,6 +106,7 @@ Use the *With* tab to define some parameters for a subscription:
* 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.
* 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.
* 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.
* 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.
Click the *SQL* tab to continue.
@@ -652,6 +652,14 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
if len(res['rows']) == 0:
return gone(self._NOT_FOUND_PUB_INFORMATION)
if self.manager.version >= 150000:
res['rows'][0]['two_phase'] = \
self.two_phase_mapping[res['rows'][0]['two_phase']]
if self.manager.version >= 160000:
res['rows'][0]['streaming'] = \
self.streaming_mapping[res['rows'][0]['streaming']]
old_data = res['rows'][0]
data, old_data = self.get_required_details(data, old_data)
@@ -26,10 +26,11 @@ export default class SubscriptionSchema extends BaseUISchema{
binary:false,
two_phase:false,
disable_on_error:false,
streaming:false,
streaming: (node_info?.node_info?.version >= 180000) ? 'parallel' : false,
password_required:true,
run_as_owner:false,
origin:'any',
failover:false,
copy_data_after_refresh:false,
sync:'off',
refresh_pub: false,
@@ -72,13 +73,7 @@ export default class SubscriptionSchema extends BaseUISchema{
(this.node_info['node_info'].host == 'localhost' || this.node_info['node_info'].host == '127.0.0.1')){
host = this.node_info['node_info'].host;
}
if (host == this.node_info['node_info'].host && port == this.node_info['node_info'].port){
state.create_slot = false;
return true;
} else {
state.create_slot = true;
}
return false;
return (host == this.node_info['node_info'].host && port == this.node_info['node_info'].port);
}
isAllConnectionDataEnter(state){
let host = state.host,
@@ -317,6 +312,14 @@ export default class SubscriptionSchema extends BaseUISchema{
readonly: obj.isConnect, deps :['connect', 'host', 'port'],
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.'),
helpMessageMode: ['edit', 'create'],
depChange: (state) => {
// Set create_slot to false if same DB, else true
if(obj.isSameDB(state)) {
state.create_slot = false;
} else {
state.create_slot = true;
}
},
},
{
id: 'enabled', label: gettext('Enabled?'),
@@ -419,9 +422,18 @@ export default class SubscriptionSchema extends BaseUISchema{
helpMessageMode: ['edit', 'create'],
},
{
id: 'two_phase', label: gettext('Two phase?'),
type: 'switch', mode: ['create', 'properties'],
id: 'two_phase',
label: gettext('Two phase?'),
type: 'switch',
group: gettext('With'),
mode: (() => {
if (obj.version >= 180000) {
return ['create', 'edit', 'properties'];
} else if (obj.version >= 150000 && obj.version < 180000) {
return ['create', 'properties'];
}
return [];
})(),
min_version: 150000,
helpMessage: gettext('Specifies whether two-phase commit is enabled for this subscription.'),
helpMessageMode: ['edit', 'create'],
@@ -465,6 +477,14 @@ export default class SubscriptionSchema extends BaseUISchema{
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.'),
helpMessageMode: ['edit', 'create'],
},
{
id: 'failover', label: gettext('Failover'),
type: 'switch', mode: ['create', 'edit', 'properties'],
group: gettext('With'),
min_version: 170000,
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'),
helpMessageMode: ['edit', 'create'],
},
];
}
@@ -0,0 +1,24 @@
{% if data.copy_data is defined or data.create_slot is defined or data.slot_name is defined or data.sync is defined %}
{% set add_semicolon_after_enabled = 'enabled' %}
{% endif %}
{% if data.create_slot is defined or data.slot_name is defined %}
{% set add_semicolon_after_copy_data = 'copy_data' %}
{% endif %}
{% if data.slot_name is defined or data.sync is defined %}
{% set add_semicolon_after_create_slot = 'create_slot' %}
{% endif %}
{% if data.sync is defined %}
{% set add_semicolon_after_slot_name = 'slot_name' %}
{% endif %}
CREATE SUBSCRIPTION {{ conn|qtIdent(data.name) }}
{% 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 %}
{% endif %}
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 %}
{% if data.create_slot is defined %}create_slot = {{ data.create_slot|lower }}{% if add_semicolon_after_create_slot == 'create_slot' %}, {% endif %}{% endif %}
{% 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}});
@@ -0,0 +1,35 @@
SELECT sub.oid as oid,
subname as name,
subpublications as pub,
subpublications as proppub,
sub.subsynccommit as sync,
pga.rolname as subowner,
subslotname as slot_name,
subenabled as enabled,
subbinary as binary,
substream as streaming,
subtwophasestate as two_phase,
subdisableonerr as disable_on_error,
subpasswordrequired as password_required,
subrunasowner as run_as_owner,
suborigin as origin,
subfailover as failover,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,' port',1), '=',2) as host,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'port=',2), ' ',1) as port,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'user=',2), ' ',1) as username,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'dbname=',2), ' ',1) as db,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'connect_timeout=',2), ' ',1) as connect_timeout,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'passfile=',2), ' ',1) as passfile,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslmode=',2), ' ',1) as sslmode,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcompression=',2), ' ',1) as sslcompression,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcert=',2), ' ',1) as sslcert,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslkey=',2), ' ',1) as sslkey,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslrootcert=',2), ' ',1) as sslrootcert,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcrl=',2), ' ',1) as sslcrl
FROM pg_catalog.pg_subscription sub join pg_catalog.pg_roles pga on sub.subowner= pga.oid
WHERE
{% if subid %}
sub.oid = {{ subid }};
{% else %}
sub.subdbid = {{ did }};
{% endif %}
@@ -0,0 +1,80 @@
{% if data.sync is defined or data.failover is defined %}
{% set add_comma_after_slot_name = 'slot_name' %}
{% endif %}
{% if data.binary is defined or data.streaming is defined or data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_sync = 'sync' %}
{% endif %}
{% if data.streaming is defined or data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_binary = 'binary' %}
{% endif %}
{% if data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_streaming = 'streaming' %}
{% endif %}
{% if data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_disable_on_error = 'disable_on_error' %}
{% endif %}
{% if data.password_required is defined or data.origin is defined %}
{% set add_comma_after_run_as_owner = 'run_as_owner' %}
{% endif %}
{% if data.origin is defined %}
{% set add_comma_after_password_required = 'password_required' %}
{% endif %}
{% if data.failover is defined %}
{% set add_comma_after_origin = 'origin' %}
{% endif %}
{#####################################################}
{## Change owner of subscription ##}
{#####################################################}
{% if data.subowner and data.subowner != o_data.subowner %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
OWNER TO {{ data.subowner }};
{% endif %}
{### Disable subscription ###}
{% if data.enabled is defined and data.enabled != o_data.enabled %}
{% if not data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }} DISABLE;
{% endif %}
{% endif %}
{### Alter parameters of subscription ###}
{% if (data.slot_name is defined and data.slot_name != o_data.slot_name) or (data.sync is defined and data.sync != o_data.sync) or (data.binary is defined and data.binary!=o_data.binary) or (data.streaming is defined and data.streaming!=o_data.streaming) or (data.disable_on_error is defined and data.disable_on_error!=o_data.disable_on_error) or (data.run_as_owner is defined and data.run_as_owner!=o_data.run_as_owner) or (data.password_required is defined and data.password_required!=o_data.password_required) or (data.origin is defined and data.origin!=o_data.origin) or (data.failover is defined and data.failover!=o_data.failover) %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET ({% if data.slot_name is defined and data.slot_name != o_data.slot_name %}slot_name = {{ data.slot_name }}{% if add_comma_after_slot_name == 'slot_name' %}, {% endif %}{% endif %}{% if data.sync is defined and data.sync != o_data.sync %}synchronous_commit = '{{ data.sync }}'{% if add_comma_after_sync == 'sync' %}, {% endif %}{% endif %}{% if data.binary is defined and data.binary!=o_data.binary %}binary = {{ data.binary|lower}}{% if add_comma_after_binary == 'binary' %}, {% endif %}{% endif %}{% if data.streaming is defined and data.streaming!=o_data.streaming %}streaming = '{{ data.streaming }}'{% if add_comma_after_streaming == 'streaming' %}, {% endif %}{% endif %}{% if data.disable_on_error is defined and data.disable_on_error!=o_data.disable_on_error %}disable_on_error = {{ data.disable_on_error|lower}}{% if add_comma_after_disable_on_error == 'disable_on_error' %}, {% endif %}{% endif %}{% if data.run_as_owner is defined and data.run_as_owner!=o_data.run_as_owner %}run_as_owner = {{ data.run_as_owner|lower}}{% if add_comma_after_run_as_owner == 'run_as_owner' %}, {% endif %}{% endif %}{% if data.password_required is defined and data.password_required!=o_data.password_required %}password_required = {{ data.password_required|lower}}{% if add_comma_after_password_required == 'password_required' %}, {% endif %}{% endif %}{% if data.origin is defined and data.origin!=o_data.origin %}origin = '{{ data.origin }}'{% if add_comma_after_origin == 'origin' %}, {% endif %}{% endif %}{% if data.failover is defined and data.failover!=o_data.failover %}failover = {{ data.failover|lower}}{% endif %});
{% endif %}
{### Enable subscription ###}
{% if data.enabled is defined and data.enabled != o_data.enabled %}
{% if data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }} ENABLE;
{% endif %}
{% endif %}
{### Refresh publication ###}
{% if data.refresh_pub %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
REFRESH PUBLICATION{% if not data.copy_data_after_refresh %} WITH (copy_data = false){% else %} WITH (copy_data = true){% endif %};
{% endif %}
{### Alter publication of subscription ###}
{% if data.pub%}
{% if data.pub and not data.refresh_pub and not data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %} WITH (refresh = false);
{% else %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %};
{% endif %}
{% endif %}
{### Alter subscription connection info ###}
{% if data.host or data.port or data.username 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 }} connect_timeout={{ o_data.connect_timeout }} {% if data.passfile %} passfile={{ o_data.passfile }}{% endif %} sslmode={{ o_data.sslmode }}{% 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 %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
@@ -0,0 +1,83 @@
{% if data.sync is defined or data.failover is defined %}
{% set add_comma_after_slot_name = 'slot_name' %}
{% endif %}
{% if data.binary is defined or data.streaming is defined or data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_sync = 'sync' %}
{% endif %}
{% if data.streaming is defined or data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_binary = 'binary' %}
{% endif %}
{% if data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_streaming = 'streaming' %}
{% endif %}
{% if data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_disable_on_error = 'disable_on_error' %}
{% endif %}
{% if data.password_required is defined or data.origin is defined %}
{% set add_comma_after_run_as_owner = 'run_as_owner' %}
{% endif %}
{% if data.origin is defined %}
{% set add_comma_after_password_required = 'password_required' %}
{% endif %}
{% if data.failover is defined %}
{% set add_comma_after_origin = 'origin' %}
{% endif %}
{% if data.two_phase is defined %}
{% set add_comma_after_failover = 'failover' %}
{% endif %}
{#####################################################}
{## Change owner of subscription ##}
{#####################################################}
{% if data.subowner and data.subowner != o_data.subowner %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
OWNER TO {{ data.subowner }};
{% endif %}
{### Disable subscription ###}
{% if data.enabled is defined and data.enabled != o_data.enabled %}
{% if not data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }} DISABLE;
{% endif %}
{% endif %}
{### Alter parameters of subscription ###}
{% if (data.slot_name is defined and data.slot_name != o_data.slot_name) or (data.sync is defined and data.sync != o_data.sync) or (data.binary is defined and data.binary!=o_data.binary) or (data.streaming is defined and data.streaming!=o_data.streaming) or (data.disable_on_error is defined and data.disable_on_error!=o_data.disable_on_error) or (data.run_as_owner is defined and data.run_as_owner!=o_data.run_as_owner) or (data.password_required is defined and data.password_required!=o_data.password_required) or (data.origin is defined and data.origin!=o_data.origin) or (data.failover is defined and data.failover!=o_data.failover) or (data.two_phase is defined and data.two_phase!=o_data.two_phase) %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET ({% if data.slot_name is defined and data.slot_name != o_data.slot_name %}slot_name = {{ data.slot_name }}{% if add_comma_after_slot_name == 'slot_name' %}, {% endif %}{% endif %}{% if data.sync is defined and data.sync != o_data.sync %}synchronous_commit = '{{ data.sync }}'{% if add_comma_after_sync == 'sync' %}, {% endif %}{% endif %}{% if data.binary is defined and data.binary!=o_data.binary %}binary = {{ data.binary|lower}}{% if add_comma_after_binary == 'binary' %}, {% endif %}{% endif %}{% if data.streaming is defined and data.streaming!=o_data.streaming %}streaming = '{{ data.streaming }}'{% if add_comma_after_streaming == 'streaming' %}, {% endif %}{% endif %}{% if data.disable_on_error is defined and data.disable_on_error!=o_data.disable_on_error %}disable_on_error = {{ data.disable_on_error|lower}}{% if add_comma_after_disable_on_error == 'disable_on_error' %}, {% endif %}{% endif %}{% if data.run_as_owner is defined and data.run_as_owner!=o_data.run_as_owner %}run_as_owner = {{ data.run_as_owner|lower}}{% if add_comma_after_run_as_owner == 'run_as_owner' %}, {% endif %}{% endif %}{% if data.password_required is defined and data.password_required!=o_data.password_required %}password_required = {{ data.password_required|lower}}{% if add_comma_after_password_required == 'password_required' %}, {% endif %}{% endif %}{% if data.origin is defined and data.origin!=o_data.origin %}origin = '{{ data.origin }}'{% if add_comma_after_origin == 'origin' %}, {% endif %}{% endif %}{% if data.failover is defined and data.failover!=o_data.failover %}failover = {{ data.failover|lower}}{% if add_comma_after_failover == 'failover' %}, {% endif %}{% endif %}{% if data.two_phase is defined and data.two_phase!=o_data.two_phase %}two_phase = {{ data.two_phase|lower}}{% endif %});
{% endif %}
{### Enable subscription ###}
{% if data.enabled is defined and data.enabled != o_data.enabled %}
{% if data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }} ENABLE;
{% endif %}
{% endif %}
{### Refresh publication ###}
{% if data.refresh_pub %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
REFRESH PUBLICATION{% if not data.copy_data_after_refresh %} WITH (copy_data = false){% else %} WITH (copy_data = true){% endif %};
{% endif %}
{### Alter publication of subscription ###}
{% if data.pub%}
{% if data.pub and not data.refresh_pub and not data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %} WITH (refresh = false);
{% else %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %};
{% endif %}
{% endif %}
{### Alter subscription connection info ###}
{% if data.host or data.port or data.username 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 }} connect_timeout={{ o_data.connect_timeout }} {% if data.passfile %} passfile={{ o_data.passfile }}{% endif %} sslmode={{ o_data.sslmode }}{% 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 %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
@@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5917 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = true);
@@ -0,0 +1,2 @@
ALTER SUBSCRIPTION test_create_subscription
SET (failover = true);
@@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5917 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = true);
@@ -0,0 +1,2 @@
ALTER SUBSCRIPTION test_create_subscription
SET (slot_name = None);
@@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5917 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);
@@ -0,0 +1,4 @@
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5917 user=postgres dbname=postgres connect_timeout=10 password=xxxxxx sslmode=prefer'
PUBLICATION test_publication
WITH (connect = true, enabled = false, copy_data = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);
@@ -0,0 +1,122 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Table For Subscription",
"endpoint": "NODE-table.obj",
"sql_endpoint": "NODE-table.sql_id",
"data": {
"name": "test_table",
"columns": [
{
"name": "emp_id",
"cltype": "integer",
"is_primary_key": true
},
{
"name": "name",
"cltype": "text"
},
{
"name": "salary",
"cltype": "bigint"
}
],
"is_partitioned": false,
"schema": "public",
"spcname": "pg_default"
},
"store_object_id": true
},
{
"type": "create",
"name": "Create Publication for Subscription",
"endpoint": "NODE-publication.obj",
"sql_endpoint": "NODE-publication.sql_id",
"msql_endpoint": "NODE-publication.msql",
"data": {
"name": "test_publication",
"evnt_insert": true,
"evnt_update": true,
"evnt_delete": true,
"evnt_truncate": true,
"pubowner": "postgres",
"publish_via_partition_root": false,
"all_table": false,
"only_table": false,
"pubtable": [{
"table_name": "public.test_table"
}],
"pubschema": ""
}
},
{
"type": "create",
"name": "Create Subscription",
"endpoint": "NODE-subscription.obj",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql",
"data": {
"name": "test_create_subscription",
"subowner": "postgres",
"host": "localhost",
"port": 5917,
"db": "postgres",
"username": "postgres",
"password": "postgres",
"connect_timeout": 10,
"pub": ["test_publication"],
"sslmode": "prefer",
"copy_data": false,
"create_slot": false,
"enabled": false,
"connect": true,
"slot_name": "test_create_subscription",
"sync": "remote_apply",
"streaming": true,
"binary": true,
"two_phase": true,
"disable_on_error": true,
"run_as_owner": true,
"password_required": true,
"origin": "any",
"service": "",
"failover": false
},
"expected_sql_file": "create_subscription.sql",
"expected_msql_file": "create_subscription_msql.sql"
},
{
"type": "alter",
"name": "Alter failover parameter of subscription",
"endpoint": "NODE-subscription.obj_id",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql_id",
"data": {
"failover": true
},
"expected_sql_file": "alter_failover_parameter.sql",
"expected_msql_file": "alter_failover_parameter_msql.sql"
},
{
"type": "alter",
"name": "Alter slot_name parameter of subscription",
"endpoint": "NODE-subscription.obj_id",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql_id",
"data": {
"slot_name": "None"
},
"expected_sql_file": "alter_slot_name_parameter.sql",
"expected_msql_file": "alter_slot_name_parameter_msql.sql"
},
{
"type": "delete",
"name": "Drop subscription",
"endpoint": "NODE-subscription.delete_id",
"data": {
"name": "test_create_subscription"
}
}
]
}
@@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5434 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);
@@ -0,0 +1,2 @@
ALTER SUBSCRIPTION test_create_subscription
SET (slot_name = None);
@@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5434 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);
@@ -0,0 +1,2 @@
ALTER SUBSCRIPTION test_create_subscription
SET (two_phase = true);
@@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5434 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = false, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);
@@ -0,0 +1,4 @@
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5434 user=postgres dbname=postgres connect_timeout=10 password=xxxxxx sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, copy_data = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = false, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);
@@ -0,0 +1,72 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Subscription",
"endpoint": "NODE-subscription.obj",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql",
"data": {
"name": "test_create_subscription",
"subowner": "postgres",
"host": "localhost",
"port": 5434,
"db": "postgres",
"username": "postgres",
"password": "edb",
"connect_timeout": 10,
"pub": ["test_publication"],
"sslmode": "prefer",
"copy_data": false,
"create_slot": false,
"enabled": false,
"connect": false,
"slot_name": "test_create_subscription",
"sync": "remote_apply",
"streaming": true,
"binary": true,
"two_phase": false,
"disable_on_error": true,
"run_as_owner": true,
"password_required": true,
"origin": "any",
"service": "",
"failover": false
},
"expected_sql_file": "create_subscription.sql",
"expected_msql_file": "create_subscription_msql.sql"
},
{
"type": "alter",
"name": "Alter two_phase parameter of subscription",
"endpoint": "NODE-subscription.obj_id",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql_id",
"data": {
"two_phase": true
},
"expected_sql_file": "alter_two_phase_parameter.sql",
"expected_msql_file": "alter_two_phase_parameter_msql.sql"
},
{
"type": "alter",
"name": "Alter slot_name parameter of subscription",
"endpoint": "NODE-subscription.obj_id",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql_id",
"data": {
"slot_name": "None"
},
"expected_sql_file": "alter_slot_name_parameter.sql",
"expected_msql_file": "alter_slot_name_parameter_msql.sql"
},
{
"type": "delete",
"name": "Drop subscription",
"endpoint": "NODE-subscription.delete_id",
"data": {
"name": "test_create_subscription"
}
}
]
}
@@ -25,6 +25,7 @@
"streaming":false,
"password_required":true,
"run_as_owner":false,
"failover": false,
"origin":"any",
"pub": ["sample__1"]
},
@@ -76,7 +76,8 @@ def get_version_mapping_directories():
:param server_type:
:return:
"""
return ({'name': "17_plus", 'number': 170000},
return ({'name': "18_plus", 'number': 180000},
{'name': "17_plus", 'number': 170000},
{'name': "16_plus", 'number': 160000},
{'name': "15_plus", 'number': 150000},
{'name': "14_plus", 'number': 140000},
+52
View File
@@ -95,6 +95,35 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
# Added line break after scenario name
print("")
# Create replication slot if it does not exist for the
# RESQL test-cases of Subscriptions for PGv17 and above
if self.server_information['server_version'] >= 170000:
try:
self.get_db_connection()
pg_cursor = self.connection.cursor()
pg_cursor.execute("""
SELECT 1 FROM pg_replication_slots
WHERE slot_name = 'test_create_subscription'
""")
exists = pg_cursor.fetchone()
if not exists:
pg_cursor.execute("""
SELECT pg_create_logical_replication_slot(
'test_create_subscription',
'pgoutput',
failover := false
);
""")
self.connection.commit()
print("Replication slot "
"'test_create_subscription' created.")
else:
print("Replication slot 'test_create_subscription' "
"already exists.")
pg_cursor.close()
except Exception as e:
print("Could not create replication slot: ", e)
def runTest(self):
""" Create the module list on which reverse engineeredsql test
cases will be executed."""
@@ -154,6 +183,29 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
self.assertEqual(self.final_test_status, True)
def tearDown(self):
# Drop the replication slot created for the RESQL test-cases of
# Subscriptions, if it exists before disconnecting for PGv17 and above
if self.server_information['server_version'] >= 170000:
try:
self.get_db_connection()
pg_cursor = self.connection.cursor()
pg_cursor.execute("""
SELECT 1 FROM pg_replication_slots
WHERE slot_name = 'test_create_subscription'
""")
exists = pg_cursor.fetchone()
if exists:
pg_cursor.execute("""
SELECT
pg_drop_replication_slot('test_create_subscription');
""")
self.connection.commit()
print("Replication slot "
"'test_create_subscription' dropped.")
pg_cursor.close()
except Exception as e:
print("Could not drop replication slot: ", e)
database_utils.disconnect_database(
self, self.server_information['server_id'],
self.server_information['db_id'])