|
31 | 31 | import org.testng.annotations.Test; |
32 | 32 |
|
33 | 33 | import java.math.BigDecimal; |
| 34 | +import java.sql.Connection; |
| 35 | +import java.sql.DriverManager; |
| 36 | +import java.sql.ResultSet; |
| 37 | +import java.sql.Statement; |
34 | 38 | import java.time.LocalDate; |
35 | 39 | import java.time.ZoneId; |
36 | 40 |
|
|
53 | 57 | import static com.google.common.base.Strings.repeat; |
54 | 58 | import static com.google.common.base.Verify.verify; |
55 | 59 | import static java.lang.String.format; |
| 60 | +import static org.testng.Assert.assertEquals; |
| 61 | +import static org.testng.Assert.assertTrue; |
56 | 62 |
|
57 | 63 | @Test |
58 | 64 | public class TestMySqlTypeMapping |
@@ -254,124 +260,120 @@ public void testDate() |
254 | 260 | } |
255 | 261 |
|
256 | 262 | @Test |
257 | | - public void testDatetime() |
| 263 | + public void testDatetimeUnderlyingStorageVerification() |
| 264 | + throws Exception |
258 | 265 | { |
259 | | - try { |
260 | | - assertUpdate("CREATE TABLE tpch.test_datetime (id INT PRIMARY KEY, dt DATETIME(6))"); |
261 | | - |
262 | | - assertUpdate("INSERT INTO tpch.test_datetime VALUES (1, '1970-01-01 00:00:00.000000')"); |
263 | | - |
264 | | - assertUpdate("INSERT INTO tpch.test_datetime VALUES (2, '2023-06-15 14:30:00.000000')"); |
265 | | - |
266 | | - for (String timeZoneId : ImmutableList.of("UTC", "America/New_York", "Asia/Tokyo", "Europe/Warsaw")) { |
267 | | - Session session = Session.builder(getQueryRunner().getDefaultSession()) |
268 | | - .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(timeZoneId)) |
269 | | - .setSystemProperty("legacy_timestamp", "false") |
270 | | - .build(); |
271 | | - |
272 | | - assertQuery( |
273 | | - session, |
274 | | - "SELECT dt FROM mysql.tpch.test_datetime WHERE id = 1", |
275 | | - "VALUES TIMESTAMP '1970-01-01 00:00:00.000000'"); |
276 | | - |
277 | | - assertQuery( |
278 | | - session, |
279 | | - "SELECT dt FROM mysql.tpch.test_datetime WHERE id = 2", |
280 | | - "VALUES TIMESTAMP '2023-06-15 14:30:00.000000'"); |
281 | | - } |
282 | | - } |
283 | | - finally { |
284 | | - assertUpdate("DROP TABLE IF EXISTS tpch.test_datetime"); |
285 | | - } |
286 | | - } |
| 266 | + String jdbcUrl = mysqlContainer.getJdbcUrl(); |
| 267 | + String jdbcUrlWithCredentials = format("%s%suser=%s&password=%s", |
| 268 | + jdbcUrl, |
| 269 | + jdbcUrl.contains("?") ? "&" : "?", |
| 270 | + mysqlContainer.getUsername(), |
| 271 | + mysqlContainer.getPassword()); |
| 272 | + JdbcSqlExecutor jdbcExecutor = new JdbcSqlExecutor(jdbcUrlWithCredentials); |
287 | 273 |
|
288 | | - @Test |
289 | | - public void testTimestamp() |
290 | | - { |
291 | 274 | try { |
292 | | - assertUpdate("CREATE TABLE tpch.test_timestamp_col (id INT PRIMARY KEY, ts TIMESTAMP(6))"); |
293 | | - |
294 | | - assertUpdate("INSERT INTO tpch.test_timestamp_col VALUES (1, '2023-06-15 14:30:00.000000')"); |
| 275 | + jdbcExecutor.execute("CREATE TABLE tpch.test_datetime_storage (" + |
| 276 | + "id INT PRIMARY KEY, " + |
| 277 | + "dt DATETIME(6), " + |
| 278 | + "source VARCHAR(10))"); |
295 | 279 |
|
296 | | - for (String timeZoneId : ImmutableList.of("UTC", "America/Los_Angeles", "Europe/Paris")) { |
297 | | - Session session = Session.builder(getQueryRunner().getDefaultSession()) |
298 | | - .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(timeZoneId)) |
299 | | - .setSystemProperty("legacy_timestamp", "false") |
300 | | - .build(); |
| 280 | + // MySQL insertion, MySQL retrieval, and Presto retrieval all agree on wall clock time |
| 281 | + jdbcExecutor.execute("INSERT INTO tpch.test_datetime_storage VALUES (1, '1970-01-01 00:00:00.000000', 'jdbc')"); |
301 | 282 |
|
302 | | - assertQuery( |
303 | | - session, |
304 | | - "SELECT ts FROM mysql.tpch.test_timestamp_col WHERE id = 1", |
305 | | - "VALUES TIMESTAMP '2023-06-15 14:30:00.000000'"); |
| 283 | + try (Connection conn = DriverManager.getConnection(jdbcUrlWithCredentials); |
| 284 | + Statement stmt = conn.createStatement(); |
| 285 | + ResultSet rs = stmt.executeQuery("SELECT CAST(dt AS CHAR) FROM tpch.test_datetime_storage WHERE id = 1")) { |
| 286 | + assertTrue(rs.next(), "Expected one row"); |
| 287 | + String dbValue1 = rs.getString(1); |
| 288 | + assertEquals(dbValue1, "1970-01-01 00:00:00.000000", "JDBC insert should store wall clock time 1970-01-01 00:00:00 in DB"); |
306 | 289 | } |
307 | | - } |
308 | | - finally { |
309 | | - assertUpdate("DROP TABLE IF EXISTS tpch.test_timestamp_col"); |
310 | | - } |
311 | | - } |
312 | | - |
313 | | - @Test |
314 | | - public void testDatetimeLegacy() |
315 | | - { |
316 | | - try { |
317 | | - assertUpdate("CREATE TABLE tpch.test_datetime_legacy (id INT PRIMARY KEY, dt DATETIME(6))"); |
318 | 290 |
|
319 | | - assertUpdate("INSERT INTO tpch.test_datetime_legacy VALUES (1, '1970-01-01 00:00:00.000000')"); |
320 | | - |
321 | | - Session utcSession = Session.builder(getQueryRunner().getDefaultSession()) |
322 | | - .setTimeZoneKey(TimeZoneKey.getTimeZoneKey("UTC")) |
323 | | - .setSystemProperty("legacy_timestamp", "true") |
324 | | - .build(); |
325 | | - |
326 | | - Session nySession = Session.builder(getQueryRunner().getDefaultSession()) |
327 | | - .setTimeZoneKey(TimeZoneKey.getTimeZoneKey("America/New_York")) |
328 | | - .setSystemProperty("legacy_timestamp", "true") |
| 291 | + Session session = Session.builder(getQueryRunner().getDefaultSession()) |
| 292 | + .setSystemProperty("legacy_timestamp", "false") |
329 | 293 | .build(); |
| 294 | + assertQuery(session, |
| 295 | + "SELECT dt FROM mysql.tpch.test_datetime_storage WHERE id = 1", |
| 296 | + "VALUES TIMESTAMP '1970-01-01 00:00:00.000000'"); |
| 297 | + |
| 298 | + // Presto insertion, retrieval via MySQL, and retrieval via Presto all agree on wall clock time |
| 299 | + assertUpdate(session, "INSERT INTO mysql.tpch.test_datetime_storage VALUES (2, TIMESTAMP '2023-06-15 14:30:00.000000', 'presto')", 1); |
| 300 | + |
| 301 | + try (Connection conn = DriverManager.getConnection(jdbcUrlWithCredentials); |
| 302 | + Statement stmt = conn.createStatement(); |
| 303 | + ResultSet rs = stmt.executeQuery("SELECT CAST(dt AS CHAR) FROM tpch.test_datetime_storage WHERE id = 2")) { |
| 304 | + assertTrue(rs.next(), "Expected one row"); |
| 305 | + String dbValue2 = rs.getString(1); |
| 306 | + assertEquals(dbValue2, "2023-06-15 14:30:00.000000", "Presto insert should store wall clock time 2023-06-15 14:30:00 in DB"); |
| 307 | + } |
330 | 308 |
|
331 | | - assertQuery( |
332 | | - utcSession, |
333 | | - "SELECT dt FROM mysql.tpch.test_datetime_legacy WHERE id = 1", |
334 | | - "VALUES TIMESTAMP '1970-01-01 07:00:00.000000'"); |
335 | | - |
336 | | - assertQuery( |
337 | | - nySession, |
338 | | - "SELECT dt FROM mysql.tpch.test_datetime_legacy WHERE id = 1", |
339 | | - "VALUES TIMESTAMP '1970-01-01 02:00:00.000000'"); // 07:00 UTC = 02:00 EST |
| 309 | + assertQuery(session, |
| 310 | + "SELECT dt FROM mysql.tpch.test_datetime_storage WHERE id = 2", |
| 311 | + "VALUES TIMESTAMP '2023-06-15 14:30:00.000000'"); |
340 | 312 | } |
341 | 313 | finally { |
342 | | - assertUpdate("DROP TABLE IF EXISTS tpch.test_datetime_legacy"); |
| 314 | + jdbcExecutor.execute("DROP TABLE IF EXISTS tpch.test_datetime_storage"); |
343 | 315 | } |
344 | 316 | } |
345 | 317 |
|
346 | 318 | @Test |
347 | | - public void testDatetimeWritePath() |
| 319 | + public void testDatetimeLegacyUnderlyingStorageVerification() |
| 320 | + throws Exception |
348 | 321 | { |
| 322 | + String jdbcUrl = mysqlContainer.getJdbcUrl(); |
| 323 | + String jdbcUrlWithCredentials = format("%s%suser=%s&password=%s", |
| 324 | + jdbcUrl, |
| 325 | + jdbcUrl.contains("?") ? "&" : "?", |
| 326 | + mysqlContainer.getUsername(), |
| 327 | + mysqlContainer.getPassword()); |
| 328 | + JdbcSqlExecutor jdbcExecutor = new JdbcSqlExecutor(jdbcUrlWithCredentials); |
| 329 | + |
349 | 330 | try { |
350 | | - assertUpdate("CREATE TABLE tpch.test_datetime_write (" + |
| 331 | + jdbcExecutor.execute("CREATE TABLE tpch.test_datetime_legacy_storage (" + |
351 | 332 | "id INT PRIMARY KEY, " + |
352 | 333 | "dt DATETIME(6), " + |
353 | 334 | "source VARCHAR(10))"); |
354 | 335 |
|
355 | | - assertUpdate("INSERT INTO tpch.test_datetime_write VALUES (1, '1970-01-01 00:00:00.000000', 'jdbc')"); |
| 336 | + // MySQL insertion and MySQL retrieval agree, Presto incorrectly interprets DB value due to legacy mode |
| 337 | + jdbcExecutor.execute("INSERT INTO tpch.test_datetime_legacy_storage VALUES (1, '1970-01-01 00:00:00.000000', 'jdbc')"); |
356 | 338 |
|
357 | | - Session session = Session.builder(getQueryRunner().getDefaultSession()) |
358 | | - .setSystemProperty("legacy_timestamp", "false") |
359 | | - .build(); |
360 | | - assertUpdate(session, "INSERT INTO mysql.tpch.test_datetime_write VALUES (2, TIMESTAMP '1970-01-01 00:00:00.000000', 'presto')", 1); |
361 | | - |
362 | | - assertUpdate("INSERT INTO tpch.test_datetime_write VALUES (3, '2023-06-15 14:30:00.000000', 'jdbc')"); |
363 | | - assertUpdate(session, "INSERT INTO mysql.tpch.test_datetime_write VALUES (4, TIMESTAMP '2023-06-15 14:30:00.000000', 'presto')", 1); |
| 339 | + // Prove that the value is 1970-01-01 00:00:00 by reading directly from the DB via JDBC |
| 340 | + try (Connection conn = DriverManager.getConnection(jdbcUrlWithCredentials); |
| 341 | + Statement stmt = conn.createStatement(); |
| 342 | + ResultSet rs = stmt.executeQuery("SELECT CAST(dt AS CHAR) FROM tpch.test_datetime_legacy_storage WHERE id = 1")) { |
| 343 | + assertTrue(rs.next(), "Expected one row"); |
| 344 | + String dbValue1 = rs.getString(1); |
| 345 | + assertEquals(dbValue1, "1970-01-01 00:00:00.000000", "JDBC insert should store wall clock time 1970-01-01 00:00:00 in DB"); |
| 346 | + } |
364 | 347 |
|
365 | | - assertQuery(session, |
366 | | - "SELECT dt FROM mysql.tpch.test_datetime_write WHERE id IN (1, 2) ORDER BY id", |
367 | | - "VALUES TIMESTAMP '1970-01-01 00:00:00.000000', TIMESTAMP '1970-01-01 00:00:00.000000'"); |
| 348 | + // In legacy mode, DB value 1970-01-01 00:00:00 is interpreted as if it's in JVM timezone (America/Bahia_Banderas UTC-7) |
| 349 | + // and then converted to the session timezone. Since both are the same (America/Bahia_Banderas), |
| 350 | + // the offset comes from treating the wall-clock DB time as UTC, resulting in 1969-12-31 20:00:00 |
| 351 | + Session legacySession = Session.builder(getQueryRunner().getDefaultSession()) |
| 352 | + .setSystemProperty("legacy_timestamp", "true") |
| 353 | + .build(); |
| 354 | + assertQuery(legacySession, |
| 355 | + "SELECT dt FROM mysql.tpch.test_datetime_legacy_storage WHERE id = 1", |
| 356 | + "VALUES TIMESTAMP '1969-12-31 20:00:00.000000'"); |
| 357 | + |
| 358 | + // Presto insertion with legacy mode, verify DB storage via JDBC (should apply JVM timezone conversion during write) |
| 359 | + assertUpdate(legacySession, "INSERT INTO mysql.tpch.test_datetime_legacy_storage VALUES (2, TIMESTAMP '2023-06-15 14:30:00.000000', 'presto')", 1); |
| 360 | + |
| 361 | + try (Connection conn = DriverManager.getConnection(jdbcUrlWithCredentials); |
| 362 | + Statement stmt = conn.createStatement(); |
| 363 | + ResultSet rs = stmt.executeQuery("SELECT CAST(dt AS CHAR) FROM tpch.test_datetime_legacy_storage WHERE id = 2")) { |
| 364 | + assertTrue(rs.next(), "Expected one row"); |
| 365 | + String dbValue2 = rs.getString(1); |
| 366 | + // JVM timezone is America/Bahia_Banderas (UTC-7), so 2023-06-15 14:30:00 becomes 2023-06-14 19:30:00.000000 |
| 367 | + assertEquals(dbValue2, "2023-06-14 19:30:00.000000", "Legacy mode applies timezone conversion during write, expected 2023-06-14 19:30:00.000000"); |
| 368 | + } |
368 | 369 |
|
369 | | - assertQuery(session, |
370 | | - "SELECT dt FROM mysql.tpch.test_datetime_write WHERE id IN (3, 4) ORDER BY id", |
371 | | - "VALUES TIMESTAMP '2023-06-15 14:30:00.000000', TIMESTAMP '2023-06-15 14:30:00.000000'"); |
| 370 | + // Verify Presto reads it back correctly in legacy mode (round-trip should work) |
| 371 | + assertQuery(legacySession, |
| 372 | + "SELECT dt FROM mysql.tpch.test_datetime_legacy_storage WHERE id = 2", |
| 373 | + "VALUES TIMESTAMP '2023-06-15 14:30:00.000000'"); |
372 | 374 | } |
373 | 375 | finally { |
374 | | - assertUpdate("DROP TABLE IF EXISTS tpch.test_datetime_write"); |
| 376 | + jdbcExecutor.execute("DROP TABLE IF EXISTS tpch.test_datetime_legacy_storage"); |
375 | 377 | } |
376 | 378 | } |
377 | 379 |
|
|
0 commit comments