Skip to content

Commit 54de1aa

Browse files
committed
Add utility functions to resolve intermediate type in generic aggregate functions
1 parent 543b48e commit 54de1aa

File tree

8 files changed

+686
-16
lines changed

8 files changed

+686
-16
lines changed

presto-client/src/main/java/com/facebook/presto/client/FixJsonDataUtils.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ private static Object fixValue(TypeSignature signature, Object value)
109109
if (List.class.isAssignableFrom(value.getClass())) {
110110
List<Object> fixedValue = new ArrayList<>();
111111
for (Object object : List.class.cast(value)) {
112-
fixedValue.add(fixValue(signature.getTypeParametersAsTypeSignatures().get(0), object));
112+
fixedValue.add(fixValue(signature.getTypeOrNamedTypeParametersAsTypeSignatures().get(0), object));
113113
}
114114
return fixedValue;
115115
}
@@ -122,8 +122,8 @@ else if (value.getClass() == String.class) {
122122
}
123123
if (signature.getBase().equals(MAP)) {
124124
if (Map.class.isAssignableFrom(value.getClass())) {
125-
TypeSignature keySignature = signature.getTypeParametersAsTypeSignatures().get(0);
126-
TypeSignature valueSignature = signature.getTypeParametersAsTypeSignatures().get(1);
125+
TypeSignature keySignature = signature.getTypeOrNamedTypeParametersAsTypeSignatures().get(0);
126+
TypeSignature valueSignature = signature.getTypeOrNamedTypeParametersAsTypeSignatures().get(1);
127127
Map<Object, Object> fixedValue = new HashMap<>();
128128
for (Map.Entry<?, ?> entry : (Set<Map.Entry<?, ?>>) Map.class.cast(value).entrySet()) {
129129
fixedValue.put(fixValue(keySignature, entry.getKey()), fixValue(valueSignature, entry.getValue()));

presto-common/src/main/java/com/facebook/presto/common/type/ParameterKind.java

+32
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,38 @@
2020

2121
import java.util.Optional;
2222

23+
/**
24+
* The {@code ParameterKind} enum represents various kinds of parameters used in Presto's type system.
25+
* The available parameter kinds are:
26+
*
27+
* <ul>
28+
* <li><b>TYPE</b>:
29+
* Used when the parameter itself is of type {@code TYPE_SIGNATURE}, representing a type definition.</li>
30+
*
31+
* <li><b>NAMED_TYPE</b>:
32+
* Represents parameters that are explicitly named and can be referenced using their name.
33+
* This is primarily used when the base type is a row type.</li>
34+
*
35+
* <li><b>LONG</b>:
36+
* Used for types that take a long literal as a parameter. Examples include
37+
* types like {@code decimal} and {@code varchar}.</li>
38+
*
39+
* <li><b>VARIABLE</b>:
40+
* Used when variables are passed as parameters. This allows dynamic and flexible parameter handling.</li>
41+
*
42+
* <li><b>LONG_ENUM</b>:
43+
* Represents a mapping of string values to long values. It is efficient for cases where
44+
* symbolic names correspond to numeric values.</li>
45+
*
46+
* <li><b>VARCHAR_ENUM</b>:
47+
* Represents a mapping of string values to string values. This is useful for symbolic names
48+
* that do not require numeric representation.</li>
49+
*
50+
* <li><b>DISTINCT_TYPE</b>:
51+
* Represents distinct user-defined types, enabling the creation of custom types in Presto's type system.</li>
52+
* </ul>
53+
*/
54+
2355
@ThriftEnum
2456
public enum ParameterKind
2557
{

presto-common/src/main/java/com/facebook/presto/common/type/TypeSignature.java

+13-6
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,24 @@ public List<TypeSignatureParameter> getParameters()
133133
return parameters;
134134
}
135135

136-
public List<TypeSignature> getTypeParametersAsTypeSignatures()
136+
public List<TypeSignature> getTypeOrNamedTypeParametersAsTypeSignatures()
137137
{
138138
List<TypeSignature> result = new ArrayList<>();
139139
for (TypeSignatureParameter parameter : parameters) {
140-
if (parameter.getKind() != ParameterKind.TYPE) {
141-
throw new IllegalStateException(
142-
format("Expected all parameters to be TypeSignatures but [%s] was found", parameter.toString()));
140+
switch (parameter.getKind()) {
141+
case TYPE:
142+
result.add(parameter.getTypeSignature());
143+
break;
144+
case NAMED_TYPE:
145+
result.add(parameter.getNamedTypeSignature().getTypeSignature());
146+
break;
147+
default:
148+
throw new IllegalStateException(
149+
format("Expected all parameters to be of kind TYPE or NAMED_TYPE but [%s] kind was found for parameter: [%s]",
150+
parameter.getKind(), parameter));
143151
}
144-
result.add(parameter.getTypeSignature());
145152
}
146-
return result;
153+
return unmodifiableList(result);
147154
}
148155

149156
public boolean isCalculated()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.facebook.presto.common.type;
15+
16+
import java.util.ArrayList;
17+
import java.util.HashMap;
18+
import java.util.List;
19+
import java.util.Map;
20+
import java.util.Optional;
21+
import java.util.stream.Collectors;
22+
23+
import static java.util.Collections.unmodifiableList;
24+
25+
public final class TypeSignatureUtils
26+
{
27+
private TypeSignatureUtils() {}
28+
29+
public static TypeSignature resolveIntermediateType(TypeSignature actualTypeSignature, List<TypeSignature> actualTypeSignatureParameters, List<TypeSignature> expectedTypeSignatureParameters)
30+
{
31+
Map<TypeSignature, TypeSignature> typeSignatureMap = getTypeSignatureMap(actualTypeSignatureParameters, expectedTypeSignatureParameters);
32+
return resolveTypeSignatures(actualTypeSignature, typeSignatureMap).getTypeSignature();
33+
}
34+
35+
private static Map<TypeSignature, TypeSignature> getTypeSignatureMap(List<TypeSignature> parameters, List<TypeSignature> argumentTypes)
36+
{
37+
Map<TypeSignature, TypeSignature> typeSignatureMap = new HashMap<>();
38+
if (argumentTypes.size() != parameters.size()) {
39+
throw new IllegalStateException(
40+
"Parameters size: " + parameters.size() + " and argumentTypes size: " + argumentTypes.size() + " do not match !");
41+
}
42+
43+
for (int i = 0; i < argumentTypes.size(); i++) {
44+
TypeSignature parameter = parameters.get(i);
45+
TypeSignature argumentType = argumentTypes.get(i);
46+
47+
// Realistically, there are only two cases,
48+
// 1. When parameter.getParameters() is empty :
49+
// - Eg: parameter type = generic type(T) and argumentType = array(double), we can directly put
50+
// map.put(parameter, argumentType) there is no need to loop over the argumentType.
51+
// - Eg: param type = non-generic type(bigint) , in this case argumentType will also be of the
52+
// same non-generic type hence map.put(parameter, argumentType) is valid here too.
53+
// 2. When parameter.getParameters() is not empty:
54+
// - Eg: parameter type = generic type (array(T)) and argumentType = array(double), we recursively run
55+
// this function until we reach condition 1.
56+
// Example calls for parameter type = generic type (array(T)) and argumentType = array(double)
57+
// Iteration 1:
58+
// Parameter:
59+
// Type signature base = array
60+
// parameters = T
61+
// ArgumentType :
62+
// Type signature base = array
63+
// parameters = double
64+
// Iteration 2:
65+
// Parameter:
66+
// Type signature base = T
67+
// parameters = empty
68+
// ArgumentType :
69+
// Type signature base = double
70+
// parameters = empty
71+
// return typeSignatureMap = {"T": "double"}
72+
73+
// If parameter params are of type long e.g decimal(15, 2) or
74+
// of type varchar e.g decimal(i4, i5), we don't need to recursively call the function on its params
75+
if (parameter.getParameters().isEmpty() || !(areParametersTypeSignatureOrNamedTypedSignature(parameter.getParameters()))) {
76+
typeSignatureMap.put(parameter, argumentType);
77+
}
78+
else {
79+
typeSignatureMap.putAll(getTypeSignatureMap(
80+
parameter.getTypeOrNamedTypeParametersAsTypeSignatures(),
81+
argumentType.getTypeOrNamedTypeParametersAsTypeSignatures()));
82+
}
83+
}
84+
return typeSignatureMap;
85+
}
86+
87+
// A utility function to resolve intermediate type signatures.
88+
// Realistically, these are the different cases that we can face:
89+
// 1. If there are no params/argTypes in the first call itself, return the resolvedType from the map directly.
90+
// 2. If params != empty, we loop over the params:
91+
// - Check whether the param is present in the typeSignatureMap, if its present,
92+
// add the resolvedTypeParameterSignature mapping as a param directly and continue to the next param.
93+
// - The idea behind this logic is that if param type is present in the typeSignatureMap, it means that for
94+
// that particular param, we could just resolve the type from the map and no need to recursively call as its completely resolved.
95+
// - Eg: param : T , map : {"T":"array(double)"}.
96+
// - If the mapping isn't present, we recursively call the resolveTypeSignatures() again.
97+
// - Example calls for param: array(T) , map : {"T" : "array(double)"}
98+
// resolvedIntermediateType = null
99+
// Iteration 1:
100+
// Parameter:
101+
// key to lookup in map: array(T), key found : false
102+
// Iteration 2:
103+
// Parameter:
104+
// key to lookup in map: T, key found : true
105+
// resolvedIntermediateType = array(double)
106+
// return resolvedIntermediateType = array(array(double))
107+
//
108+
//
109+
private static NamedTypeSignature resolveTypeSignatures(TypeSignature typeSignature, Map<TypeSignature, TypeSignature> typeSignatureMap)
110+
{
111+
if (typeSignatureMap.containsKey(typeSignature)) {
112+
TypeSignature resolvedTypeSignature = typeSignatureMap.get(typeSignature);
113+
return new NamedTypeSignature(Optional.empty(), resolvedTypeSignature);
114+
}
115+
List<NamedTypeSignature> namedTypeSignatures = new ArrayList<>();
116+
List<TypeSignature> typeSignatures = new ArrayList<>();
117+
for (TypeSignature typeParameterSignature : typeSignature.getTypeOrNamedTypeParametersAsTypeSignatures()) {
118+
// if base is "row" typeSignature, all typeParameterSignatures need to be of type NamedTypeSignature.
119+
boolean isRowTypeSignatureBase = typeSignature.getBase().equals("row");
120+
if (typeSignatureMap.containsKey(typeParameterSignature)) {
121+
TypeSignature resolvedTypeParameterSignature = typeSignatureMap.get(typeParameterSignature);
122+
if (isRowTypeSignatureBase) {
123+
namedTypeSignatures.add(new NamedTypeSignature(Optional.empty(), resolvedTypeParameterSignature));
124+
}
125+
else {
126+
typeSignatures.add(resolvedTypeParameterSignature);
127+
}
128+
}
129+
else {
130+
NamedTypeSignature namedTypeSignature = resolveTypeSignatures(typeParameterSignature, typeSignatureMap);
131+
if (isRowTypeSignatureBase) {
132+
namedTypeSignatures.add(namedTypeSignature);
133+
}
134+
else {
135+
typeSignatures.add(namedTypeSignature.getTypeSignature());
136+
}
137+
}
138+
}
139+
140+
List<TypeSignatureParameter> parameters;
141+
if (!typeSignatures.isEmpty()) {
142+
parameters = typeSignatures.stream().map(TypeSignatureParameter::of).collect(Collectors.toList());
143+
}
144+
else {
145+
parameters = namedTypeSignatures.stream().map(TypeSignatureParameter::of).collect(Collectors.toList());
146+
}
147+
148+
return new NamedTypeSignature(Optional.empty(), new TypeSignature(typeSignature.getBase(), unmodifiableList(parameters)));
149+
}
150+
151+
private static boolean areParametersTypeSignatureOrNamedTypedSignature(List<TypeSignatureParameter> parameters)
152+
{
153+
return !parameters.isEmpty() && parameters.stream()
154+
.map(TypeSignatureParameter::getKind)
155+
.allMatch(parameterKind ->
156+
parameterKind.equals(ParameterKind.NAMED_TYPE) || parameterKind.equals(ParameterKind.TYPE));
157+
}
158+
}

0 commit comments

Comments
 (0)