From c6bae69b6f71ee68886aae5db895952af067fff1 Mon Sep 17 00:00:00 2001 From: Khushboo Vashi Date: Tue, 22 Jan 2019 17:53:36 +0530 Subject: [PATCH] Proper SQL should be generated when create domain of type interval with precision. Fixes #3853 --- docs/en_US/release_notes_4_2.rst | 1 + .../databases/schemas/domains/__init__.py | 3 +- .../schemas/domains/tests/test_domain_sql.py | 117 ++++++++++++++++++ .../databases/schemas/domains/tests/utils.py | 61 ++++++++- 4 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/domains/tests/test_domain_sql.py diff --git a/docs/en_US/release_notes_4_2.rst b/docs/en_US/release_notes_4_2.rst index 76abbf0c7..5f6062261 100644 --- a/docs/en_US/release_notes_4_2.rst +++ b/docs/en_US/release_notes_4_2.rst @@ -26,6 +26,7 @@ Bug fixes | `Bug #3838 `_ - Proper SQL should be generated when creating/changing column with custom type argument. | `Bug #3840 `_ - Ensure that file format combo box value should be retained when hidden files checkbox is toggled. | `Bug #3846 `_ - Proper SQL should be generated when create procedure with custom type arguments. +| `Bug #3853 `_ - Proper SQL should be generated when create domain of type interval with precision. | `Bug #3858 `_ - Drop-down should be closed when click on any other toolbar button. | `Bug #3862 `_ - Fixed keyboard navigation for dialog tabs. | `Bug #3871 `_ - Fixed alignment of tree arrow icons for Internet Explorer. diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py index 90c3cf4df..c6895eb04 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -442,11 +442,10 @@ It may have been removed by another user or moved to another schema. substr = basetype[basetype.find("(") + 1:len( basetype) - 1] typlen = substr.split(",") + typ_len = typlen[0] if len(typlen) > 1: - typ_len = typlen[0] typ_precision = typlen[1] else: - typ_len = typlen typ_precision = '' return {'typlen': typ_len, 'precision': typ_precision} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/tests/test_domain_sql.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/tests/test_domain_sql.py new file mode 100644 index 000000000..0d60db3a3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/tests/test_domain_sql.py @@ -0,0 +1,117 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2019, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +import uuid +import json +import re + +from pgadmin.browser.server_groups.servers.databases.schemas.tests import \ + utils as schema_utils +from pgadmin.browser.server_groups.servers.databases.tests import utils as \ + database_utils +from pgadmin.utils.route import BaseTestGenerator +from regression import parent_node_dict +from regression.python_test_utils import test_utils as utils +from . import utils as domain_utils + + +class DomainReverseEngineeredSQLTestCase(BaseTestGenerator): + """ This class will verify reverse engineered sql for domain + under schema node. """ + scenarios = [ + # Fetching default URL for domain node. + ('Domain Reverse Engineered SQL with char', + dict(url='/browser/domain/sql/', + domain_name='domain_get_%s' % (str(uuid.uuid4())[1:8]), + domain_sql='AS "char";' + ) + ), + ('Domain Reverse Engineered SQL with Length, Precision and Default', + dict(url='/browser/domain/sql/', + domain_name='domain_get_%s' % (str(uuid.uuid4())[1:8]), + domain_sql='AS numeric(12,2) DEFAULT 12 NOT NULL;' + ) + ), + ('Domain Reverse Engineered SQL with Length', + dict(url='/browser/domain/sql/', + domain_name='domain_get_%s' % (str(uuid.uuid4())[1:8]), + domain_sql='AS interval(6);' + ) + ), + ] + + def setUp(self): + self.database_info = parent_node_dict["database"][-1] + self.db_name = self.database_info["db_name"] + self.schema_info = parent_node_dict["schema"][-1] + self.schema_name = self.schema_info["schema_name"] + self.schema_id = self.schema_info["schema_id"] + self.domain_info = domain_utils.create_domain(self.server, + self.db_name, + self.schema_name, + self.schema_id, + self.domain_name, + self.domain_sql) + + def runTest(self): + """ This function will add domain and verify the + reverse engineered sql. """ + db_id = self.database_info["db_id"] + server_id = self.database_info["server_id"] + db_con = database_utils.connect_database(self, utils.SERVER_GROUP, + server_id, db_id) + if not db_con['data']["connected"]: + raise Exception("Could not connect to database to get the domain.") + + db_name = self.database_info["db_name"] + schema_response = schema_utils.verify_schemas(self.server, + db_name, + self.schema_name) + if not schema_response: + raise Exception("Could not find the schema to get the domain.") + domain_id = self.domain_info[0] + + # Call GET API to fetch the domain sql + get_response = self.tester.get( + self.url + str(utils.SERVER_GROUP) + '/' + + str(server_id) + '/' + + str(db_id) + '/' + + str(self.schema_id) + '/' + + str(domain_id), + content_type='html/json') + + self.assertEquals(get_response.status_code, 200) + orig_sql = json.loads(get_response.data.decode('utf-8')) + + # Replace multiple spaces with one space and check the expected sql + sql = re.sub('\s+', ' ', orig_sql).strip() + expected_sql = '-- DOMAIN: {0}.{1} -- DROP DOMAIN {0}.{1}; ' \ + 'CREATE DOMAIN {0}.{1} {2} ' \ + 'ALTER DOMAIN {0}.{1} OWNER' \ + ' TO postgres;'.format(self.schema_name, + self.domain_name, + self.domain_sql) + + self.assertEquals(sql, expected_sql) + + domain_utils.delete_domain(self.server, db_name, + self.schema_name, self.domain_name) + + # Verify the reverse engineered sql with creating domain with + # the sql we get from the server + domain_utils.create_domain_from_sql(self.server, db_name, orig_sql) + + domain_utils.delete_domain(self.server, db_name, + self.schema_name, self.domain_name) + + # Disconnect the database + database_utils.disconnect_database(self, server_id, db_id) + + def tearDown(self): + pass diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/tests/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/tests/utils.py index 4354eb297..3bd9a27f8 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/tests/utils.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/tests/utils.py @@ -15,7 +15,8 @@ import traceback from regression.python_test_utils import test_utils as utils -def create_domain(server, db_name, schema_name, schema_id, domain_name): +def create_domain(server, db_name, schema_name, schema_id, domain_name, + domain_sql=None): """ This function is used to add the domain to existing schema :param server: server details @@ -37,8 +38,14 @@ def create_domain(server, db_name, schema_name, schema_id, domain_name): server['host'], server['port']) pg_cursor = connection.cursor() - query = 'CREATE DOMAIN ' + schema_name + '.' + domain_name + \ - ' AS character(10) DEFAULT 1' + + if domain_sql is None: + query = 'CREATE DOMAIN ' + schema_name + '.' + domain_name + \ + ' AS character(10) DEFAULT 1' + else: + query = 'CREATE DOMAIN ' + schema_name + '.' +\ + domain_name + ' ' + domain_sql + pg_cursor.execute(query) connection.commit() # Get 'oid' from newly created domain @@ -77,3 +84,51 @@ def verify_domain(server, db_name, schema_id, domain_name): domains = pg_cursor.fetchone() connection.close() return domains + + +def delete_domain(server, db_name, schema_name, domain_name): + """ + This function deletes the domain. + :param server: + :param db_name: + :param schema_name: + :param domain_name: + :return: + """ + + try: + connection = utils.get_db_connection(db_name, + server['username'], + server['db_password'], + server['host'], + server['port']) + pg_cursor = connection.cursor() + pg_cursor.execute("DROP DOMAIN %s.%s" % + (schema_name, domain_name)) + connection.commit() + connection.close() + except Exception: + traceback.print_exc(file=sys.stderr) + + +def create_domain_from_sql(server, db_name, sql): + """ + This function create domain from the reverse engineered sql + :param server: + :param db_name: + :param sql: + :return: + """ + + try: + connection = utils.get_db_connection(db_name, + server['username'], + server['db_password'], + server['host'], + server['port']) + pg_cursor = connection.cursor() + pg_cursor.execute(sql) + connection.commit() + connection.close() + except Exception: + traceback.print_exc(file=sys.stderr)