mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Added message flags: read/unread and starred/unstarred.
This commit is contained in:
@@ -32,6 +32,7 @@ import net.sf.briar.api.transport.ConnectionWindow;
|
|||||||
* <ul>
|
* <ul>
|
||||||
* <li> contact
|
* <li> contact
|
||||||
* <li> message
|
* <li> message
|
||||||
|
* <li> messageFlag
|
||||||
* <li> messageStatus
|
* <li> messageStatus
|
||||||
* <li> rating
|
* <li> rating
|
||||||
* <li> subscription
|
* <li> subscription
|
||||||
@@ -304,6 +305,13 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
Rating getRating(T txn, AuthorId a) throws DbException;
|
Rating getRating(T txn, AuthorId a) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given message has been read.
|
||||||
|
* <p>
|
||||||
|
* Locking: message read, messageFlag read.
|
||||||
|
*/
|
||||||
|
boolean getRead(T txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all remote properties for the given transport.
|
* Returns all remote properties for the given transport.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -346,6 +354,13 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
byte[] getSharedSecret(T txn, ContactId c) throws DbException;
|
byte[] getSharedSecret(T txn, ContactId c) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given message has been starred.
|
||||||
|
* <p>
|
||||||
|
* Locking: message read, messageFlag read.
|
||||||
|
*/
|
||||||
|
boolean getStarred(T txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the groups to which the user subscribes.
|
* Returns the groups to which the user subscribes.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -433,8 +448,8 @@ interface Database<T> {
|
|||||||
/**
|
/**
|
||||||
* Removes a contact (and all associated state) from the database.
|
* Removes a contact (and all associated state) from the database.
|
||||||
* <p>
|
* <p>
|
||||||
* Locking: contact write, message write, messageStatus write,
|
* Locking: contact write, message write, messageFlag write,
|
||||||
* subscription write, transport write.
|
* messageStatus write, subscription write, transport write.
|
||||||
*/
|
*/
|
||||||
void removeContact(T txn, ContactId c) throws DbException;
|
void removeContact(T txn, ContactId c) throws DbException;
|
||||||
|
|
||||||
@@ -450,7 +465,8 @@ interface Database<T> {
|
|||||||
/**
|
/**
|
||||||
* Removes a message (and all associated state) from the database.
|
* Removes a message (and all associated state) from the database.
|
||||||
* <p>
|
* <p>
|
||||||
* Locking: contact read, message write, messageStatus write.
|
* Locking: contact read, message write, messageFlag write,
|
||||||
|
* messageStatus write.
|
||||||
*/
|
*/
|
||||||
void removeMessage(T txn, MessageId m) throws DbException;
|
void removeMessage(T txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
@@ -458,8 +474,8 @@ interface Database<T> {
|
|||||||
* Unsubscribes from the given group. Any messages belonging to the group
|
* Unsubscribes from the given group. Any messages belonging to the group
|
||||||
* are deleted from the database.
|
* are deleted from the database.
|
||||||
* <p>
|
* <p>
|
||||||
* Locking: contact read, message write, messageStatus write,
|
* Locking: contact read, message write, messageFlag write,
|
||||||
* subscription write.
|
* messageStatus write, subscription write.
|
||||||
*/
|
*/
|
||||||
void removeSubscription(T txn, GroupId g) throws DbException;
|
void removeSubscription(T txn, GroupId g) throws DbException;
|
||||||
|
|
||||||
@@ -497,6 +513,14 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
Rating setRating(T txn, AuthorId a, Rating r) throws DbException;
|
Rating setRating(T txn, AuthorId a, Rating r) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the given message read or unread and returns true if it was
|
||||||
|
* previously read.
|
||||||
|
* <p>
|
||||||
|
* Locking: message read, messageFlag write.
|
||||||
|
*/
|
||||||
|
boolean setRead(T txn, MessageId m, boolean read) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the sendability score of the given message.
|
* Sets the sendability score of the given message.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -504,6 +528,14 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
void setSendability(T txn, MessageId m, int sendability) throws DbException;
|
void setSendability(T txn, MessageId m, int sendability) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the given message starred or unstarred and returns true if it was
|
||||||
|
* previously starred.
|
||||||
|
* <p>
|
||||||
|
* Locking: message read, messageFlag write.
|
||||||
|
*/
|
||||||
|
boolean setStarred(T txn, MessageId m, boolean starred) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the status of the given message with respect to the given contact.
|
* Sets the status of the given message with respect to the given contact.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ DatabaseCleaner.Callback {
|
|||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
private final ReentrantReadWriteLock messageLock =
|
private final ReentrantReadWriteLock messageLock =
|
||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
|
private final ReentrantReadWriteLock messageFlagLock =
|
||||||
|
new ReentrantReadWriteLock(true);
|
||||||
private final ReentrantReadWriteLock messageStatusLock =
|
private final ReentrantReadWriteLock messageStatusLock =
|
||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
private final ReentrantReadWriteLock ratingLock =
|
private final ReentrantReadWriteLock ratingLock =
|
||||||
@@ -1137,28 +1139,33 @@ DatabaseCleaner.Callback {
|
|||||||
try {
|
try {
|
||||||
messageLock.writeLock().lock();
|
messageLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
messageStatusLock.writeLock().lock();
|
messageFlagLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
subscriptionLock.writeLock().lock();
|
messageStatusLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
transportLock.writeLock().lock();
|
subscriptionLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
T txn = db.startTransaction();
|
transportLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
db.removeContact(txn, c);
|
T txn = db.startTransaction();
|
||||||
db.commitTransaction(txn);
|
try {
|
||||||
} catch(DbException e) {
|
db.removeContact(txn, c);
|
||||||
db.abortTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
throw e;
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
transportLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
transportLock.writeLock().unlock();
|
subscriptionLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
subscriptionLock.writeLock().unlock();
|
messageStatusLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
messageStatusLock.writeLock().unlock();
|
messageFlagLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
messageLock.writeLock().unlock();
|
messageLock.writeLock().unlock();
|
||||||
@@ -1399,26 +1406,31 @@ DatabaseCleaner.Callback {
|
|||||||
try {
|
try {
|
||||||
messageLock.writeLock().lock();
|
messageLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
messageStatusLock.writeLock().lock();
|
messageFlagLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
subscriptionLock.writeLock().lock();
|
messageStatusLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
T txn = db.startTransaction();
|
subscriptionLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
if(db.containsSubscription(txn, g)) {
|
T txn = db.startTransaction();
|
||||||
affected = db.getVisibility(txn, g);
|
try {
|
||||||
db.removeSubscription(txn, g);
|
if(db.containsSubscription(txn, g)) {
|
||||||
|
affected = db.getVisibility(txn, g);
|
||||||
|
db.removeSubscription(txn, g);
|
||||||
|
}
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
db.commitTransaction(txn);
|
} finally {
|
||||||
} catch(DbException e) {
|
subscriptionLock.writeLock().unlock();
|
||||||
db.abortTransaction(txn);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
subscriptionLock.writeLock().unlock();
|
messageStatusLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
messageStatusLock.writeLock().unlock();
|
messageFlagLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
messageLock.writeLock().unlock();
|
messageLock.writeLock().unlock();
|
||||||
@@ -1457,19 +1469,24 @@ DatabaseCleaner.Callback {
|
|||||||
try {
|
try {
|
||||||
messageLock.writeLock().lock();
|
messageLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
messageStatusLock.writeLock().lock();
|
messageFlagLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
T txn = db.startTransaction();
|
messageStatusLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
old = db.getOldMessages(txn, size);
|
T txn = db.startTransaction();
|
||||||
for(MessageId m : old) removeMessage(txn, m);
|
try {
|
||||||
db.commitTransaction(txn);
|
old = db.getOldMessages(txn, size);
|
||||||
} catch(DbException e) {
|
for(MessageId m : old) removeMessage(txn, m);
|
||||||
db.abortTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
throw e;
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
messageStatusLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
messageStatusLock.writeLock().unlock();
|
messageFlagLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
messageLock.writeLock().unlock();
|
messageLock.writeLock().unlock();
|
||||||
|
|||||||
@@ -225,6 +225,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
|
private static final String CREATE_FLAGS =
|
||||||
|
"CREATE TABLE flags"
|
||||||
|
+ " (messageId HASH NOT NULL,"
|
||||||
|
+ " read BOOLEAN NOT NULL,"
|
||||||
|
+ " starred BOOLEAN NOT NULL,"
|
||||||
|
+ " PRIMARY KEY (messageId),"
|
||||||
|
+ " FOREIGN KEY (messageId) REFERENCES messages (messageId)"
|
||||||
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(JdbcDatabase.class.getName());
|
Logger.getLogger(JdbcDatabase.class.getName());
|
||||||
|
|
||||||
@@ -233,6 +242,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private final ConnectionWindowFactory connectionWindowFactory;
|
private final ConnectionWindowFactory connectionWindowFactory;
|
||||||
private final GroupFactory groupFactory;
|
private final GroupFactory groupFactory;
|
||||||
private final MessageHeaderFactory messageHeaderFactory;
|
private final MessageHeaderFactory messageHeaderFactory;
|
||||||
|
|
||||||
private final LinkedList<Connection> connections =
|
private final LinkedList<Connection> connections =
|
||||||
new LinkedList<Connection>(); // Locking: self
|
new LinkedList<Connection>(); // Locking: self
|
||||||
|
|
||||||
@@ -316,6 +326,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
s.executeUpdate(insertTypeNames(CREATE_CONNECTION_WINDOWS));
|
s.executeUpdate(insertTypeNames(CREATE_CONNECTION_WINDOWS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS));
|
s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS));
|
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS));
|
||||||
|
s.executeUpdate(insertTypeNames(CREATE_FLAGS));
|
||||||
s.close();
|
s.close();
|
||||||
} catch(SQLException e) {
|
} catch(SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s);
|
||||||
@@ -1319,6 +1330,27 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getRead(Connection txn, MessageId m) throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
String sql = "SELECT read FROM flags WHERE messageId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBytes(1, m.getBytes());
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
boolean read = false;
|
||||||
|
if(rs.next()) read = rs.getBoolean(1);
|
||||||
|
if(rs.next()) throw new DbStateException();
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
return read;
|
||||||
|
} catch(SQLException e) {
|
||||||
|
tryToClose(rs);
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<ContactId, TransportProperties> getRemoteProperties(
|
public Map<ContactId, TransportProperties> getRemoteProperties(
|
||||||
Connection txn, TransportId t) throws DbException {
|
Connection txn, TransportId t) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -1513,6 +1545,27 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getStarred(Connection txn, MessageId m) throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
String sql = "SELECT starred FROM flags WHERE messageId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBytes(1, m.getBytes());
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
boolean starred = false;
|
||||||
|
if(rs.next()) starred = rs.getBoolean(1);
|
||||||
|
if(rs.next()) throw new DbStateException();
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
return starred;
|
||||||
|
} catch(SQLException e) {
|
||||||
|
tryToClose(rs);
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<Group> getSubscriptions(Connection txn)
|
public Collection<Group> getSubscriptions(Connection txn)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -2068,7 +2121,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Rating old;
|
Rating old;
|
||||||
if(rs.next()) {
|
if(rs.next()) {
|
||||||
// A rating row exists - update it
|
// A rating row exists - update it if necessary
|
||||||
old = Rating.values()[rs.getByte(1)];
|
old = Rating.values()[rs.getByte(1)];
|
||||||
if(rs.next()) throw new DbStateException();
|
if(rs.next()) throw new DbStateException();
|
||||||
rs.close();
|
rs.close();
|
||||||
@@ -2083,17 +2136,70 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No rating row exists - create one
|
// No rating row exists - create one if necessary
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
old = Rating.UNRATED;
|
old = Rating.UNRATED;
|
||||||
sql = "INSERT INTO ratings (authorId, rating) VALUES (?, ?)";
|
if(!old.equals(r)) {
|
||||||
ps = txn.prepareStatement(sql);
|
sql = "INSERT INTO ratings (authorId, rating)"
|
||||||
ps.setBytes(1, a.getBytes());
|
+ " VALUES (?, ?)";
|
||||||
ps.setShort(2, (short) r.ordinal());
|
ps = txn.prepareStatement(sql);
|
||||||
int affected = ps.executeUpdate();
|
ps.setBytes(1, a.getBytes());
|
||||||
if(affected != 1) throw new DbStateException();
|
ps.setShort(2, (short) r.ordinal());
|
||||||
|
int affected = ps.executeUpdate();
|
||||||
|
if(affected != 1) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
} catch(SQLException e) {
|
||||||
|
tryToClose(rs);
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setRead(Connection txn, MessageId m, boolean read)
|
||||||
|
throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
String sql = "SELECT read FROM flags WHERE messageId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBytes(1, m.getBytes());
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
boolean old;
|
||||||
|
if(rs.next()) {
|
||||||
|
// A flag row exists - update it if necessary
|
||||||
|
old = rs.getBoolean(1);
|
||||||
|
if(rs.next()) throw new DbStateException();
|
||||||
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
|
if(old != read) {
|
||||||
|
sql = "UPDATE flags SET read = ? WHERE messageId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBoolean(1, read);
|
||||||
|
ps.setBytes(2, m.getBytes());
|
||||||
|
int affected = ps.executeUpdate();
|
||||||
|
if(affected != 1) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No flag row exists - create one if necessary
|
||||||
|
ps.close();
|
||||||
|
rs.close();
|
||||||
|
old = false;
|
||||||
|
if(old != read) {
|
||||||
|
sql = "INSERT INTO flags (messageId, read, starred)"
|
||||||
|
+ " VALUES (?, ?, ?)";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBytes(1, m.getBytes());
|
||||||
|
ps.setBoolean(2, read);
|
||||||
|
ps.setBoolean(3, false);
|
||||||
|
int affected = ps.executeUpdate();
|
||||||
|
if(affected != 1) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return old;
|
return old;
|
||||||
} catch(SQLException e) {
|
} catch(SQLException e) {
|
||||||
@@ -2121,6 +2227,56 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean setStarred(Connection txn, MessageId m, boolean starred)
|
||||||
|
throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
String sql = "SELECT starred FROM flags WHERE messageId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBytes(1, m.getBytes());
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
boolean old;
|
||||||
|
if(rs.next()) {
|
||||||
|
// A flag row exists - update it if necessary
|
||||||
|
old = rs.getBoolean(1);
|
||||||
|
if(rs.next()) throw new DbStateException();
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
if(old != starred) {
|
||||||
|
sql = "UPDATE flags SET starred = ? WHERE messageId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBoolean(1, starred);
|
||||||
|
ps.setBytes(2, m.getBytes());
|
||||||
|
int affected = ps.executeUpdate();
|
||||||
|
if(affected != 1) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No flag row exists - create one if necessary
|
||||||
|
ps.close();
|
||||||
|
rs.close();
|
||||||
|
old = false;
|
||||||
|
if(old != starred) {
|
||||||
|
sql = "INSERT INTO flags (messageId, read, starred)"
|
||||||
|
+ " VALUES (?, ?, ?)";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setBytes(1, m.getBytes());
|
||||||
|
ps.setBoolean(2, false);
|
||||||
|
ps.setBoolean(3, starred);
|
||||||
|
int affected = ps.executeUpdate();
|
||||||
|
if(affected != 1) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
} catch(SQLException e) {
|
||||||
|
tryToClose(rs);
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setStatus(Connection txn, ContactId c, MessageId m, Status s)
|
public void setStatus(Connection txn, ContactId c, MessageId m, Status s)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -2133,7 +2289,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(2, c.getInt());
|
ps.setInt(2, c.getInt());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
if(rs.next()) {
|
if(rs.next()) {
|
||||||
// A status row exists - update it
|
// A status row exists - update it if necessary
|
||||||
Status old = Status.values()[rs.getByte(1)];
|
Status old = Status.values()[rs.getByte(1)];
|
||||||
if(rs.next()) throw new DbStateException();
|
if(rs.next()) throw new DbStateException();
|
||||||
rs.close();
|
rs.close();
|
||||||
|
|||||||
@@ -1705,6 +1705,58 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadFlag() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// Subscribe to a group and store a message
|
||||||
|
db.addSubscription(txn, group);
|
||||||
|
db.addGroupMessage(txn, message);
|
||||||
|
|
||||||
|
// The message should be unread by default
|
||||||
|
assertFalse(db.getRead(txn, messageId));
|
||||||
|
// Marking the message read should return the old value
|
||||||
|
assertFalse(db.setRead(txn, messageId, true));
|
||||||
|
assertTrue(db.setRead(txn, messageId, true));
|
||||||
|
// The message should be read
|
||||||
|
assertTrue(db.getRead(txn, messageId));
|
||||||
|
// Marking the message unread should return the old value
|
||||||
|
assertTrue(db.setRead(txn, messageId, false));
|
||||||
|
assertFalse(db.setRead(txn, messageId, false));
|
||||||
|
// Unsubscribe from the group
|
||||||
|
db.removeSubscription(txn, groupId);
|
||||||
|
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStarredFlag() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// Subscribe to a group and store a message
|
||||||
|
db.addSubscription(txn, group);
|
||||||
|
db.addGroupMessage(txn, message);
|
||||||
|
|
||||||
|
// The message should be unstarred by default
|
||||||
|
assertFalse(db.getStarred(txn, messageId));
|
||||||
|
// Starring the message should return the old value
|
||||||
|
assertFalse(db.setStarred(txn, messageId, true));
|
||||||
|
assertTrue(db.setStarred(txn, messageId, true));
|
||||||
|
// The message should be starred
|
||||||
|
assertTrue(db.getStarred(txn, messageId));
|
||||||
|
// Unstarring the message should return the old value
|
||||||
|
assertTrue(db.setStarred(txn, messageId, false));
|
||||||
|
assertFalse(db.setStarred(txn, messageId, false));
|
||||||
|
// Unsubscribe from the group
|
||||||
|
db.removeSubscription(txn, groupId);
|
||||||
|
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
private void assertHeadersAreEqual(MessageHeader h1, MessageHeader h2) {
|
private void assertHeadersAreEqual(MessageHeader h1, MessageHeader h2) {
|
||||||
assertEquals(h1.getId(), h2.getId());
|
assertEquals(h1.getId(), h2.getId());
|
||||||
if(h1.getParent() == null) assertNull(h2.getParent());
|
if(h1.getParent() == null) assertNull(h2.getParent());
|
||||||
|
|||||||
Reference in New Issue
Block a user