/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.facebook.presto.metadata;
import com.facebook.presto.block.BlockEncodingManager;
import com.facebook.presto.spi.type.BigintType;
import com.facebook.presto.spi.type.DecimalType;
import com.facebook.presto.spi.type.StandardTypes;
import com.facebook.presto.spi.type.Type;
import com.facebook.presto.spi.type.TypeManager;
import com.facebook.presto.spi.type.TypeSignature;
import com.facebook.presto.sql.analyzer.FeaturesConfig;
import com.facebook.presto.sql.analyzer.TypeSignatureProvider;
import com.facebook.presto.type.TypeRegistry;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.testng.annotations.Test;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static com.facebook.presto.metadata.FunctionKind.SCALAR;
import static com.facebook.presto.metadata.Signature.comparableTypeParameter;
import static com.facebook.presto.metadata.Signature.typeVariable;
import static com.facebook.presto.metadata.Signature.withVariadicBound;
import static com.facebook.presto.spi.type.BooleanType.BOOLEAN;
import static com.facebook.presto.spi.type.DoubleType.DOUBLE;
import static com.facebook.presto.spi.type.IntegerType.INTEGER;
import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature;
import static com.facebook.presto.spi.type.VarcharType.VARCHAR;
import static com.facebook.presto.sql.analyzer.TypeSignatureProvider.fromTypes;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
public class TestSignatureBinder
{
private final TypeManager typeRegistry = new TypeRegistry();
{
// associate typeRegistry with a function registry
new FunctionRegistry(typeRegistry, new BlockEncodingManager(typeRegistry), new FeaturesConfig());
}
@Test
public void testBindLiteralForDecimal()
{
TypeSignature leftType = parseTypeSignature("decimal(p1,s1)", ImmutableSet.of("p1", "s1"));
TypeSignature rightType = parseTypeSignature("decimal(p2,s2)", ImmutableSet.of("p2", "s2"));
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(leftType, rightType)
.build();
assertThat(function)
.boundTo("decimal(2,1)", "decimal(1,0)")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of(
"p1", 2L,
"s1", 1L,
"p2", 1L,
"s2", 0L
)
));
}
@Test
public void testBindPartialDecimal()
{
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature("decimal(4,s)", ImmutableSet.of("s")))
.build();
assertThat(function)
.boundTo("decimal(2,1)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("s", 1L)
));
function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature("decimal(p,1)", ImmutableSet.of("p")))
.build();
assertThat(function)
.boundTo("decimal(2,0)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("p", 3L)
));
assertThat(function)
.boundTo("decimal(2,1)")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("p", 2L)
));
assertThat(function)
.boundTo("bigint")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("p", 20L)
));
}
@Test
public void testBindLiteralForVarchar()
{
TypeSignature leftType = parseTypeSignature("varchar(x)", ImmutableSet.of("x"));
TypeSignature rightType = parseTypeSignature("varchar(y)", ImmutableSet.of("y"));
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(leftType, rightType)
.build();
assertThat(function)
.boundTo("varchar(42)", "varchar(44)")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of(
"x", 42L,
"y", 44L
)
));
assertThat(function)
.boundTo("unknown", "varchar(44)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of(
"x", 0L,
"y", 44L
)
));
}
@Test
public void testBindLiteralForRepeatedVarcharWithReturn()
{
TypeSignature leftType = parseTypeSignature("varchar(x)", ImmutableSet.of("x"));
TypeSignature rightType = parseTypeSignature("varchar(x)", ImmutableSet.of("x"));
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(leftType, rightType)
.build();
assertThat(function)
.boundTo("varchar(44)", "varchar(44)")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("x", 44L)
));
assertThat(function)
.boundTo("varchar(44)", "varchar(42)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("x", 44L)
));
assertThat(function)
.boundTo("varchar(42)", "varchar(44)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("x", 44L)
));
assertThat(function)
.boundTo("unknown", "varchar(44)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("x", 44L)
));
}
@Test
public void testBindLiteralForRepeatedDecimal()
{
TypeSignature leftType = parseTypeSignature("decimal(p,s)", ImmutableSet.of("p", "s"));
TypeSignature rightType = parseTypeSignature("decimal(p,s)", ImmutableSet.of("p", "s"));
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(leftType, rightType)
.build();
assertThat(function)
.boundTo("decimal(10,5)", "decimal(10,5)")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("p", 10L, "s", 5L)
));
assertThat(function)
.boundTo("decimal(10,8)", "decimal(9,8)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("p", 10L, "s", 8L)
));
assertThat(function)
.boundTo("decimal(10,2)", "decimal(10,8)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("p", 16L, "s", 8L)
));
assertThat(function)
.boundTo("unknown", "decimal(10,5)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of("p", 10L, "s", 5L)
));
}
@Test
public void testBindLiteralForRepeatedVarchar()
throws Exception
{
Set<String> literalParameters = ImmutableSet.of("x");
TypeSignature leftType = parseTypeSignature("varchar(x)", literalParameters);
TypeSignature rightType = parseTypeSignature("varchar(x)", literalParameters);
TypeSignature returnType = parseTypeSignature("varchar(x)", literalParameters);
Signature function = functionSignature()
.returnType(returnType)
.argumentTypes(leftType, rightType)
.build();
assertThat(function)
.withCoercion()
.boundTo(ImmutableList.of("varchar(3)", "varchar(5)"), "varchar(5)")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of(
"x", 5L
)
));
assertThat(function)
.withCoercion()
.boundTo(ImmutableList.of("varchar(3)", "varchar(5)"), "varchar(6)")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of(
"x", 6L
)
));
}
@Test
public void testBindUnknown()
{
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature("varchar(x)", ImmutableSet.of("x")))
.build();
assertThat(function)
.boundTo("unknown")
.fails();
assertThat(function)
.boundTo("unknown")
.withCoercion()
.succeeds();
}
@Test
public void testBindMixedLiteralAndTypeVariables()
throws Exception
{
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.argumentTypes(parseTypeSignature("array(T)"), parseTypeSignature("array(decimal(p,s))", ImmutableSet.of("p", "s")))
.build();
assertThat(function)
.boundTo("array(decimal(2,1))", "array(decimal(3,1))")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(
"T", type("decimal(2,1)")
),
ImmutableMap.of(
"p", 3L,
"s", 1L
)
));
}
@Test
public void testBindDifferentLiteralParameters()
throws Exception
{
TypeSignature argType = parseTypeSignature("decimal(p,s)", ImmutableSet.of("p", "s"));
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(argType, argType)
.build();
assertThat(function)
.boundTo("decimal(2,1)", "decimal(3,1)")
.fails();
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testNoVariableReuseAcrossTypes()
throws Exception
{
Set<String> literalParameters = ImmutableSet.of("p1", "p2", "s");
TypeSignature leftType = parseTypeSignature("decimal(p1,s)", literalParameters);
TypeSignature rightType = parseTypeSignature("decimal(p2,s)", literalParameters);
Signature function = functionSignature()
.returnType(BOOLEAN.getTypeSignature())
.argumentTypes(leftType, rightType)
.build();
assertThat(function)
.boundTo("decimal(2,1)", "decimal(3,1)")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of()
));
}
@Test
public void testBindUnknownToDecimal()
throws Exception
{
Signature function = functionSignature()
.returnType(parseTypeSignature("boolean"))
.argumentTypes(parseTypeSignature("decimal(p,s)", ImmutableSet.of("p", "s")))
.build();
assertThat(function)
.boundTo("unknown")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of(
"p", 1L,
"s", 0L
)
));
}
@Test
public void testBindUnknownToConcreteArray()
{
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature("array(boolean)"))
.build();
assertThat(function)
.boundTo("unknown")
.withCoercion()
.succeeds();
}
@Test
public void testBindTypeVariablesBasedOnTheSecondArgument()
{
Signature function = functionSignature()
.returnType(parseTypeSignature("T"))
.argumentTypes(parseTypeSignature("array(T)"), parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.build();
assertThat(function)
.boundTo("unknown", "decimal(2,1)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("decimal(2,1)")),
ImmutableMap.of()
));
}
@Test
public void testBindParametricTypeParameterToUnknown()
{
Signature function = functionSignature()
.returnType(parseTypeSignature("T"))
.argumentTypes(parseTypeSignature("array(T)"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.build();
assertThat(function)
.boundTo("unknown")
.fails();
assertThat(function)
.withCoercion()
.boundTo("unknown")
.succeeds();
}
@Test
public void testBindUnknownToTypeParameter()
{
Signature function = functionSignature()
.returnType(parseTypeSignature("T"))
.argumentTypes(parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.build();
assertThat(function)
.boundTo("unknown")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("unknown")),
ImmutableMap.of()
));
}
@Test
public void testBindDoubleToBigint()
throws Exception
{
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature(StandardTypes.DOUBLE), parseTypeSignature(StandardTypes.DOUBLE))
.build();
assertThat(function)
.boundTo("double", "bigint")
.withCoercion()
.succeeds();
}
@Test
public void testBindVarcharTemplateStyle()
{
Signature function = functionSignature()
.returnType(parseTypeSignature("T2"))
.argumentTypes(parseTypeSignature("T1"))
.typeVariableConstraints(ImmutableList.of(
new TypeVariableConstraint("T1", true, false, "varchar"),
new TypeVariableConstraint("T2", true, false, "varchar")
))
.build();
assertThat(function)
.boundTo(ImmutableList.of("varchar(42)"), "varchar(1)")
.produces(new BoundVariables(
ImmutableMap.of(
"T1", type("varchar(42)"),
"T2", type("varchar(1)")
),
ImmutableMap.of()
));
}
@Test
public void testBindVarchar()
{
Signature function = functionSignature()
.returnType(parseTypeSignature("varchar(42)"))
.argumentTypes(parseTypeSignature("varchar(42)"))
.build();
assertThat(function)
.boundTo(ImmutableList.of("varchar(1)"), "varchar(1)")
.fails();
assertThat(function)
.boundTo(ImmutableList.of("varchar(1)"), "varchar(1)")
.withCoercion()
.fails();
assertThat(function)
.boundTo(ImmutableList.of("varchar(1)"), "varchar(42)")
.withCoercion()
.succeeds();
assertThat(function)
.boundTo(ImmutableList.of("varchar(44)"), "varchar(44)")
.withCoercion()
.fails();
}
@Test
public void testBindUnparametrizedVarchar()
throws Exception
{
Signature function = functionSignature()
.returnType(parseTypeSignature("boolean"))
.argumentTypes(parseTypeSignature("varchar(x)", ImmutableSet.of("x")))
.build();
assertThat(function)
.boundTo("varchar")
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of(
"x", (long) Integer.MAX_VALUE
)
));
}
@Test
public void testBindToUnparametrizedVarcharIsImpossible()
throws Exception
{
Signature function = functionSignature()
.returnType(parseTypeSignature("boolean"))
.argumentTypes(parseTypeSignature("varchar"))
.build();
assertThat(function)
.boundTo("varchar(3)")
.withCoercion()
.succeeds();
assertThat(function)
.boundTo("unknown")
.withCoercion()
.succeeds();
}
@Test
public void testBasic()
throws Exception
{
Signature function = functionSignature()
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.returnType(parseTypeSignature("T"))
.argumentTypes(parseTypeSignature("T"))
.build();
assertThat(function)
.boundTo("bigint")
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(function)
.boundTo("varchar")
.produces(new BoundVariables(
ImmutableMap.of("T", type("varchar")),
ImmutableMap.of()
));
assertThat(function)
.boundTo("varchar", "bigint")
.fails();
assertThat(function)
.boundTo("array(bigint)")
.produces(new BoundVariables(
ImmutableMap.of("T", type("array(bigint)")),
ImmutableMap.of()
));
}
@Test
public void testMismatchedArgumentCount()
throws Exception
{
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature(StandardTypes.BIGINT), parseTypeSignature(StandardTypes.BIGINT))
.build();
assertThat(function)
.boundTo("bigint", "bigint", "bigint")
.fails();
assertThat(function)
.boundTo("bigint")
.fails();
}
@Test
public void testNonParametric()
throws Exception
{
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature(StandardTypes.BIGINT))
.build();
assertThat(function)
.boundTo("bigint")
.succeeds();
assertThat(function)
.boundTo("varchar")
.withCoercion()
.fails();
assertThat(function)
.boundTo("varchar", "bigint")
.withCoercion()
.fails();
assertThat(function)
.boundTo("array(bigint)")
.withCoercion()
.fails();
}
@Test
public void testArray()
throws Exception
{
Signature getFunction = functionSignature()
.returnType(parseTypeSignature("T"))
.argumentTypes(parseTypeSignature("array(T)"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.build();
assertThat(getFunction)
.boundTo("array(bigint)")
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(getFunction)
.boundTo("bigint")
.withCoercion()
.fails();
assertThat(getFunction)
.boundTo("row(bigint)")
.withCoercion()
.fails();
Signature containsFunction = functionSignature()
.returnType(parseTypeSignature("T"))
.argumentTypes(parseTypeSignature("array(T)"), parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(comparableTypeParameter("T")))
.build();
assertThat(containsFunction)
.boundTo("array(bigint)", "bigint")
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(containsFunction)
.boundTo("array(bigint)", "varchar")
.withCoercion()
.fails();
assertThat(containsFunction)
.boundTo("array(HyperLogLog)", "HyperLogLog")
.withCoercion()
.fails();
Signature castFunction = functionSignature()
.returnType(parseTypeSignature("array(T2)"))
.argumentTypes(parseTypeSignature("array(T1)"), parseTypeSignature("array(T2)"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T1"), typeVariable("T2")))
.build();
assertThat(castFunction)
.boundTo("array(unknown)", "array(decimal(2,1))")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(
"T1", type("unknown"),
"T2", type("decimal(2,1)")
),
ImmutableMap.of()
));
Signature fooFunction = functionSignature()
.returnType(parseTypeSignature("T"))
.argumentTypes(parseTypeSignature("array(T)"), parseTypeSignature("array(T)"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.build();
assertThat(fooFunction)
.boundTo("array(bigint)", "array(bigint)")
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(fooFunction)
.boundTo("array(bigint)", "array(varchar)")
.withCoercion()
.fails();
}
@Test
public void testMap()
throws Exception
{
Signature getValueFunction = functionSignature()
.returnType(parseTypeSignature("V"))
.argumentTypes(parseTypeSignature("map(K,V)"), parseTypeSignature("K"))
.typeVariableConstraints(ImmutableList.of(typeVariable("K"), typeVariable("V")))
.build();
assertThat(getValueFunction)
.boundTo("map(bigint,varchar)", "bigint")
.produces(new BoundVariables(
ImmutableMap.of(
"K", type("bigint"),
"V", type("varchar")
),
ImmutableMap.of()
));
assertThat(getValueFunction)
.boundTo("map(bigint,varchar)", "varchar")
.withCoercion()
.fails();
}
@Test
public void testRow()
throws Exception
{
Signature function = functionSignature()
.returnType(BOOLEAN.getTypeSignature())
.argumentTypes(parseTypeSignature("row(bigint)"))
.build();
assertThat(function)
.boundTo("row(bigint)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of(),
ImmutableMap.of()
));
assertThat(function)
.boundTo("row(integer)")
.withCoercion()
.fails();
Signature biFunction = functionSignature()
.returnType(BOOLEAN.getTypeSignature())
.argumentTypes(parseTypeSignature("row(T)"), parseTypeSignature("row(T)"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.build();
assertThat(biFunction)
.boundTo("row(bigint)", "row(bigint)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(biFunction)
.boundTo("row(integer)", "row(bigint)")
.withCoercion()
.fails();
}
@Test
public void testVariadic()
throws Exception
{
Signature mapVariadicBoundFunction = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BIGINT))
.argumentTypes(parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(withVariadicBound("T", "map")))
.build();
assertThat(mapVariadicBoundFunction)
.boundTo("map(bigint,bigint)")
.produces(new BoundVariables(
ImmutableMap.of("T", type("map(bigint,bigint)")),
ImmutableMap.of()
));
assertThat(mapVariadicBoundFunction)
.boundTo("array(bigint)")
.fails();
assertThat(mapVariadicBoundFunction)
.boundTo("array(bigint)")
.withCoercion()
.fails();
Signature decimalVariadicBoundFunction = functionSignature()
.returnType(parseTypeSignature("bigint"))
.argumentTypes(parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(withVariadicBound("T", "decimal")))
.build();
assertThat(decimalVariadicBoundFunction)
.boundTo("decimal(2,1)")
.produces(new BoundVariables(
ImmutableMap.of("T", type("decimal(2,1)")),
ImmutableMap.of()
));
assertThat(decimalVariadicBoundFunction)
.boundTo("bigint")
.fails();
}
@Test
public void testBindUnknownToVariadic()
{
Signature rowFunction = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature("T"), parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(withVariadicBound("T", "row")))
.build();
assertThat(rowFunction)
.boundTo("unknown", "row(a bigint)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("row(a bigint)")),
ImmutableMap.of()
));
Signature arrayFunction = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature("T"), parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(withVariadicBound("T", "array")))
.build();
assertThat(arrayFunction)
.boundTo("unknown", "array(bigint)")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("array(bigint)")),
ImmutableMap.of()
));
}
@Test
public void testVarArgs()
throws Exception
{
Signature variableArityFunction = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.setVariableArity(true)
.build();
assertThat(variableArityFunction)
.boundTo("bigint")
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(variableArityFunction)
.boundTo("varchar")
.produces(new BoundVariables(
ImmutableMap.of("T", type("varchar")),
ImmutableMap.of()
));
assertThat(variableArityFunction)
.boundTo("bigint", "bigint")
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(variableArityFunction)
.boundTo("bigint", "varchar")
.withCoercion()
.fails();
}
@Test
public void testCoercion()
throws Exception
{
Signature function = functionSignature()
.returnType(parseTypeSignature(StandardTypes.BOOLEAN))
.argumentTypes(parseTypeSignature("T"), parseTypeSignature(StandardTypes.DOUBLE))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.build();
assertThat(function)
.boundTo("double", "double")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("double")),
ImmutableMap.of()
));
assertThat(function)
.boundTo("bigint", "bigint")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(function)
.boundTo("varchar", "bigint")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("varchar")),
ImmutableMap.of()
));
assertThat(function)
.boundTo("bigint", "varchar")
.withCoercion()
.fails();
}
@Test
public void testUnknownCoercion()
throws Exception
{
Signature foo = functionSignature()
.returnType(parseTypeSignature("boolean"))
.argumentTypes(parseTypeSignature("T"), parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(typeVariable("T")))
.build();
assertThat(foo)
.boundTo("unknown", "unknown")
.produces(new BoundVariables(
ImmutableMap.of("T", type("unknown")),
ImmutableMap.of()
));
assertThat(foo)
.boundTo("unknown", "bigint")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(foo)
.boundTo("varchar", "bigint")
.withCoercion()
.fails();
Signature bar = functionSignature()
.returnType(parseTypeSignature("boolean"))
.argumentTypes(parseTypeSignature("T"), parseTypeSignature("T"))
.typeVariableConstraints(ImmutableList.of(comparableTypeParameter("T")))
.build();
assertThat(bar)
.boundTo("unknown", "bigint")
.withCoercion()
.produces(new BoundVariables(
ImmutableMap.of("T", type("bigint")),
ImmutableMap.of()
));
assertThat(bar)
.boundTo("varchar", "bigint")
.withCoercion()
.fails();
assertThat(bar)
.boundTo("HyperLogLog", "HyperLogLog")
.withCoercion()
.fails();
}
@Test
public void testFunction()
throws Exception
{
Signature simple = functionSignature()
.returnType(parseTypeSignature("boolean"))
.argumentTypes(parseTypeSignature("function(integer,integer)"))
.build();
assertThat(simple)
.boundTo("integer")
.fails();
assertThat(simple)
.boundTo("function(integer,integer)")
.succeeds();
// TODO: This should eventually be supported
assertThat(simple)
.boundTo("function(integer,smallint)")
.withCoercion()
.fails();
assertThat(simple)
.boundTo("function(integer,bigint)")
.withCoercion()
.fails();
Signature applyTwice = functionSignature()
.returnType(parseTypeSignature("V"))
.argumentTypes(parseTypeSignature("T"), parseTypeSignature("function(T,U)"), parseTypeSignature("function(U,V)"))
.typeVariableConstraints(typeVariable("T"), typeVariable("U"), typeVariable("V"))
.build();
assertThat(applyTwice)
.boundTo("integer", "integer", "integer")
.fails();
assertThat(applyTwice)
.boundTo("integer", "function(integer,varchar)", "function(varchar,double)")
.produces(BoundVariables.builder()
.setTypeVariable("T", INTEGER)
.setTypeVariable("U", VARCHAR)
.setTypeVariable("V", DOUBLE)
.build());
assertThat(applyTwice)
.boundTo(
"integer",
new TypeSignatureProvider(functionArgumentTypes -> TypeSignature.parseTypeSignature("function(integer,varchar)")),
new TypeSignatureProvider(functionArgumentTypes -> TypeSignature.parseTypeSignature("function(varchar,double)")))
.produces(BoundVariables.builder()
.setTypeVariable("T", INTEGER)
.setTypeVariable("U", VARCHAR)
.setTypeVariable("V", DOUBLE)
.build());
assertThat(applyTwice)
.boundTo(
// pass function argument to non-function position of a function
new TypeSignatureProvider(functionArgumentTypes -> TypeSignature.parseTypeSignature("function(integer,varchar)")),
new TypeSignatureProvider(functionArgumentTypes -> TypeSignature.parseTypeSignature("function(integer,varchar)")),
new TypeSignatureProvider(functionArgumentTypes -> TypeSignature.parseTypeSignature("function(varchar,double)")))
.fails();
assertThat(applyTwice)
.boundTo(
new TypeSignatureProvider(functionArgumentTypes -> TypeSignature.parseTypeSignature("function(integer,varchar)")),
// pass non-function argument to function position of a function
"integer",
new TypeSignatureProvider(functionArgumentTypes -> TypeSignature.parseTypeSignature("function(varchar,double)")))
.fails();
Signature flatMap = functionSignature()
.returnType(parseTypeSignature("array(T)"))
.argumentTypes(parseTypeSignature("array(T)"), parseTypeSignature("function(T, array(T))"))
.typeVariableConstraints(typeVariable("T"))
.build();
assertThat(flatMap)
.boundTo("array(integer)", "function(integer, array(integer))")
.produces(BoundVariables.builder()
.setTypeVariable("T", INTEGER)
.build());
Signature varargApply = functionSignature()
.returnType(parseTypeSignature("T"))
.argumentTypes(parseTypeSignature("T"), parseTypeSignature("function(T, T)"))
.typeVariableConstraints(typeVariable("T"))
.setVariableArity(true)
.build();
assertThat(varargApply)
.boundTo("integer", "function(integer, integer)", "function(integer, integer)", "function(integer, integer)")
.produces(BoundVariables.builder()
.setTypeVariable("T", INTEGER)
.build());
assertThat(varargApply)
.boundTo("integer", "function(integer, integer)", "function(integer, double)", "function(double, double)")
.fails();
}
@Test
public void testBindParameters()
throws Exception
{
BoundVariables boundVariables = BoundVariables.builder()
.setTypeVariable("T1", DOUBLE)
.setTypeVariable("T2", BigintType.BIGINT)
.setTypeVariable("T3", DecimalType.createDecimalType(5, 3))
.setLongVariable("p", 1L)
.setLongVariable("s", 2L)
.build();
assertThat("bigint", boundVariables, "bigint");
assertThat("T1", boundVariables, "double");
assertThat("T2", boundVariables, "bigint");
assertThat("array(T1)", boundVariables, "array(double)");
assertThat("array(T3)", boundVariables, "array(decimal(5,3))");
assertThat("array<T1>", boundVariables, "array(double)");
assertThat("map(T1,T2)", boundVariables, "map(double,bigint)");
assertThat("map<T1,T2>", boundVariables, "map(double,bigint)");
assertThat("bla(T1,42,T2)", boundVariables, "bla(double,42,bigint)");
assertThat("varchar(p)", boundVariables, "varchar(1)");
assertThat("char(p)", boundVariables, "char(1)");
assertThat("decimal(p,s)", boundVariables, "decimal(1,2)");
assertThat("array(decimal(p,s))", boundVariables, "array(decimal(1,2))");
assertBindVariablesFails("T1(bigint)", boundVariables, "Unbounded parameters can not have parameters");
}
private static void assertBindVariablesFails(String typeSignature, BoundVariables boundVariables, String reason)
{
try {
SignatureBinder.applyBoundVariables(parseTypeSignature(typeSignature, ImmutableSet.of("p", "s")), boundVariables);
fail(reason);
}
catch (RuntimeException e) {
// Expected
}
}
private static void assertThat(String typeSignature, BoundVariables boundVariables, String expectedTypeSignature)
{
assertEquals(
SignatureBinder.applyBoundVariables(parseTypeSignature(typeSignature, ImmutableSet.of("p", "s")), boundVariables).toString(),
expectedTypeSignature
);
}
private static SignatureBuilder functionSignature()
{
return new SignatureBuilder()
.name("function")
.kind(SCALAR);
}
private Type type(String signature)
{
TypeSignature typeSignature = TypeSignature.parseTypeSignature(signature);
return requireNonNull(typeRegistry.getType(typeSignature));
}
private List<Type> types(String... signatures)
{
return ImmutableList.copyOf(signatures)
.stream()
.map(this::type)
.collect(toList());
}
private BindSignatureAssertion assertThat(Signature function)
{
return new BindSignatureAssertion(function);
}
private class BindSignatureAssertion
{
private final Signature function;
private List<TypeSignatureProvider> argumentTypes = null;
private Type returnType = null;
private boolean allowCoercion = false;
private BindSignatureAssertion(Signature function)
{
this.function = function;
}
public BindSignatureAssertion withCoercion()
{
allowCoercion = true;
return this;
}
public BindSignatureAssertion boundTo(String... arguments)
{
this.argumentTypes = fromTypes(types(arguments));
return this;
}
public BindSignatureAssertion boundTo(Object... arguments)
{
ImmutableList.Builder<TypeSignatureProvider> builder = ImmutableList.builder();
for (Object argument : arguments) {
if (argument instanceof String) {
builder.add(new TypeSignatureProvider(TypeSignature.parseTypeSignature((String) argument)));
continue;
}
if (argument instanceof TypeSignatureProvider) {
builder.add((TypeSignatureProvider) argument);
continue;
}
throw new IllegalArgumentException(format("argument is of type %s. It should be String or TypeSignatureProvider", argument.getClass()));
}
this.argumentTypes = builder.build();
return this;
}
public BindSignatureAssertion boundTo(List<String> arguments, String returnType)
{
this.argumentTypes = fromTypes(types(arguments.toArray(new String[arguments.size()])));
this.returnType = type(returnType);
return this;
}
public BindSignatureAssertion succeeds()
{
assertTrue(bindVariables().isPresent());
return this;
}
public BindSignatureAssertion fails()
{
assertFalse(bindVariables().isPresent());
return this;
}
public BindSignatureAssertion produces(BoundVariables expected)
{
Optional<BoundVariables> actual = bindVariables();
assertTrue(actual.isPresent());
assertEquals(actual.get(), expected);
return this;
}
private Optional<BoundVariables> bindVariables()
{
assertNotNull(argumentTypes);
SignatureBinder signatureBinder = new SignatureBinder(typeRegistry, function, allowCoercion);
if (returnType == null) {
return signatureBinder.bindVariables(argumentTypes);
}
else {
return signatureBinder.bindVariables(argumentTypes, returnType);
}
}
}
}