-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support flattening and unflattening structured types (#79)
- Loading branch information
1 parent
08729e9
commit 64b7b73
Showing
19 changed files
with
372 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,55 @@ | ||
plugins { | ||
id 'java' | ||
id 'maven-publish' | ||
} | ||
|
||
dependencies { | ||
implementation project(':hoptimator-api') | ||
implementation libs.avro | ||
implementation libs.calcite.core | ||
} | ||
|
||
publishing { | ||
repositories { | ||
maven { | ||
name 'GitHubPackages' | ||
url = 'https://maven.pkg.github.com/linkedin/Hoptimator' | ||
credentials { | ||
username = System.getenv('GITHUB_ACTOR') | ||
password = System.getenv('GITHUB_TOKEN') | ||
} | ||
} | ||
maven { | ||
name 'LinkedInJFrog' | ||
url 'https://linkedin.jfrog.io/artifactory/hoptimator' | ||
credentials { | ||
username = System.getenv('JFROG_USERNAME') | ||
password = System.getenv('JFROG_API_KEY') | ||
} | ||
} | ||
} | ||
publications { | ||
maven(MavenPublication) { | ||
groupId = 'com.linkedin.hoptimator' | ||
artifactId = 'hoptimator-avro' | ||
version = System.getenv('VERSION') | ||
from components.java | ||
pom { | ||
name = 'hoptimator-avro' | ||
description = 'Hoptimator plugin for Apache Avro' | ||
url = 'https://github.com/linkedin/Hoptimator' | ||
licenses { | ||
license { | ||
name = 'BSD 2-Clause' | ||
url = 'https://raw.githubusercontent.com/linkedin/Hoptimator/main/LICENSE' | ||
} | ||
} | ||
scm { | ||
connection = 'scm:git:git://github.com:linkedin/Hoptimator.git' | ||
developerConnection = 'scm:git:ssh://github.com:linkedin/Hoptimator.git' | ||
url = 'https://github.com/linkedin/Hoptimator' | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,69 @@ | ||
plugins { | ||
id 'java' | ||
id 'maven-publish' | ||
} | ||
|
||
dependencies { | ||
implementation project(':hoptimator-api') | ||
implementation libs.calcite.core | ||
|
||
testImplementation(testFixtures(project(':hoptimator-jdbc'))) | ||
testImplementation(platform('org.junit:junit-bom:5.11.3')) | ||
testImplementation 'org.junit.jupiter:junit-jupiter' | ||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' | ||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' | ||
} | ||
|
||
test { | ||
useJUnitPlatform { | ||
excludeTags 'integration' | ||
} | ||
testLogging { | ||
events "passed", "skipped", "failed" | ||
} | ||
} | ||
|
||
publishing { | ||
repositories { | ||
maven { | ||
name 'GitHubPackages' | ||
url = 'https://maven.pkg.github.com/linkedin/Hoptimator' | ||
credentials { | ||
username = System.getenv('GITHUB_ACTOR') | ||
password = System.getenv('GITHUB_TOKEN') | ||
} | ||
} | ||
maven { | ||
name 'LinkedInJFrog' | ||
url 'https://linkedin.jfrog.io/artifactory/hoptimator' | ||
credentials { | ||
username = System.getenv('JFROG_USERNAME') | ||
password = System.getenv('JFROG_API_KEY') | ||
} | ||
} | ||
} | ||
publications { | ||
maven(MavenPublication) { | ||
groupId = 'com.linkedin.hoptimator' | ||
artifactId = 'hoptimator-util' | ||
version = System.getenv('VERSION') | ||
from components.java | ||
pom { | ||
name = 'hoptimator-util' | ||
description = 'Utilities to help with extending Hoptimator' | ||
url = 'https://github.com/linkedin/Hoptimator' | ||
licenses { | ||
license { | ||
name = 'BSD 2-Clause' | ||
url = 'https://raw.githubusercontent.com/linkedin/Hoptimator/main/LICENSE' | ||
} | ||
} | ||
scm { | ||
connection = 'scm:git:git://github.com:linkedin/Hoptimator.git' | ||
developerConnection = 'scm:git:ssh://github.com:linkedin/Hoptimator.git' | ||
url = 'https://github.com/linkedin/Hoptimator' | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
hoptimator-util/src/main/java/com/linkedin/hoptimator/util/DataTypeUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package com.linkedin.hoptimator.util; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.LinkedHashMap; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import org.apache.calcite.rel.type.RelDataType; | ||
import org.apache.calcite.rel.type.RelDataTypeFactory; | ||
import org.apache.calcite.rel.type.RelDataTypeField; | ||
import org.apache.calcite.sql.type.SqlTypeName; | ||
|
||
|
||
public final class DataTypeUtils { | ||
|
||
private DataTypeUtils() { | ||
} | ||
|
||
/** | ||
* Flattens nested structs and complex arrays. | ||
* | ||
* Nested structs like `FOO Row(BAR Row(QUX VARCHAR)))` are promoted to | ||
* top-level fields like `FOO$BAR$QUX VARCHAR`. | ||
* | ||
* Complex arrays are demoted to just `ANY ARRAY`. Primitive arrays are | ||
* unchanged. | ||
* | ||
*/ | ||
public static RelDataType flatten(RelDataType dataType, RelDataTypeFactory typeFactory) { | ||
if (!dataType.isStruct()) { | ||
return dataType; | ||
} | ||
RelDataTypeFactory.Builder builder = new RelDataTypeFactory.Builder(typeFactory); | ||
flattenInto(typeFactory, dataType, builder, Collections.emptyList()); | ||
return builder.build(); | ||
} | ||
|
||
private static void flattenInto(RelDataTypeFactory typeFactory, RelDataType dataType, | ||
RelDataTypeFactory.Builder builder, List<String> path) { | ||
if (dataType.getComponentType() != null && (dataType.getComponentType().isStruct() | ||
|| dataType.getComponentType().getComponentType() != null)) { | ||
// demote complex arrays to just `ANY ARRAY` | ||
builder.add(path.stream().collect(Collectors.joining("$")), typeFactory.createArrayType( | ||
typeFactory.createSqlType(SqlTypeName.ANY), -1)); | ||
} else if (!dataType.isStruct()) { | ||
builder.add(path.stream().collect(Collectors.joining("$")), dataType); | ||
} else { | ||
for (RelDataTypeField field : dataType.getFieldList()) { | ||
flattenInto(typeFactory, field.getType(), builder, Stream.concat(path.stream(), | ||
Stream.of(field.getName())).collect(Collectors.toList())); | ||
} | ||
} | ||
} | ||
|
||
/** Restructures flattened types, from `FOO$BAR VARCHAR` to `FOO Row(BAR VARCHAR...)` */ | ||
public static RelDataType unflatten(RelDataType dataType, RelDataTypeFactory typeFactory) { | ||
if (!dataType.isStruct()) { | ||
throw new IllegalArgumentException("Can only unflatten a struct type."); | ||
} | ||
Node root = new Node(); | ||
for (RelDataTypeField field : dataType.getFieldList()) { | ||
buildNodes(root, field.getName(), field.getType()); | ||
} | ||
return buildRecord(root, typeFactory); | ||
} | ||
|
||
private static void buildNodes(Node pos, String name, RelDataType dataType) { | ||
if (!name.contains("$")) { | ||
pos.children.put(name, new Node(dataType)); | ||
} else { | ||
String[] parts = name.split("\\$", 2); | ||
Node child = pos.children.computeIfAbsent(parts[0], x -> new Node()); | ||
buildNodes(child, parts[1], dataType); | ||
} | ||
} | ||
|
||
private static RelDataType buildRecord(Node node, RelDataTypeFactory typeFactory) { | ||
if (node.dataType != null) { | ||
return node.dataType; | ||
} | ||
RelDataTypeFactory.Builder builder = new RelDataTypeFactory.Builder(typeFactory); | ||
for (LinkedHashMap.Entry<String, Node> child : node.children.entrySet()) { | ||
builder.add(child.getKey(), buildRecord(child.getValue(), typeFactory)); | ||
} | ||
return builder.build(); | ||
} | ||
|
||
private static class Node { | ||
RelDataType dataType; | ||
LinkedHashMap<String, Node> children = new LinkedHashMap<>(); | ||
|
||
Node(RelDataType dataType) { | ||
this.dataType = dataType; | ||
} | ||
|
||
Node() { | ||
// nop | ||
} | ||
} | ||
} |
38 changes: 0 additions & 38 deletions
38
hoptimator-util/src/main/java/com/linkedin/hoptimator/util/HoptimatorJdbcCatalogSchema.java
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.