Skip to content

Commit 5002419

Browse files
Fixed an issue where the 'Quote strings only' configuration was ignored when downloading the result set. #7578
1 parent f2756a3 commit 5002419

3 files changed

Lines changed: 26 additions & 8 deletions

File tree

web/pgadmin/tools/sqleditor/tests/test_download_csv_query_tool.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ class TestDownloadCSV(BaseTestGenerator):
2929
(
3030
'Download csv URL with valid query',
3131
dict(
32-
sql='SELECT 1 as "A",2 as "B",3 as "C"',
32+
sql='SELECT 1 as "A",2 as "B",3 as "C",2300::numeric'
33+
' as "Price"',
3334
init_url='/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}',
3435
donwload_url="/sqleditor/query_tool/download/{0}",
35-
output_columns='"A","B","C"',
36-
output_values='1,2,3',
36+
output_columns='"A","B","C","Price"',
37+
output_values='1,2,3,2300',
3738
is_valid_tx=True,
3839
is_valid=True,
3940
download_as_txt=False,
@@ -205,17 +206,17 @@ def runTest(self):
205206
# when valid query
206207
self.assertEqual(response.status_code, 200)
207208
csv_data = response.data.decode()
208-
self.assertTrue(self.output_columns in csv_data)
209-
self.assertTrue(self.output_values in csv_data)
209+
self.assertIn(self.output_columns, csv_data)
210+
self.assertIn(self.output_values, csv_data)
210211
self.assertIn('text/csv', headers['Content-Type'])
211212
self.assertIn(self.filename, headers['Content-Disposition'])
212213
elif not self.is_valid and self.is_valid_tx:
213214
# When user enters wrong query
214215
self.assertEqual(response.status_code, 200)
215216
response_data = json.loads(response.data.decode('utf-8'))
216217
self.assertFalse(response_data['data']['status'])
217-
self.assertTrue(
218-
'relation "this_table_does_not_exist" does not exist' in
218+
self.assertIn(
219+
'relation "this_table_does_not_exist" does not exist',
219220
response_data['data']['result']
220221
)
221222
else:

web/pgadmin/utils/driver/psycopg3/connection.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
from .cursor import DictCursor, AsyncDictCursor, AsyncDictServerCursor
3535
from .typecast import register_global_typecasters,\
3636
register_string_typecasters, register_binary_typecasters, \
37-
register_array_to_string_typecasters, ALL_JSON_TYPES
37+
register_array_to_string_typecasters, ALL_JSON_TYPES, \
38+
register_numeric_typecasters
3839
from .encoding import get_encoding, configure_driver_encodings
3940
from pgadmin.utils import csv_lib as csv
4041
from pgadmin.utils.master_password import get_crypt_key
@@ -903,6 +904,8 @@ def gen(conn_obj, trans_obj, quote='strings', quote_char="'",
903904
cur.scroll(0, mode='absolute')
904905
except Exception as e:
905906
print(str(e))
907+
# Make sure numeric values will be fetched without quoting
908+
register_numeric_typecasters(cur)
906909
results = cur.fetchmany(records)
907910
if not results:
908911
yield gettext('The query executed did not return any data.')

web/pgadmin/utils/driver/psycopg3/typecast.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from psycopg._encodings import py_codecs as encodings
1818
from .encoding import get_encoding, configure_driver_encodings
1919
from psycopg.types.net import InetLoader
20+
from psycopg.types.numeric import NumericLoader, IntLoader, FloatLoader
2021
from psycopg.adapt import Loader
2122
from ipaddress import ip_address, ip_interface
2223
from psycopg._encodings import py_codecs as encodings
@@ -168,6 +169,19 @@ def register_string_typecasters(connection):
168169
connection.adapters.register_loader(typ, TextLoaderpgAdmin)
169170

170171

172+
def register_numeric_typecasters(_cursor):
173+
# These original loader registration works on cursor level
174+
175+
_cursor.adapters.register_loader(1700,
176+
NumericLoader)
177+
_cursor.adapters.register_loader(700,
178+
FloatLoader)
179+
_cursor.adapters.register_loader(701,
180+
FloatLoader)
181+
_cursor.adapters.register_loader(20,
182+
IntLoader)
183+
184+
171185
def register_binary_typecasters(connection):
172186
# The new classes can be registered globally, on a connection, on a cursor
173187

0 commit comments

Comments
 (0)