55import uuid
66
77import aiopg
8- from aiopg .sa .engine import APGCompiler_psycopg2
9- from sqlalchemy .dialects .postgresql .psycopg2 import PGDialect_psycopg2
108from sqlalchemy .engine .cursor import CursorResultMetaData
119from sqlalchemy .engine .interfaces import Dialect , ExecutionContext
1210from sqlalchemy .engine .row import Row
1311from sqlalchemy .sql import ClauseElement
1412from sqlalchemy .sql .ddl import DDLElement
1513
16- from databases .core import DatabaseURL
14+ from databases .backends .common .records import Record , Row , create_column_maps
15+ from databases .backends .compilers .psycopg import PGCompiler_psycopg
16+ from databases .backends .dialects .psycopg import PGDialect_psycopg
17+ from databases .core import LOG_EXTRA , DatabaseURL
1718from databases .interfaces import (
1819 ConnectionBackend ,
1920 DatabaseBackend ,
20- Record ,
21+ Record as RecordInterface ,
2122 TransactionBackend ,
2223)
2324
@@ -34,10 +35,10 @@ def __init__(
3435 self ._pool : typing .Union [aiopg .Pool , None ] = None
3536
3637 def _get_dialect (self ) -> Dialect :
37- dialect = PGDialect_psycopg2 (
38+ dialect = PGDialect_psycopg (
3839 json_serializer = json .dumps , json_deserializer = lambda x : x
3940 )
40- dialect .statement_compiler = APGCompiler_psycopg2
41+ dialect .statement_compiler = PGCompiler_psycopg
4142 dialect .implicit_returning = True
4243 dialect .supports_native_enum = True
4344 dialect .supports_smallserial = True # 9.2+
@@ -117,50 +118,55 @@ async def release(self) -> None:
117118 await self ._database ._pool .release (self ._connection )
118119 self ._connection = None
119120
120- async def fetch_all (self , query : ClauseElement ) -> typing .List [Record ]:
121+ async def fetch_all (self , query : ClauseElement ) -> typing .List [RecordInterface ]:
121122 assert self ._connection is not None , "Connection is not acquired"
122- query_str , args , context = self ._compile (query )
123+ query_str , args , result_columns , context = self ._compile (query )
124+ column_maps = create_column_maps (result_columns )
125+ dialect = self ._dialect
126+
123127 cursor = await self ._connection .cursor ()
124128 try :
125129 await cursor .execute (query_str , args )
126130 rows = await cursor .fetchall ()
127131 metadata = CursorResultMetaData (context , cursor .description )
128- return [
132+ rows = [
129133 Row (
130134 metadata ,
131135 metadata ._processors ,
132136 metadata ._keymap ,
133- Row ._default_key_style ,
134137 row ,
135138 )
136139 for row in rows
137140 ]
141+ return [Record (row , result_columns , dialect , column_maps ) for row in rows ]
138142 finally :
139143 cursor .close ()
140144
141- async def fetch_one (self , query : ClauseElement ) -> typing .Optional [Record ]:
145+ async def fetch_one (self , query : ClauseElement ) -> typing .Optional [RecordInterface ]:
142146 assert self ._connection is not None , "Connection is not acquired"
143- query_str , args , context = self ._compile (query )
147+ query_str , args , result_columns , context = self ._compile (query )
148+ column_maps = create_column_maps (result_columns )
149+ dialect = self ._dialect
144150 cursor = await self ._connection .cursor ()
145151 try :
146152 await cursor .execute (query_str , args )
147153 row = await cursor .fetchone ()
148154 if row is None :
149155 return None
150156 metadata = CursorResultMetaData (context , cursor .description )
151- return Row (
157+ row = Row (
152158 metadata ,
153159 metadata ._processors ,
154160 metadata ._keymap ,
155- Row ._default_key_style ,
156161 row ,
157162 )
163+ return Record (row , result_columns , dialect , column_maps )
158164 finally :
159165 cursor .close ()
160166
161167 async def execute (self , query : ClauseElement ) -> typing .Any :
162168 assert self ._connection is not None , "Connection is not acquired"
163- query_str , args , context = self ._compile (query )
169+ query_str , args , _ , _ = self ._compile (query )
164170 cursor = await self ._connection .cursor ()
165171 try :
166172 await cursor .execute (query_str , args )
@@ -173,7 +179,7 @@ async def execute_many(self, queries: typing.List[ClauseElement]) -> None:
173179 cursor = await self ._connection .cursor ()
174180 try :
175181 for single_query in queries :
176- single_query , args , context = self ._compile (single_query )
182+ single_query , args , _ , _ = self ._compile (single_query )
177183 await cursor .execute (single_query , args )
178184 finally :
179185 cursor .close ()
@@ -182,36 +188,37 @@ async def iterate(
182188 self , query : ClauseElement
183189 ) -> typing .AsyncGenerator [typing .Any , None ]:
184190 assert self ._connection is not None , "Connection is not acquired"
185- query_str , args , context = self ._compile (query )
191+ query_str , args , result_columns , context = self ._compile (query )
192+ column_maps = create_column_maps (result_columns )
193+ dialect = self ._dialect
186194 cursor = await self ._connection .cursor ()
187195 try :
188196 await cursor .execute (query_str , args )
189197 metadata = CursorResultMetaData (context , cursor .description )
190198 async for row in cursor :
191- yield Row (
199+ record = Row (
192200 metadata ,
193201 metadata ._processors ,
194202 metadata ._keymap ,
195- Row ._default_key_style ,
196203 row ,
197204 )
205+ yield Record (record , result_columns , dialect , column_maps )
198206 finally :
199207 cursor .close ()
200208
201209 def transaction (self ) -> TransactionBackend :
202210 return AiopgTransaction (self )
203211
204- def _compile (
205- self , query : ClauseElement
206- ) -> typing .Tuple [str , dict , CompilationContext ]:
212+ def _compile (self , query : ClauseElement ) -> typing .Tuple [str , list , tuple ]:
207213 compiled = query .compile (
208214 dialect = self ._dialect , compile_kwargs = {"render_postcompile" : True }
209215 )
210-
211216 execution_context = self ._dialect .execution_ctx_cls ()
212217 execution_context .dialect = self ._dialect
213218
214219 if not isinstance (query , DDLElement ):
220+ compiled_params = sorted (compiled .params .items ())
221+
215222 args = compiled .construct_params ()
216223 for key , val in args .items ():
217224 if key in compiled ._bind_processors :
@@ -224,11 +231,23 @@ def _compile(
224231 compiled ._ad_hoc_textual ,
225232 compiled ._loose_column_name_matching ,
226233 )
234+
235+ mapping = {
236+ key : "$" + str (i ) for i , (key , _ ) in enumerate (compiled_params , start = 1 )
237+ }
238+ compiled_query = compiled .string % mapping
239+ result_map = compiled ._result_columns
240+
227241 else :
228242 args = {}
243+ result_map = None
244+ compiled_query = compiled .string
229245
230- logger .debug ("Query: %s\n Args: %s" , compiled .string , args )
231- return compiled .string , args , CompilationContext (execution_context )
246+ query_message = compiled_query .replace (" \n " , " " ).replace ("\n " , " " )
247+ logger .debug (
248+ "Query: %s Args: %s" , query_message , repr (tuple (args )), extra = LOG_EXTRA
249+ )
250+ return compiled .string , args , result_map , CompilationContext (execution_context )
232251
233252 @property
234253 def raw_connection (self ) -> aiopg .connection .Connection :
0 commit comments