Hello!

I would like to submit the attached patch for consideration. The changes 
can also be viewed here: https://github.com/h2database/h2database/pull/451.

The patch adds support for the ENUM data type. The syntax is very similar 
to that of MySQL and Oracle, e.g.

CREATE TABLE card (suit ENUM('clubs', 'hearts', 'diamonds', 'spades'));

The behavior is very similar as well, with some minor differences, which 
could be addressed now or at a later time. This patch does not include 
support for Postgres syntax for defining and using ENUM types.

I wrote the code, it's mine, and I'm contributing it to H2 for distribution 
multiple-licensed under the MPL 2.0, and the EPL 1.0 
(http://h2database.com/html/license.html).

Best,
Max Englander

-- 
You received this message because you are subscribed to the Google Groups "H2 
Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/h2-database.
For more options, visit https://groups.google.com/d/optout.
>From 2b755214fe1f1219287c33173b6fb2dddcdcdc3c Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Sat, 5 Nov 2016 16:48:43 -0400
Subject: [PATCH 01/19] Enum type does not support precision or scale

---
 h2/src/main/org/h2/command/Parser.java             | 15 ++++++-
 h2/src/main/org/h2/expression/ValueExpression.java |  1 +
 h2/src/main/org/h2/table/Column.java               | 25 ++++++++++-
 h2/src/main/org/h2/value/DataType.java             | 32 ++++++++++++-
 h2/src/main/org/h2/value/Value.java                |  9 ++++
 h2/src/main/org/h2/value/ValueEnum.java            | 52 ++++++++++++++++++++++
 6 files changed, 129 insertions(+), 5 deletions(-)
 create mode 100644 h2/src/main/org/h2/value/ValueEnum.java

diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java
index 460539f..365422d 100644
--- a/h2/src/main/org/h2/command/Parser.java
+++ b/h2/src/main/org/h2/command/Parser.java
@@ -4058,6 +4058,7 @@ private Column parseColumnWithType(String columnName) {
         }
         long precision = -1;
         int displaySize = -1;
+        java.util.Set<String> permittedValues = new HashSet<>();
         int scale = -1;
         String comment = null;
         Column templateColumn = null;
@@ -4131,6 +4132,13 @@ private Column parseColumnWithType(String columnName) {
                 }
                 read(")");
             }
+        } else if (dataType.supportsPermittedValues) {
+            if (readIf("(")) {
+                permittedValues.add(readString());
+                while(readIf(","))
+                    readString();
+                read(")");
+            }
         } else if (readIf("(")) {
             // Support for MySQL: INT(11), MEDIUMINT(8) and so on.
             // Just ignore the precision.
@@ -4151,8 +4159,13 @@ private Column parseColumnWithType(String columnName) {
             throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION,
                     Integer.toString(scale), Long.toString(precision));
         }
-        Column column = new Column(columnName, type, precision, scale,
+        Column column;
+        if (permittedValues.isEmpty()) {
+            column = new Column(columnName, type, precision, scale,
                 displaySize);
+        } else {
+            column = new Column(columnName, type, permittedValues);
+        }
         if (templateColumn != null) {
             column.setNullable(templateColumn.isNullable());
             column.setDefaultExpression(session,
diff --git a/h2/src/main/org/h2/expression/ValueExpression.java b/h2/src/main/org/h2/expression/ValueExpression.java
index b8ed4f7..2d46869 100644
--- a/h2/src/main/org/h2/expression/ValueExpression.java
+++ b/h2/src/main/org/h2/expression/ValueExpression.java
@@ -13,6 +13,7 @@
 import org.h2.value.Value;
 import org.h2.value.ValueArray;
 import org.h2.value.ValueBoolean;
+import org.h2.value.ValueEnum;
 import org.h2.value.ValueNull;
 
 /**
diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index bd08a49..579c2ee 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -6,6 +6,7 @@
 package org.h2.table;
 
 import java.sql.ResultSetMetaData;
+import java.util.Set;
 import org.h2.api.ErrorCode;
 import org.h2.command.Parser;
 import org.h2.engine.Constants;
@@ -66,6 +67,7 @@
     private final int type;
     private long precision;
     private int scale;
+    private Set<String> permittedValues;
     private int displaySize;
     private Table table;
     private String name;
@@ -88,11 +90,20 @@
     private boolean primaryKey;
 
     public Column(String name, int type) {
-        this(name, type, -1, -1, -1);
+        this(name, type, -1, -1, -1, null);
+    }
+
+    public Column(String name, int type, Set<String> permittedValues) {
+        this(name, type, -1, -1, -1, permittedValues);
     }
 
     public Column(String name, int type, long precision, int scale,
             int displaySize) {
+        this(name, type, precision, scale, displaySize, null);
+    }
+
+    public Column(String name, int type, long precision, int scale,
+            int displaySize, Set<String> permittedValues) {
         this.name = name;
         this.type = type;
         if (precision == -1 && scale == -1 && displaySize == -1 && type != Value.UNKNOWN) {
@@ -104,6 +115,7 @@ public Column(String name, int type, long precision, int scale,
         this.precision = precision;
         this.scale = scale;
         this.displaySize = displaySize;
+        this.permittedValues = permittedValues;
     }
 
     @Override
@@ -133,7 +145,7 @@ public int hashCode() {
     }
 
     public Column getClone() {
-        Column newColumn = new Column(name, type, precision, scale, displaySize);
+        Column newColumn = new Column(name, type, precision, scale, displaySize, permittedValues);
         newColumn.copy(this);
         return newColumn;
     }
@@ -257,6 +269,14 @@ public void setNullable(boolean b) {
         nullable = b;
     }
 
+    public Set<String> getPermittedValues() {
+        return permittedValues;
+    }
+
+    public void setPermittedValues(Set<String> permittedValues) {
+        this.permittedValues = permittedValues;
+    }
+
     /**
      * Validate the value, convert it if required, and update the sequence value
      * if required. If the value is null, the default value (NULL if no default
@@ -733,6 +753,7 @@ public void copy(Column source) {
         displaySize = source.displaySize;
         name = source.name;
         precision = source.precision;
+        permittedValues = source.permittedValues;
         scale = source.scale;
         // table is not set
         // columnId is not set
diff --git a/h2/src/main/org/h2/value/DataType.java b/h2/src/main/org/h2/value/DataType.java
index 466c5d9..1d4f155 100644
--- a/h2/src/main/org/h2/value/DataType.java
+++ b/h2/src/main/org/h2/value/DataType.java
@@ -141,6 +141,11 @@
     public boolean caseSensitive;
 
     /**
+     * If permitted values are supports.
+     */
+    public boolean supportsPermittedValues;
+
+    /**
      * If the precision parameter is supported.
      */
     public boolean supportsPrecision;
@@ -380,6 +385,11 @@
                 new String[]{"RESULT_SET"},
                 400
         );
+        add(Value.ENUM, Types.OTHER, "Enum",
+                createEnum(),
+                new String[]{"ENUM"},
+                48
+        );
         for (Integer i : TYPES_BY_VALUE_TYPE.keySet()) {
             Value.getOrder(i);
         }
@@ -401,6 +411,7 @@ private static void add(int type, int sqlType, String jdbc,
             dt.params = dataType.params;
             dt.prefix = dataType.prefix;
             dt.suffix = dataType.suffix;
+            dt.supportsPermittedValues = dataType.supportsPermittedValues;
             dt.supportsPrecision = dataType.supportsPrecision;
             dt.supportsScale = dataType.supportsScale;
             dt.defaultPrecision = dataType.defaultPrecision;
@@ -454,6 +465,13 @@ private static DataType createDate(int precision, String prefix, int scale,
         return dataType;
     }
 
+    private static DataType createEnum() {
+        DataType dataType = createString(false);
+        dataType.supportsPermittedValues = true;
+        dataType.supportsPrecision = false;
+        dataType.supportsScale = false;
+        return dataType;
+    }
     private static DataType createString(boolean caseSensitive) {
         DataType dataType = new DataType();
         dataType.prefix = "'";
@@ -664,6 +682,13 @@ public static Value readValue(SessionInterface session, ResultSet rs,
                 v = ValueArray.get(values);
                 break;
             }
+            case Value.ENUM: {
+                Object x = rs.getObject(columnIndex);
+                if (x == null) {
+                    return ValueNull.INSTANCE;
+                }
+                return ValueEnum.get((String)x);
+            }
             case Value.RESULT_SET: {
                 ResultSet x = (ResultSet) rs.getObject(columnIndex);
                 if (x == null) {
@@ -998,6 +1023,9 @@ private static Value convertToValue1(SessionInterface session, Object x,
             return ValueJavaObject.getNoCopy(x, null, session.getDataHandler());
         }
         if (x instanceof String) {
+            if (type == Value.ENUM) {
+                return ValueEnum.get((String) x);
+            }
             return ValueString.get((String) x);
         } else if (x instanceof Value) {
             return (Value) x;
@@ -1145,8 +1173,8 @@ public static boolean isLargeObject(int type) {
      * @return true if the value type is a String type
      */
     public static boolean isStringType(int type) {
-        if (type == Value.STRING || type == Value.STRING_FIXED
-                || type == Value.STRING_IGNORECASE) {
+        if (type == Value.ENUM || type == Value.STRING
+                || type == Value.STRING_FIXED || type == Value.STRING_IGNORECASE) {
             return true;
         }
         return false;
diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java
index 0aabad9..423d1a3 100644
--- a/h2/src/main/org/h2/value/Value.java
+++ b/h2/src/main/org/h2/value/Value.java
@@ -167,6 +167,11 @@
     public static final int TIMESTAMP_TZ = 24;
 
     /**
+     * The value type for ENUM values.
+     */
+    public static final int ENUM = 25;
+
+    /**
      * The number of value types.
      */
     public static final int TYPE_COUNT = TIMESTAMP_TZ;
@@ -321,6 +326,8 @@ static int getOrder(int type) {
             return 50;
         case RESULT_SET:
             return 51;
+        case ENUM:
+            return 52;
         default:
             throw DbException.throwInternalError("type:"+type);
         }
@@ -965,6 +972,8 @@ public Value convertTo(int targetType) {
                 return ValueUuid.get(s);
             case GEOMETRY:
                 return ValueGeometry.get(s);
+            case ENUM:
+                return ValueEnum.get(s);
             default:
                 throw DbException.throwInternalError("type=" + targetType);
             }
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
new file mode 100644
index 0000000..7e03143
--- /dev/null
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
+ * and the EPL 1.0 (http://h2database.com/html/license.html).
+ * Initial Developer: H2 Group
+ */
+package org.h2.value;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.h2.engine.SysProperties;
+import org.h2.util.MathUtils;
+import org.h2.util.StringUtils;
+
+/**
+ * Implementation of the ENUM data type.
+ */
+public class ValueEnum extends ValueString {
+    protected ValueEnum(String value) {
+        super(value);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        return other instanceof ValueEnum
+                && value.equals(((ValueEnum) other).value);
+    }
+
+    @Override
+    protected int compareSecure(Value o, CompareMode mode) {
+        // compatibility: the other object could be another type
+        ValueEnum v = (ValueEnum) o;
+        return mode.compareString(value, v.value, false);
+    }
+
+    @Override
+    public int getType() {
+        return Value.ENUM;
+    }
+
+    /**
+     * Create a new String value of the current class.
+     * This method is meant to be overridden by subclasses.
+     *
+     * @param s the string
+     * @return the value
+     */
+    protected Value getNew(String s) {
+        return ValueEnum.get(s);
+    }
+
+}

>From 4e06655acd6ef8fd72e192f49378542f3bf8f5a7 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Wed, 16 Nov 2016 22:56:43 -0500
Subject: [PATCH 02/19] restrict permitted values for enum columns

---
 h2/src/docsrc/textbase/_messages_en.prop |  1 +
 h2/src/main/org/h2/api/ErrorCode.java    | 13 +++++++++++++
 h2/src/main/org/h2/command/Parser.java   |  2 +-
 h2/src/main/org/h2/res/_messages_en.prop |  1 +
 h2/src/main/org/h2/res/_messages_es.prop |  1 +
 h2/src/main/org/h2/table/Column.java     | 20 ++++++++++++++++++++
 h2/src/main/org/h2/value/Value.java      | 11 +++++++++++
 7 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/h2/src/docsrc/textbase/_messages_en.prop b/h2/src/docsrc/textbase/_messages_en.prop
index 194768f..fbfc85c 100644
--- a/h2/src/docsrc/textbase/_messages_en.prop
+++ b/h2/src/docsrc/textbase/_messages_en.prop
@@ -9,6 +9,7 @@
 22012=Division by zero: {0}
 22018=Data conversion error converting {0}
 22025=Error in LIKE ESCAPE: {0}
+22030=Value not permitted for column {0}: {1}
 23502=NULL not allowed for column {0}
 23503=Referential integrity constraint violation: {0}
 23505=Unique index or primary key violation: {0}
diff --git a/h2/src/main/org/h2/api/ErrorCode.java b/h2/src/main/org/h2/api/ErrorCode.java
index ee4ba29..f3dc26b 100644
--- a/h2/src/main/org/h2/api/ErrorCode.java
+++ b/h2/src/main/org/h2/api/ErrorCode.java
@@ -159,6 +159,19 @@
      */
     public static final int LIKE_ESCAPE_ERROR_1 = 22025;
 
+    /**
+     * The error with code <code>22030</code> is thrown when
+     * an attempt is made to insert or update an ENUM value,
+     * but the target value is not one of the values permitted
+     * by the column.
+     * Example:
+     * <pre>
+     * CREATE TABLE TEST(ID INT, CASE ENUM('sensitive','insensitive'));
+     * INSERT INTO TEST VALUES(1, 'Hello');
+     * </pre>
+     */
+    public static final int VALUE_NOT_PERMITTED = 22030;
+
     // 23: constraint violation
 
     /**
diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java
index 365422d..eb453b4 100644
--- a/h2/src/main/org/h2/command/Parser.java
+++ b/h2/src/main/org/h2/command/Parser.java
@@ -4136,7 +4136,7 @@ private Column parseColumnWithType(String columnName) {
             if (readIf("(")) {
                 permittedValues.add(readString());
                 while(readIf(","))
-                    readString();
+                    permittedValues.add(readString());
                 read(")");
             }
         } else if (readIf("(")) {
diff --git a/h2/src/main/org/h2/res/_messages_en.prop b/h2/src/main/org/h2/res/_messages_en.prop
index 194768f..fbfc85c 100644
--- a/h2/src/main/org/h2/res/_messages_en.prop
+++ b/h2/src/main/org/h2/res/_messages_en.prop
@@ -9,6 +9,7 @@
 22012=Division by zero: {0}
 22018=Data conversion error converting {0}
 22025=Error in LIKE ESCAPE: {0}
+22030=Value not permitted for column {0}: {1}
 23502=NULL not allowed for column {0}
 23503=Referential integrity constraint violation: {0}
 23505=Unique index or primary key violation: {0}
diff --git a/h2/src/main/org/h2/res/_messages_es.prop b/h2/src/main/org/h2/res/_messages_es.prop
index 2960023..b992149 100644
--- a/h2/src/main/org/h2/res/_messages_es.prop
+++ b/h2/src/main/org/h2/res/_messages_es.prop
@@ -9,6 +9,7 @@
 22012=División por cero: {0}
 22018=Conversión de datos fallida, convirtiendo {0}
 22025=Error en LIKE ESCAPE: {0}
+22030=Valor no permitido para la columna {0}: {1}
 23502=La columna {0} no permite valores nulos (NULL)
 23503=Violación de una restricción de Integridad Referencial: {0}
 23505=Violación de indice de Unicidad ó Clave primaria: {0}
diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index 579c2ee..1d0726a 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -6,6 +6,7 @@
 package org.h2.table;
 
 import java.sql.ResultSetMetaData;
+import java.util.Iterator;
 import java.util.Set;
 import org.h2.api.ErrorCode;
 import org.h2.command.Parser;
@@ -356,6 +357,16 @@ public Value validateConvertUpdateSequence(Session session, Value value) {
                         getCreateSQL(), s + " (" + value.getPrecision() + ")");
             }
         }
+        if (permittedValues != null) {
+            if (!value.checkPermitted(permittedValues)) {
+                String s = value.getTraceSQL();
+                if (s.length() > 127) {
+                    s = s.substring(0, 128) + "...";
+                }
+                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
+                        getCreateSQL(), s + " (" + value.getString() + ")");
+            }
+        }
         updateSequenceIfRequired(session, value);
         return value;
     }
@@ -444,6 +455,15 @@ public String getCreateSQL() {
             case Value.DECIMAL:
                 buff.append('(').append(precision).append(", ").append(scale).append(')');
                 break;
+            case Value.ENUM:
+                buff.append('(');
+                Iterator<String> it = permittedValues.iterator();
+                while(it.hasNext()) {
+                    buff.append('\'').append(it.next()).append('\'');
+                    if(it.hasNext()) {
+                        buff.append(',');
+                    }
+                }
             case Value.BYTES:
             case Value.STRING:
             case Value.STRING_IGNORECASE:
diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java
index 423d1a3..993055b 100644
--- a/h2/src/main/org/h2/value/Value.java
+++ b/h2/src/main/org/h2/value/Value.java
@@ -18,6 +18,7 @@
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
+import java.util.Set;
 import org.h2.api.ErrorCode;
 import org.h2.engine.Constants;
 import org.h2.engine.SysProperties;
@@ -1130,6 +1131,16 @@ public void remove() {
     }
 
     /**
+     * Check to see if this value is one of the given permitted values.
+     *
+     * @param permittedValues the permitted values
+     * @return true if this value is one of the permitted values
+     */
+    public boolean checkPermitted(Set<String> permittedValues) {
+        return permittedValues.contains(getString());
+    }
+
+    /**
      * Check if the precision is smaller or equal than the given precision.
      *
      * @param precision the maximum precision

>From ef597352b592913695206e7402d769a2d335763e Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Sun, 12 Feb 2017 20:46:50 -0500
Subject: [PATCH 03/19] Add prepared statement tests for enum data type

---
 .../org/h2/test/jdbc/TestPreparedStatement.java    | 31 ++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
index c1a61cf..947ea2a 100644
--- a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
+++ b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
@@ -57,6 +57,7 @@ public void test() throws Exception {
         testToString(conn);
         testExecuteUpdateCall(conn);
         testPrepareExecute(conn);
+        testEnum(conn);
         testUUID(conn);
         testUUIDAsJavaObject(conn);
         testScopedGeneratedKey(conn);
@@ -443,6 +444,36 @@ private void testArray(Connection conn) throws SQLException {
         assertFalse(rs.next());
     }
 
+    private void testEnum(Connection conn) throws SQLException {
+        Statement stat = conn.createStatement();
+        stat.execute("CREATE TABLE test_enum(size ENUM('small', 'medium', 'large'))");
+
+        String[] badSizes = new String[]{"green", "wool", "discount"};
+        for (int i = 0; i < badSizes.length; i++) {
+            PreparedStatement prep = conn.prepareStatement(
+                    "INSERT INTO test_enum VALUES(?)");
+            prep.setObject(1, badSizes[i]);
+            assertThrows(ErrorCode.VALUE_NOT_PERMITTED, prep).execute();
+        }
+
+        String[] goodSizes = new String[]{"small", "medium", "large"};
+        for (int i = 0; i < goodSizes.length; i++) {
+            PreparedStatement prep = conn.prepareStatement(
+                    "INSERT INTO test_enum VALUES(?)");
+            prep.setObject(1, goodSizes[i]);
+            prep.execute();
+            ResultSet rs = stat.executeQuery("SELECT * FROM test_enum");
+            for (int j = 0; j <= i; j++) {
+                rs.next();
+            }
+            assertEquals(goodSizes[i], rs.getString(1));
+            Object o = rs.getObject(1);
+            assertEquals("java.lang.String", o.getClass().getName());
+        }
+
+        stat.execute("DROP TABLE test_enum");
+    }
+
     private void testUUID(Connection conn) throws SQLException {
         Statement stat = conn.createStatement();
         stat.execute("create table test_uuid(id uuid primary key)");

>From 9015bc1e79aa902842ba4643b3c006e9ac4b2d02 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Sat, 18 Feb 2017 03:54:00 -0500
Subject: [PATCH 04/19] store enums as ints

---
 h2/src/main/org/h2/command/Parser.java | 17 ++++++---------
 h2/src/main/org/h2/table/Column.java   | 39 +++++++++++++++++++++-------------
 h2/src/main/org/h2/value/DataType.java |  8 +++----
 h2/src/main/org/h2/value/Value.java    |  8 +++++--
 4 files changed, 40 insertions(+), 32 deletions(-)

diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java
index 6b436b1..1bba403 100644
--- a/h2/src/main/org/h2/command/Parser.java
+++ b/h2/src/main/org/h2/command/Parser.java
@@ -4122,7 +4122,7 @@ private Column parseColumnWithType(String columnName) {
         }
         long precision = -1;
         int displaySize = -1;
-        java.util.Set<String> permittedValues = new HashSet<>();
+        java.util.List<String> enumerators = new ArrayList<String>();
         int scale = -1;
         String comment = null;
         Column templateColumn = null;
@@ -4196,11 +4196,11 @@ private Column parseColumnWithType(String columnName) {
                 }
                 read(")");
             }
-        } else if (dataType.supportsPermittedValues) {
+        } else if (dataType.enumerated) {
             if (readIf("(")) {
-                permittedValues.add(readString());
+                enumerators.add(readString());
                 while(readIf(","))
-                    permittedValues.add(readString());
+                    enumerators.add(readString());
                 read(")");
             }
         } else if (readIf("(")) {
@@ -4223,13 +4223,8 @@ private Column parseColumnWithType(String columnName) {
             throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION,
                     Integer.toString(scale), Long.toString(precision));
         }
-        Column column;
-        if (permittedValues.isEmpty()) {
-            column = new Column(columnName, type, precision, scale,
-                displaySize);
-        } else {
-            column = new Column(columnName, type, permittedValues);
-        }
+        Column column = new Column(columnName, type, precision, scale,
+            displaySize, enumerators);
         if (templateColumn != null) {
             column.setNullable(templateColumn.isNullable());
             column.setDefaultExpression(session,
diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index 34d5c35..be13f9e 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -7,7 +7,7 @@
 
 import java.sql.ResultSetMetaData;
 import java.util.Iterator;
-import java.util.Set;
+import java.util.List;
 import org.h2.api.ErrorCode;
 import org.h2.command.Parser;
 import org.h2.engine.Constants;
@@ -68,7 +68,7 @@
     private final int type;
     private long precision;
     private int scale;
-    private Set<String> permittedValues;
+    private List<String> enumerators;
     private int displaySize;
     private Table table;
     private String name;
@@ -94,8 +94,8 @@ public Column(String name, int type) {
         this(name, type, -1, -1, -1, null);
     }
 
-    public Column(String name, int type, Set<String> permittedValues) {
-        this(name, type, -1, -1, -1, permittedValues);
+    public Column(String name, int type, List<String> enumerators) {
+        this(name, type, -1, -1, -1, enumerators);
     }
 
     public Column(String name, int type, long precision, int scale,
@@ -104,7 +104,7 @@ public Column(String name, int type, long precision, int scale,
     }
 
     public Column(String name, int type, long precision, int scale,
-            int displaySize, Set<String> permittedValues) {
+            int displaySize, List<String> enumerators) {
         this.name = name;
         this.type = type;
         if (precision == -1 && scale == -1 && displaySize == -1 && type != Value.UNKNOWN) {
@@ -116,7 +116,7 @@ public Column(String name, int type, long precision, int scale,
         this.precision = precision;
         this.scale = scale;
         this.displaySize = displaySize;
-        this.permittedValues = permittedValues;
+        this.enumerators = enumerators;
     }
 
     @Override
@@ -146,7 +146,7 @@ public int hashCode() {
     }
 
     public Column getClone() {
-        Column newColumn = new Column(name, type, precision, scale, displaySize, permittedValues);
+        Column newColumn = new Column(name, type, precision, scale, displaySize, enumerators);
         newColumn.copy(this);
         return newColumn;
     }
@@ -270,12 +270,12 @@ public void setNullable(boolean b) {
         nullable = b;
     }
 
-    public Set<String> getPermittedValues() {
-        return permittedValues;
+    public List<String> getEnumerators() {
+        return enumerators;
     }
 
-    public void setPermittedValues(Set<String> permittedValues) {
-        this.permittedValues = permittedValues;
+    public void setEnumerators(List<String> enumerators) {
+        this.enumerators = enumerators;
     }
 
     /**
@@ -357,14 +357,23 @@ public Value validateConvertUpdateSequence(Session session, Value value) {
                         getCreateSQL(), s + " (" + value.getPrecision() + ")");
             }
         }
-        if (permittedValues != null) {
-            if (!value.checkPermitted(permittedValues)) {
+        if (!enumerators.isEmpty()) {
+            int index;
+            if (DataType.isStringType(value.getType())) {
+                index = enumerators.indexOf(value.getString());
+            } else {
+                index = value.getInt() < enumerators.size() ? value.getInt() : -1;
+            }
+
+            if (index == -1) {
                 String s = value.getTraceSQL();
                 if (s.length() > 127) {
                     s = s.substring(0, 128) + "...";
                 }
                 throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
                         getCreateSQL(), s + " (" + value.getString() + ")");
+            } else {
+                value = ValueInt.get(index);
             }
         }
         updateSequenceIfRequired(session, value);
@@ -458,7 +467,7 @@ public String getCreateSQL() {
                 break;
             case Value.ENUM:
                 buff.append('(');
-                Iterator<String> it = permittedValues.iterator();
+                Iterator<String> it = enumerators.iterator();
                 while(it.hasNext()) {
                     buff.append('\'').append(it.next()).append('\'');
                     if(it.hasNext()) {
@@ -774,7 +783,7 @@ public void copy(Column source) {
         displaySize = source.displaySize;
         name = source.name;
         precision = source.precision;
-        permittedValues = source.permittedValues;
+        enumerators = source.enumerators;
         scale = source.scale;
         // table is not set
         // columnId is not set
diff --git a/h2/src/main/org/h2/value/DataType.java b/h2/src/main/org/h2/value/DataType.java
index a1cf882..1b8fcc8 100644
--- a/h2/src/main/org/h2/value/DataType.java
+++ b/h2/src/main/org/h2/value/DataType.java
@@ -143,7 +143,7 @@
     /**
      * If permitted values are supports.
      */
-    public boolean supportsPermittedValues;
+    public boolean enumerated;
 
     /**
      * If the precision parameter is supported.
@@ -416,7 +416,7 @@ private static void add(int type, int sqlType, String jdbc,
             dt.params = dataType.params;
             dt.prefix = dataType.prefix;
             dt.suffix = dataType.suffix;
-            dt.supportsPermittedValues = dataType.supportsPermittedValues;
+            dt.enumerated = dataType.enumerated;
             dt.supportsPrecision = dataType.supportsPrecision;
             dt.supportsScale = dataType.supportsScale;
             dt.defaultPrecision = dataType.defaultPrecision;
@@ -472,7 +472,7 @@ private static DataType createDate(int precision, String prefix, int scale,
 
     private static DataType createEnum() {
         DataType dataType = createString(false);
-        dataType.supportsPermittedValues = true;
+        dataType.enumerated = true;
         dataType.supportsPrecision = false;
         dataType.supportsScale = false;
         return dataType;
@@ -1183,7 +1183,7 @@ public static boolean isLargeObject(int type) {
      * @return true if the value type is a String type
      */
     public static boolean isStringType(int type) {
-        if (type == Value.ENUM || type == Value.STRING
+        if (type == Value.STRING
                 || type == Value.STRING_FIXED || type == Value.STRING_IGNORECASE) {
             return true;
         }
diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java
index 36eda57..be64e11 100644
--- a/h2/src/main/org/h2/value/Value.java
+++ b/h2/src/main/org/h2/value/Value.java
@@ -852,6 +852,11 @@ public Value convertTo(int targetType) {
                 }
                 break;
             }
+            case ENUM:
+                switch (getType()) {
+                    case INT:
+                        return this;
+                }
             case BLOB: {
                 switch (getType()) {
                 case BYTES:
@@ -944,6 +949,7 @@ public Value convertTo(int targetType) {
             case JAVA_OBJECT:
                 return ValueJavaObject.getNoCopy(null,
                         StringUtils.convertHexToBytes(s.trim()), getDataHandler());
+            case ENUM:
             case STRING:
                 return ValueString.get(s);
             case STRING_IGNORECASE:
@@ -973,8 +979,6 @@ public Value convertTo(int targetType) {
                 return ValueUuid.get(s);
             case GEOMETRY:
                 return ValueGeometry.get(s);
-            case ENUM:
-                return ValueEnum.get(s);
             default:
                 throw DbException.throwInternalError("type=" + targetType);
             }

>From 7f4b8391e228dc2c722eaa0e1a212819392df377 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Sat, 18 Feb 2017 20:06:52 -0500
Subject: [PATCH 05/19] enum-support: drop ValueEnum.java (for now); fix
 originalSQL when parsing enum columns

---
 h2/src/main/org/h2/command/Parser.java             | 14 ++++--
 h2/src/main/org/h2/expression/ValueExpression.java |  1 -
 h2/src/main/org/h2/value/DataType.java             | 12 ++---
 h2/src/main/org/h2/value/Value.java                |  2 +-
 h2/src/main/org/h2/value/ValueEnum.java            | 52 ----------------------
 5 files changed, 16 insertions(+), 65 deletions(-)
 delete mode 100644 h2/src/main/org/h2/value/ValueEnum.java

diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java
index 1bba403..c045cdd 100644
--- a/h2/src/main/org/h2/command/Parser.java
+++ b/h2/src/main/org/h2/command/Parser.java
@@ -4198,10 +4198,18 @@ private Column parseColumnWithType(String columnName) {
             }
         } else if (dataType.enumerated) {
             if (readIf("(")) {
-                enumerators.add(readString());
-                while(readIf(","))
-                    enumerators.add(readString());
+                original += '(';
+                String enumerator0 = readString();
+                enumerators.add(enumerator0);
+                original += "'" + enumerator0 + "'";
+                while(readIf(",")) {
+                    original += ',';
+                    String enumeratorN = readString();
+                    original += "'" + enumeratorN + "'";
+                    enumerators.add(enumeratorN);
+                }
                 read(")");
+                original += ')';
             }
         } else if (readIf("(")) {
             // Support for MySQL: INT(11), MEDIUMINT(8) and so on.
diff --git a/h2/src/main/org/h2/expression/ValueExpression.java b/h2/src/main/org/h2/expression/ValueExpression.java
index 2d46869..b8ed4f7 100644
--- a/h2/src/main/org/h2/expression/ValueExpression.java
+++ b/h2/src/main/org/h2/expression/ValueExpression.java
@@ -13,7 +13,6 @@
 import org.h2.value.Value;
 import org.h2.value.ValueArray;
 import org.h2.value.ValueBoolean;
-import org.h2.value.ValueEnum;
 import org.h2.value.ValueNull;
 
 /**
diff --git a/h2/src/main/org/h2/value/DataType.java b/h2/src/main/org/h2/value/DataType.java
index 1b8fcc8..cbfd134 100644
--- a/h2/src/main/org/h2/value/DataType.java
+++ b/h2/src/main/org/h2/value/DataType.java
@@ -686,11 +686,10 @@ public static Value readValue(SessionInterface session, ResultSet rs,
                 break;
             }
             case Value.ENUM: {
-                Object x = rs.getObject(columnIndex);
-                if (x == null) {
-                    return ValueNull.INSTANCE;
-                }
-                return ValueEnum.get((String)x);
+                int value = rs.getInt(columnIndex);
+                v = rs.wasNull() ? (Value) ValueNull.INSTANCE :
+                    ValueInt.get(value);
+                break;
             }
             case Value.RESULT_SET: {
                 ResultSet x = (ResultSet) rs.getObject(columnIndex);
@@ -1026,9 +1025,6 @@ private static Value convertToValue1(SessionInterface session, Object x,
             return ValueJavaObject.getNoCopy(x, null, session.getDataHandler());
         }
         if (x instanceof String) {
-            if (type == Value.ENUM) {
-                return ValueEnum.get((String) x);
-            }
             return ValueString.get((String) x);
         } else if (x instanceof Value) {
             return (Value) x;
diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java
index be64e11..b47c129 100644
--- a/h2/src/main/org/h2/value/Value.java
+++ b/h2/src/main/org/h2/value/Value.java
@@ -175,7 +175,7 @@
     /**
      * The number of value types.
      */
-    public static final int TYPE_COUNT = TIMESTAMP_TZ;
+    public static final int TYPE_COUNT = ENUM;
 
     private static SoftReference<Value[]> softCache =
             new SoftReference<Value[]>(null);
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
deleted file mode 100644
index 7e03143..0000000
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
- * and the EPL 1.0 (http://h2database.com/html/license.html).
- * Initial Developer: H2 Group
- */
-package org.h2.value;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-import org.h2.engine.SysProperties;
-import org.h2.util.MathUtils;
-import org.h2.util.StringUtils;
-
-/**
- * Implementation of the ENUM data type.
- */
-public class ValueEnum extends ValueString {
-    protected ValueEnum(String value) {
-        super(value);
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        return other instanceof ValueEnum
-                && value.equals(((ValueEnum) other).value);
-    }
-
-    @Override
-    protected int compareSecure(Value o, CompareMode mode) {
-        // compatibility: the other object could be another type
-        ValueEnum v = (ValueEnum) o;
-        return mode.compareString(value, v.value, false);
-    }
-
-    @Override
-    public int getType() {
-        return Value.ENUM;
-    }
-
-    /**
-     * Create a new String value of the current class.
-     * This method is meant to be overridden by subclasses.
-     *
-     * @param s the string
-     * @return the value
-     */
-    protected Value getNew(String s) {
-        return ValueEnum.get(s);
-    }
-
-}

>From d0fe846bd17ff88b17a3669a0af6adbd6162bdef Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Wed, 15 Mar 2017 02:39:18 +0000
Subject: [PATCH 06/19] display enum labels while maintaining sort-on-int-value

---
 .../main/org/h2/expression/ExpressionColumn.java   |   6 +
 h2/src/main/org/h2/server/TcpServerThread.java     |   1 +
 h2/src/main/org/h2/table/TableFilter.java          |   1 +
 h2/src/main/org/h2/value/ValueEnum.java            | 123 +++++++++++++++++++++
 4 files changed, 131 insertions(+)
 create mode 100644 h2/src/main/org/h2/value/ValueEnum.java

diff --git a/h2/src/main/org/h2/expression/ExpressionColumn.java b/h2/src/main/org/h2/expression/ExpressionColumn.java
index 6a886f9..f80bc55 100644
--- a/h2/src/main/org/h2/expression/ExpressionColumn.java
+++ b/h2/src/main/org/h2/expression/ExpressionColumn.java
@@ -22,6 +22,7 @@
 import org.h2.table.TableFilter;
 import org.h2.value.Value;
 import org.h2.value.ValueBoolean;
+import org.h2.value.ValueEnum;
 
 /**
  * A expression that represents a column of a table or view.
@@ -187,6 +188,11 @@ public Value getValue(Session session) {
             columnResolver.getValue(column);
             throw DbException.get(ErrorCode.MUST_GROUP_BY_COLUMN_1, getSQL());
         }
+        if (column.getEnumerators() != null) {
+            final int ordinal = value.getInt();
+            return new ValueEnum(column.getEnumerators()
+                    .toArray(new String[column.getEnumerators().size()]), ordinal);
+        }
         return value;
     }
 
diff --git a/h2/src/main/org/h2/server/TcpServerThread.java b/h2/src/main/org/h2/server/TcpServerThread.java
index 11c0736..a03d666 100644
--- a/h2/src/main/org/h2/server/TcpServerThread.java
+++ b/h2/src/main/org/h2/server/TcpServerThread.java
@@ -38,6 +38,7 @@
 import org.h2.value.Transfer;
 import org.h2.value.Value;
 import org.h2.value.ValueLobDb;
+import org.h2.value.ValueString;
 
 /**
  * One server thread is opened per client connection.
diff --git a/h2/src/main/org/h2/table/TableFilter.java b/h2/src/main/org/h2/table/TableFilter.java
index d199deb..07c5405 100644
--- a/h2/src/main/org/h2/table/TableFilter.java
+++ b/h2/src/main/org/h2/table/TableFilter.java
@@ -32,6 +32,7 @@
 import org.h2.util.StringUtils;
 import org.h2.value.Value;
 import org.h2.value.ValueLong;
+import org.h2.value.ValueString;
 import org.h2.value.ValueNull;
 
 /**
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
new file mode 100644
index 0000000..afd63c9
--- /dev/null
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -0,0 +1,123 @@
+package org.h2.value;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.h2.util.MathUtils;
+
+public class ValueEnum extends Value {
+    public static final int PRECISION = 10;
+    public static final int DISPLAY_SIZE = 11;
+
+    private final String[] labels;
+    private final String label;
+    private final int ordinal;
+
+    public ValueEnum(final String[] labels, final int ordinal) {
+        this.label = labels[ordinal];
+        this.labels = labels;
+        this.ordinal = ordinal;
+    }
+
+    @Override 
+    public Value add(final Value v) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    @Override
+    protected int compareSecure(final Value o, final CompareMode mode) {
+        final ValueEnum v = (ValueEnum) o;
+        return MathUtils.compareInt(ordinal(), v.ordinal());
+    }
+
+    @Override
+    public Value divide(final Value v) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof ValueEnum &&
+            ordinal() == ((ValueEnum) other).ordinal();
+    }
+
+    @Override
+    public int getDisplaySize() {
+        return DISPLAY_SIZE;
+    }
+
+    @Override
+    public int getInt() {
+        return ordinal;
+    }
+
+    @Override
+    public long getLong() {
+        return ordinal;
+    }
+
+    @Override
+    public Object getObject() {
+        return ordinal;
+    }
+
+    @Override
+    public long getPrecision() {
+        return PRECISION;
+    }
+
+    @Override
+    public int getSignum() {
+        return Integer.signum(ordinal);
+    }
+
+    @Override
+    public String getSQL() {
+        return getString();
+    }
+
+    @Override
+    public String getString() {
+        return label;
+    }
+
+    @Override
+    public int hashCode() {
+        return ordinal;
+    }
+
+    @Override
+    public int getType() {
+        return Value.INT;
+    }
+
+    @Override
+    public Value modulus(final Value v) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    @Override
+    public Value multiply(final Value v) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    @Override
+    public Value negate() {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    protected int ordinal() {
+        return ordinal;
+    }
+
+    @Override
+    public void set(final PreparedStatement prep, final int parameterIndex)
+            throws SQLException {
+         prep.setInt(parameterIndex, ordinal);
+    }
+
+    @Override
+    public Value subtract(final Value v) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+}

>From eb39694549d77a69029f714ec491486b56b0fdde Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Thu, 16 Mar 2017 05:36:21 +0000
Subject: [PATCH 07/19] enum-support: handle enum comparisons with ints and
 strings

---
 h2/src/main/org/h2/command/Parser.java             |   6 +-
 .../main/org/h2/expression/ExpressionColumn.java   |   4 +-
 h2/src/main/org/h2/mvstore/db/ValueDataType.java   |   1 +
 h2/src/main/org/h2/table/Column.java               |  38 ++---
 h2/src/main/org/h2/value/Value.java                |   6 +-
 h2/src/main/org/h2/value/ValueEnum.java            | 181 +++++++++++++++++----
 6 files changed, 179 insertions(+), 57 deletions(-)

diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java
index c045cdd..b35b548 100644
--- a/h2/src/main/org/h2/command/Parser.java
+++ b/h2/src/main/org/h2/command/Parser.java
@@ -4200,13 +4200,13 @@ private Column parseColumnWithType(String columnName) {
             if (readIf("(")) {
                 original += '(';
                 String enumerator0 = readString();
-                enumerators.add(enumerator0);
+                enumerators.add(enumerator0.toLowerCase().trim());
                 original += "'" + enumerator0 + "'";
                 while(readIf(",")) {
                     original += ',';
                     String enumeratorN = readString();
                     original += "'" + enumeratorN + "'";
-                    enumerators.add(enumeratorN);
+                    enumerators.add(enumeratorN.toLowerCase().trim());
                 }
                 read(")");
                 original += ')';
@@ -4232,7 +4232,7 @@ private Column parseColumnWithType(String columnName) {
                     Integer.toString(scale), Long.toString(precision));
         }
         Column column = new Column(columnName, type, precision, scale,
-            displaySize, enumerators);
+            displaySize, enumerators.toArray(new String[enumerators.size()]));
         if (templateColumn != null) {
             column.setNullable(templateColumn.isNullable());
             column.setDefaultExpression(session,
diff --git a/h2/src/main/org/h2/expression/ExpressionColumn.java b/h2/src/main/org/h2/expression/ExpressionColumn.java
index f80bc55..221ae10 100644
--- a/h2/src/main/org/h2/expression/ExpressionColumn.java
+++ b/h2/src/main/org/h2/expression/ExpressionColumn.java
@@ -189,9 +189,7 @@ public Value getValue(Session session) {
             throw DbException.get(ErrorCode.MUST_GROUP_BY_COLUMN_1, getSQL());
         }
         if (column.getEnumerators() != null) {
-            final int ordinal = value.getInt();
-            return new ValueEnum(column.getEnumerators()
-                    .toArray(new String[column.getEnumerators().size()]), ordinal);
+            return ValueEnum.get(column.getEnumerators(), value);
         }
         return value;
     }
diff --git a/h2/src/main/org/h2/mvstore/db/ValueDataType.java b/h2/src/main/org/h2/mvstore/db/ValueDataType.java
index fa7a10b..d634104 100644
--- a/h2/src/main/org/h2/mvstore/db/ValueDataType.java
+++ b/h2/src/main/org/h2/mvstore/db/ValueDataType.java
@@ -199,6 +199,7 @@ private void writeValue(WriteBuffer buff, Value v) {
         case Value.SHORT:
             buff.put((byte) type).putShort(v.getShort());
             break;
+        case Value.ENUM:
         case Value.INT: {
             int x = v.getInt();
             if (x < 0) {
diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index be13f9e..4a0f13f 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -7,7 +7,6 @@
 
 import java.sql.ResultSetMetaData;
 import java.util.Iterator;
-import java.util.List;
 import org.h2.api.ErrorCode;
 import org.h2.command.Parser;
 import org.h2.engine.Constants;
@@ -28,6 +27,7 @@
 import org.h2.value.DataType;
 import org.h2.value.Value;
 import org.h2.value.ValueDate;
+import org.h2.value.ValueEnum;
 import org.h2.value.ValueInt;
 import org.h2.value.ValueLong;
 import org.h2.value.ValueNull;
@@ -68,7 +68,7 @@
     private final int type;
     private long precision;
     private int scale;
-    private List<String> enumerators;
+    private String[] enumerators;
     private int displaySize;
     private Table table;
     private String name;
@@ -94,7 +94,7 @@ public Column(String name, int type) {
         this(name, type, -1, -1, -1, null);
     }
 
-    public Column(String name, int type, List<String> enumerators) {
+    public Column(String name, int type, String[] enumerators) {
         this(name, type, -1, -1, -1, enumerators);
     }
 
@@ -104,7 +104,7 @@ public Column(String name, int type, long precision, int scale,
     }
 
     public Column(String name, int type, long precision, int scale,
-            int displaySize, List<String> enumerators) {
+            int displaySize, String[] enumerators) {
         this.name = name;
         this.type = type;
         if (precision == -1 && scale == -1 && displaySize == -1 && type != Value.UNKNOWN) {
@@ -145,6 +145,10 @@ public int hashCode() {
         return table.getId() ^ name.hashCode();
     }
 
+    public boolean isEnumerated() {
+        return enumerators != null && enumerators.length > 0;
+    }
+
     public Column getClone() {
         Column newColumn = new Column(name, type, precision, scale, displaySize, enumerators);
         newColumn.copy(this);
@@ -270,11 +274,11 @@ public void setNullable(boolean b) {
         nullable = b;
     }
 
-    public List<String> getEnumerators() {
+    public String[] getEnumerators() {
         return enumerators;
     }
 
-    public void setEnumerators(List<String> enumerators) {
+    public void setEnumerators(String[] enumerators) {
         this.enumerators = enumerators;
     }
 
@@ -357,24 +361,17 @@ public Value validateConvertUpdateSequence(Session session, Value value) {
                         getCreateSQL(), s + " (" + value.getPrecision() + ")");
             }
         }
-        if (!enumerators.isEmpty()) {
-            int index;
-            if (DataType.isStringType(value.getType())) {
-                index = enumerators.indexOf(value.getString());
-            } else {
-                index = value.getInt() < enumerators.size() ? value.getInt() : -1;
-            }
-
-            if (index == -1) {
+        if (isEnumerated()) {
+            if (!ValueEnum.isValid(enumerators, value)) {
                 String s = value.getTraceSQL();
                 if (s.length() > 127) {
                     s = s.substring(0, 128) + "...";
                 }
                 throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
                         getCreateSQL(), s + " (" + value.getString() + ")");
-            } else {
-                value = ValueInt.get(index);
             }
+
+            value = ValueEnum.get(enumerators, value);
         }
         updateSequenceIfRequired(session, value);
         return value;
@@ -467,10 +464,9 @@ public String getCreateSQL() {
                 break;
             case Value.ENUM:
                 buff.append('(');
-                Iterator<String> it = enumerators.iterator();
-                while(it.hasNext()) {
-                    buff.append('\'').append(it.next()).append('\'');
-                    if(it.hasNext()) {
+                for (int i = 0; i < enumerators.length; i++) {
+                    buff.append('\'').append(enumerators[i]).append('\'');
+                    if(i < enumerators.length - 1) {
                         buff.append(',');
                     }
                 }
diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java
index b47c129..e98a689 100644
--- a/h2/src/main/org/h2/value/Value.java
+++ b/h2/src/main/org/h2/value/Value.java
@@ -852,11 +852,13 @@ public Value convertTo(int targetType) {
                 }
                 break;
             }
-            case ENUM:
+            case ENUM: {
                 switch (getType()) {
                     case INT:
+                    case STRING:
                         return this;
                 }
+            }
             case BLOB: {
                 switch (getType()) {
                 case BYTES:
@@ -984,7 +986,7 @@ public Value convertTo(int targetType) {
             }
         } catch (NumberFormatException e) {
             throw DbException.get(
-                    ErrorCode.DATA_CONVERSION_ERROR_1, e, getString());
+                    ErrorCode.DATA_CONVERSION_ERROR_1, e, getString() + " type=" + getType());
         }
     }
 
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index afd63c9..a182157 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -3,36 +3,91 @@
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 
+import org.h2.api.ErrorCode;
+import org.h2.message.DbException;
 import org.h2.util.MathUtils;
+import org.h2.value.DataType;
 
 public class ValueEnum extends Value {
     public static final int PRECISION = 10;
     public static final int DISPLAY_SIZE = 11;
 
-    private final String[] labels;
+    private static enum Validation {
+        DUPLICATE,
+        EMPTY,
+        INVALID,
+        VALID
+    }
+
+    private final String[] enumerators;
     private final String label;
     private final int ordinal;
 
-    public ValueEnum(final String[] labels, final int ordinal) {
-        this.label = labels[ordinal];
-        this.labels = labels;
+    private ValueEnum(final String[] enumerators, final int ordinal) {
+        this.label = enumerators[ordinal];
+        this.enumerators = enumerators;
         this.ordinal = ordinal;
     }
 
-    @Override 
-    public Value add(final Value v) {
-        throw new UnsupportedOperationException("Not yet implemented");
+    private static final void check(final String[] enumerators) {
+        switch (validate(enumerators)) {
+            case VALID:
+                return;
+            case EMPTY:
+                throw DbException.get(ErrorCode.INVALID_VALUE_2,
+                        "Empty enum is not allowed");
+            case DUPLICATE:
+                throw DbException.get(ErrorCode.INVALID_VALUE_2,
+                        "Enum with duplicate enumerator is not allowed");
+            default:
+                throw DbException.get(ErrorCode.INVALID_VALUE_2,
+                        "Invalid enumerator list for enum");
+        }
     }
 
-    @Override
-    protected int compareSecure(final Value o, final CompareMode mode) {
-        final ValueEnum v = (ValueEnum) o;
-        return MathUtils.compareInt(ordinal(), v.ordinal());
+    private static final void check(final String[] enumerators, final String label) {
+        check(enumerators);
+
+        switch (validate(enumerators, label)) {
+            case VALID:
+                return;
+            case EMPTY:
+                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
+                        "Enumerator label may not be empty");
+            default:
+                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
+                        "Enumerator label is invalid");
+        }
+    }
+
+    private static final void check(final String[] enumerators, final int ordinal) {
+        check(enumerators);
+
+        switch (validate(enumerators, ordinal)) {
+            case VALID:
+                return;
+            default:
+                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
+                        "Provided enumerator label does not match any member of enum");
+        }
+    }
+
+    private static final void check(final String[] enumerators, final Value value) {
+        check(enumerators);
+
+        switch (validate(enumerators, value)) {
+            case VALID:
+                return;
+            default:
+                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
+                        "Provided value is does not match any enumerators");
+        }
     }
 
     @Override
-    public Value divide(final Value v) {
-        throw new UnsupportedOperationException("Not yet implemented");
+    protected int compareSecure(final Value o, final CompareMode mode) {
+        final ValueEnum v = ValueEnum.get(enumerators, o);
+        return MathUtils.compareInt(ordinal(), v.ordinal());
     }
 
     @Override
@@ -41,6 +96,32 @@ public boolean equals(final Object other) {
             ordinal() == ((ValueEnum) other).ordinal();
     }
 
+    public static ValueEnum get(final String[] enumerators, final String label) {
+        check(enumerators, label);
+
+        for (int i = 0; i < enumerators.length; i++) {
+            if (label.equals(enumerators[i]))
+                return new ValueEnum(enumerators, i);
+        }
+
+        throw DbException.get(ErrorCode.GENERAL_ERROR_1, "Unexpected error");
+    }
+
+    public static ValueEnum get(final String[] enumerators, final int ordinal) {
+        check(enumerators, ordinal);
+        return new ValueEnum(enumerators, ordinal);
+    }
+
+    public static ValueEnum get(final String[] enumerators, final Value value) {
+        check(enumerators, value);
+
+        if (DataType.isStringType(value.getType())) {
+            return get(enumerators, value.getString());
+        } else {
+            return get(enumerators, value.getInt());
+        }
+    }
+
     @Override
     public int getDisplaySize() {
         return DISPLAY_SIZE;
@@ -83,27 +164,24 @@ public String getString() {
 
     @Override
     public int hashCode() {
-        return ordinal;
+        return enumerators.hashCode() + ordinal;
     }
 
-    @Override
-    public int getType() {
-        return Value.INT;
+    public static boolean isValid(final String enumerators[], final String label) {
+        return validate(enumerators, label).equals(Validation.VALID);
     }
 
-    @Override
-    public Value modulus(final Value v) {
-        throw new UnsupportedOperationException("Not yet implemented");
+    public static boolean isValid(final String enumerators[], final int ordinal) {
+        return validate(enumerators, ordinal).equals(Validation.VALID);
     }
 
-    @Override
-    public Value multiply(final Value v) {
-        throw new UnsupportedOperationException("Not yet implemented");
+    public static boolean isValid(final String enumerators[], final Value value) {
+        return validate(enumerators, value).equals(Validation.VALID);
     }
 
     @Override
-    public Value negate() {
-        throw new UnsupportedOperationException("Not yet implemented");
+    public int getType() {
+        return Value.ENUM;
     }
 
     protected int ordinal() {
@@ -116,8 +194,55 @@ public void set(final PreparedStatement prep, final int parameterIndex)
          prep.setInt(parameterIndex, ordinal);
     }
 
-    @Override
-    public Value subtract(final Value v) {
-        throw new UnsupportedOperationException("Not yet implemented");
+    private static Validation validate(final String[] enumerators, final String label) {
+        check(enumerators);
+
+        if (label == null || label.trim().length() == 0) {
+            return Validation.EMPTY;
+        }
+
+        for (int i = 0; i < enumerators.length; i++) {
+            if (label.equals(enumerators[i])) {
+                return Validation.VALID;
+            }
+        }
+
+        return Validation.INVALID;
+    }
+
+    private static Validation validate(final String[] enumerators) {
+        for (int i = 0; i < enumerators.length; i++) {
+            if (enumerators[i] == null || enumerators[i].equals("")) {
+                return Validation.EMPTY;
+            }
+
+            if (i < enumerators.length - 1) {
+                for (int j = i + 1; j < enumerators.length; j++) {
+                    if (enumerators[i].equals(enumerators[j])) {
+                        return Validation.DUPLICATE;
+                    }
+                }
+            }
+        }
+
+        return Validation.VALID;
+    }
+
+    private static Validation validate(final String[] enumerators, final int ordinal) {
+        check(enumerators);
+
+        if (ordinal < 0 || ordinal >= enumerators.length) {
+            return Validation.INVALID;
+        }
+
+        return Validation.VALID;
+    }
+
+    private static Validation validate(final String[] enumerators, final Value value) {
+        if (DataType.isStringType(value.getType())) {
+            return validate(enumerators, value.getString());
+        } else {
+            return validate(enumerators, value.getInt());
+        }
     }
 }

>From 62e880b1168329850aeba151edac961900fc3acd Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Wed, 22 Mar 2017 03:27:53 +0000
Subject: [PATCH 08/19] enum-support: create table (with enum column) from
 select statement

---
 h2/src/main/org/h2/command/ddl/CreateTable.java | 14 +++++++++++++-
 h2/src/main/org/h2/table/Column.java            |  5 +++--
 h2/src/main/org/h2/value/ValueEnum.java         |  2 +-
 3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/h2/src/main/org/h2/command/ddl/CreateTable.java b/h2/src/main/org/h2/command/ddl/CreateTable.java
index e7aeab8..c0dcb76 100644
--- a/h2/src/main/org/h2/command/ddl/CreateTable.java
+++ b/h2/src/main/org/h2/command/ddl/CreateTable.java
@@ -16,6 +16,7 @@
 import org.h2.engine.DbObject;
 import org.h2.engine.Session;
 import org.h2.expression.Expression;
+import org.h2.expression.ExpressionColumn;
 import org.h2.message.DbException;
 import org.h2.schema.Schema;
 import org.h2.schema.Sequence;
@@ -248,7 +249,18 @@ private void generateColumnsFromQuery() {
             if (scale > precision) {
                 precision = scale;
             }
-            Column col = new Column(name, type, precision, scale, displaySize);
+            String[] enumerators = null;
+            if (dt.enumerated) {
+                /**
+                 * Only columns of tables may be enumerated.
+                 */
+                if(!(expr instanceof ExpressionColumn)) {
+                    throw DbException.get(ErrorCode.GENERAL_ERROR_1,
+                            "Unable to resolve enumerators of expression");
+                }
+                enumerators = ((ExpressionColumn)expr).getColumn().getEnumerators();
+            }
+            Column col = new Column(name, type, precision, scale, displaySize, enumerators);
             addColumn(col);
         }
     }
diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index 4a0f13f..1cc4808 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -6,7 +6,7 @@
 package org.h2.table;
 
 import java.sql.ResultSetMetaData;
-import java.util.Iterator;
+import java.util.Arrays;
 import org.h2.api.ErrorCode;
 import org.h2.command.Parser;
 import org.h2.engine.Constants;
@@ -470,6 +470,7 @@ public String getCreateSQL() {
                         buff.append(',');
                     }
                 }
+                buff.append(')');
             case Value.BYTES:
             case Value.STRING:
             case Value.STRING_IGNORECASE:
@@ -779,7 +780,7 @@ public void copy(Column source) {
         displaySize = source.displaySize;
         name = source.name;
         precision = source.precision;
-        enumerators = source.enumerators;
+        enumerators = Arrays.copyOf(source.enumerators, source.enumerators.length);
         scale = source.scale;
         // table is not set
         // columnId is not set
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index a182157..e5ecf67 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -80,7 +80,7 @@ private static final void check(final String[] enumerators, final Value value) {
                 return;
             default:
                 throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
-                        "Provided value is does not match any enumerators");
+                        "Provided value does not match any enumerators", value.toString());
         }
     }
 

>From 9cc7cb9aeac00aca220b78ef3ee63c16df5a2f85 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Mon, 20 Mar 2017 00:37:07 +0000
Subject: [PATCH 09/19] enum-support: fix bug where enumerators incorrectly
 assigned to column

---
 h2/src/main/org/h2/command/Parser.java  |  5 +++--
 h2/src/main/org/h2/value/ValueEnum.java | 14 +++++++++++++-
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java
index b35b548..9e71cb9 100644
--- a/h2/src/main/org/h2/command/Parser.java
+++ b/h2/src/main/org/h2/command/Parser.java
@@ -4122,7 +4122,7 @@ private Column parseColumnWithType(String columnName) {
         }
         long precision = -1;
         int displaySize = -1;
-        java.util.List<String> enumerators = new ArrayList<String>();
+        java.util.List<String> enumerators = null;
         int scale = -1;
         String comment = null;
         Column templateColumn = null;
@@ -4198,6 +4198,7 @@ private Column parseColumnWithType(String columnName) {
             }
         } else if (dataType.enumerated) {
             if (readIf("(")) {
+                enumerators = new ArrayList<String>();
                 original += '(';
                 String enumerator0 = readString();
                 enumerators.add(enumerator0.toLowerCase().trim());
@@ -4232,7 +4233,7 @@ private Column parseColumnWithType(String columnName) {
                     Integer.toString(scale), Long.toString(precision));
         }
         Column column = new Column(columnName, type, precision, scale,
-            displaySize, enumerators.toArray(new String[enumerators.size()]));
+            displaySize, enumerators == null ? null : enumerators.toArray(new String[enumerators.size()]));
         if (templateColumn != null) {
             column.setNullable(templateColumn.isNullable());
             column.setDefaultExpression(session,
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index e5ecf67..3d077e6 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -80,7 +80,7 @@ private static final void check(final String[] enumerators, final Value value) {
                 return;
             default:
                 throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
-                        "Provided value does not match any enumerators", value.toString());
+                        "Provided value does not match any enumerators " + toString(enumerators), value.toString());
         }
     }
 
@@ -194,6 +194,18 @@ public void set(final PreparedStatement prep, final int parameterIndex)
          prep.setInt(parameterIndex, ordinal);
     }
 
+    private static String toString(final String[] enumerators) {
+        String result = "(";
+        for (int i = 0; i < enumerators.length; i++) {
+            result += "'" + enumerators[i] + "'";
+            if (i < enumerators.length - 1) {
+                result += ", ";
+            }
+        }
+        result += ")";
+        return result;
+    }
+
     private static Validation validate(final String[] enumerators, final String label) {
         check(enumerators);
 

>From deaf41b3ce014971a0e4b8b3a9c11592b1f09212 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Mon, 20 Mar 2017 00:43:16 +0000
Subject: [PATCH 10/19] enum-support: clean up

---
 h2/src/main/org/h2/server/TcpServerThread.java |  1 -
 h2/src/main/org/h2/table/TableFilter.java      |  1 -
 h2/src/main/org/h2/value/DataType.java         |  4 ++--
 h2/src/main/org/h2/value/Value.java            | 13 +------------
 4 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/h2/src/main/org/h2/server/TcpServerThread.java b/h2/src/main/org/h2/server/TcpServerThread.java
index a03d666..11c0736 100644
--- a/h2/src/main/org/h2/server/TcpServerThread.java
+++ b/h2/src/main/org/h2/server/TcpServerThread.java
@@ -38,7 +38,6 @@
 import org.h2.value.Transfer;
 import org.h2.value.Value;
 import org.h2.value.ValueLobDb;
-import org.h2.value.ValueString;
 
 /**
  * One server thread is opened per client connection.
diff --git a/h2/src/main/org/h2/table/TableFilter.java b/h2/src/main/org/h2/table/TableFilter.java
index 07c5405..d199deb 100644
--- a/h2/src/main/org/h2/table/TableFilter.java
+++ b/h2/src/main/org/h2/table/TableFilter.java
@@ -32,7 +32,6 @@
 import org.h2.util.StringUtils;
 import org.h2.value.Value;
 import org.h2.value.ValueLong;
-import org.h2.value.ValueString;
 import org.h2.value.ValueNull;
 
 /**
diff --git a/h2/src/main/org/h2/value/DataType.java b/h2/src/main/org/h2/value/DataType.java
index cbfd134..68e61c8 100644
--- a/h2/src/main/org/h2/value/DataType.java
+++ b/h2/src/main/org/h2/value/DataType.java
@@ -1179,8 +1179,8 @@ public static boolean isLargeObject(int type) {
      * @return true if the value type is a String type
      */
     public static boolean isStringType(int type) {
-        if (type == Value.STRING
-                || type == Value.STRING_FIXED || type == Value.STRING_IGNORECASE) {
+        if (type == Value.STRING || type == Value.STRING_FIXED
+                || type == Value.STRING_IGNORECASE) {
             return true;
         }
         return false;
diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java
index e98a689..3799852 100644
--- a/h2/src/main/org/h2/value/Value.java
+++ b/h2/src/main/org/h2/value/Value.java
@@ -18,7 +18,6 @@
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
-import java.util.Set;
 import org.h2.api.ErrorCode;
 import org.h2.engine.Constants;
 import org.h2.engine.SysProperties;
@@ -986,7 +985,7 @@ public Value convertTo(int targetType) {
             }
         } catch (NumberFormatException e) {
             throw DbException.get(
-                    ErrorCode.DATA_CONVERSION_ERROR_1, e, getString() + " type=" + getType());
+                    ErrorCode.DATA_CONVERSION_ERROR_1, e, getString());
         }
     }
 
@@ -1137,16 +1136,6 @@ public void remove() {
     }
 
     /**
-     * Check to see if this value is one of the given permitted values.
-     *
-     * @param permittedValues the permitted values
-     * @return true if this value is one of the permitted values
-     */
-    public boolean checkPermitted(Set<String> permittedValues) {
-        return permittedValues.contains(getString());
-    }
-
-    /**
      * Check if the precision is smaller or equal than the given precision.
      *
      * @param precision the maximum precision

>From f24ff52721dbfed2d9a82ebac7b1afef84194856 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Mon, 20 Mar 2017 00:52:50 +0000
Subject: [PATCH 11/19] enum-support: fix NPE with Column copy

---
 h2/src/main/org/h2/table/Column.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index 946e0dd..77bfec9 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -794,7 +794,8 @@ public void copy(Column source) {
         displaySize = source.displaySize;
         name = source.name;
         precision = source.precision;
-        enumerators = Arrays.copyOf(source.enumerators, source.enumerators.length);
+        enumerators = source.enumerators == null ? null :
+            Arrays.copyOf(source.enumerators, source.enumerators.length);
         scale = source.scale;
         // table is not set
         // columnId is not set

>From 063eb7ea0f64625cf722419325dbe5cc4d6f913b Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Tue, 21 Mar 2017 02:47:29 +0000
Subject: [PATCH 12/19] enum-support: fix/add tests

---
 .../org/h2/test/jdbc/TestPreparedStatement.java    |  3 +-
 h2/src/test/org/h2/test/testScript.sql             | 45 +++++++++++++++++++++-
 2 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
index 947ea2a..93db008 100644
--- a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
+++ b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
@@ -467,8 +467,9 @@ private void testEnum(Connection conn) throws SQLException {
                 rs.next();
             }
             assertEquals(goodSizes[i], rs.getString(1));
+            assertEquals(i, rs.getInt(1));
             Object o = rs.getObject(1);
-            assertEquals("java.lang.String", o.getClass().getName());
+            assertEquals(Integer.class, o.getClass());
         }
 
         stat.execute("DROP TABLE test_enum");
diff --git a/h2/src/test/org/h2/test/testScript.sql b/h2/src/test/org/h2/test/testScript.sql
index 87ba3cb..ca1806a 100644
--- a/h2/src/test/org/h2/test/testScript.sql
+++ b/h2/src/test/org/h2/test/testScript.sql
@@ -10588,4 +10588,47 @@ create table z.z (id int);
 > ok
 
 drop schema z;
-> ok
\ No newline at end of file
+> ok
+
+--- enum support
+create table card (rank int, suit enum('hearts', 'clubs', 'spades', 'diamonds'));
+> ok
+
+insert into card (rank, suit) values (0, 'clubs'), (3, 'hearts');
+> update count: 2
+
+select * from card;
+> RANK SUIT
+> ---- ------
+> 0    clubs
+> 3    hearts
+
+select * from card order by suit;
+> RANK SUIT
+> ---- ------
+> 3    hearts
+> 0    clubs
+
+insert into card (rank, suit) values (8, 'diamonds'), (10, 'clubs'), (7, 'hearts');
+> update count: 3
+
+select suit, count(rank) from card group by suit order by suit, count(rank);
+> SUIT     COUNT(RANK)
+> -------- -----------
+> hearts   2
+> clubs    2
+> diamonds 1
+
+select rank from card where suit = 'diamonds';
+> RANK
+> ----
+> 8
+
+select rank from card where suit = 1;
+> RANK
+> ----
+> 0
+> 10
+
+drop table card;
+> ok

>From fbce5a3f1690b72e55d29b496e4b63313b9aa69f Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Tue, 21 Mar 2017 04:55:35 +0000
Subject: [PATCH 13/19] enum-support: fix Transfer.{read,write}Value for enum

---
 h2/src/main/org/h2/value/Transfer.java  | 22 ++++++++++++++++++++++
 h2/src/main/org/h2/value/ValueEnum.java |  4 ++++
 2 files changed, 26 insertions(+)

diff --git a/h2/src/main/org/h2/value/Transfer.java b/h2/src/main/org/h2/value/Transfer.java
index 7ed94c8..57c8efc 100644
--- a/h2/src/main/org/h2/value/Transfer.java
+++ b/h2/src/main/org/h2/value/Transfer.java
@@ -494,6 +494,20 @@ public void writeValue(Value v) throws IOException {
             }
             break;
         }
+        case Value.ENUM: {
+            ValueEnum ve = (ValueEnum) v;
+            String[] enumerators = ve.getEnumerators();
+
+            writeInt(enumerators.length);
+
+            for (String member : enumerators) {
+                writeString(member);
+            }
+
+            writeInt(ve.getInt());
+
+            break;
+        }
         case Value.RESULT_SET: {
             try {
                 ResultSet rs = ((ValueResultSet) v).getResultSet();
@@ -590,6 +604,14 @@ public Value readValue() throws IOException {
             return ValueDouble.get(readDouble());
         case Value.FLOAT:
             return ValueFloat.get(readFloat());
+        case Value.ENUM: {
+            int len = readInt();
+            String[] enumerators = new String[len];
+            for (int i = 0; i < len; i++) {
+                enumerators[i] = readString();
+            }
+            return ValueEnum.get(enumerators, readInt());
+        }
         case Value.INT:
             return ValueInt.get(readInt());
         case Value.LONG:
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index 3d077e6..1c4ceaa 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -127,6 +127,10 @@ public int getDisplaySize() {
         return DISPLAY_SIZE;
     }
 
+    public String[] getEnumerators() {
+        return enumerators;
+    }
+
     @Override
     public int getInt() {
         return ordinal;

>From 4d8dd6e72a465bc98801eebbf4a66e69dd2ddac3 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Thu, 23 Mar 2017 00:25:21 +0000
Subject: [PATCH 14/19] enum-support: add support for math operations

---
 h2/src/docsrc/textbase/_messages_en.prop           |  3 +
 h2/src/main/org/h2/api/ErrorCode.java              | 58 +++++++++++++++--
 h2/src/main/org/h2/res/_messages_en.prop           |  3 +
 h2/src/main/org/h2/table/Column.java               |  4 +-
 h2/src/main/org/h2/value/Value.java                |  2 +
 h2/src/main/org/h2/value/ValueEnum.java            | 76 ++++++++++++++--------
 .../org/h2/test/jdbc/TestPreparedStatement.java    |  2 +-
 7 files changed, 113 insertions(+), 35 deletions(-)

diff --git a/h2/src/docsrc/textbase/_messages_en.prop b/h2/src/docsrc/textbase/_messages_en.prop
index d41e56a..a47e773 100644
--- a/h2/src/docsrc/textbase/_messages_en.prop
+++ b/h2/src/docsrc/textbase/_messages_en.prop
@@ -10,6 +10,9 @@
 22018=Data conversion error converting {0}
 22025=Error in LIKE ESCAPE: {0}
 22030=Value not permitted for column {0}: {1}
+22031=Value not a member of enumerators {0}: {1}
+22032=Empty enums are not allowed
+22033=Duplicate enumerators are not allowed for enum types: {0}
 23502=NULL not allowed for column {0}
 23503=Referential integrity constraint violation: {0}
 23505=Unique index or primary key violation: {0}
diff --git a/h2/src/main/org/h2/api/ErrorCode.java b/h2/src/main/org/h2/api/ErrorCode.java
index 5d355b3..13702a9 100644
--- a/h2/src/main/org/h2/api/ErrorCode.java
+++ b/h2/src/main/org/h2/api/ErrorCode.java
@@ -161,16 +161,62 @@
 
     /**
      * The error with code <code>22030</code> is thrown when
-     * an attempt is made to insert or update an ENUM value,
-     * but the target value is not one of the values permitted
-     * by the column.
+     * an attempt is made to INSERT or UPDATE an ENUM-typed cell,
+     * but the value is not one of the values enumerated by the
+     * type.
+     *
+     * This error is best thrown in a context when the column name
+     * and it's enumerated values are known.
+     *
      * Example:
      * <pre>
-     * CREATE TABLE TEST(ID INT, CASE ENUM('sensitive','insensitive'));
-     * INSERT INTO TEST VALUES(1, 'Hello');
+     * CREATE TABLE TEST(CASE ENUM('sensitive','insensitive'));
+     * INSERT INTO TEST VALUES('snake');
+     * </pre>
+     */
+    public static final int ENUM_VALUE_NOT_PERMITTED_1 = 22030;
+
+    /**
+     * The error with code <code>22031</code> is typically thrown
+     * when a math operation is attempted on an ENUM-typed cell,
+     * but the value resulting from the operation is not one of
+     * values enumerated by the type.
+     *
+     * This error is best thrown in a context when the column name
+     * is not known, but the enumerated values of the type are known.
+     *
+     * Example:
+     * <pre>
+     * CREATE TABLE TEST(CASE ENUM('sensitive','insensitive'));
+     * INSERT INTO TEST VALUES('sensitive');
+     * UPDATE TEST SET CASE = CASE + 100;
+     * </pre>
+     */
+    public static final int ENUM_VALUE_NOT_PERMITTED_2 = 22031;
+
+    /**
+     * The error with code <code>22032</code> is thrown when an
+     * attempt is made to add or modify an ENUM-typed column so
+     * that it would not have any enumerated values.
+     *
+     * Example:
+     * <pre>
+     * CREATE TABLE TEST(CASE ENUM());
+     * </pre>
+     */
+    public static final int ENUM_EMPTY = 22032;
+
+    /**
+     * The error with code <code>22033</code> is thrown when an
+     * attempt is made to add or modify an ENUM-typed column so
+     * that it would have duplicate values.
+     *
+     * Example:
+     * <pre>
+     * CREATE TABLE TEST(CASE ENUM('sensitive', 'sensitive'));
      * </pre>
      */
-    public static final int VALUE_NOT_PERMITTED = 22030;
+    public static final int ENUM_DUPLICATE = 22033;
 
     // 23: constraint violation
 
diff --git a/h2/src/main/org/h2/res/_messages_en.prop b/h2/src/main/org/h2/res/_messages_en.prop
index d41e56a..a47e773 100644
--- a/h2/src/main/org/h2/res/_messages_en.prop
+++ b/h2/src/main/org/h2/res/_messages_en.prop
@@ -10,6 +10,9 @@
 22018=Data conversion error converting {0}
 22025=Error in LIKE ESCAPE: {0}
 22030=Value not permitted for column {0}: {1}
+22031=Value not a member of enumerators {0}: {1}
+22032=Empty enums are not allowed
+22033=Duplicate enumerators are not allowed for enum types: {0}
 23502=NULL not allowed for column {0}
 23503=Referential integrity constraint violation: {0}
 23505=Unique index or primary key violation: {0}
diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index 77bfec9..c6d50c4 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -376,8 +376,8 @@ public Value validateConvertUpdateSequence(Session session, Value value) {
                 if (s.length() > 127) {
                     s = s.substring(0, 128) + "...";
                 }
-                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
-                        getCreateSQL(), s + " (" + value.getString() + ")");
+                throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED_1,
+                        getCreateSQL(), s + " (" + value.getInt() + ")");
             }
 
             value = ValueEnum.get(enumerators, value);
diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java
index 2ccb9fe..63ade96 100644
--- a/h2/src/main/org/h2/value/Value.java
+++ b/h2/src/main/org/h2/value/Value.java
@@ -622,6 +622,8 @@ public Value convertTo(int targetType) {
                     return ValueInt.get(getBoolean().booleanValue() ? 1 : 0);
                 case BYTE:
                     return ValueInt.get(getByte());
+                case ENUM:
+                    return ValueInt.get(getInt());
                 case SHORT:
                     return ValueInt.get(getShort());
                 case LONG:
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index 1c4ceaa..081a292 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -29,19 +29,24 @@ private ValueEnum(final String[] enumerators, final int ordinal) {
         this.ordinal = ordinal;
     }
 
+    @Override
+    public Value add(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).add(iv);
+    }
+
     private static final void check(final String[] enumerators) {
         switch (validate(enumerators)) {
             case VALID:
                 return;
             case EMPTY:
-                throw DbException.get(ErrorCode.INVALID_VALUE_2,
-                        "Empty enum is not allowed");
+                throw DbException.get(ErrorCode.ENUM_EMPTY);
             case DUPLICATE:
-                throw DbException.get(ErrorCode.INVALID_VALUE_2,
-                        "Enum with duplicate enumerator is not allowed");
+                throw DbException.get(ErrorCode.ENUM_DUPLICATE,
+                        toString(enumerators));
             default:
                 throw DbException.get(ErrorCode.INVALID_VALUE_2,
-                        "Invalid enumerator list for enum");
+                        toString(enumerators));
         }
     }
 
@@ -51,12 +56,9 @@ private static final void check(final String[] enumerators, final String label)
         switch (validate(enumerators, label)) {
             case VALID:
                 return;
-            case EMPTY:
-                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
-                        "Enumerator label may not be empty");
             default:
-                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
-                        "Enumerator label is invalid");
+                throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED_2,
+                        toString(enumerators), "'" + label + "'");
         }
     }
 
@@ -67,8 +69,8 @@ private static final void check(final String[] enumerators, final int ordinal) {
             case VALID:
                 return;
             default:
-                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
-                        "Provided enumerator label does not match any member of enum");
+                throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED_2,
+                        toString(enumerators), Integer.toString(ordinal));
         }
     }
 
@@ -79,15 +81,21 @@ private static final void check(final String[] enumerators, final Value value) {
             case VALID:
                 return;
             default:
-                throw DbException.get(ErrorCode.VALUE_NOT_PERMITTED,
-                        "Provided value does not match any enumerators " + toString(enumerators), value.toString());
+                throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED_2,
+                        toString(enumerators), value.toString());
         }
     }
 
     @Override
-    protected int compareSecure(final Value o, final CompareMode mode) {
-        final ValueEnum v = ValueEnum.get(enumerators, o);
-        return MathUtils.compareInt(ordinal(), v.ordinal());
+    protected int compareSecure(final Value v, final CompareMode mode) {
+        final ValueEnum ev = ValueEnum.get(enumerators, v);
+        return MathUtils.compareInt(ordinal(), ev.ordinal());
+    }
+
+    @Override
+    public Value divide(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).divide(iv);
     }
 
     @Override
@@ -167,6 +175,11 @@ public String getString() {
     }
 
     @Override
+    public int getType() {
+        return Value.ENUM;
+    }
+
+    @Override
     public int hashCode() {
         return enumerators.hashCode() + ordinal;
     }
@@ -183,13 +196,20 @@ public static boolean isValid(final String enumerators[], final Value value) {
         return validate(enumerators, value).equals(Validation.VALID);
     }
 
+    protected int ordinal() {
+        return ordinal;
+    }
+
     @Override
-    public int getType() {
-        return Value.ENUM;
+    public Value modulus(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).modulus(iv);
     }
 
-    protected int ordinal() {
-        return ordinal;
+    @Override
+    public Value multiply(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).multiply(iv);
     }
 
     @Override
@@ -198,6 +218,12 @@ public void set(final PreparedStatement prep, final int parameterIndex)
          prep.setInt(parameterIndex, ordinal);
     }
 
+    @Override
+    public Value subtract(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).subtract(iv);
+    }
+
     private static String toString(final String[] enumerators) {
         String result = "(";
         for (int i = 0; i < enumerators.length; i++) {
@@ -213,12 +239,10 @@ private static String toString(final String[] enumerators) {
     private static Validation validate(final String[] enumerators, final String label) {
         check(enumerators);
 
-        if (label == null || label.trim().length() == 0) {
-            return Validation.EMPTY;
-        }
+        final String cleanLabel = label.trim().toLowerCase();
 
         for (int i = 0; i < enumerators.length; i++) {
-            if (label.equals(enumerators[i])) {
+            if (cleanLabel.equals(enumerators[i])) {
                 return Validation.VALID;
             }
         }
@@ -228,7 +252,7 @@ private static Validation validate(final String[] enumerators, final String labe
 
     private static Validation validate(final String[] enumerators) {
         for (int i = 0; i < enumerators.length; i++) {
-            if (enumerators[i] == null || enumerators[i].equals("")) {
+            if (enumerators[i] == null || enumerators[i].trim().equals("")) {
                 return Validation.EMPTY;
             }
 
diff --git a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
index 93db008..28998aa 100644
--- a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
+++ b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
@@ -453,7 +453,7 @@ private void testEnum(Connection conn) throws SQLException {
             PreparedStatement prep = conn.prepareStatement(
                     "INSERT INTO test_enum VALUES(?)");
             prep.setObject(1, badSizes[i]);
-            assertThrows(ErrorCode.VALUE_NOT_PERMITTED, prep).execute();
+            assertThrows(ErrorCode.ENUM_VALUE_NOT_PERMITTED_1, prep).execute();
         }
 
         String[] goodSizes = new String[]{"small", "medium", "large"};

>From 06c8cb75072f4196460db71ba681e16f49c0614f Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Thu, 23 Mar 2017 03:40:29 +0000
Subject: [PATCH 15/19] enum-support: fix bad exception throw

---
 h2/src/main/org/h2/table/Column.java                    | 2 +-
 h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index c6d50c4..f13833b 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -377,7 +377,7 @@ public Value validateConvertUpdateSequence(Session session, Value value) {
                     s = s.substring(0, 128) + "...";
                 }
                 throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED_1,
-                        getCreateSQL(), s + " (" + value.getInt() + ")");
+                        getCreateSQL(), s);
             }
 
             value = ValueEnum.get(enumerators, value);
diff --git a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
index 28998aa..b213f0d 100644
--- a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
+++ b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
@@ -448,7 +448,7 @@ private void testEnum(Connection conn) throws SQLException {
         Statement stat = conn.createStatement();
         stat.execute("CREATE TABLE test_enum(size ENUM('small', 'medium', 'large'))");
 
-        String[] badSizes = new String[]{"green", "wool", "discount"};
+        String[] badSizes = new String[]{"green", "smalll", "0"};
         for (int i = 0; i < badSizes.length; i++) {
             PreparedStatement prep = conn.prepareStatement(
                     "INSERT INTO test_enum VALUES(?)");

>From cf06d6ec2e3ad7ca86268e6c84b3a1d465b868a9 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Mon, 27 Mar 2017 03:29:30 +0000
Subject: [PATCH 16/19] enum-support: add more test cases

---
 h2/src/main/org/h2/api/ErrorCode.java   |  4 ++--
 h2/src/main/org/h2/command/Parser.java  | 21 ++++++++++++++++-----
 h2/src/main/org/h2/value/ValueEnum.java |  6 +++++-
 h2/src/test/org/h2/test/testScript.sql  | 33 +++++++++++++++++++++++++++++++--
 4 files changed, 54 insertions(+), 10 deletions(-)

diff --git a/h2/src/main/org/h2/api/ErrorCode.java b/h2/src/main/org/h2/api/ErrorCode.java
index 13702a9..f7a3979 100644
--- a/h2/src/main/org/h2/api/ErrorCode.java
+++ b/h2/src/main/org/h2/api/ErrorCode.java
@@ -197,11 +197,11 @@
     /**
      * The error with code <code>22032</code> is thrown when an
      * attempt is made to add or modify an ENUM-typed column so
-     * that it would not have any enumerated values.
+     * that one or more of its enumerators would be empty.
      *
      * Example:
      * <pre>
-     * CREATE TABLE TEST(CASE ENUM());
+     * CREATE TABLE TEST(CASE ENUM(' '));
      * </pre>
      */
     public static final int ENUM_EMPTY = 22032;
diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java
index 750494c..4b2b715 100644
--- a/h2/src/main/org/h2/command/Parser.java
+++ b/h2/src/main/org/h2/command/Parser.java
@@ -146,6 +146,7 @@
 import org.h2.value.ValueBytes;
 import org.h2.value.ValueDate;
 import org.h2.value.ValueDecimal;
+import org.h2.value.ValueEnum;
 import org.h2.value.ValueInt;
 import org.h2.value.ValueLong;
 import org.h2.value.ValueNull;
@@ -4127,7 +4128,8 @@ private Column parseColumnWithType(String columnName) {
         }
         long precision = -1;
         int displaySize = -1;
-        java.util.List<String> enumerators = null;
+        java.util.List<String> enumeratorList = null;
+        String[] enumerators = null;
         int scale = -1;
         String comment = null;
         Column templateColumn = null;
@@ -4203,20 +4205,27 @@ private Column parseColumnWithType(String columnName) {
             }
         } else if (dataType.enumerated) {
             if (readIf("(")) {
-                enumerators = new ArrayList<String>();
+                enumeratorList = new ArrayList<String>();
                 original += '(';
                 String enumerator0 = readString();
-                enumerators.add(enumerator0.toLowerCase().trim());
+                enumeratorList.add(enumerator0.toLowerCase().trim());
                 original += "'" + enumerator0 + "'";
                 while(readIf(",")) {
                     original += ',';
                     String enumeratorN = readString();
                     original += "'" + enumeratorN + "'";
-                    enumerators.add(enumeratorN.toLowerCase().trim());
+                    enumeratorList.add(enumeratorN.toLowerCase().trim());
                 }
                 read(")");
                 original += ')';
             }
+            enumerators
+                = enumeratorList.toArray(new String[enumeratorList.size()]);
+            try {
+                ValueEnum.check(enumerators);
+            } catch(DbException e) {
+                throw e.addSQL(original);
+            }
         } else if (readIf("(")) {
             // Support for MySQL: INT(11), MEDIUMINT(8) and so on.
             // Just ignore the precision.
@@ -4237,8 +4246,10 @@ private Column parseColumnWithType(String columnName) {
             throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION,
                     Integer.toString(scale), Long.toString(precision));
         }
+
+
         Column column = new Column(columnName, type, precision, scale,
-            displaySize, enumerators == null ? null : enumerators.toArray(new String[enumerators.size()]));
+            displaySize, enumerators == null ? null : enumerators);
         if (templateColumn != null) {
             column.setNullable(templateColumn.isNullable());
             column.setDefaultExpression(session,
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index 081a292..a760155 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -35,7 +35,7 @@ public Value add(final Value v) {
         return convertTo(Value.INT).add(iv);
     }
 
-    private static final void check(final String[] enumerators) {
+    public static final void check(final String[] enumerators) {
         switch (validate(enumerators)) {
             case VALID:
                 return;
@@ -251,6 +251,10 @@ private static Validation validate(final String[] enumerators, final String labe
     }
 
     private static Validation validate(final String[] enumerators) {
+        if (enumerators == null || enumerators.length == 0) {
+            return Validation.EMPTY;
+        }
+
         for (int i = 0; i < enumerators.length; i++) {
             if (enumerators[i] == null || enumerators[i].trim().equals("")) {
                 return Validation.EMPTY;
diff --git a/h2/src/test/org/h2/test/testScript.sql b/h2/src/test/org/h2/test/testScript.sql
index ca1806a..102c3ce 100644
--- a/h2/src/test/org/h2/test/testScript.sql
+++ b/h2/src/test/org/h2/test/testScript.sql
@@ -10590,13 +10590,21 @@ create table z.z (id int);
 drop schema z;
 > ok
 
---- enum support
-create table card (rank int, suit enum('hearts', 'clubs', 'spades', 'diamonds'));
+----------------
+--- ENUM support
+----------------
+
+--- ENUM basic operations
+
+create table card (rank int, suit enum('hearts', 'clubs', 'spades'));
 > ok
 
 insert into card (rank, suit) values (0, 'clubs'), (3, 'hearts');
 > update count: 2
 
+alter table card alter column suit enum('hearts', 'clubs', 'spades', 'diamonds');
+> ok
+
 select * from card;
 > RANK SUIT
 > ---- ------
@@ -10624,11 +10632,32 @@ select rank from card where suit = 'diamonds';
 > ----
 > 8
 
+--- ENUM integer-based operations
+
 select rank from card where suit = 1;
 > RANK
 > ----
 > 0
 > 10
 
+insert into card (rank, suit) values(5, 2);
+> update count: 1
+
+select * from card where rank = 5;
+> RANK SUIT
+> ---- ------
+> 5    spades
+
+--- ENUM edge cases
+
+insert into card (rank, suit) values(6, ' ');
+> exception
+
+alter table card alter column suit enum('hearts', 'clubs', 'spades', 'diamonds', 'clubs');
+> exception
+
+alter table card alter column suit enum('hearts', 'clubs', 'spades', 'diamonds', '');
+> exception
+
 drop table card;
 > ok

>From 2c903f83a96c419df2ca4a316632a1efbf48dd94 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Wed, 29 Mar 2017 05:12:16 +0000
Subject: [PATCH 17/19] enum-support: in Transfer, use base ValueEnumBase when
 reading

---
 h2/src/main/org/h2/value/Transfer.java      |  22 ++---
 h2/src/main/org/h2/value/ValueEnum.java     | 106 +----------------------
 h2/src/main/org/h2/value/ValueEnumBase.java | 130 ++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+), 120 deletions(-)
 create mode 100644 h2/src/main/org/h2/value/ValueEnumBase.java

diff --git a/h2/src/main/org/h2/value/Transfer.java b/h2/src/main/org/h2/value/Transfer.java
index a6e319f..f0196bc 100644
--- a/h2/src/main/org/h2/value/Transfer.java
+++ b/h2/src/main/org/h2/value/Transfer.java
@@ -495,17 +495,8 @@ public void writeValue(Value v) throws IOException {
             break;
         }
         case Value.ENUM: {
-            ValueEnum ve = (ValueEnum) v;
-            String[] enumerators = ve.getEnumerators();
-
-            writeInt(enumerators.length);
-
-            for (String member : enumerators) {
-                writeString(member);
-            }
-
-            writeInt(ve.getInt());
-
+            writeInt(v.getInt());
+            writeString(v.getString());
             break;
         }
         case Value.RESULT_SET: {
@@ -609,12 +600,9 @@ public Value readValue() throws IOException {
         case Value.FLOAT:
             return ValueFloat.get(readFloat());
         case Value.ENUM: {
-            int len = readInt();
-            String[] enumerators = new String[len];
-            for (int i = 0; i < len; i++) {
-                enumerators[i] = readString();
-            }
-            return ValueEnum.get(enumerators, readInt());
+            final int ordinal = readInt();
+            final String label = readString();
+            return ValueEnumBase.get(label, ordinal);
         }
         case Value.INT:
             return ValueInt.get(readInt());
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index a760155..ce5cc26 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -1,17 +1,11 @@
 package org.h2.value;
 
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
 import org.h2.api.ErrorCode;
 import org.h2.message.DbException;
 import org.h2.util.MathUtils;
 import org.h2.value.DataType;
 
-public class ValueEnum extends Value {
-    public static final int PRECISION = 10;
-    public static final int DISPLAY_SIZE = 11;
-
+public class ValueEnum extends ValueEnumBase {
     private static enum Validation {
         DUPLICATE,
         EMPTY,
@@ -20,19 +14,10 @@
     }
 
     private final String[] enumerators;
-    private final String label;
-    private final int ordinal;
 
     private ValueEnum(final String[] enumerators, final int ordinal) {
-        this.label = enumerators[ordinal];
+        super(enumerators[ordinal], ordinal);
         this.enumerators = enumerators;
-        this.ordinal = ordinal;
-    }
-
-    @Override
-    public Value add(final Value v) {
-        final Value iv = v.convertTo(Value.INT);
-        return convertTo(Value.INT).add(iv);
     }
 
     public static final void check(final String[] enumerators) {
@@ -92,18 +77,6 @@ protected int compareSecure(final Value v, final CompareMode mode) {
         return MathUtils.compareInt(ordinal(), ev.ordinal());
     }
 
-    @Override
-    public Value divide(final Value v) {
-        final Value iv = v.convertTo(Value.INT);
-        return convertTo(Value.INT).divide(iv);
-    }
-
-    @Override
-    public boolean equals(final Object other) {
-        return other instanceof ValueEnum &&
-            ordinal() == ((ValueEnum) other).ordinal();
-    }
-
     public static ValueEnum get(final String[] enumerators, final String label) {
         check(enumerators, label);
 
@@ -130,58 +103,13 @@ public static ValueEnum get(final String[] enumerators, final Value value) {
         }
     }
 
-    @Override
-    public int getDisplaySize() {
-        return DISPLAY_SIZE;
-    }
-
     public String[] getEnumerators() {
         return enumerators;
     }
 
     @Override
-    public int getInt() {
-        return ordinal;
-    }
-
-    @Override
-    public long getLong() {
-        return ordinal;
-    }
-
-    @Override
-    public Object getObject() {
-        return ordinal;
-    }
-
-    @Override
-    public long getPrecision() {
-        return PRECISION;
-    }
-
-    @Override
-    public int getSignum() {
-        return Integer.signum(ordinal);
-    }
-
-    @Override
-    public String getSQL() {
-        return getString();
-    }
-
-    @Override
-    public String getString() {
-        return label;
-    }
-
-    @Override
-    public int getType() {
-        return Value.ENUM;
-    }
-
-    @Override
     public int hashCode() {
-        return enumerators.hashCode() + ordinal;
+        return enumerators.hashCode() + ordinal();
     }
 
     public static boolean isValid(final String enumerators[], final String label) {
@@ -196,34 +124,6 @@ public static boolean isValid(final String enumerators[], final Value value) {
         return validate(enumerators, value).equals(Validation.VALID);
     }
 
-    protected int ordinal() {
-        return ordinal;
-    }
-
-    @Override
-    public Value modulus(final Value v) {
-        final Value iv = v.convertTo(Value.INT);
-        return convertTo(Value.INT).modulus(iv);
-    }
-
-    @Override
-    public Value multiply(final Value v) {
-        final Value iv = v.convertTo(Value.INT);
-        return convertTo(Value.INT).multiply(iv);
-    }
-
-    @Override
-    public void set(final PreparedStatement prep, final int parameterIndex)
-            throws SQLException {
-         prep.setInt(parameterIndex, ordinal);
-    }
-
-    @Override
-    public Value subtract(final Value v) {
-        final Value iv = v.convertTo(Value.INT);
-        return convertTo(Value.INT).subtract(iv);
-    }
-
     private static String toString(final String[] enumerators) {
         String result = "(";
         for (int i = 0; i < enumerators.length; i++) {
diff --git a/h2/src/main/org/h2/value/ValueEnumBase.java b/h2/src/main/org/h2/value/ValueEnumBase.java
new file mode 100644
index 0000000..d22a870
--- /dev/null
+++ b/h2/src/main/org/h2/value/ValueEnumBase.java
@@ -0,0 +1,130 @@
+package org.h2.value;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.h2.message.DbException;
+import org.h2.util.MathUtils;
+
+public class ValueEnumBase extends Value {
+    public static final int PRECISION = 10;
+    public static final int DISPLAY_SIZE = 11;
+
+    private final String label;
+    private final int ordinal;
+
+    protected ValueEnumBase(final String label, final int ordinal) {
+        this.label = label;
+        this.ordinal = ordinal;
+    }
+
+    @Override
+    public Value add(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).add(iv);
+    }
+
+    @Override
+    protected int compareSecure(final Value v, final CompareMode mode) {
+        return MathUtils.compareInt(ordinal(), v.getInt());
+    }
+
+    @Override
+    public Value divide(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).divide(iv);
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+        return other instanceof ValueEnumBase &&
+            ordinal() == ((ValueEnumBase) other).ordinal() &&
+            getString() == ((ValueEnumBase) other).getString();
+    }
+
+    public static ValueEnumBase get(final String label, final int ordinal) {
+        return new ValueEnumBase(label, ordinal);
+    }
+
+    @Override
+    public int getDisplaySize() {
+        return DISPLAY_SIZE;
+    }
+
+    @Override
+    public int getInt() {
+        return ordinal;
+    }
+
+    @Override
+    public long getLong() {
+        return ordinal;
+    }
+
+    @Override
+    public Object getObject() {
+        return ordinal;
+    }
+
+    @Override
+    public long getPrecision() {
+        return PRECISION;
+    }
+
+    @Override
+    public int getSignum() {
+        return Integer.signum(ordinal);
+    }
+
+    @Override
+    public String getSQL() {
+        return getString();
+    }
+
+    @Override
+    public String getString() {
+        return label;
+    }
+
+    @Override
+    public int getType() {
+        return Value.ENUM;
+    }
+
+    @Override
+    public int hashCode() {
+        int results = 31;
+        results += getString().hashCode();
+        results += ordinal();
+        return results;
+    }
+
+    @Override
+    public Value modulus(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).modulus(iv);
+    }
+
+    @Override
+    public Value multiply(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).multiply(iv);
+    }
+
+
+    protected int ordinal() {
+        return ordinal;
+    }
+
+    @Override
+    public void set(final PreparedStatement prep, final int parameterIndex)
+            throws SQLException {
+         prep.setInt(parameterIndex, ordinal);
+    }
+
+    @Override
+    public Value subtract(final Value v) {
+        final Value iv = v.convertTo(Value.INT);
+        return convertTo(Value.INT).subtract(iv);
+    }
+}

>From e8d00a2c9254a78338edfd3b1938f6df6ba97113 Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Thu, 30 Mar 2017 06:21:27 +0000
Subject: [PATCH 18/19] enum-support: use toUpperCase(ENGLISH) instead of
 toLowerCase()

---
 h2/src/main/org/h2/command/Parser.java      |  12 ++-
 h2/src/main/org/h2/table/Column.java        |   2 +-
 h2/src/main/org/h2/value/DataType.java      |   2 +-
 h2/src/main/org/h2/value/ValueEnum.java     | 135 +++++++++++-----------------
 h2/src/main/org/h2/value/ValueEnumBase.java |   4 +-
 5 files changed, 61 insertions(+), 94 deletions(-)

diff --git a/h2/src/main/org/h2/command/Parser.java b/h2/src/main/org/h2/command/Parser.java
index 4b2b715..9142177 100644
--- a/h2/src/main/org/h2/command/Parser.java
+++ b/h2/src/main/org/h2/command/Parser.java
@@ -4128,7 +4128,6 @@ private Column parseColumnWithType(String columnName) {
         }
         long precision = -1;
         int displaySize = -1;
-        java.util.List<String> enumeratorList = null;
         String[] enumerators = null;
         int scale = -1;
         String comment = null;
@@ -4205,22 +4204,21 @@ private Column parseColumnWithType(String columnName) {
             }
         } else if (dataType.enumerated) {
             if (readIf("(")) {
-                enumeratorList = new ArrayList<String>();
+                java.util.List<String> enumeratorList = new ArrayList<String>();
                 original += '(';
                 String enumerator0 = readString();
-                enumeratorList.add(enumerator0.toLowerCase().trim());
+                enumeratorList.add(enumerator0);
                 original += "'" + enumerator0 + "'";
                 while(readIf(",")) {
                     original += ',';
                     String enumeratorN = readString();
                     original += "'" + enumeratorN + "'";
-                    enumeratorList.add(enumeratorN.toLowerCase().trim());
+                    enumeratorList.add(enumeratorN);
                 }
                 read(")");
                 original += ')';
+                enumerators = enumeratorList.toArray(new String[enumeratorList.size()]);
             }
-            enumerators
-                = enumeratorList.toArray(new String[enumeratorList.size()]);
             try {
                 ValueEnum.check(enumerators);
             } catch(DbException e) {
@@ -4249,7 +4247,7 @@ private Column parseColumnWithType(String columnName) {
 
 
         Column column = new Column(columnName, type, precision, scale,
-            displaySize, enumerators == null ? null : enumerators);
+            displaySize, enumerators);
         if (templateColumn != null) {
             column.setNullable(templateColumn.isNullable());
             column.setDefaultExpression(session,
diff --git a/h2/src/main/org/h2/table/Column.java b/h2/src/main/org/h2/table/Column.java
index f13833b..3ce0649 100644
--- a/h2/src/main/org/h2/table/Column.java
+++ b/h2/src/main/org/h2/table/Column.java
@@ -147,7 +147,7 @@ public int hashCode() {
     }
 
     public boolean isEnumerated() {
-        return enumerators != null && enumerators.length > 0;
+        return type == Value.ENUM;
     }
 
     public Column getClone() {
diff --git a/h2/src/main/org/h2/value/DataType.java b/h2/src/main/org/h2/value/DataType.java
index a5a9316..163bbd3 100644
--- a/h2/src/main/org/h2/value/DataType.java
+++ b/h2/src/main/org/h2/value/DataType.java
@@ -141,7 +141,7 @@
     public boolean caseSensitive;
 
     /**
-     * If permitted values are supports.
+     * If enumerated values are supported.
      */
     public boolean enumerated;
 
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index ce5cc26..491eab5 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -1,5 +1,7 @@
 package org.h2.value;
 
+import java.util.Locale;
+
 import org.h2.api.ErrorCode;
 import org.h2.message.DbException;
 import org.h2.util.MathUtils;
@@ -35,30 +37,6 @@ public static final void check(final String[] enumerators) {
         }
     }
 
-    private static final void check(final String[] enumerators, final String label) {
-        check(enumerators);
-
-        switch (validate(enumerators, label)) {
-            case VALID:
-                return;
-            default:
-                throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED_2,
-                        toString(enumerators), "'" + label + "'");
-        }
-    }
-
-    private static final void check(final String[] enumerators, final int ordinal) {
-        check(enumerators);
-
-        switch (validate(enumerators, ordinal)) {
-            case VALID:
-                return;
-            default:
-                throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED_2,
-                        toString(enumerators), Integer.toString(ordinal));
-        }
-    }
-
     private static final void check(final String[] enumerators, final Value value) {
         check(enumerators);
 
@@ -77,29 +55,20 @@ protected int compareSecure(final Value v, final CompareMode mode) {
         return MathUtils.compareInt(ordinal(), ev.ordinal());
     }
 
-    public static ValueEnum get(final String[] enumerators, final String label) {
-        check(enumerators, label);
-
-        for (int i = 0; i < enumerators.length; i++) {
-            if (label.equals(enumerators[i]))
-                return new ValueEnum(enumerators, i);
-        }
-
-        throw DbException.get(ErrorCode.GENERAL_ERROR_1, "Unexpected error");
-    }
-
-    public static ValueEnum get(final String[] enumerators, final int ordinal) {
-        check(enumerators, ordinal);
-        return new ValueEnum(enumerators, ordinal);
-    }
-
     public static ValueEnum get(final String[] enumerators, final Value value) {
         check(enumerators, value);
 
         if (DataType.isStringType(value.getType())) {
-            return get(enumerators, value.getString());
+            final String cleanLabel = sanitize(value.getString());
+
+            for (int i = 0; i < enumerators.length; i++) {
+                if (cleanLabel.equals(sanitize(enumerators[i])))
+                    return new ValueEnum(enumerators, i);
+            }
+
+            throw DbException.get(ErrorCode.GENERAL_ERROR_1, "Unexpected error");
         } else {
-            return get(enumerators, value.getInt());
+            return new ValueEnum(enumerators, value.getInt());
         }
     }
 
@@ -107,21 +76,24 @@ public static ValueEnum get(final String[] enumerators, final Value value) {
         return enumerators;
     }
 
-    @Override
-    public int hashCode() {
-        return enumerators.hashCode() + ordinal();
+    public static boolean isValid(final String enumerators[], final Value value) {
+        return validate(enumerators, value).equals(Validation.VALID);
     }
 
-    public static boolean isValid(final String enumerators[], final String label) {
-        return validate(enumerators, label).equals(Validation.VALID);
+    private static String sanitize(final String label) {
+        return label == null ? null : label.trim().toUpperCase(Locale.ENGLISH);
     }
 
-    public static boolean isValid(final String enumerators[], final int ordinal) {
-        return validate(enumerators, ordinal).equals(Validation.VALID);
-    }
+    public static String[] sanitize(final String[] enumerators) {
+        if (enumerators == null || enumerators.length == 0) return null;
 
-    public static boolean isValid(final String enumerators[], final Value value) {
-        return validate(enumerators, value).equals(Validation.VALID);
+        final String[] clean = new String[enumerators.length];
+
+        for (int i = 0; i < enumerators.length; i++) {
+            clean[i] = sanitize(enumerators[i]);
+        }
+
+        return clean;
     }
 
     private static String toString(final String[] enumerators) {
@@ -136,33 +108,21 @@ private static String toString(final String[] enumerators) {
         return result;
     }
 
-    private static Validation validate(final String[] enumerators, final String label) {
-        check(enumerators);
-
-        final String cleanLabel = label.trim().toLowerCase();
-
-        for (int i = 0; i < enumerators.length; i++) {
-            if (cleanLabel.equals(enumerators[i])) {
-                return Validation.VALID;
-            }
-        }
-
-        return Validation.INVALID;
-    }
-
     private static Validation validate(final String[] enumerators) {
-        if (enumerators == null || enumerators.length == 0) {
+        final String[] cleaned = sanitize(enumerators);
+
+        if (cleaned == null || cleaned.length == 0) {
             return Validation.EMPTY;
         }
 
-        for (int i = 0; i < enumerators.length; i++) {
-            if (enumerators[i] == null || enumerators[i].trim().equals("")) {
+        for (int i = 0; i < cleaned.length; i++) {
+            if (cleaned[i] == null || cleaned[i].equals("")) {
                 return Validation.EMPTY;
             }
 
-            if (i < enumerators.length - 1) {
-                for (int j = i + 1; j < enumerators.length; j++) {
-                    if (enumerators[i].equals(enumerators[j])) {
+            if (i < cleaned.length - 1) {
+                for (int j = i + 1; j < cleaned.length; j++) {
+                    if (cleaned[i].equals(cleaned[j])) {
                         return Validation.DUPLICATE;
                     }
                 }
@@ -172,21 +132,30 @@ private static Validation validate(final String[] enumerators) {
         return Validation.VALID;
     }
 
-    private static Validation validate(final String[] enumerators, final int ordinal) {
-        check(enumerators);
-
-        if (ordinal < 0 || ordinal >= enumerators.length) {
-            return Validation.INVALID;
+    private static Validation validate(final String[] enumerators, final Value value) {
+        final Validation validation = validate(enumerators);
+        if (!validation.equals(Validation.VALID)) {
+            return validation;
         }
 
-        return Validation.VALID;
-    }
-
-    private static Validation validate(final String[] enumerators, final Value value) {
         if (DataType.isStringType(value.getType())) {
-            return validate(enumerators, value.getString());
+            final String cleanLabel = sanitize(value.getString());
+
+            for (int i = 0; i < enumerators.length; i++) {
+                if (cleanLabel.equals(sanitize(enumerators[i]))) {
+                    return Validation.VALID;
+                }
+            }
+
+            return Validation.INVALID;
         } else {
-            return validate(enumerators, value.getInt());
+            final int ordinal = value.getInt();
+
+            if (ordinal < 0 || ordinal >= enumerators.length) {
+                return Validation.INVALID;
+            }
+
+            return Validation.VALID;
         }
     }
 }
diff --git a/h2/src/main/org/h2/value/ValueEnumBase.java b/h2/src/main/org/h2/value/ValueEnumBase.java
index d22a870..9e4c620 100644
--- a/h2/src/main/org/h2/value/ValueEnumBase.java
+++ b/h2/src/main/org/h2/value/ValueEnumBase.java
@@ -2,6 +2,7 @@
 
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
+import java.util.Locale;
 
 import org.h2.message.DbException;
 import org.h2.util.MathUtils;
@@ -38,8 +39,7 @@ public Value divide(final Value v) {
     @Override
     public boolean equals(final Object other) {
         return other instanceof ValueEnumBase &&
-            ordinal() == ((ValueEnumBase) other).ordinal() &&
-            getString() == ((ValueEnumBase) other).getString();
+            ordinal() == ((ValueEnumBase) other).ordinal();
     }
 
     public static ValueEnumBase get(final String label, final int ordinal) {

>From 02176ff1b1dd3d9fa1314056bbb9c2c216019a2d Mon Sep 17 00:00:00 2001
From: Max Englander <[email protected]>
Date: Thu, 30 Mar 2017 07:12:24 +0000
Subject: [PATCH 19/19] enum-support: add docs

---
 h2/src/docsrc/help/help.csv                        | 16 ++++++++-
 h2/src/docsrc/html/changelog.html                  |  2 ++
 h2/src/main/org/h2/res/help.csv                    |  6 +++-
 h2/src/main/org/h2/value/ValueEnum.java            | 26 ++++++++++++--
 h2/src/main/org/h2/value/ValueEnumBase.java        | 27 ++++++++++-----
 .../org/h2/test/jdbc/TestPreparedStatement.java    |  2 +-
 h2/src/tools/org/h2/build/doc/dictionary.txt       | 40 +++++++++++-----------
 7 files changed, 85 insertions(+), 34 deletions(-)

diff --git a/h2/src/docsrc/help/help.csv b/h2/src/docsrc/help/help.csv
index f05ec2f..47a0d5b 100644
--- a/h2/src/docsrc/help/help.csv
+++ b/h2/src/docsrc/help/help.csv
@@ -1921,7 +1921,7 @@ CALL CSVWRITE('test2.csv', 'SELECT * FROM TEST', 'charset=UTF-8 fieldSeparator=|
 intType | booleanType | tinyintType | smallintType | bigintType | identityType
     | decimalType | doubleType | realType | dateType | timeType | timestampType
     | binaryType | otherType | varcharType | varcharIgnorecaseType | charType
-    | blobType | clobType | uuidType | arrayType
+    | blobType | clobType | uuidType | arrayType | enumType
 ","
 A data type definition.
 ","
@@ -2509,6 +2509,20 @@ and ""ResultSet.getObject(..)"" or ""ResultSet.getArray(..)"" to retrieve the va
 ARRAY
 "
 
+"Data Types","ENUM Type","
+{ ENUM (string [, ... ]) }
+","
+A type with enumerated values.
+Mapped to ""java.lang.Integer"".
+
+The first provided value is mapped to 0, the
+second mapped to 1, and so on.
+
+Duplicate and empty values are not permitted.
+","
+
+ENUM('clubs', 'diamonds', 'hearts', 'spades')
+"
 "Data Types","GEOMETRY Type","
 GEOMETRY
 ","
diff --git a/h2/src/docsrc/html/changelog.html b/h2/src/docsrc/html/changelog.html
index 9d5a2ee..1743076 100644
--- a/h2/src/docsrc/html/changelog.html
+++ b/h2/src/docsrc/html/changelog.html
@@ -25,6 +25,8 @@
 </li>
 <li>Added support for invisible columns.
 </li>
+<li>Added an ENUM data type, with syntax similar to that of MySQL and Oracle.
+</li>
 </ul>
 
 <h2>Version 1.4.194 (2017-03-10)</h2>
diff --git a/h2/src/main/org/h2/res/help.csv b/h2/src/main/org/h2/res/help.csv
index ceffc2d..728afd4 100644
--- a/h2/src/main/org/h2/res/help.csv
+++ b/h2/src/main/org/h2/res/help.csv
@@ -606,7 +606,7 @@ Optional parameters for CSVREAD and CSVWRITE."
 intType | booleanType | tinyintType | smallintType | bigintType | identityType
     | decimalType | doubleType | realType | dateType | timeType | timestampType
     | binaryType | otherType | varcharType | varcharIgnorecaseType | charType
-    | blobType | clobType | uuidType | arrayType
+    | blobType | clobType | uuidType | arrayType | enumType
 ","
 A data type definition."
 "Other Grammar","Date","
@@ -834,6 +834,10 @@ Universally unique identifier."
 ARRAY
 ","
 An array of values."
+"Data Types","ENUM Type","
+{ ENUM (string [, ... ]) }
+","
+A type with enumerated values."
 "Data Types","GEOMETRY Type","
 GEOMETRY
 ","
diff --git a/h2/src/main/org/h2/value/ValueEnum.java b/h2/src/main/org/h2/value/ValueEnum.java
index 491eab5..a348848 100644
--- a/h2/src/main/org/h2/value/ValueEnum.java
+++ b/h2/src/main/org/h2/value/ValueEnum.java
@@ -22,6 +22,12 @@ private ValueEnum(final String[] enumerators, final int ordinal) {
         this.enumerators = enumerators;
     }
 
+    /**
+     * Check for any violations, such as empty
+     * values, duplicate values.
+     *
+     * @param enumerators the enumerators
+     */
     public static final void check(final String[] enumerators) {
         switch (validate(enumerators)) {
             case VALID:
@@ -52,9 +58,17 @@ private static final void check(final String[] enumerators, final Value value) {
     @Override
     protected int compareSecure(final Value v, final CompareMode mode) {
         final ValueEnum ev = ValueEnum.get(enumerators, v);
-        return MathUtils.compareInt(ordinal(), ev.ordinal());
+        return MathUtils.compareInt(getInt(), ev.getInt());
     }
 
+    /**
+     * Create an ENUM value from the provided enumerators
+     * and value.
+     *
+     * @param enumerators the enumerators
+     * @param value a value
+     * @return the ENUM value
+     */
     public static ValueEnum get(final String[] enumerators, final Value value) {
         check(enumerators, value);
 
@@ -76,6 +90,14 @@ public static ValueEnum get(final String[] enumerators, final Value value) {
         return enumerators;
     }
 
+    /**
+     * Evaluates whether a valid ENUM can be constructed
+     * from the provided enumerators and value.
+     *
+     * @param enumerators the enumerators
+     * @param value the value
+     * @return whether a valid ENUM can be constructed from the provided values
+     */
     public static boolean isValid(final String enumerators[], final Value value) {
         return validate(enumerators, value).equals(Validation.VALID);
     }
@@ -84,7 +106,7 @@ private static String sanitize(final String label) {
         return label == null ? null : label.trim().toUpperCase(Locale.ENGLISH);
     }
 
-    public static String[] sanitize(final String[] enumerators) {
+    private static String[] sanitize(final String[] enumerators) {
         if (enumerators == null || enumerators.length == 0) return null;
 
         final String[] clean = new String[enumerators.length];
diff --git a/h2/src/main/org/h2/value/ValueEnumBase.java b/h2/src/main/org/h2/value/ValueEnumBase.java
index 9e4c620..40eb542 100644
--- a/h2/src/main/org/h2/value/ValueEnumBase.java
+++ b/h2/src/main/org/h2/value/ValueEnumBase.java
@@ -7,9 +7,15 @@
 import org.h2.message.DbException;
 import org.h2.util.MathUtils;
 
+/**
+ * Base implementation of the ENUM data type.
+ *
+ * Currently, this class is used primarily for
+ * client-server communication.
+ */
 public class ValueEnumBase extends Value {
-    public static final int PRECISION = 10;
-    public static final int DISPLAY_SIZE = 11;
+    private static final int PRECISION = 10;
+    private static final int DISPLAY_SIZE = 11;
 
     private final String label;
     private final int ordinal;
@@ -27,7 +33,7 @@ public Value add(final Value v) {
 
     @Override
     protected int compareSecure(final Value v, final CompareMode mode) {
-        return MathUtils.compareInt(ordinal(), v.getInt());
+        return MathUtils.compareInt(getInt(), v.getInt());
     }
 
     @Override
@@ -39,9 +45,16 @@ public Value divide(final Value v) {
     @Override
     public boolean equals(final Object other) {
         return other instanceof ValueEnumBase &&
-            ordinal() == ((ValueEnumBase) other).ordinal();
+            getInt() == ((ValueEnumBase) other).getInt();
     }
 
+    /**
+     * Get or create an enum value with the given label and ordinal.
+     *
+     * @param label the label
+     * @param ordinal the ordinal
+     * @return the value
+     */
     public static ValueEnumBase get(final String label, final int ordinal) {
         return new ValueEnumBase(label, ordinal);
     }
@@ -95,7 +108,7 @@ public int getType() {
     public int hashCode() {
         int results = 31;
         results += getString().hashCode();
-        results += ordinal();
+        results += getInt();
         return results;
     }
 
@@ -112,10 +125,6 @@ public Value multiply(final Value v) {
     }
 
 
-    protected int ordinal() {
-        return ordinal;
-    }
-
     @Override
     public void set(final PreparedStatement prep, final int parameterIndex)
             throws SQLException {
diff --git a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
index b213f0d..708d7f1 100644
--- a/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
+++ b/h2/src/test/org/h2/test/jdbc/TestPreparedStatement.java
@@ -448,7 +448,7 @@ private void testEnum(Connection conn) throws SQLException {
         Statement stat = conn.createStatement();
         stat.execute("CREATE TABLE test_enum(size ENUM('small', 'medium', 'large'))");
 
-        String[] badSizes = new String[]{"green", "smalll", "0"};
+        String[] badSizes = new String[]{"green", "smol", "0"};
         for (int i = 0; i < badSizes.length; i++) {
             PreparedStatement prep = conn.prepareStatement(
                     "INSERT INTO test_enum VALUES(?)");
diff --git a/h2/src/tools/org/h2/build/doc/dictionary.txt b/h2/src/tools/org/h2/build/doc/dictionary.txt
index 8d2a608..9fa4095 100644
--- a/h2/src/tools/org/h2/build/doc/dictionary.txt
+++ b/h2/src/tools/org/h2/build/doc/dictionary.txt
@@ -76,7 +76,7 @@ calculation calculations calendar calendars call callable callback callbacks
 called caller calling calls cally caload came camel can cancel canceled canceling
 cancellation cancelled cancels candidates cannot canonical cap capabilities
 capability capacity capitalization capitalize capitalized capone caps capture
-captured car cardinal cardinality care careful carriage carrier cars cartesian
+captured car card cardinal cardinality care careful carriage carrier cars cartesian
 cascade cascading case cases casesensitive casewhen cash casing casqueiro cast
 casting castore cat catalina catalog catalogs cataloguing catch catcher catches
 catching category catlog caucho caught cause caused causes causing cavestro
@@ -181,8 +181,8 @@ detailed details detect detected detecting detection detector detects determine
 determining deterministic detrimental deusen deutsch dev develop developed
 developer developers developing development devenish deviation device devices
 dfile dgenerate dgroup dhe dhis diabetes diagnostic diagnostics diagram diagrams
-dialect dialog diams dick dictionary did didn died dieguez diehard dies diff
-differ difference differences different differential differentiate differently
+dialect dialog diamonds diams dick dictionary did didn died dieguez diehard dies
+diff differ difference differences different differential differentiate differently
 differs dig digest digit digital digits diligence dim dimension dimensional
 dimensions dimitrijs dinamica dining dip dips dir direct direction directly
 directories directory directs dirname dirs dirty disable disabled
@@ -218,14 +218,14 @@ encrypting encryption encrypts end ended enderbury endif ending endings endless
 endlessly endorse ends enforce enforceability enforceable enforced engine engines
 english enhance enhanced enhancement enhancer enlarge enough enqueued ensp ensure
 ensures ensuring enter entered entering enterprise entire entities entity entries
-entry enum enumerate enumeration env envelope environment environments enwiki eof
-eol epl epoch epoll epsilon equal equality equally equals equipment equitable
-equiv equivalent equivalents era erable eremainder eric erik err error errorlevel
-errors erwan ery esc escape escaped escapes escaping escargots ese espa essential
-essentials established estimate estimated estimates estimating estimation
-estoppel eta etc eth etl euml euro europe europeu euros eva eval evaluatable
-evaluate evaluated evaluates evaluating evaluation evdokimov even evenly event
-events eventually ever every everybody everyone everything everywhere evict
+entry enum enumerate enumerated enumerator enumerators enumeration env envelope
+environment environments enwiki eof eol epl epoch epoll epsilon equal equality equally
+equals equipment equitable equiv equivalent equivalents era erable eremainder eric
+erik err error errorlevel errors erwan ery esc escape escaped escapes escaping
+escargots ese espa essential essentials established estimate estimated estimates
+estimating estimation estoppel eta etc eth etl euml euro europe europeu euros eva eval
+evaluatable evaluate evaluated evaluates evaluating evaluation evdokimov even evenly
+event events eventually ever every everybody everyone everything everywhere evict
 evicted eviction evolving exact exactly example examples exceed exceeded exceeds
 excel except exception exceptions exchange exclude excluded excludes excluding
 exclusion exclusive exclusively exe exec executable executables execute executed
@@ -277,7 +277,7 @@ google googlegroups got goto goubard governed governing government gpg grabbing
 graceful graf grails grained grains grajcar grammar grammars grandin grandma
 grant grantable granted grantedrole grantee granteetype granting grantor grants
 granularity graph graphic graphical graphics grass gray great greater greatest
-greatly gredler greece greedy gregorian grep grew grid gridwidth gridx gridy
+greatly gredler greece greedy green gregorian grep grew grid gridwidth gridx gridy
 groove groovy gross group grouped grouping groups groupware grover grow growing
 grows growth guarantee guaranteed guard guardian guess guesses guest gui guid
 guide guidelines guides gumbo gustav gutierrez gzip hack had haidinyak half hallo
@@ -485,9 +485,9 @@ predict predicted prediction prefer preferable preferdoslikelineends preferences
 preferred prefix prefixes prefs premain premature prep prepare prepared prepares
 preparing prepended prepending pres presence present presentation preserve
 preserved preserving press pressed pretty prev prevent prevented prevention
-prevents previous previously pri price prices primary prime primitive primitives
-principal print printable printed printf printing println prints prio prior
-prioritize priority private privilege privileges pro prob probability probable
+prevents previous previously pri price prices primarily primary prime primitive
+primitives principal print printable printed printf printing println prints prio
+prior prioritize priority private privilege privileges pro prob probability probable
 probably problem problematic problems proc procedural procedure procedures
 proceed process processed processes processing processor processors procurement
 prod produce produced produces product production products prof profile profiler
@@ -558,7 +558,7 @@ routines row rowcount rowid rowlock rownum rows rowscn rowsize roy royalty rpad
 rsaquo rsquo rss rtree rtrim ruby ruebezahl rule rules run rund rundll runnable
 runner runners running runs runscript runtime rwd rws sabine safari safe safely
 safes safety said sainsbury salary sale sales saload salt salz sam same
-sameorigin samp sample samples sanity sans sastore sat satisfy saturday sauce
+sameorigin samp sample samples sanitize sanity sans sastore sat satisfy saturday sauce
 sauerkraut sava save saved savepoint savepoints saves saving savings say saying
 says sbquo scala scalability scalable scalar scale scaled scales scan scanned
 scanner scanners scanning scans scapegoat scc schedule scheduler schem schema
@@ -591,7 +591,7 @@ sites situation situations six sixty size sized sizeof sizes sizing skeletons sk
 skiing skill skip skipped skipping skips sky slash slashdot slashes slave sleep
 sleeping sleeps slept slice sliced slight slightly slist slot slots slovensky
 slow slowed slower slowest slowing slowly slows small smalldatetime smaller
-smallest smallint smart smith smtp smtps smuggled snapshot snapshots snipped
+smallest smallint smart smith smol smtp smtps smuggled snake snapshot snapshots snipped
 snippet snippets soap social socket sockets soerensen soffice soft software sold
 sole solely solid solo solution solutions solve solved solves solving some
 somebody somehow someone something sometime sometimes somewhat somewhere song
@@ -621,8 +621,8 @@ subscribe subselect subsequent subsequently subset substance substitute
 substituted substitution substr substring substrings substructure subsystem
 subtract subtracted subtracting subtraction subversion succeed succeeded succeeds
 success successful successfully succession such suddenly sudo sue sufficient
-sufficiently suffix sugar suggest suggested suggestion suggestions suitable suite
-suites sullivan sum summand summary summer summertime sums sun sunday sup supe
+sufficiently suffix sugar suggest suggested suggestion suggestions suit suitable
+suite suites sullivan sum summand summary summer summertime sums sun sunday sup supe
 super superclass superfluous superinterfaces superior superseded supertable
 superuser supplemental supplied supplier supply support supported supporter
 supporters supporting supports supposed suppress sure surname surrogate
@@ -666,7 +666,7 @@ trick tricky tried tries trig trigger triggered triggers trigonometric trim
 trimmed trims trip trivial trouble true trunc truncate truncated truncates
 truncating truncation trunk trust trusted trx try trying tsi tsmsys tsv tucc
 tucker tuesday tune tunes tuning turkel turkish turn turned turns tutorial tweak
-tweaking tweet twelve twice twitter two txt tymczak type typeof types typesafe
+tweaking tweet twelve twice twitter two txt tymczak type typed typeof types typesafe
 typical typically typing typlen typname typo typos typtypmod tzd tzh tzm tzr
 uacute uarr ubuntu ucase ucb ucirc ucs udt udts uffff ugly ugrave uid uint ujint
 ujlong ulimit uml umlaut umr unable unaligned unary unavailability unbound

Reply via email to