diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py index 3e4549cb1..9c6423ebc 100644 --- a/web/pgadmin/tools/sqleditor/command.py +++ b/web/pgadmin/tools/sqleditor/command.py @@ -23,6 +23,7 @@ from pgadmin.tools.sqleditor.utils.is_query_resultset_updatable \ import is_query_resultset_updatable from pgadmin.tools.sqleditor.utils.save_changed_data import save_changed_data from pgadmin.tools.sqleditor.utils.get_column_types import get_columns_types +from pgadmin.utils.preferences import Preferences from config import PG_DEFAULT_DRIVER @@ -173,6 +174,9 @@ class SQLFilter(object): self._data_sorting = kwargs.get('data_sorting', None) self._set_sorting_from_filter_dialog = False + data_sorting_by_pk = Preferences.module('sqleditor').preference( + 'table_view_data_by_pk').get() + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(self.sid) conn = manager.connection(did=self.did) @@ -180,24 +184,40 @@ class SQLFilter(object): self.sql_path = 'sqleditor/sql/#{0}#'.format(manager.version) if conn.connected(): - # Fetch the Namespace Name and object Name - query = render_template( - "/".join([self.sql_path, 'objectname.sql']), - obj_id=self.obj_id - ) + rows = self._get_result_set(conn, data_sorting_by_pk) + + # Check if primary key/s exists + if len(rows) > 0 and data_sorting_by_pk: + sorting = [] + for row in rows: + sorting.append({'name': row['attname'], 'order': 'asc'}) + self._data_sorting = kwargs.get('data_sorting', sorting) + else: + rows = self._get_result_set(conn) - status, result = conn.execute_dict(query) - if not status: - raise Exception(result) + self.nsp_name = rows[0]['nspname'] + self.object_name = rows[0]['relname'] - self.nsp_name = result['rows'][0]['nspname'] - self.object_name = result['rows'][0]['relname'] else: raise Exception(gettext( 'Not connected to server or connection with the server ' 'has been closed.') ) + def _get_result_set(self, conn, data_sorting_by_pk=False): + # Fetch the Namespace Name and object Name + query = render_template( + "/".join([self.sql_path, 'objectname.sql']), + obj_id=self.obj_id, + pk=data_sorting_by_pk + ) + + status, result = conn.execute_dict(query) + if not status: + raise Exception(result) + + return result['rows'] + def get_filter(self): """ This function returns the filter. diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectname.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectname.sql index 188e69db2..d23e1ad5e 100644 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectname.sql +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectname.sql @@ -1,5 +1,16 @@ {# ============= Fetch the schema and object name for given object id ============= #} -{% if obj_id %} +{% if obj_id and pk %} +SELECT pg_attribute.attname, n.nspname, r.relname +FROM pg_attribute, pg_index, pg_class r + LEFT JOIN pg_namespace n ON (r.relnamespace = n.oid) +WHERE r.oid = {{obj_id}} AND +indrelid = r.oid AND +r.relnamespace = n.oid AND +pg_attribute.attrelid = r.oid AND +pg_attribute.attnum = any(pg_index.indkey) AND +indisprimary; +{% endif %} +{% if obj_id and not pk %} SELECT n.nspname, r.relname FROM pg_class r LEFT JOIN pg_namespace n ON (r.relnamespace = n.oid) diff --git a/web/pgadmin/tools/sqleditor/tests/test_view_data.py b/web/pgadmin/tools/sqleditor/tests/test_view_data.py index 3466e5c7c..9e2119386 100644 --- a/web/pgadmin/tools/sqleditor/tests/test_view_data.py +++ b/web/pgadmin/tools/sqleditor/tests/test_view_data.py @@ -10,6 +10,7 @@ import uuid import json import random +import sys from pgadmin.utils.route import BaseTestGenerator from pgadmin.browser.server_groups.servers.databases.tests import utils as \ @@ -18,6 +19,11 @@ from regression import parent_node_dict from regression.python_test_utils import test_utils from pgadmin.utils import server_utils, IS_PY2 +if sys.version_info < (3, 3): + from mock import patch +else: + from unittest.mock import patch + class TestViewData(BaseTestGenerator): """ @@ -36,6 +42,34 @@ class TestViewData(BaseTestGenerator): result_data='SELECT 0', rows_fetched_to=0 ) + ), + ( + 'Sort table data without primary key in the table', + dict( + table_sql="""Create Table ( + id integer Not Null, + json_val json Not Null + );""", + result_data='SELECT 0', + rows_fetched_to=0 + ) + ), + ( + 'Sort table data by default order with primary key in table', + dict( + table_sql="""Create Table ( + id integer Not Null, + json_val json Not Null, + Constraint table_pk_sort Primary Key(id) + );""", + result_data='SELECT 0', + rows_fetched_to=0, + mock_data={ + 'function_to_be_mocked': "pgadmin.utils.preferences." + "_Preference.get", + 'return_value': False + } + ) ) ] @@ -71,10 +105,18 @@ class TestViewData(BaseTestGenerator): # Initialize query tool self.trans_id = str(random.randint(1, 9999999)) - url = '/datagrid/initialize/datagrid/{0}/3/table/{1}/{2}/{3}/{4}'\ + url = '/datagrid/initialize/datagrid/{0}/3/table/{1}/{2}/{3}/{4}' \ .format(self.trans_id, test_utils.SERVER_GROUP, self.server_id, self.db_id, table_id) - response = self.tester.post(url) + + if hasattr(self, 'mock_data'): + with patch( + self.mock_data['function_to_be_mocked'], + return_value=self.mock_data['return_value'] + ): + response = self.tester.post(url) + else: + response = self.tester.post(url) self.assertEquals(response.status_code, 200) diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py index a4d74e860..6b47de0e9 100644 --- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py +++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py @@ -100,6 +100,14 @@ def RegisterQueryToolPreferences(self): ) ) + self.table_view_data_by_pk = self.preference.register( + 'Options', 'table_view_data_by_pk', + gettext("Table data sorting (by primary key)"), 'boolean', True, + category_label=gettext('Options'), + help_str=gettext("If set to True, table data under sqleditor " + "will sort by primary key") + ) + self.show_prompt_save_data_changes = self.preference.register( 'Options', 'prompt_save_data_changes', gettext("Prompt to save unsaved data changes?"), 'boolean', True,