blob: b86e5a748264ec17eb6ebb3b94a104c1da6b7c9f [file] [log] [blame]
// Copyright (C) 2013 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.server.schema;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.reviewdb.client.PatchSetApproval.LabelId;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.gwtorm.schema.sql.DialectH2;
import com.google.gwtorm.schema.sql.DialectMySQL;
import com.google.gwtorm.schema.sql.DialectPostgreSQL;
import com.google.gwtorm.schema.sql.SqlDialect;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
public class Schema_77 extends SchemaVersion {
private final GitRepositoryManager mgr;
private final AllProjectsName allProjects;
private final PersonIdent serverUser;
@Inject
Schema_77(Provider<Schema_76> prior, AllProjectsName allProjects,
GitRepositoryManager mgr, @GerritPersonIdent PersonIdent serverUser) {
super(prior);
this.allProjects = allProjects;
this.mgr = mgr;
this.serverUser = serverUser;
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
try {
LegacyLabelTypes labelTypes = getLegacyTypes(db);
SqlDialect dialect = ((JdbcSchema) db).getDialect();
if (dialect instanceof DialectH2) {
alterTable(db, "ALTER TABLE %s ALTER COLUMN %s varchar(255)");
} else if (dialect instanceof DialectPostgreSQL) {
alterTable(db, "ALTER TABLE %s ALTER %s TYPE varchar(255)");
} else if (dialect instanceof DialectMySQL) {
alterTable(db, "ALTER TABLE %s MODIFY %s varchar(255) BINARY");
} else {
alterTable(db, "ALTER TABLE %s MODIFY %s varchar(255)");
}
migratePatchSetApprovals(db, labelTypes);
migrateLabelsToAllProjects(db, labelTypes);
} catch (RepositoryNotFoundException e) {
throw new OrmException(e);
} catch (SQLException e) {
throw new OrmException(e);
} catch (IOException e) {
throw new OrmException(e);
} catch (ConfigInvalidException e) {
throw new OrmException(e);
}
ui.message(
"Migrated label types from database to All-Projects project.config");
}
private void alterTable(ReviewDb db, String sqlFormat) throws SQLException {
Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
try {
stmt.executeUpdate(
String.format(sqlFormat, "patch_set_approvals", "category_id"));
} finally {
stmt.close();
}
}
private void migrateLabelsToAllProjects(ReviewDb db,
LegacyLabelTypes labelTypes) throws SQLException,
RepositoryNotFoundException, IOException, ConfigInvalidException {
Repository git = mgr.openRepository(allProjects);
try {
MetaDataUpdate md =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjects, git);
md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser);
ProjectConfig config = ProjectConfig.read(md);
Map<String, LabelType> configTypes = config.getLabelSections();
List<LabelType> newTypes = Lists.newArrayList();
for (LegacyLabelType type : labelTypes.getLegacyLabelTypes()) {
if (!configTypes.containsKey(type.getName())) {
newTypes.add(type);
}
}
newTypes.addAll(configTypes.values());
configTypes.clear();
for (LabelType type : newTypes) {
configTypes.put(type.getName(), type);
}
md.setMessage("Upgrade to Gerrit Code Review schema 77\n");
config.commit(md);
} finally {
git.close();
}
}
private void migratePatchSetApprovals(ReviewDb db,
LegacyLabelTypes labelTypes) throws SQLException {
PreparedStatement stmt = ((JdbcSchema) db).getConnection().prepareStatement(
"UPDATE patch_set_approvals SET category_id = ? WHERE category_id = ?");
try {
for (LegacyLabelType type : labelTypes.getLegacyLabelTypes()) {
stmt.setString(1, type.getName());
stmt.setString(2, type.getId());
stmt.addBatch();
}
stmt.executeBatch();
} finally {
stmt.close();
}
}
static class LegacyLabelType extends LabelType {
private String id;
private LegacyLabelType(String name, List<LabelValue> values) {
super(name, values);
}
String getId() {
return id;
}
private void setId(String id) {
checkArgument(id.length() <= 4, "Invalid legacy label ID: \"%s\"", id);
this.id = id;
}
}
static class LegacyLabelTypes extends LabelTypes {
private final List<LegacyLabelType> legacyTypes;
private final Map<String, LegacyLabelType> byId;
private LegacyLabelTypes(List<LegacyLabelType> types) {
super(types);
legacyTypes = types;
byId = Maps.newHashMap();
for (LegacyLabelType type : types) {
byId.put(type.getId(), type);
}
}
List<LegacyLabelType> getLegacyLabelTypes() {
return legacyTypes;
}
@Override
public LegacyLabelType byLabel(LabelId labelId) {
LegacyLabelType t = byId.get(labelId.get());
return t != null ? t : (LegacyLabelType) super.byLabel(labelId);
}
LegacyLabelType byId(LabelId id) {
return byId.get(id.get());
}
}
static LegacyLabelTypes getLegacyTypes(ReviewDb db) throws SQLException {
List<LegacyLabelType> types = Lists.newArrayListWithCapacity(2);
Statement catStmt = null;
PreparedStatement valStmt = null;
ResultSet catRs = null;
try {
catStmt = ((JdbcSchema) db).getConnection().createStatement();
catRs = catStmt.executeQuery(
"SELECT category_id, name, abbreviated_name, function_name, "
+ " copy_min_score"
+ " FROM approval_categories"
+ " ORDER BY position, name");
valStmt = ((JdbcSchema) db).getConnection().prepareStatement(
"SELECT value, name"
+ " FROM approval_category_values"
+ " WHERE category_id = ?");
while (catRs.next()) {
String id = catRs.getString("category_id");
valStmt.setString(1, id);
List<LabelValue> values = Lists.newArrayListWithCapacity(5);
ResultSet valRs = valStmt.executeQuery();
try {
while (valRs.next()) {
values.add(new LabelValue(
valRs.getShort("value"), valRs.getString("name")));
}
} finally {
valRs.close();
}
LegacyLabelType type =
new LegacyLabelType(getLabelName(catRs.getString("name")), values);
type.setId(id);
type.setAbbreviatedName(catRs.getString("abbreviated_name"));
type.setFunctionName(catRs.getString("function_name"));
type.setCopyMinScore("Y".equals(catRs.getString("copy_min_score")));
types.add(type);
}
} finally {
if (valStmt != null) {
valStmt.close();
}
if (catRs != null) {
catRs.close();
}
if (catStmt != null) {
catStmt.close();
}
}
return new LegacyLabelTypes(types);
}
private static String getLabelName(String name) {
StringBuilder r = new StringBuilder();
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (('0' <= c && c <= '9') //
|| ('a' <= c && c <= 'z') //
|| ('A' <= c && c <= 'Z') //
|| (c == '-')) {
r.append(c);
} else if (c == ' ') {
r.append('-');
}
}
return r.toString();
}
}