Skip to content

Commit 8d9aa5a

Browse files
pdabre12Naveen Mahadevuni
authored andcommitted
Add tests for getSqlInvokedFunctions() SPI
Co-Authored-by: Naveen Mahadevuni <[email protected]>
1 parent 0e6fbad commit 8d9aa5a

File tree

5 files changed

+319
-0
lines changed

5 files changed

+319
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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.functions;
15+
16+
import com.facebook.presto.spi.function.Description;
17+
import com.facebook.presto.spi.function.SqlInvokedScalarFunction;
18+
import com.facebook.presto.spi.function.SqlParameter;
19+
import com.facebook.presto.spi.function.SqlType;
20+
import com.facebook.presto.spi.function.TypeParameter;
21+
22+
public final class TestDuplicateSqlInvokedFunctions
23+
{
24+
private TestDuplicateSqlInvokedFunctions() {}
25+
26+
@SqlInvokedScalarFunction(value = "array_intersect", deterministic = true, calledOnNullInput = false)
27+
@Description("Intersects elements of all arrays in the given array")
28+
@TypeParameter("T")
29+
@SqlParameter(name = "input", type = "array<array<T>>")
30+
@SqlType("array<T>")
31+
public static String arrayIntersectArray()
32+
{
33+
return "RETURN reduce(input, IF((cardinality(input) = 0), ARRAY[], input[1]), (s, x) -> array_intersect(s, x), (s) -> s)";
34+
}
35+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.functions;
15+
16+
import com.facebook.presto.common.type.StandardTypes;
17+
import com.facebook.presto.spi.function.Description;
18+
import com.facebook.presto.spi.function.ScalarFunction;
19+
import com.facebook.presto.spi.function.SqlType;
20+
import io.airlift.slice.Slice;
21+
22+
public final class TestFunctions
23+
{
24+
private TestFunctions()
25+
{}
26+
27+
@Description("Returns modulo of value by numberOfBuckets")
28+
@ScalarFunction
29+
@SqlType(StandardTypes.BIGINT)
30+
public static long modulo(
31+
@SqlType(StandardTypes.BIGINT) long value,
32+
@SqlType(StandardTypes.BIGINT) long numberOfBuckets)
33+
{
34+
return value % numberOfBuckets;
35+
}
36+
37+
@Description(("Return the input string"))
38+
@ScalarFunction
39+
@SqlType(StandardTypes.VARCHAR)
40+
public static Slice identity(@SqlType(StandardTypes.VARCHAR) Slice slice)
41+
{
42+
return slice;
43+
}
44+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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.functions;
15+
16+
import com.facebook.presto.common.type.TimeZoneKey;
17+
import com.facebook.presto.server.testing.TestingPrestoServer;
18+
import com.facebook.presto.spi.Plugin;
19+
import com.facebook.presto.tests.TestingPrestoClient;
20+
import com.google.common.collect.ImmutableSet;
21+
import org.testng.annotations.BeforeClass;
22+
import org.testng.annotations.Test;
23+
24+
import java.util.Set;
25+
import java.util.regex.Pattern;
26+
27+
import static com.facebook.presto.metadata.BuiltInTypeAndFunctionNamespaceManager.JAVA_BUILTIN_NAMESPACE;
28+
import static com.facebook.presto.testing.TestingSession.testSessionBuilder;
29+
import static java.lang.String.format;
30+
import static org.testng.Assert.fail;
31+
32+
public class TestPluginLoadedDuplicateSqlInvokedFunctions
33+
{
34+
protected TestingPrestoServer server;
35+
protected TestingPrestoClient client;
36+
37+
@BeforeClass
38+
public void setup()
39+
throws Exception
40+
{
41+
server = new TestingPrestoServer();
42+
server.installPlugin(new TestDuplicateFunctionsPlugin());
43+
client = new TestingPrestoClient(server, testSessionBuilder()
44+
.setTimeZoneKey(TimeZoneKey.getTimeZoneKey("America/Bahia_Banderas"))
45+
.build());
46+
}
47+
48+
public void assertInvalidFunction(String expr, String exceptionPattern)
49+
{
50+
try {
51+
client.execute("SELECT " + expr);
52+
fail("Function expected to fail but not");
53+
}
54+
catch (Exception e) {
55+
if (!(e.getMessage().matches(exceptionPattern))) {
56+
fail(format("Expected exception message '%s' to match '%s' but not",
57+
e.getMessage(), exceptionPattern));
58+
}
59+
}
60+
}
61+
62+
private static class TestDuplicateFunctionsPlugin
63+
implements Plugin
64+
{
65+
@Override
66+
public Set<Class<?>> getSqlInvokedFunctions()
67+
{
68+
return ImmutableSet.<Class<?>>builder()
69+
.add(TestDuplicateSqlInvokedFunctions.class)
70+
.build();
71+
}
72+
}
73+
74+
@Test
75+
public void testDuplicateFunctionsLoaded()
76+
{
77+
assertInvalidFunction(JAVA_BUILTIN_NAMESPACE + ".modulo(10,3)",
78+
Pattern.quote(format("java.lang.IllegalArgumentException: Function already registered: %s.array_intersect<T>(array(array(T))):array(T)", JAVA_BUILTIN_NAMESPACE)));
79+
}
80+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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.functions;
15+
16+
import com.facebook.presto.common.type.TimeZoneKey;
17+
import com.facebook.presto.common.type.Type;
18+
import com.facebook.presto.server.testing.TestingPrestoServer;
19+
import com.facebook.presto.spi.Plugin;
20+
import com.facebook.presto.testing.MaterializedResult;
21+
import com.facebook.presto.tests.TestingPrestoClient;
22+
import com.google.common.collect.ImmutableSet;
23+
import org.intellij.lang.annotations.Language;
24+
import org.testng.annotations.BeforeClass;
25+
import org.testng.annotations.Test;
26+
27+
import java.util.Set;
28+
29+
import static com.facebook.presto.common.type.BigintType.BIGINT;
30+
import static com.facebook.presto.common.type.IntegerType.INTEGER;
31+
import static com.facebook.presto.common.type.VarcharType.VARCHAR;
32+
import static com.facebook.presto.metadata.BuiltInTypeAndFunctionNamespaceManager.JAVA_BUILTIN_NAMESPACE;
33+
import static com.facebook.presto.testing.TestingSession.testSessionBuilder;
34+
import static java.lang.String.format;
35+
import static org.testng.Assert.assertEquals;
36+
import static org.testng.Assert.assertThrows;
37+
import static org.testng.Assert.fail;
38+
39+
public class TestPluginLoadedSqlInvokedFunctions
40+
{
41+
protected TestingPrestoServer server;
42+
protected TestingPrestoClient client;
43+
44+
private static final String CATALOG_NAME = JAVA_BUILTIN_NAMESPACE.getCatalogName();
45+
46+
@BeforeClass
47+
public void setup()
48+
throws Exception
49+
{
50+
server = new TestingPrestoServer();
51+
server.installPlugin(new TestFunctionsPlugin());
52+
client = new TestingPrestoClient(server, testSessionBuilder()
53+
.setTimeZoneKey(TimeZoneKey.getTimeZoneKey("America/Bahia_Banderas"))
54+
.build());
55+
}
56+
57+
public void assertInvalidFunction(String expr, String exceptionPattern)
58+
{
59+
try {
60+
client.execute("SELECT " + expr);
61+
fail("Function expected to fail but not");
62+
}
63+
catch (Exception e) {
64+
if (!(e.getMessage().matches(exceptionPattern))) {
65+
fail(format("Expected exception message '%s' to match '%s' but not",
66+
e.getMessage(), exceptionPattern));
67+
}
68+
}
69+
}
70+
71+
private static class TestFunctionsPlugin
72+
implements Plugin
73+
{
74+
@Override
75+
public Set<Class<?>> getSqlInvokedFunctions()
76+
{
77+
return ImmutableSet.<Class<?>>builder()
78+
.add(TestSqlInvokedFunctionsPlugin.class)
79+
.build();
80+
}
81+
82+
@Override
83+
public Set<Class<?>> getFunctions()
84+
{
85+
return ImmutableSet.<Class<?>>builder()
86+
.add(TestFunctions.class)
87+
.build();
88+
}
89+
}
90+
91+
public void check(@Language("SQL") String query, Type expectedType, Object expectedValue)
92+
{
93+
MaterializedResult result = client.execute(query).getResult();
94+
assertEquals(result.getRowCount(), 1);
95+
assertEquals(result.getTypes().get(0), expectedType);
96+
Object actual = result.getMaterializedRows().get(0).getField(0);
97+
assertEquals(actual, expectedValue);
98+
}
99+
100+
@Test
101+
public void testNewFunctionNamespaceFunction()
102+
{
103+
check("SELECT " + JAVA_BUILTIN_NAMESPACE + ".modulo(10,3)", BIGINT, 1L);
104+
check("SELECT " + JAVA_BUILTIN_NAMESPACE + ".identity('test-functions')", VARCHAR, "test-functions");
105+
check("SELECT " + JAVA_BUILTIN_NAMESPACE + ".custom_square(2, 3)", INTEGER, 4);
106+
check("SELECT " + JAVA_BUILTIN_NAMESPACE + ".custom_square(null, 3)", INTEGER, 9);
107+
}
108+
109+
@Test
110+
public void testInvalidFunctionAndNamespace()
111+
{
112+
assertInvalidFunction(CATALOG_NAME + ".namespace.modulo(10,3)", format("line 1:8: Function %s.namespace.modulo not registered", CATALOG_NAME));
113+
assertInvalidFunction(CATALOG_NAME + ".system.some_func(10)", format("line 1:8: Function %s.system.some_func not registered", CATALOG_NAME));
114+
}
115+
116+
@Test(dependsOnMethods =
117+
{"testNewFunctionNamespaceFunction",
118+
"testInvalidFunctionAndNamespace"})
119+
public void testDuplicateFunctionsLoaded()
120+
{
121+
// Because we trigger the conflict check as soon as the plugins are loaded,
122+
// this will throw an Exception: Function already registered: presto.default.modulo(bigint,bigint):bigint while installing the plugin itself
123+
assertThrows(IllegalArgumentException.class, () -> server.installPlugin(new TestFunctionsPlugin()));
124+
}
125+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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.functions;
15+
16+
import com.facebook.presto.spi.function.Description;
17+
import com.facebook.presto.spi.function.SqlInvokedScalarFunction;
18+
import com.facebook.presto.spi.function.SqlParameter;
19+
import com.facebook.presto.spi.function.SqlParameters;
20+
import com.facebook.presto.spi.function.SqlType;
21+
22+
public final class TestSqlInvokedFunctionsPlugin
23+
{
24+
private TestSqlInvokedFunctionsPlugin()
25+
{}
26+
27+
@SqlInvokedScalarFunction(value = "custom_square", deterministic = true, calledOnNullInput = false)
28+
@Description("Custom SQL to test NULLIF in Functions")
29+
@SqlParameters({@SqlParameter(name = "x", type = "integer"), @SqlParameter(name = "y", type = "integer")})
30+
@SqlType("integer")
31+
public static String customSquare()
32+
{
33+
return "RETURN IF(NULLIF(x, y) IS NOT NULL, x * x, y * y)";
34+
}
35+
}

0 commit comments

Comments
 (0)