|
17 | 17 |
|
18 | 18 | package org.apache.ignite.internal.processors.query.calcite.integration;
|
19 | 19 |
|
| 20 | +import java.sql.Timestamp; |
20 | 21 | import java.util.Arrays;
|
21 | 22 | import java.util.Collection;
|
22 | 23 | import java.util.List;
|
| 24 | +import org.apache.calcite.schema.SchemaPlus; |
23 | 25 | import org.apache.calcite.sql.validate.SqlValidatorException;
|
24 | 26 | import org.apache.ignite.IgniteCache;
|
25 | 27 | import org.apache.ignite.IgniteCheckedException;
|
|
32 | 34 | import org.apache.ignite.configuration.CacheConfiguration;
|
33 | 35 | import org.apache.ignite.configuration.IgniteConfiguration;
|
34 | 36 | import org.apache.ignite.internal.processors.query.IgniteSQLException;
|
| 37 | +import org.apache.ignite.internal.processors.query.QueryUtils; |
35 | 38 | import org.apache.ignite.internal.util.typedef.F;
|
36 | 39 | import org.apache.ignite.testframework.GridTestUtils;
|
37 | 40 | import org.apache.ignite.testframework.ListeningTestLogger;
|
@@ -61,6 +64,67 @@ public class UserDefinedFunctionsIntegrationTest extends AbstractBasicIntegratio
|
61 | 64 | return cfg;
|
62 | 65 | }
|
63 | 66 |
|
| 67 | + /** */ |
| 68 | + @Test |
| 69 | + public void testSystemFunctionOverriding() throws Exception { |
| 70 | + // To a custom schema. |
| 71 | + client.getOrCreateCache(new CacheConfiguration<Integer, Employer>("TEST_CACHE_OWN") |
| 72 | + .setSqlSchema("OWN_SCHEMA") |
| 73 | + .setSqlFunctionClasses(OverrideSystemFunctionLibrary.class) |
| 74 | + .setQueryEntities(F.asList(new QueryEntity(Integer.class, Employer.class).setTableName("emp_own"))) |
| 75 | + ); |
| 76 | + |
| 77 | + // Make sure that the new functions didn't affect schema 'PUBLIC'. |
| 78 | + assertQuery("SELECT UPPER(?)").withParams("abc").returns("ABC").check(); |
| 79 | + assertQuery("select UNIX_SECONDS(TIMESTAMP '2021-01-01 00:00:00')").returns(1609459200L).check(); |
| 80 | + assertQuery("select * from table(SYSTEM_RANGE(1, 2))").returns(1L).returns(2L).check(); |
| 81 | + assertQuery("select TYPEOF(?)").withParams(1L).returns("BIGINT").check(); |
| 82 | + assertQuery("select ? + ?").withParams(1, 2).returns(3).check(); |
| 83 | + assertThrows("select PLUS(?, ?)", SqlValidatorException.class, "No match found for function signature", 1, 2); |
| 84 | + |
| 85 | + // Ensure that new functions are successfully created in a custom schema. |
| 86 | + assertQuery("SELECT \"OWN_SCHEMA\".UPPER(?)").withParams("abc").returns(3).check(); |
| 87 | + assertQuery("select \"OWN_SCHEMA\".UNIX_SECONDS(TIMESTAMP '2021-01-01 00:00:00')").returns(1).check(); |
| 88 | + assertQuery("select * from table(\"OWN_SCHEMA\".SYSTEM_RANGE(1, 2))").returns(100L).check(); |
| 89 | + assertQuery("select \"OWN_SCHEMA\".TYPEOF('ABC')").returns(1).check(); |
| 90 | + assertQuery("select \"OWN_SCHEMA\".PLUS(?, ?)").withParams(1, 2).returns(100).check(); |
| 91 | + |
| 92 | + LogListener logChecker0 = LogListener.matches("Unable to add user-defined SQL function 'upper'") |
| 93 | + .andMatches("Unable to add user-defined SQL function 'unix_seconds'") |
| 94 | + .andMatches("Unable to add user-defined SQL function 'system_range'") |
| 95 | + .andMatches("Unable to add user-defined SQL function 'typeof'") |
| 96 | + .andMatches("Unable to add user-defined SQL function 'plus'").times(0) |
| 97 | + .build(); |
| 98 | + |
| 99 | + listeningLog.registerListener(logChecker0); |
| 100 | + |
| 101 | + // Try to add the functions into the default schema. |
| 102 | + client.getOrCreateCache(new CacheConfiguration<Integer, Employer>("TEST_CACHE_PUB") |
| 103 | + .setSqlFunctionClasses(OverrideSystemFunctionLibrary.class) |
| 104 | + .setSqlSchema(QueryUtils.DFLT_SCHEMA) |
| 105 | + .setQueryEntities(F.asList(new QueryEntity(Integer.class, Employer.class).setTableName("emp_pub")))); |
| 106 | + |
| 107 | + assertTrue(logChecker0.check(getTestTimeout())); |
| 108 | + |
| 109 | + // Make sure that the standard functions work once again. |
| 110 | + assertQuery("SELECT UPPER(?)").withParams("abc").returns("ABC").check(); |
| 111 | + assertQuery("select UNIX_SECONDS(TIMESTAMP '2021-01-01 00:00:00')").returns(1609459200L).check(); |
| 112 | + assertQuery("select * from table(SYSTEM_RANGE(1, 2))").returns(1L).returns(2L).check(); |
| 113 | + assertQuery("select TYPEOF(?)").withParams(1L).returns("BIGINT").check(); |
| 114 | + |
| 115 | + // Make sure that operator '+' works and new function 'PLUS' also registered in the default schema. |
| 116 | + assertQuery("select ? + ?").withParams(1, 2).returns(3).check(); |
| 117 | + assertQuery("select PLUS(?, ?)").withParams(1, 2).returns(100); |
| 118 | + |
| 119 | + SchemaPlus dfltSchema = queryProcessor(client).schemaHolder().schema(QueryUtils.DFLT_SCHEMA); |
| 120 | + |
| 121 | + assertEquals(0, dfltSchema.getFunctions("UPPER").size()); |
| 122 | + assertEquals(0, dfltSchema.getFunctions("UNIX_SECONDS").size()); |
| 123 | + assertEquals(0, dfltSchema.getFunctions("SYSTEM_RANGE").size()); |
| 124 | + assertEquals(0, dfltSchema.getFunctions("TYPEOF").size()); |
| 125 | + assertEquals(1, dfltSchema.getFunctions("PLUS").size()); |
| 126 | + } |
| 127 | + |
64 | 128 | /** */
|
65 | 129 | @Test
|
66 | 130 | public void testFunctions() throws Exception {
|
@@ -260,7 +324,7 @@ public void testIncorrectTableFunctions() throws Exception {
|
260 | 324 | assertThrows("SELECT * FROM wrongRowType(1)", IgniteSQLException.class,
|
261 | 325 | "row type is neither Collection or Object[]");
|
262 | 326 |
|
263 |
| - logChecker0.check(getTestTimeout()); |
| 327 | + assertTrue(logChecker0.check(getTestTimeout())); |
264 | 328 |
|
265 | 329 | String errTxt = "No match found for function signature";
|
266 | 330 |
|
@@ -556,6 +620,39 @@ public static String echo(String s) {
|
556 | 620 | }
|
557 | 621 | }
|
558 | 622 |
|
| 623 | + /** */ |
| 624 | + public static class OverrideSystemFunctionLibrary { |
| 625 | + /** Overwrites standard 'UPPER(VARCHAR)'. */ |
| 626 | + @QuerySqlFunction |
| 627 | + public static int upper(String s) { |
| 628 | + return F.isEmpty(s) ? 0 : s.length(); |
| 629 | + } |
| 630 | + |
| 631 | + /** Overwrites standard 'UNIX_SECONDS(Timestamp)'. */ |
| 632 | + @QuerySqlFunction |
| 633 | + public static int unix_seconds(Timestamp ts) { |
| 634 | + return 1; |
| 635 | + } |
| 636 | + |
| 637 | + /** Overwrites Ignite's 'SYSTEM_RANGE(...)'. */ |
| 638 | + @QuerySqlTableFunction(columnTypes = {long.class}, columnNames = {"RESULT"}) |
| 639 | + public static Collection<Object> system_range(long x, long y) { |
| 640 | + return F.asList(F.asList(100L)); |
| 641 | + } |
| 642 | + |
| 643 | + /** Overwrites Ignite's 'TYPEOF(Object)'. */ |
| 644 | + @QuerySqlFunction |
| 645 | + public static int typeof(Object o) { |
| 646 | + return 1; |
| 647 | + } |
| 648 | + |
| 649 | + /** Same name as of operator '+' which is not a function. */ |
| 650 | + @QuerySqlFunction |
| 651 | + public static int plus(int x, int y) { |
| 652 | + return 100; |
| 653 | + } |
| 654 | + } |
| 655 | + |
559 | 656 | /** */
|
560 | 657 | public static class InnerSqlFunctionsLibrary {
|
561 | 658 | /** */
|
|
0 commit comments