]> source.dussan.org Git - sonarqube.git/blob
160247904de494c1984989ee26deefeb87e746f7
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 SonarSource SA
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.sonar.server.issue.index;
21
22 import com.google.common.base.CharMatcher;
23 import com.google.common.base.Splitter;
24 import com.google.common.collect.Iterators;
25 import java.sql.PreparedStatement;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.stream.Collectors;
32 import java.util.stream.IntStream;
33 import javax.annotation.CheckForNull;
34 import javax.annotation.Nullable;
35 import org.apache.commons.lang.StringUtils;
36 import org.sonar.api.resources.Scopes;
37 import org.sonar.api.rules.RuleType;
38 import org.sonar.db.DatabaseUtils;
39 import org.sonar.db.DbClient;
40 import org.sonar.db.DbSession;
41 import org.sonar.db.ResultSetIterator;
42
43 import static com.google.common.base.Preconditions.checkArgument;
44 import static org.sonar.api.utils.DateUtils.longToDate;
45 import static org.sonar.db.DatabaseUtils.getLong;
46 import static org.sonar.server.security.SecurityStandardHelper.getCwe;
47 import static org.sonar.server.security.SecurityStandardHelper.getOwaspTop10;
48 import static org.sonar.server.security.SecurityStandardHelper.getSansTop25;
49 import static org.sonar.server.security.SecurityStandardHelper.getSecurityStandards;
50 import static org.sonar.server.security.SecurityStandardHelper.getSonarSourceSecurityCategories;
51
52 /**
53  * Scrolls over table ISSUES and reads documents to populate
54  * the issues index
55  */
56 class IssueIteratorForSingleChunk implements IssueIterator {
57
58   private static final String[] FIELDS = {
59     // column 1
60     "i.kee",
61     "i.assignee",
62     "i.line",
63     "i.resolution",
64     "i.severity",
65     "i.status",
66     "i.effort",
67     "i.author_login",
68     "i.issue_close_date",
69     "i.issue_creation_date",
70
71     // column 11
72     "i.issue_update_date",
73     "r.id",
74     "r.language",
75     "c.uuid",
76     "c.module_uuid_path",
77     "c.path",
78     "c.scope",
79     "c.organization_uuid",
80     "c.project_uuid",
81     "c.main_branch_project_uuid",
82
83     // column 21
84     "i.tags",
85     "i.issue_type",
86     "r.security_standards"
87   };
88
89   private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from issues i " +
90     "inner join rules r on r.id = i.rule_id " +
91     "inner join projects c on c.uuid = i.component_uuid ";
92
93   private static final String PROJECT_FILTER = " and c.project_uuid = ? and i.project_uuid = ? ";
94   private static final String ISSUE_KEY_FILTER_PREFIX = " and i.kee in (";
95   private static final String ISSUE_KEY_FILTER_SUFFIX = ")";
96
97   static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
98   static final Splitter MODULE_PATH_SPLITTER = Splitter.on('.').trimResults().omitEmptyStrings();
99
100   private final DbSession session;
101
102   @CheckForNull
103   private final String projectUuid;
104
105   @CheckForNull
106   private final Collection<String> issueKeys;
107
108   private final PreparedStatement stmt;
109   private final ResultSetIterator<IssueDoc> iterator;
110
111   IssueIteratorForSingleChunk(DbClient dbClient, @Nullable String projectUuid, @Nullable Collection<String> issueKeys) {
112     checkArgument(issueKeys == null || issueKeys.size() <= DatabaseUtils.PARTITION_SIZE_FOR_ORACLE,
113       "Cannot search for more than " + DatabaseUtils.PARTITION_SIZE_FOR_ORACLE + " issue keys at once. Please provide the keys in smaller chunks.");
114     this.projectUuid = projectUuid;
115     this.issueKeys = issueKeys;
116     this.session = dbClient.openSession(false);
117
118     try {
119       String sql = createSql();
120       stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
121       iterator = createIterator();
122     } catch (Exception e) {
123       session.close();
124       throw new IllegalStateException("Fail to prepare SQL request to select all issues", e);
125     }
126   }
127
128   private IssueIteratorInternal createIterator() {
129     try {
130       setParameters(stmt);
131       return new IssueIteratorInternal(stmt);
132     } catch (SQLException e) {
133       DatabaseUtils.closeQuietly(stmt);
134       throw new IllegalStateException("Fail to prepare SQL request to select all issues", e);
135     }
136   }
137
138   @Override
139   public boolean hasNext() {
140     return iterator.hasNext();
141   }
142
143   @Override
144   public IssueDoc next() {
145     return iterator.next();
146   }
147
148   private String createSql() {
149     String sql = SQL_ALL;
150     sql += projectUuid == null ? "" : PROJECT_FILTER;
151     if (issueKeys != null && !issueKeys.isEmpty()) {
152       sql += ISSUE_KEY_FILTER_PREFIX;
153       sql += IntStream.range(0, issueKeys.size()).mapToObj(i -> "?").collect(Collectors.joining(","));
154       sql += ISSUE_KEY_FILTER_SUFFIX;
155     }
156     return sql;
157   }
158
159   private void setParameters(PreparedStatement stmt) throws SQLException {
160     int index = 1;
161     if (projectUuid != null) {
162       stmt.setString(index, projectUuid);
163       index++;
164       stmt.setString(index, projectUuid);
165       index++;
166     }
167     if (issueKeys != null) {
168       for (String key : issueKeys) {
169         stmt.setString(index, key);
170         index++;
171       }
172     }
173   }
174
175   @Override
176   public void close() {
177     try {
178       iterator.close();
179     } finally {
180       DatabaseUtils.closeQuietly(stmt);
181       session.close();
182     }
183   }
184
185   private static final class IssueIteratorInternal extends ResultSetIterator<IssueDoc> {
186
187     public IssueIteratorInternal(PreparedStatement stmt) throws SQLException {
188       super(stmt);
189     }
190
191     @Override
192     protected IssueDoc read(ResultSet rs) throws SQLException {
193       IssueDoc doc = new IssueDoc(new HashMap<>(30));
194
195       String key = rs.getString(1);
196
197       // all the fields must be present, even if value is null
198       doc.setKey(key);
199       doc.setAssigneeUuid(rs.getString(2));
200       doc.setLine(DatabaseUtils.getInt(rs, 3));
201       doc.setResolution(rs.getString(4));
202       doc.setSeverity(rs.getString(5));
203       doc.setStatus(rs.getString(6));
204       doc.setEffort(getLong(rs, 7));
205       doc.setAuthorLogin(rs.getString(8));
206       doc.setFuncCloseDate(longToDate(getLong(rs, 9)));
207       doc.setFuncCreationDate(longToDate(getLong(rs, 10)));
208       doc.setFuncUpdateDate(longToDate(getLong(rs, 11)));
209       Integer ruleId = rs.getInt(12);
210       doc.setRuleId(ruleId);
211       doc.setLanguage(rs.getString(13));
212       doc.setComponentUuid(rs.getString(14));
213       String moduleUuidPath = rs.getString(15);
214       doc.setModuleUuid(extractModule(moduleUuidPath));
215       doc.setModuleUuidPath(moduleUuidPath);
216       String scope = rs.getString(17);
217       String filePath = extractFilePath(rs.getString(16), scope);
218       doc.setFilePath(filePath);
219       doc.setDirectoryPath(extractDirPath(doc.filePath(), scope));
220       doc.setOrganizationUuid(rs.getString(18));
221       String branchUuid = rs.getString(19);
222       String mainBranchProjectUuid = DatabaseUtils.getString(rs, 20);
223       doc.setBranchUuid(branchUuid);
224       if (mainBranchProjectUuid == null) {
225         doc.setProjectUuid(branchUuid);
226         doc.setIsMainBranch(true);
227       } else {
228         doc.setProjectUuid(mainBranchProjectUuid);
229         doc.setIsMainBranch(false);
230       }
231       String tags = rs.getString(21);
232       doc.setTags(IssueIteratorForSingleChunk.TAGS_SPLITTER.splitToList(tags == null ? "" : tags));
233       doc.setType(RuleType.valueOf(rs.getInt(22)));
234       String securityStandards = rs.getString(23);
235
236       List<String> standards = getSecurityStandards(securityStandards);
237       doc.setOwaspTop10(getOwaspTop10(standards));
238       List<String> cwe = getCwe(standards);
239       doc.setCwe(cwe);
240       doc.setSansTop25(getSansTop25(cwe));
241       doc.setSonarSourceSecurityCategories(getSonarSourceSecurityCategories(cwe));
242       return doc;
243     }
244
245     @CheckForNull
246     private static String extractDirPath(@Nullable String filePath, String scope) {
247       if (filePath != null) {
248         if (Scopes.DIRECTORY.equals(scope)) {
249           return filePath;
250         }
251         int lastSlashIndex = CharMatcher.anyOf("/").lastIndexIn(filePath);
252         if (lastSlashIndex > 0) {
253           return filePath.substring(0, lastSlashIndex);
254         }
255         return "/";
256       }
257       return null;
258     }
259
260     @CheckForNull
261     private static String extractFilePath(@Nullable String filePath, String scope) {
262       // On modules, the path contains the relative path of the module starting from its parent, and in E/S we're only interested in the
263       // path
264       // of files and directories.
265       // That's why the file path should be null on modules and projects.
266       if (filePath != null && !Scopes.PROJECT.equals(scope)) {
267         return filePath;
268       }
269       return null;
270     }
271
272     private static String extractModule(String moduleUuidPath) {
273       return Iterators.getLast(IssueIteratorForSingleChunk.MODULE_PATH_SPLITTER.split(moduleUuidPath).iterator());
274     }
275   }
276 }