Skip to content

Commit 9a8849b

Browse files
vxelCédric de Launois
and
Cédric de Launois
authored
Add support for TDSType.GUID (#1582) (#2324)
* Added support for TDSType.GUID (#1582) * Fix UUID serialisation * Add unit tests * Fix typo, handle PR feedback --------- Co-authored-by: Cédric de Launois <lnscdr@forem.be> Co-authored-by: Cédric de Launois <>
1 parent 1d4b7d6 commit 9a8849b

File tree

4 files changed

+147
-3
lines changed

4 files changed

+147
-3
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java

+27
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import java.util.Set;
5858
import java.util.SimpleTimeZone;
5959
import java.util.TimeZone;
60+
import java.util.UUID;
6061
import java.util.concurrent.ScheduledFuture;
6162
import java.util.concurrent.SynchronousQueue;
6263
import java.util.concurrent.ThreadPoolExecutor;
@@ -4784,6 +4785,32 @@ void writeRPCBigDecimal(String sName, BigDecimal bdValue, int nScale, boolean bO
47844785
writeBytes(val, 0, val.length);
47854786
}
47864787

4788+
/**
4789+
* Append a UUID in RPC transmission format.
4790+
*
4791+
* @param sName
4792+
* the optional parameter name
4793+
* @param uuidValue
4794+
* the data value
4795+
* @param bOut
4796+
* boolean true if the data value is being registered as an output parameter
4797+
*/
4798+
void writeRPCUUID(String sName, UUID uuidValue, boolean bOut) throws SQLServerException {
4799+
writeRPCNameValType(sName, bOut, TDSType.GUID);
4800+
4801+
if (uuidValue == null) {
4802+
writeByte((byte) 0);
4803+
writeByte((byte) 0);
4804+
4805+
} else {
4806+
writeByte((byte) 0x10); // maximum length = 16
4807+
writeByte((byte) 0x10); // length = 16
4808+
4809+
byte[] val = Util.asGuidByteArray(uuidValue);
4810+
writeBytes(val, 0, val.length);
4811+
}
4812+
}
4813+
47874814
/**
47884815
* Appends a standard v*max header for RPC parameter transmission.
47894816
*

src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.time.OffsetTime;
2323
import java.util.Calendar;
2424
import java.util.Locale;
25+
import java.util.UUID;
2526

2627

2728
/**
@@ -1125,6 +1126,10 @@ void execute(DTV dtv, Boolean booleanValue) throws SQLServerException {
11251126
setTypeDefinition(dtv);
11261127
}
11271128

1129+
void execute(DTV dtv, UUID uuidValue) throws SQLServerException {
1130+
setTypeDefinition(dtv);
1131+
}
1132+
11281133
void execute(DTV dtv, byte[] byteArrayValue) throws SQLServerException {
11291134
// exclude JDBC typecasting for Geometry/Geography as these datatypes don't have a size limit.
11301135
if (null != byteArrayValue && byteArrayValue.length > DataTypes.SHORT_VARTYPE_MAX_BYTES

src/main/java/com/microsoft/sqlserver/jdbc/dtv.java

+18-3
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ abstract class DTVExecuteOp {
9999

100100
abstract void execute(DTV dtv, Boolean booleanValue) throws SQLServerException;
101101

102+
abstract void execute(DTV dtv, UUID uuidValue) throws SQLServerException;
103+
102104
abstract void execute(DTV dtv, byte[] byteArrayValue) throws SQLServerException;
103105

104106
abstract void execute(DTV dtv, Blob blobValue) throws SQLServerException;
@@ -291,7 +293,11 @@ final class SendByRPCOp extends DTVExecuteOp {
291293
}
292294

293295
void execute(DTV dtv, String strValue) throws SQLServerException {
294-
tdsWriter.writeRPCStringUnicode(name, strValue, isOutParam, collation, dtv.isNonPLP);
296+
if (dtv.getJdbcType() == JDBCType.GUID) {
297+
tdsWriter.writeRPCUUID(name, UUID.fromString(strValue), isOutParam);
298+
} else {
299+
tdsWriter.writeRPCStringUnicode(name, strValue, isOutParam, collation, dtv.isNonPLP);
300+
}
295301
}
296302

297303
void execute(DTV dtv, Clob clobValue) throws SQLServerException {
@@ -1126,6 +1132,10 @@ void execute(DTV dtv, Boolean booleanValue) throws SQLServerException {
11261132
tdsWriter.writeRPCBit(name, booleanValue, isOutParam);
11271133
}
11281134

1135+
void execute(DTV dtv, UUID uuidValue) throws SQLServerException {
1136+
tdsWriter.writeRPCUUID(name, uuidValue, isOutParam);
1137+
}
1138+
11291139
void execute(DTV dtv, byte[] byteArrayValue) throws SQLServerException {
11301140
if (null != cryptoMeta) {
11311141
tdsWriter.writeRPCNameValType(name, isOutParam, TDSType.BIGVARBINARY);
@@ -1537,10 +1547,13 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException {
15371547
case VARCHAR:
15381548
case LONGVARCHAR:
15391549
case CLOB:
1540-
case GUID:
15411550
op.execute(this, (byte[]) null);
15421551
break;
15431552

1553+
case GUID:
1554+
op.execute(this, (UUID) null);
1555+
break;
1556+
15441557
case TINYINT:
15451558
if (null != cryptoMeta)
15461559
op.execute(this, (byte[]) null);
@@ -1619,7 +1632,7 @@ final void executeOp(DTVExecuteOp op) throws SQLServerException {
16191632
byte[] bArray = Util.asGuidByteArray((UUID) value);
16201633
op.execute(this, bArray);
16211634
} else {
1622-
op.execute(this, String.valueOf(value));
1635+
op.execute(this, UUID.fromString(String.valueOf(value)));
16231636
}
16241637
} else if (JDBCType.SQL_VARIANT == jdbcType) {
16251638
op.execute(this, String.valueOf(value));
@@ -2194,6 +2207,8 @@ void execute(DTV dtv, Short shortValue) throws SQLServerException {}
21942207

21952208
void execute(DTV dtv, Boolean booleanValue) throws SQLServerException {}
21962209

2210+
void execute(DTV dtv, UUID uuidValue) throws SQLServerException {}
2211+
21972212
void execute(DTV dtv, byte[] byteArrayValue) throws SQLServerException {}
21982213

21992214
void execute(DTV dtv, Blob blobValue) throws SQLServerException {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.microsoft.sqlserver.jdbc.datatypes;
2+
3+
import com.microsoft.sqlserver.jdbc.RandomUtil;
4+
import com.microsoft.sqlserver.jdbc.SQLServerResultSet;
5+
import com.microsoft.sqlserver.jdbc.TestResource;
6+
import com.microsoft.sqlserver.jdbc.TestUtils;
7+
import com.microsoft.sqlserver.testframework.AbstractSQLGenerator;
8+
import com.microsoft.sqlserver.testframework.AbstractTest;
9+
import microsoft.sql.Types;
10+
import org.junit.jupiter.api.BeforeAll;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.platform.runner.JUnitPlatform;
13+
import org.junit.runner.RunWith;
14+
15+
import java.sql.Connection;
16+
import java.sql.PreparedStatement;
17+
import java.sql.Statement;
18+
import java.util.UUID;
19+
20+
import static org.junit.Assert.assertNull;
21+
import static org.junit.Assert.assertTrue;
22+
import static org.junit.Assert.fail;
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
25+
26+
/*
27+
* This test is for testing the serialisation of String as microsoft.sql.Types.GUID
28+
*/
29+
@RunWith(JUnitPlatform.class)
30+
public class GuidTest extends AbstractTest {
31+
32+
final static String tableName = RandomUtil.getIdentifier("GuidTestTable");
33+
final static String escapedTableName = AbstractSQLGenerator.escapeIdentifier(tableName);
34+
35+
@BeforeAll
36+
public static void setupTests() throws Exception {
37+
setConnection();
38+
}
39+
40+
/*
41+
* Test UUID conversions
42+
*/
43+
@Test
44+
public void testGuid() throws Exception {
45+
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
46+
47+
// Create the test table
48+
TestUtils.dropTableIfExists(escapedTableName, stmt);
49+
50+
String query = "create table " + escapedTableName
51+
+ " (uuid uniqueidentifier, id int IDENTITY primary key)";
52+
stmt.executeUpdate(query);
53+
54+
UUID uuid = UUID.randomUUID();
55+
String uuidString = uuid.toString();
56+
int id = 1;
57+
58+
try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO " + escapedTableName
59+
+ " VALUES(?) SELECT * FROM " + escapedTableName + " where id = ?")) {
60+
61+
pstmt.setObject(1, uuidString, Types.GUID);
62+
pstmt.setObject(2, id++);
63+
pstmt.execute();
64+
pstmt.getMoreResults();
65+
try (SQLServerResultSet rs = (SQLServerResultSet) pstmt.getResultSet()) {
66+
rs.next();
67+
assertEquals(uuid, UUID.fromString(rs.getUniqueIdentifier(1)));
68+
}
69+
70+
// Test NULL GUID
71+
pstmt.setObject(1, null, Types.GUID);
72+
pstmt.setObject(2, id++);
73+
pstmt.execute();
74+
pstmt.getMoreResults();
75+
try (SQLServerResultSet rs = (SQLServerResultSet) pstmt.getResultSet()) {
76+
rs.next();
77+
String s = rs.getUniqueIdentifier(1);
78+
assertNull(s);
79+
assertTrue(rs.wasNull());
80+
}
81+
82+
// Test Illegal GUID
83+
try {
84+
pstmt.setObject(1, "garbage", Types.GUID);
85+
fail(TestResource.getResource("R_expectedFailPassed"));
86+
} catch (IllegalArgumentException e) {
87+
assertEquals("Invalid UUID string: garbage", e.getMessage());
88+
}
89+
}
90+
} finally {
91+
try (Statement stmt = connection.createStatement()) {
92+
TestUtils.dropTableIfExists(escapedTableName, stmt);
93+
}
94+
}
95+
}
96+
97+
}

0 commit comments

Comments
 (0)