blob: e12bde2afdb1d4c1b79ab6df59e9977d4c8af3d0 [file] [log] [blame]
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.pgm;
import com.google.auto.value.AutoValue;
import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.ThreadSettingsConfig;
import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
import com.google.inject.Injector;
import com.google.inject.Key;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
import org.kohsuke.args4j.Option;
/** Migrates AccountPatchReviewDb from one to another */
public class MigrateAccountPatchReviewDb extends SiteProgram {
@Option(name = "--sourceUrl", usage = "Url of source database")
private String sourceUrl;
@Option(
name = "--chunkSize",
usage = "chunk size of fetching from source and push to target on each time")
private static long chunkSize = 100000;
@Override
public int run() throws Exception {
Injector dbInjector = createDbInjector();
SitePaths sitePaths = new SitePaths(getSitePath());
ThreadSettingsConfig threadSettingsConfig = dbInjector.getInstance(ThreadSettingsConfig.class);
Config fakeCfg = new Config();
if (!Strings.isNullOrEmpty(sourceUrl)) {
fakeCfg.setString("accountPatchReviewDb", null, "url", sourceUrl);
}
JdbcAccountPatchReviewStore sourceJdbcAccountPatchReviewStore =
JdbcAccountPatchReviewStore.createAccountPatchReviewStore(
fakeCfg, sitePaths, threadSettingsConfig);
Config cfg = dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
String targetUrl = cfg.getString("accountPatchReviewDb", null, "url");
if (targetUrl == null) {
System.err.println("accountPatchReviewDb.url is null in gerrit.config");
return 1;
}
System.out.println("target Url: " + targetUrl);
JdbcAccountPatchReviewStore targetJdbcAccountPatchReviewStore =
JdbcAccountPatchReviewStore.createAccountPatchReviewStore(
cfg, sitePaths, threadSettingsConfig);
targetJdbcAccountPatchReviewStore.createTableIfNotExists();
if (!isTargetTableEmpty(targetJdbcAccountPatchReviewStore)) {
System.err.println("target table is not empty, cannot proceed");
return 1;
}
try (Connection sourceCon = sourceJdbcAccountPatchReviewStore.getConnection();
Connection targetCon = targetJdbcAccountPatchReviewStore.getConnection();
PreparedStatement sourceStmt =
sourceCon.prepareStatement(
"SELECT account_id, change_id, patch_set_id, file_name "
+ "FROM account_patch_reviews "
+ "LIMIT ? "
+ "OFFSET ?");
PreparedStatement targetStmt =
targetCon.prepareStatement(
"INSERT INTO account_patch_reviews "
+ "(account_id, change_id, patch_set_id, file_name) VALUES "
+ "(?, ?, ?, ?)")) {
targetCon.setAutoCommit(false);
long offset = 0;
Stopwatch sw = Stopwatch.createStarted();
List<Row> rows = selectRows(sourceStmt, offset);
while (!rows.isEmpty()) {
insertRows(targetCon, targetStmt, rows);
offset += rows.size();
System.out.printf("%8d rows migrated\n", offset);
rows = selectRows(sourceStmt, offset);
}
double t = sw.elapsed(TimeUnit.MILLISECONDS) / 1000d;
System.out.printf("Migrated %d rows in %.01fs (%.01f/s)\n", offset, t, offset / t);
}
return 0;
}
@AutoValue
abstract static class Row {
abstract int accountId();
abstract int changeId();
abstract int patchSetId();
abstract String fileName();
}
private static boolean isTargetTableEmpty(JdbcAccountPatchReviewStore store) throws SQLException {
try (Connection con = store.getConnection();
Statement s = con.createStatement();
ResultSet r = s.executeQuery("SELECT COUNT(1) FROM account_patch_reviews")) {
if (r.next()) {
return r.getInt(1) == 0;
}
return true;
}
}
private static List<Row> selectRows(PreparedStatement stmt, long offset) throws SQLException {
List<Row> results = new ArrayList<>();
stmt.setLong(1, chunkSize);
stmt.setLong(2, offset);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
results.add(
new AutoValue_MigrateAccountPatchReviewDb_Row(
rs.getInt("account_id"),
rs.getInt("change_id"),
rs.getInt("patch_set_id"),
rs.getString("file_name")));
}
}
return results;
}
private static void insertRows(Connection con, PreparedStatement stmt, List<Row> rows)
throws SQLException {
for (Row r : rows) {
stmt.setLong(1, r.accountId());
stmt.setLong(2, r.changeId());
stmt.setLong(3, r.patchSetId());
stmt.setString(4, r.fileName());
stmt.addBatch();
}
stmt.executeBatch();
con.commit();
}
}