sql: use sqlite3_last_insert_rowid instead of SELECT

This commit is contained in:
link2xt
2021-04-17 05:54:41 +03:00
parent 59dea29e88
commit e35a8d4415
6 changed files with 40 additions and 124 deletions

View File

@@ -958,7 +958,6 @@ impl Chat {
timestamp: i64, timestamp: i64,
) -> Result<MsgId, Error> { ) -> Result<MsgId, Error> {
let mut new_references = "".into(); let mut new_references = "".into();
let mut msg_id = 0;
let mut to_id = 0; let mut to_id = 0;
let mut location_id = 0; let mut location_id = 0;
@@ -1066,10 +1065,10 @@ impl Chat {
// add independent location to database // add independent location to database
if msg.param.exists(Param::SetLatitude) if msg.param.exists(Param::SetLatitude) {
&& context if let Ok(row_id) = context
.sql .sql
.execute( .insert(
sqlx::query( sqlx::query(
"INSERT INTO locations \ "INSERT INTO locations \
(timestamp,from_id,chat_id, latitude,longitude,independent)\ (timestamp,from_id,chat_id, latitude,longitude,independent)\
@@ -1082,18 +1081,9 @@ impl Chat {
.bind(msg.param.get_float(Param::SetLongitude).unwrap_or_default()), .bind(msg.param.get_float(Param::SetLongitude).unwrap_or_default()),
) )
.await .await
.is_ok()
{ {
location_id = context location_id = row_id;
.sql }
.get_rowid2(
"locations",
"timestamp",
timestamp,
"from_id",
DC_CONTACT_ID_SELF as i64,
)
.await?;
} }
let ephemeral_timer = if msg.param.get_cmd() == SystemMessage::EphemeralTimerChanged { let ephemeral_timer = if msg.param.get_cmd() == SystemMessage::EphemeralTimerChanged {
@@ -1123,9 +1113,9 @@ impl Chat {
// add message to the database // add message to the database
if context let msg_id = context
.sql .sql
.execute( .insert(
sqlx::query( sqlx::query(
"INSERT INTO msgs ( "INSERT INTO msgs (
rfc724_mid, rfc724_mid,
@@ -1168,19 +1158,7 @@ impl Chat {
.bind(ephemeral_timer) .bind(ephemeral_timer)
.bind(ephemeral_timestamp), .bind(ephemeral_timestamp),
) )
.await
.is_ok()
{
msg_id = context
.sql
.get_rowid("msgs", "rfc724_mid", new_rfc724_mid)
.await?; .await?;
} else {
error!(
context,
"Cannot send message, cannot insert to database ({}).", self.id,
);
}
schedule_ephemeral_task(context).await; schedule_ephemeral_task(context).await;
Ok(MsgId::new(u32::try_from(msg_id)?)) Ok(MsgId::new(u32::try_from(msg_id)?))
@@ -2173,9 +2151,9 @@ pub async fn create_group_chat(
let draft_txt = stock_str::new_group_draft(context, &chat_name).await; let draft_txt = stock_str::new_group_draft(context, &chat_name).await;
let grpid = dc_create_id(); let grpid = dc_create_id();
context let row_id = context
.sql .sql
.execute( .insert(
sqlx::query( sqlx::query(
"INSERT INTO chats "INSERT INTO chats
(type, name, grpid, param, created_timestamp) (type, name, grpid, param, created_timestamp)
@@ -2188,8 +2166,6 @@ pub async fn create_group_chat(
) )
.await?; .await?;
let row_id = context.sql.get_rowid("chats", "grpid", grpid).await?;
let chat_id = ChatId::new(u32::try_from(row_id)?); let chat_id = ChatId::new(u32::try_from(row_id)?);
if add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await { if add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await {
let mut draft_msg = Message::new(Viewtype::Text); let mut draft_msg = Message::new(Viewtype::Text);
@@ -2945,9 +2921,9 @@ pub async fn add_device_msg_with_importance(
} }
} }
context let row_id = context
.sql .sql
.execute( .insert(
sqlx::query( sqlx::query(
"INSERT INTO msgs ( "INSERT INTO msgs (
chat_id, chat_id,
@@ -2979,10 +2955,6 @@ pub async fn add_device_msg_with_importance(
) )
.await?; .await?;
let row_id = context
.sql
.get_rowid("msgs", "rfc724_mid", &rfc724_mid)
.await?;
msg_id = MsgId::new(u32::try_from(row_id)?); msg_id = MsgId::new(u32::try_from(row_id)?);
} }
@@ -3056,7 +3028,8 @@ pub(crate) async fn add_info_msg_with_cmd(
param.set_cmd(cmd) param.set_cmd(cmd)
} }
context.sql.execute( let row_id =
context.sql.insert(
sqlx::query("INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid,ephemeral_timer, param) VALUES (?,?,?, ?,?,?, ?,?,?, ?);") sqlx::query("INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid,ephemeral_timer, param) VALUES (?,?,?, ?,?,?, ?,?,?, ?);")
.bind(chat_id) .bind(chat_id)
.bind(DC_CONTACT_ID_INFO as i32) .bind(DC_CONTACT_ID_INFO as i32)
@@ -3070,11 +3043,6 @@ pub(crate) async fn add_info_msg_with_cmd(
.bind(param.to_string()) .bind(param.to_string())
).await?; ).await?;
let row_id = context
.sql
.get_rowid("msgs", "rfc724_mid", &rfc724_mid)
.await
.unwrap_or_default();
let msg_id = MsgId::new(u32::try_from(row_id)?); let msg_id = MsgId::new(u32::try_from(row_id)?);
context.emit_event(EventType::MsgsChanged { chat_id, msg_id }); context.emit_event(EventType::MsgsChanged { chat_id, msg_id });
Ok(msg_id) Ok(msg_id)

View File

@@ -530,9 +530,9 @@ impl Contact {
let update_name = manual; let update_name = manual;
let update_authname = !manual; let update_authname = !manual;
if context if let Ok(new_row_id) = context
.sql .sql
.execute( .insert(
sqlx::query( sqlx::query(
"INSERT INTO contacts (name, addr, origin, authname) VALUES(?, ?, ?, ?);", "INSERT INTO contacts (name, addr, origin, authname) VALUES(?, ?, ?, ?);",
) )
@@ -550,9 +550,8 @@ impl Contact {
}), }),
) )
.await .await
.is_ok()
{ {
row_id = context.sql.get_rowid("contacts", "addr", &addr).await?; row_id = new_row_id;
sth_modified = Modifier::Created; sth_modified = Modifier::Created;
info!(context, "added contact id={} addr={}", row_id, &addr); info!(context, "added contact id={} addr={}", row_id, &addr);
} else { } else {

View File

@@ -978,9 +978,9 @@ async fn add_parts(
// also change `MsgId::trash()` and `delete_expired_messages()` // also change `MsgId::trash()` and `delete_expired_messages()`
let trash = chat_id.is_trash(); let trash = chat_id.is_trash();
context let row_id = context
.sql .sql
.execute( .insert(
sqlx::query( sqlx::query(
r#" r#"
INSERT INTO msgs INSERT INTO msgs
@@ -1040,12 +1040,7 @@ INSERT INTO msgs
.bind(ephemeral_timestamp), .bind(ephemeral_timestamp),
) )
.await?; .await?;
let msg_id = MsgId::new(u32::try_from( let msg_id = MsgId::new(u32::try_from(row_id)?);
context
.sql
.get_rowid("msgs", "rfc724_mid", &rfc724_mid)
.await?,
)?);
created_db_entries.push((*chat_id, msg_id)); created_db_entries.push((*chat_id, msg_id));
*insert_msg_id = msg_id; *insert_msg_id = msg_id;
@@ -1769,7 +1764,8 @@ async fn create_multiuser_record(
create_blocked: Blocked, create_blocked: Blocked,
create_protected: ProtectionStatus, create_protected: ProtectionStatus,
) -> Result<ChatId> { ) -> Result<ChatId> {
context.sql.execute( let row_id =
context.sql.insert(
sqlx::query( sqlx::query(
"INSERT INTO chats (type, name, grpid, blocked, created_timestamp, protected) VALUES(?, ?, ?, ?, ?, ?);") "INSERT INTO chats (type, name, grpid, blocked, created_timestamp, protected) VALUES(?, ?, ?, ?, ?, ?);")
.bind(chattype) .bind(chattype)
@@ -1780,11 +1776,6 @@ async fn create_multiuser_record(
.bind(create_protected) .bind(create_protected)
).await?; ).await?;
let row_id = context
.sql
.get_rowid("chats", "grpid", grpid.as_ref())
.await?;
let chat_id = ChatId::new(u32::try_from(row_id)?); let chat_id = ChatId::new(u32::try_from(row_id)?);
info!( info!(
context, context,

View File

@@ -571,9 +571,9 @@ pub async fn save(
.exists(sqlx::query(stmt_test).bind(timestamp).bind(contact_id)) .exists(sqlx::query(stmt_test).bind(timestamp).bind(contact_id))
.await?; .await?;
if independent || !exists { if independent || !exists {
context let row_id = context
.sql .sql
.execute( .insert(
sqlx::query(stmt_insert) sqlx::query(stmt_insert)
.bind(timestamp) .bind(timestamp)
.bind(contact_id) .bind(contact_id)
@@ -587,16 +587,7 @@ pub async fn save(
if timestamp > newest_timestamp { if timestamp > newest_timestamp {
newest_timestamp = timestamp; newest_timestamp = timestamp;
newest_location_id = context newest_location_id = row_id;
.sql
.get_rowid2(
"locations",
"timestamp",
timestamp,
"from_id",
contact_id as i64,
)
.await?;
} }
} }
} }

View File

@@ -317,7 +317,8 @@ impl Message {
pub async fn load_from_db(context: &Context, id: MsgId) -> Result<Message, Error> { pub async fn load_from_db(context: &Context, id: MsgId) -> Result<Message, Error> {
ensure!( ensure!(
!id.is_special(), !id.is_special(),
"Can not load special message IDs from DB." "Can not load special message ID {} from DB.",
id
); );
let row = context let row = context
.sql .sql

View File

@@ -222,6 +222,18 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode
Ok(rows.rows_affected()) Ok(rows.rows_affected())
} }
/// Executes the given query, returning the last inserted row ID.
pub async fn insert<'q, E>(&self, query: Query<'q, Sqlite, E>) -> Result<i64>
where
E: 'q + IntoArguments<'q, Sqlite>,
{
let lock = self.writer.read().await;
let pool = lock.as_ref().ok_or(Error::SqlNoConnection)?;
let rows = pool.execute(query).await?;
Ok(rows.last_insert_rowid())
}
/// Execute many queries. /// Execute many queries.
pub async fn execute_many<'q, E>(&self, query: Query<'q, Sqlite, E>) -> Result<()> pub async fn execute_many<'q, E>(&self, query: Query<'q, Sqlite, E>) -> Result<()>
where where
@@ -484,52 +496,6 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode
.await .await
.map(|s| s.and_then(|r| r.parse().ok())) .map(|s| s.and_then(|r| r.parse().ok()))
} }
/// Alternative to sqlite3_last_insert_rowid() which MUST NOT be used due to race conditions, see comment above.
/// the ORDER BY ensures, this function always returns the most recent id,
/// eg. if a Message-ID is split into different messages.
pub async fn get_rowid(
&self,
table: impl AsRef<str>,
field: impl AsRef<str>,
value: impl AsRef<str>,
) -> Result<i64> {
// alternative to sqlite3_last_insert_rowid() which MUST NOT be used due to race conditions, see comment above.
// the ORDER BY ensures, this function always returns the most recent id,
// eg. if a Message-ID is split into different messages.
let query = format!(
"SELECT id FROM {} WHERE {}=? ORDER BY id DESC",
table.as_ref(),
field.as_ref(),
);
self.query_get_value(sqlx::query(&query).bind(value.as_ref()))
.await
.map(|id| id.unwrap_or_default())
}
/// Fetches the rowid by restricting the rows through two different key, value settings.
pub async fn get_rowid2(
&self,
table: impl AsRef<str>,
field: impl AsRef<str>,
value: i64,
field2: impl AsRef<str>,
value2: i64,
) -> Result<i64> {
let query = format!(
"SELECT id FROM {} WHERE {}={} AND {}={} ORDER BY id DESC",
table.as_ref(),
field.as_ref(),
value,
field2.as_ref(),
value2,
);
self.query_get_value(sqlx::query(&query))
.await
.map(|id| id.unwrap_or_default())
}
} }
pub async fn housekeeping(context: &Context) -> Result<()> { pub async fn housekeeping(context: &Context) -> Result<()> {