Add support for MySQL database

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/Documentation/config-gitweb.txt b/Documentation/config-gitweb.txt
index 74f774b..bbe8711 100644
--- a/Documentation/config-gitweb.txt
+++ b/Documentation/config-gitweb.txt
@@ -12,7 +12,7 @@
 mirror, so the `refs/changes/*` namespace is available.
 
 ====
-  psql -c "UPDATE system_config SET gitweb_url='http://example.com/gitweb.cgi'" reviewdb
+  UPDATE system_config SET gitweb_url='http://example.com/gitweb.cgi'
 ====
 
 After updating `system_config`, the Gerrit server must be restarted
diff --git a/Documentation/install.txt b/Documentation/install.txt
index ccafeeb..834aa3d 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -2,7 +2,7 @@
 ============================
 
 You need a SQL database to house the Gerrit2 metadata.  Currently
-PostgreSQL is the only supported database.
+H2, MySQL and PostgreSQL are the only supported databases.
 
 Important Links
 ---------------
@@ -10,10 +10,15 @@
 PostgreSQL:
 
 * http://www.postgresql.org/docs/[Documentation]
+* link:http://jdbc.postgresql.org/download.html[JDBC Driver]
+
+MySQL:
+
+* http://dev.mysql.com/doc/[Documentation]
+* http://dev.mysql.com/downloads/connector/j/5.0.html[JDBC Driver]
 
 Optional Libraries:
 
-* link:http://jdbc.postgresql.org/download.html[PostgreSQL JDBC Driver]
 * link:http://sourceforge.net/project/showfiles.php?group_id=25357[c3p0 JDBC Driver]
 * link:http://www.bouncycastle.org/java.html[Bouncy Castle Crypto API]
 * link:http://java.sun.com/products/javamail/downloads/index.html[JavaMail]
@@ -55,10 +60,6 @@
 Setting up the Database
 -----------------------
 
-Currently PostgreSQL is the only supported database.  H2 may also
-work, but hasn't been tested in a while.  The primary concern is
-having support for the database in the gwtorm project.
-
 PostgreSQL
 ~~~~~~~~~~
 
@@ -76,6 +77,18 @@
   createdb -E UTF-8 -O gerrit2 reviewdb
 ====
 
+MySQL
+~~~~~
+
+Create a Gerrit specific user within the database and assign it a
+password, create a database, and give the user full rights:
+
+====
+  CREATE USER gerrit2 IDENTIFIED BY PASSWORD 'secret';
+  CREATE DATABASE reviewdb;
+  GRANT ALL ON reviewdb.* TO 'gerrit2'@'localhost';
+====
+
 
 Initialize the Schema
 ---------------------
@@ -102,12 +115,19 @@
 
 A script should be run to create the query indexes, so Gerrit
 can avoid table scans when looking up information.  Run the
-`sql/query_index.sql` script through your database's query tool.
+index script through your database's query tool.
 
-e.g. with PostgreSQL:
+PostgreSQL:
 
 ====
-  java -jar gerrit.war --cat sql/query_index.sql | psql reviewdb
+  java -jar gerrit.war --cat sql/index_postgres.sql | psql reviewdb
+====
+
+MySQL:
+
+====
+  java -jar gerrit.war --cat sql/index_generic.sql | mysql reviewdb
+  java -jar gerrit.war --cat sql/mysql_nextval.sql | mysql reviewdb
 ====
 
 Configure site_path
@@ -123,7 +143,7 @@
   mkdir /home/gerrit/cfg
   cd /home/gerrit/cfg
 
-  psql -c "UPDATE system_config SET site_path='/home/gerrit/cfg'" reviewdb
+  UPDATE system_config SET site_path='/home/gerrit/cfg'
 ====
 
 SSH Host Keys
@@ -159,7 +179,7 @@
 
 ====
   mkdir /srv/git
-  psql -c "UPDATE system_config SET git_base_path='/srv/git'" reviewdb
+  UPDATE system_config SET git_base_path='/srv/git'
 ====
 
 You may wish to consider also exporting this directory over the
@@ -401,7 +421,6 @@
 so that you can manage the site through the web interface:
 
 ====
-  psql reviewdb
   INSERT INTO account_group_members
     (account_id, group_id)
   VALUES (
diff --git a/Documentation/project-setup.txt b/Documentation/project-setup.txt
index 5a69d24..35f34ff 100644
--- a/Documentation/project-setup.txt
+++ b/Documentation/project-setup.txt
@@ -40,7 +40,6 @@
 for a project named `project`.
 
 ====
-  psql reviewdb
   INSERT INTO projects
   (project_id
    ,use_contributor_agreements
@@ -64,6 +63,11 @@
   ,'new/project');
 ====
 
+[NOTE]
+On MySQL use `nextval_project_id()` and `nextval_branch_id()` to
+obtain the next value in the sequences.
+
+
 Change Submit Action (submit_type)
 ----------------------------------
 
diff --git a/src/main/java/GerritServer.properties_example b/src/main/java/GerritServer.properties_example
index e5f5282..b158fef 100644
--- a/src/main/java/GerritServer.properties_example
+++ b/src/main/java/GerritServer.properties_example
@@ -24,3 +24,11 @@
 # database.url = jdbc:postgresql:reviewdb
 # database.user = gerrit2
 # database.password = supersecretcode
+
+
+# MySQL 5.0
+# curl -O http://repo1.maven.org/maven2/mysql/mysql-connector-java/5.0.8/mysql-connector-java-5.0.8.jar
+#
+# database.classpath = mysql-connector-java-5.0.8.jar
+# database.driver = com.mysql.jdbc.Driver
+# database.url = jdbc:mysql://localhost/reviewdb?user=gerrit2&password=secretcode
diff --git a/src/main/webapp/WEB-INF/sql/query_index.sql b/src/main/webapp/WEB-INF/sql/index_generic.sql
similarity index 79%
copy from src/main/webapp/WEB-INF/sql/query_index.sql
copy to src/main/webapp/WEB-INF/sql/index_generic.sql
index 3eb8df1..fcd56ba 100644
--- a/src/main/webapp/WEB-INF/sql/query_index.sql
+++ b/src/main/webapp/WEB-INF/sql/index_generic.sql
@@ -1,15 +1,5 @@
--- Cluster hot tables by their primary method of access
+-- Gerrit 2 : Generic
 --
-ALTER TABLE patch_sets CLUSTER ON patch_sets_pkey;
-ALTER TABLE patches CLUSTER ON patches_pkey;
-ALTER TABLE change_messages CLUSTER ON change_messages_pkey;
-ALTER TABLE patch_comments CLUSTER ON patch_comments_pkey;
-ALTER TABLE change_approvals CLUSTER ON change_approvals_pkey;
-
-ALTER TABLE account_group_members CLUSTER ON account_group_members_pkey;
-ALTER TABLE starred_changes CLUSTER ON starred_changes_pkey;
-CLUSTER;
-
 
 -- Indexes to support @Query
 --
@@ -65,18 +55,15 @@
 --    @PrimaryKey covers: byAccount
 --    covers:             notifyNewChanges
 CREATE INDEX account_project_watches_ntNew
-ON account_project_watches (project_id)
-WHERE notify_new_changes = 'Y';
+ON account_project_watches (notify_new_changes, project_id);
 
 --    covers:             notifyAllComments
 CREATE INDEX account_project_watches_ntCmt
-ON account_project_watches (project_id)
-WHERE notify_all_comments = 'Y';
+ON account_project_watches (notify_all_comments, project_id);
 
 --    covers:             notifySubmittedChanges
 CREATE INDEX account_project_watches_ntSub
-ON account_project_watches (project_id)
-WHERE notify_submitted_changes = 'Y';
+ON account_project_watches (notify_submitted_changes, project_id);
 
 
 -- *********************************************************************
@@ -103,33 +90,27 @@
 -- ChangeAccess
 --    covers:             byOwnerOpen
 CREATE INDEX changes_byOwnerOpen
-ON changes (owner_account_id, created_on, change_id)
-WHERE open = 'Y';
+ON changes (open, owner_account_id, created_on, change_id);
 
 --    covers:             byOwnerClosed
 CREATE INDEX changes_byOwnerClosed
-ON changes (owner_account_id, last_updated_on)
-WHERE open = 'N';
+ON changes (open, owner_account_id, last_updated_on);
 
 --    covers:             submitted, allSubmitted
 CREATE INDEX changes_submitted
-ON changes (dest_project_name, dest_branch_name, last_updated_on)
-WHERE status = 's';
+ON changes (status, dest_project_name, dest_branch_name, last_updated_on);
 
 --    covers:             allOpenPrev, allOpenNext
 CREATE INDEX changes_allOpen
-ON changes (sort_key)
-WHERE open = 'Y';
+ON changes (open, sort_key);
 
 --    covers:             byProjectOpenPrev, byProjectOpenNext
 CREATE INDEX changes_byProjectOpen
-ON changes (dest_project_name, sort_key)
-WHERE open = 'Y';
+ON changes (open, dest_project_name, sort_key);
 
 --    covers:             allClosedPrev, allClosedNext
 CREATE INDEX changes_allClosed
-ON changes (status, sort_key)
-WHERE open = 'N';
+ON changes (open, status, sort_key);
 
 
 -- *********************************************************************
@@ -137,13 +118,11 @@
 --    @PrimaryKey covers: byChange, byChangeUser
 --    covers:             openByUser
 CREATE INDEX change_approvals_openByUser
-ON change_approvals (account_id)
-WHERE change_open = 'Y';
+ON change_approvals (change_open, account_id);
 
 --    covers:             closedByUser
 CREATE INDEX change_approvals_closedByUser
-ON change_approvals (account_id, change_sort_key)
-WHERE change_open = 'N';
+ON change_approvals (change_open, account_id, change_sort_key);
 
 
 -- *********************************************************************
@@ -167,8 +146,7 @@
 -- PatchLineCommentAccess
 --    @PrimaryKey covers: published, draft
 CREATE INDEX patch_comment_drafts
-ON patch_comments (author_id)
-WHERE status = 'd';
+ON patch_comments (status, author_id);
 
 
 -- *********************************************************************
diff --git a/src/main/webapp/WEB-INF/sql/query_index.sql b/src/main/webapp/WEB-INF/sql/index_postgres.sql
similarity index 99%
rename from src/main/webapp/WEB-INF/sql/query_index.sql
rename to src/main/webapp/WEB-INF/sql/index_postgres.sql
index 3eb8df1..5cda3d6 100644
--- a/src/main/webapp/WEB-INF/sql/query_index.sql
+++ b/src/main/webapp/WEB-INF/sql/index_postgres.sql
@@ -1,3 +1,6 @@
+-- Gerrit 2 : PostgreSQL
+--
+
 -- Cluster hot tables by their primary method of access
 --
 ALTER TABLE patch_sets CLUSTER ON patch_sets_pkey;
diff --git a/src/main/webapp/WEB-INF/sql/mysql_nextval.sql b/src/main/webapp/WEB-INF/sql/mysql_nextval.sql
new file mode 100644
index 0000000..c618498
--- /dev/null
+++ b/src/main/webapp/WEB-INF/sql/mysql_nextval.sql
@@ -0,0 +1,39 @@
+-- Gerrit 2 : MySQL
+--
+delimiter //
+
+CREATE FUNCTION nextval_branch_id ()
+  RETURNS BIGINT
+  LANGUAGE SQL
+  NOT DETERMINISTIC
+  MODIFIES SQL DATA
+BEGIN
+  INSERT INTO branch_id (s) VALUES (NULL);
+  DELETE FROM branch_id WHERE s = LAST_INSERT_ID();
+  RETURN LAST_INSERT_ID();
+END;
+//
+
+CREATE FUNCTION nextval_project_id ()
+  RETURNS BIGINT
+  LANGUAGE SQL
+  NOT DETERMINISTIC
+  MODIFIES SQL DATA
+BEGIN
+  INSERT INTO project_id (s) VALUES (NULL);
+  DELETE FROM project_id WHERE s = LAST_INSERT_ID();
+  RETURN LAST_INSERT_ID();
+END;
+//
+
+CREATE FUNCTION nextval_account_id ()
+  RETURNS BIGINT
+  LANGUAGE SQL
+  NOT DETERMINISTIC
+  MODIFIES SQL DATA
+BEGIN
+  INSERT INTO account_id (s) VALUES (NULL);
+  DELETE FROM account_id WHERE s = LAST_INSERT_ID();
+  RETURN LAST_INSERT_ID();
+END;
+//