Add MySQL dialect support

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gwtorm/jdbc/Database.java b/src/main/java/com/google/gwtorm/jdbc/Database.java
index 753ea15..553dde3 100644
--- a/src/main/java/com/google/gwtorm/jdbc/Database.java
+++ b/src/main/java/com/google/gwtorm/jdbc/Database.java
@@ -24,6 +24,7 @@
 import com.google.gwtorm.schema.SchemaModel;
 import com.google.gwtorm.schema.java.JavaSchemaModel;
 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.StandardKeyEncoder;
@@ -87,6 +88,9 @@
         } else if (url.startsWith("jdbc:h2:")) {
           dialect = new DialectH2();
 
+        } else if (url.startsWith("jdbc:mysql:")) {
+          dialect = new DialectMySQL();
+
         } else {
           throw new OrmException("No dialect known for " + url);
         }
diff --git a/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java b/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
index 6645c79..9eba791 100644
--- a/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
+++ b/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
@@ -25,7 +25,6 @@
 import com.google.gwtorm.schema.sql.SqlDialect;
 
 import java.sql.Connection;
-import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 
@@ -92,28 +91,7 @@
   }
 
   protected long nextLong(final String query) throws OrmException {
-    try {
-      final Statement st = getConnection().createStatement();
-      try {
-        final ResultSet rs = st.executeQuery(query);
-        try {
-          if (!rs.next()) {
-            throw new SQLException("No result row for sequence query");
-          }
-          final long r = rs.getLong(1);
-          if (rs.next()) {
-            throw new SQLException("Too many results from sequence query");
-          }
-          return r;
-        } finally {
-          rs.close();
-        }
-      } finally {
-        st.close();
-      }
-    } catch (SQLException e) {
-      throw convertError("sequence", query, e);
-    }
+    return getDialect().nextLong(getConnection(), query);
   }
 
   public Transaction beginTransaction() {
@@ -130,12 +108,4 @@
       conn = null;
     }
   }
-
-  private OrmException convertError(final String op, final String what,
-      final SQLException err) {
-    if (err.getCause() == null && err.getNextException() != null) {
-      err.initCause(err.getNextException());
-    }
-    return getDialect().convertError(op, what, err);
-  }
 }
diff --git a/src/main/java/com/google/gwtorm/schema/sql/DialectMySQL.java b/src/main/java/com/google/gwtorm/schema/sql/DialectMySQL.java
new file mode 100644
index 0000000..0844e4c
--- /dev/null
+++ b/src/main/java/com/google/gwtorm/schema/sql/DialectMySQL.java
@@ -0,0 +1,110 @@
+// Copyright 2008 Google Inc.
+//
+// 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.gwtorm.schema.sql;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Sequence;
+import com.google.gwtorm.schema.ColumnModel;
+import com.google.gwtorm.schema.RelationModel;
+import com.google.gwtorm.schema.SequenceModel;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+
+/** Dialect for <a href="http://www.mysql.com/">MySQL</a> */
+public class DialectMySQL extends SqlDialect {
+  public DialectMySQL() {
+    types.put(String.class, new SqlStringTypeInfo() {
+      @Override
+      public String getSqlType(final ColumnModel col, final SqlDialect dialect) {
+        final Column column = col.getColumnAnnotation();
+        final StringBuilder r = new StringBuilder();
+        final int type;
+
+        if (column.length() <= 0) {
+          r.append("VARCHAR(255)");
+          if (col.isNotNull()) {
+            r.append(" DEFAULT ''");
+          }
+        } else if (column.length() <= 255) {
+          r.append("VARCHAR(" + column.length() + ")");
+          if (col.isNotNull()) {
+            r.append(" DEFAULT ''");
+          }
+        } else {
+          r.append(dialect.getSqlTypeName(Types.LONGVARCHAR));
+        }
+
+        if (col.isNotNull()) {
+          r.append(" NOT NULL");
+        }
+
+        return r.toString();
+      }
+    });
+    types.put(java.sql.Timestamp.class, new SqlTimestampTypeInfo() {
+      @Override
+      public String getSqlDefault() {
+        return "'1970-01-01 00:00:01'";
+      }
+    });
+  }
+
+  @Override
+  public String getCreateSequenceSql(final SequenceModel seq) {
+    final Sequence s = seq.getSequence();
+    final StringBuilder r = new StringBuilder();
+    r.append("CREATE TABLE ");
+    r.append(seq.getSequenceName());
+    r.append("(s SERIAL)");
+    return r.toString();
+  }
+
+  @Override
+  public String getNextSequenceValueSql(final String seqname) {
+    return seqname;
+  }
+
+  @Override
+  public long nextLong(final Connection conn, final String seqname)
+      throws OrmException {
+    try {
+      final Statement st = conn.createStatement();
+      try {
+        st.execute("INSERT INTO " + seqname + "(s)VALUES(NULL)");
+        final long r;
+        final ResultSet rs = st.getGeneratedKeys();
+        try {
+          if (!rs.next()) {
+            throw new SQLException("No result row for sequence query");
+          }
+          r = rs.getLong(1);
+        } finally {
+          rs.close();
+        }
+        st.execute("DELETE FROM " + seqname + " WHERE s=" + r);
+        return r;
+      } finally {
+        st.close();
+      }
+    } catch (SQLException e) {
+      throw convertError("sequence", seqname, e);
+    }
+  }
+}
diff --git a/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java b/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
index aad86f0..8fa1cf3 100644
--- a/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
+++ b/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
@@ -21,7 +21,9 @@
 import com.google.gwtorm.schema.SequenceModel;
 
 import java.sql.Connection;
+import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.sql.Types;
 import java.util.HashMap;
 import java.util.Map;
@@ -110,9 +112,38 @@
    */
   public OrmException convertError(final String op, final String entity,
       final SQLException err) {
+    if (err.getCause() == null && err.getNextException() != null) {
+      err.initCause(err.getNextException());
+    }
     return new OrmException(op + " failure on " + entity, err);
   }
 
+  public long nextLong(final Connection conn, final String query)
+      throws OrmException {
+    try {
+      final Statement st = conn.createStatement();
+      try {
+        final ResultSet rs = st.executeQuery(query);
+        try {
+          if (!rs.next()) {
+            throw new SQLException("No result row for sequence query");
+          }
+          final long r = rs.getLong(1);
+          if (rs.next()) {
+            throw new SQLException("Too many results from sequence query");
+          }
+          return r;
+        } finally {
+          rs.close();
+        }
+      } finally {
+        st.close();
+      }
+    } catch (SQLException e) {
+      throw convertError("sequence", query, e);
+    }
+  }
+
   public String getCreateSequenceSql(final SequenceModel seq) {
     final Sequence s = seq.getSequence();
     final StringBuilder r = new StringBuilder();
diff --git a/src/main/java/com/google/gwtorm/schema/sql/SqlTimestampTypeInfo.java b/src/main/java/com/google/gwtorm/schema/sql/SqlTimestampTypeInfo.java
index 9a71ad3..79be0b9 100644
--- a/src/main/java/com/google/gwtorm/schema/sql/SqlTimestampTypeInfo.java
+++ b/src/main/java/com/google/gwtorm/schema/sql/SqlTimestampTypeInfo.java
@@ -34,9 +34,14 @@
     final StringBuilder r = new StringBuilder();
     r.append(dialect.getSqlTypeName(getSqlTypeConstant()));
     if (col.isNotNull()) {
-      r.append(" DEFAULT '1900-01-01 00:00:00'");
+      r.append(" DEFAULT ");
+      r.append(getSqlDefault());
       r.append(" NOT NULL");
     }
     return r.toString();
   }
+
+  public String getSqlDefault() {
+    return "'1900-01-01 00:00:00'";
+  }
 }