From 33137e8819de49ea0e38fb1abf6cc8e3d4014f75 Mon Sep 17 00:00:00 2001 From: Malte Bellmann Date: Mon, 22 Feb 2021 14:36:47 +0100 Subject: [PATCH] [CALCITE-2317] Support JDBC DatabaseMetaData.getFunctions DatabaseMetaData.getFunctions() on a connection to Calcite now returns UDFs in the respective schema and system functions in the "metadata" schema. --- .../apache/calcite/jdbc/CalciteMetaImpl.java | 69 +++++++++++++++++++ .../apache/calcite/test/JdbcAdapterTest.java | 42 +++++++++++ 2 files changed, 111 insertions(+) diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java index 2122ff5b25e2..11dbb3f2d7c3 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java @@ -46,8 +46,13 @@ import org.apache.calcite.schema.SchemaPlus; import org.apache.calcite.schema.Table; import org.apache.calcite.schema.impl.AbstractTableQueryable; +import org.apache.calcite.schema.impl.MaterializedViewTable; import org.apache.calcite.server.CalciteServerStatement; import org.apache.calcite.sql.SqlJdbcFunctionCall; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlOperatorTable; +import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParser; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.tools.FrameworkConfig; @@ -502,6 +507,70 @@ public Enumerable columns(final MetaTable table_) { "TABLE_TYPE"); } + @Override public MetaResultSet getFunctions(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat functionNamePattern) { + final Predicate1 schemaMatcher = namedMatcher(schemaPattern); + return createResultSet(schemas(catalog) + .where(schemaMatcher) + .selectMany(schema -> functions(schema, catalog, matcher(functionNamePattern))) + .orderBy(x -> + (Comparable) FlatLists.of( + x.functionCat, x.functionSchem, x.functionName, x.specificName + )), + MetaFunction.class, + "FUNCTION_CAT", + "FUNCTION_SCHEM", + "FUNCTION_NAME", + "REMARKS", + "FUNCTION_TYPE", + "SPECIFIC_NAME"); + } + + Enumerable functions(final MetaSchema schema_, final String catalog) { + final CalciteMetaSchema schema = (CalciteMetaSchema) schema_; + Enumerable opTableFunctions = Linq4j.emptyEnumerable(); + if (schema.calciteSchema.schema.equals(MetadataSchema.INSTANCE)) { + SqlOperatorTable opTable = getConnection().config() + .fun(SqlOperatorTable.class, SqlStdOperatorTable.instance()); + List q = opTable.getOperatorList(); + opTableFunctions = Linq4j.asEnumerable(q) + .where(op -> SqlKind.FUNCTION.contains(op.getKind())) + .select(op -> + new MetaFunction( + catalog, + schema.getName(), + op.getName(), + (short) DatabaseMetaData.functionResultUnknown, + op.getName() + ) + ); + } + return Linq4j.asEnumerable(schema.calciteSchema.getFunctionNames()) + .selectMany(name -> + Linq4j.asEnumerable(schema.calciteSchema.getFunctions(name, true)) + //exclude materialized views from the result set + .where(fn -> !(fn instanceof MaterializedViewTable.MaterializedViewTableMacro)) + .select(fnx -> + new MetaFunction( + catalog, + schema.getName(), + name, + (short) DatabaseMetaData.functionResultUnknown, + name + ) + ) + ) + .concat(opTableFunctions); + } + + Enumerable functions(final MetaSchema schema, final String catalog, + final Predicate1 functionNameMatcher) { + return functions(schema, catalog) + .where(v1 -> functionNameMatcher.apply(v1.functionName)); + } + @Override public Iterable createIterable(StatementHandle handle, QueryState state, Signature signature, @Nullable List parameterValues, @Nullable Frame firstFrame) { // Drop QueryState diff --git a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java index cc8184ae4680..ebc2ee070470 100644 --- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java +++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java @@ -20,6 +20,7 @@ import org.apache.calcite.config.Lex; import org.apache.calcite.test.CalciteAssert.AssertThat; import org.apache.calcite.test.CalciteAssert.DatabaseInstance; +import org.apache.calcite.util.Smalls; import org.apache.calcite.util.TestUtil; import org.hsqldb.jdbcDriver; @@ -760,6 +761,47 @@ class JdbcAdapterTest { }); } + @Test void testMetadataFunctions() { + final String model = "" + + "{\n" + + " version: '1.0',\n" + + " schemas: [\n" + + " {\n" + + " name: 'adhoc',\n" + + " functions: [\n" + + " {\n" + + " name: 'MY_STR',\n" + + " className: '" + Smalls.MyToStringFunction.class.getName() + "'\n" + + " },\n" + + " {\n" + + " name: 'FIBONACCI_TABLE',\n" + + " className: '" + Smalls.class.getName() + "',\n" + + " methodName: 'fibonacciTable'\n" + + " }\n" + + " ],\n" + + " materializations: [\n" + + " {\n" + + " table: 'TEST_VIEW',\n" + + " sql: 'SELECT 1'\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"; + CalciteAssert.model(model) + .withDefaultSchema("adhoc") + .metaData(connection -> { + try { + return connection.getMetaData().getFunctions(null, "adhoc", "%"); + } catch (SQLException e) { + throw TestUtil.rethrow(e); + } + }) + .returns("" + + "FUNCTION_CAT=null; FUNCTION_SCHEM=adhoc; FUNCTION_NAME=FIBONACCI_TABLE; REMARKS=null; FUNCTION_TYPE=0; SPECIFIC_NAME=FIBONACCI_TABLE\n" + + "FUNCTION_CAT=null; FUNCTION_SCHEM=adhoc; FUNCTION_NAME=MY_STR; REMARKS=null; FUNCTION_TYPE=0; SPECIFIC_NAME=MY_STR\n"); + } + /** Test case for * [CALCITE-666] * Anti-semi-joins against JDBC adapter give wrong results. */