Fixed an issue where the 'Quote strings only' configuration was ignored when downloading the result set. #7578

pull/9606/head
Khushboo Vashi 2026-02-17 17:19:41 +05:30 committed by GitHub
parent f2756a3dcf
commit 5002419611
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 26 additions and 8 deletions

View File

@ -29,11 +29,12 @@ class TestDownloadCSV(BaseTestGenerator):
(
'Download csv URL with valid query',
dict(
sql='SELECT 1 as "A",2 as "B",3 as "C"',
sql='SELECT 1 as "A",2 as "B",3 as "C",2300::numeric'
' as "Price"',
init_url='/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}',
donwload_url="/sqleditor/query_tool/download/{0}",
output_columns='"A","B","C"',
output_values='1,2,3',
output_columns='"A","B","C","Price"',
output_values='1,2,3,2300',
is_valid_tx=True,
is_valid=True,
download_as_txt=False,
@ -205,8 +206,8 @@ class TestDownloadCSV(BaseTestGenerator):
# when valid query
self.assertEqual(response.status_code, 200)
csv_data = response.data.decode()
self.assertTrue(self.output_columns in csv_data)
self.assertTrue(self.output_values in csv_data)
self.assertIn(self.output_columns, csv_data)
self.assertIn(self.output_values, csv_data)
self.assertIn('text/csv', headers['Content-Type'])
self.assertIn(self.filename, headers['Content-Disposition'])
elif not self.is_valid and self.is_valid_tx:
@ -214,8 +215,8 @@ class TestDownloadCSV(BaseTestGenerator):
self.assertEqual(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.assertFalse(response_data['data']['status'])
self.assertTrue(
'relation "this_table_does_not_exist" does not exist' in
self.assertIn(
'relation "this_table_does_not_exist" does not exist',
response_data['data']['result']
)
else:

View File

@ -34,7 +34,8 @@ from ..abstract import BaseConnection
from .cursor import DictCursor, AsyncDictCursor, AsyncDictServerCursor
from .typecast import register_global_typecasters,\
register_string_typecasters, register_binary_typecasters, \
register_array_to_string_typecasters, ALL_JSON_TYPES
register_array_to_string_typecasters, ALL_JSON_TYPES, \
register_numeric_typecasters
from .encoding import get_encoding, configure_driver_encodings
from pgadmin.utils import csv_lib as csv
from pgadmin.utils.master_password import get_crypt_key
@ -903,6 +904,8 @@ WHERE db.datname = current_database()""")
cur.scroll(0, mode='absolute')
except Exception as e:
print(str(e))
# Make sure numeric values will be fetched without quoting
register_numeric_typecasters(cur)
results = cur.fetchmany(records)
if not results:
yield gettext('The query executed did not return any data.')

View File

@ -17,6 +17,7 @@ from psycopg.types.json import JsonDumper, _JsonDumper, _JsonLoader
from psycopg._encodings import py_codecs as encodings
from .encoding import get_encoding, configure_driver_encodings
from psycopg.types.net import InetLoader
from psycopg.types.numeric import NumericLoader, IntLoader, FloatLoader
from psycopg.adapt import Loader
from ipaddress import ip_address, ip_interface
from psycopg._encodings import py_codecs as encodings
@ -168,6 +169,19 @@ def register_string_typecasters(connection):
connection.adapters.register_loader(typ, TextLoaderpgAdmin)
def register_numeric_typecasters(_cursor):
# These original loader registration works on cursor level
_cursor.adapters.register_loader(1700,
NumericLoader)
_cursor.adapters.register_loader(700,
FloatLoader)
_cursor.adapters.register_loader(701,
FloatLoader)
_cursor.adapters.register_loader(20,
IntLoader)
def register_binary_typecasters(connection):
# The new classes can be registered globally, on a connection, on a cursor