diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java index af0a67cf44..72b97376da 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java @@ -34,8 +34,10 @@ import com.google.gerrit.server.git.GitProjectImporter; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.LocalDiskRepositoryManager; import com.google.gerrit.server.schema.SchemaUpdater; +import com.google.gerrit.server.schema.UpdateUI; import com.google.gerrit.server.util.HostPlatform; import com.google.gwtorm.client.OrmException; +import com.google.gwtorm.client.StatementExecutor; import com.google.inject.AbstractModule; import com.google.inject.CreationException; import com.google.inject.Guice; @@ -76,7 +78,7 @@ public class Init extends SiteProgram { init.flags.deleteOnFailure = false; run = createSiteRun(init); - run.schemaUpdater.update(); + run.upgradeSchema(); run.importGit(); } catch (Exception failure) { if (init.flags.deleteOnFailure) { @@ -176,6 +178,39 @@ public class Init extends SiteProgram { this.browser = browser; } + void upgradeSchema() throws OrmException { + schemaUpdater.update(new UpdateUI() { + @Override + public void message(String msg) { + System.err.println(msg); + System.err.flush(); + } + + @Override + public void pruneSchema(StatementExecutor e, List pruneList) + throws OrmException { + StringBuilder msg = new StringBuilder(); + msg.append("Execute the following SQL to drop unused objects:\n"); + msg.append("\n"); + for (String sql : pruneList) { + msg.append(" "); + msg.append(sql); + msg.append(";\n"); + } + + if (ui.isBatch()) { + System.err.print(msg); + System.err.flush(); + + } else if (ui.yesno(true, "%s\nExecute now", msg)) { + for (String sql : pruneList) { + e.execute(sql); + } + } + } + }); + } + void importGit() throws OrmException, IOException { if (flags.importProjects) { gitProjectImporter.run(new GitProjectImporter.Messages() { diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java index c7cf057bc1..e5ca2c66d4 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java @@ -18,10 +18,10 @@ import static com.google.inject.Scopes.SINGLETON; import static com.google.inject.Stage.PRODUCTION; import com.google.gerrit.lifecycle.LifecycleModule; -import com.google.gerrit.server.config.DatabaseModule; import com.google.gerrit.server.config.GerritServerConfigModule; import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.schema.DataSourceProvider; +import com.google.gerrit.server.schema.DatabaseModule; import com.google.gwtorm.client.OrmException; import com.google.inject.AbstractModule; import com.google.inject.CreationException; diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersion.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/CurrentSchemaVersion.java similarity index 85% rename from gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersion.java rename to gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/CurrentSchemaVersion.java index 896d827e25..2a6e980d24 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersion.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/CurrentSchemaVersion.java @@ -18,7 +18,7 @@ import com.google.gwtorm.client.Column; import com.google.gwtorm.client.StringKey; /** Current version of the database schema, to facilitate live upgrades. */ -public final class SchemaVersion { +public final class CurrentSchemaVersion { public static final class Key extends StringKey> { private static final long serialVersionUID = 1L; @@ -43,9 +43,9 @@ public final class SchemaVersion { } /** Construct a new, unconfigured instance. */ - public static SchemaVersion create() { - final SchemaVersion r = new SchemaVersion(); - r.singleton = new SchemaVersion.Key(); + public static CurrentSchemaVersion create() { + final CurrentSchemaVersion r = new CurrentSchemaVersion(); + r.singleton = new CurrentSchemaVersion.Key(); return r; } @@ -56,6 +56,6 @@ public final class SchemaVersion { @Column public transient int versionNbr; - protected SchemaVersion() { + protected CurrentSchemaVersion() { } } diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java index f962e33cca..60724f5378 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java @@ -31,7 +31,7 @@ import com.google.gwtorm.client.Sequence; * */ public interface ReviewDb extends Schema { - public static final int VERSION = 19; + /* If you change anything, update SchemaVersion.C to use a new version. */ @Relation SchemaVersionAccess schemaVersion(); diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersionAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersionAccess.java index 520435c961..eec164352f 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersionAccess.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersionAccess.java @@ -18,9 +18,9 @@ import com.google.gwtorm.client.Access; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.PrimaryKey; -/** Access interface for {@link SchemaVersion}. */ +/** Access interface for {@link CurrentSchemaVersion}. */ public interface SchemaVersionAccess extends - Access { + Access { @PrimaryKey("singleton") - SchemaVersion get(SchemaVersion.Key key) throws OrmException; + CurrentSchemaVersion get(CurrentSchemaVersion.Key key) throws OrmException; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java index d3348fab94..ba3e0fcb7f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java @@ -14,9 +14,11 @@ package com.google.gerrit.server.config; +import com.google.gerrit.reviewdb.CurrentSchemaVersion; import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.SchemaVersion; import com.google.gerrit.reviewdb.SystemConfig; +import com.google.gerrit.server.schema.Current; +import com.google.gerrit.server.schema.SchemaVersion; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; @@ -29,9 +31,14 @@ import java.util.List; public class SystemConfigProvider implements Provider { private final SchemaFactory schema; + @Current + private final Provider version; + @Inject - public SystemConfigProvider(final SchemaFactory sf) { - schema = sf; + public SystemConfigProvider(final SchemaFactory schemaFactory, + @Current final Provider version) { + this.schema = schemaFactory; + this.version = version; } @Override @@ -39,15 +46,17 @@ public class SystemConfigProvider implements Provider { try { final ReviewDb db = schema.open(); try { - SchemaVersion sVer = getSchemaVersion(db); + final CurrentSchemaVersion sVer = getSchemaVersion(db); + final int eVer = version.get().getVersionNbr(); if (sVer == null) { throw new OrmException("Schema not yet initialized." + " Run init to initialize the schema."); } - if (sVer.versionNbr != ReviewDb.VERSION) { - throw new OrmException("Unsupported schema version " + sVer.versionNbr - + "; expected schema version " + ReviewDb.VERSION + ". Run init to upgrade."); + if (sVer.versionNbr != eVer) { + throw new OrmException("Unsupported schema version " + + sVer.versionNbr + "; expected schema version " + eVer + + ". Run init to upgrade."); } final List all = db.systemConfig().all().toList(); @@ -57,8 +66,8 @@ public class SystemConfigProvider implements Provider { case 0: throw new OrmException("system_config table is empty"); default: - throw new OrmException("system_config must have exactly 1 row;" + " found " - + all.size() + " rows instead"); + throw new OrmException("system_config must have exactly 1 row;" + + " found " + all.size() + " rows instead"); } } finally { db.close(); @@ -68,9 +77,9 @@ public class SystemConfigProvider implements Provider { } } - private SchemaVersion getSchemaVersion(final ReviewDb db) { + private CurrentSchemaVersion getSchemaVersion(final ReviewDb db) { try { - return db.schemaVersion().get(new SchemaVersion.Key()); + return db.schemaVersion().get(new CurrentSchemaVersion.Key()); } catch (OrmException e) { return null; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Current.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Current.java new file mode 100644 index 0000000000..b16c977051 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Current.java @@ -0,0 +1,27 @@ +// Copyright (C) 2009 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 java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; + +import java.lang.annotation.Retention; + +/** Indicates the {@link SchemaVersion} is the current one. */ +@Retention(RUNTIME) +@BindingAnnotation +public @interface Current { +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/DatabaseModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java similarity index 89% rename from gerrit-server/src/main/java/com/google/gerrit/server/config/DatabaseModule.java rename to gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java index 71142b9191..55a36d6d74 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/DatabaseModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.server.config; +package com.google.gerrit.server.schema; import static com.google.inject.Scopes.SINGLETON; import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gerrit.server.config.FactoryModule; import com.google.gwtorm.client.SchemaFactory; import com.google.gwtorm.jdbc.Database; import com.google.inject.TypeLiteral; @@ -25,6 +26,8 @@ import com.google.inject.TypeLiteral; public class DatabaseModule extends FactoryModule { @Override protected void configure() { + install(new SchemaVersion.Module()); + bind(new TypeLiteral>() {}).to( new TypeLiteral>() {}).in(SINGLETON); bind(new TypeLiteral>() {}).toProvider( diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java similarity index 97% rename from gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java rename to gerrit-server/src/main/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java index e5b7e8119a..56b0379773 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.server.config; +package com.google.gerrit.server.schema; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gwtorm.client.OrmException; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java index 32e34ce0d6..e1c7f50da9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java @@ -17,10 +17,10 @@ package com.google.gerrit.server.schema; import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.ApprovalCategoryValue; +import com.google.gerrit.reviewdb.CurrentSchemaVersion; import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.ProjectRight; import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.SchemaVersion; import com.google.gerrit.reviewdb.SystemConfig; import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.config.SitePaths; @@ -30,6 +30,7 @@ import com.google.gerrit.server.workflow.SubmitFunction; import com.google.gwtjsonrpc.server.SignedToken; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.Transaction; +import com.google.gwtorm.jdbc.JdbcExecutor; import com.google.gwtorm.jdbc.JdbcSchema; import com.google.gwtorm.schema.sql.DialectH2; import com.google.gwtorm.schema.sql.DialectMySQL; @@ -50,17 +51,21 @@ public class SchemaCreator { private final @SitePath File site_path; + private final int versionNbr; private final ScriptRunner index_generic; private final ScriptRunner index_postgres; private final ScriptRunner mysql_nextval; @Inject - public SchemaCreator(final SitePaths site) { - this(site.site_path); + public SchemaCreator(final SitePaths site, + @Current final SchemaVersion version) { + this(site.site_path, version); } - public SchemaCreator(final @SitePath File site) { + public SchemaCreator(final @SitePath File site, + @Current final SchemaVersion version) { site_path = site; + versionNbr = version.getVersionNbr(); index_generic = new ScriptRunner("index_generic.sql"); index_postgres = new ScriptRunner("index_postgres.sql"); mysql_nextval = new ScriptRunner("mysql_nextval.sql"); @@ -68,11 +73,15 @@ public class SchemaCreator { public void create(final ReviewDb db) throws OrmException { final JdbcSchema jdbc = (JdbcSchema) db; + final JdbcExecutor e = new JdbcExecutor(jdbc); + try { + jdbc.updateSchema(e); + } finally { + e.close(); + } - jdbc.createSchema(); - - final SchemaVersion sVer = SchemaVersion.create(); - sVer.versionNbr = ReviewDb.VERSION; + final CurrentSchemaVersion sVer = CurrentSchemaVersion.create(); + sVer.versionNbr = versionNbr; db.schemaVersion().insert(Collections.singleton(sVer)); final SystemConfig sConfig = initSystemConfig(db); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java index b9daca27bc..0b776b4c53 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java @@ -14,15 +14,17 @@ package com.google.gerrit.server.schema; +import com.google.gerrit.reviewdb.CurrentSchemaVersion; import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.SchemaVersion; import com.google.gerrit.reviewdb.SystemConfig; import com.google.gerrit.server.config.SitePaths; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; +import com.google.inject.Provider; import java.io.IOException; +import java.sql.SQLException; import java.util.Collections; /** Creates or updates the current database schema. */ @@ -30,23 +32,32 @@ public class SchemaUpdater { private final SchemaFactory schema; private final SitePaths site; private final SchemaCreator creator; + private final Provider updater; @Inject SchemaUpdater(final SchemaFactory schema, final SitePaths site, - final SchemaCreator creator) { + final SchemaCreator creator, @Current final Provider update) { this.schema = schema; this.site = site; this.creator = creator; + this.updater = update; } - public void update() throws OrmException { + public void update(final UpdateUI ui) throws OrmException { final ReviewDb db = schema.open(); try { - final SchemaVersion version = getSchemaVersion(db); + final SchemaVersion u = updater.get(); + final CurrentSchemaVersion version = getSchemaVersion(db); if (version == null) { creator.create(db); } else { + try { + u.check(ui, version, db); + } catch (SQLException e) { + throw new OrmException("Cannot upgrade schema", e); + } + updateSystemConfig(db); } } finally { @@ -54,9 +65,9 @@ public class SchemaUpdater { } } - private SchemaVersion getSchemaVersion(final ReviewDb db) { + private CurrentSchemaVersion getSchemaVersion(final ReviewDb db) { try { - return db.schemaVersion().get(new SchemaVersion.Key()); + return db.schemaVersion().get(new CurrentSchemaVersion.Key()); } catch (OrmException e) { return null; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java new file mode 100644 index 0000000000..8de7c8ccf1 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java @@ -0,0 +1,153 @@ +// Copyright (C) 2009 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 com.google.gerrit.reviewdb.CurrentSchemaVersion; +import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gwtorm.client.OrmException; +import com.google.gwtorm.client.StatementExecutor; +import com.google.gwtorm.jdbc.JdbcExecutor; +import com.google.gwtorm.jdbc.JdbcSchema; +import com.google.inject.AbstractModule; +import com.google.inject.Provider; + +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** A version of the database schema. */ +public abstract class SchemaVersion { + /** The current schema version. */ + private static final Class C = Schema_19.class; + + public static class Module extends AbstractModule { + @Override + protected void configure() { + bind(SchemaVersion.class).annotatedWith(Current.class).to(C); + } + } + + private final Provider prior; + private final int versionNbr; + + protected SchemaVersion(final Provider prior) { + this.prior = prior; + this.versionNbr = guessVersion(getClass()); + } + + private static int guessVersion(Class c) { + String n = c.getName(); + n = n.substring(n.lastIndexOf('_') + 1); + while (n.startsWith("0")) + n = n.substring(1); + return Integer.parseInt(n); + } + + protected SchemaVersion(final Provider prior, + final int versionNbr) { + this.prior = prior; + this.versionNbr = versionNbr; + } + + /** @return the {@link CurrentSchemaVersion#versionNbr} this step targets. */ + public final int getVersionNbr() { + return versionNbr; + } + + public final void check(UpdateUI ui, CurrentSchemaVersion curr, ReviewDb db) + throws OrmException, SQLException { + if (curr.versionNbr == versionNbr) { + // Nothing to do, we are at the correct schema. + // + } else { + upgradeFrom(ui, curr, db); + } + } + + /** Runs check on the prior schema version, and then upgrades. */ + protected void upgradeFrom(UpdateUI ui, CurrentSchemaVersion curr, ReviewDb db) + throws OrmException, SQLException { + final JdbcSchema s = (JdbcSchema) db; + + prior.get().check(ui, curr, db); + + ui.message("Upgrading database schema from version " + curr.versionNbr + + " to " + versionNbr + " ..."); + + preUpdateSchema(db); + final JdbcExecutor e = new JdbcExecutor(s); + try { + s.updateSchema(e); + migrateData(db); + + final List pruneList = new ArrayList(); + s.pruneSchema(new StatementExecutor() { + public void execute(String sql) { + pruneList.add(sql); + } + }); + + if (!pruneList.isEmpty()) { + ui.pruneSchema(e, pruneList); + } + } finally { + e.close(); + } + finish(curr, db); + } + + /** Invoke before updateSchema adds new columns/tables. */ + protected void preUpdateSchema(ReviewDb db) throws OrmException, SQLException { + } + + /** + * Invoked between updateSchema (adds new columns/tables) and pruneSchema + * (removes deleted columns/tables). + */ + @SuppressWarnings("unused") + protected void migrateData(ReviewDb db) throws OrmException, SQLException { + } + + /** Mark the current schema version. */ + protected void finish(CurrentSchemaVersion curr, ReviewDb db) + throws OrmException { + curr.versionNbr = versionNbr; + db.schemaVersion().update(Collections.singleton(curr)); + } + + /** Rename an existing column. */ + protected void renameColumn(ReviewDb db, String table, String from, String to) + throws OrmException { + final JdbcSchema s = (JdbcSchema) db; + final JdbcExecutor e = new JdbcExecutor(s); + try { + s.renameField(e, table, from, to); + } finally { + e.close(); + } + } + + /** Execute a SQL statement. */ + protected void execute(ReviewDb db, String sql) throws SQLException { + Statement s = ((JdbcSchema) db).getConnection().createStatement(); + try { + s.execute(sql); + } finally { + s.close(); + } + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java new file mode 100644 index 0000000000..dfe595ccc9 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java @@ -0,0 +1,42 @@ +// Copyright (C) 2009 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 com.google.gerrit.reviewdb.CurrentSchemaVersion; +import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gwtorm.client.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.ProvisionException; + +class Schema_19 extends SchemaVersion { + @Inject + Schema_19() { + super(new Provider() { + public SchemaVersion get() { + throw new ProvisionException("Cannot upgrade from 18"); + } + }); + } + + @Override + protected void upgradeFrom(UpdateUI ui, CurrentSchemaVersion curr, + ReviewDb db) throws OrmException { + throw new OrmException("Cannot upgrade from " + curr.versionNbr + + "; manually run scripts from" + + " http://gerrit.googlecode.com/files/schema-upgrades003_019.zip" + + " and restart."); + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/UpdateUI.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/UpdateUI.java new file mode 100644 index 0000000000..f0109db848 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/UpdateUI.java @@ -0,0 +1,27 @@ +// Copyright (C) 2009 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 com.google.gwtorm.client.OrmException; +import com.google.gwtorm.client.StatementExecutor; + +import java.util.List; + +public interface UpdateUI { + void message(String msg); + + void pruneSchema(StatementExecutor e, List pruneList) + throws OrmException; +} diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java index cccc2f6ec8..cc34a49602 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java @@ -20,9 +20,7 @@ import com.google.gerrit.reviewdb.ApprovalCategoryValue; import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.ProjectRight; import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.SchemaVersion; import com.google.gerrit.reviewdb.SystemConfig; -import com.google.gerrit.server.config.SystemConfigProvider; import com.google.gerrit.server.config.WildProjectNameProvider; import com.google.gerrit.server.workflow.NoOpFunction; import com.google.gerrit.server.workflow.SubmitFunction; @@ -75,11 +73,8 @@ public class SchemaCreatorTest extends TestCase { // Create the schema using the current schema version. // db.create(); - final SystemConfig config = getSystemConfig(); - final SchemaVersion version = getSchemaVersion(); - assertNotNull(version); - assertEquals(ReviewDb.VERSION, version.versionNbr); - + db.assertSchemaVersion(); + final SystemConfig config = db.getSystemConfig(); assertNotNull(config); assertNotNull(config.adminGroupId); assertNotNull(config.anonymousGroupId); @@ -101,8 +96,8 @@ public class SchemaCreatorTest extends TestCase { public void testSubsequentGetReads() throws OrmException { db.create(); - final SystemConfig exp = getSystemConfig(); - final SystemConfig act = getSystemConfig(); + final SystemConfig exp = db.getSystemConfig(); + final SystemConfig act = db.getSystemConfig(); assertNotSame(exp, act); assertEquals(exp.adminGroupId, act.adminGroupId); @@ -114,7 +109,7 @@ public class SchemaCreatorTest extends TestCase { public void testCreateSchema_Group_Administrators() throws OrmException { db.create(); - final SystemConfig config = getSystemConfig(); + final SystemConfig config = db.getSystemConfig(); final ReviewDb c = db.open(); try { final AccountGroup admin = c.accountGroups().get(config.adminGroupId); @@ -129,7 +124,7 @@ public class SchemaCreatorTest extends TestCase { public void testCreateSchema_Group_AnonymousUsers() throws OrmException { db.create(); - final SystemConfig config = getSystemConfig(); + final SystemConfig config = db.getSystemConfig(); final ReviewDb c = db.open(); try { final AccountGroup anon = c.accountGroups().get(config.anonymousGroupId); @@ -144,7 +139,7 @@ public class SchemaCreatorTest extends TestCase { public void testCreateSchema_Group_RegisteredUsers() throws OrmException { db.create(); - final SystemConfig config = getSystemConfig(); + final SystemConfig config = db.getSystemConfig(); final ReviewDb c = db.open(); try { final AccountGroup reg = c.accountGroups().get(config.registeredGroupId); @@ -323,14 +318,14 @@ public class SchemaCreatorTest extends TestCase { public void testCreateSchema_DefaultAccess_AnonymousUsers() throws OrmException { db.create(); - final SystemConfig config = getSystemConfig(); + final SystemConfig config = db.getSystemConfig(); assertDefaultRight(config.anonymousGroupId, ApprovalCategory.READ, 1, 1); } public void testCreateSchema_DefaultAccess_RegisteredUsers() throws OrmException { db.create(); - final SystemConfig config = getSystemConfig(); + final SystemConfig config = db.getSystemConfig(); assertDefaultRight(config.registeredGroupId, ApprovalCategory.READ, 1, 2); assertDefaultRight(config.registeredGroupId, codeReview, -1, 1); } @@ -338,7 +333,7 @@ public class SchemaCreatorTest extends TestCase { public void testCreateSchema_DefaultAccess_Administrators() throws OrmException { db.create(); - final SystemConfig config = getSystemConfig(); + final SystemConfig config = db.getSystemConfig(); assertDefaultRight(config.adminGroupId, ApprovalCategory.READ, 1, 1); } @@ -363,17 +358,4 @@ public class SchemaCreatorTest extends TestCase { c.close(); } } - - private SystemConfig getSystemConfig() { - return new SystemConfigProvider(db).get(); - } - - private SchemaVersion getSchemaVersion() throws OrmException { - final ReviewDb c = db.open(); - try { - return c.schemaVersion().get(new SchemaVersion.Key()); - } finally { - c.close(); - } - } } diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java new file mode 100644 index 0000000000..bbd8d77c7d --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java @@ -0,0 +1,82 @@ +// Copyright (C) 2009 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 com.google.gerrit.reviewdb.ReviewDb; +import com.google.gerrit.reviewdb.SystemConfig; +import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.testutil.TestDatabase; +import com.google.gwtorm.client.OrmException; +import com.google.gwtorm.client.SchemaFactory; +import com.google.gwtorm.client.StatementExecutor; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.TypeLiteral; + +import junit.framework.TestCase; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.List; +import java.util.UUID; + +public class SchemaUpdaterTest extends TestCase { + private TestDatabase db; + + @Override + protected void setUp() throws Exception { + super.setUp(); + db = new TestDatabase(); + } + + @Override + protected void tearDown() throws Exception { + TestDatabase.drop(db); + super.tearDown(); + } + + public void testUpdate() throws OrmException, FileNotFoundException { + db.create(); + + final File site = new File(UUID.randomUUID().toString()); + final SitePaths paths = new SitePaths(site); + SchemaUpdater u = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(new TypeLiteral>() {}).toInstance(db); + bind(SitePaths.class).toInstance(paths); + install(new SchemaVersion.Module()); + } + }).getInstance(SchemaUpdater.class); + + u.update(new UpdateUI() { + @Override + public void message(String msg) { + } + + @Override + public void pruneSchema(StatementExecutor e, List pruneList) + throws OrmException { + for (String sql : pruneList) { + e.execute(sql); + } + } + }); + + db.assertSchemaVersion(); + final SystemConfig sc = db.getSystemConfig(); + assertEquals(paths.site_path.getAbsolutePath(), sc.sitePath); + } +} diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestDatabase.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestDatabase.java index 51c006ff08..1f71a917d5 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestDatabase.java +++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestDatabase.java @@ -14,12 +14,22 @@ package com.google.gerrit.testutil; +import com.google.gerrit.reviewdb.CurrentSchemaVersion; import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gerrit.reviewdb.SystemConfig; +import com.google.gerrit.server.config.SystemConfigProvider; +import com.google.gerrit.server.schema.Current; import com.google.gerrit.server.schema.SchemaCreator; +import com.google.gerrit.server.schema.SchemaVersion; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.SchemaFactory; import com.google.gwtorm.jdbc.Database; import com.google.gwtorm.jdbc.SimpleDataSource; +import com.google.inject.Guice; +import com.google.inject.Key; +import com.google.inject.Provider; + +import junit.framework.TestCase; import java.io.File; import java.sql.Connection; @@ -57,6 +67,7 @@ public class TestDatabase implements SchemaFactory { private Connection openHandle; private Database database; private boolean created; + private SchemaVersion schemaVersion; public TestDatabase() throws OrmException { try { @@ -71,6 +82,10 @@ public class TestDatabase implements SchemaFactory { // Build the access layer around the connection factory. // database = new Database(dataSource, ReviewDb.class); + + schemaVersion = + Guice.createInjector(new SchemaVersion.Module()).getBinding( + Key.get(SchemaVersion.class, Current.class)).getProvider().get(); } catch (SQLException e) { throw new OrmException(e); } @@ -91,7 +106,7 @@ public class TestDatabase implements SchemaFactory { created = true; final ReviewDb c = open(); try { - new SchemaCreator(new File(".")).create(c); + new SchemaCreator(new File("."), schemaVersion).create(c); } finally { c.close(); } @@ -112,4 +127,26 @@ public class TestDatabase implements SchemaFactory { database = null; } } + + public SystemConfig getSystemConfig() { + return new SystemConfigProvider(this, new Provider() { + public SchemaVersion get() { + return schemaVersion; + } + }).get(); + } + + public CurrentSchemaVersion getSchemaVersion() throws OrmException { + final ReviewDb c = open(); + try { + return c.schemaVersion().get(new CurrentSchemaVersion.Key()); + } finally { + c.close(); + } + } + + public void assertSchemaVersion() throws OrmException { + final CurrentSchemaVersion act = getSchemaVersion(); + TestCase.assertEquals(schemaVersion.getVersionNbr(), act.versionNbr); + } } diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java index 8f064c4d74..19c16ca91e 100644 --- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java +++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java @@ -21,13 +21,13 @@ import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.server.config.AuthConfigModule; import com.google.gerrit.server.config.CanonicalWebUrlModule; -import com.google.gerrit.server.config.DatabaseModule; import com.google.gerrit.server.config.GerritGlobalModule; import com.google.gerrit.server.config.GerritServerConfigModule; import com.google.gerrit.server.config.MasterNodeStartup; import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.config.SitePathFromSystemConfigProvider; import com.google.gerrit.server.schema.DataSourceProvider; +import com.google.gerrit.server.schema.DatabaseModule; import com.google.gerrit.sshd.SshModule; import com.google.gerrit.sshd.commands.MasterCommandModule; import com.google.inject.AbstractModule; diff --git a/pom.xml b/pom.xml index effb1c9fae..cad979b3de 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ limitations under the License. 0.5.1.51-g96b2e76 - 1.1.2 + 1.1.3-SNAPSHOT 1.2.1-SNAPSHOT 1.2.0-SNAPSHOT 2.0.0