Skip to content

Commit ca6c8b8

Browse files
authored
Add support for mssql nbcrow & nullable datatypes (#628)
* add support for mssql nbcrow & nullable datatypes Signed-off-by: Billy Yuan <billy112487983@gmail.com> * add more variable data types support for mssql Signed-off-by: Billy Yuan <billy112487983@gmail.com> * remove not null constraint for mssql table tck so that it could be tested with null values Signed-off-by: Billy Yuan <billy112487983@gmail.com> * tweak mssql data type testing structure and decode tinyint as short Signed-off-by: Billy Yuan <billy112487983@gmail.com> * minors for documentation Signed-off-by: Billy Yuan <billy112487983@gmail.com>
1 parent f1fa258 commit ca6c8b8

20 files changed

+906
-59
lines changed

vertx-mssql-client/src/main/asciidoc/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ include::queries.adoc[]
9898

9999
Currently the client supports the following SQL Server types
100100

101-
* TINYINT(`java.lang.Byte`)
101+
* TINYINT(`java.lang.Short`)
102102
* SMALLINT(`java.lang.Short`)
103103
* INT(`java.lang.Integer`)
104104
* BIGINT(`java.lang.Long`)

vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/ExtendedQueryCommandCodec.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ void decodeMessage(TdsMessage message, TdsMessageEncoder encoder) {
5454
case DataPacketStreamTokenType.ROW_TOKEN:
5555
handleRow(messageBody);
5656
break;
57+
case DataPacketStreamTokenType.NBCROW_TOKEN:
58+
handleNbcRow(messageBody);
59+
break;
5760
case DataPacketStreamTokenType.DONE_TOKEN:
5861
messageBody.skipBytes(12); // this should only be after ERROR_TOKEN?
5962
handleDoneToken();
@@ -203,14 +206,7 @@ private void encodeParamValue(ByteBuf payload, Object value) {
203206
private void encodeNullParameter(ByteBuf payload) {
204207
payload.writeByte(0x00);
205208
payload.writeByte(0x00);
206-
payload.writeByte(MSSQLDataTypeId.NVARCHARTYPE_ID);
207-
payload.writeShortLE(8000); // maximal length
208-
payload.writeByte(0x09);
209-
payload.writeByte(0x04);
210-
payload.writeByte(0xd0);
211-
payload.writeByte(0x00);
212-
payload.writeByte(0x34); // Collation for param definitions TODO always this value?
213-
payload.writeShortLE(0xFFFF);
209+
payload.writeByte(MSSQLDataTypeId.NULLTYPE_ID);
214210
}
215211

216212
private void encodeIntNParameter(ByteBuf payload, int n, Object value) {

vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/MSSQLDataTypeCodec.java

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@
1111

1212
package io.vertx.mssqlclient.impl.codec;
1313

14-
import io.vertx.mssqlclient.impl.protocol.datatype.MSSQLDataType;
15-
import io.vertx.mssqlclient.impl.protocol.datatype.MSSQLDataTypeId;
16-
import io.vertx.mssqlclient.impl.protocol.datatype.NumericDataType;
17-
import io.vertx.mssqlclient.impl.protocol.datatype.TimeNDataType;
14+
import io.vertx.mssqlclient.impl.protocol.datatype.*;
1815
import io.netty.buffer.ByteBuf;
1916
import io.vertx.sqlclient.data.Numeric;
2017

@@ -73,12 +70,18 @@ static Object decode(MSSQLDataType dataType, ByteBuf in) {
7370
case MSSQLDataTypeId.NUMERICNTYPE_ID:
7471
case MSSQLDataTypeId.DECIMALNTYPE_ID:
7572
return decodeNumeric((NumericDataType) dataType, in);
73+
case MSSQLDataTypeId.INTNTYPE_ID:
74+
return decodeIntN(in);
7675
case MSSQLDataTypeId.FLT4TYPE_ID:
7776
return decodeFloat4(in);
7877
case MSSQLDataTypeId.FLT8TYPE_ID:
7978
return decodeFloat8(in);
79+
case MSSQLDataTypeId.FLTNTYPE_ID:
80+
return decodeFltN(in);
8081
case MSSQLDataTypeId.BITTYPE_ID:
8182
return decodeBit(in);
83+
case MSSQLDataTypeId.BITNTYPE_ID:
84+
return decodeBitN(in);
8285
case MSSQLDataTypeId.DATENTYPE_ID:
8386
return decodeDateN(in);
8487
case MSSQLDataTypeId.TIMENTYPE_ID:
@@ -217,4 +220,51 @@ private static BigInteger readUnsignedInt128LE(ByteBuf buffer) {
217220
buffer.skipBytes(16);
218221
return new BigInteger(result);
219222
}
223+
224+
private static Object decodeIntN(ByteBuf buffer) {
225+
int intNDataTypeLength = buffer.readByte();
226+
switch (intNDataTypeLength) {
227+
case 0:
228+
// this means we read a NULL value(nullable data type).
229+
return null;
230+
case 1:
231+
return buffer.readUnsignedByte();
232+
case 2:
233+
return buffer.readShortLE();
234+
case 4:
235+
return buffer.readIntLE();
236+
case 8:
237+
return buffer.readLongLE();
238+
default:
239+
throw new UnsupportedOperationException(String.format("SEVERE: Unsupported length=[%d] for decoding IntNDataType row value.", intNDataTypeLength));
240+
}
241+
}
242+
243+
private static Object decodeFltN(ByteBuf buffer) {
244+
int fltNDataTypeLength = buffer.readByte();
245+
switch (fltNDataTypeLength) {
246+
case 0:
247+
// this means we read a NULL value(nullable data type).
248+
return null;
249+
case 4:
250+
return buffer.readFloatLE();
251+
case 8:
252+
return buffer.readDoubleLE();
253+
default:
254+
throw new UnsupportedOperationException(String.format("SEVERE: Unsupported length=[%d] for decoding FLTNTYPE row value.", fltNDataTypeLength));
255+
}
256+
}
257+
258+
private static Object decodeBitN(ByteBuf buffer) {
259+
int bitNDataTypeLength = buffer.readByte();
260+
switch (bitNDataTypeLength) {
261+
case 0:
262+
// this means we read a NULL value(nullable data type).
263+
return null;
264+
case 1:
265+
return buffer.readBoolean();
266+
default:
267+
throw new UnsupportedOperationException(String.format("SEVERE: Unsupported length=[%d] for decoding BITNTYPE row value.", bitNDataTypeLength));
268+
}
269+
}
220270
}

vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/QueryCommandBaseCodec.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ protected void handleRow(ByteBuf payload) {
6060
rowResultDecoder.handleRow(rowResultDecoder.desc.columnDatas.length, payload);
6161
}
6262

63+
protected void handleNbcRow(ByteBuf payload) {
64+
rowResultDecoder.handleNbcRow(rowResultDecoder.desc.columnDatas.length, payload);
65+
}
66+
6367
protected void handleResultSetDone(int affectedRows) {
6468
this.result = false;
6569
T result;
@@ -110,6 +114,15 @@ private MSSQLDataType decodeDataTypeMetadata(ByteBuf payload) {
110114
byte numericPrecision = payload.readByte();
111115
byte numericScale = payload.readByte();
112116
return new NumericDataType(NUMERICNTYPE_ID, Numeric.class, numericPrecision, numericScale);
117+
case INTNTYPE_ID:
118+
byte intNTypeLength = payload.readByte();
119+
return IntNDataType.valueOf(intNTypeLength);
120+
case FLTNTYPE_ID:
121+
byte fltNTypeLength = payload.readByte();
122+
return FloatNDataType.valueOf(fltNTypeLength);
123+
case BITNTYPE_ID:
124+
payload.skipBytes(1); // should only be 1
125+
return BitNDataType.BIT_1_DATA_TYPE;
113126
case DATENTYPE_ID:
114127
return FixedLenDataType.DATENTYPE;
115128
case TIMENTYPE_ID:

vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/RowResultDecoder.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,37 @@ class RowResultDecoder<C, R> extends RowDecoder<C, R> {
2222

2323
final MSSQLRowDesc desc;
2424

25+
private RowStreamTokenType handleType = RowStreamTokenType.ROW;
26+
2527
RowResultDecoder(Collector<Row, C, R> collector, MSSQLRowDesc desc) {
2628
super(collector);
2729
this.desc = desc;
2830
}
2931

3032
@Override
3133
public Row decodeRow(int len, ByteBuf in) {
34+
switch (handleType) {
35+
case ROW:
36+
return decodeMssqlRow(len, in);
37+
case NBCROW:
38+
return decodeMssqlNbcRow(len, in);
39+
default:
40+
throw new UnsupportedOperationException("Unknown row stream token type");
41+
}
42+
}
43+
44+
@Override
45+
public void handleRow(int len, ByteBuf in) {
46+
this.handleType = RowStreamTokenType.ROW;
47+
super.handleRow(len, in);
48+
}
49+
50+
public void handleNbcRow(int len, ByteBuf in) {
51+
this.handleType = RowStreamTokenType.NBCROW;
52+
super.handleRow(len, in);
53+
}
54+
55+
private Row decodeMssqlRow(int len, ByteBuf in) {
3256
Row row = new MSSQLRowImpl(desc);
3357
for (int c = 0; c < len; c++) {
3458
Object decoded = null;
@@ -38,4 +62,30 @@ public Row decodeRow(int len, ByteBuf in) {
3862
}
3963
return row;
4064
}
65+
66+
private Row decodeMssqlNbcRow(int len, ByteBuf in) {
67+
Row row = new MSSQLRowImpl(desc);
68+
int nullBitmapByteCount = (len >> 3) + 1;
69+
int nullBitMapStartIdx = in.readerIndex();
70+
in.skipBytes(nullBitmapByteCount);
71+
72+
for (int c = 0; c < len; c++) {
73+
int bytePos = c >> 3;
74+
int bitPos = c & 7;
75+
byte mask = (byte) (1 << bitPos);
76+
byte nullByte = in.getByte(nullBitMapStartIdx + bytePos);
77+
Object decoded = null;
78+
if ((nullByte & mask) == 0) {
79+
// not null
80+
ColumnData columnData = desc.columnDatas[c];
81+
decoded = MSSQLDataTypeCodec.decode(columnData.dataType(), in);
82+
}
83+
row.addValue(decoded);
84+
}
85+
return row;
86+
}
87+
88+
enum RowStreamTokenType {
89+
ROW, NBCROW, ALTROW
90+
}
4191
}

vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/codec/SQLBatchCommandCodec.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ void decodeMessage(TdsMessage message, TdsMessageEncoder encoder) {
4545
case DataPacketStreamTokenType.ROW_TOKEN:
4646
handleRow(messageBody);
4747
break;
48+
case DataPacketStreamTokenType.NBCROW_TOKEN:
49+
handleNbcRow(messageBody);
50+
break;
4851
case DataPacketStreamTokenType.DONE_TOKEN:
4952
short status = messageBody.readShortLE();
5053
short curCmd = messageBody.readShortLE();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.vertx.mssqlclient.impl.protocol.datatype;
2+
3+
/**
4+
* BITNTYPE, the only valid lengths are 0x01 for non-null instances and 0x00 for NULL instances.
5+
*/
6+
public class BitNDataType extends MSSQLDataType {
7+
public static final BitNDataType BIT_1_DATA_TYPE = new BitNDataType(MSSQLDataTypeId.BITNTYPE_ID, Boolean.class, 1);
8+
9+
private final int length;
10+
11+
public BitNDataType(int id, Class<?> mappedJavaType, int length) {
12+
super(id, mappedJavaType);
13+
this.length = length;
14+
}
15+
16+
public int length() {
17+
return length;
18+
}
19+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.vertx.mssqlclient.impl.protocol.datatype;
2+
3+
/**
4+
* FLTNTYPE, Variable-Length Data type, the only valid lengths are 0x04 and 0x08, which map to 7-digit precision float and 15-digit precision float SQL data types respectively.
5+
*/
6+
public class FloatNDataType extends MSSQLDataType {
7+
public static final FloatNDataType FLT_4_DATA_TYPE = new FloatNDataType(MSSQLDataTypeId.FLTNTYPE_ID, Float.class, 4);
8+
public static final FloatNDataType FLT_8_DATA_TYPE = new FloatNDataType(MSSQLDataTypeId.FLTNTYPE_ID, Double.class, 8);
9+
10+
private final int length;
11+
12+
public FloatNDataType(int id, Class<?> mappedJavaType, int length) {
13+
super(id, mappedJavaType);
14+
this.length = length;
15+
}
16+
17+
public int length() {
18+
return length;
19+
}
20+
21+
public static FloatNDataType valueOf(int length) {
22+
switch (length) {
23+
case 4:
24+
return FLT_4_DATA_TYPE;
25+
case 8:
26+
return FLT_8_DATA_TYPE;
27+
default:
28+
throw new UnsupportedOperationException(String.format("SEVERE: Unsupported length=[%d] for decoding FLTNTYPE column metadata.", length));
29+
}
30+
}
31+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
12+
package io.vertx.mssqlclient.impl.protocol.datatype;
13+
14+
/**
15+
* Variable-Length Data type, length may be 0x01, 0x02, 0x04, and 0x08.
16+
*/
17+
public class IntNDataType extends MSSQLDataType {
18+
public static final IntNDataType INT_1_DATA_TYPE = new IntNDataType(MSSQLDataTypeId.INTNTYPE_ID, Byte.class, 1);
19+
public static final IntNDataType INT_2_DATA_TYPE = new IntNDataType(MSSQLDataTypeId.INTNTYPE_ID, Short.class, 2);
20+
public static final IntNDataType INT_4_DATA_TYPE = new IntNDataType(MSSQLDataTypeId.INTNTYPE_ID, Integer.class, 4);
21+
public static final IntNDataType INT_8_DATA_TYPE = new IntNDataType(MSSQLDataTypeId.INTNTYPE_ID, Long.class, 8);
22+
23+
private final int length;
24+
25+
private IntNDataType(int id, Class<?> mappedJavaType, int length) {
26+
super(id, mappedJavaType);
27+
this.length = length;
28+
}
29+
30+
public int length() {
31+
return length;
32+
}
33+
34+
public static IntNDataType valueOf(int length) {
35+
switch (length) {
36+
case 1:
37+
return INT_1_DATA_TYPE;
38+
case 2:
39+
return INT_2_DATA_TYPE;
40+
case 4:
41+
return INT_4_DATA_TYPE;
42+
case 8:
43+
return INT_8_DATA_TYPE;
44+
default:
45+
throw new UnsupportedOperationException(String.format("SEVERE: Unsupported length=[%d] for decoding IntNDataType column metadata.", length));
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)