public inbox for [email protected]
help / color / mirror / Atom feedFrom: Pradip Parkale <[email protected]>
To: pgadmin-hackers <[email protected]>
Subject: [pgAdmin][RM6153]: Add publication and subscription support in Schema Diff.
Date: Wed, 3 Feb 2021 21:51:30 +0530
Message-ID: <CAJ9T6Ss--gC0W-uRKj0BTr2+CWQYMGT3KB4iYv22yijSANdjmg@mail.gmail.com> (raw)
Hi Hackers,
Please find the attached patch for publication and subscription support in
Schema Diff.
Also, added the dependencies for publication and subscription.
--
Thanks & Regards,
Pradip Parkale
Software Engineer | EnterpriseDB Corporation
Attachments:
[application/octet-stream] RM6153.patch (47.0K, 3-RM6153.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/publications/__init__.py
index 3d98f4388..5207b79b2 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/publications/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/publications/__init__.py
@@ -21,6 +21,8 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
+from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
+from urllib.parse import unquote
class PublicationModule(CollectionNodeModule):
@@ -160,10 +162,6 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
- This function will generate sql to show it in sql pane for the
selected publication node.
- * dependents(gid, sid, did, pbid):
- - This function get the dependents and return ajax response for the
- publication node.
-
* dependencies(self, gid, sid, did, pbid):
- This function get the dependencies and return ajax response for the
publication node.
@@ -281,7 +279,8 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
)
status, pname = self.conn.execute_scalar(get_name_sql)
table_sql = render_template(
- "/".join([self.template_path, 'get_tables.sql']),
+ "/".join([self.template_path,
+ self._GET_TABLE_FOR_PUBLICATION]),
pname=pname
)
@@ -412,7 +411,8 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
)
status, pname = self.conn.execute_scalar(get_name_sql)
table_sql = render_template(
- "/".join([self.template_path, 'get_tables.sql']),
+ "/".join([self.template_path,
+ self._GET_TABLE_FOR_PUBLICATION]),
pname=pname
)
@@ -444,6 +444,9 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
try:
data = self._parser_data_input_from_client(data)
+ # unquote the table data
+ data = self.unquote_the_table(data)
+
sql, name = self.get_sql(data, pbid)
# Most probably this is due to error
@@ -465,6 +468,22 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
except Exception as e:
return internal_server_error(errormsg=str(e))
+ def unquote_the_table(self, data):
+ """
+ This function unquote the table value
+ :param data:
+ :return: data
+ """
+ pubtable = []
+
+ # Unquote the values
+ if 'pubtable' in data:
+ for table in data['pubtable']:
+ pubtable.append(unquote(table))
+ data['pubtable'] = pubtable
+
+ return data
+
@check_precondition
def create(self, gid, sid, did):
"""
@@ -495,6 +514,9 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
try:
data = self._parser_data_input_from_client(data)
+ # unquote the table data
+ data = self.unquote_the_table(data)
+
sql = render_template("/".join([self.template_path,
self._CREATE_SQL]),
data=data, conn=self.conn)
@@ -525,7 +547,7 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
return internal_server_error(errormsg=str(e))
@check_precondition
- def delete(self, gid, sid, did, pbid=None):
+ def delete(self, gid, sid, did, pbid=None, only_sql=False):
"""
This function will drop the publication object
@@ -551,6 +573,7 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
"/".join([self.template_path, self._DELETE_SQL]),
pbid=pbid, conn=self.conn
)
+
status, pname = self.conn.execute_scalar(sql)
if not status:
@@ -562,6 +585,10 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
pname=pname, cascade=cascade, conn=self.conn
)
+ # Used for schema diff tool
+ if only_sql:
+ return sql
+
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@@ -598,6 +625,9 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
except ValueError:
data[k] = v
try:
+ # unquote the table data
+ data = self.unquote_the_table(data)
+
sql, name = self.get_sql(data, pbid)
# Most probably this is due to error
if not isinstance(sql, str):
@@ -637,6 +667,31 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
return data
+ def _get_table_details_to_add_and_delete(self, old_data, data):
+ """
+ This function returns the tables which need to add and delete
+ :param old_data:
+ :param data:
+ :return:
+ """
+ drop_table_data = []
+ add_table_data = []
+ drop_table = False
+ add_table = False
+
+ for table in old_data['pubtable']:
+ if 'pubtable' in data and table not in data['pubtable']:
+ drop_table_data.append(table)
+ drop_table = True
+
+ if 'pubtable' in data:
+ for table in data['pubtable']:
+ if table not in old_data['pubtable']:
+ add_table_data.append(table)
+ add_table = True
+
+ return drop_table, add_table, drop_table_data, add_table_data
+
def get_sql(self, data, pbid=None):
"""
This function will generate sql from model data.
@@ -648,9 +703,6 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
required_args = [
'name'
]
- drop_table = False
- add_table = False
-
if pbid is not None:
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]), pbid=pbid
@@ -664,20 +716,8 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
old_data = self._get_old_table_data(res['rows'][0]['name'], res)
- drop_table_data = []
-
- add_table_data = []
-
- for table in old_data['pubtable']:
- if 'pubtable' in data and table not in data['pubtable']:
- drop_table_data.append(table)
- drop_table = True
-
- if 'pubtable' in data:
- for table in data['pubtable']:
- if table not in old_data['pubtable']:
- add_table_data.append(table)
- add_table = True
+ drop_table, add_table, drop_table_data, add_table_data = \
+ self._get_table_details_to_add_and_delete(old_data, data)
for arg in required_args:
if arg not in data:
@@ -743,7 +783,7 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
"""
table_sql = render_template(
- "/".join([self.template_path, 'get_tables.sql']),
+ "/".join([self.template_path, self._GET_TABLE_FOR_PUBLICATION]),
pname=pname
)
@@ -854,5 +894,99 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare):
status=200
)
+ def get_dependencies(self, conn, object_id, where=None,
+ show_system_objects=None, is_schema_diff=False):
+ """
+ This function gets the dependencies and returns an ajax response
+ for the publication node.
+ :param conn:
+ :param object_id:
+ :param where:
+ :param show_system_objects:
+ :param is_schema_diff:
+ :return: dependencies result
+ """
+
+ get_name_sql = render_template(
+ "/".join([self.template_path, self._DELETE_SQL]),
+ pbid=object_id, conn=self.conn
+ )
+ status, pname = self.conn.execute_scalar(get_name_sql)
+ table_sql = render_template(
+ "/".join([self.template_path, 'dependencies.sql']),
+ pname=pname
+ )
+ status, res = self.conn.execute_dict(table_sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ dependencies_result = []
+
+ for pub_table in res['rows']:
+ dependencies_result.append(
+ {'type': 'table',
+ 'name': pub_table['pubtable'],
+ 'field': 'normal',
+ 'oid': pub_table['oid']})
+
+ return dependencies_result
+
+ @check_precondition
+ def fetch_objects_to_compare(self, sid, did):
+ """
+ This function will fetch the list of all the event triggers for
+ specified database id.
+
+ :param sid: Server Id
+ :param did: Database Id
+ :return:
+ """
+ res = dict()
+
+ last_system_oid = 0
+ if self.manager.db_info is not None and did in self.manager.db_info:
+ last_system_oid = (self.manager.db_info[did])['datlastsysoid']
+
+ sql = render_template(
+ "/".join([self.template_path, 'nodes.sql']),
+ datlastsysoid=last_system_oid,
+ showsysobj=self.blueprint.show_system_objects
+ )
+ status, rset = self.conn.execute_2darray(sql)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ status, data = self._fetch_properties(did, row['oid'])
+ if status:
+ res[row['name']] = data
+
+ return res
+
+ def get_sql_from_diff(self, **kwargs):
+ """
+ This function is used to get the DDL/DML statements.
+ :param kwargs:
+ :return:
+ """
+ gid = kwargs.get('gid')
+ sid = kwargs.get('sid')
+ did = kwargs.get('did')
+ oid = kwargs.get('oid')
+ data = kwargs.get('data', None)
+ drop_sql = kwargs.get('drop_sql', False)
+
+ if data:
+ sql, name = self.get_sql(data=data, pbid=oid)
+ else:
+ if drop_sql:
+ sql = self.delete(gid=gid, sid=sid, did=did,
+ pbid=oid, only_sql=True)
+ else:
+ sql = self.sql(gid=gid, sid=sid, did=did, pbid=oid,
+ json_resp=False)
+ return sql
+
+SchemaDiffRegistry(blueprint.node_type, PublicationView, 'Database')
PublicationView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/delete.sql
index 98cb5d0d1..7efb13acf 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/delete.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/delete.sql
@@ -1,6 +1,6 @@
{# ============= Get the publication name using oid ============= #}
{% if pbid %}
- SELECT pubname FROM pg_publication WHERE oid = {{pbid}}::oid;
+SELECT pubname FROM pg_publication WHERE oid = {{pbid}}::oid;
{% endif %}
{# ============= Drop the publication ============= #}
{% if pname %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/dependencies.sql b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/dependencies.sql
new file mode 100644
index 000000000..46399e304
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/dependencies.sql
@@ -0,0 +1,5 @@
+SELECT cl.oid AS oid,
+quote_ident(pgb_table.schemaname)||'.'||quote_ident(pgb_table.tablename) AS pubtable
+FROM pg_publication_tables pgb_table
+LEFT JOIN pg_class cl ON pgb_table.tablename = cl.relname
+WHERE pubname = '{{ pname }}' AND pgb_table.schemaname NOT LIKE 'pgagent';
diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_all_tables.sql b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_all_tables.sql
index f5f702503..874cf00eb 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_all_tables.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_all_tables.sql
@@ -1,6 +1,6 @@
SELECT quote_ident(c.table_schema)||'.'||quote_ident(c.table_name) AS table
FROM information_schema.tables c
-where c.table_type = 'BASE TABLE'
+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 IN ('information_schema') ORDER BY 1;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_position.sql b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_position.sql
index 656f6907f..80914b606 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_position.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_position.sql
@@ -1 +1 @@
-SELECT oid, pubname AS name FROM pg_publication where pubname = '{{ pubname }}';
+SELECT oid, pubname AS name FROM pg_publication WHERE pubname = '{{ pubname }}';
diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_tables.sql b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_tables.sql
index b18039b9a..1849b65ea 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_tables.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/get_tables.sql
@@ -1 +1,3 @@
-SELECT quote_ident(pgb_table.schemaname)||'.'||quote_ident(pgb_table.tablename) AS pubtable FROM pg_publication_tables pgb_table where pubname = '{{ pname }}' and pgb_table.schemaname NOT LIKE 'pgagent';
+SELECT quote_ident(pgb_table.schemaname)||'.'||quote_ident(pgb_table.tablename)
+AS pubtable FROM pg_publication_tables pgb_table WHERE pubname = '{{ pname }}'
+AND pgb_table.schemaname NOT LIKE 'pgagent';
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/__init__.py
index e970e79e9..fe91f1bbc 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/__init__.py
@@ -169,7 +169,7 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
- This function get the dependents and return ajax response for the
subscription node.
- * dependencies(self, gid, sid, did, subid):
+ * dependencies(gid, sid, did, subid):
- This function get the dependencies and return ajax response for the
subscription node.
"""
@@ -379,28 +379,10 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
return True, res['rows'][0]
- @check_precondition
- def dependents(self, gid, sid, did, subid):
- """
- This function gets the dependents and returns an ajax response
- for the view node.
-
- Args:
- gid: Server Group ID
- sid: Server ID
- did: Database ID
- subid: View ID
- """
- dependents_result = self.get_dependents(self.conn, subid)
- return ajax_response(
- response=dependents_result,
- status=200
- )
-
@check_precondition
def statistics(self, gid, sid, did, subid):
"""
- This function gets the dependents and returns an ajax response
+ This function gets the statistics and returns an ajax response
for the view node.
Args:
@@ -418,24 +400,6 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
status=200
)
- @check_precondition
- def dependencies(self, gid, sid, did, subid):
- """
- This function gets the dependencies and returns an ajax response
- for the view node.
-
- Args:
- gid: Server Group ID
- sid: Server ID
- did: Database ID
- subid: View ID
- """
- dependencies_result = self.get_dependencies(self.conn, subid)
- return ajax_response(
- response=dependencies_result,
- status=200
- )
-
@check_precondition
def update(self, gid, sid, did, subid):
"""
@@ -537,7 +501,7 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
return internal_server_error(errormsg=str(e))
@check_precondition
- def delete(self, gid, sid, did, subid=None):
+ def delete(self, gid, sid, did, subid=None, only_sql=False):
"""
This function will drop the subscription object
@@ -575,6 +539,10 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
subname=subname, cascade=cascade, conn=self.conn
)
+ # Used for schema diff tool
+ if only_sql:
+ return sql
+
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@@ -625,26 +593,32 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
except Exception as e:
return internal_server_error(errormsg=str(e))
- def get_details(self, data, old_data):
+ def get_required_details(self, data, old_data):
"""
This function returns the required data to create subscription
:param data:
- :return:
+ :return:data , old_data
"""
required_args = ['name']
required_connection_args = ['host', 'port', 'username', 'db',
'connect_timeout', 'passfile']
+
+ # Set connection time out to zero if initial set
+ # value is replaced to ''
+ if 'connect_timeout' in data and data['connect_timeout'] == '':
+ data['connect_timeout'] = 0
+
for arg in required_args:
- if arg not in data and arg in old_data:
+ if arg not in data:
data[arg] = old_data[arg]
for arg in required_connection_args:
- if arg not in data and arg in old_data:
- data[arg] = old_data[arg]
+ if arg in data:
+ old_data[arg] = data[arg]
- return data
+ return data, old_data
def get_sql(self, data, subid=None, operation=None):
"""
@@ -655,10 +629,6 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
subid: Subscription ID
"""
- required_args = ['name']
-
- required_connection_args = ['host', 'port', 'username', 'db',
- 'connect_timeout', 'passfile']
if operation == 'msql':
dummy = True
else:
@@ -677,13 +647,7 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
return gone(self._NOT_FOUND_PUB_INFORMATION)
old_data = res['rows'][0]
- for arg in required_args:
- if arg not in data:
- data[arg] = old_data[arg]
-
- for arg in required_connection_args:
- if arg in data:
- old_data[arg] = data[arg]
+ data, old_data = self.get_required_details(data, old_data)
if 'slot_name' in data and data['slot_name'] == '':
data['slot_name'] = 'None'
@@ -702,6 +666,11 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
return sql.strip('\n'), data['name']
def get_connection(self, connection_details):
+ """
+ This function is used to connect to DB and returns the publications
+ :param connection_details:
+ :return: publication list
+ """
passfile = connection_details['passfile'] if \
'passfile' in connection_details and \
@@ -772,13 +741,13 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
def sql(self, gid, sid, did, subid, json_resp=True):
"""
This function will generate sql to show in the sql pane for the
- selected publication node.
+ selected subscription node.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
- subid: Publication ID
+ subid: Subscription ID
json_resp:
"""
sql = render_template(
@@ -794,8 +763,11 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
# Making copy of output for future use
old_data = dict(res['rows'][0])
- if old_data['slot_name'] is None and 'create_slot' not in old_data:
- old_data['create_slot'] = False
+ old_data['create_slot'] = False
+ if old_data['enabled']:
+ old_data['connect'] = True
+ else:
+ old_data['connect'] = False
sql = render_template("/".join([self.template_path,
self._CREATE_SQL]),
@@ -856,5 +828,111 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
status=200
)
+ def get_dependencies(self, conn, object_id, where=None,
+ show_system_objects=None, is_schema_diff=False):
+ """
+ This function gets the dependencies and returns an ajax response
+ for the subscription node.
+ :param conn:
+ :param object_id:
+ :param where:
+ :param show_system_objects:
+ :param is_schema_diff:
+ :return: dependencies result
+ """
+
+ get_name_sql = render_template(
+ "/".join([self.template_path, self._DELETE_SQL]),
+ subid=object_id, conn=self.conn
+ )
+ status, subname = self.conn.execute_scalar(get_name_sql)
+ table_sql = render_template(
+ "/".join([self.template_path, 'dependencies.sql']),
+ subname=subname
+ )
+ status, res = self.conn.execute_dict(table_sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+ dependencies_result = []
+
+ for publication in res['rows'][0]['pub']:
+ dependencies_result.append(
+ {'type': 'publication',
+ 'name': publication,
+ 'field': 'normal'})
+
+ return dependencies_result
+
+ @check_precondition
+ def fetch_objects_to_compare(self, sid, did):
+ """
+ This function will fetch the list of all the event triggers for
+ specified database id.
+
+ :param sid: Server Id
+ :param did: Database Id
+ :return:
+ """
+ res = dict()
+
+ last_system_oid = 0
+ if self.manager.db_info is not None and did in self.manager.db_info:
+ last_system_oid = (self.manager.db_info[did])['datlastsysoid']
+
+ sql = render_template(
+ "/".join([self.template_path, 'nodes.sql']),
+ datlastsysoid=last_system_oid,
+ showsysobj=self.blueprint.show_system_objects,
+ did=did
+ )
+ status, rset = self.conn.execute_2darray(sql)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ status, data = self._fetch_properties(did, row['oid'])
+ if status:
+ res[row['name']] = data
+
+ return res
+
+ def get_sql_from_diff(self, **kwargs):
+ """
+ This function is used to get the DDL/DML statements.
+ :param kwargs:
+ :return:
+ """
+ gid = kwargs.get('gid')
+ sid = kwargs.get('sid')
+ did = kwargs.get('did')
+ oid = kwargs.get('oid')
+ data = kwargs.get('data', None)
+ drop_sql = kwargs.get('drop_sql', False)
+
+ if data:
+ if 'pub' in data and type(data['pub']) == str:
+ # Convert publication details to list
+ data['pub'] = data['pub'].split(',,')
+ sql, name = self.get_sql(data=data, subid=oid)
+ else:
+ if drop_sql:
+ sql = self.delete(gid=gid, sid=sid, did=did,
+ subid=oid, only_sql=True)
+ else:
+ sql = self.sql(gid=gid, sid=sid, did=did, subid=oid,
+ json_resp=False)
+ if 'source' in kwargs:
+ sql_header = \
+ '-- WARNING:\n' \
+ '-- create_slot will be set to false by ' \
+ 'default.'
+ sql_header += "\n\n"
+
+ sql_header += sql
+ sql = sql_header
+ return sql
+
+
+SchemaDiffRegistry(blueprint.node_type, SubscriptionView, 'Database')
SubscriptionView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/static/js/subscription.js b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/static/js/subscription.js
index caa084827..6c1b71521 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/static/js/subscription.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/static/js/subscription.js
@@ -77,6 +77,7 @@ define('pgadmin.node.subscription', [
name: undefined,
subowner: undefined,
pubtable: undefined,
+ connect_timeout: undefined,
pub:[],
enabled:true,
create_slot: true,
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/templates/subscriptions/sql/default/dependencies.sql b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/templates/subscriptions/sql/default/dependencies.sql
new file mode 100644
index 000000000..a71f0cd25
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/templates/subscriptions/sql/default/dependencies.sql
@@ -0,0 +1,2 @@
+SELECT subpublications AS pub FROM pg_subscription
+WHERE subname = '{{subname}}';
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_maintenance_db.sql b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_maintenance_db.sql
index b341b0935..1a2d6204b 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_maintenance_db.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_maintenance_db.sql
@@ -5,4 +5,4 @@
CREATE SUBSCRIPTION test_alter_subscription
CONNECTION 'host=localhost port=5432 user=postgres dbname=edb'
PUBLICATION sample__1
- WITH (enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'remote_apply');
+ WITH (connect = false, enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'remote_apply');
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_subscription.sql b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_subscription.sql
index 8010a4959..ce72dfcab 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_subscription.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_subscription.sql
@@ -5,4 +5,4 @@
CREATE SUBSCRIPTION test_alter_subscription
CONNECTION 'host=localhost port=5432 user=postgres dbname=postgres'
PUBLICATION sample__1
- WITH (enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'off');
+ WITH (connect = false, enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'off');
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_sync.sql b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_sync.sql
index 54e678273..03debd0b8 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_sync.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/alter_sync.sql
@@ -5,4 +5,4 @@
CREATE SUBSCRIPTION test_alter_subscription
CONNECTION 'host=localhost port=5432 user=postgres dbname=postgres'
PUBLICATION sample__1
- WITH (enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'remote_apply');
+ WITH (connect = false, enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'remote_apply');
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/create_subscription.sql b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/create_subscription.sql
index a7819f196..6548c5f7e 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/create_subscription.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/10_plus/create_subscription.sql
@@ -5,4 +5,4 @@
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5432 user=postgres dbname=postgres'
PUBLICATION sample__1
- WITH (enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'off');
+ WITH (connect = false, enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'off');
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/subscription_test_data.json b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/subscription_test_data.json
index 4773557fe..dca2e5a78 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/subscription_test_data.json
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/subscription_test_data.json
@@ -14,12 +14,12 @@
"subowner": "postgres",
"enabled": false,
"host": "localhost",
- "slot_name": "NONE",
+ "slot_name": "None",
"service": "",
"port": 5432,
"password": "",
"sync": "off",
- "pub": "PLACE_HOLDER"
+ "pub": "[\"sample__1\"]"
},
"mocking_required": false,
"mock_data": {},
@@ -42,12 +42,12 @@
"subowner": "postgres",
"enabled": false,
"host": "localhost",
- "slot_name": "NONE",
+ "slot_name": "None",
"service": "",
"port": 5432,
"password": "",
"sync": "off",
- "pub": "PLACE_HOLDER"
+ "pub": "[\"sample__1\"]"
},
"mocking_required": false,
"mock_data": {},
@@ -70,12 +70,12 @@
"subowner": "postgres",
"enabled": false,
"host": "localhost",
- "slot_name": "NONE",
+ "slot_name": "None",
"service": "",
"port": 5432,
"password": "",
"sync": "off",
- "pub": "PLACE_HOLDER"
+ "pub": "[\"sample__1\"]"
},
"mocking_required": false,
"mock_data": {},
@@ -98,12 +98,12 @@
"subowner": "postgres",
"enabled": false,
"host": "localhost",
- "slot_name": "NONE",
+ "slot_name": "None",
"service": "",
"port": 5432,
"password": "",
"sync": "off",
- "pub": "PLACE_HOLDER"
+ "pub": "[\"sample__1\"]"
},
"mocking_required": true,
"mock_data": {
@@ -128,12 +128,12 @@
"subowner": "postgres",
"enabled": false,
"host": "localhost",
- "slot_name": "NONE",
+ "slot_name": "None",
"service": "",
"port": 5432,
"password": "",
"sync": "off",
- "pub": "PLACE_HOLDER"
+ "pub": "[\"sample__1\"]"
},
"mocking_required": true,
"mock_data": {
diff --git a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/test_subscription_create.py b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/test_subscription_create.py
index 6b80182dd..d558320f4 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/test_subscription_create.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/subscriptions/tests/test_subscription_create.py
@@ -59,8 +59,7 @@ class SubscriptionAddTestCase(BaseTestGenerator):
"""This function will subscription."""
self.test_data['name'] = \
"test_subscription_add_%s" % (str(uuid.uuid4())[1:8])
-
- self.test_data['pub'] = """["sample__1"]"""
+ self.test_data['slot_name'] = None
data = self.test_data
if self.is_positive_test:
diff --git a/web/pgadmin/browser/server_groups/servers/templates/depends/pg/11_plus/dependents.sql b/web/pgadmin/browser/server_groups/servers/templates/depends/pg/11_plus/dependents.sql
index 853847f5a..6f2e48288 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/depends/pg/11_plus/dependents.sql
+++ b/web/pgadmin/browser/server_groups/servers/templates/depends/pg/11_plus/dependents.sql
@@ -19,14 +19,13 @@ SELECT DISTINCT dep.deptype, dep.classid, cl.relkind, ad.adbin, ad.adsrc,
WHEN ftst.oid IS NOT NULL THEN 'Ft'::text
WHEN ext.oid IS NOT NULL THEN 'Ex'::text
WHEN pl.oid IS NOT NULL THEN 'Rs'::text
- WHEN pub_rel.oid IS NOT NULL THEN 'r'::text
ELSE ''
END AS type,
COALESCE(coc.relname, clrw.relname) AS ownertable,
CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname,
fs.srvname, fdw.fdwname, evt.evtname, col.collname, ftsc.cfgname, ftsd.dictname, ftsp.prsname,
- ftst.tmplname, ext.extname, pl.polname, quote_ident(pubns.nspname)||'.'||quote_ident(pubcl.relname))
+ ftst.tmplname, ext.extname, pl.polname)
END AS refname,
COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname, colns.nspname, ftscns.nspname,
ftsdns.nspname, ftspns.nspname, ftstns.nspname) AS nspname,
@@ -68,12 +67,9 @@ LEFT JOIN pg_ts_template ftst ON ftst.oid=dep.objid
LEFT JOIN pg_namespace ftstns ON ftst.tmplnamespace=ftstns.oid
LEFT JOIN pg_extension ext ON ext.oid=dep.objid
LEFT JOIN pg_policy pl ON pl.oid=dep.objid
-LEFT JOIN pg_publication_rel pub_rel ON pub_rel.oid = dep.objid
-LEFT JOIN pg_class pubcl ON pubcl.oid = pub_rel.prrelid
-LEFT JOIN pg_namespace pubns ON pubns.oid=pubcl.relnamespace
{{where_clause}} AND
classid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
- 'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension', 'pg_policy', 'pg_subscription', 'pg_publication_rel'))
+ 'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension', 'pg_policy'))
ORDER BY classid, cl.relkind
diff --git a/web/pgadmin/browser/server_groups/servers/templates/depends/pg/12_plus/dependents.sql b/web/pgadmin/browser/server_groups/servers/templates/depends/pg/12_plus/dependents.sql
index 5bac508ae..ef76c885d 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/depends/pg/12_plus/dependents.sql
+++ b/web/pgadmin/browser/server_groups/servers/templates/depends/pg/12_plus/dependents.sql
@@ -19,14 +19,13 @@ SELECT DISTINCT dep.deptype, dep.classid, cl.relkind, ad.adbin, pg_get_expr(ad.a
WHEN ftst.oid IS NOT NULL THEN 'Ft'::text
WHEN ext.oid IS NOT NULL THEN 'Ex'::text
WHEN pl.oid IS NOT NULL THEN 'Rs'::text
- WHEN pub_rel.oid IS NOT NULL THEN 'r'::text
ELSE ''
END AS type,
COALESCE(coc.relname, clrw.relname) AS ownertable,
CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname,
fs.srvname, fdw.fdwname, evt.evtname, col.collname, ftsc.cfgname, ftsd.dictname, ftsp.prsname,
- ftst.tmplname, ext.extname, pl.polname, quote_ident(pubns.nspname)||'.'||quote_ident(pubcl.relname))
+ ftst.tmplname, ext.extname, pl.polname)
END AS refname,
COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname, colns.nspname, ftscns.nspname,
ftsdns.nspname, ftspns.nspname, ftstns.nspname) AS nspname,
@@ -68,12 +67,9 @@ LEFT JOIN pg_ts_template ftst ON ftst.oid=dep.objid
LEFT JOIN pg_namespace ftstns ON ftst.tmplnamespace=ftstns.oid
LEFT JOIN pg_extension ext ON ext.oid=dep.objid
LEFT JOIN pg_policy pl ON pl.oid=dep.objid
-LEFT JOIN pg_publication_rel pub_rel ON pub_rel.oid = dep.objid
-LEFT JOIN pg_class pubcl ON pubcl.oid = pub_rel.prrelid
-LEFT JOIN pg_namespace pubns ON pubns.oid=pubcl.relnamespace
{{where_clause}} AND
classid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
- 'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension', 'pg_policy', 'pg_subscription', 'pg_publication_rel'))
+ 'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension', 'pg_policy'))
ORDER BY classid, cl.relkind
diff --git a/web/pgadmin/browser/server_groups/servers/templates/depends/ppas/11_plus/dependents.sql b/web/pgadmin/browser/server_groups/servers/templates/depends/ppas/11_plus/dependents.sql
index ad70e3b93..b6e368731 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/depends/ppas/11_plus/dependents.sql
+++ b/web/pgadmin/browser/server_groups/servers/templates/depends/ppas/11_plus/dependents.sql
@@ -20,14 +20,13 @@ SELECT DISTINCT dep.deptype, dep.classid, cl.relkind, ad.adbin, ad.adsrc,
WHEN ext.oid IS NOT NULL THEN 'Ex'::text
WHEN syn.oid IS NOT NULL THEN 'Sy'::text
WHEN pl.oid IS NOT NULL THEN 'Rs'::text
- WHEN pub_rel.oid IS NOT NULL THEN 'r'::text
ELSE ''
END AS type,
COALESCE(coc.relname, clrw.relname) AS ownertable,
CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname,
fs.srvname, fdw.fdwname, evt.evtname, col.collname, ftsc.cfgname, ftsd.dictname, ftsp.prsname,
- ftst.tmplname, ext.extname, syn.synname, pl.polname, quote_ident(pubns.nspname)||'.'||quote_ident(pubcl.relname))
+ ftst.tmplname, ext.extname, syn.synname, pl.polname)
END AS refname,
COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname, colns.nspname, ftscns.nspname,
ftsdns.nspname, ftspns.nspname, ftstns.nspname, synns.nspname) AS nspname,
@@ -71,12 +70,10 @@ LEFT JOIN pg_extension ext ON ext.oid=dep.objid
LEFT JOIN pg_synonym syn ON syn.oid=dep.objid
LEFT JOIN pg_namespace synns ON syn.synnamespace=synns.oid
LEFT JOIN pg_policy pl ON pl.oid=dep.objid
-LEFT JOIN pg_publication_rel pub_rel ON pub_rel.oid = dep.objid
-LEFT JOIN pg_class pubcl ON pubcl.oid = pub_rel.prrelid
-LEFT JOIN pg_namespace pubns ON pubns.oid=pubcl.relnamespace
{{where_clause}} AND
classid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
- 'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension', 'pg_policy', 'pg_subscription', 'pg_publication_rel'))
+ 'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension',
+ 'pg_synonym', 'pg_policy'))
ORDER BY classid, cl.relkind
diff --git a/web/pgadmin/browser/server_groups/servers/templates/depends/ppas/12_plus/dependents.sql b/web/pgadmin/browser/server_groups/servers/templates/depends/ppas/12_plus/dependents.sql
index 9b1002b64..c6781827f 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/depends/ppas/12_plus/dependents.sql
+++ b/web/pgadmin/browser/server_groups/servers/templates/depends/ppas/12_plus/dependents.sql
@@ -20,14 +20,13 @@ SELECT DISTINCT dep.deptype, dep.classid, cl.relkind, ad.adbin, pg_get_expr(ad.a
WHEN ext.oid IS NOT NULL THEN 'Ex'::text
WHEN syn.oid IS NOT NULL THEN 'Sy'::text
WHEN pl.oid IS NOT NULL THEN 'Rs'::text
- WHEN pub_rel.oid IS NOT NULL THEN 'r'::text
ELSE ''
END AS type,
COALESCE(coc.relname, clrw.relname) AS ownertable,
CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname,
fs.srvname, fdw.fdwname, evt.evtname, col.collname, ftsc.cfgname, ftsd.dictname, ftsp.prsname,
- ftst.tmplname, ext.extname, syn.synname, pl.polname, quote_ident(pubns.nspname)||'.'||quote_ident(pubcl.relname))
+ ftst.tmplname, ext.extname, syn.synname, pl.polname)
END AS refname,
COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname, colns.nspname, ftscns.nspname,
ftsdns.nspname, ftspns.nspname, ftstns.nspname, synns.nspname) AS nspname,
@@ -71,13 +70,10 @@ LEFT JOIN pg_extension ext ON ext.oid=dep.objid
LEFT JOIN pg_synonym syn ON syn.oid=dep.objid
LEFT JOIN pg_namespace synns ON syn.synnamespace=synns.oid
LEFT JOIN pg_policy pl ON pl.oid=dep.objid
-LEFT JOIN pg_publication_rel pub_rel ON pub_rel.oid = dep.objid
-LEFT JOIN pg_class pubcl ON pubcl.oid = pub_rel.prrelid
-LEFT JOIN pg_namespace pubns ON pubns.oid=pubcl.relnamespace
{{where_clause}} AND
classid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension',
- 'pg_synonym', 'pg_policy', 'pg_subscription', 'pg_publication_rel'))
+ 'pg_synonym', 'pg_policy'))
ORDER BY classid, cl.relkind
diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py
index 42342e320..5dd361c28 100644
--- a/web/pgadmin/browser/utils.py
+++ b/web/pgadmin/browser/utils.py
@@ -393,6 +393,7 @@ class PGChildNodeView(NodeView):
_GET_COLUMNS_FOR_TABLE_SQL = 'get_columns_for_table.sql'
_GET_SUBTYPES_SQL = 'get_subtypes.sql'
_GET_EXTERNAL_FUNCTIONS_SQL = 'get_external_functions.sql'
+ _GET_TABLE_FOR_PUBLICATION = 'get_tables.sql'
def get_children_nodes(self, manager, **kwargs):
"""
@@ -638,9 +639,7 @@ class PGChildNodeView(NodeView):
# if type is present in the types dictionary, but it's
# value is None then it requires special handling.
if type_str[0] == 'r':
- if len(type_str) == 1:
- type_name = 'table'
- elif (type_str[1].isdigit() and int(type_str[1]) > 0) or \
+ if (type_str[1].isdigit() and int(type_str[1]) > 0) or \
(len(type_str) > 2 and type_str[2].isdigit() and
int(type_str[2]) > 0):
type_name = 'column'
diff --git a/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js b/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js
index d542aa072..32903aad6 100644
--- a/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js
+++ b/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js
@@ -20,7 +20,8 @@ export class ModelValidation {
validate() {
const serviceId = this.model.get('service'),
- pub = this.model.get('pub');
+ pub = this.model.get('pub'),
+ password = this.model.get('password');
if (!this.model.isNew() && 'id' in this.model.sessAttrs) {
this.err['id'] = gettext('The ID cannot be changed.');
@@ -39,12 +40,16 @@ export class ModelValidation {
this.checkForEmpty('db', gettext('Maintenance database must be specified.'));
this.checkForEmpty('username', gettext('Username must be specified.'));
this.checkForEmpty('port', gettext('Port must be specified.'));
- if(!_.isUndefined(pub) && pub.length == 0){
+ if(!_.isUndefined(pub) || pub.length == 0){
+ 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) && pub.length == 0){
+ if(!_.isUndefined(pub) || pub.length == 0){
+ if (this.model.isNew())
+ this.checkForEmpty('password', gettext('Password must be specified.'));
this.checkForEmpty('pub', gettext('Publication must be specified.'));
}
this.clearHostAddressAndDbErrors();
diff --git a/web/pgadmin/tools/schema_diff/directory_compare.py b/web/pgadmin/tools/schema_diff/directory_compare.py
index 8a1f5348d..739a91f58 100644
--- a/web/pgadmin/tools/schema_diff/directory_compare.py
+++ b/web/pgadmin/tools/schema_diff/directory_compare.py
@@ -68,6 +68,8 @@ def _get_source_list(**kwargs):
source_ddl = view_object.get_sql_from_diff(**temp_src_params)
temp_src_params.update({'target_schema': target_schema})
+ # To know that the changes are in source only
+ temp_src_params.update({'source': True})
diff_ddl = view_object.get_sql_from_diff(**temp_src_params)
source_dependencies = view_object.get_dependencies(
view_object.conn, source_object_id, where=None,
view thread (6+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [pgAdmin][RM6153]: Add publication and subscription support in Schema Diff.
In-Reply-To: <CAJ9T6Ss--gC0W-uRKj0BTr2+CWQYMGT3KB4iYv22yijSANdjmg@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox