Partial implemenation of load-Analysis code.
authorChris Jaekl <cejaekl@yahoo.com>
Tue, 29 Sep 2015 13:23:09 +0000 (22:23 +0900)
committerChris Jaekl <cejaekl@yahoo.com>
Tue, 29 Sep 2015 13:23:09 +0000 (22:23 +0900)
Modified storage of TIMESTAMPTZ to ye olde workaround:  store milliseconds since the Unix epoch.
It seems that, even in 2015, we still haven't got a reliable mechanism to load TIMESTAMPTZ
via JDBC, unless you roll your own converter from java.sql.Timestamp.toString(), and that is
way too brittle (and more work than I feel like engaging in).

prod/net/jaekl/cfb/analyze/Analysis.java
prod/net/jaekl/cfb/db/Column.java
prod/net/jaekl/cfb/db/Row.java
prod/net/jaekl/cfb/db/Sort.java [new file with mode: 0644]
prod/net/jaekl/cfb/db/driver/DbDriver.java
prod/net/jaekl/cfb/store/DbStore.java

index f61d44dbabf4db52d869a7d81b47ded8b69a802d..66c0b8850cea2102f5502295bd4ac8219f00d983 100644 (file)
@@ -17,12 +17,14 @@ import org.xml.sax.XMLReader;
 import org.xml.sax.helpers.XMLReaderFactory;
 
 public class Analysis {
+       long m_id;
        BugCollection m_bugCollection;
        String m_buildNumber;
        Date m_start;   // Date/time when analysis was started
        Date m_end;
        
        public Analysis(String buildNumber) {
+               m_id = (-1);
                m_bugCollection = null;
                m_buildNumber = buildNumber;
                m_start = new Date();
@@ -30,9 +32,15 @@ public class Analysis {
        }
        
        public BugCollection getBugCollection() { return m_bugCollection; }
+       public long getId() { return m_id; }
        public String getBuildNumber() { return m_buildNumber; }
        public Date getStart() { return m_start; }
-       public Date getEnd() { return m_end; }
+       public Date getEnd() { return m_end; }  // the end time (when FindBugs was done analyzing)
+
+       public void setBugCollection(BugCollection bugs) { m_bugCollection = bugs; }
+       public void setId(long id) { m_id = id; }
+       public void setStart(Date start) { m_start = start; }
+       public void setEnd(Date date) { m_end = date; }
        
        public void parse(InputSource xml) throws FileNotFoundException, IOException, SAXException 
        {
@@ -46,11 +54,6 @@ public class Analysis {
            reader.parse(xml);
        }
        
-       // Set the end time (when FindBugs was done analyzing)
-       public void setEnd(Date date) 
-       {
-               m_end = date;
-       }
        
        public void dump(PrintWriter pw)
        {
index 527ad7ddc5e2eea64c7653635393de02efa40b5a..db748b2a0afd083c32d083d1d01a7116153ce518 100644 (file)
@@ -4,7 +4,7 @@ package net.jaekl.cfb.db;
 
 public class Column {
        public enum Type {
-               CHAR, INTEGER, TIMESTAMP, TIMESTAMPTZ, VARCHAR 
+               CHAR, INTEGER, TIMESTAMPTZ, VARCHAR 
        };
        public enum Null {
                NOT_NULL, NULL
index c5d5478954ce08468daf90074b035624eda52ead..f25b0ae0bd93432e4b2378248794ad2545ca5939 100644 (file)
@@ -32,6 +32,14 @@ public class Row {
                return num.longValue();
        }
        
+       public java.util.Date getDate(int index) throws TypeMismatchException
+       {
+               checkType(index, Column.Type.INTEGER);
+               long milliseconds = (Long)m_values[index];
+               java.util.Date date = new java.util.Date(milliseconds);
+               return date;
+       }
+       
        protected void checkType(int index, Column.Type type) throws TypeMismatchException {
                Column column = m_columns[index];
                if (column.getType().equals(type)) {
diff --git a/prod/net/jaekl/cfb/db/Sort.java b/prod/net/jaekl/cfb/db/Sort.java
new file mode 100644 (file)
index 0000000..f1d0deb
--- /dev/null
@@ -0,0 +1,16 @@
+package net.jaekl.cfb.db;
+
+public class Sort {
+       public static enum Direction { ASCENDING, DESCENDING };
+       
+       private Direction m_dir;
+       private Column m_col;
+       
+       public Sort(Column col, Direction dir) {
+               m_col = col;
+               m_dir = dir;
+       }
+       
+       public Column getColumn() { return m_col; }
+       public Direction getDirection() { return m_dir; }
+}
index f85db06ca3345f66f7e9732f8fa3c75db1e2ca18..eea3e22c44c8d89f8fa79d21a52089b87e05555b 100644 (file)
@@ -2,13 +2,12 @@ package net.jaekl.cfb.db.driver;
 
 // Copyright (C) 2015 Christian Jaekl
 
-import static net.jaekl.cfb.db.Column.Null.*;
+import static net.jaekl.cfb.db.Column.Null.NOT_NULL;
 
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -18,6 +17,7 @@ import net.jaekl.cfb.db.Column.Type;
 import net.jaekl.cfb.db.Condition;
 import net.jaekl.cfb.db.Row;
 import net.jaekl.cfb.db.Sequence;
+import net.jaekl.cfb.db.Sort;
 import net.jaekl.cfb.db.Table;
 
 public abstract class DbDriver {
@@ -83,7 +83,16 @@ public abstract class DbDriver {
        public List<Row> select(Connection con, Column[] columns, Table[] tables, Condition[] conditions)
                throws SQLException
        {
-               String sql = selectSql(columns, tables, conditions);
+               Sort[] sorts = new Sort[0];
+               int limit = (-1);       // no limit
+               
+               return select(con, columns, tables, conditions, sorts, limit);
+       }
+       
+       public List<Row> select(Connection con, Column[] columns, Table[] tables, Condition[] conditions, Sort[] sorts, int limit)
+               throws SQLException
+       {
+               String sql = selectSql(columns, tables, conditions, sorts, limit);
                ArrayList<Row> result = new ArrayList<Row>();
                
                try (PreparedStatement ps = con.prepareStatement(sql)) {
@@ -99,7 +108,13 @@ public abstract class DbDriver {
                                while (rs.next()) {
                                        Object[] values = new Object[columns.length];
                                        for (index = 0; index < columns.length; ++index) {
-                                               values[index] = rs.getObject(index + 1);
+                                               if (columns[index].getType().equals(Type.TIMESTAMPTZ)) {
+                                                       long milliseconds = rs.getLong(index + 1);
+                                                       values[index] = new java.util.Date(milliseconds);
+                                               }
+                                               else {
+                                                       values[index] = rs.getObject(index + 1);
+                                               }
                                        }
                                        Row row = new Row(columns, values);
                                        result.add(row);
@@ -128,10 +143,13 @@ public abstract class DbDriver {
                                
                                for (int col = 0; col < data.length; ++col) {
                                        Object obj = data[col];
-                                       if (obj instanceof java.util.Date) {
+                                       Column column = table.getColumn(col);
+                                       if (column.getType().equals(Type.TIMESTAMPTZ)) {
+                                               // Special case:  because there's no good way to read a TIMESTAMPTZ from 
+                                               // the database using JDBC, we store it as an integer (milliseconds since
+                                               // the epoch, 01.01.1970 00:00:00.000 UTC).
                                                Date date = (Date)obj;
-                                               Timestamp ts = new Timestamp(date.getTime());
-                                               ps.setTimestamp(col + 1, ts);
+                                               ps.setLong(col + 1, date.getTime());
                                        }
                                        else {
                                                ps.setObject(col + 1, data[col]);
@@ -202,7 +220,7 @@ public abstract class DbDriver {
                return sb.toString();
        }
        
-       protected String selectSql(Column[] columns, Table[] tables, Condition[] conditions) 
+       protected String selectSql(Column[] columns, Table[] tables, Condition[] conditions, Sort[] sorts, int limit
        {
                StringBuilder sb = new StringBuilder("SELECT ");
                
@@ -247,11 +265,47 @@ public abstract class DbDriver {
                                  .append(condition.getOperation().getSql());
                        }
                }
+
+               if (null != sorts && sorts.length > 0) {
+                       sb.append(" ORDER BY ");
+                       
+                       boolean firstSort = true;
+                       
+                       for (Sort sort : sorts) {
+                               if (firstSort) {
+                                       firstSort = false;
+                               }
+                               else {
+                                       sb.append(", ");
+                               }
+                               
+                               sb.append(sort.getColumn().getName());
+                               
+                               if (sort.getDirection().equals(Sort.Direction.ASCENDING)) {
+                                       sb.append(" ASCENDING ");
+                               }
+                               else {
+                                       sb.append(" DESCENDING ");
+                               }
+                       }
+               }
+               
+               if (limit > 0) {
+                       sb.append(" LIMIT " + limit + " ");
+               }
                
                return sb.toString();
        }
        
        protected String typeName(Type type) {
+               // Special case:  TIMESTAMPTZ stored as INTEGER (milliseconds since the epoch)
+               // Reading a TIMESTAMPTZ back from the DB, and converting it to a java.util.Date,
+               // is fraught with peril.  The best way around this is to store the dates in 
+               // milliseconds-since-the-epoch (01.01.1970 00:00:00.000 UTC).
+               if (Type.TIMESTAMPTZ.equals(type)) {
+                       return Type.INTEGER.toString();
+               }
+               
                return type.toString();
        }
        
index 315ade579272e91cb8d91b22bd8b5d1d58fca54c..37a389801ca5ced85bf4246de05a012ae68e4a2a 100644 (file)
@@ -11,10 +11,12 @@ import net.jaekl.cfb.db.Column;
 import net.jaekl.cfb.db.Condition;
 import net.jaekl.cfb.db.Operation;
 import net.jaekl.cfb.db.Row;
+import net.jaekl.cfb.db.Sort;
 import net.jaekl.cfb.db.Table;
 import net.jaekl.cfb.db.TypeMismatchException;
 import net.jaekl.cfb.db.driver.DbDriver;
 import net.jaekl.cfb.xml.BugClass;
+import net.jaekl.cfb.xml.BugCollection;
 import net.jaekl.cfb.xml.BugInstance;
 import net.jaekl.cfb.xml.BugMethod;
 import net.jaekl.cfb.xml.LocalVariable;
@@ -31,6 +33,18 @@ public class DbStore {
                m_driver = driver;
                m_msgColl = msgColl;
        }
+
+       public Analysis getPrior(Analysis analysis) throws SQLException, TypeMismatchException {
+               if (null == analysis) {
+                       return null;
+               }
+               Long priorId = getPriorId(analysis);
+               if (null == priorId) {
+                       return null;
+               }
+               
+               return getAnalysis(priorId);
+       }
        
        public boolean put(Analysis analysis) throws SQLException, TypeMismatchException {
                if (null == analysis) {
@@ -261,4 +275,51 @@ public class DbStore {
                
                return Long.valueOf(varId);
        }
+       
+       Long getPriorId(Analysis analysis) throws SQLException, TypeMismatchException
+       {
+               Column[] columns = { CfbSchema.RUNID };
+               Table[] tables = { CfbSchema.RUNS };
+               Condition[] conditions = { new Condition( CfbSchema.STARTTIME, analysis.getStart(), Operation.LESS_THAN ) };
+               Sort[] sorts = { new Sort( CfbSchema.STARTTIME, Sort.Direction.DESCENDING ) };
+               int limit = 1;
+               
+               List<Row> rows = m_driver.select(m_con, columns, tables, conditions, sorts, limit);
+               if (rows.size() < 1) {
+                       return null;
+               }
+               return rows.get(0).getLong(0);
+       }
+       
+       Analysis getAnalysis(Long priorId) throws SQLException, TypeMismatchException
+       {
+               Column[] columns = { CfbSchema.VERSION, CfbSchema.STARTTIME, CfbSchema.ENDTIME };
+               Table[] tables = { CfbSchema.RUNS };
+               Condition[] conditions = { new Condition( CfbSchema.RUNID, priorId, Operation.EQUAL ) };
+               
+               List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
+               if (rows.size() < 1) {
+                       return null;
+               }
+               
+               Row row = rows.get(0);
+               
+               String version = row.getString(0);
+               java.util.Date start= row.getDate(1);
+               java.util.Date end = row.getDate(2);
+               
+               Analysis prior = new Analysis(version);
+               prior.setId(priorId.longValue());
+               prior.setStart(start);
+               prior.setEnd(end);
+               
+               prior.setBugCollection(getBugCollection(priorId));
+               
+               return prior;
+       }
+       
+       BugCollection getBugCollection(Long priorId) throws SQLException, TypeMismatchException 
+       {
+               throw new UnsupportedOperationException("Not yet implemented");
+       }
 }