Changeset: 71b039bc2d99 for monetdb-java
URL: https://dev.monetdb.org/hg/monetdb-java?cmd=changeset;node=71b039bc2d99
Modified Files:
        ChangeLog
        src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
        src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java
Branch: default
Log Message:

Added support for querying the sys.comments table for some meta data methods
when connected to a MonetDB server which has table sys.comments.


diffs (290 lines):

diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,12 @@
 # ChangeLog file for monetdb-java
 # This file is updated with Maddlog
 
+* Thu Apr 19 2018 Martin van Dinther <martin.van.dint...@monetdbsolutions.com>
+- DatabaseMetaData methods getTables(), getColumns(), getProcedures() and
+  getFunctions() now return the comment in the REMARKS result column when a
+  comment has been set for the table / view / column / procedure / function
+  via the SQL command COMMENT ON <db-object> IS 'comment-text'.
+
 * Mon Oct 23 2017 Sjoerd Mullender <sjo...@acm.org>
 - Compiled and released new jars: monetdb-jdbc-2.27.jar, monetdb-mcl-1.16.jar
   and updated jdbcclient.jar
diff --git a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java 
b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
@@ -147,6 +147,11 @@ public class MonetConnection
        /** Whether or not CLOB is mapped to Types.VARCHAR instead of 
Types.CLOB within this connection */
        private boolean treatClobAsVarChar = false;
 
+       // Internal cache for determining if system table sys.comments (new as 
of Mar2018 release) exists on server
+       private boolean queriedCommentsTable = false;
+       private boolean hasCommentsTable = false;
+
+
        /**
         * Constructor of a Connection for MonetDB. At this moment the
         * current implementation limits itself to storing the given host,
@@ -1671,6 +1676,46 @@ public class MonetConnection
        }
 
        /**
+        * Internal utility method to query the server to find out if it has
+        * the system table sys.comments (which is new as of Mar2018 release).
+        * The result is cached and reused, so that we only test the query once 
per connection.
+        * This method is used by methods from MonetDatabaseMetaData.
+        */
+       boolean commentsTableExists() {
+               if (queriedCommentsTable)
+                       return hasCommentsTable;
+
+               queriedCommentsTable = true;    // set flag, so the querying 
part below is done only once, at first invocation.
+               Statement stmt = null;
+               ResultSet rs = null;
+               try {
+                       stmt = createStatement();
+                       if (stmt != null) {
+                               rs = stmt.executeQuery( "SELECT \"id\", 
\"remark\" FROM \"sys\".\"comments\" LIMIT 1");
+                               if (rs != null) {
+                                       rs.next();
+                                       hasCommentsTable = true;
+                               }
+                       }
+               } catch (SQLException se) {
+                       /* ignore */
+               } finally {
+                       if (rs != null) {
+                               try {
+                                       rs.close();
+                               } catch (SQLException e) { /* ignore */ }
+                       }
+                       if (stmt != null) {
+                               try {
+                                        stmt.close();
+                               } catch (SQLException e) { /* ignore */ }
+                       }
+               }
+// for debug: System.out.println("commentsTableExists returns: " + 
hasCommentsTable);
+               return hasCommentsTable;
+       }
+
+       /**
         * Sends the given string to MonetDB as special transaction command.
         * All possible returned information is discarded.
         * Encountered errors are reported.
diff --git a/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java 
b/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java
@@ -23,7 +23,7 @@ import java.util.ArrayList;
  * A DatabaseMetaData object suitable for the MonetDB database.
  *
  * @author Fabian Groffen, Martin van Dinther
- * @version 0.6
+ * @version 0.7
  */
 public class MonetDatabaseMetaData extends MonetWrapper implements 
DatabaseMetaData {
        private Connection con;
@@ -1705,21 +1705,25 @@ public class MonetDatabaseMetaData exten
                String procedureNamePattern
        ) throws SQLException
        {
+               boolean useCommentsTable = 
((MonetConnection)con).commentsTableExists();
                StringBuilder query = new StringBuilder(980);
                query.append("SELECT cast(null as varchar(1)) AS 
\"PROCEDURE_CAT\", " +
                        "\"schemas\".\"name\" AS \"PROCEDURE_SCHEM\", " +
                        "\"functions\".\"name\" AS \"PROCEDURE_NAME\", " +
                        "cast(null as char(1)) AS \"Field4\", " +
                        "cast(null as char(1)) AS \"Field5\", " +
-                       "cast(null as char(1)) AS \"Field6\", " +
-                       "cast(\"functions\".\"func\" as varchar(9999)) AS 
\"REMARKS\", " +
+                       "cast(null as char(1)) AS \"Field6\", ")
+                       .append(useCommentsTable ? 
"COALESCE(\"comments\".\"remark\", cast(\"functions\".\"func\" as 
varchar(9999)))" : "cast(\"functions\".\"func\" as varchar(9999))").append(" AS 
\"REMARKS\", " +
                        // in MonetDB procedures have no return value by design.
                        
"CAST(").append(DatabaseMetaData.procedureNoResult).append(" AS smallint) AS 
\"PROCEDURE_TYPE\", " +
                        // only the id value uniquely identifies a procedure. 
Include it to be able to differentiate between multiple overloaded procedures 
with the same name
                        "cast(\"functions\".\"id\" as varchar(10)) AS 
\"SPECIFIC_NAME\" " +
-               "FROM \"sys\".\"functions\" JOIN \"sys\".\"schemas\" ON 
(\"functions\".\"schema_id\" = \"schemas\".\"id\") " +
+               "FROM \"sys\".\"functions\" JOIN \"sys\".\"schemas\" ON 
(\"functions\".\"schema_id\" = \"schemas\".\"id\") ");
+               if (useCommentsTable) {
+                       query.append("LEFT OUTER JOIN \"sys\".\"comments\" ON 
(\"functions\".\"id\" = \"comments\".\"id\") ");
+               }
                // include procedures only (type = 2). Others will be returned 
via getFunctions()
-               "WHERE \"functions\".\"type\" = 2");
+               query.append("WHERE \"functions\".\"type\" = 2");
 
                if (catalog != null && catalog.length() > 0) {
                        // none empty catalog selection.
@@ -1732,7 +1736,6 @@ public class MonetDatabaseMetaData exten
                if (procedureNamePattern != null) {
                        query.append(" AND \"functions\".\"name\" 
").append(composeMatchPart(procedureNamePattern));
                }
-
                query.append(" ORDER BY \"PROCEDURE_SCHEM\", 
\"PROCEDURE_NAME\", \"SPECIFIC_NAME\"");
 
                return executeMetaDataQuery(query.toString());
@@ -1837,11 +1840,11 @@ public class MonetDatabaseMetaData exten
                        "CAST('' as varchar(3)) AS \"IS_NULLABLE\", " +
                        // the specific name contains the function id, in order 
to be able to match the args to the correct overloaded procedure name
                        "cast(\"functions\".\"id\" as varchar(10)) AS 
\"SPECIFIC_NAME\" " +
-               "FROM \"sys\".\"args\", \"sys\".\"functions\", 
\"sys\".\"schemas\" " +
-               "WHERE \"args\".\"func_id\" = \"functions\".\"id\" " +
-               "AND \"functions\".\"schema_id\" = \"schemas\".\"id\" " +
+               "FROM \"sys\".\"args\" " +
+               "JOIN \"sys\".\"functions\" ON (\"args\".\"func_id\" = 
\"functions\".\"id\") " +
+               "JOIN \"sys\".\"schemas\" ON (\"functions\".\"schema_id\" = 
\"schemas\".\"id\") " +
                // include procedures only (type = 2). Others will be returned 
via getFunctionColumns()
-               "AND \"functions\".\"type\" = 2");
+               "WHERE \"functions\".\"type\" = 2");
 
                if (catalog != null && catalog.length() > 0) {
                        // none empty catalog selection.
@@ -1950,6 +1953,7 @@ public class MonetDatabaseMetaData exten
                boolean preJul2015 = 
("11.19.15".compareTo(getDatabaseProductVersion()) >= 0);
                /* for debug: System.out.println("getDatabaseProductVersion() 
is " + getDatabaseProductVersion() + "  preJul2015 is " + preJul2015); */
 
+               boolean useCommentsTable = 
((MonetConnection)con).commentsTableExists();
                StringBuilder query = new StringBuilder(1600);
                if (preJul2015 && types != null && types.length > 0) {
                        // we need to filter on the constructed "TABLE_TYPE" 
expression, this is only possible when we use a subquery in the FROM
@@ -1972,13 +1976,17 @@ public class MonetDatabaseMetaData exten
                } else {
                        query.append("\"table_types\".\"table_type_name\" AS 
\"TABLE_TYPE\", ");
                }
-               query.append("\"tables\".\"query\" AS \"REMARKS\", " +
+               query.append(useCommentsTable ? 
"COALESCE(\"comments\".\"remark\", \"tables\".\"query\")" : 
"\"tables\".\"query\"").append(" AS \"REMARKS\", " +
                        "cast(null as char(1)) AS \"TYPE_CAT\", " +
                        "cast(null as char(1)) AS \"TYPE_SCHEM\", " +
                        "cast(null as char(1)) AS \"TYPE_NAME\", " +
                        "cast(null as char(1)) AS 
\"SELF_REFERENCING_COL_NAME\", " +
                        "cast(null as char(1)) AS \"REF_GENERATION\" " +
-                       "FROM \"sys\".\"tables\", \"sys\".\"schemas\"");
+                       "FROM \"sys\".\"tables\"");
+               if (useCommentsTable) {
+                       query.append(" LEFT OUTER JOIN \"sys\".\"comments\" ON 
(\"tables\".\"id\" = \"comments\".\"id\")");
+               }
+               query.append(", \"sys\".\"schemas\"");
                if (!preJul2015) {
                        query.append(", \"sys\".\"table_types\"");
                }
@@ -2195,6 +2203,7 @@ public class MonetDatabaseMetaData exten
                String columnNamePattern
        ) throws SQLException
        {
+               boolean useCommentsTable = 
((MonetConnection)con).commentsTableExists();
                StringBuilder query = new StringBuilder(2450);
                query.append("SELECT cast(null as char(1)) AS \"TABLE_CAT\", " +
                        "\"schemas\".\"name\" AS \"TABLE_SCHEM\", " +
@@ -2209,8 +2218,8 @@ public class MonetDatabaseMetaData exten
                                "WHEN \"columns\".\"type\" IN ('int', 
'smallint', 'tinyint', 'bigint', 'hugeint', 'float', 'real', 'double', 'oid', 
'wrd') THEN 2 " +
                                "ELSE 0 END AS int) AS \"NUM_PREC_RADIX\", " +
                        "cast(CASE \"null\" WHEN true THEN 
").append(ResultSetMetaData.columnNullable)
-                       .append(" WHEN false THEN 
").append(ResultSetMetaData.columnNoNulls).append(" END AS int) AS 
\"NULLABLE\", " +
-                       "cast(null AS varchar(1)) AS \"REMARKS\", " +
+                       .append(" WHEN false THEN 
").append(ResultSetMetaData.columnNoNulls).append(" END AS int) AS 
\"NULLABLE\", ")
+                       .append(useCommentsTable ? "\"comments\".\"remark\"" : 
"cast(null AS varchar(1))").append(" AS \"REMARKS\", " +
                        "\"columns\".\"default\" AS \"COLUMN_DEF\", " +
                        "cast(0 as int) AS \"SQL_DATA_TYPE\", " +
                        "cast(0 as int) AS \"SQL_DATETIME_SUB\", " +
@@ -2223,11 +2232,14 @@ public class MonetDatabaseMetaData exten
                        "cast(null AS smallint) AS \"SOURCE_DATA_TYPE\", " +
                        "cast(CASE WHEN \"columns\".\"default\" IS NOT NULL AND 
\"columns\".\"default\" LIKE 'next value for %' THEN 'YES' ELSE 'NO' END AS 
varchar(3)) AS \"IS_AUTOINCREMENT\", " +
                        "cast('NO' AS varchar(3)) AS \"IS_GENERATEDCOLUMN\" " +
-               "FROM \"sys\".\"columns\", " +
-                       "\"sys\".\"tables\", " +
-                       "\"sys\".\"schemas\" " +
-               "WHERE \"columns\".\"table_id\" = \"tables\".\"id\" " +
-                       "AND \"tables\".\"schema_id\" = \"schemas\".\"id\"");
+               "FROM \"sys\".\"columns\"");
+               if (useCommentsTable) {
+                       query.append(" LEFT OUTER JOIN \"sys\".\"comments\" ON 
(\"columns\".\"id\" = \"comments\".\"id\")");
+               }
+               query.append(", \"sys\".\"tables\"" +
+                       ", \"sys\".\"schemas\" " +
+               "WHERE \"columns\".\"table_id\" = \"tables\".\"id\"" +
+               "  AND \"tables\".\"schema_id\" = \"schemas\".\"id\"");
 
                if (catalog != null && catalog.length() > 0) {
                        // none empty catalog selection.
@@ -3277,7 +3289,7 @@ public class MonetDatabaseMetaData exten
                        "\"schemas\".\"name\" AS \"TYPE_SCHEM\", " +
                        "\"types\".\"sqlname\" AS \"TYPE_NAME\", " +
                        "CASE \"types\".\"sqlname\"" +
-                               // next 4 UDTs are known
+                               // next 4 UDTs are standard
                                " WHEN 'inet' THEN 
'nl.cwi.monetdb.jdbc.types.INET'" +
                                " WHEN 'json' THEN 'java.lang.String'" +
                                " WHEN 'url'  THEN 
'nl.cwi.monetdb.jdbc.types.URL'" +
@@ -3287,9 +3299,9 @@ public class MonetDatabaseMetaData exten
                                .append(" ELSE ").append(Types.STRUCT).append(" 
END AS int) AS \"DATA_TYPE\", " +
                        "\"types\".\"systemname\" AS \"REMARKS\", " +
                        "cast(null as smallint) AS \"BASE_TYPE\" " +
-                       "FROM \"sys\".\"types\" JOIN \"sys\".\"schemas\" ON 
\"types\".\"schema_id\" = \"schemas\".\"id\" " +
-                       // exclude the built-in types (I assume they always 
have id <= 99 and eclass < 15)
-                       "WHERE \"types\".\"id\" > 99 AND \"types\".\"eclass\" 
>= 15");
+               "FROM \"sys\".\"types\" JOIN \"sys\".\"schemas\" ON 
\"types\".\"schema_id\" = \"schemas\".\"id\" " +
+               // exclude the built-in types (I assume they always have id <= 
99 and eclass < 15)
+               "WHERE \"types\".\"id\" > 99 AND \"types\".\"eclass\" >= 15");
 
                if (catalog != null && catalog.length() > 0) {
                        // none empty catalog selection.
@@ -3302,6 +3314,7 @@ public class MonetDatabaseMetaData exten
                if (typeNamePattern != null) {
                        query.append(" AND \"types\".\"sqlname\" 
").append(composeMatchPart(typeNamePattern));
                }
+
                if (types != null && types.length > 0) {
                        query.append(") AS getUDTs WHERE \"DATA_TYPE\" IN (");
                        for (int i = 0; i < types.length; i++) {
@@ -3866,11 +3879,12 @@ public class MonetDatabaseMetaData exten
                        String functionNamePattern)
                throws SQLException
        {
+               boolean useCommentsTable = 
((MonetConnection)con).commentsTableExists();
                StringBuilder query = new StringBuilder(800);
                query.append("SELECT cast(null as varchar(1)) AS 
\"FUNCTION_CAT\", " +
                        "\"schemas\".\"name\" AS \"FUNCTION_SCHEM\", " +
-                       "\"functions\".\"name\" AS \"FUNCTION_NAME\", " +
-                       "cast(\"functions\".\"func\" as varchar(9999)) AS 
\"REMARKS\", " +
+                       "\"functions\".\"name\" AS \"FUNCTION_NAME\", ")
+                       .append(useCommentsTable ? 
"COALESCE(\"comments\".\"remark\", cast(\"functions\".\"func\" as 
varchar(9999)))" : "cast(\"functions\".\"func\" as varchar(9999))").append(" AS 
\"REMARKS\", " +
                        "CASE \"functions\".\"type\"" +
                                " WHEN 1 THEN 
").append(DatabaseMetaData.functionNoTable)
                        .append(" WHEN 2 THEN 
").append(DatabaseMetaData.functionNoTable)
@@ -3880,10 +3894,12 @@ public class MonetDatabaseMetaData exten
                        .append(" ELSE 
").append(DatabaseMetaData.functionResultUnknown).append(" END AS 
\"FUNCTION_TYPE\", " +
                        // only the id value uniquely identifies a function. 
Include it to be able to differentiate between multiple overloaded functions 
with the same name
                        "cast(\"functions\".\"id\" as varchar(10)) AS 
\"SPECIFIC_NAME\" " +
-               "FROM \"sys\".\"functions\", \"sys\".\"schemas\" " +
-               "WHERE \"functions\".\"schema_id\" = \"schemas\".\"id\" " +
-               // exclude procedures (type = 2). Those need to be returned via 
getProcedures()
-               "AND \"functions\".\"type\" <> 2");
+               "FROM \"sys\".\"functions\" JOIN \"sys\".\"schemas\" ON 
(\"functions\".\"schema_id\" = \"schemas\".\"id\") ");
+               if (useCommentsTable) {
+                       query.append("LEFT OUTER JOIN \"sys\".\"comments\" ON 
(\"functions\".\"id\" = \"comments\".\"id\") ");
+               }
+               // only functions, so exclude procedures (type = 2). Those will 
to be returned via getProcedures()
+               query.append("WHERE \"functions\".\"type\" <> 2");
 
                if (catalog != null && catalog.length() > 0) {
                        // none empty catalog selection.
@@ -3994,11 +4010,11 @@ public class MonetDatabaseMetaData exten
                        "CAST('' as varchar(3)) AS \"IS_NULLABLE\", " +
                        // the specific name contains the function id, in order 
to be able to match the args to the correct overloaded function name
                        "cast(\"functions\".\"id\" as varchar(10)) AS 
\"SPECIFIC_NAME\" " +
-               "FROM \"sys\".\"args\", \"sys\".\"functions\", 
\"sys\".\"schemas\" " +
-               "WHERE \"args\".\"func_id\" = \"functions\".\"id\" " +
-               "AND \"functions\".\"schema_id\" = \"schemas\".\"id\" " +
-               // exclude procedures (type = 2). Those need to be returned via 
getProcedureColumns()
-               "AND \"functions\".\"type\" <> 2");
+               "FROM \"sys\".\"args\" " +
+               "JOIN \"sys\".\"functions\" ON (\"args\".\"func_id\" = 
\"functions\".\"id\") " +
+               "JOIN \"sys\".\"schemas\" ON (\"functions\".\"schema_id\" = 
\"schemas\".\"id\") " +
+               // only functions, so exclude procedures (type = 2). Those will 
to be returned via getProcedureColumns()
+               "WHERE \"functions\".\"type\" <> 2");
 
                if (catalog != null && catalog.length() > 0) {
                        // none empty catalog selection.
_______________________________________________
checkin-list mailing list
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to