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