[feature](function) support variadic template type in SQL function (#17985)

Inspired by c++ function `std::vector::emplace_back()`, we can use variadic template for this issue.

e.g.

```
[['struct'], 'STRUCT<TYPES>', ['TYPES'], 'ALWAYS_NOT_NULLABLE', ['TYPES...']]
```

`...TYPES` in template_types defines a variadic template `TYPE`.  Then the variadic template will be expanded to multiple normal templates based on actual input arguments at runtime in FE.

But make sure `TYPES...` is placed on the last position in all template type arguments.

BTW, the origin template function logic is not affected.
This commit is contained in:
xy720
2023-03-28 11:08:24 +08:00
committed by GitHub
parent d2839eb41f
commit daeaa91dd6
7 changed files with 263 additions and 10 deletions

View File

@ -40,6 +40,7 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
@ -858,6 +859,28 @@ public class Function implements Writable {
return false;
}
public boolean hasVariadicTemplateArg() {
for (Type t : getArgs()) {
if (t.needExpandTemplateType()) {
return true;
}
}
return false;
}
// collect expand size of variadic template
public void collectTemplateExpandSize(Type[] args, Map<String, Integer> expandSizeMap) throws TypeException {
for (int i = argTypes.length - 1; i >= 0; i--) {
if (argTypes[i].hasTemplateType()) {
if (argTypes[i].needExpandTemplateType()) {
argTypes[i].collectTemplateExpandSize(
Arrays.copyOfRange(args, i, args.length), expandSizeMap);
}
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) {

View File

@ -1227,9 +1227,14 @@ public class FunctionSet<T> {
List<Function> normalFunctions = Lists.newArrayList();
List<Function> templateFunctions = Lists.newArrayList();
List<Function> variadicTemplateFunctions = Lists.newArrayList();
for (Function fn : fns) {
if (fn.hasTemplateArg()) {
templateFunctions.add(fn);
if (!fn.hasVariadicTemplateArg()) {
templateFunctions.add(fn);
} else {
variadicTemplateFunctions.add(fn);
}
} else {
normalFunctions.add(fn);
}
@ -1244,12 +1249,29 @@ public class FunctionSet<T> {
// then specialize template functions and try them
List<Function> specializedTemplateFunctions = Lists.newArrayList();
for (Function f : templateFunctions) {
f = FunctionSet.specializeTemplateFunction(f, desc);
f = FunctionSet.specializeTemplateFunction(f, desc, false);
if (f != null) {
specializedTemplateFunctions.add(f);
}
}
return getFunction(desc, mode, specializedTemplateFunctions);
// try template function second
fn = getFunction(desc, mode, specializedTemplateFunctions);
if (fn != null) {
return fn;
}
// then specialize variadic template function and try them
List<Function> specializedVariadicTemplateFunctions = Lists.newArrayList();
for (Function f : variadicTemplateFunctions) {
f = FunctionSet.specializeTemplateFunction(f, desc, true);
if (f != null) {
specializedVariadicTemplateFunctions.add(f);
}
}
// try variadic template function
return getFunction(desc, mode, specializedVariadicTemplateFunctions);
}
private Function getFunction(Function desc, Function.CompareMode mode, List<Function> fns) {
@ -1292,19 +1314,43 @@ public class FunctionSet<T> {
return null;
}
public static Function specializeTemplateFunction(Function templateFunction, Function requestFunction) {
public static Function specializeTemplateFunction(Function templateFunction, Function requestFunction, boolean isVariadic) {
try {
boolean hasTemplateType = false;
LOG.debug("templateFunction signature: " + templateFunction.signatureString()
+ " return: " + templateFunction.getReturnType());
LOG.debug("requestFunction signature: " + requestFunction.signatureString()
+ " return: " + requestFunction.getReturnType());
List<Type> newArgTypes = Lists.newArrayList();
List<Type> newRetType = Lists.newArrayList();
if (isVariadic) {
Map<String, Integer> expandSizeMap = Maps.newHashMap();
templateFunction.collectTemplateExpandSize(requestFunction.getArgs(), expandSizeMap);
// expand the variadic template in arg types
for (Type argType : templateFunction.getArgs()) {
if (argType.needExpandTemplateType()) {
newArgTypes.addAll(argType.expandVariadicTemplateType(expandSizeMap));
} else {
newArgTypes.add(argType);
}
}
// expand the variadic template in ret type
if (templateFunction.getReturnType().needExpandTemplateType()) {
newRetType.addAll(templateFunction.getReturnType().expandVariadicTemplateType(expandSizeMap));
Preconditions.checkState(newRetType.size() == 1);
} else {
newRetType.add(templateFunction.getReturnType());
}
} else {
newArgTypes.addAll(Lists.newArrayList(templateFunction.getArgs()));
newRetType.add(templateFunction.getReturnType());
}
Function specializedFunction = templateFunction;
if (templateFunction instanceof ScalarFunction) {
ScalarFunction f = (ScalarFunction) templateFunction;
specializedFunction = new ScalarFunction(f.getFunctionName(), Lists.newArrayList(f.getArgs()),
f.getReturnType(), f.hasVarArgs(), f.getSymbolName(), f.getBinaryType(),
f.isUserVisible(), f.isVectorized(), f.getNullableMode());
specializedFunction = new ScalarFunction(f.getFunctionName(), newArgTypes, newRetType.get(0), f.hasVarArgs(),
f.getSymbolName(), f.getBinaryType(), f.isUserVisible(), f.isVectorized(), f.getNullableMode());
} else {
// TODO(xk)
}