]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3825 improve performance of SQL request
authorSimon Brandhof <simon.brandhof@gmail.com>
Fri, 14 Dec 2012 17:26:23 +0000 (18:26 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Fri, 14 Dec 2012 17:26:23 +0000 (18:26 +0100)
* UNION is replaced by INNER JOINs
* sorting is done by Java but not by db

plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java
sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java
sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterResult.java
sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterRow.java
sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java
sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java
sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterConditionTest.java
sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterFactoryTest.java

index 296540d7726dcd9c5c9267473ffa32e4d1b229f7..01429a561d7104d45471999a2331d37827611b89 100644 (file)
@@ -448,7 +448,6 @@ measure_filter.treemap.change=Change Treemap
 measure_filter.add_column_button=Add Column
 measure_filter.widget.unknown_filter_warning=This widget is configured to display a measure filter that doesn't exist anymore.
 measure_filter.error.UNKNOWN=Unexpected error. Please contact the administrator.
-measure_filter.error.TOO_MANY_RESULTS=Too many results. Please refine your search.
 
 #------------------------------------------------------------------------------
 #
index 746902aff2515770e9109b9105cd0a4430a218f9..a483e0f2b4b40fa07bfa9b2b4df72f2cea1920ea 100644 (file)
@@ -81,17 +81,22 @@ public class MeasureFilterCondition {
     return period;
   }
 
-  String valueColumn() {
+  StringBuilder appendSqlColumn(StringBuilder sb, int conditionIndex) {
+    sb.append("pmcond").append(conditionIndex);
     if (period != null) {
-      return "pm.variation_value_" + period;
+      sb.append(".variation_value_").append(period).toString();
+    } else {
+      sb.append(".value");
     }
-    return "pm.value";
+    return sb;
   }
 
-  StringBuilder appendSqlCondition(StringBuilder sql) {
-    sql.append(" pm.metric_id=");
+  StringBuilder appendSqlCondition(StringBuilder sql, int conditionIndex) {
+    sql.append(" pmcond").append(conditionIndex).append(".metric_id=");
     sql.append(metric.getId());
-    sql.append(" AND ").append(valueColumn()).append(operator.getSql()).append(value);
+    sql.append(" AND ");
+    appendSqlColumn(sql, conditionIndex);
+    sql.append(operator.getSql()).append(value);
     return sql;
   }
 
index 84d3211ecf2ee22e442bb07ea1c81ea53076c1b6..97146643ce95eb157ea5d3b9ee07485b5664bcfc 100644 (file)
@@ -36,7 +36,6 @@ public class MeasureFilterEngine implements ServerComponent {
   private final MeasureFilterFactory factory;
   private final MeasureFilterExecutor executor;
 
-  private static final int MAX_ROWS = 5000;
 
   public MeasureFilterEngine(MeasureFilterFactory factory, MeasureFilterExecutor executor) {
     this.executor = executor;
@@ -57,11 +56,7 @@ public class MeasureFilterEngine implements ServerComponent {
     try {
       MeasureFilter filter = factory.create(filterMap);
       List<MeasureFilterRow> rows = executor.execute(filter, context);
-      if (rows.size() <= MAX_ROWS) {
-        result.setRows(rows);
-      } else {
-        result.setError(MeasureFilterResult.Error.TOO_MANY_RESULTS);
-      }
+      result.setRows(rows);
       result.setDurationInMs(System.currentTimeMillis() - start);
       log(context, result, logger);
 
index 118733e9e59d9053b8f2ea512a3533441a510558..562e69a24e102c37925119d4ca1c9011a147805a 100644 (file)
@@ -26,7 +26,7 @@ import java.util.List;
 public class MeasureFilterResult {
 
   public static enum Error {
-    TOO_MANY_RESULTS, UNKNOWN
+    UNKNOWN
   }
 
   private List<MeasureFilterRow> rows = null;
index cf041422c4289a3989d4cf6ddf8f009e0baa7f23..a168e3401118021319397bccaa6b88afb958157f 100644 (file)
  */
 package org.sonar.core.measure;
 
+import org.apache.commons.lang.StringUtils;
+
+import java.sql.Date;
+
 public class MeasureFilterRow {
   private final long snapshotId;
   private final long resourceId;
   private final long resourceRootId;
-  private String sortText;
+  private String sortText = null;
+  private Date sortDate = null;
+  private Double sortDouble = null;
 
   MeasureFilterRow(long snapshotId, long resourceId, long resourceRootId) {
     this.snapshotId = snapshotId;
@@ -47,8 +53,23 @@ public class MeasureFilterRow {
     return sortText;
   }
 
-  MeasureFilterRow setSortText(String s) {
-    this.sortText = s;
-    return this;
+  void setSortText(String s) {
+    this.sortText = StringUtils.defaultString(s);
+  }
+
+  Date getSortDate() {
+    return sortDate;
+  }
+
+  void setSortDate(Date sortDate) {
+    this.sortDate = sortDate;
+  }
+
+  Double getSortDouble() {
+    return sortDouble;
+  }
+
+  void setSortDouble(Double sortDouble) {
+    this.sortDouble = sortDouble;
   }
 }
index 3fc40ed04f1ea16c97c6a2d3930b0b79e2529d97..682292dee8ed6fdec7420e8200f1becdb7aa3177 100644 (file)
@@ -67,10 +67,18 @@ class MeasureFilterSort {
     return metric;
   }
 
-  boolean isSortedByDatabase() {
+  boolean isOnMeasure() {
+    return metric != null;
+  }
+
+  boolean isOnNumericMeasure() {
     return metric != null && metric.isNumericType();
   }
 
+  boolean isOnDate() {
+    return Field.DATE.equals(field);
+  }
+
   boolean isAsc() {
     return asc;
   }
@@ -102,9 +110,9 @@ class MeasureFilterSort {
         break;
       case METRIC:
         if (metric.isNumericType()) {
-          column = (period != null ? "pm.variation_value_" + period : "pm.value");
+          column = (period != null ? "pmsort.variation_value_" + period : "pmsort.value");
         } else {
-          column = "pm.text_value";
+          column = "pmsort.text_value";
         }
         break;
       default:
@@ -112,5 +120,4 @@ class MeasureFilterSort {
     }
     return column;
   }
-
 }
index f332a2f0d8318e3aa3f6aa3a6fcc8ba7d931c52a..77d461fd99b61d42fe4c9503a3600e503f509b8c 100644 (file)
@@ -22,12 +22,11 @@ package org.sonar.core.measure;
 import com.google.common.base.Function;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
+import com.google.common.primitives.Doubles;
 import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang.StringUtils;
-import org.sonar.api.measures.Metric;
 import org.sonar.core.persistence.Database;
 import org.sonar.core.persistence.DatabaseUtils;
-import org.sonar.core.persistence.dialect.PostgreSql;
 import org.sonar.core.resource.SnapshotDto;
 
 import javax.annotation.Nullable;
@@ -37,25 +36,28 @@ import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Comparator;
 import java.util.List;
 
 class MeasureFilterSql {
 
+  private static final int FETCH_SIZE = 1000;
   private final Database database;
   private final MeasureFilter filter;
   private final MeasureFilterContext context;
-  private final StringBuilder sql = new StringBuilder(1000);
+  private final String sql;
   private final List<Date> dateParameters = Lists.newArrayList();
 
   MeasureFilterSql(Database database, MeasureFilter filter, MeasureFilterContext context) {
     this.database = database;
     this.filter = filter;
     this.context = context;
-    init();
+    this.sql = generateSql();
   }
 
   List<MeasureFilterRow> execute(Connection connection) throws SQLException {
-    PreparedStatement statement = connection.prepareStatement(sql.toString());
+    PreparedStatement statement = connection.prepareStatement(sql);
+    statement.setFetchSize(FETCH_SIZE);
     ResultSet rs = null;
     try {
       for (int index = 0; index < dateParameters.size(); index++) {
@@ -71,196 +73,288 @@ class MeasureFilterSql {
   }
 
   String sql() {
-    return sql.toString();
+    return sql;
   }
 
-  private void init() {
-    sql.append("SELECT block.id, max(block.rid) AS rid, max(block.rootid) AS rootid, max(sortval) AS sortmax, CASE WHEN max(sortval) IS NULL THEN 1 ELSE 0 END AS sortflag ");
-    for (int index = 0; index < filter.getMeasureConditions().size(); index++) {
-      sql.append(", max(crit_").append(index).append(")");
-    }
-    sql.append(" FROM (");
+  private String generateSql() {
+    StringBuilder sb = new StringBuilder(1000);
+    sb.append("SELECT s.id, s.project_id, s.root_project_id, ");
+    sb.append(filter.sort().column());
+    sb.append(" FROM snapshots s INNER JOIN projects p ON s.project_id=p.id ");
 
-    appendSortBlock();
     for (int index = 0; index < filter.getMeasureConditions().size(); index++) {
       MeasureFilterCondition condition = filter.getMeasureConditions().get(index);
-      sql.append(" UNION ");
-      appendConditionBlock(index, condition);
+      sb.append(" INNER JOIN project_measures pmcond").append(index);
+      sb.append(" ON s.id=pmcond").append(index).append(".snapshot_id AND ");
+      condition.appendSqlCondition(sb, index);
     }
 
-    sql.append(") block GROUP BY block.id ");
-    if (!filter.getMeasureConditions().isEmpty()) {
-      sql.append(" HAVING ");
-      for (int index = 0; index < filter.getMeasureConditions().size(); index++) {
-        if (index > 0) {
-          sql.append(" AND ");
-        }
-        sql.append(" max(crit_").append(index).append(") IS NOT NULL ");
-      }
+    if (filter.isOnFavourites()) {
+      sb.append(" INNER JOIN properties props ON props.resource_id=s.project_id ");
     }
-    if (filter.sort().isSortedByDatabase()) {
-      sql.append(" ORDER BY sortflag ASC, sortmax ");
-      sql.append(filter.sort().isAsc() ? "ASC " : "DESC ");
+
+    if (filter.sort().isOnMeasure()) {
+      sb.append(" LEFT OUTER JOIN project_measures pmsort ON s.id=pmsort.snapshot_id AND pmsort.metric_id=");
+      sb.append(filter.sort().metric().getId());
+      sb.append(" AND pmsort.rule_id IS NULL AND pmsort.rule_priority IS NULL AND pmsort.characteristic_id IS NULL AND pmsort.person_id IS NULL ");
     }
-  }
 
-  private void appendSortBlock() {
-    sql.append(" SELECT s.id, s.project_id AS rid, s.root_project_id AS rootid, ").append(filter.sort().column()).append(" AS sortval ");
+    sb.append(" WHERE ");
+    appendResourceConditions(sb);
+
     for (int index = 0; index < filter.getMeasureConditions().size(); index++) {
       MeasureFilterCondition condition = filter.getMeasureConditions().get(index);
-      sql.append(", ").append(nullSelect(condition.metric())).append(" AS crit_").append(index);
+      sb.append(" AND ");
+      condition.appendSqlCondition(sb, index);
     }
-    sql.append(" FROM snapshots s INNER JOIN projects p ON s.project_id=p.id ");
-    if (filter.isOnFavourites()) {
-      sql.append(" INNER JOIN properties props ON props.resource_id=s.project_id ");
-    }
-    if (filter.sort().onMeasures()) {
-      sql.append(" LEFT OUTER JOIN project_measures pm ON s.id=pm.snapshot_id AND pm.metric_id=");
-      sql.append(filter.sort().metric().getId());
-      sql.append(" AND pm.rule_id IS NULL AND pm.rule_priority IS NULL AND pm.characteristic_id IS NULL AND pm.person_id IS NULL ");
-    }
-    sql.append(" WHERE ");
-    appendResourceConditions();
-  }
 
-  private void appendConditionBlock(int conditionIndex, MeasureFilterCondition condition) {
-    sql.append(" SELECT s.id, s.project_id AS rid, s.root_project_id AS rootid, null AS sortval ");
-    for (int j = 0; j < filter.getMeasureConditions().size(); j++) {
-      sql.append(", ");
-      if (j == conditionIndex) {
-        sql.append(condition.valueColumn());
-      } else {
-        sql.append(nullSelect(filter.getMeasureConditions().get(j).metric()));
-      }
-      sql.append(" AS crit_").append(j);
-    }
-    sql.append(" FROM snapshots s INNER JOIN projects p ON s.project_id=p.id INNER JOIN project_measures pm ON s.id=pm.snapshot_id ");
-    if (filter.isOnFavourites()) {
-      sql.append(" INNER JOIN properties props ON props.resource_id=s.project_id ");
-    }
-    sql.append(" WHERE ");
-    appendResourceConditions();
-    sql.append(" AND pm.rule_id IS NULL AND pm.rule_priority IS NULL AND pm.characteristic_id IS NULL AND pm.person_id IS NULL AND ");
-    condition.appendSqlCondition(sql);
+    return sb.toString();
   }
 
-  private void appendResourceConditions() {
-    sql.append(" s.status='P' AND s.islast=").append(database.getDialect().getTrueSqlValue());
+  private void appendResourceConditions(StringBuilder sb) {
+    sb.append(" s.status='P' AND s.islast=").append(database.getDialect().getTrueSqlValue());
     if (context.getBaseSnapshot() == null) {
-      sql.append(" AND p.copy_resource_id IS NULL ");
+      sb.append(" AND p.copy_resource_id IS NULL ");
     }
     if (!filter.getResourceQualifiers().isEmpty()) {
-      sql.append(" AND s.qualifier IN ");
-      appendInStatement(filter.getResourceQualifiers(), sql);
+      sb.append(" AND s.qualifier IN ");
+      appendInStatement(filter.getResourceQualifiers(), sb);
     }
     if (!filter.getResourceScopes().isEmpty()) {
-      sql.append(" AND s.scope IN ");
-      appendInStatement(filter.getResourceScopes(), sql);
+      sb.append(" AND s.scope IN ");
+      appendInStatement(filter.getResourceScopes(), sb);
     }
     if (!filter.getResourceLanguages().isEmpty()) {
-      sql.append(" AND p.language IN ");
-      appendInStatement(filter.getResourceLanguages(), sql);
+      sb.append(" AND p.language IN ");
+      appendInStatement(filter.getResourceLanguages(), sb);
     }
-    appendDateConditions();
-    appendFavouritesCondition();
-    appendResourceNameCondition();
-    appendResourceKeyCondition();
-    appendResourceBaseCondition();
+    appendDateConditions(sb);
+    appendFavouritesCondition(sb);
+    appendResourceNameCondition(sb);
+    appendResourceKeyCondition(sb);
+    appendResourceBaseCondition(sb);
   }
 
-  private void appendDateConditions() {
+  private void appendDateConditions(StringBuilder sb) {
     if (filter.getFromDate() != null) {
-      sql.append(" AND s.created_at >= ? ");
+      sb.append(" AND s.created_at >= ? ");
       dateParameters.add(new Date(filter.getFromDate().getTime()));
     }
     if (filter.getToDate() != null) {
-      sql.append(" AND s.created_at <= ? ");
+      sb.append(" AND s.created_at <= ? ");
       dateParameters.add(new Date(filter.getToDate().getTime()));
     }
   }
 
-  private void appendFavouritesCondition() {
+  private void appendFavouritesCondition(StringBuilder sb) {
     if (filter.isOnFavourites()) {
-      sql.append(" AND props.prop_key='favourite' AND props.resource_id IS NOT NULL AND props.user_id=");
-      sql.append(context.getUserId());
-      sql.append(" ");
+      sb.append(" AND props.prop_key='favourite' AND props.resource_id IS NOT NULL AND props.user_id=");
+      sb.append(context.getUserId());
+      sb.append(" ");
     }
   }
 
-  private void appendResourceBaseCondition() {
+  private void appendResourceBaseCondition(StringBuilder sb) {
     SnapshotDto baseSnapshot = context.getBaseSnapshot();
     if (baseSnapshot != null) {
       if (filter.isOnBaseResourceChildren()) {
-        sql.append(" AND s.parent_snapshot_id=").append(baseSnapshot.getId());
+        sb.append(" AND s.parent_snapshot_id=").append(baseSnapshot.getId());
       } else {
         Long rootSnapshotId = (baseSnapshot.getRootId() != null ? baseSnapshot.getRootId() : baseSnapshot.getId());
-        sql.append(" AND s.root_snapshot_id=").append(rootSnapshotId);
-        sql.append(" AND s.path LIKE '").append(StringUtils.defaultString(baseSnapshot.getPath())).append(baseSnapshot.getId()).append(".%'");
+        sb.append(" AND s.root_snapshot_id=").append(rootSnapshotId);
+        sb.append(" AND s.path LIKE '").append(StringUtils.defaultString(baseSnapshot.getPath())).append(baseSnapshot.getId()).append(".%'");
       }
     }
   }
 
-  private void appendResourceKeyCondition() {
+  private void appendResourceKeyCondition(StringBuilder sb) {
     if (StringUtils.isNotBlank(filter.getResourceKeyRegexp())) {
-      sql.append(" AND UPPER(p.kee) LIKE '");
+      sb.append(" AND UPPER(p.kee) LIKE '");
       // limitation : special characters _ and % are not escaped
       String regexp = StringEscapeUtils.escapeSql(filter.getResourceKeyRegexp());
       regexp = StringUtils.replaceChars(regexp, '*', '%');
       regexp = StringUtils.replaceChars(regexp, '?', '_');
-      sql.append(StringUtils.upperCase(regexp)).append("'");
+      sb.append(StringUtils.upperCase(regexp)).append("'");
     }
   }
 
-  private void appendResourceNameCondition() {
+  private void appendResourceNameCondition(StringBuilder sb) {
     if (StringUtils.isNotBlank(filter.getResourceName())) {
-      sql.append(" AND s.project_id IN (SELECT rindex.resource_id FROM resource_index rindex WHERE rindex.kee like '");
-      sql.append(StringEscapeUtils.escapeSql(StringUtils.lowerCase(filter.getResourceName())));
-      sql.append("%'");
+      sb.append(" AND s.project_id IN (SELECT rindex.resource_id FROM resource_index rindex WHERE rindex.kee like '");
+      sb.append(StringEscapeUtils.escapeSql(StringUtils.lowerCase(filter.getResourceName())));
+      sb.append("%'");
       if (!filter.getResourceQualifiers().isEmpty()) {
-        sql.append(" AND rindex.qualifier IN ");
-        appendInStatement(filter.getResourceQualifiers(), sql);
+        sb.append(" AND rindex.qualifier IN ");
+        appendInStatement(filter.getResourceQualifiers(), sb);
       }
-      sql.append(") ");
+      sb.append(") ");
     }
   }
 
   List<MeasureFilterRow> process(ResultSet rs) throws SQLException {
     List<MeasureFilterRow> rows = Lists.newArrayList();
-    boolean sortTextValues = !filter.sort().isSortedByDatabase();
+    RowProcessor rowProcessor;
+    if (filter.sort().isOnNumericMeasure()) {
+      rowProcessor = new NumericSortRowProcessor();
+    } else if (filter.sort().isOnDate()) {
+      rowProcessor = new DateSortRowProcessor();
+    } else {
+      rowProcessor = new TextSortRowProcessor();
+    }
+
     while (rs.next()) {
+      rows.add(rowProcessor.fetch(rs));
+    }
+
+    return rowProcessor.sort(rows, filter.sort().isAsc());
+  }
+
+  private static void appendInStatement(List<String> values, StringBuilder to) {
+    to.append(" ('");
+    to.append(StringUtils.join(values, "','"));
+    to.append("') ");
+  }
+
+  static abstract class RowProcessor {
+    abstract Function sortFieldFunction();
+
+    abstract Ordering sortFieldOrdering(boolean ascending);
+
+    abstract MeasureFilterRow fetch(ResultSet rs) throws SQLException;
+
+    final List<MeasureFilterRow> sort(List<MeasureFilterRow> rows, boolean ascending) {
+      Ordering<MeasureFilterRow> ordering = sortFieldOrdering(ascending).onResultOf(sortFieldFunction());
+      return ordering.immutableSortedCopy(rows);
+    }
+  }
+
+  static class TextSortRowProcessor extends RowProcessor {
+    MeasureFilterRow fetch(ResultSet rs) throws SQLException {
       MeasureFilterRow row = new MeasureFilterRow(rs.getLong(1), rs.getLong(2), rs.getLong(3));
-      if (sortTextValues) {
-        row.setSortText(rs.getString(4));
-      }
-      rows.add(row);
+      row.setSortText(rs.getString(4));
+      return row;
     }
-    if (sortTextValues) {
-      // database does not manage case-insensitive text sorting. It must be done programmatically
-      Function<MeasureFilterRow, String> function = new Function<MeasureFilterRow, String>() {
-        public String apply(@Nullable MeasureFilterRow row) {
-          return (row != null ? StringUtils.defaultString(row.getSortText()) : "");
+
+    Function sortFieldFunction() {
+      return new Function<MeasureFilterRow, String>() {
+        public String apply(MeasureFilterRow row) {
+          return row.getSortText();
         }
       };
-      Ordering<MeasureFilterRow> ordering = Ordering.from(String.CASE_INSENSITIVE_ORDER).onResultOf(function).nullsFirst();
-      if (!filter.sort().isAsc()) {
+    }
+
+    Ordering sortFieldOrdering(boolean ascending) {
+      Ordering<String> ordering = Ordering.from(String.CASE_INSENSITIVE_ORDER);
+      if (!ascending) {
         ordering = ordering.reverse();
       }
-      rows = ordering.sortedCopy(rows);
+      return ordering;
     }
-    return rows;
+
   }
 
-  private String nullSelect(Metric metric) {
-    if (metric.isNumericType() && PostgreSql.ID.equals(database.getDialect().getId())) {
-      return "null::integer";
+  static class NumericSortRowProcessor extends RowProcessor {
+    MeasureFilterRow fetch(ResultSet rs) throws SQLException {
+      MeasureFilterRow row = new MeasureFilterRow(rs.getLong(1), rs.getLong(2), rs.getLong(3));
+      double value = rs.getDouble(4);
+      if (!rs.wasNull()) {
+        row.setSortDouble(value);
+      }
+      return row;
+    }
+
+    Function sortFieldFunction() {
+      return new Function<MeasureFilterRow, Double>() {
+        public Double apply(MeasureFilterRow row) {
+          return row.getSortDouble();
+        }
+      };
+    }
+
+    Ordering sortFieldOrdering(boolean ascending) {
+      if (ascending) {
+        return Ordering.from(new Comparator<Double>() {
+          public int compare(@Nullable Double left, @Nullable Double right) {
+            if (left == right) {
+              return 0;
+            }
+            if (left == null) {
+              return 1;
+            }
+            if (right == null) {
+              return -1;
+            }
+
+            return Doubles.compare(left, right);
+          }
+        });
+      }
+      return Ordering.from(new Comparator<Double>() {
+        public int compare(@Nullable Double left, @Nullable Double right) {
+          if (left == right) {
+            return 0;
+          }
+          if (left == null) {
+            return 1;
+          }
+          if (right == null) {
+            return -1;
+          }
+
+          return -Doubles.compare(left, right);
+        }
+      });
     }
-    return "null";
   }
 
+  static class DateSortRowProcessor extends RowProcessor {
+    MeasureFilterRow fetch(ResultSet rs) throws SQLException {
+      MeasureFilterRow row = new MeasureFilterRow(rs.getLong(1), rs.getLong(2), rs.getLong(3));
+      row.setSortDate(rs.getDate(4));
+      return row;
+    }
 
-  private static void appendInStatement(List<String> values, StringBuilder to) {
-    to.append(" ('");
-    to.append(StringUtils.join(values, "','"));
-    to.append("') ");
+    Function sortFieldFunction() {
+      return new Function<MeasureFilterRow, Date>() {
+        public Date apply(MeasureFilterRow row) {
+          return row.getSortDate();
+        }
+      };
+    }
+
+    Ordering sortFieldOrdering(boolean ascending) {
+      if (ascending) {
+        return Ordering.from(new Comparator<Date>() {
+          public int compare(@Nullable Date left, @Nullable Date right) {
+            if (left == right) {
+              return 0;
+            }
+            if (left == null) {
+              return 1;
+            }
+            if (right == null) {
+              return -1;
+            }
+
+            return left.compareTo(right);
+          }
+        });
+      }
+      return Ordering.from(new Comparator<Date>() {
+        public int compare(@Nullable Date left, @Nullable Date right) {
+          if (left == right) {
+            return 0;
+          }
+          if (left == null) {
+            return 1;
+          }
+          if (right == null) {
+            return -1;
+          }
+
+          return -left.compareTo(right);
+        }
+      });
+    }
   }
 }
index 112975ff908fa0c7ab1e7cb0334f2dcc1aa45471..73b5cdb7c7421aa569c5feab8b9fa43ae1356277 100644 (file)
@@ -59,9 +59,9 @@ public class MeasureFilterConditionTest {
     assertThat(condition.operator()).isEqualTo(MeasureFilterCondition.Operator.GREATER);
     assertThat(condition.period()).isNull();
     assertThat(condition.value()).isEqualTo(10.0);
-    assertThat(condition.valueColumn()).isEqualTo("pm.value");
+    assertThat(condition.appendSqlColumn(new StringBuilder(), 1).toString()).isEqualTo("pmcond1.value");
     assertThat(condition.toString()).isNotEmpty();
-    assertThat(condition.appendSqlCondition(new StringBuilder()).toString()).isEqualTo(" pm.metric_id=123 AND pm.value>10.0");
+    assertThat(condition.appendSqlCondition(new StringBuilder(), 1).toString()).isEqualTo(" pmcond1.metric_id=123 AND pmcond1.value>10.0");
   }
 
   @Test
@@ -75,8 +75,8 @@ public class MeasureFilterConditionTest {
     assertThat(condition.operator()).isEqualTo(MeasureFilterCondition.Operator.LESS_OR_EQUALS);
     assertThat(condition.period()).isEqualTo(3);
     assertThat(condition.value()).isEqualTo(10.0);
-    assertThat(condition.valueColumn()).isEqualTo("pm.variation_value_3");
+    assertThat(condition.appendSqlColumn(new StringBuilder(), 2).toString()).isEqualTo("pmcond2.variation_value_3");
     assertThat(condition.toString()).isNotEmpty();
-    assertThat(condition.appendSqlCondition(new StringBuilder()).toString()).isEqualTo(" pm.metric_id=123 AND pm.variation_value_3<=10.0");
+    assertThat(condition.appendSqlCondition(new StringBuilder(), 2).toString()).isEqualTo(" pmcond2.metric_id=123 AND pmcond2.variation_value_3<=10.0");
   }
 }
index 936b3afff77532f0d62b4dfa058de07cde7510ae..472976e65cae65b19534886b395d4e26d934b2ee 100644 (file)
@@ -43,7 +43,7 @@ public class MeasureFilterFactoryTest {
     Map<String, Object> props = ImmutableMap.<String, Object>of("sort", "metric:ncloc");
     MeasureFilter filter = factory.create(props);
 
-    assertThat(filter.sort().column()).isEqualTo("pm.value");
+    assertThat(filter.sort().column()).isEqualTo("pmsort.value");
     assertThat(filter.sort().metric().getKey()).isEqualTo("ncloc");
     assertThat(filter.sort().period()).isNull();
   }
@@ -54,7 +54,7 @@ public class MeasureFilterFactoryTest {
     Map<String, Object> props = ImmutableMap.<String, Object>of("sort", "metric:ncloc:3");
     MeasureFilter filter = factory.create(props);
 
-    assertThat(filter.sort().column()).isEqualTo("pm.variation_value_3");
+    assertThat(filter.sort().column()).isEqualTo("pmsort.variation_value_3");
     assertThat(filter.sort().metric().getKey()).isEqualTo("ncloc");
     assertThat(filter.sort().period()).isEqualTo(3);
   }