Skip to content

Commit 3bb501f

Browse files
authored
Fix missing parentheses around OR conditions in JDBCZipkinQueryDAO.getTraces() (#13790)
Fix incorrect trace ID filter (OR chain without parentheses) in JDBCZipkinQueryDAO.getTraces(), replaced with IN clause.
1 parent f213c3b commit 3bb501f

File tree

3 files changed

+116
-10
lines changed

3 files changed

+116
-10
lines changed

docs/en/changes/changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* Fix missing `taskId` filter and incorrect `IN` clause parameter binding in `JDBCJFRDataQueryDAO` and `JDBCPprofDataQueryDAO`.
99
* Remove deprecated `GroupBy.field_name` from BanyanDB `MeasureQuery` request building (Phase 1 of staged removal across repos).
1010
* Push `taskId` filter down to the storage layer in `IAsyncProfilerTaskLogQueryDAO`, removing in-memory filtering from `AsyncProfilerQueryService`.
11+
* Fix missing parentheses around OR conditions in `JDBCZipkinQueryDAO.getTraces()`, which caused the table filter to be bypassed for all but the first trace ID. Replaced with a proper `IN` clause.
1112

1213
#### UI
1314

oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/common/dao/JDBCZipkinQueryDAO.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.sql.ResultSet;
4343
import java.sql.SQLException;
4444
import java.util.ArrayList;
45+
import java.util.Collections;
4546
import java.util.HashSet;
4647
import java.util.LinkedHashMap;
4748
import java.util.List;
@@ -286,16 +287,9 @@ public List<List<Span>> getTraces(final Set<String> traceIds, final Duration dur
286287
sql.append(JDBCTableInstaller.TABLE_COLUMN).append(" = ?");
287288
condition.add(ZipkinSpanRecord.INDEX_NAME);
288289

289-
int i = 0;
290-
sql.append(" and ");
291-
for (final String traceId : traceIds) {
292-
sql.append(ZipkinSpanRecord.TRACE_ID).append(" = ?");
293-
condition.add(traceId);
294-
if (i != traceIds.size() - 1) {
295-
sql.append(" or ");
296-
}
297-
i++;
298-
}
290+
sql.append(" and ").append(ZipkinSpanRecord.TRACE_ID)
291+
.append(" in (").append(String.join(",", Collections.nCopies(traceIds.size(), "?"))).append(")");
292+
condition.addAll(traceIds);
299293

300294
sql.append(" order by ").append(ZipkinSpanRecord.TIMESTAMP_MILLIS).append(" desc");
301295

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.oap.server.storage.plugin.jdbc.common.dao;
20+
21+
import org.apache.skywalking.oap.server.core.zipkin.ZipkinSpanRecord;
22+
import org.apache.skywalking.oap.server.library.client.jdbc.hikaricp.JDBCClient;
23+
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.JDBCTableInstaller;
24+
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.TableHelper;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Test;
27+
import org.junit.jupiter.api.extension.ExtendWith;
28+
import org.mockito.Mock;
29+
import org.mockito.junit.jupiter.MockitoExtension;
30+
import org.mockito.junit.jupiter.MockitoSettings;
31+
import org.mockito.quality.Strictness;
32+
33+
import java.util.ArrayList;
34+
import java.util.Arrays;
35+
import java.util.Collections;
36+
import java.util.LinkedHashSet;
37+
import java.util.Set;
38+
import java.util.concurrent.atomic.AtomicReference;
39+
40+
import static org.assertj.core.api.Assertions.assertThat;
41+
import static org.mockito.ArgumentMatchers.any;
42+
import static org.mockito.ArgumentMatchers.anyString;
43+
import static org.mockito.Mockito.doAnswer;
44+
import static org.mockito.Mockito.when;
45+
46+
@ExtendWith(MockitoExtension.class)
47+
@MockitoSettings(strictness = Strictness.LENIENT)
48+
class JDBCZipkinQueryDAOTest {
49+
50+
@Mock
51+
private JDBCClient jdbcClient;
52+
@Mock
53+
private TableHelper tableHelper;
54+
55+
private JDBCZipkinQueryDAO dao;
56+
57+
@BeforeEach
58+
void setUp() {
59+
dao = new JDBCZipkinQueryDAO(jdbcClient, tableHelper);
60+
}
61+
62+
@Test
63+
void getTraces_shouldUseInClauseForMultipleTraceIds() throws Exception {
64+
when(tableHelper.getTablesWithinTTL(ZipkinSpanRecord.INDEX_NAME))
65+
.thenReturn(Collections.singletonList("zipkin_span"));
66+
67+
final AtomicReference<String> capturedSql = new AtomicReference<>();
68+
final AtomicReference<Object[]> capturedParams = new AtomicReference<>();
69+
doAnswer(invocation -> {
70+
capturedSql.set(invocation.getArgument(0));
71+
final Object[] allArgs = invocation.getArguments();
72+
capturedParams.set(Arrays.copyOfRange(allArgs, 2, allArgs.length));
73+
return new ArrayList<>();
74+
}).when(jdbcClient).executeQuery(anyString(), any(), any(Object[].class));
75+
76+
final Set<String> traceIds = new LinkedHashSet<>(Arrays.asList("abc123", "def456", "ghi789"));
77+
dao.getTraces(traceIds, null);
78+
79+
final String sql = capturedSql.get();
80+
assertThat(sql).contains(ZipkinSpanRecord.TRACE_ID + " in (?,?,?)");
81+
assertThat(sql).doesNotContain(" or ");
82+
83+
assertThat(capturedParams.get())
84+
.contains("abc123", "def456", "ghi789");
85+
}
86+
87+
@Test
88+
void getTraces_shouldReturnEmptyListWhenTraceIdsEmpty() throws Exception {
89+
final Set<String> traceIds = Collections.emptySet();
90+
assertThat(dao.getTraces(traceIds, null)).isEmpty();
91+
}
92+
93+
@Test
94+
void getTraces_singleTraceIdShouldProduceInClauseWithOnePlaceholder() throws Exception {
95+
when(tableHelper.getTablesWithinTTL(ZipkinSpanRecord.INDEX_NAME))
96+
.thenReturn(Collections.singletonList("zipkin_span"));
97+
98+
final AtomicReference<String> capturedSql = new AtomicReference<>();
99+
doAnswer(invocation -> {
100+
capturedSql.set(invocation.getArgument(0));
101+
return new ArrayList<>();
102+
}).when(jdbcClient).executeQuery(anyString(), any(), any(Object[].class));
103+
104+
final Set<String> traceIds = Collections.singleton("abc123");
105+
dao.getTraces(traceIds, null);
106+
107+
final String sql = capturedSql.get();
108+
assertThat(sql).contains(JDBCTableInstaller.TABLE_COLUMN + " = ?");
109+
assertThat(sql).contains(ZipkinSpanRecord.TRACE_ID + " in (?)");
110+
}
111+
}

0 commit comments

Comments
 (0)