X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Fmail%2FSqliteMailbox.java;h=384dc4d73d76c65de78e7300c7f1f1a2b07bf99c;hb=f110e29a9029e8f12957edfcda0330a95c6814ee;hp=0d8053294af772a9514996a44ac09c21ed7a92fb;hpb=eb54d55cb9ae403a6824687b44b2ee2fafb8e7c5;p=org.ibex.mail.git diff --git a/src/org/ibex/mail/SqliteMailbox.java b/src/org/ibex/mail/SqliteMailbox.java index 0d80532..384dc4d 100644 --- a/src/org/ibex/mail/SqliteMailbox.java +++ b/src/org/ibex/mail/SqliteMailbox.java @@ -54,6 +54,7 @@ public class SqliteMailbox extends Mailbox.Default implements MailTree { private final File file; public int uidValidity() { return uidValidity; } + public String toString() { return file.getName(); } public SqliteMailbox(String filename) throws SQLException { try { this.file = new File(filename); @@ -77,17 +78,60 @@ public class SqliteMailbox extends Mailbox.Default implements MailTree { catch (ClassNotFoundException e) { throw new RuntimeException(e); } } + private HashMap imapToUid = new HashMap(); + private HashMap uidToImap = new HashMap(); + private boolean imapNumberCacheValid = false; + public void updateImapNumberCache() throws SQLException { + synchronized(this) { + Log.warn(this+"", "rebuilding imapNumberCache..."); + imapToUid.clear(); + uidToImap.clear(); + PreparedStatement q = conn.prepareStatement("select uid_ from mail"); + ResultSet rs = q.executeQuery(); + int num = 1; + while(rs.next()) { + imapToUid.put(num, rs.getInt(1)); + uidToImap.put(rs.getInt(1), num); + num++; + } + imapNumberCacheValid = true; + } + } + public int queryImapNumberCache(int uid) throws SQLException { + synchronized(this) { + if (!imapNumberCacheValid) updateImapNumberCache(); + Integer ret = uidToImap.get(uid); + if (ret == null) return -1; + return ret; + } + } + public int queryUidForImapNum(int imapNumber) throws SQLException { + synchronized(this) { + if (!imapNumberCacheValid) updateImapNumberCache(); + Integer ret = imapToUid.get(imapNumber); + if (ret == null) return -1; + return ret; + } + } + + + public int maxuid() { return uidNext(); } public int uidNext() { try { PreparedStatement q = conn.prepareStatement("select max(uid_) from mail"); ResultSet rs = q.executeQuery(); - if (!rs.next()) return -1; + //if (!rs.next()) return -1; + if (!rs.next()) throw new RuntimeException("select max(uid_) returned no rows!"); return rs.getInt(1)+1; } catch (Exception e) { throw new RuntimeException(e); } } - public Mailbox.Iterator iterator() { return new SqliteJdbcIterator(); } - private static String set(int[] set, String arg) { + public Mailbox.Iterator iterator() { + Log.warn(this, "performance warning: called iterator() on entire mailbox"); + Log.printStackTrace(this, Log.WARN); + return new SqliteJdbcIterator(); + } + private String set(int[] set, String arg) { String whereClause = ""; boolean needsOr = false; for(int i=0; i resorting to superclass: " + q); + throw new UnsupportedQueryException(); + } + set[i] = uid; + } + return getWhereClause(Query.uid(set)); + } catch (SQLException e) { + Log.error(this, e); + Log.info(SqliteMailbox.class, "resorting to superclass: " + q); + throw new UnsupportedQueryException(); + } + } + default: { - Log.info(SqliteMailbox.class, "resorting to superclass: " + q.type); + Log.info(SqliteMailbox.class, "resorting to superclass: " + q); throw new UnsupportedQueryException(); } } @@ -157,6 +226,7 @@ public class SqliteMailbox extends Mailbox.Default implements MailTree { String whereClause = getWhereClause(q); Log.info(this, "whereClause = " + whereClause); try { + Log.warn("SQL", "select count(*) from mail where " + whereClause); ResultSet rs = conn.prepareStatement("select count(*) from mail where " + whereClause).executeQuery(); rs.next(); return rs.getInt(1); @@ -167,17 +237,43 @@ public class SqliteMailbox extends Mailbox.Default implements MailTree { } public void insert(Message m, int flags) { try { + PreparedStatement query = conn.prepareStatement("select headers_,body_,flags_ from 'mail' where messageid_=?"); + query.setString(1, m.messageid); + Log.warn("SQL", "select headers_,body_,flags_ from 'mail' where messageid_="+m.messageid); + ResultSet rs2 = query.executeQuery(); + if (rs2.next()) { + Message m2 = Message.newMessage(Fountain.Util.concat(Fountain.Util.create(rs2.getString(1)), + Fountain.Util.create("\r\n\r\n"), + Fountain.Util.create(rs2.getString(2)))); + StringBuffer s1 = new StringBuffer(); + m.getBody().getStream().transcribe(s1); + StringBuffer s2 = new StringBuffer(); + m2.getBody().getStream().transcribe(s2); + if (!s1.toString().equals(s2.toString())) { + Log.error(this.toString(), + "attempt to insert two messages with identical messageid ("+m.messageid+") but different bodies:\n"+ + " (body length="+s1.length()+") "+m.summary()+"\n"+ + " (body length="+s2.length()+") "+m2.summary()+"\n"); + } else { + Log.warn(this.toString(), + "silently dropping duplicate insert() [messageids and bodies match]: " + m.summary()); + return; + } + } PreparedStatement add = - conn.prepareStatement("insert or replace into 'mail' ("+columns+") values (?,?,?,?,?,?,?,?)"); + conn.prepareStatement("insert "+/*"or replace "+*/"into 'mail' ("+columns+") values (?,?,?,?,?,?,?,?)"); add.setString(1, m.messageid+""); add.setString(2, m.from+""); add.setString(3, m.to+""); add.setString(4, m.date+""); add.setString(5, m.subject+""); - add.setString(6, streamToString(m.headers.getStream())); - add.setString(7, streamToString(m.getBody().getStream())); + add.setString(6, SqliteDB.streamToString(m.headers.getStream())); + add.setString(7, SqliteDB.streamToString(m.getBody().getStream())); add.setInt (8, flags); add.executeUpdate(); + + // FIXME: be smarter here? + imapNumberCacheValid = false; } catch (Exception e) { throw new RuntimeException(e); } } @@ -192,27 +288,42 @@ public class SqliteMailbox extends Mailbox.Default implements MailTree { public SqliteJdbcIterator() { this(""); } public SqliteJdbcIterator(String whereClause) { try { + /* + if (whereClause.equals("")) + Log.warn(this, "performance warning: empty whereClause"); + */ this.whereClause = whereClause; + Log.warn("SQL", "select messageid_,uid_,flags_ from 'mail' "+whereClause); PreparedStatement query = conn.prepareStatement("select messageid_,uid_,flags_ from 'mail' "+whereClause); rs = query.executeQuery(); } catch (Exception e) { throw new RuntimeException(e); } } + public Headers head() { + if (m != null) return m.headers; + try { + PreparedStatement query = conn.prepareStatement("select headers_,flags_ from 'mail' where messageid_=?"); + query.setString(1, rs.getString(1)); + Log.warn("SQL", "select headers_,flags_ from 'mail' where messageid_="+rs.getString(1)); + + ResultSet rs2 = query.executeQuery(); + if (!rs2.next()) { Log.error("XXX", "should not happen"); return null; } + flags = rs2.getInt(2); + return new Headers(Fountain.Util.create(rs2.getString(1))); + } catch (Exception e) { throw new RuntimeException(e); } + } public Message cur() { try { if (m!=null) return m; PreparedStatement query = conn.prepareStatement("select headers_,body_,flags_ from 'mail' where messageid_=?"); query.setString(1, rs.getString(1)); + Log.warn("SQL", "select headers_,body_,flags_ from 'mail' where messageid_="+rs.getString(1)); ResultSet rs2 = query.executeQuery(); - if (!rs2.next()) { - Log.error("XXX", "should not happen"); - return null; - } + if (!rs2.next()) { Log.error("XXX", "should not happen"); return null; } m = Message.newMessage(Fountain.Util.concat(Fountain.Util.create(rs2.getString(1)), Fountain.Util.create("\r\n\r\n"), Fountain.Util.create(rs2.getString(2)))); flags = rs2.getInt(3); - return m; } catch (Exception e) { throw new RuntimeException(e); } } @@ -224,13 +335,13 @@ public class SqliteMailbox extends Mailbox.Default implements MailTree { int oldflags = rs.getInt("flags_"); if (oldflags==flags) return; Log.info(this, "setflags (old="+oldflags+")" + "update mail set flags_="+(flags)+" where uid_="+uid()+""); + if ((flags & Mailbox.Flag.DELETED) != 0) Log.printStackTrace("deletion", Log.WARN); PreparedStatement update = conn.prepareStatement("update mail set flags_=? where uid_=?"); update.setInt(1, flags); update.setInt(2, uid()); update.executeUpdate(); } catch (Exception e) { throw new RuntimeException(e); } } - public Headers head() { return cur().headers; } public boolean next() { try { m = null; uid = -1; count++; boolean ret = rs.next(); @@ -243,18 +354,21 @@ public class SqliteMailbox extends Mailbox.Default implements MailTree { } public int imapNumber() { if ("".equals(whereClause)) return count; - try { - ResultSet rs = conn.prepareStatement("select count(*) from mail where uid_ <= " + uid()).executeQuery(); - rs.next(); - return rs.getInt(1); - } catch (Exception e) { throw new RuntimeException(e); } + try { return queryImapNumberCache(uid()); } catch (SQLException s) { throw new RuntimeException(s); } } public int nntpNumber() { return uid(); } public void delete() { try { + Log.error("sqlite", "actually deleting message "+uid()+" "+head().get("subject")); + Log.printStackTrace("sqlite", Log.ERROR); + PreparedStatement update = conn.prepareStatement("delete from mail where uid_=?"); update.setInt(1, uid()); update.executeUpdate(); + + // FIXME: be smarter here? + imapNumberCacheValid = false; + } catch (Exception e) { throw new RuntimeException(e); } } }