From 9c00dd01a2b7ed3bc50466e2b5bedbbb8878a0b4 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 3 Feb 2019 09:17:37 +0100 Subject: [PATCH 1/8] Draft to ease PRAGMA usage (#273 and #265) --- src/lib.rs | 1 + src/pragma.rs | 405 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 406 insertions(+) create mode 100644 src/pragma.rs diff --git a/src/lib.rs b/src/lib.rs index 9bb11a9..03318ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,6 +117,7 @@ mod inner_connection; pub mod limits; #[cfg(feature = "load_extension")] mod load_extension_guard; +mod pragma; mod raw_statement; mod row; #[cfg(feature = "session")] diff --git a/src/pragma.rs b/src/pragma.rs new file mode 100644 index 0000000..a9daff8 --- /dev/null +++ b/src/pragma.rs @@ -0,0 +1,405 @@ +//! Pragma helpers + +use std::ops::Deref; + +use crate::error::Error; +use crate::ffi; +use crate::types::{ToSql, ToSqlOutput, ValueRef}; +use crate::{Connection, DatabaseName, Result, Row, NO_PARAMS}; + +pub struct Sql { + buf: String, +} + +impl Sql { + pub fn new() -> Sql { + Sql { buf: String::new() } + } + + pub fn push_pragma( + &mut self, + schema_name: Option>, + pragma_name: &str, + ) -> Result<()> { + self.push_keyword("PRAGMA")?; + self.push_space(); + if let Some(schema_name) = schema_name { + self.push_schema_name(schema_name); + self.push_dot(); + } + self.push_keyword(pragma_name) + } + + pub fn push_keyword(&mut self, keyword: &str) -> Result<()> { + if !keyword.is_empty() && is_identifier(keyword) { + self.buf.push_str(keyword); + Ok(()) + } else { + Err(Error::SqliteFailure( + ffi::Error::new(ffi::SQLITE_MISUSE), + Some(format!("Invalid keyword \"{}\"", keyword)), + )) + } + } + + pub fn push_schema_name(&mut self, schema_name: DatabaseName<'_>) { + match schema_name { + DatabaseName::Main => self.buf.push_str("main"), + DatabaseName::Temp => self.buf.push_str("temp"), + DatabaseName::Attached(s) => self.push_identifier(s), + }; + } + + pub fn push_identifier(&mut self, s: &str) { + if is_identifier(s) { + self.buf.push_str(s); + } else { + self.wrap_and_escape(s, '"'); + } + } + + pub fn push_value(&mut self, value: &dyn ToSql) -> Result<()> { + let value = value.to_sql()?; + let value = match value { + ToSqlOutput::Borrowed(v) => v, + ToSqlOutput::Owned(ref v) => ValueRef::from(v), + #[cfg(feature = "blob")] + ToSqlOutput::ZeroBlob(_) => { + return Err(Error::SqliteFailure( + ffi::Error::new(ffi::SQLITE_MISUSE), + Some(format!("Unsupported value \"{:?}\"", value)), + )); + } + #[cfg(feature = "array")] + ToSqlOutput::Array(_) => { + return Err(Error::SqliteFailure( + ffi::Error::new(ffi::SQLITE_MISUSE), + Some(format!("Unsupported value \"{:?}\"", value)), + )); + } + }; + match value { + ValueRef::Integer(i) => { + self.push_int(i); + } + ValueRef::Real(r) => { + self.push_real(r); + } + ValueRef::Text(s) => { + self.push_string_literal(s); + } + _ => { + return Err(Error::SqliteFailure( + ffi::Error::new(ffi::SQLITE_MISUSE), + Some(format!("Unsupported value \"{:?}\"", value)), + )); + } + }; + Ok(()) + } + + pub fn push_string_literal(&mut self, s: &str) { + self.wrap_and_escape(s, '\''); + } + + pub fn push_int(&mut self, i: i64) { + self.buf.push_str(&i.to_string()); + } + + pub fn push_real(&mut self, f: f64) { + self.buf.push_str(&f.to_string()); + } + + pub fn push_space(&mut self) { + self.buf.push(' '); + } + + pub fn push_dot(&mut self) { + self.buf.push('.'); + } + + pub fn push_equal_sign(&mut self) { + self.buf.push('='); + } + + pub fn open_brace(&mut self) { + self.buf.push('('); + } + + pub fn close_brace(&mut self) { + self.buf.push(')'); + } + + pub fn as_str(&self) -> &str { + &self.buf + } + + fn wrap_and_escape(&mut self, s: &str, quote: char) { + self.buf.push(quote); + let chars = s.chars(); + for ch in chars { + // escape `quote` by doubling it + if ch == quote { + self.buf.push(ch); + } + self.buf.push(ch) + } + self.buf.push(quote); + } +} + +impl Deref for Sql { + type Target = str; + + fn deref(&self) -> &str { + self.as_str() + } +} + +impl Connection { + /// Query the current value of `pragma_name`. + /// + /// Some pragmas will return multiple rows/values which cannot be retrieved + /// with this method. + pub fn pragma_query_value( + &self, + schema_name: Option>, + pragma_name: &str, + f: F, + ) -> Result + where + F: FnOnce(Row<'_, '_>) -> Result, + { + let mut query = Sql::new(); + query.push_pragma(schema_name, pragma_name)?; + let mut stmt = self.prepare(&query)?; + let mut rows = stmt.query(NO_PARAMS)?; + if let Some(result_row) = rows.next() { + let row = result_row?; + f(row) + } else { + Err(Error::QueryReturnedNoRows) + } + } + + /// Query the current rows/values of `pragma_name`. + pub fn pragma_query( + &self, + schema_name: Option>, + pragma_name: &str, + mut f: F, + ) -> Result<()> + where + F: FnMut(&Row<'_, '_>) -> Result<()>, + { + let mut query = Sql::new(); + query.push_pragma(schema_name, pragma_name)?; + let mut stmt = self.prepare(&query)?; + let mut rows = stmt.query(NO_PARAMS)?; + while let Some(result_row) = rows.next() { + let row = result_row?; + f(&row)?; + } + Ok(()) + } + + /// Query the current value(s) of `pragma_name` associated to + /// `pragma_value`. + /// + /// This method can be used with query-only pragmas which need an argument + /// (e.g. `table_info('one_tbl')`) or pragmas which return the updated value + /// (e.g. `journal_mode`). + pub fn pragma( + &self, + schema_name: Option>, + pragma_name: &str, + pragma_value: &dyn ToSql, + mut f: F, + ) -> Result<()> + where + F: FnMut(&Row<'_, '_>) -> Result<()>, + { + let mut sql = Sql::new(); + sql.push_pragma(schema_name, pragma_name)?; + // The argument is may be either in parentheses + // or it may be separated from the pragma name by an equal sign. + // The two syntaxes yield identical results. + sql.open_brace(); + sql.push_value(pragma_value)?; + sql.close_brace(); + let mut stmt = self.prepare(&sql)?; + let mut rows = stmt.query(NO_PARAMS)?; + while let Some(result_row) = rows.next() { + let row = result_row?; + f(&row)?; + } + Ok(()) + } + + /// Set a new value to `pragma_name`. + /// + /// Some pragmas will return the updated value which cannot be retrieved + /// with this method. + pub fn pragma_update( + &self, + schema_name: Option>, + pragma_name: &str, + pragma_value: &dyn ToSql, + ) -> Result<()> { + let mut sql = Sql::new(); + sql.push_pragma(schema_name, pragma_name)?; + // The argument is may be either in parentheses + // or it may be separated from the pragma name by an equal sign. + // The two syntaxes yield identical results. + sql.push_equal_sign(); + sql.push_value(pragma_value)?; + self.execute_batch(&sql) + } + + /// Set a new value to `pragma_name` and return the updated value. + /// + /// Only few pragmas automatically return the updated value. + pub fn pragma_update_and_check( + &self, + schema_name: Option>, + pragma_name: &str, + pragma_value: &dyn ToSql, + f: F, + ) -> Result + where + F: FnOnce(Row<'_, '_>) -> Result, + { + let mut sql = Sql::new(); + sql.push_pragma(schema_name, pragma_name)?; + // The argument is may be either in parentheses + // or it may be separated from the pragma name by an equal sign. + // The two syntaxes yield identical results. + sql.push_equal_sign(); + sql.push_value(pragma_value)?; + let mut stmt = self.prepare(&sql)?; + let mut rows = stmt.query(NO_PARAMS)?; + if let Some(result_row) = rows.next() { + let row = result_row?; + f(row) + } else { + Err(Error::QueryReturnedNoRows) + } + } +} + +fn is_identifier(s: &str) -> bool { + let chars = s.char_indices(); + for (i, ch) in chars { + if i == 0 { + if !is_identifier_start(ch) { + return false; + } + } else if !is_identifier_continue(ch) { + return false; + } + } + true +} + +fn is_identifier_start(c: char) -> bool { + (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z') || c > '\x7F' +} + +fn is_identifier_continue(c: char) -> bool { + c == '$' + || (c >= '0' && c <= '9') + || (c >= 'A' && c <= 'Z') + || c == '_' + || (c >= 'a' && c <= 'z') + || c > '\x7F' +} + +#[cfg(test)] +mod test { + use super::Sql; + use crate::pragma; + use crate::{Connection, DatabaseName}; + + #[test] + fn pragma_query_value() { + let db = Connection::open_in_memory().unwrap(); + let user_version: i32 = db + .pragma_query_value(None, "user_version", |row| row.get_checked(0)) + .unwrap(); + assert_eq!(0, user_version); + } + + #[test] + fn pragma_query_no_schema() { + let db = Connection::open_in_memory().unwrap(); + let mut user_version = -1; + db.pragma_query(None, "user_version", |row| { + user_version = row.get_checked(0)?; + Ok(()) + }) + .unwrap(); + assert_eq!(0, user_version); + } + + #[test] + fn pragma_query_with_schema() { + let db = Connection::open_in_memory().unwrap(); + let mut user_version = -1; + db.pragma_query(Some(DatabaseName::Main), "user_version", |row| { + user_version = row.get_checked(0)?; + Ok(()) + }) + .unwrap(); + assert_eq!(0, user_version); + } + + #[test] + fn pragma() { + let db = Connection::open_in_memory().unwrap(); + let mut columns = Vec::new(); + db.pragma(None, "table_info", &"sqlite_master", |row| { + let column: String = row.get_checked(1)?; + columns.push(column); + Ok(()) + }) + .unwrap(); + assert_eq!(5, columns.len()); + } + + #[test] + fn pragma_update() { + let db = Connection::open_in_memory().unwrap(); + db.pragma_update(None, "user_version", &1).unwrap(); + } + + #[test] + fn pragma_update_and_check() { + let db = Connection::open_in_memory().unwrap(); + let journal_mode: String = db + .pragma_update_and_check(None, "journal_mode", &"OFF", |row| row.get_checked(0)) + .unwrap(); + assert_eq!("off", &journal_mode); + } + + #[test] + fn is_identifier() { + assert!(pragma::is_identifier("full")); + assert!(pragma::is_identifier("r2d2")); + assert!(!pragma::is_identifier("sp ce")); + assert!(!pragma::is_identifier("semi;colon")); + } + + #[test] + fn double_quote() { + let mut sql = Sql::new(); + sql.push_schema_name(DatabaseName::Attached(r#"schema";--"#)); + assert_eq!(r#""schema"";--""#, sql.as_str()); + } + + #[test] + fn wrap_and_escape() { + let mut sql = Sql::new(); + sql.push_string_literal("value'; --"); + assert_eq!("'value''; --'", sql.as_str()); + } +} From 6ce5c9ddccb74453fce595fd8656f1d7d3dffc2e Mon Sep 17 00:00:00 2001 From: gwenn Date: Thu, 14 Feb 2019 20:24:16 +0100 Subject: [PATCH 2/8] Suggest users to use PRAGMA function instead --- src/pragma.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/pragma.rs b/src/pragma.rs index a9daff8..134582d 100644 --- a/src/pragma.rs +++ b/src/pragma.rs @@ -161,6 +161,9 @@ impl Connection { /// /// Some pragmas will return multiple rows/values which cannot be retrieved /// with this method. + /// + /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20: + /// `SELECT user_version FROM pragma_user_version;` pub fn pragma_query_value( &self, schema_name: Option>, @@ -183,6 +186,9 @@ impl Connection { } /// Query the current rows/values of `pragma_name`. + /// + /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20: + /// `SELECT * FROM pragma_collation_list;` pub fn pragma_query( &self, schema_name: Option>, @@ -207,8 +213,11 @@ impl Connection { /// `pragma_value`. /// /// This method can be used with query-only pragmas which need an argument - /// (e.g. `table_info('one_tbl')`) or pragmas which return the updated value - /// (e.g. `journal_mode`). + /// (e.g. `table_info('one_tbl')`) or pragmas which returns value(s) + /// (e.g. `integrity_check`). + /// + /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20: + /// `SELECT * FROM pragma_table_info(?);` pub fn pragma( &self, schema_name: Option>, @@ -318,7 +327,7 @@ fn is_identifier_continue(c: char) -> bool { mod test { use super::Sql; use crate::pragma; - use crate::{Connection, DatabaseName}; + use crate::{Connection, DatabaseName, NO_PARAMS}; #[test] fn pragma_query_value() { @@ -329,6 +338,20 @@ mod test { assert_eq!(0, user_version); } + #[test] + #[cfg(feature = "bundled")] + fn pragma_func_query_value() { + let db = Connection::open_in_memory().unwrap(); + let user_version: i32 = db + .query_row( + "SELECT user_version FROM pragma_user_version", + NO_PARAMS, + |row| row.get(0), + ) + .unwrap(); + assert_eq!(0, user_version); + } + #[test] fn pragma_query_no_schema() { let db = Connection::open_in_memory().unwrap(); @@ -366,6 +389,22 @@ mod test { assert_eq!(5, columns.len()); } + #[test] + #[cfg(feature = "bundled")] + fn pragma_func() { + let db = Connection::open_in_memory().unwrap(); + let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?)").unwrap(); + let mut columns = Vec::new(); + let mut rows = table_info.query(&["sqlite_master"]).unwrap(); + + while let Some(row) = rows.next() { + let row = row.unwrap(); + let column: String = row.get(1); + columns.push(column); + } + assert_eq!(5, columns.len()); + } + #[test] fn pragma_update() { let db = Connection::open_in_memory().unwrap(); From 364e885b8904d115737fe71e8bdfd1576afe8326 Mon Sep 17 00:00:00 2001 From: gwenn Date: Thu, 14 Feb 2019 20:53:00 +0100 Subject: [PATCH 3/8] Fix warning --- src/pragma.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pragma.rs b/src/pragma.rs index 134582d..d7f2144 100644 --- a/src/pragma.rs +++ b/src/pragma.rs @@ -327,7 +327,7 @@ fn is_identifier_continue(c: char) -> bool { mod test { use super::Sql; use crate::pragma; - use crate::{Connection, DatabaseName, NO_PARAMS}; + use crate::{Connection, DatabaseName}; #[test] fn pragma_query_value() { @@ -341,6 +341,8 @@ mod test { #[test] #[cfg(feature = "bundled")] fn pragma_func_query_value() { + use crate::NO_PARAMS; + let db = Connection::open_in_memory().unwrap(); let user_version: i32 = db .query_row( From 1d1ee97ab971d7b78658f6dbd3ef1a5f869b5afd Mon Sep 17 00:00:00 2001 From: gwenn Date: Thu, 14 Feb 2019 20:53:26 +0100 Subject: [PATCH 4/8] Try to fix AppVeyor build --- appveyor.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index db0bfad..acf29ba 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,10 +16,10 @@ install: - rustc -V - cargo -V # download SQLite dll (useful only when the `bundled` feature is not set) - - appveyor-retry appveyor DownloadFile https://sqlite.org/2018/sqlite-dll-win64-x64-3250000.zip -FileName sqlite-dll-win64-x64.zip + - appveyor-retry appveyor DownloadFile https://sqlite.org/2018/sqlite-dll-win64-x64-3260000.zip -FileName sqlite-dll-win64-x64.zip - if not defined VCPKG_DEFAULT_TRIPLET 7z e sqlite-dll-win64-x64.zip -y > nul # download SQLite headers (useful only when the `bundled` feature is not set) - - appveyor-retry appveyor DownloadFile https://sqlite.org/2018/sqlite-amalgamation-3250000.zip -FileName sqlite-amalgamation.zip + - appveyor-retry appveyor DownloadFile https://sqlite.org/2018/sqlite-amalgamation-3260000.zip -FileName sqlite-amalgamation.zip - if not defined VCPKG_DEFAULT_TRIPLET 7z e sqlite-amalgamation.zip -y > nul # specify where the SQLite dll has been downloaded (useful only when the `bundled` feature is not set) - if not defined VCPKG_DEFAULT_TRIPLET SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER% @@ -33,10 +33,10 @@ build: false test_script: - cargo test --lib --verbose - cargo test --lib --verbose --features bundled - - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab" - - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab buildtime_bindgen" - - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled" - - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled buildtime_bindgen" + - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab_v3" + - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab_v3 buildtime_bindgen" + - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab_v3 bundled" + - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab_v3 bundled buildtime_bindgen" cache: - C:\Users\appveyor\.cargo From b6470f371fc1d5815a755c4922bb2b59a9c71938 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 16 Feb 2019 10:07:55 +0100 Subject: [PATCH 5/8] Try to fix AppVeyor build --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index acf29ba..e4775b0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -33,8 +33,8 @@ build: false test_script: - cargo test --lib --verbose - cargo test --lib --verbose --features bundled - - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab_v3" - - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab_v3 buildtime_bindgen" + - cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace" + - cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace buildtime_bindgen" - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab_v3 bundled" - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab_v3 bundled buildtime_bindgen" From 37027a6087235115366439d22624d0124216d361 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 16 Feb 2019 11:06:21 +0100 Subject: [PATCH 6/8] Try to fix AppVeyor build --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e4775b0..5e4761f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,10 +16,10 @@ install: - rustc -V - cargo -V # download SQLite dll (useful only when the `bundled` feature is not set) - - appveyor-retry appveyor DownloadFile https://sqlite.org/2018/sqlite-dll-win64-x64-3260000.zip -FileName sqlite-dll-win64-x64.zip + - appveyor-retry appveyor DownloadFile https://sqlite.org/2018/sqlite-dll-win64-x64-3250200.zip -FileName sqlite-dll-win64-x64.zip - if not defined VCPKG_DEFAULT_TRIPLET 7z e sqlite-dll-win64-x64.zip -y > nul # download SQLite headers (useful only when the `bundled` feature is not set) - - appveyor-retry appveyor DownloadFile https://sqlite.org/2018/sqlite-amalgamation-3260000.zip -FileName sqlite-amalgamation.zip + - appveyor-retry appveyor DownloadFile https://sqlite.org/2018/sqlite-amalgamation-3250200.zip -FileName sqlite-amalgamation.zip - if not defined VCPKG_DEFAULT_TRIPLET 7z e sqlite-amalgamation.zip -y > nul # specify where the SQLite dll has been downloaded (useful only when the `bundled` feature is not set) - if not defined VCPKG_DEFAULT_TRIPLET SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER% From fcaf5b9dd8711fa1a60f55d9ed2c907ae39a178e Mon Sep 17 00:00:00 2001 From: gwenn Date: Thu, 21 Feb 2019 18:55:51 +0100 Subject: [PATCH 7/8] Fix typos --- src/pragma.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pragma.rs b/src/pragma.rs index d7f2144..50302be 100644 --- a/src/pragma.rs +++ b/src/pragma.rs @@ -230,7 +230,7 @@ impl Connection { { let mut sql = Sql::new(); sql.push_pragma(schema_name, pragma_name)?; - // The argument is may be either in parentheses + // The argument may be either in parentheses // or it may be separated from the pragma name by an equal sign. // The two syntaxes yield identical results. sql.open_brace(); @@ -257,7 +257,7 @@ impl Connection { ) -> Result<()> { let mut sql = Sql::new(); sql.push_pragma(schema_name, pragma_name)?; - // The argument is may be either in parentheses + // The argument may be either in parentheses // or it may be separated from the pragma name by an equal sign. // The two syntaxes yield identical results. sql.push_equal_sign(); @@ -280,7 +280,7 @@ impl Connection { { let mut sql = Sql::new(); sql.push_pragma(schema_name, pragma_name)?; - // The argument is may be either in parentheses + // The argument may be either in parentheses // or it may be separated from the pragma name by an equal sign. // The two syntaxes yield identical results. sql.push_equal_sign(); From 32881d7a762f0580f41efe2e3a78784d4add8491 Mon Sep 17 00:00:00 2001 From: gwenn Date: Thu, 21 Feb 2019 21:14:55 +0100 Subject: [PATCH 8/8] Unify callback parameter signature --- src/pragma.rs | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/pragma.rs b/src/pragma.rs index 50302be..19c6791 100644 --- a/src/pragma.rs +++ b/src/pragma.rs @@ -171,18 +171,11 @@ impl Connection { f: F, ) -> Result where - F: FnOnce(Row<'_, '_>) -> Result, + F: FnOnce(&Row<'_, '_>) -> Result, { let mut query = Sql::new(); query.push_pragma(schema_name, pragma_name)?; - let mut stmt = self.prepare(&query)?; - let mut rows = stmt.query(NO_PARAMS)?; - if let Some(result_row) = rows.next() { - let row = result_row?; - f(row) - } else { - Err(Error::QueryReturnedNoRows) - } + self.query_row(&query, NO_PARAMS, f) } /// Query the current rows/values of `pragma_name`. @@ -276,7 +269,7 @@ impl Connection { f: F, ) -> Result where - F: FnOnce(Row<'_, '_>) -> Result, + F: FnOnce(&Row<'_, '_>) -> Result, { let mut sql = Sql::new(); sql.push_pragma(schema_name, pragma_name)?; @@ -285,14 +278,7 @@ impl Connection { // The two syntaxes yield identical results. sql.push_equal_sign(); sql.push_value(pragma_value)?; - let mut stmt = self.prepare(&sql)?; - let mut rows = stmt.query(NO_PARAMS)?; - if let Some(result_row) = rows.next() { - let row = result_row?; - f(row) - } else { - Err(Error::QueryReturnedNoRows) - } + self.query_row(&sql, NO_PARAMS, f) } } @@ -333,7 +319,7 @@ mod test { fn pragma_query_value() { let db = Connection::open_in_memory().unwrap(); let user_version: i32 = db - .pragma_query_value(None, "user_version", |row| row.get_checked(0)) + .pragma_query_value(None, "user_version", |row| row.get(0)) .unwrap(); assert_eq!(0, user_version); } @@ -359,7 +345,7 @@ mod test { let db = Connection::open_in_memory().unwrap(); let mut user_version = -1; db.pragma_query(None, "user_version", |row| { - user_version = row.get_checked(0)?; + user_version = row.get(0)?; Ok(()) }) .unwrap(); @@ -371,7 +357,7 @@ mod test { let db = Connection::open_in_memory().unwrap(); let mut user_version = -1; db.pragma_query(Some(DatabaseName::Main), "user_version", |row| { - user_version = row.get_checked(0)?; + user_version = row.get(0)?; Ok(()) }) .unwrap(); @@ -383,7 +369,7 @@ mod test { let db = Connection::open_in_memory().unwrap(); let mut columns = Vec::new(); db.pragma(None, "table_info", &"sqlite_master", |row| { - let column: String = row.get_checked(1)?; + let column: String = row.get(1)?; columns.push(column); Ok(()) }) @@ -401,7 +387,7 @@ mod test { while let Some(row) = rows.next() { let row = row.unwrap(); - let column: String = row.get(1); + let column: String = row.get(1).unwrap(); columns.push(column); } assert_eq!(5, columns.len()); @@ -417,7 +403,7 @@ mod test { fn pragma_update_and_check() { let db = Connection::open_in_memory().unwrap(); let journal_mode: String = db - .pragma_update_and_check(None, "journal_mode", &"OFF", |row| row.get_checked(0)) + .pragma_update_and_check(None, "journal_mode", &"OFF", |row| row.get(0)) .unwrap(); assert_eq!("off", &journal_mode); }