diff --git a/Cargo.lock b/Cargo.lock index 0af841dd6..7a50141e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,9 +115,9 @@ checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" [[package]] name = "ahash" -version = "0.6.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796540673305a66d127804eef19ad696f1f204b8c1025aaca4958c17eab32877" +checksum = "7f200cbb1e856866d9eade941cf3aa0c5d7dd36f74311c4273b494f4ef036957" dependencies = [ "getrandom 0.2.2", "once_cell", @@ -2132,9 +2132,9 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "libsqlite3-sys" -version = "0.20.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd" +checksum = "2f6332d94daa84478d55a6aa9dbb3b305ed6500fb0cb9400cb9e1525d0e0e188" dependencies = [ "cc", "pkg-config", @@ -3465,7 +3465,7 @@ dependencies = [ [[package]] name = "sqlx" version = "0.5.1" -source = "git+https://github.com/deltachat/sqlx?branch=master#8915916c76dd578f1db445f9d6f18af480285aed" +source = "git+https://github.com/launchbadge/sqlx?branch=master#9e8e3346970cd382a9baca1bba6462b7df4b4b63" dependencies = [ "sqlx-core", "sqlx-macros", @@ -3474,9 +3474,9 @@ dependencies = [ [[package]] name = "sqlx-core" version = "0.5.1" -source = "git+https://github.com/deltachat/sqlx?branch=master#8915916c76dd578f1db445f9d6f18af480285aed" +source = "git+https://github.com/launchbadge/sqlx?branch=master#9e8e3346970cd382a9baca1bba6462b7df4b4b63" dependencies = [ - "ahash 0.6.3", + "ahash 0.7.2", "atoi", "bitflags", "byteorder", @@ -3512,7 +3512,7 @@ dependencies = [ [[package]] name = "sqlx-macros" version = "0.5.1" -source = "git+https://github.com/deltachat/sqlx?branch=master#8915916c76dd578f1db445f9d6f18af480285aed" +source = "git+https://github.com/launchbadge/sqlx?branch=master#9e8e3346970cd382a9baca1bba6462b7df4b4b63" dependencies = [ "dotenv", "either", @@ -3530,7 +3530,7 @@ dependencies = [ [[package]] name = "sqlx-rt" version = "0.3.0" -source = "git+https://github.com/deltachat/sqlx?branch=master#8915916c76dd578f1db445f9d6f18af480285aed" +source = "git+https://github.com/launchbadge/sqlx?branch=master#9e8e3346970cd382a9baca1bba6462b7df4b4b63" dependencies = [ "async-native-tls", "async-std", diff --git a/Cargo.toml b/Cargo.toml index a55c4e4a2..8fe90a6b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,9 +61,9 @@ async-std-resolver = "0.19.5" async-tar = "0.3.0" uuid = { version = "0.8", features = ["serde", "v4"] } rust-hsluv = "0.1.4" -sqlx = { git = "https://github.com/deltachat/sqlx", branch = "master", features = ["runtime-async-std-native-tls", "sqlite"] } +sqlx = { git = "https://github.com/launchbadge/sqlx", branch = "master", features = ["runtime-async-std-native-tls", "sqlite"] } # keep in sync with sqlx -libsqlite3-sys = { version = "0.20.1", default-features = false, features = [ "pkg-config", "vcpkg", "bundled" ] } +libsqlite3-sys = { version = "0.22.0", default-features = false, features = [ "pkg-config", "vcpkg", "bundled" ] } pretty_env_logger = { version = "0.4.0", optional = true } log = {version = "0.4.8", optional = true } diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index bdf1ffcb0..61e289b44 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -32,13 +32,17 @@ use std::time::{Duration, SystemTime}; async fn reset_tables(context: &Context, bits: i32) { println!("Resetting tables ({})...", bits); if 0 != bits & 1 { - context.sql().execute("DELETE FROM jobs;").await.unwrap(); + context + .sql() + .execute(sqlx::query("DELETE FROM jobs;")) + .await + .unwrap(); println!("(1) Jobs reset."); } if 0 != bits & 2 { context .sql() - .execute("DELETE FROM acpeerstates;") + .execute(sqlx::query("DELETE FROM acpeerstates;")) .await .unwrap(); println!("(2) Peerstates reset."); @@ -46,7 +50,7 @@ async fn reset_tables(context: &Context, bits: i32) { if 0 != bits & 4 { context .sql() - .execute("DELETE FROM keypairs;") + .execute(sqlx::query("DELETE FROM keypairs;")) .await .unwrap(); println!("(4) Private keypairs reset."); @@ -54,34 +58,34 @@ async fn reset_tables(context: &Context, bits: i32) { if 0 != bits & 8 { context .sql() - .execute("DELETE FROM contacts WHERE id>9;") + .execute(sqlx::query("DELETE FROM contacts WHERE id>9;")) .await .unwrap(); context .sql() - .execute("DELETE FROM chats WHERE id>9;") + .execute(sqlx::query("DELETE FROM chats WHERE id>9;")) .await .unwrap(); context .sql() - .execute("DELETE FROM chats_contacts;") + .execute(sqlx::query("DELETE FROM chats_contacts;")) .await .unwrap(); context .sql() - .execute("DELETE FROM msgs WHERE id>9;") + .execute(sqlx::query("DELETE FROM msgs WHERE id>9;")) .await .unwrap(); context .sql() - .execute( + .execute(sqlx::query( "DELETE FROM config WHERE keyname LIKE 'imap.%' OR keyname LIKE 'configured%';", - ) + )) .await .unwrap(); context .sql() - .execute("DELETE FROM leftgrps;") + .execute(sqlx::query("DELETE FROM leftgrps;")) .await .unwrap(); println!("(8) Rest but server config reset."); diff --git a/src/chat.rs b/src/chat.rs index aa88ede13..052f81596 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -2860,7 +2860,9 @@ pub(crate) async fn get_chat_cnt(context: &Context) -> Result { // no database, no chats - this is no error (needed eg. for information) let count = context .sql - .count("SELECT COUNT(*) FROM chats WHERE id>9 AND blocked=0;") + .count(sqlx::query( + "SELECT COUNT(*) FROM chats WHERE id>9 AND blocked=0;", + )) .await?; Ok(count as usize) } else { @@ -3030,7 +3032,10 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul .sql .execute(sqlx::query("DELETE FROM msgs WHERE from_id=?;").bind(DC_CONTACT_ID_DEVICE as i32)) .await?; - context.sql.execute("DELETE FROM devmsglabels;").await?; + context + .sql + .execute(sqlx::query("DELETE FROM devmsglabels;")) + .await?; Ok(()) } diff --git a/src/chatlist.rs b/src/chatlist.rs index fc0133b3f..2eff0b83b 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -410,7 +410,9 @@ impl Chatlist { pub async fn dc_get_archived_cnt(context: &Context) -> Result { let count = context .sql - .count("SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;") + .count(sqlx::query( + "SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;", + )) .await?; Ok(count) } @@ -420,7 +422,7 @@ async fn get_last_deaddrop_fresh_msg(context: &Context) -> Result> // sufficient as there are typically only few fresh messages. let id = context .sql - .query_get_value(concat!( + .query_get_value(sqlx::query(concat!( "SELECT m.id", " FROM msgs m", " LEFT JOIN chats c", @@ -429,7 +431,7 @@ async fn get_last_deaddrop_fresh_msg(context: &Context) -> Result> " AND m.hidden=0", " AND c.blocked=2", " ORDER BY m.timestamp DESC, m.id DESC;" - )) + ))) .await?; Ok(id) } diff --git a/src/config.rs b/src/config.rs index 4d2e90ad4..69824110f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -242,7 +242,7 @@ impl Context { match key { Config::Selfavatar => { self.sql - .execute("UPDATE contacts SET selfavatar_sent=0;") + .execute(sqlx::query("UPDATE contacts SET selfavatar_sent=0;")) .await?; self.sql .set_raw_config_bool("attach_selfavatar", true) diff --git a/src/context.rs b/src/context.rs index de66f5bf6..5bd0026e6 100644 --- a/src/context.rs +++ b/src/context.rs @@ -290,16 +290,22 @@ impl Context { .unwrap_or_default(); let journal_mode = self .sql - .query_get_value("PRAGMA journal_mode;") + .query_get_value(sqlx::query("PRAGMA journal_mode;")) .await? .unwrap_or_else(|| "unknown".to_string()); let e2ee_enabled = self.get_config_int(Config::E2eeEnabled).await?; let mdns_enabled = self.get_config_int(Config::MdnsEnabled).await?; let bcc_self = self.get_config_int(Config::BccSelf).await?; - let prv_key_cnt = self.sql.count("SELECT COUNT(*) FROM keypairs;").await?; + let prv_key_cnt = self + .sql + .count(sqlx::query("SELECT COUNT(*) FROM keypairs;")) + .await?; - let pub_key_cnt = self.sql.count("SELECT COUNT(*) FROM acpeerstates;").await?; + let pub_key_cnt = self + .sql + .count(sqlx::query("SELECT COUNT(*) FROM acpeerstates;")) + .await?; let fingerprint_str = match SignedPublicKey::load_self(self).await { Ok(key) => key.fingerprint().hex(), Err(err) => format!("", err), diff --git a/src/imex.rs b/src/imex.rs index dd448e593..1c323a5fc 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -595,7 +595,7 @@ async fn import_backup_old(context: &Context, backup_to_import: impl AsRef let total_files_cnt = context .sql - .count("SELECT COUNT(*) FROM backup_blobs;") + .count(sqlx::query("SELECT COUNT(*) FROM backup_blobs;")) .await?; info!( @@ -607,7 +607,7 @@ async fn import_backup_old(context: &Context, backup_to_import: impl AsRef // consuming too much memory. let file_ids = context .sql - .fetch("SELECT id FROM backup_blobs ORDER BY id") + .fetch(sqlx::query("SELECT id FROM backup_blobs ORDER BY id")) .await? .map(|row| row?.try_get(0)) .collect::>>() @@ -648,8 +648,11 @@ async fn import_backup_old(context: &Context, backup_to_import: impl AsRef if all_files_extracted { // only delete backup_blobs if all files were successfully extracted - context.sql.execute("DROP TABLE backup_blobs;").await?; - context.sql.execute("VACUUM;").await.ok(); + context + .sql + .execute(sqlx::query("DROP TABLE backup_blobs;")) + .await?; + context.sql.execute(sqlx::query("VACUUM;")).await.ok(); Ok(()) } else { bail!("received stop signal"); @@ -674,7 +677,7 @@ async fn export_backup(context: &Context, dir: impl AsRef) -> Result<()> { context .sql - .execute("VACUUM;") + .execute(sqlx::query("VACUUM;")) .await .map_err(|e| warn!(context, "Vacuum failed, exporting anyway {}", e)); @@ -829,7 +832,9 @@ async fn export_self_keys(context: &Context, dir: impl AsRef) -> Result<() let mut keys = context .sql - .fetch("SELECT id, public_key, private_key, is_default FROM keypairs;") + .fetch(sqlx::query( + "SELECT id, public_key, private_key, is_default FROM keypairs;", + )) .await? .map(|row| -> sqlx::Result<_> { let row = row?; diff --git a/src/key.rs b/src/key.rs index 82554f27c..cfd6628ed 100644 --- a/src/key.rs +++ b/src/key.rs @@ -123,14 +123,14 @@ impl DcKey for SignedPublicKey { async fn load_self(context: &Context) -> Result { match context .sql - .fetch_optional( + .fetch_optional(sqlx::query( r#" SELECT public_key FROM keypairs WHERE addr=(SELECT value FROM config WHERE keyname="configured_addr") AND is_default=1; "#, - ) + )) .await? { Some(row) => Self::from_slice(row.try_get(0)?), @@ -165,14 +165,14 @@ impl DcKey for SignedSecretKey { async fn load_self(context: &Context) -> Result { match context .sql - .fetch_optional( + .fetch_optional(sqlx::query( r#" SELECT private_key FROM keypairs WHERE addr=(SELECT value FROM config WHERE keyname="configured_addr") AND is_default=1; "#, - ) + )) .await? { Some(row) => Self::from_slice(row.try_get(0)?), @@ -328,7 +328,7 @@ pub async fn store_self_keypair( if default == KeyPairUse::Default { context .sql - .execute("UPDATE keypairs SET is_default=0;") + .execute(sqlx::query("UPDATE keypairs SET is_default=0;")) .await .map_err(|err| SaveKeyError::new("failed to clear default", err))?; } @@ -625,7 +625,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD let nrows = || async { ctx.sql - .count("SELECT COUNT(*) FROM keypairs;") + .count(sqlx::query("SELECT COUNT(*) FROM keypairs;")) .await .unwrap() }; diff --git a/src/location.rs b/src/location.rs index f3c3230a5..3671339ca 100644 --- a/src/location.rs +++ b/src/location.rs @@ -401,7 +401,10 @@ fn is_marker(txt: &str) -> bool { /// Deletes all locations from the database. pub async fn delete_all(context: &Context) -> Result<(), Error> { - context.sql.execute("DELETE FROM locations;").await?; + context + .sql + .execute(sqlx::query("DELETE FROM locations;")) + .await?; context.emit_event(EventType::LocationChanged(None)); Ok(()) } diff --git a/src/message.rs b/src/message.rs index cb7dc59c4..dda736660 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1878,11 +1878,11 @@ async fn ndn_maybe_add_info_msg( pub async fn get_real_msg_cnt(context: &Context) -> usize { match context .sql - .count( + .count(sqlx::query( "SELECT COUNT(*) \ FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \ WHERE m.id>9 AND m.chat_id>9 AND c.blocked=0;", - ) + )) .await { Ok(res) => res, @@ -1896,11 +1896,11 @@ pub async fn get_real_msg_cnt(context: &Context) -> usize { pub async fn get_deaddrop_msg_cnt(context: &Context) -> usize { match context .sql - .count( + .count(sqlx::query( "SELECT COUNT(*) \ FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \ WHERE c.blocked=2;", - ) + )) .await { Ok(res) => res, diff --git a/src/peerstate.rs b/src/peerstate.rs index 4b6ff0133..18a3841b0 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -5,7 +5,7 @@ use std::fmt; use anyhow::{bail, Result}; use num_traits::FromPrimitive; -use sqlx::Row; +use sqlx::{query::Query, sqlite::Sqlite, Row}; use crate::aheader::{Aheader, EncryptPreference}; use crate::chat; @@ -173,10 +173,12 @@ impl Peerstate { Self::from_stmt(context, query).await } - async fn from_stmt<'e, 'q, E>(context: &Context, query: E) -> Result> + async fn from_stmt<'q, E>( + context: &Context, + query: Query<'q, Sqlite, E>, + ) -> Result> where - 'q: 'e, - E: 'q + sqlx::Execute<'q, sqlx::Sqlite>, + E: 'q + sqlx::IntoArguments<'q, sqlx::Sqlite>, { if let Some(row) = context.sql.fetch_optional(query).await? { // all the above queries start with this: SELECT diff --git a/src/sql/migrations.rs b/src/sql/migrations.rs index f7ff2b624..1a99220ae 100644 --- a/src/sql/migrations.rs +++ b/src/sql/migrations.rs @@ -417,9 +417,9 @@ ALTER TABLE msgs ADD COLUMN mime_modified INTEGER DEFAULT 0;"#, if dbversion < 73 { use Config::*; info!(context, "[migration] v73"); - sql.execute( + sql.execute(sqlx::query( r#" -CREATE TABLE imap_sync (folder TEXT PRIMARY KEY, uidvalidity INTEGER DEFAULT 0, uid_next INTEGER DEFAULT 0);"#, +CREATE TABLE imap_sync (folder TEXT PRIMARY KEY, uidvalidity INTEGER DEFAULT 0, uid_next INTEGER DEFAULT 0);"#), ) .await?; for c in &[ diff --git a/src/sql/mod.rs b/src/sql/mod.rs index a6aa9aa99..52ea0ff6f 100644 --- a/src/sql/mod.rs +++ b/src/sql/mod.rs @@ -10,8 +10,9 @@ use async_std::prelude::*; use async_std::sync::RwLock; use sqlx::{ pool::PoolOptions, + query::Query, sqlite::{Sqlite, SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqliteSynchronous}, - Execute, Executor, Row, + Executor, IntoArguments, Row, }; use crate::chat::{add_device_msg, update_device_icon, update_saved_messages_icon}; @@ -171,7 +172,9 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode if recalc_fingerprints { info!(context, "[migration] recalc fingerprints"); - let mut rows = self.fetch("SELECT addr FROM acpeerstates;").await?; + let mut rows = self + .fetch(sqlx::query("SELECT addr FROM acpeerstates;")) + .await?; while let Some(row) = rows.next().await { let row = row?; @@ -208,10 +211,9 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode } /// Execute the given query, returning the number of affected rows. - pub async fn execute<'e, 'q, E>(&self, query: E) -> Result + pub async fn execute<'q, E>(&self, query: Query<'q, Sqlite, E>) -> Result where - 'q: 'e, - E: 'q + Execute<'q, Sqlite>, + E: 'q + IntoArguments<'q, Sqlite>, { let lock = self.writer.read().await; let pool = lock.as_ref().ok_or(Error::SqlNoConnection)?; @@ -221,10 +223,9 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode } /// Execute many queries. - pub async fn execute_many<'e, 'q, E>(&self, query: E) -> Result<()> + pub async fn execute_many<'q, E>(&self, query: Query<'q, Sqlite, E>) -> Result<()> where - 'q: 'e, - E: 'q + Execute<'q, Sqlite>, + E: 'q + IntoArguments<'q, Sqlite>, { let lock = self.writer.read().await; let pool = lock.as_ref().ok_or(Error::SqlNoConnection)?; @@ -236,13 +237,12 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode } /// Fetch the given query. - pub async fn fetch<'e, 'q, E>( + pub async fn fetch<'q, E>( &self, - query: E, - ) -> Result::Row>> + 'e + Send> + query: Query<'q, Sqlite, E>, + ) -> Result::Row>> + Send + 'q> where - 'q: 'e, - E: 'q + Execute<'q, Sqlite>, + E: 'q + IntoArguments<'q, Sqlite>, { let lock = self.reader.read().await; let pool = lock.as_ref().ok_or(Error::SqlNoConnection)?; @@ -252,10 +252,12 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode } /// Fetch exactly one row, errors if no row is found. - pub async fn fetch_one<'e, 'q, E>(&self, query: E) -> Result<::Row> + pub async fn fetch_one<'q, E>( + &self, + query: Query<'q, Sqlite, E>, + ) -> Result<::Row> where - 'q: 'e, - E: 'q + Execute<'q, Sqlite>, + E: 'q + IntoArguments<'q, Sqlite>, { let lock = self.reader.read().await; let pool = lock.as_ref().ok_or(Error::SqlNoConnection)?; @@ -267,11 +269,10 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode /// Fetches at most one row. pub async fn fetch_optional<'e, 'q, E>( &self, - query: E, + query: Query<'q, Sqlite, E>, ) -> Result::Row>> where - 'q: 'e, - E: 'q + Execute<'q, Sqlite>, + E: 'q + IntoArguments<'q, Sqlite>, { let lock = self.reader.read().await; let pool = lock.as_ref().ok_or(Error::SqlNoConnection)?; @@ -281,10 +282,9 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode } /// Used for executing `SELECT COUNT` statements only. Returns the resulting count. - pub async fn count<'e, 'q, E>(&self, query: E) -> Result + pub async fn count<'e, 'q, E>(&self, query: Query<'q, Sqlite, E>) -> Result where - 'q: 'e, - E: 'q + Execute<'q, Sqlite>, + E: 'q + IntoArguments<'q, Sqlite>, { use std::convert::TryFrom; @@ -296,10 +296,9 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode /// Used for executing `SELECT COUNT` statements only. Returns `true`, if the count is at least /// one, `false` otherwise. - pub async fn exists<'e, 'q, E>(&self, query: E) -> Result + pub async fn exists<'e, 'q, E>(&self, query: Query<'q, Sqlite, E>) -> Result where - 'q: 'e, - E: 'q + Execute<'q, Sqlite>, + E: 'q + IntoArguments<'q, Sqlite>, { let count = self.count(query).await?; Ok(count > 0) @@ -383,10 +382,12 @@ PRAGMA query_only=1; -- Protect against writes even in read-write mode /// 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)`. - pub async fn query_get_value<'e, 'q, E, T>(&self, query: E) -> Result> + pub async fn query_get_value<'e, 'q, E, T>( + &self, + query: Query<'q, Sqlite, E>, + ) -> Result> where - 'q: 'e, - E: 'q + Execute<'q, Sqlite>, + E: 'q + IntoArguments<'q, Sqlite>, T: for<'r> sqlx::Decode<'r, Sqlite> + sqlx::Type, { let res = self @@ -569,7 +570,10 @@ pub async fn housekeeping(context: &Context) -> Result<()> { ) .await?; - let mut rows = context.sql.fetch("SELECT value FROM config;").await?; + let mut rows = context + .sql + .fetch(sqlx::query("SELECT value FROM config;")) + .await?; while let Some(row) = rows.next().await { let row: String = row?.try_get(0)?; maybe_add_file(&mut files_in_use, row); @@ -692,7 +696,7 @@ async fn maybe_add_from_param( query: &str, param_id: Param, ) -> Result<()> { - let mut rows = sql.fetch(query).await?; + let mut rows = sql.fetch(sqlx::query(query)).await?; while let Some(row) = rows.next().await { let row: String = row?.try_get(0)?; let param: Params = row.parse().unwrap_or_default();