001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.server.filters; 021 022 import org.apache.commons.lang.StringUtils; 023 import org.slf4j.Logger; 024 import org.slf4j.LoggerFactory; 025 import org.sonar.api.ServerComponent; 026 import org.sonar.api.database.DatabaseSession; 027 import org.sonar.api.database.model.Snapshot; 028 import org.sonar.api.utils.SonarException; 029 import org.sonar.api.utils.TimeProfiler; 030 031 import javax.persistence.Query; 032 import java.util.Collections; 033 034 public class FilterExecutor implements ServerComponent { 035 private static final Logger LOG = LoggerFactory.getLogger(FilterExecutor.class); 036 private static final int SQL_INITIAL_SIZE = 1000; 037 private DatabaseSession session; 038 039 public FilterExecutor(DatabaseSession session) { 040 this.session = session; 041 } 042 043 public FilterResult execute(Filter filter) { 044 if (filter.mustReturnEmptyResult()) { 045 return new FilterResult(filter, Collections.emptyList()); 046 } 047 048 String sql = null; 049 try { 050 TimeProfiler profiler = new TimeProfiler(FilterExecutor.class).setLevelToDebug().start("Build/execute SQL query"); 051 sql = toSql(filter); 052 LOG.debug("SQL: " + sql); 053 Query query = session.getEntityManager().createNativeQuery(sql); 054 setHqlParameters(filter, query); 055 FilterResult result = new FilterResult(filter, query.getResultList()); 056 profiler.stop(); 057 058 profiler.start("Process rows"); 059 result.removeUnvalidRows(); 060 profiler.stop(); 061 062 profiler.start("Sort rows"); 063 result.sort(); 064 profiler.stop(); 065 return result; 066 067 } catch (Exception e) { 068 throw new SonarException("Fail to execute filter: " + filter.toString() + ", sql=" + sql, e); 069 } 070 } 071 072 private String toSql(Filter filter) { 073 StringBuilder sql = new StringBuilder(SQL_INITIAL_SIZE); 074 addSelectColumns(filter, sql); 075 addFromClause(filter, sql); 076 addWhereClause(filter, sql); 077 return sql.toString(); 078 } 079 080 private void addSelectColumns(Filter filter, StringBuilder sql) { 081 sql.append("SELECT s.id, MAX(s.project_id) as pid, MAX(s.root_project_id) as rpid"); 082 if (filter.isSortedByLanguage()) { 083 sql.append(", MAX(p.language) as lang "); 084 085 } else if (filter.isSortedByName()) { 086 sql.append(", MAX(p.long_name) as name "); 087 088 } else if (filter.isSortedByDate()) { 089 sql.append(", MAX(s.created_at) as createdat "); 090 091 } else if (filter.isSortedByVersion()) { 092 sql.append(", MAX(s.version) as version "); 093 } 094 if (filter.getSortedMetricId() != null) { 095 sql.append(", MAX(CASE WHEN pm.metric_id="); 096 sql.append(filter.getSortedMetricId()); 097 sql.append(" THEN "); 098 sql.append(filter.getColumnToSort()); 099 sql.append(" ELSE NULL END) AS sortvalue"); 100 sql.append(" "); 101 } 102 for (int index = 0; index < filter.getMeasureCriteria().size(); index++) { 103 MeasureCriterion criterion = filter.getMeasureCriteria().get(index); 104 String column = (criterion.isVariation() ? Filter.getVariationColumn(filter.getPeriodIndex()) : "value"); 105 sql.append(", MAX(CASE WHEN pm.metric_id="); 106 sql.append(criterion.getMetricId()); 107 sql.append(" AND pm."); 108 sql.append(column); 109 sql.append(criterion.getOperator()); 110 sql.append(criterion.getValue()); 111 sql.append(" THEN "); 112 sql.append(column); 113 sql.append(" ELSE NULL END) AS crit_"); 114 sql.append(index); 115 sql.append(" "); 116 } 117 } 118 119 private void addFromClause(Filter filter, StringBuilder sql) { 120 sql.append(" FROM snapshots s "); 121 if (filter.mustJoinMeasuresTable()) { 122 sql.append(" INNER JOIN project_measures pm ON s.id=pm.snapshot_id "); 123 } 124 sql.append(" INNER JOIN projects p ON s.project_id=p.id "); 125 } 126 127 private void addWhereClause(Filter filter, StringBuilder sql) { 128 sql.append(" WHERE "); 129 if (filter.mustJoinMeasuresTable()) { 130 if (filter.hasMeasureCriteria()) { 131 sql.append(" ( "); 132 int index = 0; 133 while (index < filter.getMeasureCriteria().size()) { 134 if (index > 0) { 135 sql.append(" OR "); 136 } 137 MeasureCriterion criterion = filter.getMeasureCriteria().get(index); 138 String column = (criterion.isVariation() ? Filter.getVariationColumn(filter.getPeriodIndex()) : "value"); 139 sql.append("(pm.metric_id=").append(criterion.getMetricId()).append(" and pm.").append(column) 140 .append(criterion.getOperator()).append(criterion.getValue()).append(")"); 141 index++; 142 } 143 144 if (filter.getSortedMetricId() != null && !filter.hasMeasureCriteriaOnMetric(filter.getSortedMetricId())) { 145 sql.append(" OR (pm.metric_id=").append(filter.getSortedMetricId()).append(") "); 146 } 147 148 sql.append(" ) AND "); 149 } 150 sql.append(" pm.rule_id IS NULL AND pm.rule_priority IS NULL"); 151 sql.append(" AND pm.characteristic_id IS NULL"); 152 sql.append(" AND pm.person_id IS NULL"); 153 sql.append(" AND "); 154 } 155 sql.append(" s.status=:status AND s.islast=:islast "); 156 if (filter.getScopes() != null) { 157 sql.append(filter.getScopes().isEmpty() ? " AND s.scope IS NULL " : " AND s.scope IN (:scopes) "); 158 } 159 if (filter.hasQualifiers()) { 160 sql.append(" AND s.qualifier IN (:qualifiers) "); 161 } else { 162 sql.append(" AND s.qualifier IS NULL "); 163 } 164 if (filter.hasLanguages()) { 165 sql.append(" AND p.language IN (:languages) "); 166 } 167 if (filter.getFavouriteIds() != null) { 168 sql.append(filter.getFavouriteIds().isEmpty() ? " AND s.project_id IS NULL " : " AND s.project_id IN (:favourites) "); 169 } 170 if (filter.hasBaseSnapshot()) { 171 sql.append(" AND s.root_snapshot_id=:root_sid AND s.path LIKE :path "); 172 } 173 if (filter.getDateCriterion() != null) { 174 sql.append(" AND s.created_at"); 175 sql.append(filter.getDateCriterion().getOperator()); 176 sql.append(" :date "); 177 } 178 if (StringUtils.isNotBlank(filter.getKeyRegexp())) { 179 sql.append(" AND UPPER(p.kee) LIKE :kee"); 180 } 181 if (StringUtils.isNotBlank(filter.getNameRegexp())) { 182 sql.append(" AND UPPER(p.long_name) LIKE :name"); 183 } 184 if (!filter.isViewContext()) { 185 sql.append(" AND p.copy_resource_id IS NULL "); 186 } 187 sql.append(" GROUP BY s.id"); 188 } 189 190 private void setHqlParameters(Filter filter, Query query) { 191 query.setParameter("status", Snapshot.STATUS_PROCESSED); 192 query.setParameter("islast", true); 193 if (filter.hasScopes()) { 194 query.setParameter("scopes", filter.getScopes()); 195 } 196 if (filter.hasQualifiers()) { 197 query.setParameter("qualifiers", filter.getQualifiers()); 198 } 199 if (filter.hasLanguages()) { 200 query.setParameter("languages", filter.getLanguages()); 201 } 202 if (filter.hasFavouriteIds()) { 203 query.setParameter("favourites", filter.getFavouriteIds()); 204 } 205 if (filter.getDateCriterion() != null) { 206 query.setParameter("date", filter.getDateCriterion().getDate()); 207 } 208 if (filter.hasBaseSnapshot()) { 209 query.setParameter("root_sid", filter.getRootSnapshotId()); 210 query.setParameter("path", new StringBuilder().append( 211 filter.getBaseSnapshotPath()).append(filter.getBaseSnapshotId()).append(".%").toString()); 212 } 213 if (StringUtils.isNotBlank(filter.getKeyRegexp())) { 214 query.setParameter("kee", StringUtils.upperCase(StringUtils.replaceChars(filter.getKeyRegexp(), '*', '%'))); 215 } 216 if (StringUtils.isNotBlank(filter.getNameRegexp())) { 217 query.setParameter("name", StringUtils.upperCase(StringUtils.replaceChars(filter.getNameRegexp(), '*', '%'))); 218 } 219 } 220 }