More schema-stuff. newdbschema
authorBenjamin Smedberg <benjamin@smedbergs.us>
Mon, 28 Sep 2009 11:46:53 -0400
branchnewdbschema
changeset 21 b792fdef1b26
parent 20 5c4f22089101
child 22 4c1458dcdab3
push id13
push userbsmedberg@mozilla.com
push dateTue, 29 Sep 2009 17:12:47 +0000
More schema-stuff.
warning-ui/build.html
warning-ui/dbschema.sql
warning-ui/ui.py
warningstep.py
--- a/warning-ui/build.html
+++ b/warning-ui/build.html
@@ -15,37 +15,34 @@
     </div>
     <div class="h2block" py:if="previd is not None and prevrev != rev">
       <h2>Changes since <a href="/build?id=${previd}">last build</a>:</h2>
       <a href="http://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=${prevrev}&amp;tochange=${rev}">Log</a>
     </div>
     <div class="h2block">
       <h2>Unique warnings:</h2> ${unique}
     </div>
-    <div class="h2block">
-      <h2>Total warnings:</h2> ${total}
-    </div>
 
     <py:if test="previd is not None">
       <div py:if="len(newwarnings)">
         <h3>New:</h3>
 
         <ul>
-          <li py:for="signature, file, lineno, msg in newwarnings">
-            <a href="${genlink('warning', signature=signature)}">${file}:${lineno} - ${msg}</a>
+          <li py:for="id, file, lineno, msg in newwarnings">
+            <a href="${genlink('warning', id=id)}">${file}:${lineno} - ${msg}</a>
           </li>
         </ul>
       </div>
 
       <div py:if="len(fixedwarnings)">
         <h3>Fixed:</h3>
 
         <ul>
-          <li py:for="signature, file, lineno, msg in fixedwarnings">
-            <a href="${genlink('warning', signature=signature)}">${file}:${lineno} - ${msg}</a>
+          <li py:for="id, file, lineno, msg in fixedwarnings">
+            <a href="${genlink('warning', id=id)}">${file}:${lineno} - ${msg}</a>
           </li>
         </ul>
       </div>
     </py:if>
 
     <h2>Search warnings:</h2>
 
     ${searchform(id, '', '', 'unused variable%')}
new file mode 100644
--- /dev/null
+++ b/warning-ui/dbschema.sql
@@ -0,0 +1,277 @@
+DROP TABLE IF EXISTS buildwarninglines, buildwarnings, builds, warninglines, warnings, locations, messages;
+DROP PROCEDURE IF EXISTS insertbuild;
+
+CREATE TABLE locations (
+  locationid INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
+  ltype VARCHAR(10) NOT NULL, -- 'cvs', 'hg', 'system' (will not have directory) or 'build'
+  lfile VARCHAR(255) NOT NULL,
+  lline INTEGER NOT NULL, -- use -1 for unknown
+  lrev VARCHAR(40) NOT NULL,
+  lwho VARCHAR(400) NOT NULL,
+  CONSTRAINT location_uniq UNIQUE (ltype, lfile, lline, lrev)
+) ENGINE=InnoDB;
+CREATE INDEX idx_locations_lfile
+  ON locations (lfile);
+INSERT INTO locations
+  (locationid, ltype, lfile, lline, lrev, lwho)
+VALUES
+  (-1,     'invalid', '',    -1,    '',   '');
+
+CREATE TABLE messages (
+  msgid INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
+  msg VARCHAR(255) NOT NULL UNIQUE
+) ENGINE=InnoDB;
+INSERT INTO messages
+  (msgid, msg)
+VALUES
+  (-1, 'warningdb sentinel message');
+
+CREATE TABLE warnings (
+  warningid INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
+  linecount TINYINT NOT NULL,
+  primaryline TINYINT NOT NULL,
+  l0 INTEGER NOT NULL DEFAULT -1 REFERENCES locations (locationid),
+  msg0 INTEGER NOT NULL DEFAULT -1 REFERENCES messages (msgid),
+  l1 INTEGER NOT NULL DEFAULT -1 REFERENCES locations (locationid),
+  msg1 INTEGER NOT NULL DEFAULT -1 REFERENCES messages (msgid),
+  l2 INTEGER NOT NULL DEFAULT -1 REFERENCES locations (locationid),
+  msg2 INTEGER NOT NULL DEFAULT -1 REFERENCES messages (msgid),
+  l3 INTEGER NOT NULL DEFAULT -1 REFERENCES locations (locationid),
+  msg3 INTEGER NOT NULL DEFAULT -1 REFERENCES messages (msgid),
+  l4 INTEGER NOT NULL DEFAULT -1 REFERENCES locations (locationid),
+  msg4 INTEGER NOT NULL DEFAULT -1 REFERENCES messages (msgid),
+  l5 INTEGER NOT NULL DEFAULT -1 REFERENCES locations (locationid),
+  msg5 INTEGER NOT NULL DEFAULT -1 REFERENCES messages (msgid),
+  l6 INTEGER NOT NULL DEFAULT -1 REFERENCES locations (locationid),
+  msg6 INTEGER NOT NULL DEFAULT -1 REFERENCES messages (msgid),
+  l7 INTEGER NOT NULL DEFAULT -1 REFERENCES locations (locationid),
+  msg7 INTEGER NOT NULL DEFAULT -1 REFERENCES messages (msgid),
+  CONSTRAINT warnings_data_uniq UNIQUE (l0, msg0, l1, msg1, l2, msg2, l3, msg3, l4, msg4, l5, msg5, l6, msg6, l7, msg7)
+) ENGINE=InnoDB;
+
+CREATE OR REPLACE
+ALGORITHM = MERGE
+VIEW v_warninglines_unjoined
+AS
+  SELECT warningid, l0 AS locationid, msg0 as msgid, 0 AS wline
+  FROM warnings
+  UNION SELECT warningid, l1 AS locationid, msg1 as msgid, 1 AS wline
+  FROM warnings
+  WHERE linecount > 1
+  UNION SELECT warningid, l2 AS locationid, msg2 as msgid, 2 AS wline
+  FROM warnings
+  WHERE linecount > 2
+  UNION SELECT warningid, l3 AS locationid, msg3 as msgid, 3 AS wline
+  FROM warnings
+  WHERE linecount > 3
+  UNION SELECT warningid, l4 AS locationid, msg4 as msgid, 4 AS wline
+  FROM warnings
+  WHERE linecount > 4
+  UNION SELECT warningid, l5 AS locationid, msg5 as msgid, 5 AS wline
+  FROM warnings
+  WHERE linecount > 5
+  UNION SELECT warningid, l6 AS locationid, msg6 as msgid, 6 AS wline
+  FROM warnings
+  WHERE linecount > 6
+  UNION SELECT warningid, l7 AS locationid, msg7 as msgid, 7 AS wline
+  FROM warnings
+  WHERE linecount > 7;
+
+CREATE OR REPLACE
+ALGORITHM = MERGE
+VIEW v_warninglines
+AS
+  SELECT warningid, ltype, lfile, lline, lrev, lwho, msg, wline
+  FROM v_warninglines_unjoined, locations, messages
+  WHERE locations.locationid = v_warninglines_unjoined.locationid
+    AND messages.msgid = v_warninglines_unjoined.msgid;
+
+CREATE OR REPLACE
+ALGORITHM = MERGE
+VIEW v_warningline_primary
+  AS
+    SELECT v_warninglines.warningid AS warningid, ltype, lfile, lline, lrev, lwho, msg
+    FROM v_warninglines, warnings
+    WHERE v_warninglines.warningid = warnings.warningid
+      AND v_warninglines.wline = warnings.primaryline;
+
+CREATE TABLE builds (
+  buildnumber INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
+  rev VARCHAR(40) NOT NULL
+) ENGINE=InnoDB;
+
+CREATE TABLE buildwarnings (
+  buildnumber INTEGER NOT NULL,
+  CONSTRAINT buildwarnings_buildnumber_fkey FOREIGN KEY (buildnumber)
+    REFERENCES builds (buildnumber),
+  warningid INTEGER NOT NULL,
+  CONSTRAINT buildwarnings_pkey PRIMARY KEY (buildnumber, warningid),
+  wcount INTEGER NOT NULL DEFAULT 1
+) ENGINE=InnoDB;
+CREATE INDEX idx_buildwarnings_warningid
+  ON buildwarnings (warningid);
+
+CREATE TABLE buildwarninglines (
+  buildnumber INTEGER NOT NULL,
+  warningid INTEGER NOT NULL,
+  CONSTRAINT buildwarninglines_warningid_fkey FOREIGN KEY (buildnumber, warningid)
+    REFERENCES buildwarnings (buildnumber, warningid),
+  wline INTEGER NOT NULL,
+  CONSTRAINT buildwarninglines_pkey PRIMARY KEY (buildnumber, warningid, wline),
+  locationid INTEGER NOT NULL REFERENCES locations (locationid)
+) ENGINE=InnoDB;
+
+CREATE VIEW v_buildwarninglines
+AS
+  SELECT buildnumber, warningid, wline, ltype, lfile, lline, lrev, lwho
+  FROM buildwarninglines, locations
+  WHERE locations.locationid = buildwarninglines.locationid;
+
+CREATE VIEW v_buildwarningline_primary
+AS
+  SELECT buildnumber, warningid, ltype, lfile, lline, lrev, lwho
+  FROM v_buildwarninglines, buildwarnings
+  WHERE buildwarnings.warningid = v_buildwarninglines.warningid
+    AND v_buildwarninglines.wline = buidlwarnings.primaryline;
+
+delimiter /;
+CREATE PROCEDURE insertbuild(buildnumber INTEGER, rev VARCHAR(40))
+MODIFIES SQL DATA
+BEGIN
+  -- Takes a temporary table #BUILDDATA and inserts the data in all the right places
+  -- Schema of #BUILDDATA should be id, ord, file, line, msg,
+  --   blametype, blamewho, blamefile, blamerev, blameline, loc, blameloc, msgid
+
+  DECLARE done INT DEFAULT 0;
+  DECLARE _curid, _curord, _id, _ord, _msgid, _warningid INTEGER;
+  DECLARE _blametype VARCHAR(10);
+  DECLARE _file, _blamefile VARCHAR(255);
+  DECLARE _line, _blameline INTEGER;
+  DECLARE _loc, _blameloc INTEGER;
+  DECLARE _blamerev VARCHAR(40);
+  DECLARE _blamewho VARCHAR(400);
+  DECLARE _msg VARCHAR(255);
+  DECLARE _loc0, _msg0, _loc1, _msg1, _loc2, _msg2, _loc3, _msg3, _loc4, _msg4, _loc5, _msg5, _loc6, _msg6, _loc7, _msg7 INTEGER DEFAULT -1;
+
+  DECLARE readlocs CURSOR FOR
+    SELECT id, ord, file, line, msg, blametype, blamewho, blamefile, blamerev, blameline
+    FROM _BUILDDATA
+    ORDER BY id, ord;
+  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
+
+  CREATE TEMPORARY TABLE IF NOT EXISTS _BUILDWARNINGS (
+    warningid INTEGER NOT NULL PRIMARY KEY,
+    id INTEGER NOT NULL,
+    wcount INTEGER NOT NULL DEFAULT 1
+  );
+  TRUNCATE _BUILDWARNINGS;
+  CREATE TEMPORARY TABLE IF NOT EXISTS _BUILDWARNINGLINES (
+    id INTEGER NOT NULL,
+    wline INTEGER NOT NULL,
+    CONSTRAINT PRIMARY KEY (id, wline),
+    locationid INTEGER NOT NULL
+  );
+  TRUNCATE _BUILDWARNINGLINES;
+
+  OPEN readlocs;
+  readloop: LOOP
+    FETCH readlocs
+      INTO _id, _ord, _file, _line, _msg, _blametype, _blamewho, _blamefile, _blamerev, _blameline;
+
+    IF done OR (_curid is not NULL and _curid != _id) THEN
+      INSERT INTO warnings (linecount, primaryline,
+          l0, msg0, l1, msg1, l2, msg2, l3, msg3,
+          l4, msg4, l5, msg5, l6, msg6, l7, msg7)
+        VALUES (_curord + 1, 0,
+          _loc0, _msg0, _loc1, _msg1, _loc2, _msg2, _loc3, _msg3,
+          _loc4, _msg4, _loc5, _msg5, _loc6, _msg6, _loc7, _msg7)
+        ON DUPLICATE KEY UPDATE warningid = LAST_INSERT_ID(warningid);
+      SET _warningid = LAST_INSERT_ID();
+
+      INSERT INTO _BUILDWARNINGS (id, warningid) VALUES (_curid, _warningid)
+        ON DUPLICATE KEY UPDATE wcount = wcount + 1;
+
+      SET _loc0 = -1, _msg0 = -1,
+          _loc1 = -1, _msg1 = -1,
+          _loc2 = -1, _msg2 = -1,
+          _loc3 = -1, _msg3 = -1,
+          _loc4 = -1, _msg4 = -1,
+          _loc5 = -1, _msg5 = -1,
+          _loc6 = -1, _msg6 = -1,
+          _loc7 = -1, _msg7 = -1,
+          _curord = NULL;
+    END IF;
+
+    IF done THEN LEAVE readloop; END IF;
+
+    SET _curid = _id;
+    IF _curord IS NOT NULL AND _curord != _ord - 1 THEN
+      PREPARE throw_err FROM 'CALL incoming_data_error, ord were not sequential';
+      EXECUTE throw_err;
+    END IF;
+    SET _curord = _ord;
+
+    IF _blametype IS NULL THEN
+      SET _blametype = 'system';
+    END IF;
+    IF _line IS NULL THEN
+      SET _line = -1;
+    END IF;
+    IF _blamewho IS NULL THEN
+      SET _blamewho = '';
+    END IF;
+    IF _blamefile IS NULL THEN
+      SET _blamefile = SUBSTRING_INDEX(_file, '/', -1);
+    END IF;
+    IF _blamerev IS NULL THEN
+      SET _blamerev = '';
+    END IF;
+    IF _blameline IS NULL THEN
+      SET _blameline = _line;
+    END IF;
+
+    INSERT INTO locations (ltype, lfile, lline, lrev, lwho)
+      VALUES (_blametype, _blamefile, _blameline, _blamerev, _blamewho)
+      ON DUPLICATE KEY UPDATE locationid = LAST_INSERT_ID(locationid);
+    SET _blameloc = LAST_INSERT_ID();
+    COMMIT;
+
+    INSERT INTO locations(ltype, lfile, lline, lrev, lwho)
+      VALUES ('build', _file, _line, '', '')
+      ON DUPLICATE KEY UPDATE locationid = LAST_INSERT_ID(locationid);
+    SET _loc = LAST_INSERT_ID();
+    COMMIT;
+
+    INSERT INTO messages (msg)
+      VALUES (_msg)
+      ON DUPLICATE KEY UPDATE msgid = LAST_INSERT_ID(msgid);
+    SET _msgid = LAST_INSERT_ID();
+    COMMIT;
+
+    INSERT INTO _BUILDWARNINGLINES (id, wline, locationid)
+      VALUES (_curid, _curord, _loc);
+
+    CASE _curord
+      WHEN 0 THEN SET _loc0 = _blameloc, _msg0 = _msgid;
+      WHEN 1 THEN SET _loc1 = _blameloc, _msg1 = _msgid;
+      WHEN 2 THEN SET _loc2 = _blameloc, _msg2 = _msgid;
+      WHEN 3 THEN SET _loc3 = _blameloc, _msg3 = _msgid;
+      WHEN 4 THEN SET _loc4 = _blameloc, _msg4 = _msgid;
+      WHEN 5 THEN SET _loc5 = _blameloc, _msg5 = _msgid;
+      WHEN 6 THEN SET _loc6 = _blameloc, _msg6 = _msgid;
+      WHEN 7 THEN SET _loc7 = _blameloc, _msg7 = _msgid;
+    END CASE;
+  END LOOP;
+
+  START TRANSACTION;
+  INSERT INTO builds(buildnumber, rev) VALUES (buildnumber, rev);
+  INSERT INTO buildwarnings(buildnumber, warningid, wcount)
+    SELECT buildnumber, warningid, wcount
+    FROM _BUILDWARNINGS;
+  INSERT INTO buildwarninglines(buildnumber, warningid, wline, locationid)
+    SELECT buildnumber, warningid, wline, locationid
+    FROM _BUILDWARNINGS, _BUILDWARNINGLINES
+    WHERE _BUILDWARNINGS.id = _BUILDWARNINGLINES.id;
+  COMMIT;
+END
+/;
--- a/warning-ui/ui.py
+++ b/warning-ui/ui.py
@@ -1,34 +1,32 @@
-import cherrypy, os, sys, re, sqlite3
+import cherrypy, os, sys, re, MySQLdb
 from genshi.template import TemplateLoader
 
 class Root(object):
     def __init__(self):
         self.loader = TemplateLoader(cherrypy.config['tools.staticdir.root'],
                                      auto_reload=True)
 
     def dbcursor(self):
-        return sqlite3.connect(cherrypy.request.app.config['database']['path']).cursor()
+        return MySQLdb.connect(**cherrypy.request.app.config['database']).cursor()
 
     def render(self, tmpl, **kwargs):
         cherrypy.response.headers['Content-Type'] = 'application/xhtml+xml'
         tmpl = self.loader.load(tmpl)
         return tmpl.generate(**kwargs).render('xhtml', doctype='xhtml')
 
     @cherrypy.expose
     def index(self):
         try:
             cur = self.dbcursor()
-            cur.execute('''SELECT builds.buildnumber, builds.rev, uwarnings.ucount
-                           FROM builds LEFT OUTER JOIN
-                             (SELECT buildnumber, count(*) AS ucount
-                              FROM warnings
-                              GROUP BY buildnumber) AS uwarnings
-                           ON builds.buildnumber = uwarnings.buildnumber
+            cur.execute('''SELECT builds.buildnumber, builds.rev, count(*)
+                           FROM builds, buildwarnings
+                           WHERE builds.buildnumber = buildwarnings.buildnumber
+                           GROUP BY builds.buildnumber
                            ORDER BY builds.buildnumber DESC LIMIT 100''')
             return self.render('index.html', builds=cur.fetchall())
         finally:
             cur.close()
 
     @cherrypy.expose
     def robots_txt(self):
         cherrypy.response.headers['Content-Type'] = 'text/plain'
@@ -128,114 +126,127 @@ Disallow: /"""
 
     @cherrypy.expose
     def build(self, id):
         id = int(id)
         try:
             cur = self.dbcursor()
             cur.execute('''SELECT rev
                            FROM builds
-                           WHERE buildnumber = ?''', (id,))
+                           WHERE buildnumber = %s''', (id,))
             rev, = cur.fetchone()
 
-            cur.execute('''SELECT count(*) as ucount, sum(count) AS tcount
-                           FROM warnings
-                           WHERE buildnumber = ?''', (id,))
-            unique, total = cur.fetchone()
+            cur.execute('''SELECT count(*) as ucount
+                           FROM buildwarnings
+                           WHERE buildnumber = %s''', (id,))
+            unique, = cur.fetchone()
 
             cur.execute('''SELECT buildnumber, rev
                            FROM builds
                            WHERE
-                             buildnumber < ?
+                             buildnumber < %s
                            ORDER BY buildnumber DESC
                            LIMIT 1''', (id,))
             prev = cur.fetchone()
             if prev is None:
                 previd = None
                 prevrev = None
                 newwarnings = None
                 fixedwarnings = None
             else:
                 previd, prevrev = prev
 
-                cur.execute('''SELECT warnings.signature, file, lineno, msg
-                               FROM warnings, wlines
+                cur.execute('''SELECT buildwarnings.warningid,
+                                      locations.lfile, locations.lline, messages.msg
+                               FROM buildwarnings, buildwarninglines, locations,
+                                    warnings, messages
                                WHERE
-                                 warnings.buildnumber = wlines.buildnumber AND
-                                 warnings.signature = wlines.signature AND
-                                 wlines.wline = 0 AND
-                                 warnings.buildnumber = ? AND
-                                 NOT EXISTS (SELECT *
-                                             FROM warnings AS oldwarnings
-                                             WHERE oldwarnings.signature = warnings.signature AND
-                                             oldwarnings.buildnumber = ?)''', (id, previd))
+                                 buildwarnings.buildnumber = %s
+                                 AND NOT EXISTS (
+                                   SELECT *
+                                   FROM buildwarnings AS oldwarnings
+                                   WHERE oldwarnings.warningid = warnings.warningid
+                                     AND oldwarnings.buildnumber = %s)
+                                 AND buildwarninglines.buildnumber = buildwarnings.buildnumber
+                                 AND buildwarninglines.warningid = buildwarnings.warningid
+                                 AND buildwarninglines.wline = 0
+                                 AND locations.locationid = buildwarninglines.locationid
+                                 AND warnings.warningid = buildwarnings.warningid
+                                 AND messages.msgid = warnings.msg0''',
+                            (id, previd))
                 newwarnings = cur.fetchall()
 
-                cur.execute('''SELECT warnings.signature, file, lineno, msg
-                               FROM warnings, wlines
+                cur.execute('''SELECT buildwarnings.warningid,
+                                      locations.lfile, locations.lline, messages.msg
+                               FROM buildwarnings, buildwarninglines, locations,
+                                    warnings, messages
                                WHERE
-                                 warnings.buildnumber = wlines.buildnumber AND
-                                 warnings.signature = wlines.signature AND
-                                 wlines.wline = 0 AND
-                                 warnings.buildnumber = ? AND
-                                 NOT EXISTS (SELECT *
-                                             FROM warnings AS oldwarnings
-                                             WHERE oldwarnings.signature = warnings.signature AND
-                                             oldwarnings.buildnumber = ?)''', (previd, id))
+                                 buildwarnings.buildnumber = %s
+                                 AND NOT EXISTS (
+                                   SELECT *
+                                   FROM buildwarnings AS oldwarnings
+                                   WHERE oldwarnings.warningid = warnings.warningid
+                                     AND oldwarnings.buildnumber = %s)
+                                 AND buildwarninglines.buildnumber = buildwarnings.buildnumber
+                                 AND buildwarninglines.warningid = buildwarnings.warningid
+                                 AND buildwarninglines.wline = 0
+                                 AND locations.locationid = buildwarninglines.locationid
+                                 AND warnings.warningid = buildwarnings.warningid
+                                 AND messages.msgid = warnings.msg0''',
+                            (previd, id))
                 fixedwarnings = cur.fetchall()
-                
+
             return self.render('build.html',
                                rev=rev,
                                id=id,
                                unique=unique,
-                               total=total,
                                previd=previd,
                                prevrev=prevrev,
                                newwarnings=newwarnings,
                                fixedwarnings=fixedwarnings)
         finally:
             cur.close()
 
     @cherrypy.expose
-    def warning(self, signature):
+    def warning(self, id):
         try:
             cur = self.dbcursor()
-            cur.execute('''SELECT buildnumber, rev
-                           FROM builds
+            cur.execute('''SELECT wmin.buildnumber, rev
+                           FROM builds,
+                             (
+                                SELECT MIN(buildnumber) AS buildnumber
+                                FROM buildwarnings
+                                WHERE warningid = %s
+                             ) AS wmin
                            WHERE
-                             EXISTS (SELECT *
-                                     FROM warnings
-                                     WHERE
-                                       warnings.buildnumber = builds.buildnumber AND
-                                       warnings.signature = ?)
-                           ORDER BY buildnumber ASC LIMIT 1''', (signature,))
-
+                             builds.buildnumber = wmin.buildnumber''', (id,))
             firstid, firstrev = cur.fetchone()
-            
-            cur.execute('''SELECT buildnumber, rev
-                           FROM builds
+
+            cur.execute('''SELECT wmax.buildnumber, rev
+                           FROM builds,
+                             (
+                                SELECT MAX(buildnumber) AS buildnumber
+                                FROM buildwarnings
+                                WHERE warningid = %s
+                             ) AS wmax
                            WHERE
-                             NOT EXISTS (SELECT *
-                                         FROM warnings
-                                         WHERE
-                                           warnings.buildnumber = builds.buildnumber AND
-                                           warnings.signature = ?) AND
-                             buildnumber > ?
-                           ORDER BY buildnumber ASC LIMIT 1''', (signature, firstid))
-
-            lastid, lastrev = cur.fetchone() or (None, None)
+                             builds.buildnumber = wmax.buildnumber''', (id,))
+            lastid, lastrev = cur.fetchone()
 
             cur.execute('''SELECT file, lineno, msg, blametype, blamefile, blamerev, blameline, blamewho
                            FROM wlines
                            WHERE
                              buildnumber = ? AND
                              signature = ?
                            ORDER BY wline ASC''', (firstid, signature))
+            wlines = cur.fetchall()
 
-            wlines = cur.fetchall()
+            if lastid == firstid:
+                lastid = None
+                lastrev = None
 
             return self.render('warning.html',
                                firstid=firstid, firstrev=firstrev,
                                lastid=lastid, lastrev=lastrev,
                                wlines=wlines)
         finally:
             cur.close()
 
--- a/warningstep.py
+++ b/warningstep.py
@@ -1,84 +1,12 @@
 from buildbot.process.buildstep import SUCCESS, FAILURE
 from buildbot.steps.shell import ShellCommand
 import MySQLdb, os.path, sys
 
-class Location(object):
-    def __init__(self, type, file, lineno, rev, who):
-        self.type = type
-        self.file = file
-        self.lineno = lineno
-        self.rev = rev
-        self.who = who
-
-    def getLocationID(self, db, cur):
-        cur.execute('''INSERT INTO locations (ltype, lfile, lline, lrev, lwho)
-                       VALUES (%s, %s, %s, %s, %s)
-                       ON DUPLICATE KEY UPDATE locationid = LAST_INSERT_ID(locationid)''',
-                    (self.type, self.file, self.lineno, self.rev, self.who))
-        print "locationid: %i" % (db.insert_id(),)
-        return db.insert_id()
-
-class WarningLine(object):
-    def __init__(self, data):
-        file, lineno, msg, blametype, blamewho, blamefile, blamerev, blameline = data
-
-        if blametype is None:
-            blametype = 'system'
-        if lineno is None:
-            lineno = -1
-        if blamewho is None:
-            blamewho = ''
-        if blamefile is None:
-            blamefile = file.rsplit('/', 1)[-1]
-        if blameline is None:
-            blameline = lineno
-        if blamerev is None:
-            blamerev = ''
-
-        self.blame = Location(blametype, blamefile, blameline, blamerev, blamewho)
-        self.loc = Location('build', file, lineno, '', '')
-        self.msg = msg
-
-    def getMessageID(self, db, cur):
-        cur.execute('''INSERT INTO messages (msg) VALUES (%s)
-                       ON DUPLICATE KEY UPDATE msgid = LAST_INSERT_ID(msgid)''',
-                    (self.msg,))
-        print "msgid: %i" % (db.insert_id(),)
-        return db.insert_id()
-
-class Warning(object):
-    def __init__(self):
-        self.lines = []
-
-    def append(self, wl):
-        self.lines.append(wl)
-
-    def findWarningID(self, db, cur):
-        cols = 'linecount, primaryline, ' + ','.join(['l%i, msg%i' % (i, i) for i in range(0, len(self.lines))])
-        values = '%s, 0, ' + ','.join(['%s, %s' for i in range(0, len(self.lines))])
-        p = [len(self.lines)]
-        for i in range(0, len(self.lines)):
-            p += [self.lines[i].blame.getLocationID(db, cur), self.lines[i].getMessageID(db, cur)]
-
-        q = '''INSERT INTO warnings (%s) VALUES (%s)
-               ON DUPLICATE KEY UPDATE warningid = LAST_INSERT_ID(warningid)''' % (cols, values)
-        cur.execute(q, p)
-        print "warningid: %i" % (db.insert_id(),)
-        return db.insert_id()
-
-    def insertIntoBuild(self, buildnumber, warningid, db, cur):
-        cur.execute('''INSERT INTO buildwarnings (buildnumber, warningid) VALUES (%s, %s)
-                       ON DUPLICATE KEY UPDATE wcount = wcount + 1''', (buildnumber, warningid))
-        if cur.rowcount == 1: # New row, not DUPLICATE KEY
-            p = [[buildnumber, warningid, i, self.lines[i].loc.getLocationID(db, cur)] for i in range(0, len(self.lines))]
-            cur.executemany('''INSERT INTO buildwarninglines (buildnumber, warningid, wline, locationid)
-                               VALUES (%s, %s, %s, %s)''', p)
-
 class WarningParserStep(ShellCommand):
     """
     A subclass of shellcommand used for running warning-parser.py.
 
     This parses the output and saves interesting information to the
     master-side sqlite database. It produces output about "new" warnings, etc.
 
     If the dbfile does not exist, it will be created the first time it is needed.
@@ -97,77 +25,77 @@ class WarningParserStep(ShellCommand):
             if result == FAILURE:
                 self.addCompleteLog('skipped', 'Skipped analyzing warnings because a prior step failed.')
                 return
 
         comparison = self.parseLog(log.getText(), self.getProperty('buildnumber'), self.getProperty('got_revision'))
         self.addCompleteLog('comparison', '\n'.join(comparison))
 
     def parseLog(self, logdata, buildnumber, revision):
-        # Use different connections for the warningID-finder (which does table locking and can't have a long-lived transaction),
-        # and the buildwarnings inserter, which should have a long-lived transaction
-        warndb = MySQLdb.connect(**self.connectargs)
-        warndb.autocommit(True)
-        warncur = warndb.cursor()
-
         db = MySQLdb.connect(**self.connectargs)
         dbcur = db.cursor()
-
-        dbcur.execute('INSERT INTO builds (buildnumber, rev) VALUES ( %s, %s )',
-                      (buildnumber, revision))
-
-        def processWarning(w):
-            warningid = w.findWarningID(warndb, warncur)
-            w.insertIntoBuild(buildnumber, warningid, db, dbcur)
+        dbcur.arraysize = 200
+        dbcur.execute('''CREATE TEMPORARY TABLE _BUILDDATA (
+                           id INTEGER NOT NULL,
+                           ord INTEGER NOT NULL,
+                           CONSTRAINT PRIMARY KEY (id, ord),
+                           file VARCHAR(255) NOT NULL,
+                           line INTEGER,
+                           msg VARCHAR(255) NOT NULL,
+                           blametype VARCHAR(10),
+                           blamewho VARCHAR(400),
+                           blamefile VARCHAR(255),
+                           blamerev VARCHAR(40),
+                           blameline INTEGER
+                         )''')
 
-        curwarning = None
-        for line in logdata.split("\n"):
-            if line.startswith('WARN-DB: '):
-                data = eval(line[9:].strip())
-                id, ord = data[0:2]
-                wl = WarningLine(data[2:])
+        def getmany():
+            i = 0
+            for line in logdata.split('\n'):
+                if line.startswith('WARN-DB: '):
+                    i += 1
+                    d = eval(line[9:].strip())
+                    print repr(d)
+                    yield d
 
-                if ord == 0:
-                    if curwarning is not None:
-                        print "processing #%s" % id
-                        processWarning(curwarning)
-                    curwarning = Warning()
+        dbcur.executemany('''INSERT INTO _BUILDDATA (id, ord, file, line, msg, blametype, blamewho, blamefile, blamerev, blameline) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)''',
+                          [eval(line[9:].strip())
+                           for line in logdata.split('\n')
+                           if line.startswith('WARN-DB: ')])
 
-                curwarning.append(wl)
+        dbcur.callproc('insertbuild', (buildnumber, revision))
 
-        if curwarning is not None:
-            processWarning(curwarning)
-
-        db.commit()
+        # dbcur.execute('INSERT INTO builds (buildnumber, rev) VALUES ( %s, %s )',
+        #               (buildnumber, revision))
 
         # A list of line to stick in the comparison log
         comparison = []
 
         dbcur.execute('''SELECT COUNT(*) as unqiue, SUM(wcount) AS total
                        FROM buildwarnings
                        WHERE buildnumber = %s''', (buildnumber,))
-        unique, total = cur.fetchone()
+        unique, total = dbcur.fetchone()
         comparison.append('TinderboxPrint:<a href="http://office.smedbergs.us:8080/build?id=%i">warn:%i</a>' % (buildnumber, unique))
         comparison.append('TinderboxPrint:(%s total)' % (total))
 
         dbcur.execute('SELECT MAX(buildnumber) FROM builds WHERE buildnumber < %s', (buildnumber,))
         r = dbcur.fetchone()
         if r is None:
             comparison.append('no prior build found: skipping comparison')
         else:
             prevbuildnumber, = r
 
-            cbcur.execute('''SELECT COUNT(*)
+            dbcur.execute('''SELECT COUNT(*)
                              FROM buildwarnings
                              WHERE buildnumber = %s AND
                                NOT EXISTS (SELECT *
                                            FROM buildwarnings AS oldwarnings
-                                           WHERE oldwarnings.warningid = warnings.warningid AND
+                                           WHERE oldwarnings.warningid = buildwarnings.warningid AND
                                              oldwarnings.buildnumber = %s)''', (buildnumber, prevbuildnumber))
-            wnewcount, = cur.fetchone()
+            wnewcount, = dbcur.fetchone()
             if wnewcount > 0:
                 comparison.append('TinderboxPrint:<a href="http://office.smedbergs.us:8080/build?id=%i">warn-new:%i</a>' % (buildnumber, wnewcount))
 
         dbcur.close()
         db.close()
 
         return comparison