diff --git a/src/chat.rs b/src/chat.rs index a557df6e5b..6c1b727bd4 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1042,7 +1042,13 @@ impl ChatId { pub(crate) async fn get_timestamp(self, context: &Context) -> Result> { let timestamp = context .sql - .query_get_value("SELECT MAX(timestamp) FROM msgs WHERE chat_id=?", (self,)) + .query_get_value( + "SELECT MAX(timestamp) + FROM msgs + WHERE chat_id=? + HAVING COUNT(*) > 0", + (self,), + ) .await?; Ok(timestamp) } @@ -1247,7 +1253,7 @@ impl ChatId { ) -> Result> { self.parent_query( context, - "rfc724_mid, mime_in_reply_to, mime_references", + "rfc724_mid, mime_in_reply_to, IFNULL(mime_references, '')", state_out_min, |row: &rusqlite::Row| { let rfc724_mid: String = row.get(0)?; @@ -1401,7 +1407,10 @@ impl ChatId { context .sql .query_get_value( - "SELECT MAX(timestamp) FROM msgs WHERE chat_id=? AND state!=?", + "SELECT MAX(timestamp) + FROM msgs + WHERE chat_id=? AND state!=? + HAVING COUNT(*) > 0", (self, MessageState::OutDraft), ) .await? @@ -1417,7 +1426,10 @@ impl ChatId { context .sql .query_get_value( - "SELECT MAX(timestamp) FROM msgs WHERE chat_id=? AND hidden=0 AND state>?", + "SELECT MAX(timestamp) + FROM msgs + WHERE chat_id=? AND hidden=0 AND state>? + HAVING COUNT(*) > 0", (self, MessageState::InFresh), ) .await? @@ -4400,7 +4412,10 @@ pub async fn add_device_msg_with_importance( if let Some(last_msg_time) = context .sql .query_get_value( - "SELECT MAX(timestamp) FROM msgs WHERE chat_id=?", + "SELECT MAX(timestamp) + FROM msgs + WHERE chat_id=? + HAVING COUNT(*) > 0", (chat_id,), ) .await? diff --git a/src/ephemeral.rs b/src/ephemeral.rs index 1fc77f291f..706a6e35b6 100644 --- a/src/ephemeral.rs +++ b/src/ephemeral.rs @@ -176,9 +176,16 @@ impl ChatId { pub async fn get_ephemeral_timer(self, context: &Context) -> Result { let timer = context .sql - .query_get_value("SELECT ephemeral_timer FROM chats WHERE id=?;", (self,)) + .query_row( + "SELECT IFNULL(ephemeral_timer, 0) FROM chats WHERE id=?", + (self,), + |row| { + let timer: Timer = row.get(0)?; + Ok(timer) + }, + ) .await?; - Ok(timer.unwrap_or_default()) + Ok(timer) } /// Set ephemeral timer value without sending a message. @@ -509,7 +516,8 @@ async fn next_delete_device_after_timestamp(context: &Context) -> Result ? AND chat_id != ? - AND chat_id != ?; + AND chat_id != ? + HAVING count(*) > 0 "#, (DC_CHAT_ID_TRASH, self_chat_id, device_chat_id), ) @@ -533,7 +541,8 @@ async fn next_expiration_timestamp(context: &Context) -> Option { SELECT min(ephemeral_timestamp) FROM msgs WHERE ephemeral_timestamp != 0 - AND chat_id != ?; + AND chat_id != ? + HAVING count(*) > 0 "#, (DC_CHAT_ID_TRASH,), // Trash contains already deleted messages, skip them ) diff --git a/src/message.rs b/src/message.rs index b5be44a9c5..5eea43a104 100644 --- a/src/message.rs +++ b/src/message.rs @@ -222,7 +222,7 @@ impl MsgId { pub async fn hop_info(self, context: &Context) -> Result> { context .sql - .query_get_value("SELECT hop_info FROM msgs WHERE id=?", (self,)) + .query_get_value("SELECT IFNULL(hop_info, '') FROM msgs WHERE id=?", (self,)) .await } @@ -1993,7 +1993,9 @@ pub(crate) async fn rfc724_mid_exists_ex( .query_row_optional( &("SELECT id, timestamp_sent, MIN(".to_string() + expr - + ") FROM msgs WHERE rfc724_mid=? ORDER BY timestamp_sent DESC"), + + ") FROM msgs WHERE rfc724_mid=? + HAVING COUNT(*) > 0 -- Prevent MIN(expr) from returning NULL when there are no rows. + ORDER BY timestamp_sent DESC"), (rfc724_mid,), |row| { let msg_id: MsgId = row.get(0)?; diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 5c5add1484..df14e5bc25 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -201,7 +201,8 @@ impl MimeFactory { let (in_reply_to, references) = context .sql .query_row( - "SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?", + "SELECT mime_in_reply_to, IFNULL(mime_references, '') + FROM msgs WHERE id=?", (msg.id,), |row| { let in_reply_to: String = row.get(0)?; diff --git a/src/sql.rs b/src/sql.rs index c1f37b48ae..02c709bad9 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -558,15 +558,13 @@ impl Sql { self.call(move |conn| match conn.query_row(sql.as_ref(), params, f) { Ok(res) => Ok(Some(res)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(rusqlite::Error::InvalidColumnType(_, _, rusqlite::types::Type::Null)) => Ok(None), Err(err) => Err(err.into()), }) .await } /// Executes a query which is expected to return one row and one - /// column. If the query does not return a value or returns SQL - /// `NULL`, returns `Ok(None)`. + /// column. If the query does not return any rows, returns `Ok(None)`. pub async fn query_get_value( &self, query: &str,