aboutsummaryrefslogtreecommitdiff
path: root/src/column.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/column.rs')
-rw-r--r--src/column.rs99
1 files changed, 88 insertions, 11 deletions
diff --git a/src/column.rs b/src/column.rs
index 4f6daac..b9122c4 100644
--- a/src/column.rs
+++ b/src/column.rs
@@ -11,11 +11,13 @@ pub struct Column<'stmt> {
impl Column<'_> {
/// Returns the name of the column.
+ #[inline]
pub fn name(&self) -> &str {
self.name
}
/// Returns the type of the column (`None` for expression).
+ #[inline]
pub fn decl_type(&self) -> Option<&str> {
self.decl_type
}
@@ -23,6 +25,10 @@ impl Column<'_> {
impl Statement<'_> {
/// Get all the column names in the result set of the prepared statement.
+ ///
+ /// If associated DB schema can be altered concurrently, you should make
+ /// sure that current statement has already been stepped once before
+ /// calling this method.
pub fn column_names(&self) -> Vec<&str> {
let n = self.column_count();
let mut cols = Vec::with_capacity(n as usize);
@@ -35,10 +41,35 @@ impl Statement<'_> {
/// Return the number of columns in the result set returned by the prepared
/// statement.
+ ///
+ /// If associated DB schema can be altered concurrently, you should make
+ /// sure that current statement has already been stepped once before
+ /// calling this method.
+ #[inline]
pub fn column_count(&self) -> usize {
self.stmt.column_count()
}
+ /// Check that column name reference lifetime is limited:
+ /// https://www.sqlite.org/c3ref/column_name.html
+ /// > The returned string pointer is valid...
+ ///
+ /// `column_name` reference can become invalid if `stmt` is reprepared
+ /// (because of schema change) when `query_row` is called. So we assert
+ /// that a compilation error happens if this reference is kept alive:
+ /// ```compile_fail
+ /// use rusqlite::{Connection, Result};
+ /// fn main() -> Result<()> {
+ /// let db = Connection::open_in_memory()?;
+ /// let mut stmt = db.prepare("SELECT 1 as x")?;
+ /// let column_name = stmt.column_name(0)?;
+ /// let x = stmt.query_row([], |r| r.get::<_, i64>(0))?; // E0502
+ /// assert_eq!(1, x);
+ /// assert_eq!("x", column_name);
+ /// Ok(())
+ /// }
+ /// ```
+ #[inline]
pub(super) fn column_name_unwrap(&self, col: usize) -> &str {
// Just panic if the bounds are wrong for now, we never call this
// without checking first.
@@ -48,12 +79,17 @@ impl Statement<'_> {
/// Returns the name assigned to a particular column in the result set
/// returned by the prepared statement.
///
+ /// If associated DB schema can be altered concurrently, you should make
+ /// sure that current statement has already been stepped once before
+ /// calling this method.
+ ///
/// ## Failure
///
/// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
/// column range for this row.
///
/// Panics when column name is not valid UTF-8.
+ #[inline]
pub fn column_name(&self, col: usize) -> Result<&str> {
self.stmt
.column_name(col)
@@ -68,10 +104,15 @@ impl Statement<'_> {
/// If there is no AS clause then the name of the column is unspecified and
/// may change from one release of SQLite to the next.
///
+ /// If associated DB schema can be altered concurrently, you should make
+ /// sure that current statement has already been stepped once before
+ /// calling this method.
+ ///
/// # Failure
///
/// Will return an `Error::InvalidColumnName` when there is no column with
/// the specified `name`.
+ #[inline]
pub fn column_index(&self, name: &str) -> Result<usize> {
let bytes = name.as_bytes();
let n = self.column_count();
@@ -86,6 +127,10 @@ impl Statement<'_> {
}
/// Returns a slice describing the columns of the result of the query.
+ ///
+ /// If associated DB schema can be altered concurrently, you should make
+ /// sure that current statement has already been stepped once before
+ /// calling this method.
#[cfg(feature = "column_decltype")]
pub fn columns(&self) -> Vec<Column> {
let n = self.column_count();
@@ -104,26 +149,31 @@ impl Statement<'_> {
impl<'stmt> Rows<'stmt> {
/// Get all the column names.
+ #[inline]
pub fn column_names(&self) -> Option<Vec<&str>> {
self.stmt.map(Statement::column_names)
}
/// Return the number of columns.
+ #[inline]
pub fn column_count(&self) -> Option<usize> {
self.stmt.map(Statement::column_count)
}
/// Return the name of the column.
+ #[inline]
pub fn column_name(&self, col: usize) -> Option<Result<&str>> {
self.stmt.map(|stmt| stmt.column_name(col))
}
/// Return the index of the column.
+ #[inline]
pub fn column_index(&self, name: &str) -> Option<Result<usize>> {
self.stmt.map(|stmt| stmt.column_index(name))
}
/// Returns a slice describing the columns of the Rows.
+ #[inline]
#[cfg(feature = "column_decltype")]
pub fn columns(&self) -> Option<Vec<Column>> {
self.stmt.map(Statement::columns)
@@ -132,26 +182,31 @@ impl<'stmt> Rows<'stmt> {
impl<'stmt> Row<'stmt> {
/// Get all the column names of the Row.
+ #[inline]
pub fn column_names(&self) -> Vec<&str> {
self.stmt.column_names()
}
/// Return the number of columns in the current row.
+ #[inline]
pub fn column_count(&self) -> usize {
self.stmt.column_count()
}
/// Return the name of the column.
+ #[inline]
pub fn column_name(&self, col: usize) -> Result<&str> {
self.stmt.column_name(col)
}
/// Return the index of the column.
+ #[inline]
pub fn column_index(&self, name: &str) -> Result<usize> {
self.stmt.column_index(name)
}
/// Returns a slice describing the columns of the Row.
+ #[inline]
#[cfg(feature = "column_decltype")]
pub fn columns(&self) -> Vec<Column> {
self.stmt.columns()
@@ -160,15 +215,15 @@ impl<'stmt> Row<'stmt> {
#[cfg(test)]
mod test {
- use crate::Connection;
+ use crate::{Connection, Result};
#[test]
#[cfg(feature = "column_decltype")]
- fn test_columns() {
+ fn test_columns() -> Result<()> {
use super::Column;
- let db = Connection::open_in_memory().unwrap();
- let query = db.prepare("SELECT * FROM sqlite_master").unwrap();
+ let db = Connection::open_in_memory()?;
+ let query = db.prepare("SELECT * FROM sqlite_master")?;
let columns = query.columns();
let column_names: Vec<&str> = columns.iter().map(Column::name).collect();
assert_eq!(
@@ -180,22 +235,22 @@ mod test {
&column_types[..3],
&[Some("text"), Some("text"), Some("text"),]
);
+ Ok(())
}
#[test]
- fn test_column_name_in_error() {
+ fn test_column_name_in_error() -> Result<()> {
use crate::{types::Type, Error};
- let db = Connection::open_in_memory().unwrap();
+ let db = Connection::open_in_memory()?;
db.execute_batch(
"BEGIN;
CREATE TABLE foo(x INTEGER, y TEXT);
INSERT INTO foo VALUES(4, NULL);
END;",
- )
- .unwrap();
- let mut stmt = db.prepare("SELECT x as renamed, y FROM foo").unwrap();
- let mut rows = stmt.query(crate::NO_PARAMS).unwrap();
- let row = rows.next().unwrap().unwrap();
+ )?;
+ let mut stmt = db.prepare("SELECT x as renamed, y FROM foo")?;
+ let mut rows = stmt.query([])?;
+ let row = rows.next()?.unwrap();
match row.get::<_, String>(0).unwrap_err() {
Error::InvalidColumnType(idx, name, ty) => {
assert_eq!(idx, 0);
@@ -216,5 +271,27 @@ mod test {
panic!("Unexpected error type: {:?}", e);
}
}
+ Ok(())
+ }
+
+ /// `column_name` reference should stay valid until `stmt` is reprepared (or
+ /// reset) even if DB schema is altered (SQLite documentation is
+ /// ambiguous here because it says reference "is valid until (...) the next
+ /// call to sqlite3_column_name() or sqlite3_column_name16() on the same
+ /// column.". We assume that reference is valid if only
+ /// `sqlite3_column_name()` is used):
+ #[test]
+ #[cfg(feature = "modern_sqlite")]
+ fn test_column_name_reference() -> Result<()> {
+ let db = Connection::open_in_memory()?;
+ db.execute_batch("CREATE TABLE y (x);")?;
+ let stmt = db.prepare("SELECT x FROM y;")?;
+ let column_name = stmt.column_name(0)?;
+ assert_eq!("x", column_name);
+ db.execute_batch("ALTER TABLE y RENAME COLUMN x TO z;")?;
+ // column name is not refreshed until statement is re-prepared
+ let same_column_name = stmt.column_name(0)?;
+ assert_eq!(same_column_name, column_name);
+ Ok(())
}
}