Skip to content

Commit 4a3aef7

Browse files
authored
[PECOBLR-239][PECOBLR-279] added sql types integration tests (#769)
* added edge cases for string and null * update stubs * add test for variant type * add test for timestamp
1 parent db7d149 commit 4a3aef7

File tree

170 files changed

+5480
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+5480
-0
lines changed
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
package com.databricks.jdbc.integration.fakeservice.tests;
2+
3+
import static com.databricks.jdbc.integration.IntegrationTestUtil.*;
4+
import static org.junit.jupiter.api.Assertions.*;
5+
6+
import com.databricks.jdbc.api.impl.DatabricksConnection;
7+
import com.databricks.jdbc.common.DatabricksClientType;
8+
import com.databricks.jdbc.integration.fakeservice.AbstractFakeServiceIntegrationTests;
9+
import com.databricks.jdbc.integration.fakeservice.FakeServiceExtension;
10+
import java.sql.*;
11+
import java.util.Properties;
12+
import java.util.stream.Stream;
13+
import org.junit.jupiter.api.AfterEach;
14+
import org.junit.jupiter.api.BeforeEach;
15+
import org.junit.jupiter.api.Test;
16+
import org.junit.jupiter.params.ParameterizedTest;
17+
import org.junit.jupiter.params.provider.Arguments;
18+
import org.junit.jupiter.params.provider.MethodSource;
19+
20+
/** Integration tests for string edge cases and nested complex types. */
21+
public class DataTypesIntegrationTests extends AbstractFakeServiceIntegrationTests {
22+
23+
private Connection connection;
24+
private Connection inlineConnection;
25+
26+
private static final String LEADING_TRAILING_SPACES = " leading and trailing spaces ";
27+
private static final String UNICODE_TEXT = "こんにちは";
28+
private static final String SPECIAL_CHARS = "special chars: !@#$%^&*()";
29+
private static final String DOUBLE_QUOTES = "string with \"double quotes\" inside";
30+
31+
@BeforeEach
32+
void setUp() throws SQLException {
33+
connection = getValidJDBCConnection();
34+
Properties properties = new Properties();
35+
properties.setProperty("enableArrow", "0");
36+
inlineConnection = getValidJDBCConnection(properties);
37+
}
38+
39+
@AfterEach
40+
void cleanUp() throws SQLException {
41+
closeConnection(connection);
42+
closeConnection(inlineConnection);
43+
}
44+
45+
@Test
46+
void testStringEdgeCases() throws SQLException {
47+
String query =
48+
"SELECT * FROM (VALUES "
49+
+ "(1, '"
50+
+ LEADING_TRAILING_SPACES
51+
+ "'),"
52+
+ "(2, '"
53+
+ UNICODE_TEXT
54+
+ "'),"
55+
+ "(3, '"
56+
+ SPECIAL_CHARS
57+
+ "'),"
58+
+ "(4, '"
59+
+ DOUBLE_QUOTES
60+
+ "'),"
61+
+ "(5, NULL)"
62+
+ ") AS string_edge_cases(id, test_string) "
63+
+ "ORDER BY id";
64+
ResultSet resultSet = executeQuery(connection, query);
65+
ResultSet inlineResultSet = executeQuery(inlineConnection, query);
66+
67+
validateStringResults(resultSet);
68+
validateStringResults(inlineResultSet);
69+
70+
resultSet.close();
71+
inlineResultSet.close();
72+
}
73+
74+
@ParameterizedTest
75+
@MethodSource("nullHandlingProvider")
76+
void testNullHandling(String query, int expectedType) throws SQLException {
77+
ResultSet resultSet = executeQuery(connection, query);
78+
ResultSet inlineResultSet = executeQuery(inlineConnection, query);
79+
assertTrue(resultSet.next());
80+
assertTrue(inlineResultSet.next());
81+
assertNull(resultSet.getObject(1));
82+
assertNull(inlineResultSet.getObject(1));
83+
assertEquals(expectedType, resultSet.getMetaData().getColumnType(1));
84+
assertEquals(expectedType, inlineResultSet.getMetaData().getColumnType(1));
85+
resultSet.close();
86+
inlineResultSet.close();
87+
}
88+
89+
private static Stream<Arguments> nullHandlingProvider() {
90+
return Stream.of(
91+
Arguments.of("SELECT NULL", Types.VARCHAR),
92+
Arguments.of("SELECT CAST(NULL AS DOUBLE)", Types.DOUBLE),
93+
Arguments.of("SELECT NULL UNION (SELECT 1) order by 1", Types.INTEGER));
94+
}
95+
96+
private void validateStringResults(ResultSet resultSet) throws SQLException {
97+
int rowCount = 0;
98+
while (resultSet.next()) {
99+
rowCount++;
100+
int id = resultSet.getInt("id");
101+
String value = resultSet.getString("test_string");
102+
switch (id) {
103+
case 1:
104+
assertEquals(LEADING_TRAILING_SPACES, value);
105+
break;
106+
case 2:
107+
assertEquals(UNICODE_TEXT, value);
108+
break;
109+
case 3:
110+
assertEquals(SPECIAL_CHARS, value);
111+
break;
112+
case 4:
113+
assertEquals(DOUBLE_QUOTES, value);
114+
break;
115+
case 5:
116+
assertEquals(null, value);
117+
break;
118+
default:
119+
fail("Unexpected row id: " + id);
120+
}
121+
}
122+
assertEquals(5, rowCount);
123+
}
124+
125+
@Test
126+
void testVariantTypes() throws SQLException {
127+
String tableName = "variant_types_table";
128+
String createTableSQL =
129+
"CREATE TABLE IF NOT EXISTS "
130+
+ getFullyQualifiedTableName(tableName)
131+
+ " (id INT PRIMARY KEY, variant_col VARIANT)";
132+
setupDatabaseTable(connection, tableName, createTableSQL);
133+
134+
// Insert rows with JSON data via PARSE_JSON:
135+
// - A simple JSON object
136+
// - A nested JSON object with an array and boolean value
137+
// - A null variant
138+
String insertSQL =
139+
"INSERT INTO "
140+
+ getFullyQualifiedTableName(tableName)
141+
+ " (id, variant_col) VALUES "
142+
+ "(1, PARSE_JSON('{\"key\": \"value\", \"number\": 123}')), "
143+
+ "(2, PARSE_JSON('{\"nested\": {\"a\": \"b\", \"c\": [1, 2, 3]}, \"flag\": true}')), "
144+
+ "(3, NULL)";
145+
executeSQL(connection, insertSQL);
146+
147+
String query =
148+
"SELECT id, variant_col FROM " + getFullyQualifiedTableName(tableName) + " ORDER BY id";
149+
ResultSet rs = executeQuery(connection, query);
150+
ResultSetMetaData rsmd = rs.getMetaData();
151+
assertEquals(Types.OTHER, rsmd.getColumnType(2));
152+
assertEquals("VARIANT", rsmd.getColumnTypeName(2));
153+
int rowCount = 0;
154+
while (rs.next()) {
155+
rowCount++;
156+
int id = rs.getInt("id");
157+
Object variant = rs.getObject("variant_col");
158+
switch (id) {
159+
case 1:
160+
String variantStr1 = variant.toString();
161+
assertTrue(variantStr1.contains("\"key\":\"value\""));
162+
assertTrue(variantStr1.contains("\"number\":123"));
163+
break;
164+
case 2:
165+
String variantStr2 = variant.toString();
166+
assertTrue(variantStr2.contains("\"nested\""));
167+
assertTrue(variantStr2.contains("\"a\":\"b\""));
168+
assertTrue(variantStr2.contains("\"c\":[1,2,3]"));
169+
assertTrue(variantStr2.contains("\"flag\":true"));
170+
break;
171+
case 3:
172+
assertNull(variant);
173+
break;
174+
default:
175+
fail("Unexpected row id in variant test: " + id);
176+
}
177+
}
178+
assertEquals(3, rowCount);
179+
deleteTable(connection, tableName);
180+
rs.close();
181+
}
182+
183+
@Test
184+
void testTimestampWithTimezoneConversion() throws SQLException {
185+
String tableName = "timestamp_test_timezone";
186+
String createTableSQL =
187+
"CREATE TABLE IF NOT EXISTS "
188+
+ getFullyQualifiedTableName(tableName)
189+
+ " (id INT PRIMARY KEY, ts TIMESTAMP)";
190+
setupDatabaseTable(connection, tableName, createTableSQL);
191+
192+
/*
193+
* Use from_utc_timestamp to simulate converting a UTC timestamp into a specific timezone.
194+
* converting '2021-06-15 12:34:56.789' from UTC to America/Los_Angeles.
195+
* expected local time should be:
196+
* 2021-06-15 12:34:56.789 UTC --> 2021-06-15 05:34:56.789 PDT
197+
*/
198+
String insertSQL =
199+
"INSERT INTO "
200+
+ getFullyQualifiedTableName(tableName)
201+
+ " (id, ts) VALUES "
202+
+ "(1, from_utc_timestamp('2021-06-15 12:34:56.789', 'America/Los_Angeles'))";
203+
executeSQL(connection, insertSQL);
204+
205+
// Query and validate the timezone conversion result.
206+
String query = "SELECT id, ts FROM " + getFullyQualifiedTableName(tableName) + " ORDER BY id";
207+
ResultSet rs = executeQuery(connection, query);
208+
assertTrue(rs.next());
209+
int id = rs.getInt("id");
210+
Timestamp ts = rs.getTimestamp("ts");
211+
assertEquals(1, id);
212+
Timestamp expected = Timestamp.valueOf("2021-06-15 05:34:56.789"); // Expected PDT value.
213+
assertEquals(expected, ts);
214+
deleteTable(connection, tableName);
215+
rs.close();
216+
}
217+
218+
@Test
219+
void testTimestamp() throws SQLException {
220+
String tableName = "timestamp_test_table";
221+
String createTableSQL =
222+
"CREATE TABLE IF NOT EXISTS "
223+
+ getFullyQualifiedTableName(tableName)
224+
+ " (id INT PRIMARY KEY, ts TIMESTAMP)";
225+
setupDatabaseTable(connection, tableName, createTableSQL);
226+
String insertSQL =
227+
"INSERT INTO "
228+
+ getFullyQualifiedTableName(tableName)
229+
+ " (id, ts) VALUES "
230+
+ "(1, '2021-01-01 10:00:00'), "
231+
+ "(2, '2021-06-15 12:34:56.789'), "
232+
+ "(3, NULL)";
233+
executeSQL(connection, insertSQL);
234+
235+
String query = "SELECT id, ts FROM " + getFullyQualifiedTableName(tableName) + " ORDER BY id";
236+
ResultSet resultSet = executeQuery(connection, query);
237+
ResultSet inlineResultSet = executeQuery(inlineConnection, query);
238+
validateTimestampResults(resultSet);
239+
validateTimestampResults(inlineResultSet);
240+
241+
deleteTable(connection, tableName);
242+
resultSet.close();
243+
}
244+
245+
private void validateTimestampResults(ResultSet resultSet) throws SQLException {
246+
int rowCount = 0;
247+
while (resultSet.next()) {
248+
rowCount++;
249+
int id = resultSet.getInt("id");
250+
Timestamp ts = resultSet.getTimestamp("ts");
251+
switch (id) {
252+
case 1:
253+
Timestamp expected1 = Timestamp.valueOf("2021-01-01 10:00:00");
254+
assertEquals(expected1, ts);
255+
break;
256+
case 2:
257+
Timestamp expected2 = Timestamp.valueOf("2021-06-15 12:34:56.789");
258+
assertEquals(expected2, ts);
259+
break;
260+
case 3:
261+
assertNull(ts);
262+
break;
263+
default:
264+
fail("Unexpected row id in timestamp test: " + id);
265+
}
266+
}
267+
assertEquals(3, rowCount);
268+
}
269+
270+
private void closeConnection(Connection connection) throws SQLException {
271+
if (connection != null) {
272+
if (((DatabricksConnection) connection).getConnectionContext().getClientType()
273+
== DatabricksClientType.THRIFT
274+
&& getFakeServiceMode() == FakeServiceExtension.FakeServiceMode.REPLAY) {
275+
// Hacky fix for THRIFT + REPLAY mode
276+
} else {
277+
connection.close();
278+
}
279+
}
280+
}
281+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"id" : "6fcb956f-3961-4522-ad6d-3b72dfe84c50",
3+
"name" : "oregon-staging_6051921418418893.jobs_sql_extended_results_2025-04-02t223316z_cfe75cc7-a4ec-4b74-acaf-4afd29a23b6c",
4+
"request" : {
5+
"urlPath" : "/oregon-staging/6051921418418893.jobs/sql/extended/results_2025-04-02T22%3A33%3A16Z_cfe75cc7-a4ec-4b74-acaf-4afd29a23b6c",
6+
"method" : "GET"
7+
},
8+
"response" : {
9+
"status" : 200,
10+
"base64Body" : "BCJNGHRwjm4AAACg/////4gAAAAQAAEAsAoADgAGAA0ACAAKDwAiAAQYAKEBCgAMAAAACAAEGAARCAwAASEAYQABAAAAGAsAlRIAGAAUABMAEiwAUxIAAAAUBAACJAAgAQEQAAQCABEEAgAACACgAABOVUxMAAAAAIlR7KMAAAAAhkuWawQiTRh0cI5OAAAApP////9gAAAAFAABALEKAA4ABgANAAgAChMAMAQAEAgAoQMKABgADAAIAAQYAAAwAFAYAAAAARwACwIAABQAAAQAAAIAgAEAAAAAAAAAR0kUogAAAAAEAp6mBCJNGHRwjggAAID/////AAAAAIaGkggAAAAAhoaSCA==",
11+
"headers" : {
12+
"Accept-Ranges" : "bytes",
13+
"Server" : "AmazonS3",
14+
"ETag" : "\"38256e3c0c574de76f018642d42b384e\"",
15+
"Last-Modified" : "Tue, 01 Apr 2025 21:33:17 GMT",
16+
"x-amz-request-id" : "HJWJB3DEH55CXTPC",
17+
"x-amz-server-side-encryption" : "AES256",
18+
"x-amz-id-2" : "AnOxi7S0+a6hVPiFwugy4BfQbdpjbjhqGe9R2Tsa8YZ386JbbyfxCv7bfBefxsdXPMNTxxjcw/M=",
19+
"Date" : "Tue, 01 Apr 2025 21:33:19 GMT",
20+
"Content-Type" : "binary/octet-stream"
21+
}
22+
},
23+
"uuid" : "6fcb956f-3961-4522-ad6d-3b72dfe84c50",
24+
"insertionIndex" : 1
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"id" : "a57f1ff8-b16f-42e9-bcdf-88eaab7323ac",
3+
"name" : "oregon-staging_6051921418418893.jobs_sql_extended_results_2025-04-02t223328z_8bdd9299-944a-4ef4-9597-1227609a5329",
4+
"request" : {
5+
"urlPath" : "/oregon-staging/6051921418418893.jobs/sql/extended/results_2025-04-02T22%3A33%3A28Z_8bdd9299-944a-4ef4-9597-1227609a5329",
6+
"method" : "GET"
7+
},
8+
"response" : {
9+
"status" : 200,
10+
"base64Body" : "BCJNGHRwjoUAAACg/////6AAAAAQAAEAsAoADgAGAA0ACAAKDwAiAAQYAKEBCgAMAAAACAAEGAARCAwAASEAYQABAAAAGAsAlRIAGAAUABMAEiwAUxIAAAAUBAARHCQAIgMBCAAEAgBxBgAIAAYABg8AEQIsAPAJQ0FTVChOVUxMIEFTIERPVUJMRSkAAAAAd3FnmwAAAAC62iu3BCJNGHRwjmwAAACi/////4gAAAAUAAEA8gIMABYADgAVABAABAAMAAAAEBcAQgAAAAQMAMMDCgAYAAwACAAEAAo4AFE4AAAAASQAAgIAEgIHAAECAAQYABEIDgAGCAAAAgAAHAAABAAAAgAECAAHAgBQAAAAAACP9wR3AAAAAIGkfS8EIk0YdHCOCAAAgP////8AAAAAhoaSCAAAAACGhpII",
11+
"headers" : {
12+
"Accept-Ranges" : "bytes",
13+
"Server" : "AmazonS3",
14+
"ETag" : "\"69d88943d9a3b4fa2fc5d7e9b21a5df2\"",
15+
"Last-Modified" : "Tue, 01 Apr 2025 21:33:29 GMT",
16+
"x-amz-request-id" : "1EPXCM7RGRBQY9CC",
17+
"x-amz-server-side-encryption" : "AES256",
18+
"x-amz-id-2" : "YORF7/i4IfZifhbAYfm8ArRO3N6SPebMuob/W4aYQXb9DUDzsq6OjhMVMHL/4zU3LI3PHiBgwfs=",
19+
"Date" : "Tue, 01 Apr 2025 21:33:30 GMT",
20+
"Content-Type" : "binary/octet-stream"
21+
}
22+
},
23+
"uuid" : "a57f1ff8-b16f-42e9-bcdf-88eaab7323ac",
24+
"insertionIndex" : 3
25+
}

0 commit comments

Comments
 (0)