Skip to content

Commit 29b29d1

Browse files
dougqhdevflow.devflow-routing-intake
authored andcommitted
Fix NPE in DatabaseClientDecorator when JDBC connection has no db type (#12371)
Fix NPE in DatabaseClientDecorator when JDBC connection has no db type processDatabaseType/dbService dereferenced the NamingEntry returned by CACHE.computeIfAbsent(dbType, NamingEntry::new) without checking for null. FixedSizeCache.computeIfAbsent returns null for a null key without invoking the producer, so a null dbType() (e.g. an undetermined DBInfo.getType()) led to a NullPointerException in tracing instrumentation instead of a graceful no-op. Guard on dbType == null directly instead of inferring it from the cache's return value, and add a test exercising both methods with a null dbType. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Merge branch 'master' into dougqh/fix-database-client-decorator-npe Merge branch 'master' into dougqh/fix-database-client-decorator-npe Merge branch 'master' into dougqh/fix-database-client-decorator-npe Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
1 parent 093c8ae commit 29b29d1

2 files changed

Lines changed: 79 additions & 3 deletions

File tree

‎dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/DatabaseClientDecorator.java‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,10 @@ public String dbService(final String dbType, final String instanceName) {
9696
if (instanceName != null && Config.get().isDbClientSplitByInstance()) {
9797
return dbClientService(instanceName);
9898
}
99-
final NamingEntry entry = CACHE.computeIfAbsent(dbType, NamingEntry::new);
100-
return entry.getService();
99+
if (dbType == null) {
100+
return null;
101+
}
102+
return CACHE.computeIfAbsent(dbType, NamingEntry::new).getService();
101103
}
102104

103105
public String dbClientService(final String instanceName) {
@@ -144,11 +146,15 @@ public void onRawStatement(AgentSpan span, String sql) {
144146
}
145147

146148
protected void processDatabaseType(AgentSpan span, String dbType) {
149+
if (dbType == null) {
150+
return;
151+
}
152+
147153
final NamingEntry namingEntry = CACHE.computeIfAbsent(dbType, NamingEntry::new);
148154
span.setTag(DB_TYPE, namingEntry.dbType);
149155
postProcessServiceAndOperationName(span, namingEntry);
150156

151-
if (Config.get().isAppSecRaspEnabled() && dbType != null) {
157+
if (Config.get().isAppSecRaspEnabled()) {
152158
BiConsumer<RequestContext, String> connectDbCallback =
153159
AgentTracer.get()
154160
.getCallbackProvider(RequestContextSlot.APPSEC)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package datadog.trace.bootstrap.instrumentation.decorator;
2+
3+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
import static org.mockito.ArgumentMatchers.anyString;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.never;
8+
import static org.mockito.Mockito.verify;
9+
10+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
11+
import org.junit.jupiter.api.Test;
12+
13+
class DatabaseClientDecoratorNullDbTypeTest {
14+
15+
private final AgentSpan span = mock(AgentSpan.class);
16+
private final DatabaseClientDecorator<Object> decorator =
17+
new DatabaseClientDecorator<Object>() {
18+
@Override
19+
protected String[] instrumentationNames() {
20+
return new String[] {"test"};
21+
}
22+
23+
@Override
24+
protected CharSequence spanType() {
25+
return "test-type";
26+
}
27+
28+
@Override
29+
protected CharSequence component() {
30+
return "test-component";
31+
}
32+
33+
@Override
34+
protected String service() {
35+
return "test-service";
36+
}
37+
38+
@Override
39+
protected String dbType() {
40+
return null;
41+
}
42+
43+
@Override
44+
protected String dbUser(Object connection) {
45+
return null;
46+
}
47+
48+
@Override
49+
protected String dbInstance(Object connection) {
50+
return null;
51+
}
52+
53+
@Override
54+
protected CharSequence dbHostname(Object connection) {
55+
return null;
56+
}
57+
};
58+
59+
@Test
60+
void processDatabaseTypeWithNullDbTypeDoesNotThrowOrTag() {
61+
assertDoesNotThrow(() -> decorator.processDatabaseType(span, null));
62+
63+
verify(span, never()).setTag(anyString(), anyString());
64+
}
65+
66+
@Test
67+
void dbServiceWithNullDbTypeReturnsNull() {
68+
assertNull(decorator.dbService(null, null));
69+
}
70+
}

0 commit comments

Comments
 (0)