aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2015-03-18 09:17:50 +0100
committerJulien Lancelot <julien.lancelot@sonarsource.com>2015-03-18 09:18:46 +0100
commit8f376a0a64dac99cfddfafcded66445997a65670 (patch)
treec85216945eef38babfc84a77daec32c0077b27c5 /server/sonar-server
parentdd574e51443d8c9dc64aa7d889f7f6f1281d9ec5 (diff)
parentd8bc34c28c0b81ad685ac66e63e362a7ed29e8a1 (diff)
downloadsonarqube-8f376a0a64dac99cfddfafcded66445997a65670.tar.gz
sonarqube-8f376a0a64dac99cfddfafcded66445997a65670.zip
Merge branch-5.1
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java38
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java54
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/AddColumnsBuilder.java152
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/DdlChange.java68
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/DropColumnsBuilder.java68
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddIssuesColumns.java93
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/DropIssuesColumns.java59
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java65
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java158
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java25
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java21
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/search/SearchClient.java15
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/source/index/SourceFileResultSetIterator.java209
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexDefinition.java55
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java74
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java141
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/util/DateCollector.java46
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/util/ProgressLogger.java7
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java42
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/AddColumnsBuilderTest.java131
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/DropColumnsBuilderTest.java59
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/AddIssuesColumnsTest.java54
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/DropIssuesColumnsTest.java51
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest.java87
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java27
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java10
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/es/FakeIndexDefinition.java1
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java27
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/search/SearchClientMediumTest.java14
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/source/index/SourceFileResultSetIteratorTest.java165
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java135
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java113
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/util/DateCollectorTest.java44
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/util/ProgressLoggerTest.java4
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddIssuesColumnsTest/schema.sql26
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/before.xml22
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/schema.sql5
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceFileResultSetIteratorTest/schema.sql (renamed from server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/schema.sql)0
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceFileResultSetIteratorTest/shared.xml6
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F1_line1.json (renamed from server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line2_other_file.json)8
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F1_line2.json (renamed from server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line2.json)6
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F2_line1.json (renamed from server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line3.json)8
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P2_F3_line1.json (renamed from server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line3_other_project.json)8
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/shared.xml6
47 files changed, 1668 insertions, 754 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java
index babf3f671bd..81f9d3b85bc 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java
@@ -19,7 +19,6 @@
*/
package org.sonar.server.activity.index;
-import org.elasticsearch.action.update.UpdateRequest;
import org.sonar.core.persistence.DbSession;
import org.sonar.server.db.DbClient;
import org.sonar.server.es.BaseIndexer;
@@ -27,7 +26,6 @@ import org.sonar.server.es.BulkIndexer;
import org.sonar.server.es.EsClient;
import java.sql.Connection;
-import java.util.Iterator;
/**
* Add to Elasticsearch index {@link org.sonar.server.activity.index.ActivityIndexDefinition} the rows of
@@ -51,38 +49,18 @@ public class ActivityIndexer extends BaseIndexer {
DbSession dbSession = dbClient.openSession(false);
Connection dbConnection = dbSession.getConnection();
try {
- ActivityResultSetIterator rowIt = ActivityResultSetIterator.create(dbClient, dbConnection, lastUpdatedAt);
- long maxUpdatedAt = doIndex(bulk, rowIt);
- rowIt.close();
- return maxUpdatedAt;
+ ActivityResultSetIterator it = ActivityResultSetIterator.create(dbClient, dbConnection, lastUpdatedAt);
+ bulk.start();
+ while (it.hasNext()) {
+ bulk.add(it.next());
+ }
+ bulk.stop();
+ it.close();
+ return it.getMaxRowDate();
} finally {
dbSession.close();
}
}
- public long index(Iterator<ActivityDoc> activities) {
- BulkIndexer bulk = new BulkIndexer(esClient, ActivityIndexDefinition.INDEX);
- return doIndex(bulk, activities);
- }
-
- private long doIndex(BulkIndexer bulk, Iterator<ActivityDoc> activities) {
- long maxUpdatedAt = 0L;
- bulk.start();
- while (activities.hasNext()) {
- ActivityDoc activity = activities.next();
- bulk.add(newUpsertRequest(activity));
-
- // it's more efficient to sort programmatically than in SQL on some databases (MySQL for instance)
- maxUpdatedAt = Math.max(maxUpdatedAt, activity.getCreatedAt().getTime());
- }
- bulk.stop();
- return maxUpdatedAt;
- }
-
- private UpdateRequest newUpsertRequest(ActivityDoc doc) {
- return new UpdateRequest(ActivityIndexDefinition.INDEX, ActivityIndexDefinition.TYPE, doc.getKey())
- .doc(doc.getFields())
- .upsert(doc.getFields());
- }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java
index 701d649ff8f..5e6aae81311 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java
@@ -20,25 +20,26 @@
package org.sonar.server.activity.index;
import org.apache.commons.lang.StringUtils;
+import org.elasticsearch.action.update.UpdateRequest;
import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.text.JsonWriter;
import org.sonar.server.db.DbClient;
import org.sonar.server.db.ResultSetIterator;
+import org.sonar.server.es.EsUtils;
+import org.sonar.server.util.DateCollector;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.HashMap;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.sql.*;
+import java.util.Date;
/**
* Scrolls over table ACTIVITIES and reads documents to populate
* the index "activities/activity"
*/
-class ActivityResultSetIterator extends ResultSetIterator<ActivityDoc> {
+class ActivityResultSetIterator extends ResultSetIterator<UpdateRequest> {
private static final String[] FIELDS = {
- // column 1
"log_key",
"log_action",
"log_message",
@@ -52,6 +53,8 @@ class ActivityResultSetIterator extends ResultSetIterator<ActivityDoc> {
private static final String SQL_AFTER_DATE = SQL_ALL + " where created_at>=?";
+ private final DateCollector dates = new DateCollector();
+
private ActivityResultSetIterator(PreparedStatement stmt) throws SQLException {
super(stmt);
}
@@ -70,17 +73,30 @@ class ActivityResultSetIterator extends ResultSetIterator<ActivityDoc> {
}
@Override
- protected ActivityDoc read(ResultSet rs) throws SQLException {
- ActivityDoc doc = new ActivityDoc(new HashMap<String, Object>(10));
-
+ protected UpdateRequest read(ResultSet rs) throws SQLException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
// all the fields must be present, even if value is null
- doc.setKey(rs.getString(1));
- doc.setAction(rs.getString(2));
- doc.setMessage(rs.getString(3));
- doc.setDetails(KeyValueFormat.parse(rs.getString(4)));
- doc.setLogin(rs.getString(5));
- doc.setType(rs.getString(6));
- doc.setCreatedAt(rs.getTimestamp(7));
- return doc;
+ JsonWriter writer = JsonWriter.of(new OutputStreamWriter(bytes)).setSerializeNulls(true);
+ writer.beginObject();
+ String key = rs.getString(1);
+ writer.prop(ActivityIndexDefinition.FIELD_KEY, key);
+ writer.prop(ActivityIndexDefinition.FIELD_ACTION, rs.getString(2));
+ writer.prop(ActivityIndexDefinition.FIELD_MESSAGE, rs.getString(3));
+ writer.name(ActivityIndexDefinition.FIELD_DETAILS).valueObject(KeyValueFormat.parse(rs.getString(4)));
+ writer.prop(ActivityIndexDefinition.FIELD_LOGIN, rs.getString(5));
+ writer.prop(ActivityIndexDefinition.FIELD_TYPE, rs.getString(6));
+ Date createdAt = rs.getTimestamp(7);
+ writer.prop(ActivityIndexDefinition.FIELD_CREATED_AT, EsUtils.formatDateTime(createdAt));
+ writer.endObject().close();
+ byte[] jsonDoc = bytes.toByteArray();
+
+ // it's more efficient to sort programmatically than in SQL on some databases (MySQL for instance)
+ dates.add(createdAt);
+
+ return new UpdateRequest(ActivityIndexDefinition.INDEX, ActivityIndexDefinition.TYPE, key).doc(jsonDoc).upsert(jsonDoc);
+ }
+
+ long getMaxRowDate() {
+ return dates.getMax();
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/AddColumnsBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/AddColumnsBuilder.java
new file mode 100644
index 00000000000..8d884296c8f
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/AddColumnsBuilder.java
@@ -0,0 +1,152 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Preconditions;
+import org.sonar.core.persistence.dialect.Dialect;
+import org.sonar.core.persistence.dialect.MsSql;
+import org.sonar.core.persistence.dialect.Oracle;
+import org.sonar.core.persistence.dialect.PostgreSql;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class AddColumnsBuilder {
+
+ private final Dialect dialect;
+ private final String tableName;
+ private List<ColumnDef> columnDefs = newArrayList();
+
+ public AddColumnsBuilder(Dialect dialect, String tableName) {
+ this.tableName = tableName;
+ this.dialect = dialect;
+ }
+
+ public AddColumnsBuilder addColumn(ColumnDef columnDef) {
+ columnDefs.add(columnDef);
+ return this;
+ }
+
+ public String build() {
+ StringBuilder sql = new StringBuilder().append("ALTER TABLE ").append(tableName).append(" ");
+ switch (dialect.getId()) {
+ case PostgreSql.ID:
+ addColumns(sql, "ADD COLUMN ");
+ break;
+ case MsSql.ID:
+ sql.append("ADD ");
+ addColumns(sql, "");
+ break;
+ default:
+ sql.append("ADD (");
+ addColumns(sql, "");
+ sql.append(")");
+ }
+ return sql.toString();
+ }
+
+ private void addColumns(StringBuilder sql, String columnPrefix) {
+ for (int i = 0; i < columnDefs.size(); i++) {
+ sql.append(columnPrefix);
+ addColumn(sql, columnDefs.get(i));
+ if (i < columnDefs.size() - 1) {
+ sql.append(", ");
+ }
+ }
+ }
+
+ private void addColumn(StringBuilder sql, ColumnDef columnDef) {
+ sql.append(columnDef.getName()).append(" ").append(typeToSql(columnDef));
+ Integer limit = columnDef.getLimit();
+ if (limit != null) {
+ sql.append(" (").append(Integer.toString(limit)).append(")");
+ }
+ sql.append(columnDef.isNullable() ? " NULL" : " NOT NULL");
+ }
+
+ private String typeToSql(ColumnDef columnDef) {
+ switch (columnDef.getType()) {
+ case STRING:
+ return "VARCHAR";
+ case BIG_INTEGER:
+ if (dialect.getId().equals(Oracle.ID)) {
+ return "NUMBER (38)";
+ } else {
+ return "BIGINT";
+ }
+ default:
+ throw new IllegalArgumentException("Unsupported type : " + columnDef.getType());
+ }
+ }
+
+ public static class ColumnDef {
+ private String name;
+ private Type type;
+ private boolean isNullable;
+ private Integer limit;
+
+ public enum Type {
+ STRING, BIG_INTEGER
+ }
+
+ public ColumnDef setNullable(boolean isNullable) {
+ this.isNullable = isNullable;
+ return this;
+ }
+
+ public ColumnDef setLimit(@Nullable Integer limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ public ColumnDef setName(String name) {
+ Preconditions.checkArgument(CharMatcher.JAVA_LOWER_CASE.or(CharMatcher.anyOf("_")).matchesAllOf(name), "Column name should only contains lowercase and _ characters");
+ this.name = name;
+ return this;
+ }
+
+ public ColumnDef setType(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ public boolean isNullable() {
+ return isNullable;
+ }
+
+ @CheckForNull
+ public Integer getLimit() {
+ return limit;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Type getType() {
+ return type;
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java
index dba501d891c..72fb8da8e70 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java
@@ -91,11 +91,12 @@ public interface DatabaseMigrations {
FeedIssuesLongDates.class,
FeedFileSourcesBinaryData.class,
FeedSemaphoresLongDates.class,
- FeedProjectMeasuresLongDates.class,
FeedManualMeasuresLongDates.class,
FeedEventsLongDates.class,
AddNewCharacteristics.class,
RemovePermissionsOnModulesMigration.class,
+ AddIssuesColumns.class,
+ DropIssuesColumns.class,
// 5.2
FeedProjectLinksComponentUuid.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DdlChange.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DdlChange.java
new file mode 100644
index 00000000000..6dafe50ec1b
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DdlChange.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations;
+
+import org.apache.commons.dbutils.DbUtils;
+import org.sonar.core.persistence.Database;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public abstract class DdlChange implements DatabaseMigration {
+
+ private final Database db;
+
+ public DdlChange(Database db) {
+ this.db = db;
+ }
+
+ @Override
+ public final void execute() throws SQLException {
+ Connection writeConnection = null;
+ try {
+ writeConnection = db.getDataSource().getConnection();
+ writeConnection.setAutoCommit(false);
+ Context context = new Context(writeConnection);
+ execute(context);
+
+ } finally {
+ DbUtils.closeQuietly(writeConnection);
+ }
+ }
+
+ public class Context {
+ private final Connection writeConnection;
+
+ public Context(Connection writeConnection) {
+ this.writeConnection = writeConnection;
+ }
+
+ public void execute(String sql) throws SQLException {
+ try {
+ UpsertImpl.create(writeConnection, sql).execute().commit();
+ } catch (Exception e) {
+ throw new IllegalStateException(String.format("Fail to execute %s", sql), e);
+ }
+ }
+ }
+
+ public abstract void execute(Context context) throws SQLException;
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DropColumnsBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DropColumnsBuilder.java
new file mode 100644
index 00000000000..ce61d645b16
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DropColumnsBuilder.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations;
+
+import org.sonar.core.persistence.dialect.*;
+
+public class DropColumnsBuilder {
+
+ private final Dialect dialect;
+ private final String tableName;
+ private final String[] columns;
+
+ public DropColumnsBuilder(Dialect dialect, String tableName, String... columns) {
+ this.tableName = tableName;
+ this.dialect = dialect;
+ this.columns = columns;
+ }
+
+ public String build() {
+ StringBuilder sql = new StringBuilder().append("ALTER TABLE ").append(tableName).append(" ");
+ switch (dialect.getId()) {
+ case PostgreSql.ID:
+ case MySql.ID:
+ dropColumns(sql, "DROP COLUMN ");
+ break;
+ case MsSql.ID:
+ sql.append("DROP COLUMN ");
+ dropColumns(sql, "");
+ break;
+ case Oracle.ID:
+ sql.append("DROP (");
+ dropColumns(sql, "");
+ sql.append(")");
+ break;
+ default:
+ throw new IllegalStateException(String.format("Unsupported database '%s'", dialect.getId()));
+ }
+ return sql.toString();
+ }
+
+ private void dropColumns(StringBuilder sql, String columnPrefix) {
+ for (int i = 0; i < columns.length; i++) {
+ sql.append(columnPrefix);
+ sql.append(columns[i]);
+ if (i < columns.length - 1) {
+ sql.append(", ");
+ }
+ }
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddIssuesColumns.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddIssuesColumns.java
new file mode 100644
index 00000000000..529c3436e37
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddIssuesColumns.java
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.db.migrations.v51;
+
+import org.sonar.core.persistence.Database;
+import org.sonar.server.db.migrations.AddColumnsBuilder;
+import org.sonar.server.db.migrations.DdlChange;
+
+import java.sql.SQLException;
+
+/**
+ * Add the following columns to the issues table :
+ * - issue_creation_date_ms
+ * - issue_update_date_ms
+ * - issue_close_date_ms
+ * - tags
+ * - component_uuid
+ * - project_uuid
+ */
+public class AddIssuesColumns extends DdlChange {
+
+ private final Database db;
+
+ public AddIssuesColumns(Database db) {
+ super(db);
+ this.db = db;
+ }
+
+ @Override
+ public void execute(DdlChange.Context context) throws SQLException {
+ context.execute(generateSql());
+ }
+
+ private String generateSql() {
+ return new AddColumnsBuilder(db.getDialect(), "issues")
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("issue_creation_date_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true)
+ )
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("issue_update_date_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true)
+ )
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("issue_close_date_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true)
+ )
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("tags")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setLimit(4000)
+ .setNullable(true))
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("component_uuid")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setLimit(50)
+ .setNullable(true))
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("project_uuid")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setLimit(50)
+ .setNullable(true))
+ .build();
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/DropIssuesColumns.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/DropIssuesColumns.java
new file mode 100644
index 00000000000..3b04ac3dbe1
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/DropIssuesColumns.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.db.migrations.v51;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.sonar.core.persistence.Database;
+import org.sonar.server.db.migrations.DdlChange;
+import org.sonar.server.db.migrations.DropColumnsBuilder;
+
+import java.sql.SQLException;
+
+/**
+ * Drop the following columns from the issues table :
+ * - issue_creation_date
+ * - issue_update_date
+ * - issue_close_date
+ * - component_id
+ * - root_component_id
+ */
+public class DropIssuesColumns extends DdlChange {
+
+ private final Database db;
+
+ public DropIssuesColumns(Database db) {
+ super(db);
+ this.db = db;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(generateSql());
+ }
+
+ @VisibleForTesting
+ String generateSql() {
+ return new DropColumnsBuilder(db.getDialect(), "issues",
+ "issue_creation_date", "issue_update_date", "issue_close_date", "component_id", "root_component_id")
+ .build();
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java
deleted file mode 100644
index dce97a78d7d..00000000000
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.db.migrations.v51;
-
-import org.sonar.api.utils.System2;
-import org.sonar.core.persistence.Database;
-import org.sonar.server.db.migrations.BaseDataChange;
-import org.sonar.server.db.migrations.MassUpdate;
-import org.sonar.server.db.migrations.Select;
-import org.sonar.server.db.migrations.SqlStatement;
-
-import java.sql.SQLException;
-import java.util.Date;
-
-public class FeedProjectMeasuresLongDates extends BaseDataChange {
-
- private final System2 system2;
-
- public FeedProjectMeasuresLongDates(Database db, System2 system2) {
- super(db);
- this.system2 = system2;
- }
-
- @Override
- public void execute(Context context) throws SQLException {
- final long now = system2.now();
- MassUpdate massUpdate = context.prepareMassUpdate();
- massUpdate
- .select("SELECT m.measure_date, m.id FROM project_measures m WHERE measure_date_ms IS NULL");
- massUpdate
- .update("UPDATE project_measures SET measure_date_ms=? WHERE id=?");
- massUpdate.rowPluralName("project measures");
- massUpdate.execute(new MassUpdate.Handler() {
- @Override
- public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Date date = row.getNullableDate(1);
- update.setLong(1, date == null ? null : Math.min(now, date.getTime()));
-
- Long id = row.getNullableLong(2);
- update.setLong(2, id);
-
- return true;
- }
- });
- }
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java
index fd6f3fec50c..218da47bd84 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java
@@ -22,15 +22,21 @@ package org.sonar.server.es;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequestBuilder;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.search.SearchHit;
import org.picocontainer.Startable;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
@@ -38,6 +44,8 @@ import org.sonar.server.util.ProgressLogger;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -49,19 +57,21 @@ import java.util.concurrent.atomic.AtomicLong;
* </ul>
*/
public class BulkIndexer implements Startable {
+
private static final Logger LOGGER = Loggers.get(BulkIndexer.class);
- private static final long FLUSH_BYTE_SIZE = new ByteSizeValue(2, ByteSizeUnit.MB).bytes();
+ private static final long FLUSH_BYTE_SIZE = new ByteSizeValue(1, ByteSizeUnit.MB).bytes();
private static final String REFRESH_INTERVAL_SETTING = "index.refresh_interval";
private static final String ALREADY_STARTED_MESSAGE = "Bulk indexing is already started";
private final EsClient client;
private final String indexName;
private boolean large = false;
- private long flushByteSize = FLUSH_BYTE_SIZE;
+ private boolean disableRefresh = false;
private BulkRequestBuilder bulkRequest = null;
private Map<String, Object> largeInitialSettings = null;
-
private final AtomicLong counter = new AtomicLong(0L);
+ private final int concurrentRequests;
+ private final Semaphore semaphore;
private final ProgressLogger progress;
public BulkIndexer(EsClient client, String indexName) {
@@ -69,13 +79,15 @@ public class BulkIndexer implements Startable {
this.indexName = indexName;
this.progress = new ProgressLogger(String.format("Progress[BulkIndexer[%s]]", indexName), counter, LOGGER)
.setPluralLabel("requests");
+
+ this.concurrentRequests = Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
+ this.semaphore = new Semaphore(concurrentRequests);
}
/**
* Large indexing is an heavy operation that populates an index generally from scratch. Replicas and
* automatic refresh are disabled during bulk indexing and lucene segments are optimized at the end.
*/
-
public BulkIndexer setLarge(boolean b) {
Preconditions.checkState(bulkRequest == null, ALREADY_STARTED_MESSAGE);
this.large = b;
@@ -83,14 +95,16 @@ public class BulkIndexer implements Startable {
}
/**
- * Default value is {@link org.sonar.server.es.BulkIndexer#FLUSH_BYTE_SIZE}
- * @see org.elasticsearch.common.unit.ByteSizeValue
+ * By default refresh of index is executed in method {@link #stop()}. Set to true
+ * to disable refresh.
*/
- public BulkIndexer setFlushByteSize(long l) {
- this.flushByteSize = l;
+ public BulkIndexer setDisableRefresh(boolean b) {
+ this.disableRefresh = b;
return this;
}
+
+
@Override
public void start() {
Preconditions.checkState(bulkRequest == null, ALREADY_STARTED_MESSAGE);
@@ -113,31 +127,64 @@ public class BulkIndexer implements Startable {
updateSettings(bulkSettings);
}
- bulkRequest = client.prepareBulk();
+ bulkRequest = client.prepareBulk().setRefresh(false);
counter.set(0L);
progress.start();
}
public void add(ActionRequest request) {
bulkRequest.request().add(request);
- counter.getAndIncrement();
- if (bulkRequest.request().estimatedSizeInBytes() >= flushByteSize) {
- executeBulk(bulkRequest);
- bulkRequest = client.prepareBulk();
+ if (bulkRequest.request().estimatedSizeInBytes() >= FLUSH_BYTE_SIZE) {
+ executeBulk();
+ }
+ }
+
+ public void addDeletion(SearchRequestBuilder searchRequest) {
+ searchRequest
+ .setScroll(TimeValue.timeValueMinutes(5))
+ .setSearchType(SearchType.SCAN)
+ // load only doc ids, not _source fields
+ .setFetchSource(false);
+
+ // this search is synchronous. An optimization would be to be non-blocking,
+ // but it requires to tracking pending requests in close().
+ // Same semaphore can't be reused because of potential deadlock (requires to acquire
+ // two locks)
+ SearchResponse searchResponse = searchRequest.get();
+ searchResponse = client.prepareSearchScroll(searchResponse.getScrollId()).get();
+ for (SearchHit hit : searchResponse.getHits()) {
+ add(client.prepareDelete(hit.index(), hit.type(), hit.getId()).request());
}
}
+ /**
+ * Delete all the documents matching the given search request. This method is blocking.
+ * Index is refreshed, so docs are not searchable as soon as method is executed.
+ */
+ public static void delete(EsClient client, String indexName, SearchRequestBuilder searchRequest) {
+ BulkIndexer bulk = new BulkIndexer(client, indexName);
+ bulk.start();
+ bulk.addDeletion(searchRequest);
+ bulk.stop();
+ }
+
@Override
public void stop() {
+ if (bulkRequest.numberOfActions() > 0) {
+ executeBulk();
+ }
try {
- if (bulkRequest.numberOfActions() > 0) {
- executeBulk(bulkRequest);
+ if (semaphore.tryAcquire(concurrentRequests, 10, TimeUnit.MINUTES)) {
+ semaphore.release(concurrentRequests);
}
- } finally {
- progress.stop();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("Elasticsearch bulk requests still being executed after 10 minutes", e);
}
+ progress.stop();
- client.prepareRefresh(indexName).get();
+ if (!disableRefresh) {
+ client.prepareRefresh(indexName).get();
+ }
if (large) {
// optimize lucene segments and revert index settings
// Optimization must be done before re-applying replicas:
@@ -155,39 +202,54 @@ public class BulkIndexer implements Startable {
req.get();
}
- private void executeBulk(BulkRequestBuilder bulkRequest) {
- List<ActionRequest> retries = Lists.newArrayList();
- BulkResponse response = bulkRequest.get();
-
- for (BulkItemResponse item : response.getItems()) {
- if (item.isFailed()) {
- ActionRequest retry = bulkRequest.request().requests().get(item.getItemId());
- retries.add(retry);
- }
- }
-
- if (!retries.isEmpty()) {
- LOGGER.warn(String.format("%d index requests failed. Trying again.", retries.size()));
- BulkRequestBuilder retryBulk = client.prepareBulk();
- for (ActionRequest retry : retries) {
- retryBulk.request().add(retry);
- }
- BulkResponse retryBulkResponse = retryBulk.get();
- if (retryBulkResponse.hasFailures()) {
- LOGGER.error("New attempt to index documents failed");
- for (int index = 0; index < retryBulkResponse.getItems().length; index++) {
- BulkItemResponse item = retryBulkResponse.getItems()[index];
+ private void executeBulk() {
+ final BulkRequestBuilder req = this.bulkRequest;
+ this.bulkRequest = client.prepareBulk().setRefresh(false);
+ semaphore.acquireUninterruptibly();
+ req.execute(new ActionListener<BulkResponse>() {
+ @Override
+ public void onResponse(BulkResponse response) {
+ semaphore.release();
+ counter.addAndGet(response.getItems().length);
+
+ List<ActionRequest> retries = Lists.newArrayList();
+ for (BulkItemResponse item : response.getItems()) {
if (item.isFailed()) {
- StringBuilder sb = new StringBuilder();
- String msg = sb.append("\n[").append(index)
- .append("]: index [").append(item.getIndex()).append("], type [").append(item.getType()).append("], id [").append(item.getId())
- .append("], message [").append(item.getFailureMessage()).append("]").toString();
- LOGGER.error(msg);
+ ActionRequest retry = req.request().requests().get(item.getItemId());
+ retries.add(retry);
+ }
+ }
+
+ if (!retries.isEmpty()) {
+ LOGGER.warn(String.format("%d index requests failed. Trying again.", retries.size()));
+ BulkRequestBuilder retryBulk = client.prepareBulk();
+ for (ActionRequest retry : retries) {
+ retryBulk.request().add(retry);
+ }
+ BulkResponse retryBulkResponse = retryBulk.get();
+ if (retryBulkResponse.hasFailures()) {
+ LOGGER.error("New attempt to index documents failed");
+ for (int index = 0; index < retryBulkResponse.getItems().length; index++) {
+ BulkItemResponse item = retryBulkResponse.getItems()[index];
+ if (item.isFailed()) {
+ StringBuilder sb = new StringBuilder();
+ String msg = sb.append("\n[").append(index)
+ .append("]: index [").append(item.getIndex()).append("], type [").append(item.getType()).append("], id [").append(item.getId())
+ .append("], message [").append(item.getFailureMessage()).append("]").toString();
+ LOGGER.error(msg);
+ }
+ }
+ } else {
+ LOGGER.info("New index attempt succeeded");
}
}
- } else {
- LOGGER.info("New index attempt succeeded");
}
- }
+
+ @Override
+ public void onFailure(Throwable e) {
+ semaphore.release();
+ LOGGER.error("Fail to execute bulk index request: " + req, e);
+ }
+ });
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java b/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java
index 837754cf20a..5c2f6f8e37a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java
@@ -21,15 +21,16 @@ package org.sonar.server.es;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
+import org.elasticsearch.common.joda.time.format.ISODateTimeFormat;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.sonar.server.search.BaseDoc;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.*;
public class EsUtils {
@@ -62,4 +63,20 @@ public class EsUtils {
}
});
}
+
+ @CheckForNull
+ public static Date parseDateTime(@Nullable String s) {
+ if (s != null) {
+ return ISODateTimeFormat.dateTime().parseDateTime(s).toDate();
+ }
+ return null;
+ }
+
+ @CheckForNull
+ public static String formatDateTime(@Nullable Date date) {
+ if (date != null) {
+ return ISODateTimeFormat.dateTime().print(date.getTime());
+ }
+ return null;
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java
index f1522fcbdf4..acb87886f66 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java
@@ -22,7 +22,6 @@ package org.sonar.server.es.request;
import com.google.common.collect.LinkedHashMultiset;
import com.google.common.collect.Multiset;
-import com.google.common.collect.Multiset.Entry;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ListenableActionFuture;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
@@ -70,7 +69,7 @@ public class ProxyBulkRequestBuilder extends BulkRequestBuilder {
@Override
public ListenableActionFuture<BulkResponse> execute() {
- throw new UnsupportedOperationException("execute() should not be called as it's used for asynchronous");
+ throw unsupported();
}
private UnsupportedOperationException unsupported() {
@@ -107,10 +106,10 @@ public class ProxyBulkRequestBuilder extends BulkRequestBuilder {
groupedRequests.add(new BulkRequestKey(requestType, index, docType));
}
- Set<Entry<BulkRequestKey>> entrySet = groupedRequests.entrySet();
+ Set<Multiset.Entry<BulkRequestKey>> entrySet = groupedRequests.entrySet();
int size = entrySet.size();
int current = 0;
- for (Entry<BulkRequestKey> requestEntry : entrySet) {
+ for (Multiset.Entry<BulkRequestKey> requestEntry : entrySet) {
message.append(requestEntry.getCount()).append(" ").append(requestEntry.getElement().toString());
current++;
if (current < size) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java
index 84420be5d48..d7be429f8e7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java
@@ -20,9 +20,9 @@
package org.sonar.server.issue.index;
import org.apache.commons.dbutils.DbUtils;
+import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.index.query.FilterBuilders;
-import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.sonar.core.persistence.DbSession;
import org.sonar.server.db.DbClient;
@@ -89,14 +89,17 @@ public class IssueIndexer extends BaseIndexer {
}
public void deleteProject(String uuid, boolean refresh) {
- QueryBuilder query = QueryBuilders.filteredQuery(
- QueryBuilders.matchAllQuery(),
- FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, uuid))
- );
- esClient.prepareDeleteByQuery(IssueIndexDefinition.INDEX).setQuery(query).get();
- if (refresh) {
- esClient.prepareRefresh(IssueIndexDefinition.INDEX).get();
- }
+ BulkIndexer bulk = new BulkIndexer(esClient, IssueIndexDefinition.INDEX);
+ bulk.setDisableRefresh(!refresh);
+ bulk.start();
+ SearchRequestBuilder search = esClient.prepareSearch(IssueIndexDefinition.INDEX)
+ .setRouting(uuid)
+ .setQuery(QueryBuilders.filteredQuery(
+ QueryBuilders.matchAllQuery(),
+ FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, uuid))
+ ));
+ bulk.addDeletion(search);
+ bulk.stop();
}
BulkIndexer createBulkIndexer(boolean large) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/SearchClient.java b/server/sonar-server/src/main/java/org/sonar/server/search/SearchClient.java
index 83bc7c3d368..ddf5c512ad2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/search/SearchClient.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/search/SearchClient.java
@@ -54,18 +54,7 @@ import org.picocontainer.Startable;
import org.sonar.api.config.Settings;
import org.sonar.process.LoopbackAddress;
import org.sonar.process.ProcessConstants;
-import org.sonar.server.es.request.ProxyBulkRequestBuilder;
-import org.sonar.server.es.request.ProxyCountRequestBuilder;
-import org.sonar.server.es.request.ProxyCreateIndexRequestBuilder;
-import org.sonar.server.es.request.ProxyDeleteByQueryRequestBuilder;
-import org.sonar.server.es.request.ProxyDeleteRequestBuilder;
-import org.sonar.server.es.request.ProxyGetRequestBuilder;
-import org.sonar.server.es.request.ProxyIndicesExistsRequestBuilder;
-import org.sonar.server.es.request.ProxyMultiGetRequestBuilder;
-import org.sonar.server.es.request.ProxyPutMappingRequestBuilder;
-import org.sonar.server.es.request.ProxyRefreshRequestBuilder;
-import org.sonar.server.es.request.ProxySearchRequestBuilder;
-import org.sonar.server.es.request.ProxySearchScrollRequestBuilder;
+import org.sonar.server.es.request.*;
/**
* ElasticSearch Node used to connect to index.
@@ -136,7 +125,7 @@ public class SearchClient extends TransportClient implements Startable {
@Override
public DeleteByQueryRequestBuilder prepareDeleteByQuery(String... indices) {
- return new ProxyDeleteByQueryRequestBuilder(this).setIndices(indices);
+ throw new UnsupportedOperationException("Delete by query must not be used. See https://github.com/elastic/elasticsearch/issues/10067. See alternatives in BulkIndexer.");
}
// ****************************************************************************************************************
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceFileResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceFileResultSetIterator.java
new file mode 100644
index 00000000000..ab01ae4d44d
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceFileResultSetIterator.java
@@ -0,0 +1,209 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.source.index;
+
+import org.apache.commons.lang.StringUtils;
+import org.elasticsearch.action.update.UpdateRequest;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.source.db.FileSourceDto;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.db.ResultSetIterator;
+import org.sonar.server.es.EsUtils;
+import org.sonar.server.source.db.FileSourceDb;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Scroll over table FILE_SOURCES and directly parse data required to
+ * populate the index sourcelines
+ */
+public class SourceFileResultSetIterator extends ResultSetIterator<SourceFileResultSetIterator.Row> {
+
+ public static class Row {
+ private final String fileUuid, projectUuid;
+ private final long updatedAt;
+ private final List<UpdateRequest> lineUpdateRequests = new ArrayList<>();
+
+ public Row(String projectUuid, String fileUuid, long updatedAt) {
+ this.projectUuid = projectUuid;
+ this.fileUuid = fileUuid;
+ this.updatedAt = updatedAt;
+ }
+
+ public String getProjectUuid() {
+ return projectUuid;
+ }
+
+ public String getFileUuid() {
+ return fileUuid;
+ }
+
+ public long getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public List<UpdateRequest> getLineUpdateRequests() {
+ return lineUpdateRequests;
+ }
+ }
+
+ private static final String[] FIELDS = {
+ "project_uuid",
+ "file_uuid",
+ "updated_at",
+ "binary_data"
+ };
+ private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from file_sources";
+ private static final String SQL_AFTER_DATE = SQL_ALL + " where updated_at>?";
+
+ public static SourceFileResultSetIterator create(DbClient dbClient, Connection connection, long afterDate) {
+ try {
+ String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL;
+ // rows are big, so they are scrolled once at a time (one row in memory at a time)
+ PreparedStatement stmt = dbClient.newScrollingSingleRowSelectStatement(connection, sql);
+ if (afterDate > 0L) {
+ stmt.setLong(1, afterDate);
+ }
+ return new SourceFileResultSetIterator(stmt);
+ } catch (SQLException e) {
+ throw new IllegalStateException("Fail to prepare SQL request to select all file sources", e);
+ }
+ }
+
+ private SourceFileResultSetIterator(PreparedStatement stmt) throws SQLException {
+ super(stmt);
+ }
+
+ @Override
+ protected Row read(ResultSet rs) throws SQLException {
+ String projectUuid = rs.getString(1);
+ String fileUuid = rs.getString(2);
+ Date updatedAt = new Date(rs.getLong(3));
+ FileSourceDb.Data data = FileSourceDto.decodeData(rs.getBinaryStream(4));
+ return toRow(projectUuid, fileUuid, updatedAt, data);
+ }
+
+ /**
+ * Convert protobuf message to data required for Elasticsearch indexing
+ */
+ public static Row toRow(String projectUuid, String fileUuid, Date updatedAt, FileSourceDb.Data data) {
+ Row result = new Row(projectUuid, fileUuid, updatedAt.getTime());
+ for (FileSourceDb.Line line : data.getLinesList()) {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+ // all the fields must be present, even if value is null
+ JsonWriter writer = JsonWriter.of(new OutputStreamWriter(bytes)).setSerializeNulls(true);
+ writer.beginObject();
+ writer.prop(SourceLineIndexDefinition.FIELD_PROJECT_UUID, projectUuid);
+ writer.prop(SourceLineIndexDefinition.FIELD_FILE_UUID, fileUuid);
+ writer.prop(SourceLineIndexDefinition.FIELD_LINE, line.getLine());
+ writer.prop(SourceLineIndexDefinition.FIELD_UPDATED_AT, EsUtils.formatDateTime(updatedAt));
+ writer.prop(SourceLineIndexDefinition.FIELD_SCM_REVISION, line.getScmRevision());
+ writer.prop(SourceLineIndexDefinition.FIELD_SCM_AUTHOR, line.getScmAuthor());
+ writer.prop(SourceLineIndexDefinition.FIELD_SCM_DATE, EsUtils.formatDateTime(line.hasScmDate() ? new Date(line.getScmDate()) : null));
+
+ // unit tests
+ if (line.hasUtLineHits()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_UT_LINE_HITS, line.getUtLineHits());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_UT_LINE_HITS).valueObject(null);
+ }
+ if (line.hasUtConditions()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_UT_CONDITIONS, line.getUtConditions());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_UT_CONDITIONS).valueObject(null);
+ }
+ if (line.hasUtCoveredConditions()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_UT_COVERED_CONDITIONS, line.getUtCoveredConditions());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_UT_COVERED_CONDITIONS).valueObject(null);
+ }
+
+ // IT
+ if (line.hasItLineHits()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_IT_LINE_HITS, line.getItLineHits());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_IT_LINE_HITS).valueObject(null);
+ }
+ if (line.hasItConditions()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_IT_CONDITIONS, line.getItConditions());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_IT_CONDITIONS).valueObject(null);
+ }
+ if (line.hasItCoveredConditions()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_IT_COVERED_CONDITIONS, line.getItCoveredConditions());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_IT_COVERED_CONDITIONS).valueObject(null);
+ }
+
+ // Overall coverage
+ if (line.hasOverallLineHits()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_OVERALL_LINE_HITS, line.getOverallLineHits());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_OVERALL_LINE_HITS).valueObject(null);
+ }
+ if (line.hasOverallConditions()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_OVERALL_CONDITIONS, line.getOverallConditions());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_OVERALL_CONDITIONS).valueObject(null);
+ }
+ if (line.hasOverallCoveredConditions()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_OVERALL_COVERED_CONDITIONS, line.getOverallCoveredConditions());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_OVERALL_COVERED_CONDITIONS).valueObject(null);
+ }
+
+ if (line.hasHighlighting()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_HIGHLIGHTING, line.getHighlighting());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_HIGHLIGHTING).valueObject(null);
+ }
+ if (line.hasSymbols()) {
+ writer.prop(SourceLineIndexDefinition.FIELD_SYMBOLS, line.getSymbols());
+ } else {
+ writer.name(SourceLineIndexDefinition.FIELD_SYMBOLS).valueObject(null);
+ }
+ writer.name(SourceLineIndexDefinition.FIELD_DUPLICATIONS).valueObject(line.getDuplicationList());
+ writer.prop(SourceLineIndexDefinition.FIELD_SOURCE, line.hasSource() ? line.getSource() : null);
+ writer.endObject().close();
+
+ // This is an optimization to reduce memory consumption and multiple conversions from Map to JSON.
+ // UpdateRequest#doc() and #upsert() take the same parameter values, so:
+ // - passing the same Map would execute two JSON serializations
+ // - Map is a useless temporarily structure: read JDBC result set -> convert to map -> convert to JSON. Generating
+ // directly JSON from result set is more efficient.
+ byte[] jsonDoc = bytes.toByteArray();
+ UpdateRequest updateRequest = new UpdateRequest(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE, SourceLineIndexDefinition.docKey(fileUuid, line.getLine()))
+ .routing(projectUuid)
+ .doc(jsonDoc)
+ .upsert(jsonDoc);
+ result.lineUpdateRequests.add(updateRequest);
+ }
+ return result;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java
index f00a5b8c457..ddf68949df2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java
@@ -25,6 +25,7 @@ import org.sonar.server.search.IndexUtils;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
@@ -125,10 +126,6 @@ public class SourceLineDoc extends BaseDoc {
return this;
}
- public String key() {
- return String.format("%s_%d", fileUuid(), line());
- }
-
@CheckForNull
public Integer utLineHits() {
Number lineHits = getNullableField(SourceLineIndexDefinition.FIELD_UT_LINE_HITS);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexDefinition.java
index ad5615fdd9a..5385b80f136 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexDefinition.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexDefinition.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.source.index;
+import com.google.common.collect.ImmutableMap;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.sonar.api.config.Settings;
import org.sonar.process.ProcessConstants;
@@ -27,6 +28,8 @@ import org.sonar.server.es.NewIndex;
public class SourceLineIndexDefinition implements IndexDefinition {
+ public static final String INDEX = "sourcelines";
+ public static final String TYPE = "sourceline";
public static final String FIELD_PROJECT_UUID = "projectUuid";
public static final String FIELD_FILE_UUID = "fileUuid";
public static final String FIELD_LINE = "line";
@@ -48,11 +51,6 @@ public class SourceLineIndexDefinition implements IndexDefinition {
public static final String FIELD_DUPLICATIONS = "duplications";
public static final String FIELD_UPDATED_AT = "updatedAt";
- public static final String INDEX = "sourcelines";
-
- public static final String TYPE = "sourceline";
-
-
private final Settings settings;
public SourceLineIndexDefinition(Settings settings) {
@@ -75,26 +73,31 @@ public class SourceLineIndexDefinition implements IndexDefinition {
}
// type "sourceline"
- NewIndex.NewIndexType sourceLineMapping = index.createType(TYPE);
- sourceLineMapping.stringFieldBuilder(FIELD_PROJECT_UUID).build();
- sourceLineMapping.stringFieldBuilder(FIELD_FILE_UUID).build();
- sourceLineMapping.createIntegerField(FIELD_LINE);
- sourceLineMapping.stringFieldBuilder(FIELD_SCM_REVISION).build();
- sourceLineMapping.stringFieldBuilder(FIELD_SCM_AUTHOR).build();
- sourceLineMapping.createDateTimeField(FIELD_SCM_DATE);
- sourceLineMapping.stringFieldBuilder(FIELD_HIGHLIGHTING).disableSearch().build();
- sourceLineMapping.stringFieldBuilder(FIELD_SOURCE).disableSearch().build();
- sourceLineMapping.createIntegerField(FIELD_UT_LINE_HITS);
- sourceLineMapping.createIntegerField(FIELD_UT_CONDITIONS);
- sourceLineMapping.createIntegerField(FIELD_UT_COVERED_CONDITIONS);
- sourceLineMapping.createIntegerField(FIELD_IT_LINE_HITS);
- sourceLineMapping.createIntegerField(FIELD_IT_CONDITIONS);
- sourceLineMapping.createIntegerField(FIELD_IT_COVERED_CONDITIONS);
- sourceLineMapping.createIntegerField(FIELD_OVERALL_LINE_HITS);
- sourceLineMapping.createIntegerField(FIELD_OVERALL_CONDITIONS);
- sourceLineMapping.createIntegerField(FIELD_OVERALL_COVERED_CONDITIONS);
- sourceLineMapping.stringFieldBuilder(FIELD_SYMBOLS).disableSearch().build();
- sourceLineMapping.createShortField(FIELD_DUPLICATIONS);
- sourceLineMapping.createDateTimeField(FIELD_UPDATED_AT);
+ NewIndex.NewIndexType mapping = index.createType(TYPE);
+ mapping.setAttribute("_routing", ImmutableMap.of("required", true, "path", FIELD_PROJECT_UUID));
+ mapping.stringFieldBuilder(FIELD_PROJECT_UUID).build();
+ mapping.stringFieldBuilder(FIELD_FILE_UUID).build();
+ mapping.createIntegerField(FIELD_LINE);
+ mapping.stringFieldBuilder(FIELD_SCM_REVISION).disableSearch().build();
+ mapping.stringFieldBuilder(FIELD_SCM_AUTHOR).disableSearch().build();
+ mapping.createDateTimeField(FIELD_SCM_DATE);
+ mapping.stringFieldBuilder(FIELD_HIGHLIGHTING).disableSearch().build();
+ mapping.stringFieldBuilder(FIELD_SOURCE).disableSearch().build();
+ mapping.createIntegerField(FIELD_UT_LINE_HITS);
+ mapping.createIntegerField(FIELD_UT_CONDITIONS);
+ mapping.createIntegerField(FIELD_UT_COVERED_CONDITIONS);
+ mapping.createIntegerField(FIELD_IT_LINE_HITS);
+ mapping.createIntegerField(FIELD_IT_CONDITIONS);
+ mapping.createIntegerField(FIELD_IT_COVERED_CONDITIONS);
+ mapping.createIntegerField(FIELD_OVERALL_LINE_HITS);
+ mapping.createIntegerField(FIELD_OVERALL_CONDITIONS);
+ mapping.createIntegerField(FIELD_OVERALL_COVERED_CONDITIONS);
+ mapping.stringFieldBuilder(FIELD_SYMBOLS).disableSearch().build();
+ mapping.createShortField(FIELD_DUPLICATIONS);
+ mapping.createDateTimeField(FIELD_UPDATED_AT);
+ }
+
+ public static String docKey(String fileUuid, int line) {
+ return String.format("%s_%d", fileUuid, line);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java
index 9e862e6b389..011cb348ffa 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.source.index;
+import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders;
@@ -55,7 +56,7 @@ public class SourceLineIndexer extends BaseIndexer {
DbSession dbSession = dbClient.openSession(false);
Connection dbConnection = dbSession.getConnection();
try {
- SourceLineResultSetIterator rowIt = SourceLineResultSetIterator.create(dbClient, dbConnection, lastUpdatedAt);
+ SourceFileResultSetIterator rowIt = SourceFileResultSetIterator.create(dbClient, dbConnection, lastUpdatedAt);
long maxUpdatedAt = doIndex(bulk, rowIt);
rowIt.close();
return maxUpdatedAt;
@@ -65,59 +66,64 @@ public class SourceLineIndexer extends BaseIndexer {
}
}
- public long index(Iterator<SourceLineResultSetIterator.SourceFile> sourceFiles) {
- final BulkIndexer bulk = new BulkIndexer(esClient, SourceLineIndexDefinition.INDEX);
- return doIndex(bulk, sourceFiles);
+ public long index(Iterator<SourceFileResultSetIterator.Row> dbRows) {
+ BulkIndexer bulk = new BulkIndexer(esClient, SourceLineIndexDefinition.INDEX);
+ return doIndex(bulk, dbRows);
}
- private long doIndex(BulkIndexer bulk, Iterator<SourceLineResultSetIterator.SourceFile> files) {
+ private long doIndex(BulkIndexer bulk, Iterator<SourceFileResultSetIterator.Row> dbRows) {
long maxUpdatedAt = 0L;
bulk.start();
- while (files.hasNext()) {
- SourceLineResultSetIterator.SourceFile file = files.next();
- for (SourceLineDoc line : file.getLines()) {
- bulk.add(newUpsertRequest(line));
+ while (dbRows.hasNext()) {
+ SourceFileResultSetIterator.Row row = dbRows.next();
+ addDeleteRequestsForLinesGreaterThan(bulk, row);
+ for (UpdateRequest updateRequest : row.getLineUpdateRequests()) {
+ bulk.add(updateRequest);
}
- deleteLinesFromFileAbove(file.getFileUuid(), file.getLines().size());
- maxUpdatedAt = Math.max(maxUpdatedAt, file.getUpdatedAt());
+ maxUpdatedAt = Math.max(maxUpdatedAt, row.getUpdatedAt());
}
bulk.stop();
return maxUpdatedAt;
}
- private UpdateRequest newUpsertRequest(SourceLineDoc lineDoc) {
- String projectUuid = lineDoc.projectUuid();
- return new UpdateRequest(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE, lineDoc.key())
- .routing(projectUuid)
- .doc(lineDoc.getFields())
- .upsert(lineDoc.getFields());
- }
-
/**
- * Unindex all lines in file with UUID <code>fileUuid</code> above line <code>lastLine</code>
+ * Use-case:
+ * - file had 10 lines in previous analysis
+ * - same file has now 5 lines
+ * Lines 6 to 10 must be removed from index.
*/
- private void deleteLinesFromFileAbove(String fileUuid, int lastLine) {
- esClient.prepareDeleteByQuery(SourceLineIndexDefinition.INDEX)
+ private void addDeleteRequestsForLinesGreaterThan(BulkIndexer bulk, SourceFileResultSetIterator.Row fileRow) {
+ int numberOfLines = fileRow.getLineUpdateRequests().size();
+ SearchRequestBuilder searchRequest = esClient.prepareSearch(SourceLineIndexDefinition.INDEX)
.setTypes(SourceLineIndexDefinition.TYPE)
- .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.boolFilter()
- .must(FilterBuilders.termFilter(FIELD_FILE_UUID, fileUuid).cache(false))
- .must(FilterBuilders.rangeFilter(SourceLineIndexDefinition.FIELD_LINE).gt(lastLine).cache(false))
- )).get();
+ .setRouting(fileRow.getProjectUuid())
+ .setQuery(QueryBuilders.filteredQuery(
+ QueryBuilders.matchAllQuery(),
+ FilterBuilders.boolFilter()
+ .must(FilterBuilders.termFilter(FIELD_FILE_UUID, fileRow.getFileUuid()).cache(false))
+ .must(FilterBuilders.rangeFilter(SourceLineIndexDefinition.FIELD_LINE).gt(numberOfLines).cache(false))
+ .cache(false)
+ ));
+ bulk.addDeletion(searchRequest);
}
public void deleteByFile(String fileUuid) {
- esClient.prepareDeleteByQuery(SourceLineIndexDefinition.INDEX)
+ // TODO would be great to have the projectUuid for routing
+ SearchRequestBuilder searchRequest = esClient.prepareSearch(SourceLineIndexDefinition.INDEX)
.setTypes(SourceLineIndexDefinition.TYPE)
- .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(),
- FilterBuilders.termFilter(FIELD_FILE_UUID, fileUuid).cache(false)))
- .get();
+ .setQuery(QueryBuilders.filteredQuery(
+ QueryBuilders.matchAllQuery(),
+ FilterBuilders.termFilter(FIELD_FILE_UUID, fileUuid).cache(false)));
+ BulkIndexer.delete(esClient, SourceLineIndexDefinition.INDEX, searchRequest);
}
public void deleteByProject(String projectUuid) {
- esClient.prepareDeleteByQuery(SourceLineIndexDefinition.INDEX)
+ SearchRequestBuilder searchRequest = esClient.prepareSearch(SourceLineIndexDefinition.INDEX)
+ .setRouting(projectUuid)
.setTypes(SourceLineIndexDefinition.TYPE)
- .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(),
- FilterBuilders.termFilter(FIELD_PROJECT_UUID, projectUuid).cache(false)))
- .get();
+ .setQuery(QueryBuilders.filteredQuery(
+ QueryBuilders.matchAllQuery(),
+ FilterBuilders.termFilter(FIELD_PROJECT_UUID, projectUuid).cache(false)));
+ BulkIndexer.delete(esClient, SourceLineIndexDefinition.INDEX, searchRequest);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java
deleted file mode 100644
index 745f6dbc500..00000000000
--- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.source.index;
-
-import com.google.common.collect.Lists;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.core.source.db.FileSourceDto;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.db.ResultSetIterator;
-import org.sonar.server.source.db.FileSourceDb;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Scroll over table FILE_SOURCES and directly parse CSV field required to
- * populate the index sourcelines
- */
-public class SourceLineResultSetIterator extends ResultSetIterator<SourceLineResultSetIterator.SourceFile> {
-
- public static class SourceFile {
- private final String fileUuid;
- private final long updatedAt;
- private final List<SourceLineDoc> lines = Lists.newArrayList();
-
- public SourceFile(String fileUuid, long updatedAt) {
- this.fileUuid = fileUuid;
- this.updatedAt = updatedAt;
- }
-
- public String getFileUuid() {
- return fileUuid;
- }
-
- public long getUpdatedAt() {
- return updatedAt;
- }
-
- public List<SourceLineDoc> getLines() {
- return lines;
- }
-
- public void addLine(SourceLineDoc line) {
- this.lines.add(line);
- }
- }
-
- private static final String[] FIELDS = {
- // column 1
- "project_uuid",
- "file_uuid",
- "updated_at",
- "binary_data"
- };
-
- private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from file_sources";
-
- private static final String SQL_AFTER_DATE = SQL_ALL + " where updated_at>?";
-
- public static SourceLineResultSetIterator create(DbClient dbClient, Connection connection, long afterDate) {
- try {
- String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL;
- // rows are big, so they are scrolled once at a time (one row in memory at a time)
- PreparedStatement stmt = dbClient.newScrollingSingleRowSelectStatement(connection, sql);
- if (afterDate > 0L) {
- stmt.setLong(1, afterDate);
- }
- return new SourceLineResultSetIterator(stmt);
- } catch (SQLException e) {
- throw new IllegalStateException("Fail to prepare SQL request to select all file sources", e);
- }
- }
-
- private SourceLineResultSetIterator(PreparedStatement stmt) throws SQLException {
- super(stmt);
- }
-
- @Override
- protected SourceFile read(ResultSet rs) throws SQLException {
- String projectUuid = rs.getString(1);
- String fileUuid = rs.getString(2);
- long updatedAt = rs.getLong(3);
- Date updatedDate = new Date(updatedAt);
-
- SourceFile result = new SourceFile(fileUuid, updatedAt);
- FileSourceDb.Data data = FileSourceDto.decodeData(rs.getBinaryStream(4));
- for (FileSourceDb.Line line : data.getLinesList()) {
- SourceLineDoc doc = new SourceLineDoc();
- doc.setProjectUuid(projectUuid);
- doc.setFileUuid(fileUuid);
- doc.setLine(line.getLine());
- doc.setUpdateDate(updatedDate);
- doc.setScmRevision(line.getScmRevision());
- doc.setScmAuthor(line.getScmAuthor());
- doc.setScmDate(line.hasScmDate() ? new Date(line.getScmDate()) : null);
- // UT
- doc.setUtLineHits(line.hasUtLineHits() ? line.getUtLineHits() : null);
- doc.setUtConditions(line.hasUtConditions() ? line.getUtConditions() : null);
- doc.setUtCoveredConditions(line.hasUtCoveredConditions() ? line.getUtCoveredConditions() : null);
- // IT
- doc.setItLineHits(line.hasItLineHits() ? line.getItLineHits() : null);
- doc.setItConditions(line.hasItConditions() ? line.getItConditions() : null);
- doc.setItCoveredConditions(line.hasItCoveredConditions() ? line.getItCoveredConditions() : null);
- // OVERALL
- doc.setOverallLineHits(line.hasOverallLineHits() ? line.getOverallLineHits() : null);
- doc.setOverallConditions(line.hasOverallConditions() ? line.getOverallConditions() : null);
- doc.setOverallCoveredConditions(line.hasOverallCoveredConditions() ? line.getOverallCoveredConditions() : null);
-
- doc.setHighlighting(line.hasHighlighting() ? line.getHighlighting() : null);
- doc.setSymbols(line.hasSymbols() ? line.getSymbols() : null);
- doc.setDuplications(line.getDuplicationList());
-
- // source is always the latest field. All future fields will be added between duplications (14) and source.
- doc.setSource(line.hasSource() ? line.getSource() : null);
-
- result.addLine(doc);
- }
- return result;
- }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/util/DateCollector.java b/server/sonar-server/src/main/java/org/sonar/server/util/DateCollector.java
new file mode 100644
index 00000000000..53ab6e993a5
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/util/DateCollector.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.util;
+
+import javax.annotation.Nullable;
+
+import java.util.Date;
+
+public class DateCollector {
+
+ private long maxDate = 0L;
+
+ public void add(@Nullable Date d) {
+ if (d != null) {
+ add(d.getTime());
+ }
+ }
+
+ public void add(long date) {
+ maxDate = Math.max(maxDate, date);
+ }
+
+ /**
+ * The most recent collected date. Value is zero if no dates were collected.
+ */
+ public long getMax() {
+ return maxDate;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/util/ProgressLogger.java b/server/sonar-server/src/main/java/org/sonar/server/util/ProgressLogger.java
index 660dfcd4ded..a159389a22b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/util/ProgressLogger.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/util/ProgressLogger.java
@@ -90,10 +90,11 @@ public class ProgressLogger {
task.log();
}
- private static class LoggerTimerTask extends TimerTask {
+ private class LoggerTimerTask extends TimerTask {
private final AtomicLong counter;
private final Logger logger;
private String pluralLabel = "rows";
+ private long previousCounter = 0L;
private LoggerTimerTask(AtomicLong counter, Logger logger) {
this.counter = counter;
@@ -106,7 +107,9 @@ public class ProgressLogger {
}
private void log() {
- logger.info(String.format("%d %s processed", counter.get(), pluralLabel));
+ long current = counter.get();
+ logger.info(String.format("%d %s processed (%d items/sec)", current, pluralLabel, 1000 * (current-previousCounter) / periodMs));
+ previousCounter = current;
}
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java
index 6a986068ae5..b2d8b6b064b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java
@@ -21,6 +21,7 @@ package org.sonar.server.activity.index;
import org.apache.commons.dbutils.DbUtils;
import org.assertj.core.data.MapEntry;
+import org.elasticsearch.action.update.UpdateRequest;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
@@ -32,6 +33,7 @@ import org.sonar.server.db.DbClient;
import org.sonar.test.DbTests;
import java.sql.Connection;
+import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@@ -56,23 +58,29 @@ public class ActivityResultSetIteratorTest {
DbUtils.closeQuietly(connection);
}
+ /**
+ * Iterate over two rows in table.
+ */
@Test
public void traverse() throws Exception {
dbTester.prepareDbUnit(getClass(), "traverse.xml");
ActivityResultSetIterator it = ActivityResultSetIterator.create(client, connection, 0L);
+
assertThat(it.hasNext()).isTrue();
- ActivityDoc doc = it.next();
- assertThat(doc).isNotNull();
- assertThat(doc.getKey()).isEqualTo("UUID1");
- assertThat(doc.getAction()).isEqualTo("THE_ACTION");
- assertThat(doc.getMessage()).isEqualTo("THE_MSG");
- assertThat(doc.getDetails()).containsOnly(MapEntry.entry("foo", "bar"));
- assertThat(doc.getLogin()).isEqualTo("THE_AUTHOR");
+ UpdateRequest request = it.next();
+ Map<String, Object> doc = request.doc().sourceAsMap();
+ assertThat(doc.get(ActivityIndexDefinition.FIELD_KEY)).isEqualTo("UUID1");
+ assertThat(doc.get(ActivityIndexDefinition.FIELD_ACTION)).isEqualTo("THE_ACTION");
+ assertThat(doc.get(ActivityIndexDefinition.FIELD_MESSAGE)).isEqualTo("THE_MSG");
+ assertThat((Map) doc.get(ActivityIndexDefinition.FIELD_DETAILS)).containsOnly(MapEntry.entry("foo", "bar"));
+ assertThat(doc.get(ActivityIndexDefinition.FIELD_LOGIN)).isEqualTo("THE_AUTHOR");
assertThat(it.hasNext()).isTrue();
assertThat(it.next()).isNotNull();
assertThat(it.hasNext()).isFalse();
it.close();
+
+ assertThat(it.getMaxRowDate()).isEqualTo(1420066800000L);
}
@Test
@@ -81,11 +89,25 @@ public class ActivityResultSetIteratorTest {
ActivityResultSetIterator it = ActivityResultSetIterator.create(client, connection, DateUtils.parseDate("2014-12-01").getTime());
assertThat(it.hasNext()).isTrue();
- ActivityDoc doc = it.next();
- assertThat(doc).isNotNull();
- assertThat(doc.getKey()).isEqualTo("UUID2");
+ UpdateRequest request = it.next();
+ assertThat(request).isNotNull();
+ Map<String, Object> doc = request.doc().sourceAsMap();
+ assertThat(doc.get(ActivityIndexDefinition.FIELD_KEY)).isEqualTo("UUID2");
assertThat(it.hasNext()).isFalse();
it.close();
+
+ assertThat(it.getMaxRowDate()).isEqualTo(1420066800000L);
+ }
+
+ @Test
+ public void nothing_to_traverse() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "traverse.xml");
+ ActivityResultSetIterator it = ActivityResultSetIterator.create(client, connection, DateUtils.parseDate("2030-01-01").getTime());
+
+ assertThat(it.hasNext()).isFalse();
+ it.close();
+
+ assertThat(it.getMaxRowDate()).isEqualTo(0L);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/AddColumnsBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/AddColumnsBuilderTest.java
new file mode 100644
index 00000000000..f5e2d4516fd
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/AddColumnsBuilderTest.java
@@ -0,0 +1,131 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations;
+
+import org.junit.Test;
+import org.sonar.core.persistence.dialect.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
+
+public class AddColumnsBuilderTest {
+
+ @Test
+ public void add_columns_on_h2() throws Exception {
+ assertThat(new AddColumnsBuilder(new H2(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)");
+ }
+
+ @Test
+ public void add_columns_on_mysql() throws Exception {
+ assertThat(new AddColumnsBuilder(new MySql(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)");
+ }
+
+ @Test
+ public void add_columns_on_oracle() throws Exception {
+ assertThat(new AddColumnsBuilder(new Oracle(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms NUMBER (38) NULL, name VARCHAR (10) NOT NULL)");
+ }
+
+ @Test
+ public void add_columns_on_postgresql() throws Exception {
+ assertThat(new AddColumnsBuilder(new PostgreSql(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD COLUMN date_in_ms BIGINT NULL, ADD COLUMN name VARCHAR (10) NOT NULL");
+ }
+
+ @Test
+ public void add_columns_on_mssql() throws Exception {
+ assertThat(new AddColumnsBuilder(new MsSql(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL");
+ }
+
+ @Test
+ public void fail_when_column_name_is_in_upper_case() throws Exception {
+ try {
+ new AddColumnsBuilder.ColumnDef()
+ .setName("DATE_IN_MS")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true);
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Column name should only contains lowercase and _ characters");
+ }
+ }
+
+ @Test
+ public void fail_when_column_name_contains_invalid_character() throws Exception {
+ try {
+ new AddColumnsBuilder.ColumnDef()
+ .setName("date-in/ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true);
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Column name should only contains lowercase and _ characters");
+ }
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/DropColumnsBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/DropColumnsBuilderTest.java
new file mode 100644
index 00000000000..316718d716c
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/DropColumnsBuilderTest.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations;
+
+import org.junit.Test;
+import org.sonar.core.persistence.dialect.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DropColumnsBuilderTest {
+
+ @Test
+ public void drop_columns_on_mysql() throws Exception {
+ assertThat(new DropColumnsBuilder(new MySql(), "issues", "date_in_ms", "name")
+ .build()).isEqualTo("ALTER TABLE issues DROP COLUMN date_in_ms, DROP COLUMN name");
+ }
+
+ @Test
+ public void drop_columns_on_oracle() throws Exception {
+ assertThat(new DropColumnsBuilder(new Oracle(), "issues", "date_in_ms", "name")
+ .build()).isEqualTo("ALTER TABLE issues DROP (date_in_ms, name)");
+ }
+
+ @Test
+ public void drop_columns_on_postgresql() throws Exception {
+ assertThat(new DropColumnsBuilder(new PostgreSql(), "issues", "date_in_ms", "name")
+ .build()).isEqualTo("ALTER TABLE issues DROP COLUMN date_in_ms, DROP COLUMN name");
+ }
+
+ @Test
+ public void drop_columns_on_mssql() throws Exception {
+ assertThat(new DropColumnsBuilder(new MsSql(), "issues", "date_in_ms", "name")
+ .build()).isEqualTo("ALTER TABLE issues DROP COLUMN date_in_ms, name");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void fail_to_drop_columns_on_h2() throws Exception {
+ new DropColumnsBuilder(new H2(), "issues", "date_in_ms", "name")
+ .build();
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/AddIssuesColumnsTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/AddIssuesColumnsTest.java
new file mode 100644
index 00000000000..6559ca4eb03
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/AddIssuesColumnsTest.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations.v51;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.db.migrations.DatabaseMigration;
+
+import java.sql.Types;
+
+public class AddIssuesColumnsTest {
+
+ @ClassRule
+ public static DbTester db = new DbTester().schema(AddIssuesColumnsTest.class, "schema.sql");
+
+ DatabaseMigration migration;
+
+ @Before
+ public void setUp() throws Exception {
+ migration = new AddIssuesColumns(db.database());
+ }
+
+ @Test
+ public void update_columns() throws Exception {
+ migration.execute();
+
+ db.assertColumnDefinition("issues", "issue_creation_date_ms", Types.BIGINT, null);
+ db.assertColumnDefinition("issues", "issue_update_date_ms", Types.BIGINT, null);
+ db.assertColumnDefinition("issues", "issue_close_date_ms", Types.BIGINT, null);
+ db.assertColumnDefinition("issues", "tags", Types.VARCHAR, 4000);
+ db.assertColumnDefinition("issues", "component_uuid", Types.VARCHAR, 50);
+ db.assertColumnDefinition("issues", "project_uuid", Types.VARCHAR, 50);
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/DropIssuesColumnsTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/DropIssuesColumnsTest.java
new file mode 100644
index 00000000000..0280ffe0af6
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/DropIssuesColumnsTest.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations.v51;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.core.persistence.Database;
+import org.sonar.core.persistence.dialect.PostgreSql;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DropIssuesColumnsTest {
+
+ DropIssuesColumns migration;
+
+ Database database;
+
+ @Before
+ public void setUp() throws Exception {
+ database = mock(Database.class);
+ migration = new DropIssuesColumns(database);
+ }
+
+ @Test
+ public void generate_sql_on_postgresql() throws Exception {
+ when(database.getDialect()).thenReturn(new PostgreSql());
+ assertThat(migration.generateSql()).isEqualTo(
+ "ALTER TABLE issues DROP COLUMN issue_creation_date, DROP COLUMN issue_update_date, DROP COLUMN issue_close_date, DROP COLUMN component_id, DROP COLUMN root_component_id"
+ );
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest.java
deleted file mode 100644
index 01b5a150ff1..00000000000
--- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.db.migrations.v51;
-
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.core.persistence.DbTester;
-import org.sonar.server.db.migrations.DatabaseMigration;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.utils.DateUtils.parseDate;
-
-public class FeedProjectMeasuresLongDatesTest {
- @ClassRule
- public static DbTester db = new DbTester().schema(FeedProjectMeasuresLongDatesTest.class, "schema.sql");
-
- @Before
- public void before() throws Exception {
- db.prepareDbUnit(getClass(), "before.xml");
- }
-
- @Test
- public void execute() throws Exception {
- DatabaseMigration migration = newMigration(System2.INSTANCE);
-
- migration.execute();
-
- int count = db
- .countSql("select count(*) from project_measures where " +
- "measure_date_ms is not null");
- assertThat(count).isEqualTo(2);
- }
-
- @Test
- public void take_now_if_date_in_the_future() throws Exception {
- System2 system = mock(System2.class);
- when(system.now()).thenReturn(1234L);
-
- DatabaseMigration migration = newMigration(system);
-
- migration.execute();
-
- int count = db
- .countSql("select count(*) from project_measures where " +
- "measure_date_ms = 1234");
- assertThat(count).isEqualTo(1);
- }
-
- @Test
- public void take_snapshot_date_if_in_the_past() throws Exception {
- DatabaseMigration migration = newMigration(System2.INSTANCE);
-
- migration.execute();
-
- long snapshotTime = parseDate("2014-09-25").getTime();
- int count = db
- .countSql("select count(*) from project_measures where " +
- "measure_date_ms=" + snapshotTime);
- assertThat(count).isEqualTo(1);
- }
-
- private FeedProjectMeasuresLongDates newMigration(System2 system) {
- return new FeedProjectMeasuresLongDates(db.database(), system);
- }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java
index 7f4e4e6a16f..6cb28e3db7e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java
@@ -23,17 +23,15 @@ import com.google.common.collect.ImmutableMap;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.cluster.metadata.IndexMetaData;
-import org.elasticsearch.common.unit.ByteSizeUnit;
-import org.elasticsearch.common.unit.ByteSizeValue;
-import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class BulkIndexerTest {
- @ClassRule
- public static EsTester esTester = new EsTester().addDefinitions(new FakeIndexDefinition().setReplicas(1));
+ @Rule
+ public EsTester esTester = new EsTester().addDefinitions(new FakeIndexDefinition().setReplicas(1));
@Test
public void index_nothing() throws Exception {
@@ -67,8 +65,7 @@ public class BulkIndexerTest {
assertThat(replicas()).isEqualTo(1);
BulkIndexer indexer = new BulkIndexer(esTester.client(), FakeIndexDefinition.INDEX)
- .setLarge(true)
- .setFlushByteSize(new ByteSizeValue(1, ByteSizeUnit.BYTES).bytes());
+ .setLarge(true);
indexer.start();
// replicas are temporarily disabled
@@ -85,6 +82,22 @@ public class BulkIndexerTest {
assertThat(replicas()).isEqualTo(1);
}
+ @Test
+ public void disable_refresh() throws Exception {
+ BulkIndexer indexer = new BulkIndexer(esTester.client(), FakeIndexDefinition.INDEX)
+ .setDisableRefresh(true);
+ indexer.start();
+ indexer.add(newIndexRequest(42));
+ indexer.add(newIndexRequest(78));
+ indexer.stop();
+
+ assertThat(count()).isEqualTo(0);
+
+ esTester.client().prepareRefresh(FakeIndexDefinition.INDEX).get();
+ assertThat(count()).isEqualTo(2);
+ }
+
+
private long count() {
return esTester.countDocuments("fakes", "fake");
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java
index e5139c098b4..c9f2890d349 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java
@@ -28,6 +28,7 @@ import org.sonar.server.issue.index.IssueDoc;
import org.sonar.server.search.BaseDoc;
import org.sonar.test.TestUtils;
+import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -66,4 +67,13 @@ public class EsUtilsTest {
public void util_class() throws Exception {
assertThat(TestUtils.hasOnlyPrivateConstructors(EsUtils.class));
}
+
+ @Test
+ public void es_date_format() throws Exception {
+ assertThat(EsUtils.formatDateTime(new Date(1_500_000_000_000L))).startsWith("2017-07-").hasSize(29);
+ assertThat(EsUtils.formatDateTime(null)).isNull();
+
+ assertThat(EsUtils.parseDateTime("2017-07-14T04:40:00.000+02:00").getTime()).isEqualTo(1_500_000_000_000L);
+ assertThat(EsUtils.parseDateTime(null)).isNull();
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/FakeIndexDefinition.java b/server/sonar-server/src/test/java/org/sonar/server/es/FakeIndexDefinition.java
index 3b033f6646b..5ed5fd6380b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/es/FakeIndexDefinition.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/es/FakeIndexDefinition.java
@@ -41,6 +41,7 @@ public class FakeIndexDefinition implements IndexDefinition {
public void define(IndexDefinitionContext context) {
NewIndex index = context.create(INDEX);
index.getSettings().put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, replicas);
+ index.getSettings().put("index.refresh_interval", "-1");
NewIndex.NewIndexType type = index.createType(TYPE);
type.createIntegerField(INT_FIELD);
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
index b1d975d55c9..8f9f5730607 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
@@ -19,7 +19,10 @@
*/
package org.sonar.server.issue;
-import com.google.common.collect.*;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
@@ -56,9 +59,9 @@ import org.sonar.server.permission.InternalPermissionService;
import org.sonar.server.permission.PermissionChange;
import org.sonar.server.rule.RuleTesting;
import org.sonar.server.rule.db.RuleDao;
-import org.sonar.server.source.index.SourceLineDoc;
+import org.sonar.server.source.db.FileSourceDb;
+import org.sonar.server.source.index.SourceFileResultSetIterator;
import org.sonar.server.source.index.SourceLineIndexer;
-import org.sonar.server.source.index.SourceLineResultSetIterator;
import org.sonar.server.tester.ServerTester;
import org.sonar.server.user.MockUserSession;
import org.sonar.server.user.NewUser;
@@ -72,7 +75,6 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.junit.Assert.fail;
-import static org.sonar.server.source.index.SourceLineIndexDefinition.*;
public class IssueServiceMediumTest {
@@ -621,16 +623,13 @@ public class IssueServiceMediumTest {
}
private void newSourceLine(ComponentDto file, int line, String scmAuthor) {
- SourceLineDoc line1 = new SourceLineDoc(ImmutableMap.<String, Object>builder()
- .put(FIELD_PROJECT_UUID, file.projectUuid())
- .put(FIELD_FILE_UUID, file.uuid())
- .put(FIELD_LINE, line)
- .put(FIELD_UPDATED_AT, new Date())
- .put(FIELD_SCM_AUTHOR, scmAuthor)
- .build());
- SourceLineResultSetIterator.SourceFile sourceFile = new SourceLineResultSetIterator.SourceFile(file.uuid(), System.currentTimeMillis());
- sourceFile.addLine(line1);
- tester.get(SourceLineIndexer.class).index(Iterators.singletonIterator(sourceFile));
+ FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder();
+ dataBuilder.addLinesBuilder()
+ .setLine(line)
+ .setScmAuthor(scmAuthor)
+ .build();
+ SourceFileResultSetIterator.Row row = SourceFileResultSetIterator.toRow(file.projectUuid(), file.uuid(), new Date(), dataBuilder.build());
+ tester.get(SourceLineIndexer.class).index(Iterators.singletonIterator(row));
}
private void newUser(String login) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/search/SearchClientMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/search/SearchClientMediumTest.java
index 038d841cf8f..5f0aa3305a4 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/search/SearchClientMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/search/SearchClientMediumTest.java
@@ -79,12 +79,22 @@ public class SearchClientMediumTest {
}
@Test
+ public void delete_by_query_is_not_supported() throws Exception {
+ try {
+ searchClient.prepareDeleteByQuery();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ assertThat(e).hasMessage("Delete by query must not be used. See https://github.com/elastic/elasticsearch/issues/10067. See alternatives in BulkIndexer.");
+ }
+ }
+
+ @Test
public void prepare_percolate_is_not_yet_implemented() throws Exception {
try {
searchClient.preparePercolate();
fail();
- } catch (Exception e) {
- assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Not yet implemented");
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("Not yet implemented");
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceFileResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceFileResultSetIteratorTest.java
new file mode 100644
index 00000000000..6775d2845b3
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceFileResultSetIteratorTest.java
@@ -0,0 +1,165 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.source.index;
+
+import org.assertj.core.data.MapEntry;
+import org.elasticsearch.action.update.UpdateRequest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.source.db.FileSourceDb;
+import org.sonar.server.source.db.FileSourceTesting;
+import org.sonar.test.DbTests;
+
+import java.sql.Connection;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+@Category(DbTests.class)
+public class SourceFileResultSetIteratorTest {
+
+ @ClassRule
+ public static DbTester db = new DbTester().schema(SourceFileResultSetIteratorTest.class, "schema.sql");
+
+ DbClient dbClient;
+
+ Connection connection;
+
+ @Before
+ public void setUp() throws Exception {
+ dbClient = new DbClient(db.database(), db.myBatis());
+ connection = db.openConnection();
+ }
+
+ @After
+ public void after() throws Exception {
+ connection.close();
+ }
+
+ @Test
+ public void traverse_db() throws Exception {
+ db.prepareDbUnit(getClass(), "shared.xml");
+ FileSourceTesting.updateDataColumn(connection, "F1", FileSourceTesting.newFakeData(3).build());
+
+ SourceFileResultSetIterator it = SourceFileResultSetIterator.create(dbClient, connection, 0L);
+ assertThat(it.hasNext()).isTrue();
+ SourceFileResultSetIterator.Row row = it.next();
+ assertThat(row.getProjectUuid()).isEqualTo("P1");
+ assertThat(row.getFileUuid()).isEqualTo("F1");
+ assertThat(row.getUpdatedAt()).isEqualTo(1416239042000L);
+ assertThat(row.getLineUpdateRequests()).hasSize(3);
+
+ UpdateRequest firstRequest = row.getLineUpdateRequests().get(0);
+ Map<String, Object> doc = firstRequest.doc().sourceAsMap();
+ assertThat(doc).contains(
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_PROJECT_UUID, "P1"),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_FILE_UUID, "F1"),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_LINE, 1),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_SCM_REVISION, "REVISION_1"),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_SCM_AUTHOR, "AUTHOR_1"),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_HIGHLIGHTING, "HIGHLIGHTING_1"),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_SYMBOLS, "SYMBOLS_1"),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_UT_LINE_HITS, 1),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_UT_CONDITIONS, 2),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_UT_COVERED_CONDITIONS, 3),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_IT_LINE_HITS, 4),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_IT_CONDITIONS, 5),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_IT_COVERED_CONDITIONS, 6),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_OVERALL_LINE_HITS, 7),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_OVERALL_CONDITIONS, 8),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_OVERALL_COVERED_CONDITIONS, 9)
+ );
+ it.close();
+ }
+
+ /**
+ * File with one line. No metadata available on the line.
+ */
+ @Test
+ public void minimal_data() throws Exception {
+ db.prepareDbUnit(getClass(), "shared.xml");
+ FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder();
+ dataBuilder.addLinesBuilder().setLine(1).build();
+ FileSourceTesting.updateDataColumn(connection, "F1", dataBuilder.build());
+
+ SourceFileResultSetIterator it = SourceFileResultSetIterator.create(dbClient, connection, 0L);
+ SourceFileResultSetIterator.Row row = it.next();
+ assertThat(row.getProjectUuid()).isEqualTo("P1");
+ assertThat(row.getFileUuid()).isEqualTo("F1");
+ assertThat(row.getUpdatedAt()).isEqualTo(1416239042000L);
+ assertThat(row.getLineUpdateRequests()).hasSize(1);
+ UpdateRequest firstRequest = row.getLineUpdateRequests().get(0);
+ Map<String, Object> doc = firstRequest.doc().sourceAsMap();
+ assertThat(doc).contains(
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_PROJECT_UUID, "P1"),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_FILE_UUID, "F1"),
+ MapEntry.entry(SourceLineIndexDefinition.FIELD_LINE, 1)
+ );
+ // null values
+ assertThat(doc).containsKeys(
+ SourceLineIndexDefinition.FIELD_SCM_REVISION,
+ SourceLineIndexDefinition.FIELD_SCM_AUTHOR,
+ SourceLineIndexDefinition.FIELD_HIGHLIGHTING,
+ SourceLineIndexDefinition.FIELD_SYMBOLS,
+ SourceLineIndexDefinition.FIELD_UT_LINE_HITS,
+ SourceLineIndexDefinition.FIELD_UT_CONDITIONS,
+ SourceLineIndexDefinition.FIELD_UT_COVERED_CONDITIONS,
+ SourceLineIndexDefinition.FIELD_IT_LINE_HITS,
+ SourceLineIndexDefinition.FIELD_IT_CONDITIONS,
+ SourceLineIndexDefinition.FIELD_IT_COVERED_CONDITIONS,
+ SourceLineIndexDefinition.FIELD_OVERALL_LINE_HITS,
+ SourceLineIndexDefinition.FIELD_OVERALL_CONDITIONS,
+ SourceLineIndexDefinition.FIELD_OVERALL_COVERED_CONDITIONS
+ );
+ it.close();
+ }
+
+ @Test
+ public void filter_by_date() throws Exception {
+ db.prepareDbUnit(getClass(), "shared.xml");
+
+ SourceFileResultSetIterator iterator = SourceFileResultSetIterator.create(dbClient, connection, 2000000000000L);
+ assertThat(iterator.hasNext()).isFalse();
+ iterator.close();
+ }
+
+ @Test
+ public void fail_on_bad_data_format() throws Exception {
+ db.prepareDbUnit(getClass(), "shared.xml");
+
+ FileSourceTesting.updateDataColumn(connection, "F1", "THIS_IS_NOT_PROTOBUF".getBytes());
+
+ SourceFileResultSetIterator iterator = SourceFileResultSetIterator.create(dbClient, connection, 0L);
+ try {
+ assertThat(iterator.hasNext()).isTrue();
+ iterator.next();
+ fail();
+ } catch (IllegalStateException e) {
+ // ok
+ }
+ iterator.close();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java
index 8560c032717..14b6c10b1a7 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java
@@ -20,10 +20,8 @@
package org.sonar.server.source.index;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
-import org.apache.commons.io.IOUtils;
-import org.elasticsearch.action.index.IndexRequestBuilder;
+import org.apache.commons.io.FileUtils;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
@@ -37,11 +35,12 @@ import org.sonar.api.utils.DateUtils;
import org.sonar.core.persistence.DbTester;
import org.sonar.server.db.DbClient;
import org.sonar.server.es.EsTester;
+import org.sonar.server.source.db.FileSourceDb;
import org.sonar.server.source.db.FileSourceTesting;
import org.sonar.test.DbTests;
import org.sonar.test.TestUtils;
-import java.io.FileInputStream;
+import java.io.IOException;
import java.sql.Connection;
import java.util.Date;
import java.util.List;
@@ -82,104 +81,100 @@ public class SourceLineIndexerTest {
assertThat(countDocuments()).isEqualTo(3);
}
+ /**
+ * File F1 in project P1 has one line -> to be updated
+ * File F2 in project P1 has one line -> untouched
+ */
@Test
public void update_already_indexed_lines() throws Exception {
- prepareIndex()
- .setSource(IOUtils.toString(new FileInputStream(TestUtils.getResource(this.getClass(), "line2.json"))))
- .get();
- prepareIndex()
- .setSource(IOUtils.toString(new FileInputStream(TestUtils.getResource(this.getClass(), "line2_other_file.json"))))
- .setRefresh(true)
- .get();
+ indexLine("P1", "F1", 1);
+ indexLine("P1", "F2", 1);
List<Integer> duplications = ImmutableList.of(1, 2, 3);
- SourceLineDoc line1 = new SourceLineDoc(ImmutableMap.<String, Object>builder()
- .put(FIELD_PROJECT_UUID, "abcd")
- .put(FIELD_FILE_UUID, "efgh")
- .put(FIELD_LINE, 1)
- .put(FIELD_SCM_REVISION, "cafebabe")
- .put(FIELD_SCM_DATE, DateUtils.parseDateTime("2014-01-01T12:34:56+0100"))
- .put(FIELD_SCM_AUTHOR, "polop")
- .put(FIELD_SOURCE, "package org.sonar.server.source;")
- .put(FIELD_DUPLICATIONS, duplications)
- .put(FIELD_UPDATED_AT, new Date())
- .build());
- SourceLineResultSetIterator.SourceFile file = new SourceLineResultSetIterator.SourceFile("efgh", System.currentTimeMillis());
- file.addLine(line1);
- indexer.index(Iterators.singletonIterator(file));
+ FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder();
+ dataBuilder.addLinesBuilder()
+ .setLine(1)
+ .setScmRevision("new_revision")
+ .setScmAuthor("new_author")
+ .setSource("new source")
+ .addAllDuplication(duplications)
+ .build();
+ SourceFileResultSetIterator.Row dbRow = SourceFileResultSetIterator.toRow("P1", "F1", new Date(), dataBuilder.build());
+ indexer.index(Iterators.singletonIterator(dbRow));
assertThat(countDocuments()).isEqualTo(2L);
SearchResponse fileSearch = prepareSearch()
- .setQuery(QueryBuilders.termQuery(FIELD_FILE_UUID, "efgh"))
+ .setQuery(QueryBuilders.termQuery(FIELD_FILE_UUID, "F1"))
.get();
assertThat(fileSearch.getHits().getTotalHits()).isEqualTo(1L);
Map<String, Object> fields = fileSearch.getHits().getHits()[0].sourceAsMap();
- assertThat(fields).hasSize(9);
assertThat(fields).contains(
- entry(FIELD_PROJECT_UUID, "abcd"),
- entry(FIELD_FILE_UUID, "efgh"),
+ entry(FIELD_PROJECT_UUID, "P1"),
+ entry(FIELD_FILE_UUID, "F1"),
entry(FIELD_LINE, 1),
- entry(FIELD_SCM_REVISION, "cafebabe"),
- entry(FIELD_SCM_DATE, "2014-01-01T11:34:56.000Z"),
- entry(FIELD_SCM_AUTHOR, "polop"),
- entry(FIELD_SOURCE, "package org.sonar.server.source;"),
+ entry(FIELD_SCM_REVISION, "new_revision"),
+ entry(FIELD_SCM_AUTHOR, "new_author"),
+ entry(FIELD_SOURCE, "new source"),
entry(FIELD_DUPLICATIONS, duplications)
);
}
@Test
public void delete_file_uuid() throws Exception {
- addSource("line2.json");
- addSource("line3.json");
- addSource("line2_other_file.json");
+ indexLine("P1", "F1", 1);
+ indexLine("P1", "F1", 2);
+ indexLine("P1", "F2", 1);
- indexer.deleteByFile("efgh");
+ indexer.deleteByFile("F1");
List<SearchHit> hits = getDocuments();
Map<String, Object> document = hits.get(0).getSource();
assertThat(hits).hasSize(1);
- assertThat(document.get(FIELD_LINE)).isEqualTo(2);
- assertThat(document.get(FIELD_FILE_UUID)).isEqualTo("fdsq");
+ assertThat(document.get(FIELD_LINE)).isEqualTo(1);
+ assertThat(document.get(FIELD_FILE_UUID)).isEqualTo("F2");
}
@Test
public void delete_by_project_uuid() throws Exception {
- addSource("line2.json");
- addSource("line3.json");
- addSource("line2_other_file.json");
- addSource("line3_other_project.json");
+ indexLine("P1", "F1", 1);
+ indexLine("P1", "F1", 2);
+ indexLine("P1", "F2", 1);
+ indexLine("P2", "F3", 1);
- indexer.deleteByProject("abcd");
+ indexer.deleteByProject("P1");
List<SearchHit> hits = getDocuments();
+ assertThat(hits).hasSize(1);
Map<String, Object> document = hits.get(0).getSource();
assertThat(hits).hasSize(1);
- assertThat(document.get(FIELD_PROJECT_UUID)).isEqualTo("plmn");
+ assertThat(document.get(FIELD_PROJECT_UUID)).isEqualTo("P2");
}
@Test
public void index_source_lines_with_big_test_data() throws Exception {
Integer bigValue = Short.MAX_VALUE * 2;
- SourceLineDoc line1 = new SourceLineDoc(ImmutableMap.<String, Object>builder()
- .put(FIELD_PROJECT_UUID, "abcd")
- .put(FIELD_FILE_UUID, "efgh")
- .put(FIELD_LINE, 1)
- .put(FIELD_SOURCE, "package org.sonar.server.source;")
- .put(FIELD_UT_LINE_HITS, bigValue)
- .put(FIELD_UT_CONDITIONS, bigValue)
- .put(FIELD_UT_COVERED_CONDITIONS, bigValue)
- .put(FIELD_IT_LINE_HITS, bigValue)
- .put(FIELD_IT_CONDITIONS, bigValue)
- .put(FIELD_IT_COVERED_CONDITIONS, bigValue)
- .put(FIELD_OVERALL_LINE_HITS, bigValue)
- .put(FIELD_OVERALL_CONDITIONS, bigValue)
- .put(FIELD_OVERALL_COVERED_CONDITIONS, bigValue)
- .put(FIELD_UPDATED_AT, new Date())
- .build());
- SourceLineResultSetIterator.SourceFile file = new SourceLineResultSetIterator.SourceFile("efgh", System.currentTimeMillis());
- file.addLine(line1);
- indexer.index(Iterators.singletonIterator(file));
+
+ FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder();
+ dataBuilder.addLinesBuilder()
+ .setLine(1)
+ .setScmRevision("cafebabe")
+ .setScmAuthor("polop")
+ .setScmDate(DateUtils.parseDateTime("2014-01-01T12:34:56+0100").getTime())
+ .setSource("package org.sonar.server.source;")
+ .setUtLineHits(bigValue)
+ .setUtConditions(bigValue)
+ .setUtCoveredConditions(bigValue)
+ .setItLineHits(bigValue)
+ .setItConditions(bigValue)
+ .setItCoveredConditions(bigValue)
+ .setOverallLineHits(bigValue)
+ .setOverallConditions(bigValue)
+ .setOverallCoveredConditions(bigValue)
+ .build();
+
+ SourceFileResultSetIterator.Row row = SourceFileResultSetIterator.toRow("P1", "F1", new Date(), dataBuilder.build());
+ indexer.index(Iterators.singletonIterator(row));
List<SearchHit> hits = getDocuments();
assertThat(hits).hasSize(1);
@@ -195,9 +190,11 @@ public class SourceLineIndexerTest {
assertThat(document.get(FIELD_OVERALL_COVERED_CONDITIONS)).isEqualTo(bigValue);
}
- private void addSource(String fileName) throws Exception {
- prepareIndex()
- .setSource(IOUtils.toString(new FileInputStream(TestUtils.getResource(this.getClass(), fileName))))
+ private void indexLine(String projectUuid, String fileUuid, int line) throws IOException {
+ es.client().prepareIndex(INDEX, TYPE)
+ .setId(SourceLineIndexDefinition.docKey(fileUuid, line))
+ .setSource(FileUtils.readFileToString(TestUtils.getResource(this.getClass(), projectUuid + "_" + fileUuid + "_line" + line + ".json")))
+ .setRefresh(true)
.get();
}
@@ -206,10 +203,6 @@ public class SourceLineIndexerTest {
.setTypes(TYPE);
}
- private IndexRequestBuilder prepareIndex() {
- return es.client().prepareIndex(INDEX, TYPE);
- }
-
private List<SearchHit> getDocuments() {
return es.getDocuments(INDEX, TYPE);
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java
deleted file mode 100644
index aaa10a7ae73..00000000000
--- a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.source.index;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.sonar.core.persistence.DbTester;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.source.db.FileSourceTesting;
-import org.sonar.test.DbTests;
-
-import java.sql.Connection;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-
-@Category(DbTests.class)
-public class SourceLineResultSetIteratorTest {
-
- @ClassRule
- public static DbTester db = new DbTester().schema(SourceLineResultSetIteratorTest.class, "schema.sql");
-
- DbClient dbClient;
-
- Connection connection;
-
- @Before
- public void setUp() throws Exception {
- dbClient = new DbClient(db.database(), db.myBatis());
- connection = db.openConnection();
- }
-
- @After
- public void after() throws Exception {
- connection.close();
- }
-
- @Test
- public void parse_db_and_generate_source_line_documents() throws Exception {
- db.prepareDbUnit(getClass(), "shared.xml");
- FileSourceTesting.updateDataColumn(connection, "FILE_UUID", FileSourceTesting.newFakeData(3).build());
-
- SourceLineResultSetIterator iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L);
- assertThat(iterator.hasNext()).isTrue();
- SourceLineResultSetIterator.SourceFile file = iterator.next();
- assertThat(file.getLines()).hasSize(3);
- SourceLineDoc firstLine = file.getLines().get(0);
- assertThat(firstLine.projectUuid()).isEqualTo("PROJECT_UUID");
- assertThat(firstLine.fileUuid()).isEqualTo("FILE_UUID");
- assertThat(firstLine.line()).isEqualTo(1);
- assertThat(firstLine.scmRevision()).isEqualTo("REVISION_1");
- assertThat(firstLine.scmAuthor()).isEqualTo("AUTHOR_1");
- assertThat(firstLine.highlighting()).isEqualTo("HIGHLIGHTING_1");
- assertThat(firstLine.symbols()).isEqualTo("SYMBOLS_1");
- assertThat(firstLine.source()).isEqualTo("SOURCE_1");
- assertThat(firstLine.utLineHits()).isEqualTo(1);
- assertThat(firstLine.utConditions()).isEqualTo(2);
- assertThat(firstLine.utCoveredConditions()).isEqualTo(3);
- assertThat(firstLine.itLineHits()).isEqualTo(4);
- assertThat(firstLine.itConditions()).isEqualTo(5);
- assertThat(firstLine.itCoveredConditions()).isEqualTo(6);
- assertThat(firstLine.overallLineHits()).isEqualTo(7);
- assertThat(firstLine.overallConditions()).isEqualTo(8);
- assertThat(firstLine.overallCoveredConditions()).isEqualTo(9);
- iterator.close();
- }
-
- @Test
- public void should_ignore_lines_already_handled() throws Exception {
- db.prepareDbUnit(getClass(), "shared.xml");
-
- SourceLineResultSetIterator iterator = SourceLineResultSetIterator.create(dbClient, connection, 2000000000000L);
- assertThat(iterator.hasNext()).isFalse();
- iterator.close();
- }
-
- @Test
- public void should_fail_on_bad_data_format() throws Exception {
- db.prepareDbUnit(getClass(), "shared.xml");
-
- FileSourceTesting.updateDataColumn(connection, "FILE_UUID", "THIS_IS_NOT_PROTOBUF".getBytes());
-
- SourceLineResultSetIterator iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L);
- try {
- assertThat(iterator.hasNext()).isTrue();
- iterator.next();
- fail();
- } catch (IllegalStateException e) {
- // ok
- }
- iterator.close();
- }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/util/DateCollectorTest.java b/server/sonar-server/src/test/java/org/sonar/server/util/DateCollectorTest.java
new file mode 100644
index 00000000000..da7369eee7f
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/util/DateCollectorTest.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.util;
+
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DateCollectorTest {
+
+ DateCollector collector = new DateCollector();
+
+ @Test
+ public void max_is_zero_if_no_dates() throws Exception {
+ assertThat(collector.getMax()).isEqualTo(0L);
+ }
+
+ @Test
+ public void max() throws Exception {
+ collector.add(DateUtils.parseDate("2013-06-01"));
+ collector.add(DateUtils.parseDate("2014-01-01"));
+ collector.add(DateUtils.parseDate("2013-08-01"));
+
+ assertThat(collector.getMax()).isEqualTo(DateUtils.parseDateQuietly("2014-01-01").getTime());
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/util/ProgressLoggerTest.java b/server/sonar-server/src/test/java/org/sonar/server/util/ProgressLoggerTest.java
index 0003d69b5e9..ec0afe6966b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/util/ProgressLoggerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/util/ProgressLoggerTest.java
@@ -38,12 +38,12 @@ public class ProgressLoggerTest {
progress.start();
Thread.sleep(80L);
progress.stop();
- verify(logger, atLeast(1)).info("42 rows processed");
+ verify(logger, atLeast(1)).info(startsWith("42 rows processed"));
// ability to manual log, generally final status
counter.incrementAndGet();
progress.log();
- verify(logger).info("43 rows processed");
+ verify(logger).info(startsWith("43 rows processed"));
}
@Test
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddIssuesColumnsTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddIssuesColumnsTest/schema.sql
new file mode 100644
index 00000000000..3e799c31508
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddIssuesColumnsTest/schema.sql
@@ -0,0 +1,26 @@
+CREATE TABLE "ISSUES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(50) UNIQUE NOT NULL,
+ "COMPONENT_ID" INTEGER NOT NULL,
+ "ROOT_COMPONENT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "SEVERITY" VARCHAR(10),
+ "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+ "MESSAGE" VARCHAR(4000),
+ "LINE" INTEGER,
+ "EFFORT_TO_FIX" DOUBLE,
+ "TECHNICAL_DEBT" INTEGER,
+ "STATUS" VARCHAR(20),
+ "RESOLUTION" VARCHAR(20),
+ "CHECKSUM" VARCHAR(1000),
+ "REPORTER" VARCHAR(255),
+ "ASSIGNEE" VARCHAR(255),
+ "AUTHOR_LOGIN" VARCHAR(255),
+ "ACTION_PLAN_KEY" VARCHAR(50) NULL,
+ "ISSUE_ATTRIBUTES" VARCHAR(4000),
+ "ISSUE_CREATION_DATE" TIMESTAMP,
+ "ISSUE_CLOSE_DATE" TIMESTAMP,
+ "ISSUE_UPDATE_DATE" TIMESTAMP,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/before.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/before.xml
deleted file mode 100644
index ee5eb2d271a..00000000000
--- a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/before.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<dataset>
- <!-- new migration -->
- <project_measures
- id="1"
- measure_date="2014-09-25"
- measure_date_ms="[null]"
- />
-
- <!-- re-entrant migration - ignore the ones that are already fed with new dates -->
- <project_measures
- id="2"
- measure_date="2014-09-25"
- measure_date_ms="1500000000"
- />
-
- <!-- NULL dates -->
- <project_measures
- id="3"
- measure_date="[null]"
- measure_date_ms="[null]"
- />
-</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/schema.sql
deleted file mode 100644
index 473af7b357e..00000000000
--- a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/schema.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-CREATE TABLE "PROJECT_MEASURES" (
- "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
- "MEASURE_DATE" TIMESTAMP,
- "MEASURE_DATE_MS" BIGINT
-);
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceFileResultSetIteratorTest/schema.sql
index 859eefe3625..859eefe3625 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/schema.sql
+++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceFileResultSetIteratorTest/schema.sql
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceFileResultSetIteratorTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceFileResultSetIteratorTest/shared.xml
new file mode 100644
index 00000000000..f56c7f5a796
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceFileResultSetIteratorTest/shared.xml
@@ -0,0 +1,6 @@
+<dataset>
+
+ <file_sources id="1" project_uuid="P1" file_uuid="F1" created_at="1416238020000" updated_at="1416239042000"
+ binary_data="" data_hash="" />
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line2_other_file.json b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F1_line1.json
index d558a7dbbd9..82498c6aceb 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line2_other_file.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F1_line1.json
@@ -1,7 +1,7 @@
{
- "projectUuid": "abcd",
- "fileUuid": "fdsq",
- "line": 2,
+ "projectUuid": "P1",
+ "fileUuid": "F1",
+ "line": 1,
"scmAuthor": "polop",
"scmDate": "2014-01-01T12:34:56.7+01:00",
"scmRevision": "cafebabe",
@@ -16,4 +16,4 @@
"overallLineHits": 0,
"overallConditions": 0,
"overallCoveredConditions": 0
-} \ No newline at end of file
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line2.json b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F1_line2.json
index a96a8cb1e0b..e33192a41e3 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line2.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F1_line2.json
@@ -1,6 +1,6 @@
{
- "projectUuid": "abcd",
- "fileUuid": "efgh",
+ "projectUuid": "P1",
+ "fileUuid": "F1",
"line": 2,
"scmAuthor": "polop",
"scmDate": "2014-01-01T12:34:56.7+01:00",
@@ -16,4 +16,4 @@
"overallLineHits": 0,
"overallConditions": 0,
"overallCoveredConditions": 0
-} \ No newline at end of file
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line3.json b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F2_line1.json
index a6899aea73e..7db71d27673 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line3.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P1_F2_line1.json
@@ -1,7 +1,7 @@
{
- "projectUuid": "abcd",
- "fileUuid": "efgh",
- "line": 3,
+ "projectUuid": "P1",
+ "fileUuid": "F2",
+ "line": 1,
"scmAuthor": "polop",
"scmDate": "2014-01-01T12:34:56.7+01:00",
"scmRevision": "cafebabe",
@@ -16,4 +16,4 @@
"overallLineHits": 0,
"overallConditions": 0,
"overallCoveredConditions": 0
-} \ No newline at end of file
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line3_other_project.json b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P2_F3_line1.json
index 107ccb1331f..e87ae84d428 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/line3_other_project.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/P2_F3_line1.json
@@ -1,7 +1,7 @@
{
- "projectUuid": "plmn",
- "fileUuid": "efgh",
- "line": 3,
+ "projectUuid": "P2",
+ "fileUuid": "F3",
+ "line": 1,
"scmAuthor": "polop",
"scmDate": "2014-01-01T12:34:56.7+01:00",
"scmRevision": "cafebabe",
@@ -16,4 +16,4 @@
"overallLineHits": 0,
"overallConditions": 0,
"overallCoveredConditions": 0
-} \ No newline at end of file
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/shared.xml
deleted file mode 100644
index 521e0db9ca5..00000000000
--- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/shared.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<dataset>
-
- <file_sources id="1" project_uuid="PROJECT_UUID" file_uuid="FILE_UUID" created_at="1416238020000" updated_at="1416239042000"
- binary_data="" data_hash="" />
-
-</dataset>