44
55import aiohttp
66
7- from .compiler import Compiler
7+ from .compiler import Compiler , Statement
88from .dialect import ClickhouseSaDialect
9- from .exc import DBException , ProtocolError
10- from .parser import parse_json_compact
9+ from .exc import DBException , ProtocolError , exc_message_re
10+ from .parser import parse_json_compact , JSONDecodeError
1111from .record import Record
1212from .types import TypeRegistry
1313
@@ -43,21 +43,26 @@ def __init__(
4343 self ._types = types
4444 self ._compiler = Compiler (dialect = dialect , escape = types .escape )
4545
46- async def _execute (self , statement : str , * args ) -> Iterable [Record ]:
47- query , json_each_row_parameters = self ._compiler .compile_statement (
46+ async def _execute (self , statement : Statement , * args ) -> Iterable [Record ]:
47+ compiled , json_each_row_parameters = self ._compiler .compile_statement (
4848 statement , args ,
4949 )
50+ sql_logger .debug (compiled )
51+ compiled_with_params = compiled
52+ rows = None
5053 if json_each_row_parameters :
5154 to_json = self ._types .to_json # lookup optimization
52- query += '\n '
53- query += '\n ' .join (
55+ rows = [
5456 json .dumps (
5557 {name : to_json (value ) for name , value in row .items ()},
5658 use_decimal = True ,
5759 )
5860 for row in json_each_row_parameters
59- )
60- sql_logger .debug (query )
61+ ]
62+ if sql_logger .isEnabledFor (logging .DEBUG ):
63+ for idx , row in enumerate (rows ):
64+ sql_logger .debug (f'{ idx } : { row } ' )
65+ compiled_with_params += '\n ' + '\n ' .join (rows )
6166
6267 # First attempt may fail due to broken state of aiohttp session
6368 # (aiohttp doesn't handle connection closing properly?)
@@ -66,18 +71,27 @@ async def _execute(self, statement: str, *args) -> Iterable[Record]:
6671 async with self ._session .post (
6772 self .url ,
6873 params = {'default_format' : 'JSONCompact' , ** self .params },
69- data = query .encode (),
74+ data = compiled_with_params .encode (),
7075 ) as response :
76+ body = await response .read ()
7177 if response .status != 200 :
72- body = await response .read ()
7378 raise DBException .from_message (
74- query , body .decode (errors = 'replace' ),
79+ body .decode (errors = 'replace' ),
80+ statement = compiled , rows = rows ,
7581 )
7682
77- if response .content_type == 'application/json' :
78- return await parse_json_compact (
79- self ._types , response .content ,
80- )
83+ elif response .content_type == 'application/json' :
84+ try :
85+ return parse_json_compact (self ._types , body )
86+ except JSONDecodeError :
87+ body_str = body .decode (errors = 'replace' )
88+ m = exc_message_re .search (body_str )
89+ if not m :
90+ raise
91+ raise DBException .from_message (
92+ body_str [m .start ():],
93+ statement = compiled , rows = rows ,
94+ )
8195 else :
8296 return ()
8397 except aiohttp .ClientError as exc :
@@ -86,22 +100,22 @@ async def _execute(self, statement: str, *args) -> Iterable[Record]:
86100 logger .debug (f'First attempt failed, retrying (error: { exc } )' )
87101
88102 async def iterate (
89- self , statement : str , * args ,
103+ self , statement : Statement , * args ,
90104 ) -> AsyncGenerator [Record , None ]:
91105 for row in await self ._execute (statement , * args ):
92106 yield row
93107
94- async def execute (self , statement : str , * args ) -> None :
108+ async def execute (self , statement : Statement , * args ) -> None :
95109 await self ._execute (statement , * args )
96110
97- async def fetch (self , statement : str , * args ) -> List [Record ]:
111+ async def fetch (self , statement : Statement , * args ) -> List [Record ]:
98112 return list (await self ._execute (statement , * args ))
99113
100- async def fetchrow (self , statement : str , * args ) -> Optional [Record ]:
114+ async def fetchrow (self , statement : Statement , * args ) -> Optional [Record ]:
101115 gen = await self ._execute (statement , * args )
102116 return next (iter (gen ), None )
103117
104- async def fetchval (self , statement : str , * args ) -> Any :
118+ async def fetchval (self , statement : Statement , * args ) -> Any :
105119 row = await self .fetchrow (statement , * args )
106120 if row is not None :
107121 return row [0 ]
0 commit comments