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