Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2018-2025 Expedia, Inc.
* Copyright (C) 2018-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,11 +29,15 @@
*/
public class GlueMetadataStringCleaner {

private static final int MAX_COLUMN_COMMENT_LENGTH = 254;
private static final int MAX_DESCRIPTION_LENGTH = 2048;

public TableInput cleanTable(TableInput input) {
// Clean SerDes
cleanColumns(input.getStorageDescriptor().getColumns());
// Clean Partition Keys
cleanColumns(input.getPartitionKeys());
input.setDescription(cleanDescription(input.getDescription()));
return input;
}

Expand All @@ -47,20 +51,28 @@ public PartitionInput cleanPartition(PartitionInput input) {
return input;
}

/**
* Cleans a Glue TableInput description to satisfy API constraints:
* <ul>
* <li>Unicode characters only</li>
* <li>Maximum 2048 characters</li>
* </ul>
*/
private String cleanDescription(String description) {
return truncateToMaxAllowedChars(removeNonUnicodeChars(description), MAX_DESCRIPTION_LENGTH);
}

private void cleanColumns(List<Column> columns) {
for (Column column : columns) {
column.setComment(this.truncateToMaxAllowedChars(this.removeNonUnicodeChars(column.getComment())));
column.setComment(this.truncateToMaxAllowedChars(this.removeNonUnicodeChars(column.getComment()), MAX_COLUMN_COMMENT_LENGTH));
}
}

public String truncateToMaxAllowedChars(String input) {
public String truncateToMaxAllowedChars(String input, int maxLength) {
if (input == null) {
return null;
}
if (input.length() > 254) {
return input.substring(0, 254);
}
return input;
return input.length() > maxLength ? input.substring(0, maxLength) : input;
}

public String removeNonUnicodeChars(String input) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2018-2025 Expedia, Inc.
* Copyright (C) 2018-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.expediagroup.apiary.extensions.gluesync.listener.service;

import java.util.*;
Expand Down Expand Up @@ -146,7 +145,7 @@ private boolean columnsEqualsCleaned(List<org.apache.hadoop.hive.metastore.api.F
return false;
// Clean Hive comment before comparing to cleaned Glue comment
String cleanedHiveComment = cleaner.removeNonUnicodeChars(h.getComment());
cleanedHiveComment = cleaner.truncateToMaxAllowedChars(cleanedHiveComment);
cleanedHiveComment = cleaner.truncateToMaxAllowedChars(cleanedHiveComment, 254);
if (!Objects.equals(cleanedHiveComment, g.getComment()))
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2018-2025 Expedia, Inc.
* Copyright (C) 2018-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -93,11 +93,15 @@ public TableInput transformTable(final Table table) {
.withSortColumns(sortOrders)
.withStoredAsSubDirectories(storageDescriptor.isStoredAsSubDirectories());

Map<String, String> tableParams = table.getParameters();
String description = (tableParams != null) ? tableParams.get("comment") : null;

return new TableInput()
.withName(table.getTableName())
.withDescription(description)
.withLastAccessTime(date)
.withOwner(table.getOwner())
.withParameters(addClassification(table.getParameters(), storageDescriptor.getInputFormat()))
.withParameters(addClassification(tableParams, storageDescriptor.getInputFormat()))
.withPartitionKeys(partitionKeys)
.withRetention(table.getRetention())
.withStorageDescriptor(sd)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2018-2025 Expedia, Inc.
* Copyright (C) 2018-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,8 @@
*/
package com.expediagroup.apiary.extensions.gluesync.listener;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.util.Collections;
Expand Down Expand Up @@ -64,6 +66,41 @@ public void testTableInput() {
assertTrue(partitionColumn.getComment().startsWith("A"));
}

@Test
public void testTableInputDescriptionNonUnicodeCharsRemoved() {
TableInput tableInput = new TableInput();
tableInput.setStorageDescriptor(new StorageDescriptor().withColumns(Collections.emptyList()));
tableInput.setPartitionKeys(Collections.emptyList());
tableInput.setDescription("\uD999valid description");

TableInput result = glueMetadataStringCleaner.cleanTable(tableInput);

assertEquals("valid description", result.getDescription());
}

@Test
public void testTableInputDescriptionTruncatedTo2048Chars() {
TableInput tableInput = new TableInput();
tableInput.setStorageDescriptor(new StorageDescriptor().withColumns(Collections.emptyList()));
tableInput.setPartitionKeys(Collections.emptyList());
tableInput.setDescription(generateCharString(3000));

TableInput result = glueMetadataStringCleaner.cleanTable(tableInput);

assertEquals(2048, result.getDescription().length());
}

@Test
public void testTableInputDescriptionNullUnchanged() {
TableInput tableInput = new TableInput();
tableInput.setStorageDescriptor(new StorageDescriptor().withColumns(Collections.emptyList()));
tableInput.setPartitionKeys(Collections.emptyList());

TableInput result = glueMetadataStringCleaner.cleanTable(tableInput);

assertNull(result.getDescription());
}

@Test
public void testPartitionInput() {
PartitionInput partitionInput = new PartitionInput();
Expand All @@ -86,7 +123,7 @@ public void testPartitionInput() {

private String generateCharString(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < 300; i++) {
for (int i = 0; i < length; i++) {
sb.append("A");
}
return sb.toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2018-2025 Expedia, Inc.
* Copyright (C) 2018-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -135,7 +135,7 @@ private com.amazonaws.services.glue.model.Partition buildGluePartitionFromHive(
c.setName(fs.getName());
c.setType(fs.getType());
String cleanedComment = cleaner.removeNonUnicodeChars(fs.getComment());
cleanedComment = cleaner.truncateToMaxAllowedChars(cleanedComment);
cleanedComment = cleaner.truncateToMaxAllowedChars(cleanedComment, 254);
c.setComment(cleanedComment);
cols.add(c);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2018-2025 Expedia, Inc.
* Copyright (C) 2018-2026 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import java.util.Arrays;
import java.util.Collections;
Expand All @@ -27,9 +28,11 @@

import org.apache.hadoop.hive.metastore.api.FieldSchema;
import org.apache.hadoop.hive.metastore.api.Partition;
import org.apache.hadoop.hive.metastore.api.Table;
import org.junit.Test;

import com.amazonaws.services.glue.model.PartitionInput;
import com.amazonaws.services.glue.model.TableInput;

public class HiveToGlueTransformerTest {
private final HiveToGlueTransformer transformer = new HiveToGlueTransformer(null);
Expand Down Expand Up @@ -103,6 +106,67 @@ private Partition createCompleteHivePartition() {
return partition;
}

@Test
public void testTransformTablePopulatesDescriptionFromCommentParam() {
Table table = createMinimalHiveTable();
table.getParameters().put("comment", "my table description");

TableInput result = transformer.transformTable(table);

assertEquals("my table description", result.getDescription());
}

@Test
public void testTransformTableDescriptionIsNullWhenNoCommentParam() {
Table table = createMinimalHiveTable();

TableInput result = transformer.transformTable(table);

assertNull(result.getDescription());
}

@Test
public void testTransformTableDescriptionIsNullWhenParamsExistButNoComment() {
Table table = createMinimalHiveTable();
table.getParameters().put("other.param", "some value");

TableInput result = transformer.transformTable(table);

assertNull(result.getDescription());
}

private Table createMinimalHiveTable() {
Table table = new Table();
table.setTableName("test_table");
table.setDbName("test_db");
table.setOwner("test_owner");
table.setTableType("EXTERNAL_TABLE");
table.setParameters(new HashMap<>());

org.apache.hadoop.hive.metastore.api.StorageDescriptor sd = new org.apache.hadoop.hive.metastore.api.StorageDescriptor();
sd.setCols(Collections.emptyList());
sd.setBucketCols(Collections.emptyList());
sd.setCompressed(false);
sd.setInputFormat("org.apache.hadoop.mapred.TextInputFormat");
sd.setLocation("s3://test-bucket/path");
sd.setNumBuckets(0);
sd.setOutputFormat("org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat");
sd.setParameters(Collections.emptyMap());

org.apache.hadoop.hive.metastore.api.SerDeInfo serdeInfo = new org.apache.hadoop.hive.metastore.api.SerDeInfo();
serdeInfo.setName("test-serde");
serdeInfo.setSerializationLib("org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe");
serdeInfo.setParameters(Collections.emptyMap());
sd.setSerdeInfo(serdeInfo);

sd.setSortCols(Collections.emptyList());
sd.setStoredAsSubDirectories(false);
table.setSd(sd);
table.setPartitionKeys(Collections.emptyList());

return table;
}

/**
* Helper method to create test parameters
*/
Expand Down
Loading