From c8c9765c3e59cb3652be800e5fc4c926a9044375 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 1 Oct 2026 10:58:43 -0400 Subject: [PATCH 1/6] ref(db)!: Drop `record_sql_params` experiment and legacy `send_default_pii` fallback `record_sql_queries` now reads only `data_collection.database_query_data` to decide whether to record query params, and the `_experiments["record_sql_params"]` option is removed. Tests that relied on the experiment now opt in with `data_collection={"database_query_data": True}`. Refs PY-2798 Refs #7566 --- sentry_sdk/consts.py | 1 - sentry_sdk/tracing_utils.py | 26 +++++-------------- tests/integrations/aiomysql/test_aiomysql.py | 14 +++++----- tests/integrations/asyncpg/test_asyncpg.py | 22 ++++++---------- .../test_clickhouse_driver.py | 7 ----- tests/integrations/django/asgi/test_asgi.py | 4 +-- tests/integrations/django/test_basic.py | 16 +++--------- .../sqlalchemy/test_sqlalchemy.py | 12 +++------ tests/test_tracing_utils.py | 26 ------------------- 9 files changed, 29 insertions(+), 99 deletions(-) diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index 4c47e4c155..ac7c72b661 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -65,7 +65,6 @@ class CompressionAlgo(Enum): "Experiments", { "max_flags": Optional[int], - "record_sql_params": Optional[bool], "continuous_profiling_auto_start": Optional[bool], "transport_zlib_compression_level": Optional[int], "transport_compression_level": Optional[int], diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 7e5907d4ba..98684f5b75 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -124,27 +124,15 @@ def record_sql_queries( ) -> "Generator[sentry_sdk.traces.Span, None, None]": # TODO: Bring back capturing of params by default client = sentry_sdk.get_client() - if has_data_collection_enabled(client.options): - if client.options["data_collection"]["database_query_data"]: - if not params_list or params_list == [None]: - params_list = None - - if paramstyle == "pyformat": - paramstyle = "format" - else: + if client.options["data_collection"]["database_query_data"]: + if not params_list or params_list == [None]: params_list = None - paramstyle = None - else: - # TODO: remove this else block once data collection is released - if client.options["_experiments"].get("record_sql_params", False): - if not params_list or params_list == [None]: - params_list = None - if paramstyle == "pyformat": - paramstyle = "format" - else: - params_list = None - paramstyle = None + if paramstyle == "pyformat": + paramstyle = "format" + else: + params_list = None + paramstyle = None query = _format_sql(cursor, query) diff --git a/tests/integrations/aiomysql/test_aiomysql.py b/tests/integrations/aiomysql/test_aiomysql.py index f7c1ca0cab..6239a56f5e 100644 --- a/tests/integrations/aiomysql/test_aiomysql.py +++ b/tests/integrations/aiomysql/test_aiomysql.py @@ -102,7 +102,7 @@ def _connect_args(): async def test_connect(sentry_init, capture_events) -> None: sentry_init( integrations=[AioMySQLIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -123,7 +123,7 @@ async def test_connect(sentry_init, capture_events) -> None: async def test_execute(sentry_init, capture_events) -> None: sentry_init( integrations=[AioMySQLIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -177,7 +177,6 @@ async def test_execute(sentry_init, capture_events) -> None: async def test_execute_many(sentry_init, capture_events) -> None: sentry_init( integrations=[AioMySQLIntegration()], - _experiments={"record_sql_params": True}, ) events = capture_events() @@ -354,7 +353,7 @@ async def test_execute_many_non_insert(sentry_init, capture_events) -> None: """Test executemany with non-INSERT queries (falls back to row-by-row).""" sentry_init( integrations=[AioMySQLIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -403,7 +402,6 @@ async def test_execute_many_non_insert(sentry_init, capture_events) -> None: async def test_record_params(sentry_init, capture_events) -> None: sentry_init( integrations=[AioMySQLIntegration(record_params=True)], - _experiments={"record_sql_params": True}, ) events = capture_events() @@ -562,7 +560,7 @@ async def test_execute_record_params_with_data_collection_default( async def test_cursor_context_manager(sentry_init, capture_events) -> None: sentry_init( integrations=[AioMySQLIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -604,7 +602,7 @@ async def test_cursor_context_manager(sentry_init, capture_events) -> None: async def test_cursor_async_iteration(sentry_init, capture_events) -> None: sentry_init( integrations=[AioMySQLIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -637,7 +635,7 @@ async def test_cursor_async_iteration(sentry_init, capture_events) -> None: async def test_connection_pool(sentry_init, capture_events) -> None: sentry_init( integrations=[AioMySQLIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() diff --git a/tests/integrations/asyncpg/test_asyncpg.py b/tests/integrations/asyncpg/test_asyncpg.py index 83c49c66ac..d580096e3f 100644 --- a/tests/integrations/asyncpg/test_asyncpg.py +++ b/tests/integrations/asyncpg/test_asyncpg.py @@ -102,9 +102,7 @@ async def test_connect( ) -> None: sentry_init( integrations=[AsyncPGIntegration()], - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, ) items = capture_items("event") @@ -131,9 +129,7 @@ async def test_execute( ) -> None: sentry_init( integrations=[AsyncPGIntegration()], - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, ) items = capture_items("event") @@ -203,9 +199,7 @@ async def test_execute_many( ) -> None: sentry_init( integrations=[AsyncPGIntegration()], - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, ) items = capture_items("event") @@ -244,7 +238,7 @@ async def test_execute_many( async def test_record_params(sentry_init, capture_events) -> None: sentry_init( integrations=[AsyncPGIntegration(record_params=True)], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -284,7 +278,7 @@ async def test_record_params(sentry_init, capture_events) -> None: async def test_cursor(sentry_init, capture_events) -> None: sentry_init( integrations=[AsyncPGIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -338,7 +332,7 @@ async def test_cursor(sentry_init, capture_events) -> None: async def test_cursor_manual(sentry_init, capture_events) -> None: sentry_init( integrations=[AsyncPGIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -400,7 +394,7 @@ async def test_cursor_manual(sentry_init, capture_events) -> None: async def test_prepared_stmt(sentry_init, capture_events) -> None: sentry_init( integrations=[AsyncPGIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() @@ -449,7 +443,7 @@ async def test_prepared_stmt(sentry_init, capture_events) -> None: async def test_connection_pool(sentry_init, capture_events) -> None: sentry_init( integrations=[AsyncPGIntegration()], - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) events = capture_events() diff --git a/tests/integrations/clickhouse_driver/test_clickhouse_driver.py b/tests/integrations/clickhouse_driver/test_clickhouse_driver.py index 5ddf9b1ab0..96a7371c74 100644 --- a/tests/integrations/clickhouse_driver/test_clickhouse_driver.py +++ b/tests/integrations/clickhouse_driver/test_clickhouse_driver.py @@ -22,7 +22,6 @@ def test_clickhouse_client_breadcrumbs(sentry_init, capture_events) -> None: sentry_init( integrations=[ClickhouseDriverIntegration()], data_collection={}, - _experiments={"record_sql_params": True}, ) events = capture_events() @@ -522,9 +521,6 @@ def test_clickhouse_client_spans( ): sentry_init( integrations=[ClickhouseDriverIntegration()], - _experiments={ - "record_sql_params": True, - }, traces_sample_rate=1.0, data_collection={}, ) @@ -758,9 +754,6 @@ def test_clickhouse_dbapi_spans( ): sentry_init( integrations=[ClickhouseDriverIntegration()], - _experiments={ - "record_sql_params": True, - }, traces_sample_rate=1.0, data_collection={}, ) diff --git a/tests/integrations/django/asgi/test_asgi.py b/tests/integrations/django/asgi/test_asgi.py index 50608d1a25..05deef0fbb 100644 --- a/tests/integrations/django/asgi/test_asgi.py +++ b/tests/integrations/django/asgi/test_asgi.py @@ -257,9 +257,7 @@ async def test_async_middleware_spans( sentry_init( integrations=[DjangoIntegration(middleware_spans=True)], traces_sample_rate=1.0, - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, ) application = make_asgi_application() diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index ef1b455516..454aec9bfb 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -474,8 +474,7 @@ def test_sql_queries( ): sentry_init( integrations=[DjangoIntegration()] if with_integration else [], - send_default_pii=True, - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) from django.db import connection @@ -509,8 +508,7 @@ def test_sql_dict_query_params( ): sentry_init( integrations=[DjangoIntegration()], - send_default_pii=True, - _experiments={"record_sql_params": True}, + data_collection={"database_query_data": True}, ) from django.db import connections @@ -586,10 +584,7 @@ def test_sql_psycopg2_string_composition( ): sentry_init( integrations=[DjangoIntegration()], - send_default_pii=True, - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, ) from django.db import connections @@ -624,10 +619,7 @@ def test_sql_psycopg2_placeholders( ): sentry_init( integrations=[DjangoIntegration()], - send_default_pii=True, - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, ) from django.db import connections diff --git a/tests/integrations/sqlalchemy/test_sqlalchemy.py b/tests/integrations/sqlalchemy/test_sqlalchemy.py index d4f0dca3aa..333cb80d2f 100644 --- a/tests/integrations/sqlalchemy/test_sqlalchemy.py +++ b/tests/integrations/sqlalchemy/test_sqlalchemy.py @@ -21,9 +21,7 @@ def test_orm_queries( ): sentry_init( integrations=[SqlalchemyIntegration()], - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, ) Base = declarative_base() # noqa: N806 @@ -87,9 +85,7 @@ def test_transactions( ): sentry_init( integrations=[SqlalchemyIntegration()], - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, traces_sample_rate=1.0, ) @@ -170,9 +166,7 @@ def test_transactions_no_engine_url( ): sentry_init( integrations=[SqlalchemyIntegration()], - _experiments={ - "record_sql_params": True, - }, + data_collection={"database_query_data": True}, traces_sample_rate=1.0, ) diff --git a/tests/test_tracing_utils.py b/tests/test_tracing_utils.py index 3098e73667..5e034d2f90 100644 --- a/tests/test_tracing_utils.py +++ b/tests/test_tracing_utils.py @@ -335,37 +335,11 @@ def _get_query_breadcrumb_data( {"db.params": [1, 2], "db.paramstyle": "format"}, id="data_collection_default_records_params", ), - pytest.param( - {"_experiments": {"record_sql_params": True}}, - {"db.params": [1, 2], "db.paramstyle": "format"}, - id="legacy_record_sql_params_on_records_params", - ), - pytest.param( - {"_experiments": {"record_sql_params": False}}, - {}, - id="legacy_record_sql_params_off_strips_params", - ), pytest.param( {}, {}, id="no_options_strips_params", ), - pytest.param( - { - "_experiments": {"record_sql_params": True}, - "data_collection": {"database_query_data": False}, - }, - {}, - id="data_collection_off_takes_precedence_over_legacy_on", - ), - pytest.param( - { - "_experiments": {"record_sql_params": False}, - "data_collection": {"database_query_data": True}, - }, - {"db.params": [1, 2], "db.paramstyle": "format"}, - id="data_collection_on_takes_precedence_over_legacy_off", - ), ), ) def test_record_sql_queries_data_collection( From af3f3f25bb547cad154481e44252fdcc9eb47ba8 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 1 Oct 2026 12:44:22 -0400 Subject: [PATCH 2/6] ref(aiomysql,asyncpg)!: Drop `record_params` option in favour of `data_collection` Remove the `record_params` argument from `AioMySQLIntegration` and `AsyncPGIntegration`. Both now decide whether to record query params solely from `data_collection.database_query_data`, matching `record_sql_queries`. Tests that relied on `record_params` now opt in with `data_collection={"database_query_data": True}`, and the legacy `send_default_pii` test cases are removed. Refs PY-2798 Refs #7566 --- sentry_sdk/integrations/aiomysql.py | 18 +-- sentry_sdk/integrations/asyncpg.py | 9 +- tests/integrations/aiomysql/test_aiomysql.py | 131 ++----------------- tests/integrations/asyncpg/test_asyncpg.py | 2 +- tests/integrations/django/test_basic.py | 4 +- 5 files changed, 18 insertions(+), 146 deletions(-) diff --git a/sentry_sdk/integrations/aiomysql.py b/sentry_sdk/integrations/aiomysql.py index 687759fcdd..50e66e6362 100644 --- a/sentry_sdk/integrations/aiomysql.py +++ b/sentry_sdk/integrations/aiomysql.py @@ -28,10 +28,6 @@ class AioMySQLIntegration(Integration): identifier = "aiomysql" origin = f"auto.db.{identifier}" - _record_params = False - - def __init__(self, *, record_params: bool = False): - AioMySQLIntegration._record_params = record_params @staticmethod def setup_once() -> None: @@ -84,12 +80,7 @@ async def _inner(*args: Any, **kwargs: Any) -> T: integration = sentry_sdk.get_client().get_integration(AioMySQLIntegration) client = sentry_sdk.get_client() - should_record_params = False - if has_data_collection_enabled(client.options): - if client.options["data_collection"]["database_query_data"]: - should_record_params = True - else: - should_record_params = integration._record_params if integration else False + should_record_params = client.options["data_collection"]["database_query_data"] params_list = params if integration and should_record_params else None param_style = "pyformat" if params_list else None @@ -134,12 +125,7 @@ async def _inner(*args: Any, **kwargs: Any) -> T: integration = sentry_sdk.get_client().get_integration(AioMySQLIntegration) client = sentry_sdk.get_client() - should_record_params = False - if has_data_collection_enabled(client.options): - if client.options["data_collection"]["database_query_data"]: - should_record_params = True - else: - should_record_params = integration._record_params if integration else False + should_record_params = client.options["data_collection"]["database_query_data"] params_list = seq_of_params if integration and should_record_params else None param_style = "pyformat" if params_list else None diff --git a/sentry_sdk/integrations/asyncpg.py b/sentry_sdk/integrations/asyncpg.py index f4e7724510..f54b22816b 100644 --- a/sentry_sdk/integrations/asyncpg.py +++ b/sentry_sdk/integrations/asyncpg.py @@ -32,10 +32,6 @@ class AsyncPGIntegration(Integration): identifier = "asyncpg" origin = f"auto.db.{identifier}" - _record_params = False - - def __init__(self, *, record_params: bool = False): - AsyncPGIntegration._record_params = record_params @staticmethod def setup_once() -> None: @@ -114,7 +110,10 @@ def _record( ) -> "Iterator[Span]": client = sentry_sdk.get_client() integration = client.get_integration(AsyncPGIntegration) - if integration is not None and not integration._record_params: + if ( + integration is not None + and not client.options["data_collection"]["database_query_data"] + ): params_list = None param_style = "pyformat" if params_list else None diff --git a/tests/integrations/aiomysql/test_aiomysql.py b/tests/integrations/aiomysql/test_aiomysql.py index 6239a56f5e..53c2a207de 100644 --- a/tests/integrations/aiomysql/test_aiomysql.py +++ b/tests/integrations/aiomysql/test_aiomysql.py @@ -160,13 +160,19 @@ async def test_execute(sentry_init, capture_events) -> None: }, { "category": "query", - "data": {}, + "data": { + "db.params": ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], + "db.paramstyle": "format", + }, "message": "INSERT INTO users(name, password, dob) VALUES (%s, %s, %s)", "type": "default", }, { "category": "query", - "data": {}, + "data": { + "db.params": ["Bob"], + "db.paramstyle": "format", + }, "message": "SELECT * FROM users WHERE name = %s", "type": "default", }, @@ -259,47 +265,6 @@ async def test_execute_many_record_params_with_data_collection_enabled( ] -@pytest.mark.asyncio -async def test_execute_many_record_params_with_data_collection_disabled( - sentry_init, capture_events -) -> None: - sentry_init( - integrations=[AioMySQLIntegration(record_params=True)], - data_collection={"database_query_data": False}, - ) - events = capture_events() - - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.executemany( - "INSERT INTO users(name, password, dob) VALUES (%s, %s, %s)", - [ - ("Bob", "secret_pw", datetime.date(1984, 3, 1)), - ("Alice", "pw", datetime.date(1990, 12, 25)), - ], - ) - - conn.close() - - capture_message("hi") - - (event,) = events - - for crumb in event["breadcrumbs"]["values"]: - del crumb["timestamp"] - - assert event["breadcrumbs"]["values"] == [ - CRUMBS_CONNECT, - { - "category": "query", - "data": {"db.executemany": True}, - "message": "INSERT INTO users(name, password, dob) VALUES (%s, %s, %s)", - "type": "default", - }, - ] - - @pytest.mark.asyncio async def test_execute_many_record_params_with_data_collection_default( sentry_init, capture_events @@ -399,47 +364,7 @@ async def test_execute_many_non_insert(sentry_init, capture_events) -> None: @pytest.mark.asyncio -async def test_record_params(sentry_init, capture_events) -> None: - sentry_init( - integrations=[AioMySQLIntegration(record_params=True)], - ) - events = capture_events() - - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES (%s, %s, %s)", - ("Bob", "secret_pw", datetime.date(1984, 3, 1)), - ) - - conn.close() - - capture_message("hi") - - (event,) = events - - for crumb in event["breadcrumbs"]["values"]: - del crumb["timestamp"] - - assert event["breadcrumbs"]["values"] == [ - CRUMBS_CONNECT, - { - "category": "query", - "data": { - "db.params": ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], - "db.paramstyle": "format", - }, - "message": "INSERT INTO users(name, password, dob) VALUES (%s, %s, %s)", - "type": "default", - }, - ] - - -@pytest.mark.asyncio -async def test_execute_record_params_with_data_collection_enabled( - sentry_init, capture_events -) -> None: +async def test_execute_record_params(sentry_init, capture_events) -> None: sentry_init( integrations=[AioMySQLIntegration()], data_collection={"database_query_data": True}, @@ -477,44 +402,6 @@ async def test_execute_record_params_with_data_collection_enabled( ] -@pytest.mark.asyncio -async def test_execute_record_params_with_data_collection_disabled( - sentry_init, capture_events -) -> None: - sentry_init( - integrations=[AioMySQLIntegration(record_params=True)], - data_collection={"database_query_data": False}, - ) - events = capture_events() - - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES (%s, %s, %s)", - ("Bob", "secret_pw", datetime.date(1984, 3, 1)), - ) - - conn.close() - - capture_message("hi") - - (event,) = events - - for crumb in event["breadcrumbs"]["values"]: - del crumb["timestamp"] - - assert event["breadcrumbs"]["values"] == [ - CRUMBS_CONNECT, - { - "category": "query", - "data": {}, - "message": "INSERT INTO users(name, password, dob) VALUES (%s, %s, %s)", - "type": "default", - }, - ] - - @pytest.mark.asyncio async def test_execute_record_params_with_data_collection_default( sentry_init, capture_events diff --git a/tests/integrations/asyncpg/test_asyncpg.py b/tests/integrations/asyncpg/test_asyncpg.py index d580096e3f..44c132edbe 100644 --- a/tests/integrations/asyncpg/test_asyncpg.py +++ b/tests/integrations/asyncpg/test_asyncpg.py @@ -237,7 +237,7 @@ async def test_execute_many( @pytest.mark.asyncio async def test_record_params(sentry_init, capture_events) -> None: sentry_init( - integrations=[AsyncPGIntegration(record_params=True)], + integrations=[AsyncPGIntegration()], data_collection={"database_query_data": True}, ) events = capture_events() diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index 454aec9bfb..c118610cd0 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -728,7 +728,7 @@ def test_django_connect_breadcrumbs( """ sentry_init( integrations=[DjangoIntegration()], - send_default_pii=True, + data_collection={"database_query_data": True}, ) from django.db import connections @@ -766,7 +766,7 @@ def test_db_connection_span_data( ): sentry_init( integrations=[DjangoIntegration()], - send_default_pii=True, + data_collection={"database_query_data": True}, traces_sample_rate=1.0, ) From 718d4551ae7f709ed83a419919e7de0969493577 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 1 Oct 2026 13:06:05 -0400 Subject: [PATCH 3/6] . --- sentry_sdk/integrations/aiomysql.py | 1 - tests/integrations/asyncpg/test_asyncpg.py | 50 ++++++++++++++++++---- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/sentry_sdk/integrations/aiomysql.py b/sentry_sdk/integrations/aiomysql.py index 50e66e6362..2cf18e1f44 100644 --- a/sentry_sdk/integrations/aiomysql.py +++ b/sentry_sdk/integrations/aiomysql.py @@ -13,7 +13,6 @@ ) from sentry_sdk.utils import ( capture_internal_exceptions, - has_data_collection_enabled, parse_version, ) diff --git a/tests/integrations/asyncpg/test_asyncpg.py b/tests/integrations/asyncpg/test_asyncpg.py index 44c132edbe..d5924ef3e0 100644 --- a/tests/integrations/asyncpg/test_asyncpg.py +++ b/tests/integrations/asyncpg/test_asyncpg.py @@ -173,13 +173,16 @@ async def test_execute( }, { "category": "query", - "data": {}, + "data": { + "db.params": ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], + "db.paramstyle": "format", + }, "message": "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)", "type": "default", }, { "category": "query", - "data": {}, + "data": {"db.params": ["Bob"], "db.paramstyle": "format"}, "message": "SELECT * FROM users WHERE name = $1", "type": "default", }, @@ -227,7 +230,14 @@ async def test_execute_many( CRUMBS_CONNECT, { "category": "query", - "data": {"db.executemany": True}, + "data": { + "db.params": [ + ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], + ["Alice", "pw", "datetime.date(1990, 12, 25)"], + ], + "db.paramstyle": "format", + "db.executemany": True, + }, "message": "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)", "type": "default", }, @@ -313,7 +323,14 @@ async def test_cursor(sentry_init, capture_events) -> None: CRUMBS_CONNECT, { "category": "query", - "data": {"db.executemany": True}, + "data": { + "db.params": [ + ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], + ["Alice", "pw", "datetime.date(1990, 12, 25)"], + ], + "db.paramstyle": "format", + "db.executemany": True, + }, "message": "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)", "type": "default", }, @@ -369,7 +386,14 @@ async def test_cursor_manual(sentry_init, capture_events) -> None: CRUMBS_CONNECT, { "category": "query", - "data": {"db.executemany": True}, + "data": { + "db.params": [ + ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], + ["Alice", "pw", "datetime.date(1990, 12, 25)"], + ], + "db.paramstyle": "format", + "db.executemany": True, + }, "message": "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)", "type": "default", }, @@ -426,7 +450,14 @@ async def test_prepared_stmt(sentry_init, capture_events) -> None: CRUMBS_CONNECT, { "category": "query", - "data": {"db.executemany": True}, + "data": { + "db.params": [ + ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], + ["Alice", "pw", "datetime.date(1990, 12, 25)"], + ], + "db.paramstyle": "format", + "db.executemany": True, + }, "message": "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)", "type": "default", }, @@ -479,7 +510,10 @@ async def test_connection_pool(sentry_init, capture_events) -> None: *[CRUMBS_CONNECT] * pool_size, { "category": "query", - "data": {}, + "data": { + "db.params": ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], + "db.paramstyle": "format", + }, "message": "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)", "type": "default", }, @@ -491,7 +525,7 @@ async def test_connection_pool(sentry_init, capture_events) -> None: }, { "category": "query", - "data": {}, + "data": {"db.params": ["Bob"], "db.paramstyle": "format"}, "message": "SELECT * FROM users WHERE name = $1", "type": "default", }, From 99863b8a3ad6f1e355ede8c991a095c5db89975e Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 1 Oct 2026 13:14:12 -0400 Subject: [PATCH 4/6] . --- tests/integrations/django/test_basic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/integrations/django/test_basic.py b/tests/integrations/django/test_basic.py index c118610cd0..510805f6cd 100644 --- a/tests/integrations/django/test_basic.py +++ b/tests/integrations/django/test_basic.py @@ -753,7 +753,12 @@ def test_django_connect_breadcrumbs( assert event["breadcrumbs"]["values"][-2:] == [ {"message": "connect", "category": "query", "type": "default"}, - {"message": "select 1", "category": "query", "data": {}, "type": "default"}, + { + "message": "select 1", + "category": "query", + "data": {"db.paramstyle": "format"}, + "type": "default", + }, ] From 745877d8acd03829d68fb194e47035718b15c1c9 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Thu, 1 Oct 2026 13:30:05 -0400 Subject: [PATCH 5/6] remove duplicate test --- tests/integrations/asyncpg/test_asyncpg.py | 78 ++-------------------- 1 file changed, 4 insertions(+), 74 deletions(-) diff --git a/tests/integrations/asyncpg/test_asyncpg.py b/tests/integrations/asyncpg/test_asyncpg.py index d5924ef3e0..2ef7993795 100644 --- a/tests/integrations/asyncpg/test_asyncpg.py +++ b/tests/integrations/asyncpg/test_asyncpg.py @@ -244,46 +244,6 @@ async def test_execute_many( ] -@pytest.mark.asyncio -async def test_record_params(sentry_init, capture_events) -> None: - sentry_init( - integrations=[AsyncPGIntegration()], - data_collection={"database_query_data": True}, - ) - events = capture_events() - - conn: Connection = await connect(PG_CONNECTION_URI) - - await conn.execute( - "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)", - "Bob", - "secret_pw", - datetime.date(1984, 3, 1), - ) - - await conn.close() - - capture_message("hi") - - (event,) = events - - for crumb in event["breadcrumbs"]["values"]: - del crumb["timestamp"] - - assert event["breadcrumbs"]["values"] == [ - CRUMBS_CONNECT, - { - "category": "query", - "data": { - "db.params": ["Bob", "secret_pw", "datetime.date(1984, 3, 1)"], - "db.paramstyle": "format", - }, - "message": "INSERT INTO users(name, password, dob) VALUES($1, $2, $3)", - "type": "default", - }, - ] - - @pytest.mark.asyncio async def test_cursor(sentry_init, capture_events) -> None: sentry_init( @@ -625,36 +585,6 @@ async def test_query_source_enabled( _assert_query_source(insert_span, "test_query_source_enabled") -@pytest.mark.asyncio -async def test_query_source(sentry_init, capture_items): - sentry_init( - integrations=[AsyncPGIntegration()], - traces_sample_rate=1.0, - enable_db_query_source=True, - db_query_source_threshold_ms=0, - ) - - items = capture_items("span") - with sentry_sdk.start_span(name="test_segment"): - conn: Connection = await connect(PG_CONNECTION_URI) - - await conn.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) - - await conn.close() - - sentry_sdk.flush() - - spans = [item.payload for item in items] - - assert len(spans) == 3 - - _, insert_span, _ = spans - - _assert_query_source(insert_span, "test_query_source") - - @pytest.mark.asyncio async def test_query_source_with_module_in_search_path( sentry_init, @@ -1139,7 +1069,7 @@ async def test_cursor_fetch_methods_create_spans( assert span["attributes"]["sentry.op"] == OP.DB_CURSOR_FETCH assert span["attributes"]["sentry.origin"] == "auto.db.asyncpg" - _assert_query_source( - span, - "test_cursor_fetch_methods_create_spans", - ) + _assert_query_source( + span, + "test_cursor_fetch_methods_create_spans", + ) From 50b20eab0aad0bee9adb63b91ec812d58d9c16ae Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 5 Oct 2026 06:39:12 -0400 Subject: [PATCH 6/6] update migration guide --- MIGRATION_GUIDE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 9fcf6322a3..41bb93ff69 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -163,6 +163,8 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh - The `trace_ignore_status_codes` option was removed. - `add_attachment` no longer accepts an `add_to_transactions` argument. - The `stream_gen_ai_spans` option was removed. All spans are streamed now. +- The experimental `record_sql_params` option was removed. Use the `database_query_data` setting of `data_collection` instead. +- The `record_params` option of `AsyncPGIntegration` and `AioMySQLIntegration` was removed. Use the `database_query_data` setting of `data_collection` instead. ## Deprecated