From 4c1aafe170574f3d1d6547b363f992a281af21bf Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Wed, 18 Feb 2015 15:02:18 +0100 Subject: [PATCH] SONAR-6174 Ignore any exception during file_sources migration when parsing duplication data --- .../sonar/server/db/migrations/Select.java | 20 ++++ .../server/db/migrations/SelectImpl.java | 10 ++ .../db/migrations/v50/FileSourceDto.java | 10 +- .../db/migrations/BaseDataChangeTest.java | 104 +++++++++++++++++- .../migrations/v50/FeedFileSourcesTest.java | 38 +++++++ .../v51/FeedFileSourcesBinaryDataTest.java | 19 ++-- .../after-with-invalid-duplication.xml | 13 +++ 7 files changed, 195 insertions(+), 19 deletions(-) create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v50/FeedFileSourcesTest/after-with-invalid-duplication.xml diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java index 4c5b35143df..869c74e33c9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java @@ -22,6 +22,7 @@ package org.sonar.server.db.migrations; import javax.annotation.CheckForNull; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Timestamp; import java.util.Date; @@ -105,6 +106,25 @@ public interface Select extends SqlStatement implements Select { rows.add(reader.read(row)); } return rows; + } catch (Exception e) { + throw newExceptionWithRowDetails(row, e); } finally { DbUtils.closeQuietly(rs); close(); @@ -60,6 +62,8 @@ class SelectImpl extends BaseSqlStatement implements Select { while (rs.next()) { handler.handle(row); } + } catch (Exception e) { + throw newExceptionWithRowDetails(row, e); } finally { DbUtils.closeQuietly(rs); close(); } } + private IllegalStateException newExceptionWithRowDetails(Select.Row row, Exception e) { + return new IllegalStateException("Error during processing of row: [" + row + "]", e); + } + static SelectImpl create(Database db, Connection connection, String sql) throws SQLException { // TODO use DbClient#newScrollingSelectStatement() PreparedStatement pstmt = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FileSourceDto.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FileSourceDto.java index f1bbcb00af0..3a4e6a7d53e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FileSourceDto.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FileSourceDto.java @@ -29,7 +29,6 @@ import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.utils.KeyValueFormat; -import org.sonar.api.utils.SonarException; import org.sonar.api.utils.text.CsvWriter; import javax.xml.stream.XMLInputFactory; @@ -161,8 +160,8 @@ class FileSourceDto { * Parses data of {@link CoreMetrics#DUPLICATIONS_DATA}. */ private static List> parseDuplicationData(String data) { + ImmutableList.Builder> groups = ImmutableList.builder(); try { - ImmutableList.Builder> groups = ImmutableList.builder(); StringReader reader = new StringReader(data); SMInputFactory inputFactory = initStax(); SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader); @@ -183,10 +182,11 @@ class FileSourceDto { } groups.add(group.build()); } - return groups.build(); - } catch (XMLStreamException e) { - throw new SonarException(e.getMessage(), e); + } catch (Exception e) { + // SONAR-6174 Ignore any issue while parsing duplication measure. There is nothing user can do and things will get solved after + // next analysis anyway } + return groups.build(); } private static int getAttrIntValue(SMInputCursor cursor, String attrName) throws XMLStreamException { diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java index cb156004ef6..1c975a4f7e5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java @@ -21,11 +21,15 @@ package org.sonar.server.db.migrations; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.junit.rules.ExpectedException; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.BatchSession; import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.migrations.Select.Row; +import org.sonar.server.db.migrations.Select.RowReader; import org.sonar.test.DbTests; import java.sql.SQLException; @@ -42,6 +46,9 @@ public class BaseDataChangeTest extends AbstractDaoTestCase { @ClassRule public static DbTester db = new DbTester().schema(BaseDataChangeTest.class, "schema.sql"); + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Before public void setUp() throws Exception { db.executeUpdateSql("truncate table persons"); @@ -103,6 +110,48 @@ public class BaseDataChangeTest extends AbstractDaoTestCase { assertThat(ids).containsOnly(2L, 3L); } + @Test + public void display_current_row_details_if_error_during_get() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Error during processing of row: [id=2]"); + + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + context.prepareSelect("select id from persons where id>=?").setLong(1, 2L).get(new RowReader() { + @Override + public Long read(Row row) throws SQLException { + throw new IllegalStateException("Unexpected error"); + } + }); + } + }.execute(); + + } + + @Test + public void display_current_row_details_if_error_during_list() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Error during processing of row: [id=2]"); + + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + context.prepareSelect("select id from persons where id>=?").setLong(1, 2L).list(new RowReader() { + @Override + public Long read(Row row) throws SQLException { + throw new IllegalStateException("Unexpected error"); + } + }); + } + }.execute(); + + } + @Test public void bad_parameterized_query() throws Exception { db.prepareDbUnit(getClass(), "persons.xml"); @@ -115,12 +164,10 @@ public class BaseDataChangeTest extends AbstractDaoTestCase { ids.addAll(context.prepareSelect("select id from persons where id>=?").list(Select.LONG_READER)); } }; - try { - change.execute(); - fail(); - } catch (SQLException e) { - // ok - } + + thrown.expect(SQLException.class); + + change.execute(); } @Test @@ -264,6 +311,28 @@ public class BaseDataChangeTest extends AbstractDaoTestCase { db.assertDbUnit(getClass(), "scroll-and-update-result.xml", "persons"); } + @Test + public void display_current_row_details_if_error_during_scroll() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Error during processing of row: [id=1]"); + + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + final Upsert upsert = context.prepareUpsert("update persons set login=?, age=? where id=?"); + context.prepareSelect("select id from persons").scroll(new Select.RowHandler() { + @Override + public void handle(Select.Row row) throws SQLException { + throw new IllegalStateException("Unexpected error"); + } + }); + upsert.commit().close(); + } + }.execute(); + } + @Test public void mass_update() throws Exception { db.prepareDbUnit(getClass(), "persons.xml"); @@ -291,6 +360,29 @@ public class BaseDataChangeTest extends AbstractDaoTestCase { db.assertDbUnit(getClass(), "mass-update-result.xml", "persons"); } + @Test + public void display_current_row_details_if_error_during_mass_update() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Error during processing of row: [id=2]"); + + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("select id from persons where id>=?").setLong(1, 2L); + massUpdate.update("update persons set login=?, age=? where id=?"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + throw new IllegalStateException("Unexpected error"); + } + }); + } + }.execute(); + } + @Test public void mass_update_nothing() throws Exception { db.prepareDbUnit(getClass(), "persons.xml"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v50/FeedFileSourcesTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v50/FeedFileSourcesTest.java index 90f12826a24..9607166b22d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v50/FeedFileSourcesTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v50/FeedFileSourcesTest.java @@ -214,4 +214,42 @@ public class FeedFileSourcesTest { db.assertDbUnit(getClass(), "after-with-scm.xml", "file_sources"); } + + @Test + public void migrate_sources_with_invalid_duplication() throws Exception { + db.prepareDbUnit(getClass(), "before.xml"); + + Connection connection = null; + try { + connection = db.openConnection(); + + connection.prepareStatement("insert into snapshot_sources " + + "(snapshot_id, data, updated_at) " + + "values " + + "(6, 'class Foo {\r\n // Empty\r\n}\r\n', '2014-10-31 16:44:02.000')") + .executeUpdate(); + + db.executeUpdateSql("insert into snapshot_sources " + + "(snapshot_id, data, updated_at) " + + "values " + + "(7, '', '2014-10-31 16:44:02.000')"); + + PreparedStatement duplicationDataStmt = connection.prepareStatement("insert into project_measures " + + "(metric_id, snapshot_id, text_value) " + + "values " + + "(13, 6, ?)"); + duplicationDataStmt + .setBytes( + 1, + " + + + + + + -- 2.39.5