Changeset: d9f9e077cd03 for monetdb-java
URL: https://dev.monetdb.org/hg/monetdb-java?cmd=changeset;node=d9f9e077cd03
Modified Files:
        ChangeLog
        src/main/java/org/monetdb/util/MDBvalidator.java
Branch: default
Log Message:

Finish implementation of foreign key referential integrity checks for user 
tables.


diffs (truncated from 421 to 300 lines):

diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -43,7 +43,7 @@
   - Primary Key uniqueness
   - Primary Key column(s) being NOT NULL (currently only for \vsci)
   - Unique constraint uniqueness
-  - Foreign Key referential integrity (currently only for \vsci)
+  - Foreign Key referential integrity
   - Column NOT NULL constraint
   - Varchar(n) max length constraint
   - Idem for char(n), clob(n), blob(n), json(n) and url(n).
diff --git a/src/main/java/org/monetdb/util/MDBvalidator.java 
b/src/main/java/org/monetdb/util/MDBvalidator.java
--- a/src/main/java/org/monetdb/util/MDBvalidator.java
+++ b/src/main/java/org/monetdb/util/MDBvalidator.java
@@ -16,6 +16,10 @@ import java.sql.Statement;
 import java.sql.SQLException;
 import java.sql.Types;
 
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
 /**
  * <pre>MonetDB Data Integrity Validator class (MDBvalidator) can
  * a) validate system tables data integrity in system schemas: sys and tmp
@@ -30,7 +34,7 @@ import java.sql.Types;
  *             primary key uniqueness
  *     TODO primary key column(s) not null
  *             unique constraint uniqueness
- *     TODO foreign key referential integrity
+ *             foreign key referential integrity
  *             column not null
  *             column maximum length for char/varchar/clob/blob/json/url 
columns which have max length &gt; 0
  *
@@ -104,7 +108,7 @@ public final class MDBvalidator {
 */
 
        public static void validateSqlCatalogIntegrity(final Connection conn) {
-               MDBvalidator mdbv = new MDBvalidator(conn);
+               final MDBvalidator mdbv = new MDBvalidator(conn);
                if (mdbv.checkMonetDBVersion()) {
                        mdbv.validateSchema("sys", null, sys_pkeys, sys_akeys, 
sys_fkeys, sys_notnull, true);
                        mdbv.validateSchema("tmp", null, tmp_pkeys, tmp_akeys, 
tmp_fkeys, tmp_notnull, true);
@@ -112,7 +116,7 @@ public final class MDBvalidator {
        }
 
        public static void validateSqlNetcdfTablesIntegrity(final Connection 
conn) {
-               MDBvalidator mdbv = new MDBvalidator(conn);
+               final MDBvalidator mdbv = new MDBvalidator(conn);
                if (mdbv.checkMonetDBVersion()) {
                        // determine if the 5 netcdf tables exist in the sys 
schema
                        if (mdbv.checkTableExists("sys", "netcdf_files")
@@ -125,7 +129,7 @@ public final class MDBvalidator {
        }
 
        public static void validateSqlGeomTablesIntegrity(final Connection 
conn) {
-               MDBvalidator mdbv = new MDBvalidator(conn);
+               final MDBvalidator mdbv = new MDBvalidator(conn);
                if (mdbv.checkMonetDBVersion()) {
                        if (mdbv.checkTableExists("sys", "spatial_ref_sys"))    
// No need to also test if view sys.geometry_columns exists
                                mdbv.validateSchema("sys", "geom", geom_pkeys, 
geom_akeys, geom_fkeys, geom_notnull, false);
@@ -133,7 +137,7 @@ public final class MDBvalidator {
        }
 
        public static void validateSchemaIntegrity(final Connection conn, final 
String schema) {
-               MDBvalidator mdbv = new MDBvalidator(conn);
+               final MDBvalidator mdbv = new MDBvalidator(conn);
                if (mdbv.checkSchemaExists(schema))
                        mdbv.validateSchema(schema, null, null, null, null, 
null, true);
                else
@@ -141,8 +145,8 @@ public final class MDBvalidator {
        }
 
        public static void validateDBIntegrity(final Connection conn) {
-               MDBvalidator mdbv = new MDBvalidator(conn);
-               Statement stmt = mdbv.createStatement("validateDBIntegrity()");
+               final MDBvalidator mdbv = new MDBvalidator(conn);
+               final Statement stmt = 
mdbv.createStatement("validateDBIntegrity()");
                if (stmt == null)
                        return;
 
@@ -179,7 +183,7 @@ public final class MDBvalidator {
                final String[][] colnotnull,
                final boolean checkMaxStr)
        {
-               boolean is_system_schema = ("sys".equals(schema) || 
"tmp".equals(schema));
+               final boolean is_system_schema = ("sys".equals(schema) || 
"tmp".equals(schema));
 
                if (pkeys != null) {
                        validateUniqueness(schema, group, pkeys, "Primary Key 
uniqueness");
@@ -220,7 +224,7 @@ public final class MDBvalidator {
                final int len = data.length;
                System.out.println("Checking " + minimumWidth(len,6) + (group 
!= null ? " " + group : "") + " tables/keys  in schema " + schema + " for " + 
checkType + " violations.");
 
-               StringBuilder sb = new StringBuilder(256);      // reusable 
buffer to compose SQL validation queries
+               final 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;
@@ -249,12 +253,12 @@ public final class MDBvalidator {
                final boolean pkey,
                final String checkType)
        {
-               Statement stmt = createStatement("validateUniqueness()");
+               final Statement stmt = createStatement("validateUniqueness()");
                if (stmt == null)
                        return;
 
                // fetch the primary or unique key info from the MonetDB system 
tables
-               StringBuilder sb = new StringBuilder(400);
+               final StringBuilder sb = new StringBuilder(400);
                sb.append(" FROM sys.keys k JOIN sys.tables t ON k.table_id = 
t.id JOIN sys.schemas s ON t.schema_id = s.id"
                                + " WHERE k.type = ").append(pkey ? 0 : 1)      
// 0 = primary keys, 1 = unique keys
                        .append(" and s.name = '").append(schema).append("'");
@@ -299,7 +303,7 @@ public final class MDBvalidator {
                                                // reuse the StringBuilder by 
cleaning it partial
                                                sb.setLength(qry_len);
                                                sb.append(keycols)
-                                               .append(" FROM 
\"").append(sch).append("\".\"").append(prv_tbl).append("\"")
+                                               .append(" FROM 
\"").append(sch).append("\".\"").append(prv_tbl).append('"')
                                                .append(" GROUP BY 
").append(keycols)
                                                .append(" HAVING COUNT(*) > 
1;");
                                                validateQuery(sb.toString(), 
sch, prv_tbl, keycols, checkType);
@@ -313,7 +317,7 @@ public final class MDBvalidator {
                                        // reuse the StringBuilder by cleaning 
it partial
                                        sb.setLength(qry_len);
                                        sb.append(keycols)
-                                       .append(" FROM 
\"").append(sch).append("\".\"").append(prv_tbl).append("\"")
+                                       .append(" FROM 
\"").append(sch).append("\".\"").append(prv_tbl).append('"')
                                        .append(" GROUP BY ").append(keycols)
                                        .append(" HAVING COUNT(*) > 1;");
                                        validateQuery(sb.toString(), sch, 
prv_tbl, keycols, checkType);
@@ -336,7 +340,7 @@ public final class MDBvalidator {
                final int len = data.length;
                System.out.println("Checking " + minimumWidth(len,6) + (group 
!= null ? " " + group : "") + " foreign keys in schema " + schema + " for " + 
checkType + " violations.");
 
-               StringBuilder sb = new StringBuilder(400);      // reusable 
buffer to compose SQL validation queries
+               final StringBuilder sb = new StringBuilder(400);        // 
reusable buffer to compose SQL validation queries
                sb.append("SELECT ");
                final int qry_len = sb.length();
                String tbl;
@@ -368,12 +372,19 @@ public final class MDBvalidator {
                final String schema,
                final String checkType)
        {
-               Statement stmt = createStatement("validateFKs()");
+               Statement stmt = null;
+               try {
+                       // the resultset needs to be scrollable (see 
rs.previous())
+                       stmt = 
con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, 
ResultSet.CONCUR_READ_ONLY);
+               } catch (SQLException e) {
+                       System.err.print("Failed to create Statement in 
validateFKs()");
+                       printExceptions(e);
+               }
                if (stmt == null)
                        return;
 
                // fetch the foreign key info from the MonetDB system tables
-               StringBuilder sb = new StringBuilder(400);
+               final StringBuilder sb = new StringBuilder(400);
                sb.append(" FROM sys.keys k JOIN sys.tables t ON k.table_id = 
t.id JOIN sys.schemas s ON t.schema_id = s.id"
                                + " WHERE k.type = 2")  // 2 = foreign keys
                        .append(" and s.name = '").append(schema).append("'");
@@ -382,8 +393,102 @@ public final class MDBvalidator {
                System.out.println("Checking " + minimumWidth(count,6) + " 
foreign keys in schema " + schema + " for " + checkType + " violations.");
 
                ResultSet rs = null;
-               // TODO: finish FK validation implementation
+               try {
+                       sb.setLength(0);        // empty previous usage of sb
+                       // fetch the foreign key columns info from the MonetDB 
system tables
+                       sb.append("SELECT " +
+                               "fs.name as fsch, ft.name as ftbl, fo.name as 
fcol, fo.nr as fnr," +
+                               "ps.name as psch, pt.name as ptbl, po.name as 
pcol" +
+                               // ",  fk.name as fkey, pk.name as pkey" +
+                               " FROM sys.keys fk" +
+                               " JOIN sys.objects fo ON fk.id = fo.id" +
+                               " JOIN sys.tables ft ON fk.table_id = ft.id" +
+                               " JOIN sys.schemas fs ON ft.schema_id = fs.id" +
+                               " JOIN sys.keys pk ON fk.rkey = pk.id" +
+                               " JOIN sys.objects po ON pk.id = po.id" +
+                               " JOIN sys.tables pt ON pk.table_id = pt.id" +
+                               " JOIN sys.schemas ps ON pt.schema_id = ps.id" +
+                               " WHERE fk.type = 2" +  // 2 = foreign keys
+                               " AND fo.nr = po.nr")   // important: matching 
fk-pk column ordering
+                       .append(" AND fs.name = '").append(schema).append("'")
+                       .append(" ORDER BY ft.name, fk.name, fo.nr;");
+                       qry = sb.toString();
+                       rs = stmt.executeQuery(qry);
+                       if (rs != null) {
+                               String fsch = null, ftbl = null, fcol = null;
+                               String psch = null, ptbl = null, pcol = null;
+                               // String fkey = null, pkey = null, 
+                               int fnr = -1;
+                               final Set<String> fk = new 
LinkedHashSet<String>(6);
+                               final Set<String> pk = new 
LinkedHashSet<String>(6);
+                               int i;
+                               while (rs.next()) {
+                                       // retrieve meta data
+                                       fsch = rs.getString(1);
+                                       ftbl = rs.getString(2);
+                                       fcol = rs.getString(3);
+                                       fnr = rs.getInt(4);
+                                       psch = rs.getString(5);
+                                       ptbl = rs.getString(6);
+                                       pcol = rs.getString(7);
+                                       // fkey = rs.getString(8);
+                                       // pkey = rs.getString(9);
+
+                                       fk.clear();
+                                       fk.add(fcol);
+                                       pk.clear();
+                                       pk.add(pcol);
 
+                                       boolean next;
+                                       while ((next = rs.next()) && 
rs.getInt(4) > 0) {
+                                               // collect the fk and pk column 
names for multicolumn fks
+                                               fk.add(rs.getString(3));
+                                               pk.add(rs.getString(7));
+                                       }
+                                       // go back one
+                                       if (next)
+                                               rs.previous();
+
+                                       // compose fk validation query for this 
specific fk
+                                       // select a1, b1, * from tst.s2fk where 
a1 IS NOT NULL AND b1 IS NOT NULL and (a1, b1) NOT IN (select a, b from tst.s2);
+                                       sb.setLength(0);        // empty 
previous usage of sb
+                                       sb.append("SELECT ");
+                                       Iterator<String> it = fk.iterator();
+                                       for (i = 0; it.hasNext(); i++) {
+                                               if (i > 0)
+                                                       sb.append(", ");
+                                               
sb.append('"').append(it.next()).append('"');
+                                       }
+                                       sb.append(", * FROM 
\"").append(fsch).append("\".\"").append(ftbl).append('"');
+                                       sb.append(" WHERE ");
+                                       it = fk.iterator();
+                                       for (i = 0; it.hasNext(); i++) {
+                                               if (i > 0)
+                                                       sb.append(" AND ");
+                                               
sb.append('"').append(it.next()).append("\" IS NOT NULL");
+                                       }
+                                       sb.append(" AND (");
+                                       it = fk.iterator();
+                                       for (i = 0; it.hasNext(); i++) {
+                                               if (i > 0)
+                                                       sb.append(", ");
+                                               
sb.append('"').append(it.next()).append('"');
+                                       }
+                                       sb.append(") NOT IN (SELECT ");
+                                       it = pk.iterator();
+                                       for (i = 0; it.hasNext(); i++) {
+                                               if (i > 0)
+                                                       sb.append(", ");
+                                               
sb.append('"').append(it.next()).append('"');
+                                       }
+                                       sb.append(" FROM 
\"").append(psch).append("\".\"").append(ptbl).append("\");");
+                                       validateQuery(sb.toString(), fsch, 
ftbl, fcol, checkType);
+                               }
+                       }
+               } catch (SQLException e) {
+                       System.err.println("Failed to execute query: " + qry);
+                       printExceptions(e);
+               }
                freeStmtRs(stmt, rs);
        }
 
@@ -397,7 +502,7 @@ public final class MDBvalidator {
                final int len = data.length;
                System.out.println("Checking " + minimumWidth(len,6) + (group 
!= null ? " " + group : "") + " columns      in schema " + schema + " for " + 
checkType + " violations.");
 
-               StringBuilder sb = new StringBuilder(256);      // reusable 
buffer to compose SQL validation queries
+               final StringBuilder sb = new StringBuilder(256);        // 
reusable buffer to compose SQL validation queries
                sb.append("SELECT ");
                final int qry_len = sb.length();
                String tbl;
@@ -437,12 +542,12 @@ public final class MDBvalidator {
                final boolean system,
                final String checkType)
        {
-               Statement stmt = createStatement("validateNotNull()");
+               final Statement stmt = createStatement("validateNotNull()");
                if (stmt == null)
                        return;
 
                // 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);
+               final 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)
@@ -468,7 +573,7 @@ public final class MDBvalidator {
                                        // 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(" FROM 
\"").append(sch).append("\".\"").append(tbl).append('"')
                                        .append(" WHERE 
\"").append(col).append("\" IS NULL;");
                                        validateQuery(sb.toString(), sch, tbl, 
col, checkType);
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to