|
53 | 53 | import org.apache.spark.sql.SparkSession;
|
54 | 54 | import org.apache.spark.sql.types.DataTypes;
|
55 | 55 | import org.junit.jupiter.api.AfterAll;
|
| 56 | +import org.junit.jupiter.api.Assertions; |
56 | 57 | import org.junit.jupiter.api.BeforeAll;
|
57 | 58 | import org.junit.jupiter.api.BeforeEach;
|
58 | 59 | import org.junit.jupiter.api.Test;
|
|
94 | 95 | import org.apache.xtable.model.storage.DataLayoutStrategy;
|
95 | 96 | import org.apache.xtable.model.storage.FileFormat;
|
96 | 97 | import org.apache.xtable.model.storage.InternalDataFile;
|
| 98 | +import org.apache.xtable.model.storage.InternalFile; |
97 | 99 | import org.apache.xtable.model.storage.PartitionFileGroup;
|
98 | 100 | import org.apache.xtable.model.storage.TableFormat;
|
99 | 101 | import org.apache.xtable.schema.SchemaFieldFinder;
|
@@ -431,6 +433,39 @@ public void testGetTargetCommitIdentifierWithNullSourceIdentifier() throws Excep
|
431 | 433 | assertFalse(unmappedTargetId.isPresent());
|
432 | 434 | }
|
433 | 435 |
|
| 436 | + @Test |
| 437 | + public void testTimestampNtz() { |
| 438 | + InternalSchema schema1 = getInternalSchemaWithTimestampNtz(); |
| 439 | + List<InternalField> fields2 = new ArrayList<>(schema1.getFields()); |
| 440 | + fields2.add( |
| 441 | + InternalField.builder() |
| 442 | + .name("float_field") |
| 443 | + .schema( |
| 444 | + InternalSchema.builder() |
| 445 | + .name("float") |
| 446 | + .dataType(InternalType.FLOAT) |
| 447 | + .isNullable(true) |
| 448 | + .build()) |
| 449 | + .build()); |
| 450 | + InternalSchema schema2 = getInternalSchema().toBuilder().fields(fields2).build(); |
| 451 | + InternalTable table1 = getInternalTable(tableName, basePath, schema1, null, LAST_COMMIT_TIME); |
| 452 | + InternalTable table2 = getInternalTable(tableName, basePath, schema2, null, LAST_COMMIT_TIME); |
| 453 | + |
| 454 | + InternalDataFile dataFile1 = getDataFile(1, Collections.emptyList(), basePath); |
| 455 | + InternalDataFile dataFile2 = getDataFile(2, Collections.emptyList(), basePath); |
| 456 | + InternalDataFile dataFile3 = getDataFile(3, Collections.emptyList(), basePath); |
| 457 | + |
| 458 | + InternalSnapshot snapshot1 = buildSnapshot(table1, "0", dataFile1, dataFile2); |
| 459 | + InternalSnapshot snapshot2 = buildSnapshot(table2, "1", dataFile2, dataFile3); |
| 460 | + |
| 461 | + TableFormatSync.getInstance() |
| 462 | + .syncSnapshot(Collections.singletonList(conversionTarget), snapshot1); |
| 463 | + validateDeltaTableUsingSpark(basePath, new HashSet<>(Arrays.asList(dataFile1, dataFile2))); |
| 464 | + TableFormatSync.getInstance() |
| 465 | + .syncSnapshot(Collections.singletonList(conversionTarget), snapshot2); |
| 466 | + validateDeltaTableUsingSpark(basePath, new HashSet<>(Arrays.asList(dataFile2, dataFile3))); |
| 467 | + } |
| 468 | + |
434 | 469 | private static Stream<Arguments> timestampPartitionTestingArgs() {
|
435 | 470 | return Stream.of(
|
436 | 471 | Arguments.of(PartitionTransformType.YEAR),
|
@@ -472,6 +507,13 @@ private void validateDeltaTable(
|
472 | 507 | internalDataFiles.size(), count, "Number of files from DeltaScan don't match expectation");
|
473 | 508 | }
|
474 | 509 |
|
| 510 | + private void validateDeltaTableUsingSpark( |
| 511 | + Path basePath, Set<InternalDataFile> internalDataFiles) { |
| 512 | + Dataset<Row> dataset = sparkSession.read().format("delta").load(basePath.toString()); |
| 513 | + long countFromFiles = internalDataFiles.stream().mapToLong(InternalFile::getRecordCount).sum(); |
| 514 | + Assertions.assertEquals(countFromFiles, dataset.count()); |
| 515 | + } |
| 516 | + |
475 | 517 | private InternalSnapshot buildSnapshot(
|
476 | 518 | InternalTable table, String sourceIdentifier, InternalDataFile... dataFiles) {
|
477 | 519 | return InternalSnapshot.builder()
|
@@ -563,6 +605,25 @@ private InternalSchema getInternalSchema() {
|
563 | 605 | .build();
|
564 | 606 | }
|
565 | 607 |
|
| 608 | + private InternalSchema getInternalSchemaWithTimestampNtz() { |
| 609 | + Map<InternalSchema.MetadataKey, Object> timestampMetadata = new HashMap<>(); |
| 610 | + timestampMetadata.put( |
| 611 | + InternalSchema.MetadataKey.TIMESTAMP_PRECISION, InternalSchema.MetadataValue.MICROS); |
| 612 | + List<InternalField> fields = new ArrayList<>(getInternalSchema().getFields()); |
| 613 | + fields.add( |
| 614 | + InternalField.builder() |
| 615 | + .name("timestamp_ntz_field") |
| 616 | + .schema( |
| 617 | + InternalSchema.builder() |
| 618 | + .name("time_ntz") |
| 619 | + .dataType(InternalType.TIMESTAMP_NTZ) |
| 620 | + .isNullable(true) |
| 621 | + .metadata(timestampMetadata) |
| 622 | + .build()) |
| 623 | + .build()); |
| 624 | + return getInternalSchema().toBuilder().fields(fields).build(); |
| 625 | + } |
| 626 | + |
566 | 627 | private static SparkSession buildSparkSession() {
|
567 | 628 | SparkConf sparkConf =
|
568 | 629 | new SparkConf()
|
|
0 commit comments