Skip to content

Commit ec59999

Browse files
Fix Psycopg3 Api Incompatibilities (#1197)
* Fix DBAPI2 wrappers to allow additional kwargs to executemany * Pass extra psycopg3 kwargs through executemany * Rename arguments in psycopg3 wrappers to match upstream * Add tests for psycopg3 returning param --------- Co-authored-by: Uma Annamalai <[email protected]>
1 parent 846536a commit ec59999

File tree

4 files changed

+52
-42
lines changed

4 files changed

+52
-42
lines changed

newrelic/hooks/database_dbapi2.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ def execute(self, sql, parameters=DEFAULT, *args, **kwargs):
5151
):
5252
return self.__wrapped__.execute(sql, **kwargs)
5353

54-
def executemany(self, sql, seq_of_parameters):
54+
def executemany(self, sql, seq_of_parameters, *args, **kwargs):
5555
try:
5656
seq_of_parameters = list(seq_of_parameters)
5757
parameters = seq_of_parameters[0]
5858
except (TypeError, IndexError):
5959
parameters = DEFAULT
60+
6061
if parameters is not DEFAULT:
6162
with DatabaseTrace(
6263
sql=sql,
@@ -66,7 +67,7 @@ def executemany(self, sql, seq_of_parameters):
6667
sql_parameters=parameters,
6768
source=self.__wrapped__.executemany,
6869
):
69-
return self.__wrapped__.executemany(sql, seq_of_parameters)
70+
return self.__wrapped__.executemany(sql, seq_of_parameters, *args, **kwargs)
7071
else:
7172
with DatabaseTrace(
7273
sql=sql,
@@ -75,7 +76,7 @@ def executemany(self, sql, seq_of_parameters):
7576
cursor_params=self._nr_cursor_params,
7677
source=self.__wrapped__.executemany,
7778
):
78-
return self.__wrapped__.executemany(sql, seq_of_parameters)
79+
return self.__wrapped__.executemany(sql, seq_of_parameters, *args, **kwargs)
7980

8081
def callproc(self, procname, parameters=DEFAULT):
8182
with DatabaseTrace(

newrelic/hooks/database_dbapi2_async.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ async def execute(self, sql, parameters=DEFAULT, *args, **kwargs):
4949
):
5050
return await self.__wrapped__.execute(sql, **kwargs)
5151

52-
async def executemany(self, sql, seq_of_parameters):
52+
async def executemany(self, sql, seq_of_parameters, *args, **kwargs):
5353
try:
5454
seq_of_parameters = list(seq_of_parameters)
5555
parameters = seq_of_parameters[0]
5656
except (TypeError, IndexError):
5757
parameters = DEFAULT
58+
5859
if parameters is not DEFAULT:
5960
with DatabaseTrace(
6061
sql=sql,
@@ -64,7 +65,7 @@ async def executemany(self, sql, seq_of_parameters):
6465
sql_parameters=parameters,
6566
source=self.__wrapped__.executemany,
6667
):
67-
return await self.__wrapped__.executemany(sql, seq_of_parameters)
68+
return await self.__wrapped__.executemany(sql, seq_of_parameters, *args, **kwargs)
6869
else:
6970
with DatabaseTrace(
7071
sql=sql,
@@ -73,7 +74,7 @@ async def executemany(self, sql, seq_of_parameters):
7374
cursor_params=self._nr_cursor_params,
7475
source=self.__wrapped__.executemany,
7576
):
76-
return await self.__wrapped__.executemany(sql, seq_of_parameters)
77+
return await self.__wrapped__.executemany(sql, seq_of_parameters, *args, **kwargs)
7778

7879
async def callproc(self, procname, parameters=DEFAULT):
7980
with DatabaseTrace(

newrelic/hooks/database_psycopg.py

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -87,49 +87,49 @@ def __enter__(self):
8787

8888
return self
8989

90-
def execute(self, sql, parameters=DEFAULT, *args, **kwargs):
91-
if hasattr(sql, "as_string"):
92-
sql = sql.as_string(self)
90+
def execute(self, query, params=DEFAULT, *args, **kwargs):
91+
if hasattr(query, "as_string"):
92+
query = query.as_string(self)
9393

94-
return super(CursorWrapper, self).execute(sql, parameters, *args, **kwargs)
94+
return super(CursorWrapper, self).execute(query, params, *args, **kwargs)
9595

96-
def executemany(self, sql, seq_of_parameters):
97-
if hasattr(sql, "as_string"):
98-
sql = sql.as_string(self)
96+
def executemany(self, query, params_seq, *args, **kwargs):
97+
if hasattr(query, "as_string"):
98+
query = query.as_string(self)
9999

100-
return super(CursorWrapper, self).executemany(sql, seq_of_parameters)
100+
return super(CursorWrapper, self).executemany(query, params_seq, *args, **kwargs)
101101

102102

103103
class ConnectionSaveParamsWrapper(DBAPI2ConnectionWrapper):
104104

105105
__cursor_wrapper__ = CursorWrapper
106106

107-
def execute(self, sql, parameters=DEFAULT, *args, **kwargs):
108-
if hasattr(sql, "as_string"):
109-
sql = sql.as_string(self)
107+
def execute(self, query, params=DEFAULT, *args, **kwargs):
108+
if hasattr(query, "as_string"):
109+
query = query.as_string(self)
110110

111-
if parameters is not DEFAULT:
111+
if params is not DEFAULT:
112112
with DatabaseTrace(
113-
sql=sql,
113+
sql=query,
114114
dbapi2_module=self._nr_dbapi2_module,
115115
connect_params=self._nr_connect_params,
116116
cursor_params=None,
117-
sql_parameters=parameters,
117+
sql_parameters=params,
118118
execute_params=(args, kwargs),
119119
source=self.__wrapped__.execute,
120120
):
121-
cursor = self.__wrapped__.execute(sql, parameters, *args, **kwargs)
121+
cursor = self.__wrapped__.execute(query, params, *args, **kwargs)
122122
else:
123123
with DatabaseTrace(
124-
sql=sql,
124+
sql=query,
125125
dbapi2_module=self._nr_dbapi2_module,
126126
connect_params=self._nr_connect_params,
127127
cursor_params=None,
128128
sql_parameters=None,
129129
execute_params=(args, kwargs),
130130
source=self.__wrapped__.execute,
131131
):
132-
cursor = self.__wrapped__.execute(sql, **kwargs)
132+
cursor = self.__wrapped__.execute(query, **kwargs)
133133

134134
return self.__cursor_wrapper__(cursor, self._nr_dbapi2_module, self._nr_connect_params, (args, kwargs))
135135

@@ -233,49 +233,49 @@ async def __aenter__(self):
233233

234234
return self
235235

236-
async def execute(self, sql, parameters=DEFAULT, *args, **kwargs):
237-
if hasattr(sql, "as_string"):
238-
sql = sql.as_string(self)
236+
async def execute(self, query, params=DEFAULT, *args, **kwargs):
237+
if hasattr(query, "as_string"):
238+
query = query.as_string(self)
239239

240-
return await super(AsyncCursorWrapper, self).execute(sql, parameters, *args, **kwargs)
240+
return await super(AsyncCursorWrapper, self).execute(query, params, *args, **kwargs)
241241

242-
async def executemany(self, sql, seq_of_parameters):
243-
if hasattr(sql, "as_string"):
244-
sql = sql.as_string(self)
242+
async def executemany(self, query, params_seq, *args, **kwargs):
243+
if hasattr(query, "as_string"):
244+
query = query.as_string(self)
245245

246-
return await super(AsyncCursorWrapper, self).executemany(sql, seq_of_parameters)
246+
return await super(AsyncCursorWrapper, self).executemany(query, params_seq, *args, **kwargs)
247247

248248

249249
class AsyncConnectionSaveParamsWrapper(DBAPI2AsyncConnectionWrapper):
250250

251251
__cursor_wrapper__ = AsyncCursorWrapper
252252

253-
async def execute(self, sql, parameters=DEFAULT, *args, **kwargs):
254-
if hasattr(sql, "as_string"):
255-
sql = sql.as_string(self)
253+
async def execute(self, query, params=DEFAULT, *args, **kwargs):
254+
if hasattr(query, "as_string"):
255+
query = query.as_string(self)
256256

257-
if parameters is not DEFAULT:
257+
if params is not DEFAULT:
258258
with DatabaseTrace(
259-
sql=sql,
259+
sql=query,
260260
dbapi2_module=self._nr_dbapi2_module,
261261
connect_params=self._nr_connect_params,
262262
cursor_params=None,
263-
sql_parameters=parameters,
263+
sql_parameters=params,
264264
execute_params=(args, kwargs),
265265
source=self.__wrapped__.execute,
266266
):
267-
cursor = await self.__wrapped__.execute(sql, parameters, *args, **kwargs)
267+
cursor = await self.__wrapped__.execute(query, params, *args, **kwargs)
268268
else:
269269
with DatabaseTrace(
270-
sql=sql,
270+
sql=query,
271271
dbapi2_module=self._nr_dbapi2_module,
272272
connect_params=self._nr_connect_params,
273273
cursor_params=None,
274274
sql_parameters=None,
275275
execute_params=(args, kwargs),
276276
source=self.__wrapped__.execute,
277277
):
278-
cursor = await self.__wrapped__.execute(sql, **kwargs)
278+
cursor = await self.__wrapped__.execute(query, **kwargs)
279279

280280
return self.__cursor_wrapper__(cursor, self._nr_dbapi2_module, self._nr_connect_params, (args, kwargs))
281281

tests/datastore_psycopg/test_cursor.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,17 @@ async def _execute(connection, cursor, row_type, wrapper):
9999
sql = "create table %s (a integer, b real, c text)" % DB_SETTINGS["table_name"]
100100
await maybe_await(cursor.execute(wrapper(sql)))
101101

102-
sql = "insert into %s " % DB_SETTINGS["table_name"] + "values (%s, %s, %s)"
102+
sql = "insert into %s " % DB_SETTINGS["table_name"] + "values (%s, %s, %s) returning a, b, c"
103103
params = [(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")]
104-
await maybe_await(cursor.executemany(wrapper(sql), params))
104+
await maybe_await(cursor.executemany(wrapper(sql), params, returning=True))
105+
106+
# Consume inserted records to check that returning param functions
107+
records = []
108+
while True:
109+
records.append(cursor.fetchone())
110+
if not cursor.nextset():
111+
break
112+
assert len(records) == len(params)
105113

106114
sql = "select * from %s" % DB_SETTINGS["table_name"]
107115
await maybe_await(cursor.execute(wrapper(sql)))

0 commit comments

Comments
 (0)