Skip to content

Commit d99dd39

Browse files
authored
fix(dbm): support byte string queries [backports #5188 to 1.8] (#5445)
1 parent 38882c7 commit d99dd39

File tree

5 files changed

+121
-6
lines changed

5 files changed

+121
-6
lines changed

ddtrace/contrib/dbapi/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Generic dbapi tracing code.
33
"""
44
from typing import TYPE_CHECKING
5+
from typing import Union
56

67
import six
78

@@ -181,15 +182,17 @@ def _propagate_dbm_context(self, dbspan, args):
181182
return args
182183

183184
def _dbm_sql_injector(self, dbm_comment, sql_statement):
185+
# type: (str, Union[str, bytes]) -> Union[str, bytes]
184186
try:
187+
if isinstance(sql_statement, bytes):
188+
return dbm_comment.encode("utf-8", errors="strict") + sql_statement
185189
return dbm_comment + sql_statement
186-
except TypeError:
190+
except (TypeError, ValueError):
187191
log.warning(
188192
"Linking Database Monitoring profiles to spans is not supported for the following query type: %s. "
189193
"To disable this feature please set the following environment variable: "
190194
"DD_DBM_PROPAGATION_MODE=disabled",
191195
type(sql_statement),
192-
exc_info=True,
193196
)
194197
return sql_statement
195198

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
dbm: Support sql queries with the type ``byte``.

tests/contrib/dbapi/test_dbapi.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import logging
2+
13
import mock
24
import pytest
35

@@ -20,6 +22,10 @@ def setUp(self):
2022
super(TestTracedCursor, self).setUp()
2123
self.cursor = mock.Mock()
2224

25+
@pytest.fixture(autouse=True)
26+
def prepare_fixture(self, caplog):
27+
self._caplog = caplog
28+
2329
def test_execute_wrapped_is_called_and_returned(self):
2430
cursor = self.cursor
2531
cursor.rowcount = 0
@@ -69,6 +75,29 @@ def test_cursor_execute_with_dbm_injection(self):
6975
# DBM comment should not be added procedure names
7076
cursor.callproc.assert_called_once_with("procedure_named_moon")
7177

78+
def test_default_sql_injector(self):
79+
# test sql injection with unicode str
80+
cursor = TracedCursor(self.cursor, Pin(), {})
81+
dbm_comment = "/*dddbs='orders-db'*/ "
82+
str_query = "select * from table;"
83+
assert cursor._dbm_sql_injector(dbm_comment, str_query) == "/*dddbs='orders-db'*/ select * from table;"
84+
85+
# test sql injection with uft-8 byte str query
86+
dbm_comment = "/*dddbs='orders-db'*/ "
87+
str_query = "select * from table;".encode("utf-8")
88+
assert cursor._dbm_sql_injector(dbm_comment, str_query) == b"/*dddbs='orders-db'*/ select * from table;"
89+
90+
# test sql injection with a non supported type
91+
with self._caplog.at_level(logging.INFO):
92+
dbm_comment = "/*dddbs='orders-db'*/ "
93+
non_string_object = object()
94+
result = cursor._dbm_sql_injector(dbm_comment, non_string_object)
95+
assert result == non_string_object
96+
assert (
97+
"Linking Database Monitoring profiles to spans is not supported for the following query type:"
98+
in self._caplog.text
99+
)
100+
72101
def test_executemany_wrapped_is_called_and_returned(self):
73102
cursor = self.cursor
74103
cursor.rowcount = 0

tests/contrib/psycopg/test_psycopg.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ def test_postgres_dbm_propagation_tag(self):
397397
# test string queries
398398
cursor.execute("select 'str blah'")
399399
cursor.executemany("select %s", (("str_foo",), ("str_bar",)))
400+
# test byte string queries
401+
cursor.execute(b"select 'byte str blah'")
402+
cursor.executemany(b"select %s", ((b"bstr_foo",), (b"bstr_bar",)))
400403
# test composed queries
401404
cursor.execute(SQL("select 'composed_blah'"))
402405
cursor.executemany(SQL("select %s"), (("composed_foo",), ("composed_bar",)))
@@ -418,9 +421,16 @@ def test_postgres_dbm_propagation_comment(self):
418421
cursor.execute("select 'blah'")
419422
cursor.executemany("select %s", (("foo",), ("bar",)))
420423
dbm_comment = "/*dddbs='postgres',dde='staging',ddps='orders-app',ddpv='v7343437-d7ac743'*/ "
421-
# test string queries
422424
cursor.__wrapped__.execute.assert_called_once_with(dbm_comment + "select 'blah'")
423425
cursor.__wrapped__.executemany.assert_called_once_with(dbm_comment + "select %s", (("foo",), ("bar",)))
426+
# test byte string queries
427+
cursor.__wrapped__.reset_mock()
428+
cursor.execute(b"select 'blah'")
429+
cursor.executemany(b"select %s", ((b"foo",), (b"bar",)))
430+
cursor.__wrapped__.execute.assert_called_once_with(dbm_comment.encode() + b"select 'blah'")
431+
cursor.__wrapped__.executemany.assert_called_once_with(
432+
dbm_comment.encode() + b"select %s", ((b"foo",), (b"bar",))
433+
)
424434
# test composed queries
425435
cursor.__wrapped__.reset_mock()
426436
cursor.execute(SQL("select 'blah'"))

tests/snapshots/tests.contrib.psycopg.test_psycopg.test_postgres_dbm_propagation_tag.json

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
{
7272
"name": "postgres.query",
7373
"service": "postgres",
74-
"resource": "select 'composed_blah'",
74+
"resource": "select 'byte str blah'",
7575
"trace_id": 2,
7676
"span_id": 1,
7777
"parent_id": 0,
@@ -133,6 +133,75 @@
133133
"process_id": 56478,
134134
"sql.rows": 2
135135
},
136-
"duration": 1988000,
137-
"start": 1669148045081837000
136+
"duration": 4226000,
137+
"start": 1677184993796426000
138+
}],
139+
[
140+
{
141+
"name": "postgres.query",
142+
"service": "postgres",
143+
"resource": "select 'composed_blah'",
144+
"trace_id": 4,
145+
"span_id": 1,
146+
"parent_id": 0,
147+
"type": "sql",
148+
"error": 0,
149+
"meta": {
150+
"_dd.dbm_trace_injected": "true",
151+
"_dd.p.dm": "-0",
152+
"component": "psycopg",
153+
"db.application": "None",
154+
"db.name": "postgres",
155+
"db.user": "postgres",
156+
"out.host": "127.0.0.1",
157+
"runtime-id": "976be4b4fbee409ba62cdc546761d15a"
158+
},
159+
"metrics": {
160+
"_dd.agent_psr": 1.0,
161+
"_dd.measured": 1,
162+
"_dd.top_level": 1,
163+
"_dd.tracer_kr": 1.0,
164+
"_sampling_priority_v1": 1,
165+
"db.rowcount": 1,
166+
"out.port": 5432,
167+
"process_id": 56478,
168+
"sql.rows": 1
169+
},
170+
"duration": 1378000,
171+
"start": 1677184993800707000
172+
}],
173+
[
174+
{
175+
"name": "postgres.query",
176+
"service": "postgres",
177+
"resource": "select %s",
178+
"trace_id": 5,
179+
"span_id": 1,
180+
"parent_id": 0,
181+
"type": "sql",
182+
"error": 0,
183+
"meta": {
184+
"_dd.dbm_trace_injected": "true",
185+
"_dd.p.dm": "-0",
186+
"component": "psycopg",
187+
"db.application": "None",
188+
"db.name": "postgres",
189+
"db.user": "postgres",
190+
"out.host": "127.0.0.1",
191+
"runtime-id": "976be4b4fbee409ba62cdc546761d15a",
192+
"sql.executemany": "true"
193+
},
194+
"metrics": {
195+
"_dd.agent_psr": 1.0,
196+
"_dd.measured": 1,
197+
"_dd.top_level": 1,
198+
"_dd.tracer_kr": 1.0,
199+
"_sampling_priority_v1": 1,
200+
"db.rowcount": 2,
201+
"out.port": 5432,
202+
"process_id": 56478,
203+
"sql.rows": 2
204+
},
205+
"duration": 2705000,
206+
"start": 1677184993802144000
138207
}]]

0 commit comments

Comments
 (0)