Skip to content

Commit 85be79a

Browse files
SLCORE-1819 Migrate existing known findings to the new database
1 parent 48baf18 commit 85be79a

File tree

9 files changed

+248
-41
lines changed

9 files changed

+248
-41
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* SonarLint Core - Commons
3+
* Copyright (C) 2016-2025 SonarSource Sàrl
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonarsource.sonarlint.core.commons.storage.repository;
21+
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import org.sonarsource.sonarlint.core.commons.KnownFinding;
25+
26+
public record Findings(List<KnownFinding> issues, List<KnownFinding> hotspots) {
27+
public Findings mergeWith(Findings other) {
28+
var mergedIssues = new ArrayList<>(issues);
29+
mergedIssues.addAll(other.issues);
30+
var mergedHotspots = new ArrayList<KnownFinding>(hotspots);
31+
mergedHotspots.addAll(other.hotspots);
32+
return new Findings(mergedIssues, mergedHotspots);
33+
}
34+
}

backend/commons/src/main/java/org/sonarsource/sonarlint/core/commons/storage/repository/KnownFindingsRepository.java

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@
2121

2222
import java.nio.file.Path;
2323
import java.time.LocalDateTime;
24-
import java.time.ZoneId;
24+
import java.time.ZoneOffset;
2525
import java.util.List;
26+
import java.util.Map;
27+
import java.util.stream.Stream;
2628
import org.jooq.Configuration;
2729
import org.jooq.Record;
2830
import org.sonarsource.sonarlint.core.commons.KnownFinding;
2931
import org.sonarsource.sonarlint.core.commons.KnownFindingType;
3032
import org.sonarsource.sonarlint.core.commons.LineWithHash;
3133
import org.sonarsource.sonarlint.core.commons.api.TextRangeWithHash;
3234
import org.sonarsource.sonarlint.core.commons.storage.SonarLintDatabase;
35+
import org.sonarsource.sonarlint.core.commons.storage.model.tables.records.KnownFindingsRecord;
3336

3437
import static org.sonarsource.sonarlint.core.commons.storage.model.Tables.KNOWN_FINDINGS;
3538

@@ -41,6 +44,56 @@ public KnownFindingsRepository(SonarLintDatabase database) {
4144
this.database = database;
4245
}
4346

47+
public void storeFindings(Map<String, Map<Path, Findings>> findingsPerFilePerConfigScopeId) {
48+
database.dsl().batchMerge(findingsPerFilePerConfigScopeId.entrySet().stream()
49+
.flatMap(configScopeEntry -> {
50+
var configScopeId = configScopeEntry.getKey();
51+
return configScopeEntry.getValue().entrySet().stream()
52+
.flatMap(fileEntry -> {
53+
var filePath = fileEntry.getKey();
54+
var findings = fileEntry.getValue();
55+
return Stream.<KnownFindingsRecord>concat(
56+
findings.issues().stream()
57+
.map(finding -> createRecord(finding, configScopeId, filePath, KnownFindingType.ISSUE)),
58+
findings.hotspots().stream()
59+
.map(finding -> createRecord(finding, configScopeId, filePath, KnownFindingType.HOTSPOT)));
60+
});
61+
})
62+
.toList())
63+
.execute();
64+
}
65+
66+
private static KnownFindingsRecord createRecord(KnownFinding finding, String configScopeId, Path filePath, KnownFindingType type) {
67+
var textRangeWithHash = finding.getTextRangeWithHash();
68+
var startLine = textRangeWithHash == null ? null : textRangeWithHash.getStartLine();
69+
var startLineOffset = textRangeWithHash == null ? null : textRangeWithHash.getStartLineOffset();
70+
var endLine = textRangeWithHash == null ? null : textRangeWithHash.getEndLine();
71+
var endLineOffset = textRangeWithHash == null ? null : textRangeWithHash.getEndLineOffset();
72+
var textRangeHash = textRangeWithHash == null ? null : textRangeWithHash.getHash();
73+
74+
var lineWithHash = finding.getLineWithHash();
75+
var line = lineWithHash == null ? null : lineWithHash.getNumber();
76+
var lineHash = lineWithHash == null ? null : lineWithHash.getHash();
77+
var introDate = LocalDateTime.ofInstant(finding.getIntroductionDate(), ZoneOffset.UTC);
78+
79+
return new KnownFindingsRecord(
80+
finding.getId(),
81+
configScopeId,
82+
filePath.toString(),
83+
finding.getServerKey(),
84+
finding.getRuleKey(),
85+
finding.getMessage(),
86+
introDate,
87+
type.name(),
88+
startLine,
89+
startLineOffset,
90+
endLine,
91+
endLineOffset,
92+
textRangeHash,
93+
line,
94+
lineHash);
95+
}
96+
4497
public void storeKnownIssues(String configurationScopeId, Path clientRelativePath, List<KnownFinding> newKnownIssues) {
4598
storeKnownFindings(configurationScopeId, clientRelativePath, newKnownIssues, KnownFindingType.ISSUE);
4699
}
@@ -69,7 +122,7 @@ private void storeKnownFindings(String configurationScopeId, Path clientRelative
69122
var lineWithHash = finding.getLineWithHash();
70123
var line = lineWithHash == null ? null : lineWithHash.getNumber();
71124
var lineHash = lineWithHash == null ? null : lineWithHash.getHash();
72-
var introDate = LocalDateTime.ofInstant(finding.getIntroductionDate(), ZoneId.systemDefault());
125+
var introDate = LocalDateTime.ofInstant(finding.getIntroductionDate(), ZoneOffset.UTC);
73126
trx.dsl().mergeInto(KNOWN_FINDINGS)
74127
.using(trx.dsl().selectOne())
75128
.on(KNOWN_FINDINGS.ID.eq(finding.getId()))
@@ -95,8 +148,7 @@ private void storeKnownFindings(String configurationScopeId, Path clientRelative
95148
.values(finding.getId(), configurationScopeId, clientRelativePath.toString(), finding.getServerKey(), finding.getRuleKey(),
96149
finding.getMessage(), introDate, type.name(),
97150
startLine, startLineOffset, endLine, endLineOffset, textRangeHash,
98-
line, lineHash
99-
)
151+
line, lineHash)
100152
.execute();
101153
}));
102154
}
@@ -106,8 +158,7 @@ private List<KnownFinding> getKnownFindingsForFile(String configurationScopeId,
106158
.selectFrom(KNOWN_FINDINGS)
107159
.where(KNOWN_FINDINGS.CONFIGURATION_SCOPE_ID.eq(configurationScopeId)
108160
.and(KNOWN_FINDINGS.IDE_RELATIVE_FILE_PATH.eq(filePath.toString()))
109-
.and(KNOWN_FINDINGS.FINDING_TYPE.eq(type.name()))
110-
)
161+
.and(KNOWN_FINDINGS.FINDING_TYPE.eq(type.name())))
111162
.fetch();
112163
return issuesInFile.stream()
113164
.map(KnownFindingsRepository::recordToKnownFinding)
@@ -116,7 +167,7 @@ private List<KnownFinding> getKnownFindingsForFile(String configurationScopeId,
116167

117168
private static KnownFinding recordToKnownFinding(Record rec) {
118169
var id = rec.get(KNOWN_FINDINGS.ID);
119-
var introductionDate = rec.get(KNOWN_FINDINGS.INTRODUCTION_DATE).atZone(ZoneId.systemDefault()).toInstant();
170+
var introductionDate = rec.get(KNOWN_FINDINGS.INTRODUCTION_DATE).toInstant(ZoneOffset.UTC);
120171
var textRangeWithHash = getTextRangeWithHash(rec);
121172
var lineWithHash = getLineWithHash(rec);
122173
return new KnownFinding(
@@ -125,25 +176,27 @@ private static KnownFinding recordToKnownFinding(Record rec) {
125176
textRangeWithHash, lineWithHash,
126177
rec.get(KNOWN_FINDINGS.RULE_KEY),
127178
rec.get(KNOWN_FINDINGS.MESSAGE),
128-
introductionDate
129-
);
179+
introductionDate);
130180
}
131181

132182
private static LineWithHash getLineWithHash(Record rec) {
133-
if (rec.get(KNOWN_FINDINGS.LINE) == null) return null;
134183
var line = rec.get(KNOWN_FINDINGS.LINE);
184+
if (line == null) {
185+
return null;
186+
}
135187
var hash = rec.get(KNOWN_FINDINGS.LINE_HASH);
136188
return new LineWithHash(line, hash);
137189
}
138190

139191
private static TextRangeWithHash getTextRangeWithHash(Record rec) {
140-
if (rec.get(KNOWN_FINDINGS.START_LINE) == null) return null;
141192
var startLine = rec.get(KNOWN_FINDINGS.START_LINE);
193+
if (startLine == null) {
194+
return null;
195+
}
142196
var endLine = rec.get(KNOWN_FINDINGS.END_LINE);
143197
var startLineOffset = rec.get(KNOWN_FINDINGS.START_LINE_OFFSET);
144198
var endLineOffset = rec.get(KNOWN_FINDINGS.END_LINE_OFFSET);
145199
var hash = rec.get(KNOWN_FINDINGS.TEXT_RANGE_HASH);
146200
return new TextRangeWithHash(startLine, startLineOffset, endLine, endLineOffset, hash);
147201
}
148-
149202
}

backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/KnownFindingsStorageService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.sonarsource.sonarlint.core.tracking;
2121

2222
import java.io.IOException;
23+
import java.nio.file.Files;
2324
import java.nio.file.Path;
2425
import java.util.concurrent.atomic.AtomicReference;
2526
import javax.annotation.PreDestroy;
@@ -36,6 +37,10 @@ public KnownFindingsStorageService(UserPaths userPaths) {
3637
this.workDir = userPaths.getWorkDir();
3738
}
3839

40+
public boolean exists() {
41+
return Files.exists(projectsStorageBaseDir.resolve(XodusKnownFindingsStore.BACKUP_TAR_GZ));
42+
}
43+
3944
public synchronized XodusKnownFindingsStore get() {
4045
var store = trackedIssuesStore.get();
4146
if (store == null) {
@@ -58,4 +63,10 @@ public void close() {
5863
}
5964
}
6065

66+
public void delete() {
67+
var store = trackedIssuesStore.getAndSet(null);
68+
if (store != null) {
69+
store.delete();
70+
}
71+
}
6172
}

backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/TrackingService.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
package org.sonarsource.sonarlint.core.tracking;
2121

22+
import jakarta.annotation.PostConstruct;
2223
import java.net.URI;
2324
import java.nio.file.Path;
2425
import java.time.Instant;
@@ -117,6 +118,17 @@ public TrackingService(SonarLintRpcClient client, ConfigurationRepository config
117118
this.databaseService = databaseService;
118119
}
119120

121+
@PostConstruct
122+
public void migrateData() {
123+
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment() && knownFindingsStorageService.exists()) {
124+
var repository = new KnownFindingsRepository(databaseService.getDatabase());
125+
var xodusKnownFindingsStore = knownFindingsStorageService.get();
126+
var findingsPerConfigScope = xodusKnownFindingsStore.loadAll();
127+
repository.storeFindings(findingsPerConfigScope);
128+
knownFindingsStorageService.delete();
129+
}
130+
}
131+
120132
@EventListener
121133
public void onAnalysisStarted(AnalysisStartedEvent event) {
122134
var configurationScopeId = event.getConfigurationScopeId();
@@ -168,7 +180,6 @@ private MatchingResult matchWithServerFindings(String configurationScopeId, Matc
168180
var effectiveBindingOpt = configurationRepository.getEffectiveBinding(configurationScopeId);
169181
var activeBranchOpt = branchTrackingService.awaitEffectiveSonarProjectBranch(configurationScopeId);
170182
var translationOpt = pathTranslationService.getOrComputePathTranslation(configurationScopeId);
171-
var knownFindingsStore = knownFindingsStorageService.get();
172183
var issuesToReport = matchingSession.getIssuesPerFile();
173184
var hotspotsToReport = matchingSession.getSecurityHotspotsPerFile();
174185
if (effectiveBindingOpt.isPresent() && activeBranchOpt.isPresent() && translationOpt.isPresent()) {
@@ -191,34 +202,34 @@ private MatchingResult matchWithServerFindings(String configurationScopeId, Matc
191202
return Map.entry(ideRelativePath, matches);
192203
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
193204
}
194-
issuesToReport.forEach((clientRelativePath, trackedIssues) -> storeTrackedIssues(knownFindingsStore, configurationScopeId, clientRelativePath, trackedIssues));
195-
hotspotsToReport.forEach((clientRelativePath, trackedHotspots) -> storeTrackedSecurityHotspots(knownFindingsStore, configurationScopeId, clientRelativePath, trackedHotspots));
205+
issuesToReport.forEach((clientRelativePath, trackedIssues) -> storeTrackedIssues(configurationScopeId, clientRelativePath, trackedIssues));
206+
hotspotsToReport.forEach((clientRelativePath, trackedHotspots) -> storeTrackedSecurityHotspots(configurationScopeId, clientRelativePath, trackedHotspots));
196207
eventPublisher.publishEvent(new MatchingSessionEndedEvent(matchingSession.countNewIssues(), matchingSession.countRemainingUnmatchedIssues()));
197208
return new MatchingResult(issuesToReport, hotspotsToReport);
198209
}
199210

200-
private void storeTrackedIssues(XodusKnownFindingsStore knownIssuesStore, String configurationScopeId, Path clientRelativePath, Collection<TrackedIssue> newKnownIssues) {
211+
private void storeTrackedIssues(String configurationScopeId, Path clientRelativePath, Collection<TrackedIssue> newKnownIssues) {
201212
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
202213
var knownFindingsRepository = new KnownFindingsRepository(databaseService.getDatabase());
203214
knownFindingsRepository.storeKnownIssues(configurationScopeId, clientRelativePath,
204215
newKnownIssues.stream().map(i -> new KnownFinding(i.getId(), i.getServerKey(), i.getTextRangeWithHash(), i.getLineWithHash(), i.getRuleKey(), i.getMessage(),
205216
i.getIntroductionDate())).toList());
206217
} else {
207-
knownIssuesStore.storeKnownIssues(configurationScopeId, clientRelativePath,
218+
knownFindingsStorageService.get().storeKnownIssues(configurationScopeId, clientRelativePath,
208219
newKnownIssues.stream().map(i -> new KnownFinding(i.getId(), i.getServerKey(), i.getTextRangeWithHash(), i.getLineWithHash(), i.getRuleKey(), i.getMessage(),
209220
i.getIntroductionDate())).toList());
210221
}
211222
}
212223

213-
private void storeTrackedSecurityHotspots(XodusKnownFindingsStore knownIssuesStore, String configurationScopeId, Path clientRelativePath,
224+
private void storeTrackedSecurityHotspots(String configurationScopeId, Path clientRelativePath,
214225
Collection<TrackedIssue> newKnownSecurityHotspots) {
215226
if (dogfoodEnvironmentDetectionService.isDogfoodEnvironment()) {
216227
var knownFindingsRepository = new KnownFindingsRepository(databaseService.getDatabase());
217228
knownFindingsRepository.storeKnownSecurityHotspots(configurationScopeId, clientRelativePath,
218229
newKnownSecurityHotspots.stream().map(i -> new KnownFinding(i.getId(), i.getServerKey(), i.getTextRangeWithHash(), i.getLineWithHash(), i.getRuleKey(), i.getMessage(),
219230
i.getIntroductionDate())).toList());
220231
} else {
221-
knownIssuesStore.storeKnownSecurityHotspots(configurationScopeId, clientRelativePath,
232+
knownFindingsStorageService.get().storeKnownSecurityHotspots(configurationScopeId, clientRelativePath,
222233
newKnownSecurityHotspots.stream().map(i -> new KnownFinding(i.getId(), i.getServerKey(), i.getTextRangeWithHash(), i.getLineWithHash(), i.getRuleKey(), i.getMessage(),
223234
i.getIntroductionDate())).toList());
224235
}
@@ -301,7 +312,6 @@ private static TrackedIssue updateTrackedIssueWithLocalOnlyIssueData(TrackedIssu
301312
}
302313

303314
private MatchingSession startMatchingSession(String configurationScopeId, Set<Path> fileRelativePaths, Set<URI> fileUris, UnaryOperator<String> fileContentProvider) {
304-
var knownFindingsStore = knownFindingsStorageService.get();
305315
var dogfoodEnvironment = dogfoodEnvironmentDetectionService.isDogfoodEnvironment();
306316
Map<Path, List<KnownFinding>> issuesByRelativePath;
307317
Map<Path, List<KnownFinding>> hotspotsByRelativePath;
@@ -312,6 +322,7 @@ private MatchingSession startMatchingSession(String configurationScopeId, Set<Pa
312322
hotspotsByRelativePath = fileRelativePaths.stream()
313323
.collect(toMap(Function.identity(), relativePath -> knownFindingsRepository.loadSecurityHotspotsForFile(configurationScopeId, relativePath)));
314324
} else {
325+
var knownFindingsStore = knownFindingsStorageService.get();
315326
issuesByRelativePath = fileRelativePaths.stream()
316327
.collect(toMap(Function.identity(), relativePath -> knownFindingsStore.loadIssuesForFile(configurationScopeId, relativePath)));
317328
hotspotsByRelativePath = fileRelativePaths.stream()

0 commit comments

Comments
 (0)