1 use std::str; 2 3 use crate::{Error, Result, Statement}; 4 5 /// Information about a column of a SQLite query. 6 #[derive(Debug)] 7 pub struct Column<'stmt> { 8 name: &'stmt str, 9 decl_type: Option<&'stmt str>, 10 } 11 12 impl Column<'_> { 13 /// Returns the name of the column. 14 #[inline] 15 #[must_use] name(&self) -> &str16 pub fn name(&self) -> &str { 17 self.name 18 } 19 20 /// Returns the type of the column (`None` for expression). 21 #[inline] 22 #[must_use] decl_type(&self) -> Option<&str>23 pub fn decl_type(&self) -> Option<&str> { 24 self.decl_type 25 } 26 } 27 28 impl Statement<'_> { 29 /// Get all the column names in the result set of the prepared statement. 30 /// 31 /// If associated DB schema can be altered concurrently, you should make 32 /// sure that current statement has already been stepped once before 33 /// calling this method. column_names(&self) -> Vec<&str>34 pub fn column_names(&self) -> Vec<&str> { 35 let n = self.column_count(); 36 let mut cols = Vec::with_capacity(n); 37 for i in 0..n { 38 let s = self.column_name_unwrap(i); 39 cols.push(s); 40 } 41 cols 42 } 43 44 /// Return the number of columns in the result set returned by the prepared 45 /// statement. 46 /// 47 /// If associated DB schema can be altered concurrently, you should make 48 /// sure that current statement has already been stepped once before 49 /// calling this method. 50 #[inline] column_count(&self) -> usize51 pub fn column_count(&self) -> usize { 52 self.stmt.column_count() 53 } 54 55 /// Check that column name reference lifetime is limited: 56 /// https://www.sqlite.org/c3ref/column_name.html 57 /// > The returned string pointer is valid... 58 /// 59 /// `column_name` reference can become invalid if `stmt` is reprepared 60 /// (because of schema change) when `query_row` is called. So we assert 61 /// that a compilation error happens if this reference is kept alive: 62 /// ```compile_fail 63 /// use rusqlite::{Connection, Result}; 64 /// fn main() -> Result<()> { 65 /// let db = Connection::open_in_memory()?; 66 /// let mut stmt = db.prepare("SELECT 1 as x")?; 67 /// let column_name = stmt.column_name(0)?; 68 /// let x = stmt.query_row([], |r| r.get::<_, i64>(0))?; // E0502 69 /// assert_eq!(1, x); 70 /// assert_eq!("x", column_name); 71 /// Ok(()) 72 /// } 73 /// ``` 74 #[inline] column_name_unwrap(&self, col: usize) -> &str75 pub(super) fn column_name_unwrap(&self, col: usize) -> &str { 76 // Just panic if the bounds are wrong for now, we never call this 77 // without checking first. 78 self.column_name(col).expect("Column out of bounds") 79 } 80 81 /// Returns the name assigned to a particular column in the result set 82 /// returned by the prepared statement. 83 /// 84 /// If associated DB schema can be altered concurrently, you should make 85 /// sure that current statement has already been stepped once before 86 /// calling this method. 87 /// 88 /// ## Failure 89 /// 90 /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid 91 /// column range for this row. 92 /// 93 /// Panics when column name is not valid UTF-8. 94 #[inline] column_name(&self, col: usize) -> Result<&str>95 pub fn column_name(&self, col: usize) -> Result<&str> { 96 self.stmt 97 .column_name(col) 98 // clippy::or_fun_call (nightly) vs clippy::unnecessary-lazy-evaluations (stable) 99 .ok_or(Error::InvalidColumnIndex(col)) 100 .map(|slice| { 101 str::from_utf8(slice.to_bytes()).expect("Invalid UTF-8 sequence in column name") 102 }) 103 } 104 105 /// Returns the column index in the result set for a given column name. 106 /// 107 /// If there is no AS clause then the name of the column is unspecified and 108 /// may change from one release of SQLite to the next. 109 /// 110 /// If associated DB schema can be altered concurrently, you should make 111 /// sure that current statement has already been stepped once before 112 /// calling this method. 113 /// 114 /// # Failure 115 /// 116 /// Will return an `Error::InvalidColumnName` when there is no column with 117 /// the specified `name`. 118 #[inline] column_index(&self, name: &str) -> Result<usize>119 pub fn column_index(&self, name: &str) -> Result<usize> { 120 let bytes = name.as_bytes(); 121 let n = self.column_count(); 122 for i in 0..n { 123 // Note: `column_name` is only fallible if `i` is out of bounds, 124 // which we've already checked. 125 if bytes.eq_ignore_ascii_case(self.stmt.column_name(i).unwrap().to_bytes()) { 126 return Ok(i); 127 } 128 } 129 Err(Error::InvalidColumnName(String::from(name))) 130 } 131 132 /// Returns a slice describing the columns of the result of the query. 133 /// 134 /// If associated DB schema can be altered concurrently, you should make 135 /// sure that current statement has already been stepped once before 136 /// calling this method. 137 #[cfg(feature = "column_decltype")] 138 #[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))] columns(&self) -> Vec<Column>139 pub fn columns(&self) -> Vec<Column> { 140 let n = self.column_count(); 141 let mut cols = Vec::with_capacity(n); 142 for i in 0..n { 143 let name = self.column_name_unwrap(i); 144 let slice = self.stmt.column_decltype(i); 145 let decl_type = slice.map(|s| { 146 str::from_utf8(s.to_bytes()).expect("Invalid UTF-8 sequence in column declaration") 147 }); 148 cols.push(Column { name, decl_type }); 149 } 150 cols 151 } 152 } 153 154 #[cfg(test)] 155 mod test { 156 use crate::{Connection, Result}; 157 158 #[test] 159 #[cfg(feature = "column_decltype")] test_columns() -> Result<()>160 fn test_columns() -> Result<()> { 161 use super::Column; 162 163 let db = Connection::open_in_memory()?; 164 let query = db.prepare("SELECT * FROM sqlite_master")?; 165 let columns = query.columns(); 166 let column_names: Vec<&str> = columns.iter().map(Column::name).collect(); 167 assert_eq!( 168 column_names.as_slice(), 169 &["type", "name", "tbl_name", "rootpage", "sql"] 170 ); 171 let column_types: Vec<Option<String>> = columns 172 .iter() 173 .map(|col| col.decl_type().map(str::to_lowercase)) 174 .collect(); 175 assert_eq!( 176 &column_types[..3], 177 &[ 178 Some("text".to_owned()), 179 Some("text".to_owned()), 180 Some("text".to_owned()), 181 ] 182 ); 183 Ok(()) 184 } 185 186 #[test] test_column_name_in_error() -> Result<()>187 fn test_column_name_in_error() -> Result<()> { 188 use crate::{types::Type, Error}; 189 let db = Connection::open_in_memory()?; 190 db.execute_batch( 191 "BEGIN; 192 CREATE TABLE foo(x INTEGER, y TEXT); 193 INSERT INTO foo VALUES(4, NULL); 194 END;", 195 )?; 196 let mut stmt = db.prepare("SELECT x as renamed, y FROM foo")?; 197 let mut rows = stmt.query([])?; 198 let row = rows.next()?.unwrap(); 199 match row.get::<_, String>(0).unwrap_err() { 200 Error::InvalidColumnType(idx, name, ty) => { 201 assert_eq!(idx, 0); 202 assert_eq!(name, "renamed"); 203 assert_eq!(ty, Type::Integer); 204 } 205 e => { 206 panic!("Unexpected error type: {:?}", e); 207 } 208 } 209 match row.get::<_, String>("y").unwrap_err() { 210 Error::InvalidColumnType(idx, name, ty) => { 211 assert_eq!(idx, 1); 212 assert_eq!(name, "y"); 213 assert_eq!(ty, Type::Null); 214 } 215 e => { 216 panic!("Unexpected error type: {:?}", e); 217 } 218 } 219 Ok(()) 220 } 221 222 /// `column_name` reference should stay valid until `stmt` is reprepared (or 223 /// reset) even if DB schema is altered (SQLite documentation is 224 /// ambiguous here because it says reference "is valid until (...) the next 225 /// call to sqlite3_column_name() or sqlite3_column_name16() on the same 226 /// column.". We assume that reference is valid if only 227 /// `sqlite3_column_name()` is used): 228 #[test] 229 #[cfg(feature = "modern_sqlite")] test_column_name_reference() -> Result<()>230 fn test_column_name_reference() -> Result<()> { 231 let db = Connection::open_in_memory()?; 232 db.execute_batch("CREATE TABLE y (x);")?; 233 let stmt = db.prepare("SELECT x FROM y;")?; 234 let column_name = stmt.column_name(0)?; 235 assert_eq!("x", column_name); 236 db.execute_batch("ALTER TABLE y RENAME COLUMN x TO z;")?; 237 // column name is not refreshed until statement is re-prepared 238 let same_column_name = stmt.column_name(0)?; 239 assert_eq!(same_column_name, column_name); 240 Ok(()) 241 } 242 } 243