diff --git a/src/main/java/org/duckdb/DuckDBDatabaseMetaData.java b/src/main/java/org/duckdb/DuckDBDatabaseMetaData.java index d764b152..7074b0ca 100644 --- a/src/main/java/org/duckdb/DuckDBDatabaseMetaData.java +++ b/src/main/java/org/duckdb/DuckDBDatabaseMetaData.java @@ -18,6 +18,7 @@ import javax.sql.rowset.RowSetProvider; public class DuckDBDatabaseMetaData implements DatabaseMetaData { + DuckDBConnection conn; public DuckDBDatabaseMetaData(DuckDBConnection conn) { @@ -170,27 +171,74 @@ public String getIdentifierQuoteString() throws SQLException { @Override public String getSQLKeywords() throws SQLException { - throw new SQLFeatureNotSupportedException("getSQLKeywords"); + Statement statement = conn.createStatement(); + statement.closeOnCompletion(); + ResultSet rs = statement.executeQuery("SELECT keyword_name FROM duckdb_keywords()"); + StringBuilder sb = new StringBuilder(); + while (rs.next()) { + sb.append(rs.getString(1)); + sb.append(','); + } + return sb.toString(); } @Override public String getNumericFunctions() throws SQLException { - throw new SQLFeatureNotSupportedException("getNumericFunctions"); + Statement statement = conn.createStatement(); + statement.closeOnCompletion(); + ResultSet rs = statement.executeQuery("SELECT DISTINCT function_name FROM duckdb_functions() " + + "WHERE parameter_types[1] ='DECIMAL'" + + "OR parameter_types[1] ='DOUBLE'" + + "OR parameter_types[1] ='SMALLINT'" + + "OR parameter_types[1] = 'BIGINT'"); + StringBuilder sb = new StringBuilder(); + while (rs.next()) { + sb.append(rs.getString(1)); + sb.append(','); + } + return sb.toString(); } @Override public String getStringFunctions() throws SQLException { - throw new SQLFeatureNotSupportedException("getStringFunctions"); + Statement statement = conn.createStatement(); + statement.closeOnCompletion(); + ResultSet rs = statement.executeQuery( + "SELECT DISTINCT function_name FROM duckdb_functions() WHERE parameter_types[1] = 'VARCHAR'"); + StringBuilder sb = new StringBuilder(); + while (rs.next()) { + sb.append(rs.getString(1)); + sb.append(','); + } + return sb.toString(); } @Override public String getSystemFunctions() throws SQLException { - throw new SQLFeatureNotSupportedException("getSystemFunctions"); + Statement statement = conn.createStatement(); + statement.closeOnCompletion(); + ResultSet rs = statement.executeQuery( + "SELECT DISTINCT function_name FROM duckdb_functions() WHERE length(parameter_types) = 0"); + StringBuilder sb = new StringBuilder(); + while (rs.next()) { + sb.append(rs.getString(1)); + sb.append(','); + } + return sb.toString(); } @Override public String getTimeDateFunctions() throws SQLException { - throw new SQLFeatureNotSupportedException("getTimeDateFunctions"); + Statement statement = conn.createStatement(); + statement.closeOnCompletion(); + ResultSet rs = statement.executeQuery( + "SELECT DISTINCT function_name FROM duckdb_functions() WHERE parameter_types[1] LIKE 'TIME%'"); + StringBuilder sb = new StringBuilder(); + while (rs.next()) { + sb.append(rs.getString(1)); + sb.append(','); + } + return sb.toString(); } @Override @@ -730,14 +778,16 @@ public ResultSet getTables(String catalog, String schemaPattern, String tableNam str.append(", NULL::VARCHAR AS 'REF_GENERATION'").append(lineSeparator()); str.append("FROM information_schema.tables").append(lineSeparator()); - // tableNamePattern - a table name pattern; must match the table name as it is stored in the database + // tableNamePattern - a table name pattern; must match the table name as it + // is stored in the database if (tableNamePattern == null) { // non-standard behavior. tableNamePattern = "%"; } str.append("WHERE table_name LIKE ?").append(lineSeparator()); - // catalog - a catalog name; must match the catalog name as it is stored in the database; + // catalog - a catalog name; must match the catalog name as it is stored in + // the database; // "" retrieves those without a catalog; // null means that the catalog name should not be used to narrow the search boolean hasCatalogParam = false; @@ -751,7 +801,8 @@ public ResultSet getTables(String catalog, String schemaPattern, String tableNam } } - // schemaPattern - a schema name pattern; must match the schema name as it is stored in the database; + // schemaPattern - a schema name pattern; must match the schema name as it + // is stored in the database; // "" retrieves those without a schema; // null means that the schema name should not be used to narrow the search boolean hasSchemaParam = false; @@ -979,7 +1030,43 @@ public ResultSet getTypeInfo() throws SQLException { @Override public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException { - throw new SQLFeatureNotSupportedException("getIndexInfo("); + StringBuilder sb = new StringBuilder(); + sb.append("SELECT database_name AS TABLE_CAT " + + ", schema_name AS TABLE_SCHEM " + + ", table_name AS TABLE_NAME " + + ", index_name AS INDEX_NAME " + + ", CASE WHEN is_unique THEN 0 ELSE 1 END AS NON_UNIQUE " + + ", NULL AS TYPE " + + ", NULL AS ORDINAL_POSITION " + + ", NULL AS COLUMN_NAME " + + ", NULL AS ASC_OR_DESC " + + ", NULL AS CARDINALITY " + + ", NULL AS PAGES " + + ", NULL AS FILTER_CONDITION " + + "FROM duckdb_indexes() WHERE TRUE "); + if (catalog != null) { + sb.append(" AND database_name = ?"); + } + if (schema != null) { + sb.append(" AND schema_name = ?"); + } + if (table != null) { + sb.append(" AND table_name = ?"); + } + sb.append(" ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, NON_UNIQUE, INDEX_NAME, ORDINAL_POSITION"); + PreparedStatement ps = conn.prepareStatement(sb.toString()); + int paramIndex = 1; + if (catalog != null) { + ps.setString(paramIndex++, catalog); + } + if (schema != null) { + ps.setString(paramIndex++, schema); + } + if (table != null) { + ps.setString(paramIndex++, table); + } + ps.closeOnCompletion(); + return ps.executeQuery(); } @Override @@ -1158,25 +1245,26 @@ public ResultSet getClientInfoProperties() throws SQLException { * * @param catalog a catalog name; must match the catalog name as it * is stored in the database; "" retrieves those without a catalog; - * null means that the catalog name should not be used to narrow - * the search + * null means that the catalog name should not be used to + * narrow the search * @param schemaPattern a schema name pattern; must match the schema name - * as it is stored in the database; "" retrieves those without a schema; - * null means that the schema name should not be used to narrow - * the search + * as it is stored in the database; "" retrieves those without a + * schema; null means that the schema name should not be used to + * narrow the search * @param functionNamePattern a function name pattern; must match the * function name as it is stored in the database * FUNCTION_CAT String => function catalog (may be null) * FUNCTION_SCHEM String => function schema (may be null) - * FUNCTION_NAME String => function name. This is the name used to invoke the function - * REMARKS String => explanatory comment on the function + * FUNCTION_NAME String => function name. This is the name used to invoke the + * function REMARKS String => explanatory comment on the function * FUNCTION_TYPE short => kind of function: - * - functionResultUnknown - Cannot determine if a return value or table will be returned + * - functionResultUnknown - Cannot determine if a return value or table will + * be returned * - functionNoTable- Does not return a table * - functionReturnsTable - Returns a table - * SPECIFIC_NAME String => the name which uniquely identifies this function within its schema. This is a user - * specified, or DBMS generated, name that may be different then the FUNCTION_NAME for example with overload - * functions + * SPECIFIC_NAME String => the name which uniquely identifies this function + * within its schema. This is a user specified, or DBMS generated, name that + * may be different then the FUNCTION_NAME for example with overload functions */ @Override public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) diff --git a/src/test/java/org/duckdb/TestDuckDBJDBC.java b/src/test/java/org/duckdb/TestDuckDBJDBC.java index c0fb4737..a68aa984 100644 --- a/src/test/java/org/duckdb/TestDuckDBJDBC.java +++ b/src/test/java/org/duckdb/TestDuckDBJDBC.java @@ -4420,6 +4420,80 @@ public static void test_column_metadata() throws Exception { } } + public static void test_metadata_get_sql_keywords() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + String rs = conn.getMetaData().getSQLKeywords(); + String[] keywords = rs.split(","); + List list = asList(keywords); + assertTrue(list.contains("select")); + assertTrue(list.contains("update")); + assertTrue(list.contains("delete")); + assertTrue(list.contains("drop")); + } + } + + public static void test_metadata_get_numeric_functions() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + String rs = conn.getMetaData().getNumericFunctions(); + // print out rs + String[] functions = rs.split(","); + List list = asList(functions); + assertTrue(list.contains("abs")); + assertTrue(list.contains("ceil")); + assertTrue(list.contains("floor")); + assertTrue(list.contains("round")); + } + } + + public static void test_metadata_get_string_functions() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + String rs = conn.getMetaData().getStringFunctions(); + String[] functions = rs.split(","); + List list = asList(functions); + assertTrue(list.contains("md5")); + assertTrue(list.contains("json_keys")); + assertTrue(list.contains("repeat")); + assertTrue(list.contains("from_base64")); + } + } + + public static void test_metadata_get_system_functions() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + String rs = conn.getMetaData().getSystemFunctions(); + String[] functions = rs.split(","); + List list = asList(functions); + assertTrue(list.contains("current_date")); + assertTrue(list.contains("now")); + } + } + + public static void test_metadata_get_time_date_functions() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + String rs = conn.getMetaData().getTimeDateFunctions(); + String[] functions = rs.split(","); + List list = asList(functions); + assertTrue(list.contains("day")); + assertTrue(list.contains("dayname")); + assertTrue(list.contains("timezone_hour")); + } + } + + public static void test_metadata_get_index_info() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + try (Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE test (id INT PRIMARY KEY, ok INT)"); + stmt.execute("CREATE INDEX idx_test_ok ON test(ok)"); + } + + try (ResultSet rs = conn.getMetaData().getIndexInfo(null, null, "test", false, false)) { + assertTrue(rs.next()); + assertEquals(rs.getString("TABLE_NAME"), "test"); + assertEquals(rs.getString("INDEX_NAME"), "idx_test_ok"); + assertEquals(rs.getBoolean("NON_UNIQUE"), true); + } + } + } + public static void main(String[] args) throws Exception { System.exit(runTests(args, TestDuckDBJDBC.class, TestExtensionTypes.class)); } diff --git a/src/test/test.iml b/src/test/test.iml new file mode 100644 index 00000000..a0e49a3b --- /dev/null +++ b/src/test/test.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file