Skip to content

Commit fffcad4

Browse files
BillyYcccvietj
authored andcommitted
MySQL client data type mapping rework - fixes #1239
1 parent f6d1376 commit fffcad4

File tree

9 files changed

+427
-195
lines changed

9 files changed

+427
-195
lines changed

vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/CommandCodec.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,17 @@ ColumnDefinition decodeColumnDefinitionPacketPayload(ByteBuf payload) {
176176
int characterSet = payload.getUnsignedShortLE(start + bytesToSkip);
177177
bytesToSkip += 6; // characterSet + columnLength
178178

179-
DataType type = DataType.valueOf(payload.getUnsignedByte(start + bytesToSkip));
179+
short type = payload.getUnsignedByte(start + bytesToSkip);
180180
bytesToSkip++;
181181

182182
int flags = payload.getUnsignedShortLE(start + bytesToSkip);
183183
bytesToSkip += 2; // flags + decimals
184184

185185
payload.skipBytes(bytesToSkip);
186186

187-
return new ColumnDefinition(name, characterSet, type, flags);
187+
// convert type+characterset+flags to dataType
188+
DataType dataType = DataType.parseDataType(type, characterSet, flags);
189+
return new ColumnDefinition(name, characterSet, dataType, flags);
188190
}
189191

190192
void skipEofPacketIfNeeded(ByteBuf payload) {

vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/ExtendedQueryCommandBaseCodec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ protected final void sendStatementExecuteCommand(MySQLPreparedStatement statemen
9898

9999
if (sendTypesToServer) {
100100
for (DataType bindingType : statement.bindingTypes()) {
101-
packet.writeByte(bindingType.id);
101+
packet.writeByte(bindingType.getColumnType());
102102
packet.writeByte(0); // parameter flag: signed
103103
}
104104
}

vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/RowResultDecoder.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ protected Row decodeRow(int len, ByteBuf in) {
5757
ColumnDefinition columnDef = rowDesc.get(c);
5858
DataType dataType = columnDef.type();
5959
int collationId = columnDef.characterSet();
60-
int columnDefinitionFlags = columnDef.flags();
61-
decoded = DataTypeCodec.decodeBinary(dataType, collationId, columnDefinitionFlags, in);
60+
decoded = DataTypeCodec.decodeBinary(dataType, collationId, in);
6261
}
6362
row.addValue(decoded);
6463
}
@@ -71,9 +70,8 @@ protected Row decodeRow(int len, ByteBuf in) {
7170
} else {
7271
ColumnDefinition columnDef = rowDesc.get(c);
7372
DataType dataType = columnDef.type();
74-
int columnDefinitionFlags = columnDef.flags();
7573
int collationId = columnDef.characterSet();
76-
decoded = DataTypeCodec.decodeText(dataType, collationId, columnDefinitionFlags, in);
74+
decoded = DataTypeCodec.decodeText(dataType, collationId, in);
7775
}
7876
row.addValue(decoded);
7977
}

vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/datatype/DataType.java

Lines changed: 129 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,75 +11,161 @@
1111

1212
package io.vertx.mysqlclient.impl.datatype;
1313

14-
import io.netty.util.collection.IntObjectHashMap;
15-
import io.netty.util.collection.IntObjectMap;
14+
import io.netty.util.collection.ShortObjectHashMap;
15+
import io.netty.util.collection.ShortObjectMap;
1616
import io.vertx.core.buffer.Buffer;
1717
import io.vertx.core.impl.logging.Logger;
1818
import io.vertx.core.impl.logging.LoggerFactory;
1919
import io.vertx.mysqlclient.data.spatial.Geometry;
20+
import io.vertx.mysqlclient.impl.MySQLCollation;
2021
import io.vertx.mysqlclient.impl.protocol.ColumnDefinition;
2122
import io.vertx.sqlclient.data.Numeric;
2223

24+
import java.math.BigInteger;
2325
import java.sql.JDBCType;
2426
import java.time.Duration;
2527
import java.time.LocalDate;
2628
import java.time.LocalDateTime;
29+
import java.util.Arrays;
30+
import java.util.List;
2731

2832
public enum DataType {
29-
INT1(ColumnDefinition.ColumnType.MYSQL_TYPE_TINY, Byte.class, Byte.class, JDBCType.TINYINT),
30-
INT2(ColumnDefinition.ColumnType.MYSQL_TYPE_SHORT, Short.class, Short.class, JDBCType.SMALLINT),
31-
INT3(ColumnDefinition.ColumnType.MYSQL_TYPE_INT24, Integer.class, Integer.class, JDBCType.INTEGER),
32-
INT4(ColumnDefinition.ColumnType.MYSQL_TYPE_LONG, Integer.class, Integer.class, JDBCType.INTEGER),
33-
INT8(ColumnDefinition.ColumnType.MYSQL_TYPE_LONGLONG, Long.class, Long.class, JDBCType.BIGINT),
34-
DOUBLE(ColumnDefinition.ColumnType.MYSQL_TYPE_DOUBLE, Double.class, Double.class, JDBCType.DOUBLE),
35-
FLOAT(ColumnDefinition.ColumnType.MYSQL_TYPE_FLOAT, Float.class, Float.class, JDBCType.REAL),
36-
NUMERIC(ColumnDefinition.ColumnType.MYSQL_TYPE_NEWDECIMAL, Numeric.class, Numeric.class, JDBCType.DECIMAL), // DECIMAL
37-
STRING(ColumnDefinition.ColumnType.MYSQL_TYPE_STRING, Buffer.class, String.class, JDBCType.VARCHAR), // CHAR, BINARY
38-
VARSTRING(ColumnDefinition.ColumnType.MYSQL_TYPE_VAR_STRING, Buffer.class, String.class, JDBCType.VARCHAR), //VARCHAR, VARBINARY
39-
TINYBLOB(ColumnDefinition.ColumnType.MYSQL_TYPE_TINY_BLOB, Buffer.class, String.class, JDBCType.BLOB),
40-
BLOB(ColumnDefinition.ColumnType.MYSQL_TYPE_BLOB, Buffer.class, String.class, JDBCType.BLOB),
41-
MEDIUMBLOB(ColumnDefinition.ColumnType.MYSQL_TYPE_MEDIUM_BLOB, Buffer.class, String.class, JDBCType.BLOB),
42-
LONGBLOB(ColumnDefinition.ColumnType.MYSQL_TYPE_LONG_BLOB, Buffer.class, String.class, JDBCType.BLOB),
43-
DATE(ColumnDefinition.ColumnType.MYSQL_TYPE_DATE, LocalDate.class, LocalDate.class, JDBCType.DATE),
44-
TIME(ColumnDefinition.ColumnType.MYSQL_TYPE_TIME, Duration.class, Duration.class, JDBCType.TIME),
45-
DATETIME(ColumnDefinition.ColumnType.MYSQL_TYPE_DATETIME, LocalDateTime.class, LocalDateTime.class, JDBCType.TIMESTAMP),
46-
YEAR(ColumnDefinition.ColumnType.MYSQL_TYPE_YEAR, Short.class, Short.class, JDBCType.SMALLINT),
47-
TIMESTAMP(ColumnDefinition.ColumnType.MYSQL_TYPE_TIMESTAMP, LocalDateTime.class, LocalDateTime.class, JDBCType.TIMESTAMP),
48-
BIT(ColumnDefinition.ColumnType.MYSQL_TYPE_BIT, Long.class, Long.class, JDBCType.BIT),
49-
JSON(ColumnDefinition.ColumnType.MYSQL_TYPE_JSON, Object.class, Object.class, JDBCType.OTHER),
50-
GEOMETRY(ColumnDefinition.ColumnType.MYSQL_TYPE_GEOMETRY, Geometry.class, Geometry.class, JDBCType.OTHER),
51-
NULL(ColumnDefinition.ColumnType.MYSQL_TYPE_NULL, Object.class, Object.class, JDBCType.OTHER), // useful for mariadb prepare statement response
52-
UNBIND(-1, Object.class, Object.class, JDBCType.OTHER); // useful for binding param values
33+
34+
// unsigned int
35+
U_INT8(ColumnDefinition.ColumnType.MYSQL_TYPE_TINY, Short.class, JDBCType.SMALLINT),
36+
U_INT16(ColumnDefinition.ColumnType.MYSQL_TYPE_SHORT, Integer.class, JDBCType.INTEGER),
37+
U_INT24(ColumnDefinition.ColumnType.MYSQL_TYPE_INT24, Integer.class, JDBCType.INTEGER),
38+
U_INT32(ColumnDefinition.ColumnType.MYSQL_TYPE_LONG, Long.class, JDBCType.BIGINT),
39+
U_INT64(ColumnDefinition.ColumnType.MYSQL_TYPE_LONGLONG, BigInteger.class, JDBCType.NUMERIC),
40+
// signed int
41+
INT8(ColumnDefinition.ColumnType.MYSQL_TYPE_TINY, Byte.class, JDBCType.TINYINT),
42+
INT16(ColumnDefinition.ColumnType.MYSQL_TYPE_SHORT, Short.class, JDBCType.SMALLINT),
43+
INT24(ColumnDefinition.ColumnType.MYSQL_TYPE_INT24, Integer.class, JDBCType.INTEGER),
44+
INT32(ColumnDefinition.ColumnType.MYSQL_TYPE_LONG, Integer.class, JDBCType.INTEGER),
45+
INT64(ColumnDefinition.ColumnType.MYSQL_TYPE_LONGLONG, Long.class, JDBCType.BIGINT),
46+
// numeric
47+
BIT(ColumnDefinition.ColumnType.MYSQL_TYPE_BIT, Long.class, JDBCType.BIT),
48+
DOUBLE(ColumnDefinition.ColumnType.MYSQL_TYPE_DOUBLE, Double.class, JDBCType.DOUBLE),
49+
FLOAT(ColumnDefinition.ColumnType.MYSQL_TYPE_FLOAT, Float.class, JDBCType.FLOAT),
50+
NUMERIC(ColumnDefinition.ColumnType.MYSQL_TYPE_NEWDECIMAL, Numeric.class, JDBCType.DECIMAL),
51+
// string
52+
STRING(ColumnDefinition.ColumnType.MYSQL_TYPE_STRING, String.class, JDBCType.CHAR),
53+
VARSTRING(ColumnDefinition.ColumnType.MYSQL_TYPE_VAR_STRING, String.class, JDBCType.VARCHAR),
54+
// clob
55+
TINY_TEXT(ColumnDefinition.ColumnType.MYSQL_TYPE_TINY_BLOB, String.class, JDBCType.CLOB),
56+
TEXT(ColumnDefinition.ColumnType.MYSQL_TYPE_BLOB, String.class, JDBCType.CLOB),
57+
MEDIUM_TEXT(ColumnDefinition.ColumnType.MYSQL_TYPE_MEDIUM_BLOB, String.class, JDBCType.CLOB),
58+
LONG_TEXT(ColumnDefinition.ColumnType.MYSQL_TYPE_LONG_BLOB, String.class, JDBCType.CLOB),
59+
// binary
60+
BINARY(ColumnDefinition.ColumnType.MYSQL_TYPE_STRING, Buffer.class, JDBCType.BINARY),
61+
VARBINARY(ColumnDefinition.ColumnType.MYSQL_TYPE_VAR_STRING, Buffer.class, JDBCType.VARBINARY),
62+
// blob
63+
TINY_BLOB(ColumnDefinition.ColumnType.MYSQL_TYPE_TINY_BLOB, Buffer.class, JDBCType.BLOB),
64+
BLOB(ColumnDefinition.ColumnType.MYSQL_TYPE_BLOB, Buffer.class, JDBCType.BLOB),
65+
MEDIUM_BLOB(ColumnDefinition.ColumnType.MYSQL_TYPE_MEDIUM_BLOB, Buffer.class, JDBCType.BLOB),
66+
LONG_BLOB(ColumnDefinition.ColumnType.MYSQL_TYPE_LONG_BLOB, Buffer.class, JDBCType.BLOB),
67+
// time
68+
DATE(ColumnDefinition.ColumnType.MYSQL_TYPE_DATE, LocalDate.class, JDBCType.DATE),
69+
TIME(ColumnDefinition.ColumnType.MYSQL_TYPE_TIME, Duration.class, JDBCType.TIME),
70+
DATETIME(ColumnDefinition.ColumnType.MYSQL_TYPE_DATETIME, LocalDateTime.class, JDBCType.TIMESTAMP),
71+
YEAR(ColumnDefinition.ColumnType.MYSQL_TYPE_YEAR, Short.class, JDBCType.SMALLINT),
72+
TIMESTAMP(ColumnDefinition.ColumnType.MYSQL_TYPE_TIMESTAMP, LocalDateTime.class, JDBCType.TIMESTAMP),
73+
74+
JSON(ColumnDefinition.ColumnType.MYSQL_TYPE_JSON, Object.class, JDBCType.OTHER),
75+
GEOMETRY(ColumnDefinition.ColumnType.MYSQL_TYPE_GEOMETRY, Geometry.class, JDBCType.OTHER),
76+
NULL(ColumnDefinition.ColumnType.MYSQL_TYPE_NULL, Object.class, JDBCType.OTHER), // useful for mariadb prepare statement response
77+
UNBIND((short) -1, Object.class, JDBCType.OTHER); // useful for binding param values
78+
;
5379

5480
private static final Logger LOGGER = LoggerFactory.getLogger(DataType.class);
5581

56-
private static IntObjectMap<DataType> idToDataType = new IntObjectHashMap<>();
82+
// protocol id
83+
private final short columnType;
5784

58-
static {
59-
for (DataType dataType : values()) {
60-
idToDataType.put(dataType.id, dataType);
61-
}
62-
}
85+
private final Class<?> javaType;
6386

64-
public final int id;
65-
public final Class<?> binaryType;
66-
public final Class<?> textType;
67-
public final JDBCType jdbcType;
87+
private final JDBCType jdbcType;
6888

69-
DataType(int id, Class<?> binaryType, Class<?> textType, JDBCType jdbcType) {
70-
this.id = id;
71-
this.binaryType = binaryType;
72-
this.textType = textType;
89+
DataType(short columnType, Class<?> javaType, JDBCType jdbcType) {
90+
this.columnType = columnType;
91+
this.javaType = javaType;
7392
this.jdbcType = jdbcType;
7493
}
7594

76-
public static DataType valueOf(int value) {
77-
DataType dataType = idToDataType.get(value);
95+
public int getColumnType() {
96+
return columnType;
97+
}
98+
99+
public Class<?> getJavaType() {
100+
return javaType;
101+
}
102+
103+
public JDBCType getJdbcType() {
104+
return jdbcType;
105+
}
106+
107+
public static DataType parseDataType(short type, int characterSet, int flags) {
108+
// integer
109+
if (COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.containsKey(type)) {
110+
return isUnsignedNumeric(flags) ? COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.get(type).get(0) : COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.get(type).get(1);
111+
}
112+
113+
// string
114+
if (COLUMN_TYPE_TO_STRING_TYPE_MAPPING.containsKey(type)) {
115+
return isText(characterSet) ? COLUMN_TYPE_TO_STRING_TYPE_MAPPING.get(type).get(0) : COLUMN_TYPE_TO_STRING_TYPE_MAPPING.get(type).get(1);
116+
}
117+
118+
// others
119+
DataType dataType = COLUMN_TYPE_TO_DATA_TYPE_MAPPING.get(type);
78120
if (dataType == null) {
79-
LOGGER.warn(String.format("MySQL data type Id =[%d] not handled - using string type instead", value));
121+
LOGGER.warn(String.format("MySQL data type Id =[%d] not handled - using string type instead", type));
80122
return STRING;
81123
} else {
82124
return dataType;
83125
}
84126
}
127+
128+
private static final ShortObjectMap<List<DataType>> COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING;
129+
private static final ShortObjectMap<List<DataType>> COLUMN_TYPE_TO_STRING_TYPE_MAPPING;
130+
private static final ShortObjectMap<DataType> COLUMN_TYPE_TO_DATA_TYPE_MAPPING;
131+
132+
static {
133+
// integer
134+
// column type -> uint, int
135+
COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING = new ShortObjectHashMap<>(5, 1.0f);
136+
COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_TINY, Arrays.asList(U_INT8, INT8));
137+
COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_SHORT, Arrays.asList(U_INT16, INT16));
138+
COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_INT24, Arrays.asList(U_INT24, INT24));
139+
COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_LONG, Arrays.asList(U_INT32, INT32));
140+
COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_LONGLONG, Arrays.asList(U_INT64, INT64));
141+
142+
// string
143+
// column type -> clob, blob
144+
COLUMN_TYPE_TO_STRING_TYPE_MAPPING = new ShortObjectHashMap<>(6, 1.0f);
145+
COLUMN_TYPE_TO_STRING_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_STRING, Arrays.asList(STRING, BINARY));
146+
COLUMN_TYPE_TO_STRING_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_VAR_STRING, Arrays.asList(VARSTRING, VARBINARY));
147+
COLUMN_TYPE_TO_STRING_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_TINY_BLOB, Arrays.asList(TINY_TEXT, TINY_BLOB));
148+
COLUMN_TYPE_TO_STRING_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_BLOB, Arrays.asList(TEXT, BLOB));
149+
COLUMN_TYPE_TO_STRING_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_MEDIUM_BLOB, Arrays.asList(MEDIUM_TEXT, MEDIUM_BLOB));
150+
COLUMN_TYPE_TO_STRING_TYPE_MAPPING.put(ColumnDefinition.ColumnType.MYSQL_TYPE_LONG_BLOB, Arrays.asList(LONG_TEXT, LONG_BLOB));
151+
152+
// others
153+
COLUMN_TYPE_TO_DATA_TYPE_MAPPING = new ShortObjectHashMap<>(13, 1.0f);
154+
for (DataType dataType : DataType.values()) {
155+
if (COLUMN_TYPE_TO_INTEGER_TYPE_MAPPING.containsKey(dataType.columnType) || COLUMN_TYPE_TO_STRING_TYPE_MAPPING.containsKey(dataType.columnType)) {
156+
continue;
157+
}
158+
159+
COLUMN_TYPE_TO_DATA_TYPE_MAPPING.put(dataType.columnType, dataType);
160+
}
161+
}
162+
163+
private static boolean isText(int collationId) {
164+
return collationId != MySQLCollation.binary.collationId();
165+
}
166+
167+
private static boolean isUnsignedNumeric(int columnDefinitionFlags) {
168+
return (columnDefinitionFlags & ColumnDefinition.ColumnDefinitionFlags.UNSIGNED_FLAG) != 0;
169+
}
170+
85171
}

0 commit comments

Comments
 (0)