Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -205,17 +206,17 @@ def runTest(self):
# 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:
# When user enters wrong query
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:
Expand Down
5 changes: 4 additions & 1 deletion web/pgadmin/utils/driver/psycopg3/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
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
Expand Down Expand Up @@ -903,6 +904,8 @@ def gen(conn_obj, trans_obj, quote='strings', quote_char="'",
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.')
Expand Down
14 changes: 14 additions & 0 deletions web/pgadmin/utils/driver/psycopg3/typecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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
Expand Down Expand Up @@ -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

Expand Down
Loading