rusqlite/
column.rs

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