Mercurial > hg > monetdb-java
changeset 365:63cccacad468
Add sys.ms_stuff() to returned list of StringFunctions.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Wed, 16 Sep 2020 18:56:18 +0200 (2020-09-16) |
parents | 37f54743b1fd |
children | 83bfade6333e |
files | src/main/java/nl/cwi/monetdb/client/JdbcClient.java src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java |
diffstat | 2 files changed, 1046 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/src/main/java/nl/cwi/monetdb/client/JdbcClient.java +++ b/src/main/java/nl/cwi/monetdb/client/JdbcClient.java @@ -26,9 +26,11 @@ import java.sql.Connection; import java.sql.DriverManager; // required as it will load the nl.cwi.monetdb.jdbc.MonetDriver class import java.sql.DatabaseMetaData; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.Statement; import java.sql.SQLException; import java.sql.SQLWarning; +import java.sql.Types; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -41,7 +43,7 @@ import java.util.List; * of JDBC only. * * @author Fabian Groffen, Martin van Dinther - * @version 1.4 + * @version 1.5 */ public final class JdbcClient { @@ -779,6 +781,8 @@ public final class JdbcClient { if (tbl != null) tbl.close(); } + } else if (command.equals("\\check_system_catalog_integrity")) { + MDBvalidator.check_system_catalog_integrity(con); } else if (command.startsWith("\\l") || command.startsWith("\\i")) { String object = command.substring(2).trim(); if (scolonterm && object.endsWith(";")) @@ -1301,3 +1305,1041 @@ final class Table { } } } + +/** + * MonetDB Data Integrity Validator program (MDBvalidator) can + * a) check system tables data integrity (in system schemas: sys, tmp, json, profiler, and possibly more depending on MonetDB version) + * this includes violations of: + * primary key uniqueness + * primary key column(s) not null + * unique constraint uniqueness (alternate keys) + * foreign key referential integrity (match / partial) + * column not null + * column maximum length for char/varchar/clob/blob/json/url + * b) check user schema tables & columns data integrity based on available meta data from system tables/views + * column not null + * column maximum length for char/varchar/clob/blob/json/url + * TODO primary key uniqueness + * TODO primary key column(s) not null + * TODO unique constraint uniqueness (alternate keys) + * TODO foreign key referential integrity (match / partial) + * + * designed and created by Martin van Dinther + */ + +final class MDBvalidator { + private static final String prg = "MDBvalidator"; + private static Connection con; + private static int majorversion; + private static int minorversion; + + private static boolean verbose = false; // set it to true for tracing all generated SQL queries +// private static boolean use_log_table = false; // Not Yet Supported (and maybe not needed) + +// MDBvalidator() {} + + public static void main(String[] args) throws Exception { + System.out.println(prg + " started with " + args.length + " arguments." + (args.length == 0 ? " Using default JDBC URL !" : "")); + // parse input args: connection (JDBC_URL), check systbls (default) or user schema or user db + + String JDBC_URL = (args.length > 0) ? args[0] + : "jdbc:monetdb://localhost:50000/demo?user=monetdb&password=monetdb&so_timeout=14000"; // &treat_clob_as_varchar=true"; + if (!JDBC_URL.startsWith("jdbc:monetdb://")) { + System.out.println("ERROR: Invalid JDBC URL. It does not start with jdbc:monetdb:"); + return; + } + try { + // make connection to target server + con = java.sql.DriverManager.getConnection(JDBC_URL); + System.out.println(prg + " connected to MonetDB server"); + printExceptions(con.getWarnings()); + + check_system_catalog_integrity(con); + } catch (SQLException e) { + printExceptions(e); + } + + // free resources + if (con != null) { + try { con.close(); } catch (SQLException e) { /* ignore */ } + } + } + + static void check_system_catalog_integrity(Connection conn) { + long start_time = System.currentTimeMillis(); + try { + con = conn; + // retrieve server version numbers (major and minor). These are needed to filter out version specific validations + DatabaseMetaData dbmd = con.getMetaData(); + if (dbmd != null) { + majorversion = dbmd.getDatabaseMajorVersion(); + minorversion = dbmd.getDatabaseMinorVersion(); + System.out.println("MonetDB server version " + dbmd.getDatabaseProductVersion()); + // validate majorversion (should be 11) and minorversion (should be >= 19) (from Jul2015 (11.19.15)) + if (majorversion < 11 || (majorversion == 11 && minorversion < 19)) { + System.out.println("ERROR: this MonetDB server is too old for " + prg + ". Please upgrade server."); + con.close(); + return; + } + } + String cur_schema = con.getSchema(); + if (!"sys".equals(cur_schema)) + con.setSchema("sys"); + + verify("sys", null, sys_pkeys, sys_akeys, sys_fkeys, sys_notnull, true); + verify("tmp", null, tmp_pkeys, tmp_akeys, tmp_fkeys, tmp_notnull, true); + + // first determine if netcdf tables (sys.netcdf_files, sys.netcdf_dims, sys.netcdf_vars, sys.netcdf_vardim) exist in the db + if (false) { // ToDo built check for existance of the 5 netcdf tables in sys + verify("sys", "netcdf", netcdf_pkeys, netcdf_akeys, netcdf_fkeys, netcdf_notnull, false); + } + + // first determine if geom tables (sys.spatial_ref_sys) exist in the db + if (true) { // ToDo built check for existance of the 2 geom tables in sys + verify("sys", "geom", geom_pkeys, geom_akeys, geom_fkeys, geom_notnull, false); + } + } catch (SQLException e) { + printExceptions(e); + } + + long elapsed = System.currentTimeMillis() - start_time; + long secs = elapsed /1000; + System.out.println("Validation completed in " + secs + "s and " + (elapsed - (secs *1000)) + "ms"); + } + + private static void verify(String schema, String group, String[][] pkeys, String[][] akeys, String[][] fkeys, String[][] colnotnull, boolean checkmaxstr) { + boolean is_system_schema = ("sys".equals(schema) || "tmp".equals(schema)); + if (pkeys != null) + verifyUniqueness(schema, group, pkeys, "Primary Key uniqueness"); + if (pkeys != null) + verifyNotNull(schema, group, pkeys, "Primary Key Not Null"); + if (akeys != null) + verifyUniqueness(schema, group, akeys, "Alternate Key uniqueness"); + if (fkeys != null) + verifyFKs(schema, group, fkeys, "Foreign Key referential integrity"); + if (colnotnull != null) + verifyNotNull(schema, group, colnotnull, "Not Null"); + else + verifyNotNull(schema, is_system_schema, "Not Null"); + if (checkmaxstr) + verifyMaxCharStrLength(schema, is_system_schema, "Max Character Length"); +/* TODO + * col char/varchar/clob/blob/json/url minimum length (some columns may not be empty, so length >= 1) + * col with sequence (serial/bigserial/autoincrement) in range (0/1/min_value .. max_value) + * col domain is valid (date/time/timestamp/json/inet/url/uuid/...) + * col in list checks (some columns may have only certain values which are not recorded somewhere (eg as fk)) + * col conditional checks (column is not null when other column is (not) null) + -- either column_id or expression in sys.table_partitions must be populated + SELECT "column_id", "expression", 'Missing either column_id or expression' AS violation, * FROM "sys"."table_partitions" WHERE "column_id" IS NULL AND "expression" IS NULL; + SELECT "column_id", "expression", 'column_id and expression may not both be populated. One of them must be NULL' AS violation, * FROM "sys"."table_partitions" WHERE "column_id" IS NOT NULL AND "expression" IS NOT NULL; + */ + } + + private static void verifyUniqueness(String schema, String group, String[][] data, String check_type) { + final int len = data.length; + System.out.println("Checking " + len + (group != null ? " " + group : "") + " tables in schema " + schema + " for " + check_type + " violations."); + + StringBuilder sb = new StringBuilder(256); // reusable buffer to compose SQL validation queries + sb.append("SELECT COUNT(*) AS duplicates, "); + final int qry_len = sb.length(); + String tbl; + String keycols; + for (int i = 0; i < len; i++) { + if (isValidVersion(data[i][2])) { + tbl = data[i][0]; + keycols = data[i][1]; + // reuse the StringBuilder by cleaning it partial + sb.setLength(qry_len); + sb.append(keycols).append(" FROM "); + if (!tbl.startsWith("(")) { // when tbl starts with ( it is a unioned table set which we cannot prefix with a schema name qualifier + sb.append(schema).append('.'); + } + sb.append(tbl) + .append(" GROUP BY ").append(keycols) + .append(" HAVING COUNT(*) > 1;"); + validate(sb.toString(), schema, tbl, keycols, check_type); + } + } + } + + private static void verifyNotNull(String schema, String group, String[][] data, String check_type) { + final int len = data.length; + System.out.println("Checking " + len + (group != null ? " " + group : "") + " columns in schema " + schema + " for " + check_type + " violations."); + + StringBuilder sb = new StringBuilder(256); // reusable buffer to compose SQL validation queries + sb.append("SELECT "); + final int qry_len = sb.length(); + String tbl; + String col; + boolean multicolumn = false; + StringBuilder isNullCond = new StringBuilder(80); + for (int i = 0; i < len; i++) { + if (isValidVersion(data[i][2])) { + tbl = data[i][0]; + col = data[i][1]; + multicolumn = col.contains(", "); // some pkeys consist of multiple columns + isNullCond.setLength(0); // empty previous content + if (multicolumn) { + String[] cols = col.split(", "); + for (int c = 0; c < cols.length; c++) { + if (c > 0) { + isNullCond.append(" OR "); + } + isNullCond.append(cols[c]).append(" IS NULL"); + } + } else { + isNullCond.append(col).append(" IS NULL"); + } + // reuse the StringBuilder by cleaning it partial + sb.setLength(qry_len); + sb.append(col) + .append(", * FROM ").append(schema).append('.').append(tbl) + .append(" WHERE ").append(isNullCond).append(';'); + validate(sb.toString(), schema, tbl, col, check_type); + } + } + } + + private static void verifyFKs(String schema, String group, String[][] data, String check_type) { + final int len = data.length; + System.out.println("Checking " + len + (group != null ? " " + group : "") + " foreign keys in schema " + schema + " for " + check_type + " violations."); + + StringBuilder sb = new StringBuilder(400); // reusable buffer to compose SQL validation queries + sb.append("SELECT "); + final int qry_len = sb.length(); + String tbl; + String cols; + String ref_tbl; + String ref_cols; + for (int i = 0; i < len; i++) { + if (isValidVersion(data[i][4])) { + tbl = data[i][0]; + cols = data[i][1]; + ref_cols = data[i][2]; + ref_tbl = data[i][3]; + // reuse the StringBuilder by cleaning it partial + sb.setLength(qry_len); + sb.append(cols).append(", * FROM ").append(schema).append('.').append(tbl); + if (!tbl.contains(" WHERE ")) + sb.append(" WHERE "); + sb.append('(').append(cols).append(") NOT IN (SELECT ").append(ref_cols).append(" FROM "); + if (!ref_tbl.contains(".")) + sb.append(schema).append('.'); + sb.append(ref_tbl).append(");"); + validate(sb.toString(), schema, tbl, cols, check_type); + } + } + } + + private static void verifyNotNull(String schema, boolean system, String check_type) { + // fetch the NOT NULL info from the MonetDB system tables as those are leading for user tables (but not system tables) + StringBuilder sb = new StringBuilder(400); + sb.append(" from sys.columns c join sys.tables t on c.table_id = t.id join sys.schemas s on t.schema_id = s.id" + + " where t.type in (0, 10, 1, 11) and c.\"null\" = false" // t.type 0 = TABLE, 10 = SYSTEM TABLE, 1 = VIEW, 11 = SYSTEM VIEW + + " and t.system = ").append(system) + .append(" and s.name = '").append(schema).append("'"); + String qry = sb.toString(); + long count = runCountQuery(qry); + System.out.println("Checking " + count + " columns in schema " + schema + " for " + check_type + " violations."); + + Statement stmt = createStatement("verifyMaxCharStrLength()"); + ResultSet rs = null; + try { + sb.setLength(0); // empty previous usage of sb + sb.append("SELECT s.name as sch_nm, t.name as tbl_nm, c.name as col_nm") // , t.type, t.system, c.type, c.type_digits + .append(qry).append(" ORDER BY s.name, t.name, c.name;"); + qry = sb.toString(); + if (stmt != null) + rs = stmt.executeQuery(qry); + if (rs != null) { + String sch, tbl, col; + while (rs.next()) { + // retrieve meta data + sch = rs.getString(1); + tbl = rs.getString(2); + col = rs.getString(3); + // compose validation query for this specific column + sb.setLength(0); // empty previous usage of sb + sb.append("SELECT '").append(sch).append('.').append(tbl).append('.').append(col).append("' as full_col_nm, *") + .append(" FROM \"").append(sch).append("\".\"").append(tbl).append("\"") + .append(" WHERE \"").append(col).append("\" IS NULL;"); + validate(sb.toString(), sch, tbl, col, check_type); + } + } + } catch (SQLException e) { + System.out.println("Failed to execute query: " + qry); + printExceptions(e); + } + freeStmtRs(stmt, rs); + } + + private static void verifyMaxCharStrLength(String schema, boolean system, String check_type) { + // fetch the max char str len info from the MonetDB system tables as those are leading + StringBuilder sb = new StringBuilder(400); + sb.append(" from sys.columns c join sys.tables t on c.table_id = t.id join sys.schemas s on t.schema_id = s.id" + + " where t.type in (0, 10, 1, 11) and c.type_digits >= 1" // t.type 0 = TABLE, 10 = SYSTEM TABLE, 1 = VIEW, 11 = SYSTEM VIEW + + " and t.system = ").append(system) + .append(" and s.name = '").append(schema).append("'") + .append(" and c.type in ('varchar', 'char', 'clob', 'json', 'url', 'blob')"); // only for variable character/bytes data type columns + String qry = sb.toString(); + long count = runCountQuery(qry); + System.out.println("Checking " + count + " columns in schema " + schema + " for " + check_type + " violations."); + + Statement stmt = createStatement("verifyMaxCharStrLength()"); + ResultSet rs = null; + try { + sb.setLength(0); // empty previous usage of sb + sb.append("SELECT s.name as sch_nm, t.name as tbl_nm, c.name as col_nm, c.type_digits") // , t.type, t.system, c.type + .append(qry).append(" ORDER BY s.name, t.name, c.name, c.type_digits;"); + qry = sb.toString(); + if (stmt != null) + rs = stmt.executeQuery(qry); + if (rs != null) { + long max_len = 0; + String sch, tbl, col; + while (rs.next()) { + // retrieve meta data + sch = rs.getString(1); + tbl = rs.getString(2); + col = rs.getString(3); + max_len = rs.getLong(4); + // compose validation query for this specific column + sb.setLength(0); // empty previous usage of sb + sb.append("SELECT '").append(sch).append('.').append(tbl).append('.').append(col).append("' as full_col_nm, ") + .append(max_len).append(" as max_allowed_length, ") + .append("length(\"").append(col).append("\") as data_length, ") + .append("\"").append(col).append("\" as data_value") + .append(" FROM \"").append(sch).append("\".\"").append(tbl).append("\"") + .append(" WHERE \"").append(col).append("\" IS NOT NULL AND length(\"").append(col).append("\") > ").append(max_len); + validate(sb.toString(), sch, tbl, col, check_type); + } + } + } catch (SQLException e) { + System.out.println("Failed to execute query: " + qry); + printExceptions(e); + } + freeStmtRs(stmt, rs); + } + + + /* Run a validation query. + * It should result in no rows returned. + * When rows are returned those are the ones that contain violations. + * Retrieve them and convert the results into a (large) violation string + * Log/Print the violation. + */ + private static void validate(String qry, String sch, String tbl, String cols, String checktype) { + Statement stmt = createStatement("validate()"); + ResultSet rs = null; + ResultSetMetaData rsmd; + StringBuilder sb = new StringBuilder(1024); + int nr_cols; + int row; + String val; + int tp; + try { + if (verbose) { + System.out.println(qry); + } + if (stmt != null) + rs = stmt.executeQuery(qry); + if (rs != null) { + rsmd = rs.getMetaData(); + nr_cols = rsmd.getColumnCount(); + sb.setLength(0); // empty previous usage of sb + row = 0; + while (rs.next()) { + // query returns found violations + row++; + if (row == 1) { + // print result header once + for (int i = 1; i <= nr_cols; i++) { + sb.append((i > 1) ? ", " : "\t"); + sb.append(rsmd.getColumnLabel(i)); + } + sb.append('\n'); + } + // retrieve row data + for (int i = 1; i <= nr_cols; i++) { + sb.append((i > 1) ? ", " : "\t"); + val = rs.getString(i); + if (val == null || rs.wasNull()) { + sb.append("null"); + } else { + tp = rsmd.getColumnType(i); // this method is very fast, so no need to cache it outside the loop + if (tp == Types.VARCHAR || tp == Types.CHAR || tp == Types.CLOB || tp == Types.BLOB) { + sb.append('"').append(val).append('"'); + } else { + sb.append(val); + } + } + } + sb.append('\n'); + } + if (row > 0) + logViolations(checktype, sch, tbl, cols, qry, sb.toString()); + } + } catch (SQLException e) { + System.out.println("Failed to execute query: " + qry); + printExceptions(e); + } + freeStmtRs(stmt, rs); + } + + private static long runCountQuery(String from_qry) { + Statement stmt = createStatement("runCountQuery()"); + ResultSet rs = null; + long count = 0; + try { + if (stmt != null) + rs = stmt.executeQuery("SELECT COUNT(*) " + from_qry); + if (rs != null) { + if (rs.next()) { + // retrieve count data + count = rs.getLong(1); + } + } + } catch (SQLException e) { + System.out.println("Failed to execute select count(*) " + from_qry); + printExceptions(e); + } + freeStmtRs(stmt, rs); + return count; + } + + private static Statement createStatement(String method) { + try { + return con.createStatement(); + } catch (SQLException e) { + System.out.print("Failed to create Statement in " + method); + printExceptions(e); + } + return null; + } + + // private static java.text.SimpleDateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss.SSS"); + private static void logViolations(String checktype, String schema, String table, String columns, String query, String violations) { +// String dts = df.format(new java.util.Date()); + StringBuilder sb = new StringBuilder(8192); +// sb.append(dts).append('\t') + sb.append(checktype).append(" violation(s) found in \"") + .append(schema).append("\".\"").append(table).append("\".\"").append(columns).append("\":\n") + .append(violations) + .append("Found using query: ").append(query).append("\n"); + System.out.println(sb.toString()); + } + + private static boolean isValidVersion(String version) { + if (version != null) { + try { + int v = Integer.parseInt(version); + return minorversion >= v; + } catch (NumberFormatException e) { + /* ignore */ + } + return false; + } + return true; // when no version string is supplied it is valid by default + } + + /* test if the standard log table already exists in target DB. + * if not try to create it. + */ +/* private static boolean create_log_table(Connection con) { + private static final String log_tbl_name = "sys.\"" + prg + "_log\""; + boolean has_log_table = false; + Statement stmt = createStatement("create_log_table()"); + ResultSet rs = null; + try { + rs = stmt.executeQuery("SELECT * FROM " + log_tbl_name + " LIMIT 1;"); + if (rs != null) { + if (rs.next()) { + has_log_table = true; + // ToDo check that the columns are the correct signature + } + } + } catch (SQLException e0) { + // we will get here when the log table does not exist yet + try { + // create it ourselves + int upd = stmt.executeUpdate("CREATE TABLE " + log_tbl_name + " IF NOT EXISTS " + + "(datetime TIMESTAMP(6) NOT NULL," + + " schema_nm VARCHAR(1024) NOT NULL," + + " table_nm VARCHAR(1024) NOT NULL," + + " column_nm VARCHAR(10240) NOT NULL," + + " validation VARCHAR(99999) NOT NULL," + + " violations VARCHAR(99999) NOT NULL);"); + if (upd >= 0) + has_log_table = true; + } catch (SQLException e) { + System.out.print("Failed to create table " + log_tbl_name); + printExceptions(e); + } + } + freeStmtRs(stmt, rs); + return has_log_table; + } +*/ + + private static void printExceptions(SQLException se) { + while (se != null) { + System.out.println(se.getSQLState() + " " + se.getMessage()); + se = se.getNextException(); + } + } + + private static void freeStmtRs(Statement stmt, ResultSet rs) { + // free resources + if (rs != null) { + try { rs.close(); } catch (SQLException e) { /* ignore */ } + } + if (stmt != null) { + try { stmt.close(); } catch (SQLException e) { /* ignore */ } + } + } + + +// ********* below are multiple 2-dimensional String arrays containing the data for constructing the validation queries ********* + // based on data from: https://dev.monetdb.org/hg/MonetDB/file/Jun2020/sql/test/sys-schema/Tests + + // static list of all sys tables with its pkey columns + // each entry contains: table_nm, pk_col_nms, from_minor_version + // data pulled from https://dev.monetdb.org/hg/MonetDB/file/Jun2020/sql/test/sys-schema/Tests/check_PrimaryKey_uniqueness.sql + private static final String[][] sys_pkeys = { + {"schemas", "id", null}, + {"table_types", "table_type_id", null}, + {"_tables", "id", null}, + {"tables", "id", null}, // is a view + {"_columns", "id", null}, + {"columns", "id", null}, // is a view + {"function_types", "function_type_id", null}, + {"function_languages", "language_id", null}, + {"functions", "id", null}, + {"systemfunctions", "function_id", null}, // has become a view in Mar2018 and maybe removed in the future as is deprecated + {"args", "id", null}, + {"types", "id", null}, + {"objects", "id, nr", null}, + {"key_types", "key_type_id", null}, + {"keys", "id", null}, + {"index_types", "index_type_id", null}, + {"idxs", "id", null}, + {"triggers", "id", null}, + {"sequences", "id", null}, + {"comments", "id", null}, + {"dependency_types", "dependency_type_id", null}, + {"dependencies", "id, depend_id", null}, + {"ids", "id", null}, // is a view + {"auths", "id", null}, + {"users", "name", null}, + {"user_role", "login_id, role_id", null}, + {"privilege_codes", "privilege_code_id", null}, + {"privileges", "obj_id, auth_id, privileges", null}, + {"querylog_catalog", "id", null}, + {"querylog_calls", "id", null}, + {"querylog_history", "id", null}, +// old {"queue", "qtag", null}, // queue has changed in Jun2020 (11.37.7), pkey was previously qtag + {"queue", "tag", "37"}, // queue has changed in Jun2020 (11.37.7), pkey is now called tag + {"optimizers", "name", null}, + {"environment", "name", null}, + {"keywords", "keyword", null}, + {"db_user_info", "name", null}, +// old {"sessions", "\"user\", login, active", null}, // sessions has changed in Jun2020 (11.37.7), pkey was previously "user", login, active + {"sessions", "sessionid", "37"}, // sessions has changed in Jun2020 (11.37.7), pkey is now called sessionid + {"statistics", "column_id", null}, + {"rejects", "rowid", "19"}, // querying this view caused problems in versions pre Jul2015, see https://www.monetdb.org/bugzilla/show_bug.cgi?id=3794 +// old {"tracelog", "event", null}, -- Error: Profiler not started. This table now (Jun2020) contains only: ticks, stmt +// {"storage", "schema, table, column", null}, // is a view on storage() + {"\"storage\"()", "schema, table, column", null}, // the function "storage"() also lists the storage for system tables + // new views introduced in Apr 2019 feature release (11.33.3) +// {"tablestorage", "schema, table", "33"}, // is a view on view storage +// {"schemastorage", "schema", "33"}, // is a view on view storage + {"storagemodelinput", "schema, table, column", null}, +// {"storagemodel", "schema, table, column", null}, // is a view on storagemodelinput +// {"tablestoragemodel", "schema, table", null}, // is a view on storagemodelinput + // new tables introduced in Apr 2019 feature release (11.33.3) + {"table_partitions", "id", "33"}, + {"range_partitions", "table_id, partition_id, minimum", "33"}, + {"value_partitions", "table_id, partition_id, \"value\"", "33"} + }; + + private static final String[][] tmp_pkeys = { + {"_tables", "id", null}, + {"_columns", "id", null}, + {"objects", "id, nr", null}, + {"keys", "id", null}, + {"idxs", "id", null}, + {"triggers", "id", null} + }; + + private static final String[][] netcdf_pkeys = { + {"netcdf_files", "file_id", null}, + {"netcdf_dims", "dim_id, file_id", null}, + {"netcdf_vars", "var_id, file_id", null}, + {"netcdf_vardim", "var_id, dim_id, file_id", null} + }; + + private static final String[][] geom_pkeys = { + {"spatial_ref_sys", "srid", null} + }; + + + // static list of all sys tables with its alternate key (unique constraint) columns + // each entry contains: table_nm, ak_col_nms, from_minor_version + // data pulled from https://dev.monetdb.org/hg/MonetDB/file/Jun2020/sql/test/sys-schema/Tests/check_AlternateKey_uniqueness.sql + private static final String[][] sys_akeys = { + {"schemas", "name", null}, + {"table_types", "table_type_name", null}, + {"_tables", "schema_id, name", null}, + {"tables", "schema_id, name", null}, // is a view + {"_columns", "table_id, name", null}, + {"columns", "table_id, name", null}, // is a view + {"_columns", "table_id, number", null}, + {"columns", "table_id, number", null}, // is a view + // The id values from sys.schemas, sys._tables, sys._columns and sys.functions combined must be exclusive (see FK from sys.privileges.obj_id) + {"(SELECT id FROM sys.schemas UNION ALL SELECT id FROM sys._tables UNION ALL SELECT id FROM sys._columns UNION ALL SELECT id FROM sys.functions) T", "T.id", null}, + {"(SELECT id FROM sys.schemas UNION ALL SELECT id FROM sys.tables UNION ALL SELECT id FROM sys.columns UNION ALL SELECT id FROM sys.functions) T", "T.id", null}, + {"function_types", "function_type_name", null}, + {"function_languages", "language_name", null}, + // the next query used to return duplicates for overloaded functions (same function but with different arg names/types), hence it has been extended + {"functions f join sys.args a on f.id=a.func_id", "schema_id, f.name, func, mod, language, f.type, side_effect, varres, vararg, a.id", null}, + {"args", "func_id, name, inout", null}, + {"types", "schema_id, systemname, sqlname", null}, + {"objects", "id, name", null}, + {"key_types", "key_type_name", null}, + {"keys", "table_id, name", null}, + {"index_types", "index_type_name", null}, + {"idxs", "table_id, name", null}, + {"triggers", "table_id, name", null}, + {"sequences", "schema_id, name", null}, + {"comments", "id", null}, + {"dependency_types", "dependency_type_name", null}, + {"auths", "name", null}, // is this always unique?? isn't it possible to define a user and a role with the same name? + {"privilege_codes", "privilege_code_name", null}, + {"optimizers", "def", null}, + // new tables introduced in Apr 2019 feature release (11.33.3) + {"table_partitions WHERE column_id IS NOT NULL", "table_id, column_id", "33"}, // requires WHERE "column_id" IS NOT NULL + {"table_partitions WHERE \"expression\" IS NOT NULL", "table_id, \"expression\"", "33"}, // requires WHERE "expression" IS NOT NULL + {"range_partitions", "table_id, partition_id, \"maximum\"", "33"} + }; + + private static final String[][] tmp_akeys = { + {"_tables", "schema_id, name", null}, + {"_columns", "table_id, name", null}, + {"_columns", "table_id, number", null}, + {"objects", "id, name", null}, + {"keys", "table_id, name", null}, + {"idxs", "table_id, name", null}, + {"triggers", "table_id, name", null} + }; + + private static final String[][] netcdf_akeys = { + {"netcdf_files", "location", null} + }; + + private static final String[][] geom_akeys = { + {"spatial_ref_sys", "auth_name, auth_srid, srtext, proj4text", null} + }; + + + // static list of all sys tables with its foreign key columns + // each entry contains: table_nm, fk_col_nms, ref_col_nms, ref_tbl_nm, from_minor_version + // data pulled from https://dev.monetdb.org/hg/MonetDB/file/Jun2020/sql/test/sys-schema/Tests/check_ForeignKey_referential_integrity.sql + private static final String[][] sys_fkeys = { + {"schemas", "authorization", "id", "auths", null}, + {"schemas", "owner", "id", "auths", null}, + {"_tables", "schema_id", "id", "schemas", null}, + {"tables", "schema_id", "id", "schemas", null}, + {"_tables", "type", "table_type_id", "table_types", null}, + {"tables", "type", "table_type_id", "table_types", null}, + {"_columns", "table_id", "id", "_tables", null}, + {"columns", "table_id", "id", "tables", null}, + {"_columns", "type", "sqlname", "types", null}, + {"columns", "type", "sqlname", "types", null}, + {"functions", "schema_id", "id", "schemas", null}, + {"functions", "type", "function_type_id", "function_types", null}, + {"functions", "language", "language_id", "function_languages", null}, + // system functions should refer only to functions in MonetDB system schemas (on Dec2016 these are: sys, json, profiler and bam) + {"functions WHERE system AND ", "schema_id", "id", "schemas WHERE system", null}, + {"args", "func_id", "id", "functions", null}, + {"args", "type", "sqlname", "types", null}, + {"types", "schema_id", "id", "schemas", null}, + // {"types WHERE schema_id <> 0 AND ", "schema_id", "id", "schemas", null}, + {"objects", "id", "id", "ids", null}, + {"ids WHERE obj_type IN ('key', 'index') AND ", "id", "id", "objects", null}, + {"keys", "id", "id", "objects", null}, + {"keys", "table_id", "id", "_tables", null}, + {"keys", "table_id", "id", "tables", null}, + {"keys", "type", "key_type_id", "key_types", null}, + {"keys WHERE rkey <> -1 AND ", "rkey", "id", "keys", null}, +// SELECT * FROM sys.keys WHERE action <> -1 AND action NOT IN (SELECT id FROM sys.?); -- TODO: find out which action values are valid and what they mean. + {"idxs", "id", "id", "objects", null}, + {"idxs", "table_id", "id", "_tables", null}, + {"idxs", "table_id", "id", "tables", null}, + {"idxs", "type", "index_type_id", "index_types", null}, + {"sequences", "schema_id", "id", "schemas", null}, + {"triggers", "table_id", "id", "_tables", null}, + {"triggers", "table_id", "id", "tables", null}, + {"comments", "id", "id", "ids", null}, + {"dependencies", "id", "id", "ids", null}, + {"dependencies", "depend_id", "id", "ids", null}, + {"dependencies", "depend_type", "dependency_type_id", "dependency_types", null}, + {"dependencies", "id, depend_id, depend_type", "v.id, v.used_by_id, v.depend_type", "dependencies_vw v", null}, + {"auths WHERE grantor > 0 AND ", "grantor", "id", "auths", null}, + {"users", "name", "name", "auths", null}, + {"users", "default_schema", "id", "schemas", null}, + {"db_user_info", "name", "name", "auths", null}, + {"db_user_info", "default_schema", "id", "schemas", null}, + {"user_role", "login_id", "id", "auths", null}, + {"user_role", "login_id", "a.id", "auths a WHERE a.name IN (SELECT u.name FROM sys.users u)", null}, + {"user_role", "role_id", "id", "auths", null}, + {"user_role", "role_id", "a.id", "auths a WHERE a.name IN (SELECT u.name FROM sys.users u)", null}, + {"user_role", "role_id", "id", "roles", null} +/* hier verder afmaken +SELECT * FROM sys.privileges WHERE obj_id NOT IN (SELECT id FROM sys.schemas UNION ALL SELECT id FROM sys._tables UNION ALL SELECT id FROM sys._columns UNION ALL SELECT id FROM sys.functions); +SELECT * FROM sys.privileges WHERE auth_id NOT IN (SELECT id FROM sys.auths); +SELECT * FROM sys.privileges WHERE grantor NOT IN (SELECT id FROM sys.auths) AND grantor > 0; +SELECT * FROM sys.privileges WHERE privileges NOT IN (SELECT privilege_code_id FROM sys.privilege_codes); +--SELECT * FROM sys.privileges WHERE privileges NOT IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,32); -- old check before table sys.privilege_codes existed + +SELECT * FROM sys.querylog_catalog WHERE owner NOT IN (SELECT name FROM sys.users); +SELECT * FROM sys.querylog_catalog WHERE pipe NOT IN (SELECT name FROM sys.optimizers); +SELECT * FROM sys.querylog_calls WHERE id NOT IN (SELECT id FROM sys.querylog_catalog); +SELECT * FROM sys.querylog_history WHERE id NOT IN (SELECT id FROM sys.querylog_catalog); +SELECT * FROM sys.querylog_history WHERE owner NOT IN (SELECT name FROM sys.users); +SELECT * FROM sys.querylog_history WHERE pipe NOT IN (SELECT name FROM sys.optimizers); + +SELECT * FROM sys.queue WHERE tag > cast(0 as oid) AND tag NOT IN (SELECT tag FROM sys.queue); +SELECT * FROM sys.queue WHERE tag > cast(0 as oid) AND tag NOT IN (SELECT cast(tag as oid) FROM sys.queue); +SELECT * FROM sys.queue WHERE tag NOT IN (SELECT cast(tag as oid) FROM sys.queue); +SELECT * FROM sys.queue WHERE "username" NOT IN (SELECT name FROM sys.users); + +SELECT * FROM sys.sessions WHERE "username" NOT IN (SELECT name FROM sys.users); + +SELECT * FROM sys.statistics WHERE column_id NOT IN (SELECT id FROM sys._columns UNION ALL SELECT id FROM tmp._columns); +SELECT * FROM sys.statistics WHERE type NOT IN (SELECT sqlname FROM sys.types); + +SELECT * FROM sys.storage() WHERE schema NOT IN (SELECT name FROM sys.schemas); +SELECT * FROM sys.storage() WHERE table NOT IN (SELECT name FROM sys._tables UNION ALL SELECT name FROM tmp._tables); +SELECT * FROM sys.storage() WHERE (schema, table) NOT IN (SELECT sch.name, tbl.name FROM sys.schemas AS sch JOIN sys.tables AS tbl ON sch.id = tbl.schema_id); +SELECT * FROM sys.storage() WHERE column NOT IN (SELECT name FROM sys._columns UNION ALL SELECT name FROM tmp._columns UNION ALL SELECT name FROM sys.keys UNION ALL SELECT name FROM tmp.keys UNION ALL SELECT name FROM sys.idxs UNION ALL SELECT name FROM tmp.idxs); +SELECT * FROM sys.storage() WHERE type NOT IN (SELECT sqlname FROM sys.types); + +SELECT * FROM sys.storage WHERE schema NOT IN (SELECT name FROM sys.schemas); +SELECT * FROM sys.storage WHERE table NOT IN (SELECT name FROM sys._tables UNION ALL SELECT name FROM tmp._tables); +SELECT * FROM sys.storage WHERE (schema, table) NOT IN (SELECT sch.name, tbl.name FROM sys.schemas AS sch JOIN sys.tables AS tbl ON sch.id = tbl.schema_id); +SELECT * FROM sys.storage WHERE column NOT IN (SELECT name FROM sys._columns UNION ALL SELECT name FROM tmp._columns UNION ALL SELECT name FROM sys.keys UNION ALL SELECT name FROM tmp.keys UNION ALL SELECT name FROM sys.idxs UNION ALL SELECT name FROM tmp.idxs); +SELECT * FROM sys.storage WHERE type NOT IN (SELECT sqlname FROM sys.types); + +SELECT * FROM sys.tablestorage WHERE schema NOT IN (SELECT name FROM sys.schemas); +SELECT * FROM sys.tablestorage WHERE table NOT IN (SELECT name FROM sys._tables UNION ALL SELECT name FROM tmp._tables); +SELECT * FROM sys.tablestorage WHERE (schema, table) NOT IN (SELECT sch.name, tbl.name FROM sys.schemas AS sch JOIN sys.tables AS tbl ON sch.id = tbl.schema_id); + +SELECT * FROM sys.schemastorage WHERE schema NOT IN (SELECT name FROM sys.schemas); + +SELECT * FROM sys.storagemodel WHERE schema NOT IN (SELECT name FROM sys.schemas); +SELECT * FROM sys.storagemodel WHERE table NOT IN (SELECT name FROM sys._tables UNION ALL SELECT name FROM tmp._tables); +SELECT * FROM sys.storagemodel WHERE (schema, table) NOT IN (SELECT sch.name, tbl.name FROM sys.schemas AS sch JOIN sys.tables AS tbl ON sch.id = tbl.schema_id); +SELECT * FROM sys.storagemodel WHERE column NOT IN (SELECT name FROM sys._columns UNION ALL SELECT name FROM tmp._columns UNION ALL SELECT name FROM sys.keys UNION ALL SELECT name FROM tmp.keys UNION ALL SELECT name FROM sys.idxs UNION ALL SELECT name FROM tmp.idxs); +SELECT * FROM sys.storagemodel WHERE type NOT IN (SELECT sqlname FROM sys.types); + +SELECT * FROM sys.storagemodelinput WHERE schema NOT IN (SELECT name FROM sys.schemas); +SELECT * FROM sys.storagemodelinput WHERE table NOT IN (SELECT name FROM sys._tables UNION ALL SELECT name FROM tmp._tables); +SELECT * FROM sys.storagemodelinput WHERE (schema, table) NOT IN (SELECT sch.name, tbl.name FROM sys.schemas AS sch JOIN sys.tables AS tbl ON sch.id = tbl.schema_id); +SELECT * FROM sys.storagemodelinput WHERE column NOT IN (SELECT name FROM sys._columns UNION ALL SELECT name FROM tmp._columns UNION ALL SELECT name FROM sys.keys UNION ALL SELECT name FROM tmp.keys UNION ALL SELECT name FROM sys.idxs UNION ALL SELECT name FROM tmp.idxs); +SELECT * FROM sys.storagemodelinput WHERE type NOT IN (SELECT sqlname FROM sys.types); + +SELECT schema, table, rowcount, columnsize, heapsize, hashsize, imprintsize, orderidxsize FROM sys.tablestoragemodel WHERE schema NOT IN (SELECT name FROM sys.schemas); +SELECT schema, table, rowcount, columnsize, heapsize, hashsize, imprintsize, orderidxsize FROM sys.tablestoragemodel WHERE table NOT IN (SELECT name FROM sys._tables UNION ALL SELECT name FROM tmp._tables); +SELECT schema, table, rowcount, columnsize, heapsize, hashsize, imprintsize, orderidxsize FROM sys.tablestoragemodel WHERE (schema, table) NOT IN (SELECT sch.name, tbl.name FROM sys.schemas AS sch JOIN sys.tables AS tbl ON sch.id = tbl.schema_id); + +-- new tables introduced in 2019 +SELECT * FROM sys.table_partitions WHERE "table_id" NOT IN (SELECT id FROM sys._tables); +SELECT * FROM sys.table_partitions WHERE "column_id" IS NOT NULL AND "column_id" NOT IN (SELECT id FROM sys._columns); +SELECT * FROM sys.table_partitions WHERE "type" NOT IN (5,6,9,10); -- 5=By Column Range (1+4), 6=By Expression Range (2+4), 9=By Column Value (1+8), 10=By Expression Value (2+8), see sql_catalog.h #define PARTITION_* + +SELECT * FROM sys.range_partitions WHERE "table_id" NOT IN (SELECT id FROM sys._tables); +SELECT * FROM sys.range_partitions WHERE "partition_id" NOT IN (SELECT id FROM sys.table_partitions); + +SELECT * FROM sys.value_partitions WHERE "table_id" NOT IN (SELECT id FROM sys._tables); +SELECT * FROM sys.value_partitions WHERE "partition_id" NOT IN (SELECT id FROM sys.table_partitions); +*/ + }; + + private static final String[][] tmp_fkeys = { + {"_tables", "schema_id", "id", "sys.schemas", null}, + {"_tables", "type", "table_type_id", "sys.table_types", null}, + {"_columns", "table_id", "id", "_tables", null}, + {"_columns", "type", "sqlname", "sys.types", null}, + {"keys", "table_id", "id", "_tables", null}, + {"keys", "type", "key_type_id", "sys.key_types", null}, + {"keys WHERE rkey <> -1 AND ", "rkey", "id", "keys", null}, +// SELECT * FROM tmp.keys WHERE action <> -1 AND action NOT IN (SELECT id FROM tmp.?); -- TODO: find out which action values are valid and what they mean. + {"idxs", "id", "id", "objects", null}, + {"idxs", "table_id", "id", "_tables", null}, + {"idxs", "type", "index_type_id", "sys.index_types", null}, + {"triggers", "table_id", "id", "_tables", null} + // nog verder afmaken, zie fkey data hierboven + }; + + private static final String[][] netcdf_fkeys = { + {"netcdf_attrs", "file_id", "file_id", "netcdf_files", null}, + {"netcdf_dims", "file_id", "file_id", "netcdf_files", null}, + {"netcdf_vars", "file_id", "file_id", "netcdf_files", null}, + {"netcdf_vardim", "file_id", "file_id", "netcdf_files", null}, + {"netcdf_vardim", "dim_id", "dim_id", "netcdf_dims", null}, + {"netcdf_vardim", "dim_id, file_id", "dim_id, file_id", "netcdf_dims", null}, + {"netcdf_vardim", "var_id", "var_id", "netcdf_vars", null}, + {"netcdf_vardim", "var_id, file_id", "var_id, file_id", "netcdf_vars", null} + }; + + private static final String[][] geom_fkeys = { + {"spatial_ref_sys", "auth_srid", "srid", "spatial_ref_sys", null} + }; + + + // static list of all sys tables with its not null constraint columns + // each entry contains: table_nm, col_nm, from_minor_version + // data pulled from https://dev.monetdb.org/hg/MonetDB/file/Jun2020/sql/test/sys-schema/Tests/check_Not_Nullable_columns.sql + private static final String[][] sys_notnull = { + {"_columns", "id", null}, + {"_columns", "name", null}, + {"_columns", "type", null}, + {"_columns", "type_digits", null}, + {"_columns", "type_scale", null}, + {"_columns", "table_id", null}, + {"_columns", "\"null\"", null}, + {"_columns", "number", null}, + {"_tables", "id", null}, + {"_tables", "name", null}, + {"_tables", "schema_id", null}, + {"_tables", "type", null}, + {"_tables", "system", null}, + {"_tables", "commit_action", null}, + {"_tables", "access", null}, + {"args", "id", null}, + {"args", "func_id", null}, + {"args", "name", null}, + {"args", "type", null}, + {"args", "type_digits", null}, + {"args", "type_scale", null}, + {"args", "inout", null}, + {"args", "number", null}, + {"auths", "id", null}, + {"auths", "name", null}, + {"auths", "grantor", null}, + {"db_user_info", "name", null}, + {"db_user_info", "fullname", null}, + {"db_user_info", "default_schema", null}, + {"dependencies", "id", null}, + {"dependencies", "depend_id", null}, + {"dependencies", "depend_type", null}, + {"function_languages", "language_id", null}, + {"function_languages", "language_name", null}, + {"function_types", "function_type_id", null}, + {"function_types", "function_type_name", null}, + {"function_types", "function_type_keyword", null}, + {"functions", "id", null}, + {"functions", "name", null}, + {"functions", "func", null}, + {"functions", "mod", null}, + {"functions", "language", null}, + {"functions", "type", null}, + {"functions", "side_effect", null}, + {"functions", "varres", null}, + {"functions", "vararg", null}, + {"functions", "schema_id", null}, + {"functions", "system", null}, + {"idxs", "id", null}, + {"idxs", "table_id", null}, + {"idxs", "type", null}, + {"idxs", "name", null}, + {"index_types", "index_type_id", null}, + {"index_types", "index_type_name", null}, + {"key_types", "key_type_id", null}, + {"key_types", "key_type_name", null}, + {"keys", "id", null}, + {"keys", "table_id", null}, + {"keys", "type", null}, + {"keys", "name", null}, + {"keys", "rkey", null}, + {"keys", "action", null}, + {"keywords", "keyword", null}, + {"objects", "id", null}, + {"objects", "name", null}, + {"objects", "nr", null}, + {"optimizers", "name", null}, + {"optimizers", "def", null}, + {"optimizers", "status", null}, + {"privilege_codes", "privilege_code_id", null}, + {"privilege_codes", "privilege_code_name", null}, + {"privileges", "obj_id", null}, + {"privileges", "auth_id", null}, + {"privileges", "privileges", null}, + {"privileges", "grantor", null}, + {"privileges", "grantable", null}, + {"schemas", "id", null}, + {"schemas", "name", null}, + {"schemas", "authorization", null}, + {"schemas", "owner", null}, + {"schemas", "system", null}, + {"sequences", "id", null}, + {"sequences", "schema_id", null}, + {"sequences", "name", null}, + {"sequences", "start", null}, + {"sequences", "minvalue", null}, + {"sequences", "maxvalue", null}, + {"sequences", "increment", null}, + {"sequences", "cacheinc", null}, + {"sequences", "cycle", null}, + {"statistics", "column_id", null}, + {"statistics", "type", null}, + {"statistics", "width", null}, + {"statistics", "stamp", null}, + {"statistics", "\"sample\"", null}, + {"statistics", "count", null}, + {"statistics", "\"unique\"", null}, + {"statistics", "nils", null}, + {"statistics", "sorted", null}, + {"statistics", "revsorted", null}, +/* temporary disabled, TODO re-enable + {"storage", "schema", null}, + {"storage", "table", null}, + {"storage", "column", null}, + {"storage", "type", null}, + {"storage", "mode", null}, + {"storage", "location", null}, + {"storage", "count", null}, + {"storage", "typewidth", null}, + {"storage", "columnsize", null}, + {"storage", "heapsize", null}, + {"storage", "hashes", null}, + {"storage", "phash", null}, + {"storage", "imprints", null}, + {"storage", "orderidx", null}, +*/ {"storagemodelinput", "schema", null}, + {"storagemodelinput", "table", null}, + {"storagemodelinput", "column", null}, + {"storagemodelinput", "type", null}, + {"storagemodelinput", "typewidth", null}, + {"storagemodelinput", "count", null}, + {"storagemodelinput", "\"distinct\"", null}, + {"storagemodelinput", "atomwidth", null}, + {"storagemodelinput", "reference", null}, + {"storagemodelinput", "sorted", null}, + {"storagemodelinput", "\"unique\"", null}, + {"storagemodelinput", "isacolumn", null}, + {"table_types", "table_type_id", null}, + {"table_types", "table_type_name", null}, + {"tables", "id", null}, + {"tables", "name", null}, + {"tables", "schema_id", null}, + {"tables", "type", null}, + {"tables", "system", null}, + {"tables", "commit_action", null}, + {"tables", "access", null}, + {"tables", "temporary", null}, + {"tracelog", "ticks", null}, + {"tracelog", "stmt", null}, + {"triggers", "id", null}, + {"triggers", "name", null}, + {"triggers", "table_id", null}, + {"triggers", "time", null}, + {"triggers", "orientation", null}, + {"triggers", "event", null}, + {"triggers", "statement", null}, + {"types", "id", null}, + {"types", "systemname", null}, + {"types", "sqlname", null}, + {"types", "digits", null}, + {"types", "scale", null}, + {"types", "radix", null}, + {"types", "eclass", null}, + {"types", "schema_id", null}, + {"user_role", "login_id", null}, + {"user_role", "role_id", null}, + {"users", "name", null}, + {"users", "fullname", null}, + {"users", "default_schema", null}, + {"var_values", "var_name", null}, + {"var_values", "value", null}, + // new tables introduced in Apr 2019 feature release (11.33.3) + {"range_partitions", "table_id", "33"}, + {"range_partitions", "partition_id", "33"}, + {"range_partitions", "with_nulls", "33"}, + {"table_partitions", "id", "33"}, + {"table_partitions", "table_id", "33"}, + {"table_partitions", "type", "33"}, + {"value_partitions", "table_id", "33"}, + {"value_partitions", "partition_id", "33"}, + {"value_partitions", "value", "33"} // ?? Can this be null when WITH NULL VALUES is specified. + }; + + private static final String[][] tmp_notnull = { + {"_columns", "id", null}, + {"_columns", "name", null}, + {"_columns", "type", null}, + {"_columns", "type_digits", null}, + {"_columns", "type_scale", null}, + {"_columns", "table_id", null}, + {"_columns", "\"null\"", null}, + {"_columns", "number", null}, + {"_tables", "id", null}, + {"_tables", "name", null}, + {"_tables", "schema_id", null}, + {"_tables", "type", null}, + {"_tables", "system", null}, + {"_tables", "commit_action", null}, + {"_tables", "access", null}, + {"idxs", "id", null}, + {"idxs", "table_id", null}, + {"idxs", "type", null}, + {"idxs", "name", null}, + {"keys", "id", null}, + {"keys", "table_id", null}, + {"keys", "type", null}, + {"keys", "name", null}, + {"keys", "rkey", null}, + {"keys", "action", null}, + {"objects", "id", null}, + {"objects", "name", null}, + {"objects", "nr", null}, + {"triggers", "id", null}, + {"triggers", "name", null}, + {"triggers", "table_id", null}, + {"triggers", "time", null}, + {"triggers", "orientation", null}, + {"triggers", "event", null}, + {"triggers", "statement", null} + }; + + private static final String[][] netcdf_notnull = { + {"netcdf_files", "file_id", null}, + {"netcdf_files", "location", null}, + {"netcdf_dims", "dim_id", null}, + {"netcdf_dims", "file_id", null}, + {"netcdf_dims", "name", null}, + {"netcdf_dims", "length", null}, + {"netcdf_vars", "var_id", null}, + {"netcdf_vars", "file_id", null}, + {"netcdf_vars", "name", null}, + {"netcdf_vars", "vartype", null}, + {"netcdf_vardim", "var_id", null}, + {"netcdf_vardim", "dim_id", null}, + {"netcdf_vardim", "file_id", null}, + {"netcdf_vardim", "dimpos", null}, + {"netcdf_attrs", "obj_name", null}, + {"netcdf_attrs", "att_name", null}, + {"netcdf_attrs", "att_type", null}, + {"netcdf_attrs", "value", null}, + {"netcdf_attrs", "file_id", null}, + {"netcdf_attrs", "gr_name", null} + }; + + private static final String[][] geom_notnull = { + {"spatial_ref_sys", "srid", null}, + {"spatial_ref_sys", "auth_name", null}, + {"spatial_ref_sys", "auth_srid", null}, + {"spatial_ref_sys", "srtext", null}, + {"spatial_ref_sys", "proj4text", null} + }; +}
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDatabaseMetaData.java @@ -408,7 +408,7 @@ public class MonetDatabaseMetaData " AND \"type\" = 1" + // only scalar functions // exclude functions which do not work or belong to string functions (code(int) and space(int)) " AND f.\"name\" NOT IN ('code','not_uniques','rotate_xor_hash','space'))" + - // include specific functions which have no 1st arg (pi()) or it is not numeric + // include specific functions which have no arguments (pi()) or are numeric. Actually ms_str is a conversion function from float to str. " OR f.\"name\" IN ('alpha','degrees','fuse','ms_round','ms_str','ms_trunc','pi','radians')"; return getConcatenatedStringFromQuery(FunctionsSelect + FunctionsWhere + match + OrFunctionsMaxMin + FunctionsOrderBy1); } @@ -422,8 +422,8 @@ public class MonetDatabaseMetaData " AND \"mod\" <> 'sql')" + // include specific functions code(int) and space(int) which belong to the 'str' module " OR \"mod\" = 'str'" + - // include 3 specific json functions, md5() and udf reverse() which all accept a string arg - " OR f.\"name\" IN ('isarray','isobject','isvalid','md5','reverse')"; + // include 3 specific json functions, md5(), ms_stuff() and udf reverse() which all accept a string arg + " OR f.\"name\" IN ('isarray','isobject','isvalid','md5','ms_stuff','reverse')"; final String unionPart = // add functions which are not listed in sys.functions but implemented in the SQL parser (see sql/server/sql_parser.y) " UNION SELECT 'position'";