diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst index 00c0ebf8f..2b5942a2f 100644 --- a/docs/en_US/query_tool.rst +++ b/docs/en_US/query_tool.rst @@ -134,6 +134,9 @@ A result set is updatable if: * All the primary keys or OIDs of the table are explicitly selected. * No columns are duplicated. +The psycopg2 driver version should be equal to or above 2.8 for updatable +query result sets to work. + An updatable result set can be modified just like in :ref:`View/Edit Data ` mode. diff --git a/docs/en_US/release_notes_4_12.rst b/docs/en_US/release_notes_4_12.rst index aa1c7831e..4b680b79a 100644 --- a/docs/en_US/release_notes_4_12.rst +++ b/docs/en_US/release_notes_4_12.rst @@ -26,3 +26,4 @@ Bug fixes | `Issue #4496 `_ - Ensure columns can be created when they are IDENTITY fields with the CYCLE option enabled. | `Issue #4497 `_ - Ensure purely numeric comments can be saved on new columns. | `Issue #4508 `_ - Fix accessibility issue for Datetime cell in backgrid. +| `Issue #4520 `_ - Ensure the query tool will work with older versions of psycopg2 than we officially support, albeit without updatable resultsets. \ No newline at end of file diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py index 27163b43e..8208b18ea 100644 --- a/web/pgadmin/feature_tests/query_tool_journey_test.py +++ b/web/pgadmin/feature_tests/query_tool_journey_test.py @@ -50,6 +50,9 @@ class QueryToolJourneyTest(BaseFeatureTest): self.page.add_server(self.server) + driver_version = test_utils.get_driver_version() + self.driver_version = float('.'.join(driver_version.split('.')[:2])) + def runTest(self): self._navigate_to_query_tool() self._execute_query( @@ -190,6 +193,9 @@ class QueryToolJourneyTest(BaseFeatureTest): self._assert_clickable(query_we_need_to_scroll_to) def _test_updatable_resultset(self): + if self.driver_version < 2.8: + return + self.page.click_tab("Query Editor") # Insert data into test table diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py index c344f5d7a..4fe4d7da6 100644 --- a/web/pgadmin/tools/sqleditor/__init__.py +++ b/web/pgadmin/tools/sqleditor/__init__.py @@ -424,16 +424,16 @@ def poll(trans_id): # If trans_obj is a QueryToolCommand then check for updatable # resultsets and primary keys if isinstance(trans_obj, QueryToolCommand): - trans_obj.check_updatable_results_pkeys_oids() - pk_names, primary_keys = trans_obj.get_primary_keys() - session_obj['has_oids'] = trans_obj.has_oids() - # Update command_obj in session obj - session_obj['command_obj'] = pickle.dumps( - trans_obj, -1) - # If primary_keys exist, add them to the session_obj to - # allow for saving any changes to the data - if primary_keys is not None: - session_obj['primary_keys'] = primary_keys + if trans_obj.check_updatable_results_pkeys_oids(): + pk_names, primary_keys = trans_obj.get_primary_keys() + session_obj['has_oids'] = trans_obj.has_oids() + # Update command_obj in session obj + session_obj['command_obj'] = pickle.dumps( + trans_obj, -1) + # If primary_keys exist, add them to the session_obj to + # allow for saving any changes to the data + if primary_keys is not None: + session_obj['primary_keys'] = primary_keys if 'has_oids' in session_obj: has_oids = session_obj['has_oids'] diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py index 258f6c603..01815f8cf 100644 --- a/web/pgadmin/tools/sqleditor/command.py +++ b/web/pgadmin/tools/sqleditor/command.py @@ -896,6 +896,13 @@ class QueryToolCommand(BaseCommand, FetchedRowTracker): manager = driver.connection_manager(self.sid) conn = manager.connection(did=self.did, conn_id=self.conn_id) + # Get the driver version as a float + driver_version = float('.'.join(driver.Version().split('.')[:2])) + + # Checking for updatable resultsets uses features in psycopg 2.8 + if driver_version < 2.8: + return False + # Get the path to the sql templates sql_path = 'sqleditor/sql/#{0}#'.format(manager.version) @@ -918,6 +925,7 @@ class QueryToolCommand(BaseCommand, FetchedRowTracker): self.__set_updatable_results_attrs(sql_path=sql_path, table_oid=table_oid, conn=conn) + return self.is_updatable_resultset def save(self, changed_data, diff --git a/web/pgadmin/tools/sqleditor/tests/test_is_query_resultset_updatable.py b/web/pgadmin/tools/sqleditor/utils/tests/test_is_query_resultset_updatable.py similarity index 93% rename from web/pgadmin/tools/sqleditor/tests/test_is_query_resultset_updatable.py rename to web/pgadmin/tools/sqleditor/utils/tests/test_is_query_resultset_updatable.py index 5c249c5b5..6a3ea38e4 100644 --- a/web/pgadmin/tools/sqleditor/tests/test_is_query_resultset_updatable.py +++ b/web/pgadmin/tools/sqleditor/utils/tests/test_is_query_resultset_updatable.py @@ -15,7 +15,7 @@ from pgadmin.browser.server_groups.servers.databases.tests import utils as \ from pgadmin.utils.route import BaseTestGenerator from regression import parent_node_dict from regression.python_test_utils import test_utils as utils -from .execute_query_utils import execute_query +from pgadmin.tools.sqleditor.tests.execute_query_utils import execute_query class TestQueryUpdatableResultset(BaseTestGenerator): @@ -120,6 +120,7 @@ class TestQueryUpdatableResultset(BaseTestGenerator): def _initialize_database_connection(self): database_info = parent_node_dict["database"][-1] + self.db_name = database_info["db_name"] self.server_id = database_info["server_id"] self.server_version = parent_node_dict["schema"][-1]["server_version"] @@ -128,6 +129,12 @@ class TestQueryUpdatableResultset(BaseTestGenerator): self.skipTest('Tables with OIDs are not supported starting ' 'PostgreSQL 12') + driver_version = utils.get_driver_version() + driver_version = float('.'.join(driver_version.split('.')[:2])) + + if driver_version < 2.8: + self.skipTest('Updatable resultsets require pyscopg 2.8 or later') + self.db_id = database_info["db_id"] db_con = database_utils.connect_database(self, utils.SERVER_GROUP, @@ -172,10 +179,5 @@ class TestQueryUpdatableResultset(BaseTestGenerator): else: create_sql += ';' - is_success, _ = \ - execute_query(tester=self.tester, - query=create_sql, - start_query_tool_url=self.start_query_tool_url, - poll_url=self.poll_url) - self.assertEquals(is_success, True) + utils.create_table_with_query(self.server, self.db_name, create_sql) return test_table_name diff --git a/web/pgadmin/tools/sqleditor/tests/test_save_changed_data.py b/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py similarity index 96% rename from web/pgadmin/tools/sqleditor/tests/test_save_changed_data.py rename to web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py index 29e9cce3b..1745a9905 100644 --- a/web/pgadmin/tools/sqleditor/tests/test_save_changed_data.py +++ b/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py @@ -15,11 +15,11 @@ from pgadmin.browser.server_groups.servers.databases.tests import utils as \ from pgadmin.utils.route import BaseTestGenerator from regression import parent_node_dict from regression.python_test_utils import test_utils as utils -from .execute_query_utils import execute_query +from pgadmin.tools.sqleditor.tests.execute_query_utils import execute_query class TestSaveChangedData(BaseTestGenerator): - """ This class tests saving data changes in the grid to the database """ + """ This class tests saving data changes to updatable query resultsets """ scenarios = [ ('When inserting new valid row', dict( save_payload={ @@ -304,6 +304,7 @@ class TestSaveChangedData(BaseTestGenerator): def _initialize_database_connection(self): database_info = parent_node_dict["database"][-1] + self.db_name = database_info["db_name"] self.server_id = database_info["server_id"] self.db_id = database_info["db_id"] @@ -311,6 +312,13 @@ class TestSaveChangedData(BaseTestGenerator): utils.SERVER_GROUP, self.server_id, self.db_id) + + driver_version = utils.get_driver_version() + driver_version = float('.'.join(driver_version.split('.')[:2])) + + if driver_version < 2.8: + self.skipTest('Updatable resultsets require pyscopg 2.8 or later') + if not db_con["info"] == "Database connected.": raise Exception("Could not connect to the database.") @@ -347,9 +355,5 @@ class TestSaveChangedData(BaseTestGenerator): self.test_table_name, self.test_table_name) self.select_sql = 'SELECT * FROM %s;' % self.test_table_name - is_success, _ = \ - execute_query(tester=self.tester, - query=create_sql, - start_query_tool_url=self.start_query_tool_url, - poll_url=self.poll_url) - self.assertEquals(is_success, True) + + utils.create_table_with_query(self.server, self.db_name, create_sql) diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py index ea24456c7..54be70a98 100644 --- a/web/regression/python_test_utils/test_utils.py +++ b/web/regression/python_test_utils/test_utils.py @@ -1080,3 +1080,8 @@ def get_watcher_dialogue_status(self): else: break return status + + +def get_driver_version(): + version = getattr(psycopg2, '__version__', None) + return version