Remove Project.Id and use only Project.NameKey

We remove the secondary unique column Project.Id and change the
schema to rely upon only the Project.NameKey property.  This is
necessary to support databases which cannot do multiple unique
attributes on the same entity.

Change-Id: I1a9184d69e4779f8e9f755a151ce55c9b9efc4d6
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
index 40d9157..04d7038 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -14,10 +14,11 @@
 
 package com.google.gerrit.client.admin;
 
-import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.Dispatcher;
+import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.rpc.ScreenLoadCallback;
 import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.client.ui.Hyperlink;
 import com.google.gerrit.client.ui.NavigationTable;
 import com.google.gerrit.client.ui.SmallHeading;
 import com.google.gerrit.common.PageLinks;
@@ -26,7 +27,6 @@
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.user.client.History;
-import com.google.gerrit.client.ui.Hyperlink;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
 import com.google.gwt.user.client.ui.HTMLTable.Cell;
@@ -96,7 +96,7 @@
 
     @Override
     protected Object getRowItemKey(final Project item) {
-      return item.getId();
+      return item.getNameKey();
     }
 
     @Override
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
index d640c37..ad8184c 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.reviewdb;
 
 import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
 import com.google.gwtorm.client.StringKey;
 
 /** Projects match a source code repository managed by Gerrit */
@@ -53,31 +52,6 @@
     }
   }
 
-  /** Synthetic key to link to within the database */
-  public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
-    private static final long serialVersionUID = 1L;
-
-    @Column(id = 1)
-    protected int id;
-
-    protected Id() {
-    }
-
-    public Id(final int id) {
-      this.id = id;
-    }
-
-    @Override
-    public int get() {
-      return id;
-    }
-
-    @Override
-    protected void set(int newValue) {
-      id = newValue;
-    }
-  }
-
   public static enum SubmitType {
     FAST_FORWARD_ONLY('F'),
 
@@ -110,35 +84,27 @@
   @Column(id = 1)
   protected NameKey name;
 
-  @Column(id = 2)
-  protected Id projectId;
-
-  @Column(id = 3, length = Integer.MAX_VALUE, notNull = false)
+  @Column(id = 2, length = Integer.MAX_VALUE, notNull = false)
   protected String description;
 
-  @Column(id = 4)
+  @Column(id = 3)
   protected boolean useContributorAgreements;
 
-  @Column(id = 5)
+  @Column(id = 4)
   protected boolean useSignedOffBy;
 
-  @Column(id = 6)
+  @Column(id = 5)
   protected char submitType;
 
   protected Project() {
   }
 
-  public Project(final Project.NameKey newName, final Project.Id newId) {
+  public Project(final Project.NameKey newName) {
     name = newName;
-    projectId = newId;
     useContributorAgreements = true;
     setSubmitType(SubmitType.MERGE_IF_NECESSARY);
   }
 
-  public Project.Id getId() {
-    return projectId;
-  }
-
   public Project.NameKey getNameKey() {
     return name;
   }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java
index e406a8fd..b9adada 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java
@@ -19,15 +19,11 @@
 import com.google.gwtorm.client.PrimaryKey;
 import com.google.gwtorm.client.Query;
 import com.google.gwtorm.client.ResultSet;
-import com.google.gwtorm.client.SecondaryKey;
 
 public interface ProjectAccess extends Access<Project, Project.NameKey> {
   @PrimaryKey("name")
   Project get(Project.NameKey name) throws OrmException;
 
-  @SecondaryKey("projectId")
-  Project get(Project.Id id) throws OrmException;
-
   @Query("ORDER BY name")
   ResultSet<Project> all() throws OrmException;
 
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 60724f5..35c30d9 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
@@ -117,10 +117,6 @@
   @Sequence
   int nextAccountGroupId() throws OrmException;
 
-  /** Next unique id for a {@link Project}. */
-  @Sequence
-  int nextProjectId() throws OrmException;
-
   /** Next unique id for a {@link Change}. */
   @Sequence
   int nextChangeId() throws OrmException;
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
index a94244c..16175e7 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
@@ -74,6 +74,10 @@
   @Column(id = 6)
   public AccountGroup.Id registeredGroupId;
 
+  /** Identity of the project  */
+  @Column(id = 7)
+  public Project.NameKey wildProjectName;
+
   protected SystemConfig() {
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java
index ffb4cde..ee8df40 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java
@@ -1,45 +1,33 @@
+// 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.config;
 
 import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
 import com.google.gerrit.reviewdb.SystemConfig;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
 
 public class WildProjectNameProvider implements Provider<Project.NameKey> {
-  /** Project.Id meaning "any and all projects on this server". */
-  public static final Project.Id WILD_PROJECT_ID = new Project.Id(0);
-
-  private final SchemaFactory<ReviewDb> schema;
+  private final Project.NameKey name;
 
   @Inject
-  WildProjectNameProvider(final SchemaFactory<ReviewDb> schema,
-  /*
-   * Unused, but we need to force it to load before we do, otherwise we risk
-   * reading an empty database without the wild project being in the database.
-   * Asking for it should ensures Guice loads it first.
-   */
-  final SystemConfig config) {
-    this.schema = schema;
+  WildProjectNameProvider(final SystemConfig config) {
+    name = config.wildProjectName;
   }
 
   public Project.NameKey get() {
-    try {
-      final ReviewDb db = schema.open();
-      try {
-        final Project p = db.projects().get(WILD_PROJECT_ID);
-        if (p == null) {
-          throw new ProvisionException("No project " + WILD_PROJECT_ID);
-        }
-        return p.getNameKey();
-      } finally {
-        db.close();
-      }
-    } catch (OrmException e) {
-      throw new ProvisionException("Cannot load " + WILD_PROJECT_ID, e);
-    }
+    return name;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java
index e0edeb0..42e695e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java
@@ -94,8 +94,7 @@
         }
 
         final Project.NameKey nameKey = new Project.NameKey(name);
-        final Project.Id idKey = new Project.Id(db.nextProjectId());
-        final Project p = new Project(nameKey, idKey);
+        final Project p = new Project(nameKey);
 
         p.setDescription(repositoryManager.getProjectDescription(name));
         p.setSubmitType(SubmitType.MERGE_IF_NECESSARY);
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 e1c7f50..9c882de 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
@@ -24,7 +24,6 @@
 import com.google.gerrit.reviewdb.SystemConfig;
 import com.google.gerrit.server.config.SitePath;
 import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.config.WildProjectNameProvider;
 import com.google.gerrit.server.workflow.NoOpFunction;
 import com.google.gerrit.server.workflow.SubmitFunction;
 import com.google.gwtjsonrpc.server.SignedToken;
@@ -139,6 +138,7 @@
     s.adminGroupId = admin.getId();
     s.anonymousGroupId = anonymous.getId();
     s.registeredGroupId = registered.getId();
+    s.wildProjectName = DEFAULT_WILD_NAME;
     try {
       s.sitePath = site_path.getCanonicalPath();
     } catch (IOException e) {
@@ -151,7 +151,7 @@
   private void initWildCardProject(final ReviewDb c) throws OrmException {
     final Project p;
 
-    p = new Project(DEFAULT_WILD_NAME, WildProjectNameProvider.WILD_PROJECT_ID);
+    p = new Project(DEFAULT_WILD_NAME);
     p.setDescription("Rights inherited by all other projects");
     p.setUseContributorAgreements(false);
     c.projects().insert(Collections.singleton(p));
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
index 4eff0fb..8d53b4b 100644
--- 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
@@ -32,7 +32,7 @@
 /** A version of the database schema. */
 public abstract class SchemaVersion {
   /** The current schema version. */
-  private static final Class<? extends SchemaVersion> C = Schema_20.class;
+  private static final Class<? extends SchemaVersion> C = Schema_21.class;
 
   public static class Module extends AbstractModule {
     @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java
new file mode 100644
index 0000000..a6be2ad
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java
@@ -0,0 +1,59 @@
+// 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.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collections;
+
+class Schema_21 extends SchemaVersion {
+  @Inject
+  Schema_21(Provider<Schema_20> prior) {
+    super(prior);
+  }
+
+  @Override
+  protected void migrateData(ReviewDb db) throws OrmException, SQLException {
+    SystemConfig sc = db.systemConfig().get(new SystemConfig.Key());
+
+    Statement s = ((JdbcSchema) db).getConnection().createStatement();
+    try {
+      ResultSet r;
+
+      r = s.executeQuery("SELECT name FROM projects WHERE project_id = 0");
+      try {
+        if (!r.next()) {
+          throw new OrmException("Cannot read old wild project");
+        }
+        sc.wildProjectName = new Project.NameKey(r.getString(1));
+      } finally {
+        r.close();
+      }
+    } finally {
+      s.close();
+    }
+
+    db.systemConfig().update(Collections.singleton(sc));
+  }
+}
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 cc34a49..7164e99 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
@@ -21,7 +21,6 @@
 import com.google.gerrit.reviewdb.ProjectRight;
 import com.google.gerrit.reviewdb.ReviewDb;
 import com.google.gerrit.reviewdb.SystemConfig;
-import com.google.gerrit.server.config.WildProjectNameProvider;
 import com.google.gerrit.server.workflow.NoOpFunction;
 import com.google.gerrit.server.workflow.SubmitFunction;
 import com.google.gerrit.testutil.TestDatabase;
@@ -155,12 +154,13 @@
   public void testCreateSchema_WildCardProject() throws OrmException {
     final ReviewDb c = db.create().open();
     try {
+      final SystemConfig cfg;
       final Project all;
 
-      all = c.projects().get(WildProjectNameProvider.WILD_PROJECT_ID);
+      cfg = c.systemConfig().get(new SystemConfig.Key());
+      all = c.projects().get(cfg.wildProjectName);
       assertNotNull(all);
       assertEquals("-- All Projects --", all.getName());
-      assertEquals(new Project.Id(0), all.getId());
       assertFalse(all.isUseContributorAgreements());
       assertFalse(all.isUseSignedOffBy());
     } finally {
@@ -341,10 +341,12 @@
       final ApprovalCategory.Id category, int min, int max) throws OrmException {
     final ReviewDb c = db.open();
     try {
+      final SystemConfig cfg;
       final Project all;
       final ProjectRight right;
 
-      all = c.projects().get(WildProjectNameProvider.WILD_PROJECT_ID);
+      cfg = c.systemConfig().get(new SystemConfig.Key());
+      all = c.projects().get(cfg.wildProjectName);
       right = c.projectRights().get( //
           new ProjectRight.Key(all.getNameKey(), category, group));
 
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminCreateProject.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminCreateProject.java
index e284173..0bd092e 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminCreateProject.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminCreateProject.java
@@ -113,9 +113,7 @@
 
   private void createProject(Transaction txn) throws OrmException {
     final Project.NameKey newProjectNameKey = new Project.NameKey(projectName);
-
-    final Project newProject =
-        new Project(newProjectNameKey, new Project.Id(db.nextProjectId()));
+    final Project newProject = new Project(newProjectNameKey);
 
     newProject.setDescription(projectDescription);
     newProject.setSubmitType(submitType);