提交 fc102859 authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #451 from maxenglander/enum-support

Enum support
......@@ -1939,7 +1939,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.
","
......@@ -2527,6 +2527,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
","
......
......@@ -25,6 +25,8 @@ Change Log
</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>
......
......@@ -9,6 +9,10 @@
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}
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}
......
......@@ -159,6 +159,65 @@ public class ErrorCode {
*/
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-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(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 one or more of its enumerators would be empty.
*
* 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 ENUM_DUPLICATE = 22033;
// 23: constraint violation
/**
......
......@@ -146,6 +146,7 @@ import org.h2.value.ValueBoolean;
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,6 +4128,7 @@ public class Parser {
}
long precision = -1;
int displaySize = -1;
String[] enumerators = null;
int scale = -1;
String comment = null;
Column templateColumn = null;
......@@ -4200,6 +4202,28 @@ public class Parser {
}
read(")");
}
} else if (dataType.enumerated) {
if (readIf("(")) {
java.util.List<String> enumeratorList = new ArrayList<String>();
original += '(';
String enumerator0 = readString();
enumeratorList.add(enumerator0);
original += "'" + enumerator0 + "'";
while(readIf(",")) {
original += ',';
String enumeratorN = readString();
original += "'" + enumeratorN + "'";
enumeratorList.add(enumeratorN);
}
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.
......@@ -4220,8 +4244,10 @@ public class Parser {
throw DbException.get(ErrorCode.INVALID_VALUE_SCALE_PRECISION,
Integer.toString(scale), Long.toString(precision));
}
Column column = new Column(columnName, type, precision, scale,
displaySize);
displaySize, enumerators);
if (templateColumn != null) {
column.setNullable(templateColumn.isNullable());
column.setDefaultExpression(session,
......
......@@ -16,6 +16,7 @@ import org.h2.engine.Database;
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 @@ public class CreateTable extends SchemaCommand {
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);
}
}
......
......@@ -22,6 +22,7 @@ import org.h2.table.Table;
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,9 @@ public class ExpressionColumn extends Expression {
columnResolver.getValue(column);
throw DbException.get(ErrorCode.MUST_GROUP_BY_COLUMN_1, getSQL());
}
if (column.getEnumerators() != null) {
return ValueEnum.get(column.getEnumerators(), value);
}
return value;
}
......
......@@ -201,6 +201,7 @@ public class ValueDataType implements DataType {
case Value.SHORT:
buff.put((byte) type).putShort(v.getShort());
break;
case Value.ENUM:
case Value.INT: {
int x = v.getInt();
if (x < 0) {
......
......@@ -9,6 +9,10 @@
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}
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}
......
......@@ -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}
......
......@@ -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
","
......
......@@ -6,6 +6,7 @@
package org.h2.table;
import java.sql.ResultSetMetaData;
import java.util.Arrays;
import org.h2.api.ErrorCode;
import org.h2.command.Parser;
import org.h2.engine.Constants;
......@@ -26,6 +27,7 @@ import org.h2.util.StringUtils;
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;
......@@ -66,6 +68,7 @@ public class Column {
private final int type;
private long precision;
private int scale;
private String[] enumerators;
private int displaySize;
private Table table;
private String name;
......@@ -89,11 +92,20 @@ public class Column {
private boolean visible = true;
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, String[] enumerators) {
this(name, type, -1, -1, -1, enumerators);
}
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, String[] enumerators) {
this.name = name;
this.type = type;
if (precision == -1 && scale == -1 && displaySize == -1 && type != Value.UNKNOWN) {
......@@ -105,6 +117,7 @@ public class Column {
this.precision = precision;
this.scale = scale;
this.displaySize = displaySize;
this.enumerators = enumerators;
}
@Override
......@@ -133,8 +146,12 @@ public class Column {
return table.getId() ^ name.hashCode();
}
public boolean isEnumerated() {
return type == Value.ENUM;
}
public Column getClone() {
Column newColumn = new Column(name, type, precision, scale, displaySize);
Column newColumn = new Column(name, type, precision, scale, displaySize, enumerators);
newColumn.copy(this);
return newColumn;
}
......@@ -258,6 +275,14 @@ public class Column {
nullable = b;
}
public String[] getEnumerators() {
return enumerators;
}
public void setEnumerators(String[] enumerators) {
this.enumerators = enumerators;
}
public boolean getVisible() {
return visible;
}
......@@ -345,6 +370,18 @@ public class Column {
getCreateSQL(), s + " (" + value.getPrecision() + ")");
}
}
if (isEnumerated()) {
if (!ValueEnum.isValid(enumerators, value)) {
String s = value.getTraceSQL();
if (s.length() > 127) {
s = s.substring(0, 128) + "...";
}
throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED_1,
getCreateSQL(), s);
}
value = ValueEnum.get(enumerators, value);
}
updateSequenceIfRequired(session, value);
return value;
}
......@@ -434,6 +471,15 @@ public class Column {
case Value.DECIMAL:
buff.append('(').append(precision).append(", ").append(scale).append(')');
break;
case Value.ENUM:
buff.append('(');
for (int i = 0; i < enumerators.length; i++) {
buff.append('\'').append(enumerators[i]).append('\'');
if(i < enumerators.length - 1) {
buff.append(',');
}
}
buff.append(')');
case Value.BYTES:
case Value.STRING:
case Value.STRING_IGNORECASE:
......@@ -748,6 +794,8 @@ public class Column {
displaySize = source.displaySize;
name = source.name;
precision = source.precision;
enumerators = source.enumerators == null ? null :
Arrays.copyOf(source.enumerators, source.enumerators.length);
scale = source.scale;
// table is not set
// columnId is not set
......
......@@ -140,6 +140,11 @@ public class DataType {
*/
public boolean caseSensitive;
/**
* If enumerated values are supported.
*/
public boolean enumerated;
/**
* If the precision parameter is supported.
*/
......@@ -385,6 +390,11 @@ public class DataType {
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);
}
......@@ -406,6 +416,7 @@ public class DataType {
dt.params = dataType.params;
dt.prefix = dataType.prefix;
dt.suffix = dataType.suffix;
dt.enumerated = dataType.enumerated;
dt.supportsPrecision = dataType.supportsPrecision;
dt.supportsScale = dataType.supportsScale;
dt.defaultPrecision = dataType.defaultPrecision;
......@@ -459,6 +470,13 @@ public class DataType {
return dataType;
}
private static DataType createEnum() {
DataType dataType = createString(false);
dataType.enumerated = true;
dataType.supportsPrecision = false;
dataType.supportsScale = false;
return dataType;
}
private static DataType createString(boolean caseSensitive) {
DataType dataType = new DataType();
dataType.prefix = "'";
......@@ -667,6 +685,12 @@ public class DataType {
v = ValueArray.get(values);
break;
}
case Value.ENUM: {
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);
if (x == null) {
......
......@@ -494,6 +494,11 @@ public class Transfer {
}
break;
}
case Value.ENUM: {
writeInt(v.getInt());
writeString(v.getString());
break;
}
case Value.RESULT_SET: {
try {
ResultSet rs = ((ValueResultSet) v).getResultSet();
......@@ -594,6 +599,11 @@ public class Transfer {
return ValueDouble.get(readDouble());
case Value.FLOAT:
return ValueFloat.get(readFloat());
case Value.ENUM: {
final int ordinal = readInt();
final String label = readString();
return ValueEnumBase.get(label, ordinal);
}
case Value.INT:
return ValueInt.get(readInt());
case Value.LONG:
......
......@@ -168,10 +168,15 @@ public abstract class Value {
*/
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;
public static final int TYPE_COUNT = ENUM;
private static SoftReference<Value[]> softCache =
new SoftReference<Value[]>(null);
......@@ -323,6 +328,8 @@ public abstract class Value {
return 50_000;
case RESULT_SET:
return 51_000;
case ENUM:
return 52_000;
default:
if (JdbcUtils.customDataTypesHandler != null) {
return JdbcUtils.customDataTypesHandler.getDataTypeOrder(type);
......@@ -618,6 +625,8 @@ public abstract class Value {
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:
......@@ -849,6 +858,13 @@ public abstract class Value {
}
break;
}
case ENUM: {
switch (getType()) {
case INT:
case STRING:
return this;
}
}
case BLOB: {
switch (getType()) {
case BYTES:
......@@ -940,6 +956,7 @@ public abstract class Value {
case JAVA_OBJECT:
return ValueJavaObject.getNoCopy(null,
StringUtils.convertHexToBytes(s.trim()), getDataHandler());
case ENUM:
case STRING:
return ValueString.get(s);
case STRING_IGNORECASE:
......
package org.h2.value;
import java.util.Locale;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
import org.h2.value.DataType;
public class ValueEnum extends ValueEnumBase {
private static enum Validation {
DUPLICATE,
EMPTY,
INVALID,
VALID
}
private final String[] enumerators;
private ValueEnum(final String[] enumerators, final int ordinal) {
super(enumerators[ordinal], 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:
return;
case EMPTY:
throw DbException.get(ErrorCode.ENUM_EMPTY);
case DUPLICATE:
throw DbException.get(ErrorCode.ENUM_DUPLICATE,
toString(enumerators));
default:
throw DbException.get(ErrorCode.INVALID_VALUE_2,
toString(enumerators));
}
}
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.ENUM_VALUE_NOT_PERMITTED_2,
toString(enumerators), value.toString());
}
}
@Override
protected int compareSecure(final Value v, final CompareMode mode) {
final ValueEnum ev = ValueEnum.get(enumerators, v);
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);
if (DataType.isStringType(value.getType())) {
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 new ValueEnum(enumerators, value.getInt());
}
}
public String[] getEnumerators() {
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);
}
private static String sanitize(final String label) {
return label == null ? null : label.trim().toUpperCase(Locale.ENGLISH);
}
private static String[] sanitize(final String[] enumerators) {
if (enumerators == null || enumerators.length == 0) return null;
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) {
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[] cleaned = sanitize(enumerators);
if (cleaned == null || cleaned.length == 0) {
return Validation.EMPTY;
}
for (int i = 0; i < cleaned.length; i++) {
if (cleaned[i] == null || cleaned[i].equals("")) {
return Validation.EMPTY;
}
if (i < cleaned.length - 1) {
for (int j = i + 1; j < cleaned.length; j++) {
if (cleaned[i].equals(cleaned[j])) {
return Validation.DUPLICATE;
}
}
}
}
return Validation.VALID;
}
private static Validation validate(final String[] enumerators, final Value value) {
final Validation validation = validate(enumerators);
if (!validation.equals(Validation.VALID)) {
return validation;
}
if (DataType.isStringType(value.getType())) {
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 {
final int ordinal = value.getInt();
if (ordinal < 0 || ordinal >= enumerators.length) {
return Validation.INVALID;
}
return Validation.VALID;
}
}
}
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Locale;
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 {
private static final int PRECISION = 10;
private 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(getInt(), 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 &&
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);
}
@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 += getInt();
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);
}
@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);
}
}
......@@ -57,6 +57,7 @@ public class TestPreparedStatement extends TestBase {
testToString(conn);
testExecuteUpdateCall(conn);
testPrepareExecute(conn);
testEnum(conn);
testUUID(conn);
testUUIDAsJavaObject(conn);
testScopedGeneratedKey(conn);
......@@ -443,6 +444,37 @@ public class TestPreparedStatement extends TestBase {
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", "smol", "0"};
for (int i = 0; i < badSizes.length; i++) {
PreparedStatement prep = conn.prepareStatement(
"INSERT INTO test_enum VALUES(?)");
prep.setObject(1, badSizes[i]);
assertThrows(ErrorCode.ENUM_VALUE_NOT_PERMITTED_1, 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));
assertEquals(i, rs.getInt(1));
Object o = rs.getObject(1);
assertEquals(Integer.class, o.getClass());
}
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)");
......
......@@ -10589,3 +10589,75 @@ create table z.z (id int);
drop schema z;
> ok
----------------
--- 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
> ---- ------
> 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
--- 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
......@@ -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
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论