feat(sqlalchemy): Support span streaming #6132
6 issues
High
Test uses StreamedSpan private attributes on NoOpSpan, causing AttributeError - `tests/integrations/sqlalchemy/test_sqlalchemy.py:1067-1068`
In streaming mode, record_sql_queries calls sentry_sdk.start_span() which delegates to Scope.start_span(). When streaming is enabled, Scope.start_span() returns NoOpSpan() (scope.py:1214). The test then tries to set _start_timestamp and _timestamp (with underscore prefix) on this NoOpSpan, but NoOpSpan inherits from Span which only has start_timestamp and timestamp (without underscore) in __slots__. This causes an AttributeError because NoOpSpan doesn't allow arbitrary attribute assignment.
Also found at:
tests/integrations/sqlalchemy/test_sqlalchemy.py:1212-1213tests/integrations/sqlalchemy/test_sqlalchemy.py:293
Medium
Potential runtime error if no root span found in streamed spans - `tests/conftest.py:488-490`
When root_span=None (streaming mode), the code attempts to find a root span by looking for a span without parent_span_id. If no such span exists in the list, root_span remains None, and line 509 will raise a runtime error when calling render_span(root_span). This could cause cryptic test failures.
Also found at:
tests/conftest.py:485-487
StreamedSpan handling code is unreachable - spans are never StreamedSpan instances - `sentry_sdk/integrations/sqlalchemy.py:101-104`
The isinstance(span, StreamedSpan) check on line 101 will never be true. The span comes from record_sql_queries() which internally calls sentry_sdk.start_span(). When span streaming is enabled, sentry_sdk.start_span() returns NoOpSpan (not StreamedSpan). When streaming is disabled, it returns Span. Neither is a StreamedSpan, making this error handling code unreachable.
Also found at:
sentry_sdk/integrations/sqlalchemy.py:142-177
Low
Missing SERVER_ADDRESS assertion in span_streaming path of test_transactions - `tests/integrations/sqlalchemy/test_sqlalchemy.py:162`
The span streaming branch of test_transactions only asserts SPANDATA.SERVER_PORT not in span["attributes"], while the non-streaming branch checks both SPANDATA.SERVER_ADDRESS not in span["data"] and SPANDATA.SERVER_PORT not in span["data"]. This asymmetry means the streaming path does not verify SERVER_ADDRESS is absent, reducing test coverage for the span streaming feature.
Also found at:
tests/integrations/sqlalchemy/test_sqlalchemy.py:293
Span streaming test path missing CODE_NAMESPACE assertion - `tests/integrations/sqlalchemy/test_sqlalchemy.py:811-813`
The span streaming branch (lines 811-813) only checks for CODE_LINE_NUMBER, CODE_FILE_PATH, and CODE_FUNCTION_NAME attributes, but omits the CODE_NAMESPACE check that exists in the non-streaming branch (lines 864-867, 871). This asymmetry in test coverage could allow a regression where span streaming mode fails to include namespace information.
Dead code: span_streaming conditional inside else branch will never execute True path - `tests/integrations/sqlalchemy/test_sqlalchemy.py:989-994`
In test_no_query_source_if_duration_too_short, inside the else: branch (line 962), the fake_record_sql_queries class contains an if span_streaming: check (lines 989-994). Since this code is inside the else: # span_streaming is False branch, the if span_streaming: block (lines 989-991) is unreachable dead code. While the test still works correctly because the else: path is taken, this appears to be an artifact of copy-paste during refactoring that should be cleaned up.
4 skills analyzed
| Skill | Findings | Duration | Cost |
|---|---|---|---|
| code-review | 3 | 8m 56s | $2.98 |
| find-bugs | 3 | 11m 50s | $8.17 |
| skill-scanner | 0 | 6m 21s | $0.89 |
| security-review | 0 | 4m 39s | $0.74 |
Duration: 31m 47s · Tokens: 9.7M in / 92.1k out · Cost: $12.80 (+extraction: $0.01, +merge: $0.01, +fix_gate: $0.01, +dedup: $0.00)