diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2014-07-16 20:29:30 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2014-07-16 20:32:09 +0200 |
commit | 944e902116b0c8a5614971c1dcb55ea0a3ab9292 (patch) | |
tree | 83787e7159fefe2b66c553696ac123966e4a727b /server/sonar-server/src | |
parent | 6ec644e6592181d7e857b2c562aa823e42aa8103 (diff) | |
download | sonarqube-944e902116b0c8a5614971c1dcb55ea0a3ab9292.tar.gz sonarqube-944e902116b0c8a5614971c1dcb55ea0a3ab9292.zip |
Improve db mass update
Diffstat (limited to 'server/sonar-server/src')
21 files changed, 1032 insertions, 90 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseDataChange.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseDataChange.java new file mode 100644 index 00000000000..473818b1b5a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseDataChange.java @@ -0,0 +1,49 @@ +/* + * 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 BaseDataChange implements DataChange, DatabaseMigration { + + private final Database db; + + protected BaseDataChange(Database db) { + this.db = db; + } + + @Override + public final void execute() throws SQLException { + Connection connection = null; + try { + connection = db.getDataSource().getConnection(); + connection.setAutoCommit(false); + Context context = new Context(db, connection); + execute(context); + + } finally { + DbUtils.closeQuietly(connection); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseSqlStatement.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseSqlStatement.java new file mode 100644 index 00000000000..1d31fd0f88c --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseSqlStatement.java @@ -0,0 +1,91 @@ +/* + * 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 javax.annotation.Nullable; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Date; + +abstract class BaseSqlStatement<CHILD extends SqlStatement> implements SqlStatement<CHILD> { + protected PreparedStatement pstmt; + + BaseSqlStatement(PreparedStatement pstmt) { + this.pstmt = pstmt; + } + + @Override + public CHILD close() { + DbUtils.closeQuietly(pstmt); + pstmt = null; + return (CHILD)this; + } + + @Override + public CHILD setString(int columnIndex, @Nullable String value) throws SQLException { + pstmt.setString(columnIndex, value); + return (CHILD) this; + } + + @Override + public CHILD setInt(int columnIndex, @Nullable Integer value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.INTEGER); + } else { + pstmt.setInt(columnIndex, value); + } + return (CHILD) this; + } + + @Override + public CHILD setLong(int columnIndex, @Nullable Long value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.BIGINT); + } else { + pstmt.setLong(columnIndex, value); + } + return (CHILD) this; + } + + @Override + public CHILD setBoolean(int columnIndex, @Nullable Boolean value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.BOOLEAN); + } else { + pstmt.setBoolean(columnIndex, value); + } + return (CHILD) this; + } + + @Override + public CHILD setDate(int columnIndex, @Nullable Date value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.TIMESTAMP); + } else { + pstmt.setTimestamp(columnIndex, new Timestamp(value.getTime())); + } + return (CHILD) this; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DataChange.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DataChange.java new file mode 100644 index 00000000000..20075cbf8bb --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DataChange.java @@ -0,0 +1,52 @@ +/* + * 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.Database; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface DataChange { + + class Context { + private final Database db; + private final Connection connection; + + public Context(Database db, Connection connection) { + this.db = db; + this.connection = connection; + } + + public Select prepareSelect(String sql) throws SQLException { + return SelectImpl.create(db, connection, sql); + } + + public Upsert prepareUpsert(String sql) throws SQLException { + return UpsertImpl.create(connection, sql); + } + + public MassUpdate prepareMassUpdate() throws SQLException { + return new MassUpdate(db, connection); + } + } + + void execute(Context context) throws SQLException; +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigration.java index b7f2b23c4cc..b20a35c9c4f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigration.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigration.java @@ -19,12 +19,14 @@ */ package org.sonar.server.db.migrations; +import java.sql.SQLException; + /** * Java alternative of ActiveRecord::Migration. Do not forget to declare implementation classes in {@link DatabaseMigrations#CLASSES} * @since 3.7 */ public interface DatabaseMigration { - void execute(); + void execute() throws SQLException; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/MassUpdate.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/MassUpdate.java new file mode 100644 index 00000000000..3c97714db16 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/MassUpdate.java @@ -0,0 +1,76 @@ +/* + * 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.Database; + +import java.sql.Connection; +import java.sql.SQLException; + +public class MassUpdate { + + public static interface Handler { + /** + * Convert some column values of a given row. + * @return true if the row must be updated, else false. If false, then the update parameter must not be touched. + */ + boolean handle(Select.Row row, SqlStatement update) throws SQLException; + } + + private final Database db; + private final Connection connection; + + private Select select; + private Upsert update; + + MassUpdate(Database db, Connection connection) { + this.db = db; + this.connection = connection; + } + + public SqlStatement select(String sql) throws SQLException { + this.select = SelectImpl.create(db, connection, sql); + return this.select; + } + + public MassUpdate update(String sql) throws SQLException { + this.update = UpsertImpl.create(connection, sql); + return this; + } + + public void execute(final Handler handler) throws SQLException { + if (select == null || update==null) { + throw new IllegalStateException("SELECT or UPDATE requests are not defined"); + } + + select.scroll(new Select.RowHandler() { + @Override + public void handle(Select.Row row) throws SQLException { + if (handler.handle(row, update)) { + update.addBatch(); + } + } + }); + if (((UpsertImpl)update).getBatchCount()>0L) { + update.execute().commit(); + } + update.close(); + } +} 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 new file mode 100644 index 00000000000..b9cb30522c1 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java @@ -0,0 +1,99 @@ +/* + * 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 javax.annotation.CheckForNull; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; + +public interface Select extends SqlStatement<Select> { + + static class Row { + private final ResultSet rs; + + Row(ResultSet rs) { + this.rs = rs; + } + + @CheckForNull + public Long getLong(int columnIndex) throws SQLException { + long l = rs.getLong(columnIndex); + return rs.wasNull() ? null : l; + } + + @CheckForNull + public Double getDouble(int columnIndex) throws SQLException { + double d = rs.getDouble(columnIndex); + return rs.wasNull() ? null : d; + } + + @CheckForNull + public Integer getInt(int columnIndex) throws SQLException { + int i = rs.getInt(columnIndex); + return rs.wasNull() ? null : i; + } + + @CheckForNull + public Boolean getBoolean(int columnIndex) throws SQLException { + boolean b = rs.getBoolean(columnIndex); + return rs.wasNull() ? null : b; + } + + @CheckForNull + public String getString(int columnIndex) throws SQLException { + String s = rs.getString(columnIndex); + return rs.wasNull() ? null : s; + } + + @CheckForNull + public Date getDate(int columnIndex) throws SQLException { + Timestamp t = rs.getTimestamp(columnIndex); + return rs.wasNull() ? null : t; + } + } + + static interface RowReader<T> { + T read(Row row) throws SQLException; + } + + static class LongReader implements RowReader<Long> { + private LongReader() { + } + + @Override + public Long read(Row row) throws SQLException { + return row.getLong(1); + } + } + + public static final LongReader LONG_READER = new LongReader(); + + static interface RowHandler<T> { + void handle(Row row) throws SQLException; + } + + <T> List<T> query(RowReader<T> reader) throws SQLException; + + void scroll(RowHandler handler) throws SQLException; +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SelectImpl.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SelectImpl.java new file mode 100644 index 00000000000..e8c05a3f432 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SelectImpl.java @@ -0,0 +1,78 @@ +/* + * 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 org.sonar.core.persistence.dialect.MySql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +class SelectImpl extends BaseSqlStatement<Select> implements Select { + + private SelectImpl(PreparedStatement pstmt) { + super(pstmt); + } + + @Override + public <T> List<T> query(Select.RowReader<T> reader) throws SQLException { + ResultSet rs = pstmt.executeQuery(); + Select.Row row = new Select.Row(rs); + try { + List<T> rows = new ArrayList<T>(); + while (rs.next()) { + rows.add(reader.read(row)); + } + return rows; + } finally { + DbUtils.closeQuietly(rs); + close(); + } + } + + @Override + public void scroll(Select.RowHandler handler) throws SQLException { + ResultSet rs = pstmt.executeQuery(); + Select.Row row = new Select.Row(rs); + try { + while (rs.next()) { + handler.handle(row); + } + } finally { + DbUtils.closeQuietly(rs); + close(); + } + } + + static SelectImpl create(Database db, Connection connection, String sql) throws SQLException { + PreparedStatement pstmt = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + if (db.getDialect().getId().equals(MySql.ID)) { + pstmt.setFetchSize(Integer.MIN_VALUE); + } else { + pstmt.setFetchSize(1000); + } + return new SelectImpl(pstmt); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SqlStatement.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SqlStatement.java new file mode 100644 index 00000000000..a8caf209ea7 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SqlStatement.java @@ -0,0 +1,38 @@ +/* + * 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 javax.annotation.Nullable; +import java.sql.SQLException; +import java.util.Date; + +public interface SqlStatement<CHILD extends SqlStatement> { + CHILD setBoolean(int columnIndex, @Nullable Boolean value) throws SQLException; + + CHILD setDate(int columnIndex, @Nullable Date value) throws SQLException; + + CHILD setInt(int columnIndex, @Nullable Integer value) throws SQLException; + + CHILD setLong(int columnIndex, @Nullable Long value) throws SQLException; + + CHILD setString(int columnIndex, @Nullable String value) throws SQLException; + + CHILD close(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Upsert.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Upsert.java new file mode 100644 index 00000000000..58835275705 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Upsert.java @@ -0,0 +1,30 @@ +/* + * 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 java.sql.SQLException; + +public interface Upsert extends SqlStatement<Upsert> { + Upsert addBatch() throws SQLException; + + Upsert execute() throws SQLException; + + Upsert commit() throws SQLException; +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/UpsertImpl.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/UpsertImpl.java new file mode 100644 index 00000000000..da1d3f4b8bf --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/UpsertImpl.java @@ -0,0 +1,70 @@ +/* + * 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.BatchSession; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class UpsertImpl extends BaseSqlStatement<Upsert> implements Upsert { + + private long batchCount = 0L; + + private UpsertImpl(PreparedStatement pstmt) { + super(pstmt); + } + + @Override + public Upsert addBatch() throws SQLException { + pstmt.addBatch(); + batchCount++; + if (batchCount % BatchSession.MAX_BATCH_SIZE == 0L) { + pstmt.executeBatch(); + pstmt.getConnection().commit(); + } + return this; + } + + @Override + public Upsert execute() throws SQLException { + if (batchCount == 0L) { + pstmt.execute(); + } else { + pstmt.executeBatch(); + } + return this; + } + + long getBatchCount() { + return batchCount; + } + + @Override + public Upsert commit() throws SQLException { + pstmt.getConnection().commit(); + return this; + } + + static UpsertImpl create(Connection connection, String sql) throws SQLException { + return new UpsertImpl(connection.prepareStatement(sql)); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java index 803616191b4..b83c8bbd6f1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java @@ -22,13 +22,12 @@ package org.sonar.server.db.migrations.v44; import org.apache.commons.lang.RandomStringUtils; import org.sonar.core.persistence.Database; -import org.sonar.server.db.migrations.DatabaseMigration; -import org.sonar.server.db.migrations.MassUpdater; -import org.sonar.server.db.migrations.SqlUtil; +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 org.sonar.server.util.Slug; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; /** @@ -36,90 +35,52 @@ import java.sql.SQLException; * * @since 4.4 */ -public class FeedQProfileKeysMigration implements DatabaseMigration { +public class FeedQProfileKeysMigration extends BaseDataChange { - private final Database db; - - public FeedQProfileKeysMigration(Database database) { - this.db = database; + public FeedQProfileKeysMigration(Database db) { + super(db); } @Override - public void execute() { - updateKeys(); - updateParentKeys(); + public void execute(Context context) throws SQLException { + updateKeys(context); + updateParentKeys(context); } - private void updateKeys() { - new MassUpdater(db, 100).execute( - new MassUpdater.InputLoader<Row>() { - @Override - public String selectSql() { - return "SELECT id,language,name FROM rules_profiles"; - } - - @Override - public Row load(ResultSet rs) throws SQLException { - Row row = new Row(); - row.id = SqlUtil.getLong(rs, 1); - row.lang = rs.getString(2); - row.name = rs.getString(3); - return row; - } - }, - new MassUpdater.InputConverter<Row>() { - - @Override - public String updateSql() { - return "UPDATE rules_profiles SET kee=? WHERE id=?"; - } - - @Override - public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException { - updateStatement.setString(1, Slug.slugify(String.format("%s %s %s", row.lang, row.name, RandomStringUtils.randomNumeric(5)))); - updateStatement.setLong(2, row.id); - return true; - } + private void updateKeys(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT id,language,name FROM rules_profiles"); + massUpdate.update("UPDATE rules_profiles SET kee=? WHERE id=?"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Long id = row.getLong(1); + String lang = row.getString(2); + String name = row.getString(3); + + update.setString(1, Slug.slugify(String.format("%s %s %s", lang, name, RandomStringUtils.randomNumeric(5)))); + update.setLong(2, id); + return true; } - ); + }); } - private void updateParentKeys() { - new MassUpdater(db, 100).execute( - new MassUpdater.InputLoader<Row>() { - @Override - public String selectSql() { - return "SELECT child.id,parent.kee FROM rules_profiles child, rules_profiles parent WHERE child.parent_name=parent.name " + - "and child.language=parent.language AND child.parent_name IS NOT NULL"; - } - - @Override - public Row load(ResultSet rs) throws SQLException { - Row row = new Row(); - row.id = SqlUtil.getLong(rs, 1); - row.parentKey = rs.getString(2); - return row; - } - }, - new MassUpdater.InputConverter<Row>() { - - @Override - public String updateSql() { - return "UPDATE rules_profiles SET parent_kee=? WHERE id=?"; - } - - @Override - public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException { - updateStatement.setString(1, row.parentKey); - updateStatement.setLong(2, row.id); - return true; - } + private void updateParentKeys(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT child.id,parent.kee FROM rules_profiles child, rules_profiles parent WHERE child.parent_name=parent.name " + + "and child.language=parent.language AND child.parent_name IS NOT NULL"); + massUpdate.update("UPDATE rules_profiles SET parent_kee=? WHERE id=?"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Long id = row.getLong(1); + String parentKey = row.getString(2); + + update.setString(1, parentKey); + update.setLong(2, id); + return true; } - ); - } + }); - private static class Row { - private Long id; - private String lang, name, parentKey; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/package-info.java new file mode 100644 index 00000000000..9d32d73158b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +@ParametersAreNonnullByDefault +package org.sonar.server.db.migrations.v44; + +import javax.annotation.ParametersAreNonnullByDefault; 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 new file mode 100644 index 00000000000..2b1b1babbba --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java @@ -0,0 +1,339 @@ +/* + * 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.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.core.persistence.AbstractDaoTestCase; +import org.sonar.core.persistence.BatchSession; +import org.sonar.core.persistence.TestDatabase; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; + +public class BaseDataChangeTest extends AbstractDaoTestCase { + + @ClassRule + public static TestDatabase db = new TestDatabase().schema(BaseDataChangeTest.class, "schema.sql"); + + @Before + public void setUp() throws Exception { + db.executeUpdateSql("truncate table persons"); + } + + @Test + public void query() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + final List<Long> ids = new ArrayList<Long>(); + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + ids.addAll(context.prepareSelect("select id from persons order by id desc").query(Select.LONG_READER)); + } + }.execute(); + assertThat(ids).containsExactly(3L, 2L, 1L); + } + + @Test + public void read_column_types() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + final List<Object[]> persons = new ArrayList<Object[]>(); + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + persons.addAll(context + .prepareSelect("select id,login,age,enabled,updated_at from persons where id=2") + .query(new UserReader())); + } + }.execute(); + assertThat(persons).hasSize(1); + assertThat(persons.get(0)[0]).isEqualTo(2L); + assertThat(persons.get(0)[1]).isEqualTo("emmerik"); + assertThat(persons.get(0)[2]).isEqualTo(14); + assertThat(persons.get(0)[3]).isEqualTo(true); + assertThat(persons.get(0)[4]).isNotNull(); + } + + @Test + public void parameterized_query() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + final List<Long> ids = new ArrayList<Long>(); + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + ids.addAll(context.prepareSelect("select id from persons where id>=?").setLong(1, 2L).query(Select.LONG_READER)); + } + }.execute(); + assertThat(ids).containsOnly(2L, 3L); + } + + @Test + public void bad_parameterized_query() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + final List<Long> ids = new ArrayList<Long>(); + BaseDataChange change = new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + // parameter value is not set + ids.addAll(context.prepareSelect("select id from persons where id>=?").query(Select.LONG_READER)); + } + }; + try { + change.execute(); + fail(); + } catch (SQLException e) { + // ok + } + } + + @Test + public void scroll() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + final List<Long> ids = new ArrayList<Long>(); + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + context.prepareSelect("select id from persons order by id desc").scroll(new Select.RowHandler() { + @Override + public void handle(Select.Row row) throws SQLException { + ids.add(row.getLong(1)); + } + }); + } + }.execute(); + assertThat(ids).containsExactly(3L, 2L, 1L); + } + + @Test + public void insert() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + context.prepareUpsert("insert into persons(id,login,age,enabled) values (?,?,?,?)") + .setLong(1, 10L) + .setString(2, "kurt") + .setInt(3, 27) + .setBoolean(4, true) + .execute().commit().close(); + } + }.execute(); + + db.assertDbUnit(getClass(), "insert-result.xml", "persons"); + } + + @Test + public void batch_insert() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + Upsert upsert = context.prepareUpsert("insert into persons(id,login,age,enabled) values (?,?,?,?)"); + upsert + .setLong(1, 10L) + .setString(2, "kurt") + .setInt(3, 27) + .setBoolean(4, true) + .addBatch(); + upsert + .setLong(1, 11L) + .setString(2, "courtney") + .setInt(3, 25) + .setBoolean(4, false) + .addBatch(); + upsert.execute().commit().close(); + } + }.execute(); + + db.assertDbUnit(getClass(), "batch-insert-result.xml", "persons"); + } + + @Test + public void update_null() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + Upsert upsert = context.prepareUpsert("update persons set login=?,age=?,enabled=?, updated_at=? where id=?"); + upsert + .setString(1, null) + .setInt(2, null) + .setBoolean(3, null) + .setDate(4, null) + .setLong(5, 2L) + .execute() + .commit() + .close(); + } + }.execute(); + + db.assertDbUnit(getClass(), "update-null-result.xml", "persons"); + } + + @Test + public void mass_batch_insert() throws Exception { + db.executeUpdateSql("truncate table persons"); + + final int count = BatchSession.MAX_BATCH_SIZE + 10; + new BaseDataChange(db.database()) { + @Override + public void execute(Context context) throws SQLException { + Upsert upsert = context.prepareUpsert("insert into persons(id,login,age,enabled) values (?,?,?,?)"); + for (int i = 0; i < count; i++) { + upsert + .setLong(1, 10L + i) + .setString(2, "login" + i) + .setInt(3, 10 + i) + .setBoolean(4, true) + .addBatch(); + } + upsert.execute().commit().close(); + + } + }.execute(); + + assertThat(db.count("select count(id) from persons")).isEqualTo(count); + } + + @Test + public void scroll_and_update() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + 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 { + long id = row.getLong(1); + upsert.setString(1, "login" + id).setInt(2, 10 + (int) id).setLong(3, id); + upsert.execute(); + } + }); + upsert.commit().close(); + } + }.execute(); + + db.assertDbUnit(getClass(), "scroll-and-update-result.xml", "persons"); + } + + @Test + public void mass_update() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + 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 { + long id = row.getLong(1); + update + .setString(1, "login" + id) + .setInt(2, 10 + (int) id) + .setLong(3, id); + return true; + } + }); + } + }.execute(); + + db.assertDbUnit(getClass(), "mass-update-result.xml", "persons"); + } + + @Test + public void mass_update_nothing() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + 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 { + return false; + } + }); + } + }.execute(); + + db.assertDbUnit(getClass(), "persons.xml", "persons"); + } + + @Test + public void bad_mass_update() throws Exception { + db.prepareDbUnit(getClass(), "persons.xml"); + + BaseDataChange change = 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); + // update is not set + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + return false; + } + }); + } + }; + try { + change.execute(); + fail(); + } catch (IllegalStateException e) { + assertThat(e).hasMessage("SELECT or UPDATE requests are not defined"); + } + } + + static class UserReader implements Select.RowReader<Object[]> { + @Override + public Object[] read(Select.Row row) throws SQLException { + return new Object[] { + // id, login, age, enabled + row.getLong(1), + row.getString(2), + row.getInt(3), + row.getBoolean(4), + row.getDate(5) + }; + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigrationTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigrationTest.java index 06fe2c6ad18..86e15563353 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigrationTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigrationTest.java @@ -20,7 +20,6 @@ package org.sonar.server.db.migrations.v44; -import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.sonar.api.utils.System2; @@ -36,18 +35,11 @@ public class FeedQProfileKeysMigrationTest { @ClassRule public static TestDatabase db = new TestDatabase().schema(FeedQProfileKeysMigrationTest.class, "schema.sql"); - FeedQProfileKeysMigration migration; - - @Before - public void setUp() throws Exception { - migration = new FeedQProfileKeysMigration(db.database()); - } - @Test public void feed_keys() throws Exception { db.prepareDbUnit(getClass(), "feed_keys.xml"); - migration.execute(); + new FeedQProfileKeysMigration(db.database()).execute(); QualityProfileDao dao = new QualityProfileDao(db.myBatis(), mock(System2.class)); @@ -74,5 +66,4 @@ public class FeedQProfileKeysMigrationTest { assertThat(phpProfile.getParentKee()).isNull(); } - } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/batch-insert-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/batch-insert-result.xml new file mode 100644 index 00000000000..b5598944c19 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/batch-insert-result.xml @@ -0,0 +1,8 @@ +<dataset> + <persons id="1" login="barbara" age="56" enabled="[false]" updated_at="2014-01-25"/> + <persons id="2" login="emmerik" age="14" enabled="[true]" updated_at="2014-01-25"/> + <persons id="3" login="morgan" age="3" enabled="[true]" updated_at="2014-01-25"/> + + <persons id="10" login="kurt" age="27" enabled="[true]" updated_at="[null]"/> + <persons id="11" login="courtney" age="25" enabled="[false]" updated_at="[null]"/> +</dataset> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/insert-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/insert-result.xml new file mode 100644 index 00000000000..007c460660c --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/insert-result.xml @@ -0,0 +1,7 @@ +<dataset> + <persons id="1" login="barbara" age="56" enabled="[false]" updated_at="2014-01-25"/> + <persons id="2" login="emmerik" age="14" enabled="[true]" updated_at="2014-01-25"/> + <persons id="3" login="morgan" age="3" enabled="[true]" updated_at="2014-01-25"/> + + <persons id="10" login="kurt" age="27" enabled="[true]" updated_at="[null]"/> +</dataset> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/mass-update-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/mass-update-result.xml new file mode 100644 index 00000000000..bb49e5580c9 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/mass-update-result.xml @@ -0,0 +1,5 @@ +<dataset> + <persons id="1" login="barbara" age="56" enabled="[false]" updated_at="2014-01-25"/> + <persons id="2" login="login2" age="12" enabled="[true]" updated_at="2014-01-25"/> + <persons id="3" login="login3" age="13" enabled="[true]" updated_at="2014-01-25"/> +</dataset> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/persons.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/persons.xml new file mode 100644 index 00000000000..3bc2f0aa6d9 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/persons.xml @@ -0,0 +1,5 @@ +<dataset> + <persons id="1" login="barbara" age="56" enabled="[false]" updated_at="2014-01-25"/> + <persons id="2" login="emmerik" age="14" enabled="[true]" updated_at="2014-01-25"/> + <persons id="3" login="morgan" age="3" enabled="[true]" updated_at="2014-01-25"/> +</dataset> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/schema.sql new file mode 100644 index 00000000000..8890f85d73c --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/schema.sql @@ -0,0 +1,7 @@ +CREATE TABLE "PERSONS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "LOGIN" VARCHAR(50), + "AGE" INTEGER, + "ENABLED" BOOLEAN, + "UPDATED_AT" TIMESTAMP +); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/scroll-and-update-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/scroll-and-update-result.xml new file mode 100644 index 00000000000..775a3663443 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/scroll-and-update-result.xml @@ -0,0 +1,5 @@ +<dataset> + <persons id="1" login="login1" age="11" enabled="[false]" updated_at="2014-01-25"/> + <persons id="2" login="login2" age="12" enabled="[true]" updated_at="2014-01-25"/> + <persons id="3" login="login3" age="13" enabled="[true]" updated_at="2014-01-25"/> +</dataset> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/update-null-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/update-null-result.xml new file mode 100644 index 00000000000..9044f69354d --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/BaseDataChangeTest/update-null-result.xml @@ -0,0 +1,5 @@ +<dataset> + <persons id="1" login="barbara" age="56" enabled="[false]" updated_at="2014-01-25"/> + <persons id="2" login="[null]" age="[null]" enabled="[null]" updated_at="[null]"/> + <persons id="3" login="morgan" age="3" enabled="[true]" updated_at="2014-01-25"/> +</dataset> |