Skip to content

Commit f75291a

Browse files
committed
Fixed an issue when the server/database connection was lost; the filter dialog is not getting saved. #6044
1 parent 99e1f00 commit f75291a

File tree

6 files changed

+146
-65
lines changed

6 files changed

+146
-65
lines changed

web/pgadmin/tools/sqleditor/__init__.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from flask import Response, url_for, render_template, session, current_app
2424
from flask import request
2525
from flask_babel import gettext
26+
from pgadmin.tools.sqleditor.utils.query_tool_connection_check \
27+
import query_tool_connection_check
2628
from pgadmin.user_login_check import pga_login_required
2729
from flask_security import current_user
2830
from pgadmin.misc.file_manager import Filemanager
@@ -819,13 +821,14 @@ def start_view_data(trans_id):
819821

820822
# Connect to the Server if not connected.
821823
if not default_conn.connected():
822-
view = SchemaDiffRegistry.get_node_view('server')
823-
response = view.connect(trans_obj.sgid,
824-
trans_obj.sid, True)
825-
if response.status_code == 428:
824+
# This will check if view/edit data tool connection is lost or not,
825+
# if lost then it will reconnect
826+
status, error_msg, conn, trans_obj, session_obj, response = \
827+
query_tool_connection_check(trans_id)
828+
# This is required for asking user to enter password
829+
# when password is not saved for the server
830+
if response is not None:
826831
return response
827-
else:
828-
conn = manager.connection(did=trans_obj.did)
829832

830833
status, msg = default_conn.connect()
831834
if not status:
@@ -839,6 +842,9 @@ def start_view_data(trans_id):
839842
# set fetched row count to 0 as we are executing query again.
840843
trans_obj.update_fetched_row_cnt(0)
841844

845+
# Fetch the columns for the table and store it in session
846+
trans_obj.fetch_all_columns(conn)
847+
842848
# Fetch the sql and primary_keys from the object
843849
sql = trans_obj.get_sql(default_conn)
844850
_, primary_keys = trans_obj.get_primary_keys(default_conn)
@@ -2264,7 +2270,6 @@ def get_filter_data(trans_id):
22642270
Args:
22652271
trans_id: unique transaction id
22662272
"""
2267-
22682273
status, error_msg, conn, trans_obj, session_ob = \
22692274
check_transaction_status(trans_id)
22702275

web/pgadmin/tools/sqleditor/command.py

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,15 @@ def get_data_sorting(self):
228228
return self._data_sorting
229229
return None
230230

231+
def get_columns_list_with_order(self):
232+
"""
233+
This function returns the list of columns with order.
234+
"""
235+
if (self.all_columns_list_with_order_from_table and
236+
len(self.all_columns_list_with_order_from_table) > 0):
237+
return self.all_columns_list_with_order_from_table
238+
return None
239+
231240
def set_data_sorting(self, data_filter, set_from_filter_dialog=False):
232241
"""
233242
This function validates the filter and set the
@@ -470,11 +479,53 @@ def __init__(self, **kwargs):
470479
# call base class init to fetch the table name
471480
super().__init__(**kwargs)
472481

482+
self.all_columns_list_with_order_from_table = None
483+
473484
# Set the default sorting on table data by primary key if user
474485
# preference value is set
475486
self.data_sorting_by_pk = Preferences.module('sqleditor').preference(
476487
'table_view_data_by_pk').get()
477488

489+
def fetch_all_columns(self, conn):
490+
"""
491+
This function fetches the list of columns for the
492+
selected table and stores it locally.
493+
"""
494+
all_columns = []
495+
# Fetch the primary key column names
496+
query = render_template(
497+
"/".join([self.sql_path, 'primary_keys.sql']),
498+
table_name=self.object_name,
499+
table_nspname=self.nsp_name,
500+
conn=conn,
501+
)
502+
503+
status, result = conn.execute_dict(query)
504+
505+
if not status:
506+
raise ExecuteError(result)
507+
508+
for row in result['rows']:
509+
all_columns.append(row['attname'])
510+
511+
# Fetch the rest of the column names
512+
query = render_template(
513+
"/".join([self.sql_path, 'get_columns.sql']),
514+
table_name=self.object_name,
515+
table_nspname=self.nsp_name,
516+
conn=conn,
517+
)
518+
status, result = conn.execute_dict(query)
519+
if not status:
520+
raise ExecuteError(result)
521+
522+
for row in result['rows']:
523+
# Only append if not already present in the list
524+
if row['attname'] not in all_columns:
525+
all_columns.append(row['attname'])
526+
527+
self.all_columns_list_with_order_from_table = all_columns
528+
478529
def get_sql(self, default_conn=None):
479530
"""
480531
This method is used to create a proper SQL query
@@ -575,49 +626,17 @@ def get_all_columns_with_order(self, default_conn=None):
575626
all_sorted_columns: Sorted columns for the Grid
576627
all_columns: List of columns for the select2 options
577628
"""
578-
driver = get_driver(PG_DEFAULT_DRIVER)
579-
if default_conn is None:
580-
manager = driver.connection_manager(self.sid)
581-
conn = manager.connection(did=self.did, conn_id=self.conn_id)
582-
else:
583-
conn = default_conn
584629

585630
all_sorted_columns = []
586631
data_sorting = self.get_data_sorting()
587632
all_columns = []
588-
# Fetch the primary key column names
589-
query = render_template(
590-
"/".join([self.sql_path, 'primary_keys.sql']),
591-
table_name=self.object_name,
592-
table_nspname=self.nsp_name,
593-
conn=conn,
594-
)
633+
data_columns_list = self.get_columns_list_with_order()
595634

596-
status, result = conn.execute_dict(query)
635+
# Assigns the list of columns from session for the table
636+
if data_columns_list and len(data_columns_list) > 0:
637+
all_columns = data_columns_list
597638

598-
if not status:
599-
raise ExecuteError(result)
600-
601-
for row in result['rows']:
602-
all_columns.append(row['attname'])
603-
604-
# Fetch the rest of the column names
605-
query = render_template(
606-
"/".join([self.sql_path, 'get_columns.sql']),
607-
table_name=self.object_name,
608-
table_nspname=self.nsp_name,
609-
conn=conn,
610-
)
611-
status, result = conn.execute_dict(query)
612-
if not status:
613-
raise ExecuteError(result)
614-
615-
for row in result['rows']:
616-
# Only append if not already present in the list
617-
if row['attname'] not in all_columns:
618-
all_columns.append(row['attname'])
619-
620-
# If user has custom data sorting then pass as it as it is
639+
# If user has custom data sorting then pass as it is
621640
if data_sorting and len(data_sorting) > 0:
622641
all_sorted_columns = data_sorting
623642

web/pgadmin/tools/sqleditor/utils/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
from .update_session_grid_transaction import update_session_grid_transaction
1313
from .start_running_query import *
1414
from .apply_explain_plan_wrapper import *
15+
from .query_tool_connection_check import *

web/pgadmin/tools/sqleditor/utils/filter_dialog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def get(*args):
3838

3939
try:
4040
columns, column_list = \
41-
trans_obj.get_all_columns_with_order(conn)
41+
trans_obj.get_all_columns_with_order()
4242
except (ConnectionLost, SSHTunnelConnectionLost):
4343
raise
4444
except Exception as e:
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
##########################################################################
2+
#
3+
# pgAdmin 4 - PostgreSQL Tools
4+
#
5+
# Copyright (C) 2013 - 2025, The pgAdmin Development Team
6+
# This software is released under the PostgreSQL Licence
7+
#
8+
##########################################################################
9+
10+
"""Check for query tool connection"""
11+
import pickle
12+
from flask_babel import gettext
13+
14+
from config import PG_DEFAULT_DRIVER
15+
from pgadmin.utils.ajax import internal_server_error
16+
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
17+
from pgadmin.tools.sqleditor.utils.start_running_query import StartRunningQuery
18+
from flask import Response, current_app, session
19+
20+
from pgadmin.utils.driver import get_driver
21+
22+
23+
def query_tool_connection_check(trans_id):
24+
# This function will check if the query tool has the connection or not
25+
# if not then establishes the connection.
26+
session_obj = StartRunningQuery.retrieve_session_information(
27+
session,
28+
trans_id
29+
)
30+
if isinstance(session_obj, Response):
31+
return session_obj
32+
33+
transaction_object = pickle.loads(session_obj['command_obj'])
34+
35+
# To verify if the transaction details for the specific query tool
36+
# or View/Edit Data tool is available or not and if the server is
37+
# disconnected from the Object Explorer then it reconnects
38+
if transaction_object is not None and session_obj is not None:
39+
view = SchemaDiffRegistry.get_node_view('server')
40+
response = view.connect(transaction_object.sgid,
41+
transaction_object.sid, True)
42+
# This is required for asking user to enter password
43+
# when password is not saved for the server
44+
if response.status_code == 428:
45+
return False, None, None, None, None, response
46+
else:
47+
manager = get_driver(
48+
PG_DEFAULT_DRIVER).connection_manager(
49+
transaction_object.sid)
50+
conn = manager.connection(
51+
did=transaction_object.did,
52+
conn_id=transaction_object.conn_id,
53+
auto_reconnect=False,
54+
use_binary_placeholder=True,
55+
array_to_string=True,
56+
**({"database": transaction_object.dbname} if hasattr(
57+
transaction_object, 'dbname') else {}))
58+
59+
status, msg = conn.connect()
60+
if not status:
61+
current_app.logger.error(msg)
62+
return internal_server_error(errormsg=str(msg))
63+
return status, None, conn, transaction_object, session_obj, None
64+
else:
65+
status = False
66+
error_msg = gettext(
67+
'Either transaction object or session object not found.')
68+
return status, error_msg, None, None, None, None

web/pgadmin/tools/sqleditor/utils/start_running_query.py

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
2929
CryptKeyMissing
3030
from pgadmin.utils.constants import ERROR_MSG_TRANS_ID_NOT_FOUND
31-
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
3231

3332

3433
class StartRunningQuery:
@@ -82,26 +81,15 @@ def execute(self, sql, trans_id, http_session, connect=False):
8281

8382
# Connect to the Server if not connected.
8483
if connect and not conn.connected():
85-
view = SchemaDiffRegistry.get_node_view('server')
86-
response = view.connect(transaction_object.sgid,
87-
transaction_object.sid, True)
88-
if response.status_code == 428:
84+
from pgadmin.tools.sqleditor.utils import \
85+
query_tool_connection_check
86+
87+
_, _, _, _, _, response = \
88+
query_tool_connection_check(trans_id)
89+
# This is required for asking user to enter password
90+
# when password is not saved for the server
91+
if response is not None:
8992
return response
90-
else:
91-
conn = manager.connection(
92-
did=transaction_object.did,
93-
conn_id=self.connection_id,
94-
auto_reconnect=False,
95-
use_binary_placeholder=True,
96-
array_to_string=True,
97-
**({"database": transaction_object.dbname} if hasattr(
98-
transaction_object, 'dbname') else {}))
99-
100-
status, msg = conn.connect()
101-
if not status:
102-
self.logger.error(msg)
103-
return internal_server_error(errormsg=str(msg))
104-
10593
effective_sql_statement = apply_explain_plan_wrapper_if_needed(
10694
manager, sql)
10795

0 commit comments

Comments
 (0)