Address some edge cases related to bootstrapping a fresh system.
[cfb.git] / prod / net / jaekl / cfb / store / DbStore.java
1 package net.jaekl.cfb.store;
2
3 import java.sql.Connection;
4 import java.sql.SQLException;
5 import java.util.List;
6
7 import net.jaekl.cfb.analyze.Analysis;
8 import net.jaekl.cfb.db.CfbSchema;
9 import net.jaekl.cfb.db.Column;
10 import net.jaekl.cfb.db.Condition;
11 import net.jaekl.cfb.db.Operation;
12 import net.jaekl.cfb.db.Row;
13 import net.jaekl.cfb.db.Sort;
14 import net.jaekl.cfb.db.Table;
15 import net.jaekl.cfb.db.TypeMismatchException;
16 import net.jaekl.cfb.db.driver.DbDriver;
17 import net.jaekl.cfb.xml.BugCollection;
18 import net.jaekl.cfb.xml.BugInstance;
19 import net.jaekl.cfb.xml.LocalVariable;
20 import net.jaekl.cfb.xml.messages.BugCategory;
21 import net.jaekl.cfb.xml.messages.BugPattern;
22 import net.jaekl.cfb.xml.messages.MessageCollection;
23
24 public class DbStore {
25         Connection m_con;
26         DbDriver m_driver;
27         MessageCollection m_msgColl;
28         
29         public DbStore(Connection con, DbDriver driver, MessageCollection msgColl) {
30                 m_con = con;
31                 m_driver = driver;
32                 m_msgColl = msgColl;
33         }
34
35         public Analysis getPrior(Analysis analysis) throws SQLException, TypeMismatchException, StoreException {
36                 if (null == analysis) {
37                         return null;
38                 }
39                 Long priorId = getPriorId(analysis);
40                 if (null == priorId) {
41                         return null;
42                 }
43                 
44                 return getAnalysis(priorId);
45         }
46         
47         public boolean put(Analysis analysis) throws SQLException, TypeMismatchException, StoreException {
48                 if (null == analysis) {
49                         return false;
50                 }
51                 
52                 assert (null != analysis.getProjectName());
53                 
54                 // ----------------------------------
55                 // Add a run record for this analysis
56                 
57                 long runId = m_driver.nextVal(m_con, CfbSchema.RUN_SEQ);
58                 Object[][] values = { 
59                                                                 {
60                                                                         Long.valueOf(runId),
61                                                                         analysis.getProjectName(),
62                                                                         analysis.getBuildNumber(),
63                                                                         analysis.getStart(),
64                                                                         analysis.getEnd() 
65                                                                 } 
66                                                         };
67                 int count = m_driver.insert(m_con, CfbSchema.RUNS, values);
68                 if (1 != count) {
69                         return false;
70                 }
71                 
72                 // -------------------------------------
73                 // Add a found record for each bug found
74                 
75                 List<BugInstance> bugs = analysis.getBugCollection().getBugs();
76                 values = new Object[bugs.size()][CfbSchema.FOUND.getNumColumns()];
77                 
78                 int row = 0;
79                 for (BugInstance bug : bugs)
80                 {
81                         Long foundId = Long.valueOf(m_driver.nextVal(m_con, CfbSchema.FOUND_SEQ));
82                         Long bugId = Long.valueOf(m_msgColl.getPattern(bug.getType()).getId());
83                         Long categoryId = Long.valueOf(m_msgColl.getCategory(bug.getCategory()).getId());
84                         List<Location> locs = bug.getLocations();
85                         Location firstLoc  = (locs.size() > 0) ? locs.get(0) : null;
86                         Location secondLoc = (locs.size() > 1) ? locs.get(1) : null;
87                         Location thirdLoc  = (locs.size() > 2) ? locs.get(2) : null;
88                         
89                         if (BugPattern.UNKNOWN.getId() == bugId) {
90                                 throw new StoreException(StoreException.Type.UNKNOWN_PATTERN, ""+bug.getType());
91                         }
92                         if (BugCategory.UNKNOWN.getId() == categoryId) {
93                                 throw new StoreException(StoreException.Type.UNKNOWN_CATEGORY, ""+bug.getCategory());
94                         }
95                         
96                         values[row][0] = foundId;
97                         values[row][1] = runId;
98                         values[row][2] = bugId;
99                         values[row][3] = categoryId;
100                         values[row][4] = getLocId(firstLoc);
101                         values[row][5] = getLocId(secondLoc);
102                         values[row][6] = getLocId(thirdLoc);
103                         values[row][7] = getVarId(bug);
104                         row++;
105                 }
106                 
107                 count = m_driver.insert(m_con, CfbSchema.FOUND, values);
108                 return (bugs.size() == count);
109         }
110         
111         String getBugType(Long bugPatternId) throws SQLException, TypeMismatchException
112         {
113                 Column[] columns = { CfbSchema.TYPE };
114                 Table[] tables = { CfbSchema.BUGS };
115                 Condition[] conditions = { new Condition(CfbSchema.BUGID, bugPatternId, Operation.EQUAL) }; 
116                 Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions);
117                 String type = row.getString(0);
118                 return type;
119         }
120         
121         String getCategoryName(Long categoryId) throws SQLException, TypeMismatchException
122         {
123                 Column[] columns = { CfbSchema.CATEGORY };
124                 Table[] tables = { CfbSchema.CATEGORIES };
125                 Condition[] conditions = { new Condition(CfbSchema.CATEGORYID, categoryId, Operation.EQUAL) };
126                 Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions);
127                 String name = row.getString(0);
128                 return name;
129         }
130         
131         
132         Location getLoc(Long locId) throws TypeMismatchException, StoreException
133         {
134                 if (null == locId) {
135                         return null;
136                 }
137                 
138                 Column[] columns = { CfbSchema.CLASSNAME, CfbSchema.METHODNAME, CfbSchema.METHODROLE, CfbSchema.STARTLINE, CfbSchema.ENDLINE };
139                 Table[] tables = { CfbSchema.LOCATIONS };
140                 Condition[] conditions = { new Condition(CfbSchema.LOCID, locId, Operation.EQUAL) };
141                 
142                 try {
143                         Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions);
144
145                         String className = row.getString(0);
146                         String methodName = row.getString(1);
147                         String methodRole = row.getString(2);
148                         Integer startLine = row.getInt(3);
149                         Integer endLine = row.getInt(4);
150                         
151                         Location loc = new Location(locId, className, methodName, methodRole, startLine, endLine);
152                         return loc;
153                 }
154                 catch (SQLException exc) {
155                         throw new StoreException(exc, StoreException.Type.INVALID_LOC_ID, ""+locId);
156                 }
157         }
158         
159         Long getLocId(Location loc) throws SQLException, TypeMismatchException 
160         {
161                 if (null == loc) {
162                         return null;
163                 }
164                 Long locId = findLocId(loc);
165                 if (null != locId) {
166                         return locId;
167                 }
168
169                 return storeLoc(loc);
170         }
171         
172         Long findLocId(Location loc) throws SQLException, TypeMismatchException
173         {
174                 Column[] columns = { CfbSchema.LOCID };
175                 Table[] tables = { CfbSchema.LOCATIONS };
176                 
177                 Condition[] conditions = { 
178                                                 new Condition( CfbSchema.CLASSNAME,  loc.getClassName(),  Operation.EQUAL ),
179                                                 new Condition( CfbSchema.METHODNAME, loc.getMethodName(), Operation.EQUAL ),
180                                                 new Condition( CfbSchema.METHODROLE, loc.getMethodRole(), Operation.EQUAL ),
181                                                 new Condition( CfbSchema.STARTLINE,  loc.getStart(),      Operation.EQUAL ),
182                                                 new Condition( CfbSchema.ENDLINE,    loc.getEnd(),        Operation.EQUAL )
183                                         };
184                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
185                 if (rows.size() > 0) {
186                         assert(1 == rows.size());       // should only have one match
187                         
188                         return rows.get(0).getLong(0);
189                 }
190                 
191                 return null;    // not found
192         }
193         
194         Long storeLoc(Location loc) throws SQLException
195         {
196                 long locId = m_driver.nextVal(m_con, CfbSchema.LOC_SEQ);
197                 
198                 Object[][] values = { { 
199                                                         Long.valueOf(locId),
200                                                         loc.getClassName(),
201                                                         loc.getMethodName(),
202                                                         loc.getMethodRole(),
203                                                         Long.valueOf(loc.getStart()),
204                                                         Long.valueOf(loc.getEnd())
205                                                 } };
206                 int count = m_driver.insert(m_con, CfbSchema.LOCATIONS, values);
207                 if (1 != count) {
208                         return null;
209                 }
210                 
211                 return Long.valueOf(locId);
212         }
213         
214         Long getVarId(BugInstance bug) throws SQLException, TypeMismatchException
215         {
216                 if (null == bug) {
217                         return null;
218                 }
219                 
220                 List<LocalVariable> vars = bug.getVariables();
221                 if ((null == vars) || (0 == vars.size())) {
222                         return null;
223                 }
224                 
225                 return getVarId(vars.get(0));
226         }
227         
228         LocalVariable getVar(Long varId) throws SQLException, TypeMismatchException, StoreException
229         {
230                 if (null == varId) {
231                         return null;
232                 }
233                 
234                 Column[] columns = { CfbSchema.NAME, CfbSchema.VARROLE };
235                 Table[] tables = { CfbSchema.VARIABLES };
236                 Condition[] conditions = { new Condition(CfbSchema.VARID_PK, varId, Operation.EQUAL) };
237                 
238                 List<Row> result = m_driver.select(m_con, columns, tables, conditions);
239                 if (result.size() < 1) {
240                         throw new StoreException(StoreException.Type.INVALID_VAR_ID, ""+varId);
241                 }
242                 if (result.size() > 1) {
243                         throw new SQLException("Too many matches (" + result.size() + ") found for variable ID " + varId);
244                 }
245                 
246                 String varName = result.get(0).getString(0);
247                 String varRole = result.get(0).getString(1);
248                 
249                 return new LocalVariable(varId, varName, varRole);
250         }
251         
252         Long getVarId(LocalVariable var) throws SQLException, TypeMismatchException
253         {
254                 if (null == var) {
255                         return null;
256                 }
257                 
258                 Long result = findVarId(var);
259                 
260                 if (null != result) {
261                         return result;
262                 }
263                 
264                 return storeVar(var);
265         }
266         
267         Long findVarId(LocalVariable var) throws SQLException, TypeMismatchException
268         {
269                 Column[] columns = { CfbSchema.VARID_PK };
270                 Table[] tables = { CfbSchema.VARIABLES };
271                 
272                 Condition[] conditions = { 
273                                                 new Condition( CfbSchema.NAME,    var.getName(), Operation.EQUAL ),
274                                                 new Condition( CfbSchema.VARROLE, var.getRole(), Operation.EQUAL )
275                                         };
276                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
277                 if (rows.size() > 0) {
278                         assert(1 == rows.size());       // should only have one match
279                         
280                         return rows.get(0).getLong(0);
281                 }
282                 
283                 return null;    // not found
284         }
285         
286         Long storeVar(LocalVariable var) throws SQLException
287         {
288                 long varId = m_driver.nextVal(m_con, CfbSchema.VARIABLE_SEQ);
289                 
290                 Object[][] values = { { 
291                                                         Long.valueOf(varId),
292                                                         var.getName(),
293                                                         var.getRole()
294                                                 } };
295                 int count = m_driver.insert(m_con, CfbSchema.VARIABLES, values);
296                 if (1 != count) {
297                         return null;
298                 }
299                 
300                 return Long.valueOf(varId);
301         }
302         
303         Long getPriorId(Analysis analysis) throws SQLException, TypeMismatchException
304         {
305                 Column[] columns = { CfbSchema.RUNID };
306                 Table[] tables = { CfbSchema.RUNS };
307                 Condition[] conditions = {
308                         new Condition( CfbSchema.PROJNAME, analysis.getProjectName(), Operation.EQUAL ),
309                         new Condition( CfbSchema.STARTTIME, analysis.getStart(), Operation.LESS_THAN ) 
310                 };
311                 Sort[] sorts = { new Sort( CfbSchema.STARTTIME, Sort.Direction.DESCENDING ) };
312                 int limit = 1;
313                 
314                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions, sorts, limit);
315                 if (rows.size() < 1) {
316                         return null;
317                 }
318                 return rows.get(0).getLong(0);
319         }
320         
321         Analysis getAnalysis(Long analysisId) throws SQLException, TypeMismatchException, StoreException
322         {
323                 Column[] columns = { CfbSchema.PROJNAME, CfbSchema.VERSION, CfbSchema.STARTTIME, CfbSchema.ENDTIME };
324                 Table[] tables = { CfbSchema.RUNS };
325                 Condition[] conditions = { new Condition( CfbSchema.RUNID, analysisId, Operation.EQUAL ) };
326                 
327                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
328                 if (rows.size() < 1) {
329                         return null;
330                 }
331                 
332                 Row row = rows.get(0);
333                 
334                 String projName = row.getString(0);
335                 String version = row.getString(1);
336                 java.util.Date start= row.getDate(2);
337                 java.util.Date end = row.getDate(3);
338                 
339                 Analysis prior = new Analysis(projName, version);
340                 prior.setId(analysisId.longValue());
341                 prior.setStart(start);
342                 prior.setEnd(end);
343                 
344                 prior.setBugCollection(getBugCollection(analysisId));
345                 
346                 return prior;
347         }
348         
349         BugCollection getBugCollection(Long runId) throws SQLException, TypeMismatchException, StoreException 
350         {
351                 Column[] columns = {
352                                 CfbSchema.FOUNDID,
353                                 CfbSchema.BUGID,
354                                 CfbSchema.CATEGORYID,
355                                 CfbSchema.FIRSTLOCID,
356                                 CfbSchema.SECONDLOCID,
357                                 CfbSchema.THIRDLOCID,
358                                 CfbSchema.VARID_FK 
359                 };
360                 Table[] tables = {
361                                 CfbSchema.FOUND
362                 };
363                 Condition[] conditions = {
364                                 new Condition(CfbSchema.RUNID, runId, Operation.EQUAL)
365                 };
366                 
367                 BugCollection coll = new BugCollection();
368                 
369                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
370                 
371                 for (Row row : rows) {
372                         // long foundId = row.getLong(0);
373                         Long bugId = row.getLong(1);
374                         Long categoryId = row.getLong(2);
375                         Long firstLocId = row.getLong(3);
376                         Long secondLocId = row.getLong(4);
377                         Long thirdLocId = row.getLong(5);
378                         Long varId = row.getLong(6);
379                         
380                         String bugType = getBugType(bugId);
381                         String category = getCategoryName(categoryId);
382                         Location[] locations = { getLoc(firstLocId), getLoc(secondLocId), getLoc(thirdLocId) };
383                         LocalVariable[] vars = { getVar(varId) };
384
385                         
386                         BugInstance bug = new BugInstance(bugId, category, bugType, locations, vars);
387                         coll.add(bug);
388                 }
389                 
390                 return coll;
391         }
392 }