]> source.dussan.org Git - sonarqube.git/blob
2e9c1c1daa54ca20546152bc00d2fcb4b19225c0
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2016 SonarSource SA
4  * mailto:contact 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
21 package org.sonar.server.component.es;
22
23 import com.google.common.base.Joiner;
24 import java.sql.PreparedStatement;
25 import java.sql.ResultSet;
26 import java.sql.SQLException;
27 import java.util.Date;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Optional;
31 import javax.annotation.Nullable;
32 import org.apache.commons.lang.StringUtils;
33 import org.sonar.api.resources.Qualifiers;
34 import org.sonar.api.resources.Scopes;
35 import org.sonar.db.DbClient;
36 import org.sonar.db.DbSession;
37 import org.sonar.db.ResultSetIterator;
38
39 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
40 import static org.sonar.api.measures.Metric.ValueType.DATA;
41 import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
42 import static org.sonar.db.DatabaseUtils.repeatCondition;
43
44 public class ProjectMeasuresResultSetIterator extends ResultSetIterator<ProjectMeasuresDoc> {
45
46   private static final Joiner METRICS_JOINER = Joiner.on("','");
47
48   private static final String SQL_PROJECTS = "SELECT p.uuid, p.kee, p.name, s.uuid, s.created_at FROM projects p " +
49     "LEFT OUTER JOIN snapshots s ON s.component_uuid=p.uuid AND s.islast=? " +
50     "WHERE p.enabled=? AND p.scope=? AND p.qualifier=?";
51
52   private static final String DATE_FILTER = " AND s.created_at>?";
53
54   private static final String PROJECT_FILTER = " AND p.uuid=?";
55
56   private static final String SQL_METRICS = "SELECT m.id, m.name FROM metrics m " +
57     "WHERE m.val_type NOT IN ('" + METRICS_JOINER.join(DATA.name(), DISTRIB.name()) + "') " +
58     "AND m.enabled=? AND m.hidden=?";
59
60   private static final String SQL_MEASURES = "SELECT pm.metric_id, pm.value, pm.variation_value_1, pm.text_value FROM project_measures pm " +
61     "WHERE pm.component_uuid = ? AND pm.analysis_uuid = ? " +
62     "AND pm.metric_id IN ({metricIds}) " +
63     "AND (pm.value IS NOT NULL OR pm.variation_value_1 IS NOT NULL OR pm.text_value IS NOT NULL) " +
64     "AND pm.person_id IS NULL ";
65
66   private final DbSession dbSession;
67   private final Map<Long, String> metrics;
68
69   private ProjectMeasuresResultSetIterator(PreparedStatement stmt, DbSession dbSession, Map<Long, String> metrics) throws SQLException {
70     super(stmt);
71     this.dbSession = dbSession;
72     this.metrics = metrics;
73   }
74
75   static ProjectMeasuresResultSetIterator create(DbClient dbClient, DbSession session, long afterDate, @Nullable String projectUuid) {
76     try {
77       PreparedStatement projectsStatement = createProjectsStatement(dbClient, session, afterDate, projectUuid);
78       Map<Long, String> metricIds = selectMetricIds(session);
79       return new ProjectMeasuresResultSetIterator(projectsStatement, session, metricIds);
80     } catch (SQLException e) {
81       throw new IllegalStateException("Fail to execute request to select all project measures", e);
82     }
83   }
84
85   private static PreparedStatement createProjectsStatement(DbClient dbClient, DbSession session, long afterDate, @Nullable String projectUuid) {
86     try {
87       String sql = SQL_PROJECTS;
88       sql += afterDate <= 0L ? "" : DATE_FILTER;
89       sql += projectUuid == null ? "" : PROJECT_FILTER;
90       PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
91       stmt.setBoolean(1, true);
92       stmt.setBoolean(2, true);
93       stmt.setString(3, Scopes.PROJECT);
94       stmt.setString(4, Qualifiers.PROJECT);
95       int index = 5;
96       if (afterDate > 0L) {
97         stmt.setLong(index, afterDate);
98         index++;
99       }
100       if (projectUuid != null) {
101         stmt.setString(index, projectUuid);
102       }
103       return stmt;
104     } catch (SQLException e) {
105       throw new IllegalStateException("Fail to prepare SQL request to select all project measures", e);
106     }
107   }
108
109   private static Map<Long, String> selectMetricIds(DbSession session) {
110     Map<Long, String> metrics = new HashMap<>();
111     try (PreparedStatement stmt = createMetricsStatement(session);
112       ResultSet rs = stmt.executeQuery()) {
113       while (rs.next()) {
114         metrics.put(rs.getLong(1), rs.getString(2));
115       }
116       return metrics;
117     } catch (SQLException e) {
118       throw new IllegalStateException("Fail to execute request to select all metrics", e);
119     }
120   }
121
122   private static PreparedStatement createMetricsStatement(DbSession session) throws SQLException {
123     PreparedStatement stmt = session.getConnection().prepareStatement(SQL_METRICS);
124     stmt.setBoolean(1, true);
125     stmt.setBoolean(2, false);
126     return stmt;
127   }
128
129   @Override
130   protected ProjectMeasuresDoc read(ResultSet rs) throws SQLException {
131     String projectUuid = rs.getString(1);
132     Measures measures = selectMeasures(projectUuid, rs.getString(4));
133     ProjectMeasuresDoc doc = new ProjectMeasuresDoc()
134       .setId(projectUuid)
135       .setKey(rs.getString(2))
136       .setName(rs.getString(3))
137       .setQualityGate(measures.qualityGateStatus)
138       .setMeasuresFromMap(measures.numericMeasures);
139     long analysisDate = rs.getLong(5);
140     doc.setAnalysedAt(rs.wasNull() ? null : new Date(analysisDate));
141     return doc;
142   }
143
144   private Measures selectMeasures(String projectUuid, @Nullable String analysisUuid) {
145     Measures measures = new Measures();
146     try (PreparedStatement stmt = createMeasuresStatement(projectUuid, analysisUuid);
147       ResultSet rs = stmt.executeQuery()) {
148       while (rs.next()) {
149         readMeasure(rs, measures);
150       }
151       return measures;
152     } catch (Exception e) {
153       throw new IllegalStateException(String.format("Fail to execute request to select measures of project %s, analysis %s", projectUuid, analysisUuid), e);
154     }
155   }
156
157   private void readMeasure(ResultSet rs, Measures measures) throws SQLException {
158     String metricKey = metrics.get(rs.getLong(1));
159     Optional<Double> value = metricKey.startsWith("new_") ? getDouble(rs, 3) : getDouble(rs, 2);
160     if (value.isPresent()) {
161       measures.addNumericMeasure(metricKey, value.get());
162       return;
163     } else if (ALERT_STATUS_KEY.equals(metricKey)) {
164       String textValue = rs.getString(4);
165       if (!rs.wasNull()) {
166         measures.setQualityGateStatus(textValue);
167         return;
168       }
169     }
170     throw new IllegalArgumentException("Measure has no value");
171   }
172
173   private PreparedStatement createMeasuresStatement(String projectUuid, String analysisUuid) throws SQLException {
174     String sql = StringUtils.replace(SQL_MEASURES, "{metricIds}", repeatCondition("?", metrics.size(), ","));
175     PreparedStatement stmt = dbSession.getConnection().prepareStatement(sql);
176     stmt.setString(1, projectUuid);
177     stmt.setString(2, analysisUuid);
178     int index = 3;
179     for (Long metricId : metrics.keySet()) {
180       stmt.setLong(index, metricId);
181       index++;
182     }
183     return stmt;
184   }
185
186   private static Optional<Double> getDouble(ResultSet rs, int index) {
187     try {
188       Double value = rs.getDouble(index);
189       if (!rs.wasNull()) {
190         return Optional.of(value);
191       }
192       return Optional.empty();
193     } catch (SQLException e) {
194       throw new IllegalStateException("Fail to get double value", e);
195     }
196   }
197
198   private static class Measures {
199     private Map<String, Object> numericMeasures = new HashMap<>();
200     private String qualityGateStatus;
201
202     Measures addNumericMeasure(String metricKey, double value) {
203       numericMeasures.put(metricKey, value);
204       return this;
205     }
206
207     Measures setQualityGateStatus(String qualityGateStatus) {
208       this.qualityGateStatus = qualityGateStatus;
209       return this;
210     }
211   }
212
213 }