/// Information about a column of a SQLite query. #[cfg(feature = "column_decltype")] #[derive(Debug)] pubstruct Column<'stmt> {
name: &'stmt str,
decl_type: Option<&'stmt str>,
}
#[cfg(feature = "column_decltype")] impl Column<'_> { /// Returns the name of the column. #[inline] #[must_use] pubfn name(&self) -> &str { self.name
}
/// Returns the type of the column (`None` for expression). #[inline] #[must_use] pubfn decl_type(&self) -> Option<&str> { self.decl_type
}
}
/// Metadata about the origin of a column of a SQLite query #[cfg(feature = "column_metadata")] #[derive(Debug)] pubstruct ColumnMetadata<'stmt> {
name: &'stmt str,
database_name: Option<&'stmt str>,
table_name: Option<&'stmt str>,
origin_name: Option<&'stmt str>,
}
#[cfg(feature = "column_metadata")] impl ColumnMetadata<'_> { #[inline] #[must_use] /// Returns the name of the column in the query results pubfn name(&self) -> &str { self.name
}
#[inline] #[must_use] /// Returns the database name from which the column originates pubfn database_name(&self) -> Option<&str> { self.database_name
}
#[inline] #[must_use] /// Returns the table name from which the column originates pubfn table_name(&self) -> Option<&str> { self.table_name
}
#[inline] #[must_use] /// Returns the column name from which the column originates pubfn origin_name(&self) -> Option<&str> { self.origin_name
}
}
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. pubfn column_names(&self) -> Vec<&str> { let n = self.column_count(); letmut cols = Vec::with_capacity(n); for i in0..n { let s = self.column_name_unwrap(i);
cols.push(s);
}
cols
}
/// 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] pubfn 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. self.column_name(col).expect("Column out of bounds")
}
/// 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 /// /// Panics when column name is not valid UTF-8. #[inline] pubfn column_name(&self, col: usize) -> Result<&str> { self.stmt
.column_name(col) // clippy::or_fun_call (nightly) vs clippy::unnecessary-lazy-evaluations (stable)
.ok_or(Error::InvalidColumnIndex(col))
.map(|slice| {
slice
.to_str()
.expect("Invalid UTF-8 sequence in column name")
})
}
/// Returns the column index in the result set for a given column name. /// /// 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] pubfn column_index(&self, name: &str) -> Result<usize> { let bytes = name.as_bytes(); let n = self.column_count(); for i in0..n { // Note: `column_name` is only fallible if `i` is out of bounds, // which we've already checked. if bytes.eq_ignore_ascii_case(self.stmt.column_name(i).unwrap().to_bytes()) { return Ok(i);
}
}
Err(Error::InvalidColumnName(String::from(name)))
}
/// 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")] pubfn columns(&self) -> Vec<Column<'_>> { let n = self.column_count(); letmut cols = Vec::with_capacity(n); for i in0..n { let name = self.column_name_unwrap(i); let slice = self.stmt.column_decltype(i); let decl_type = slice.map(|s| {
s.to_str()
.expect("Invalid UTF-8 sequence in column declaration")
});
cols.push(Column { name, decl_type });
}
cols
}
/// Returns the names of the database, table, and row from which /// each column of this query's results originate. /// /// Computed or otherwise derived columns will have None values for these fields. #[cfg(feature = "column_metadata")] pubfn columns_with_metadata(&self) -> Vec<ColumnMetadata<'_>> { let n = self.column_count(); letmut col_mets = Vec::with_capacity(n); for i in0..n { let name = self.column_name_unwrap(i); let db_slice = self.stmt.column_database_name(i); let tbl_slice = self.stmt.column_table_name(i); let origin_slice = self.stmt.column_origin_name(i);
col_mets.push(ColumnMetadata {
name,
database_name: db_slice.map(|s| {
s.to_str()
.expect("Invalid UTF-8 sequence in column db name")
}),
table_name: tbl_slice.map(|s| {
s.to_str()
.expect("Invalid UTF-8 sequence in column table name")
}),
origin_name: origin_slice.map(|s| {
s.to_str()
.expect("Invalid UTF-8 sequence in column origin name")
}),
})
}
col_mets
}
/// Extract metadata of column at specified index /// /// Returns: /// - database name /// - table name /// - original column name /// - declared data type /// - name of default collation sequence /// - True if column has a NOT NULL constraint /// - True if column is part of the PRIMARY KEY /// - True if column is AUTOINCREMENT /// /// See [Connection::column_metadata] #[cfg(feature = "column_metadata")] #[allow(clippy::type_complexity)] pubfn column_metadata(
&self,
col: usize,
) -> Result<
Option<(
&CStr,
&CStr,
&CStr,
Option<&CStr>,
Option<&CStr>,
bool,
bool,
bool,
)>,
> { let db_name = self.stmt.column_database_name(col); let table_name = self.stmt.column_table_name(col); let origin_name = self.stmt.column_origin_name(col); if db_name.is_none() || table_name.is_none() || origin_name.is_none() { return Ok(None);
} let (data_type, coll_seq, not_null, primary_key, auto_inc) = self.conn
.column_metadata(db_name, table_name.unwrap(), origin_name.unwrap())?;
Ok(Some((
db_name.unwrap(),
table_name.unwrap(),
origin_name.unwrap(),
data_type,
coll_seq,
not_null,
primary_key,
auto_inc,
)))
}
}
impl Connection { /// Check if `table_name`.`column_name` exists. /// /// `db_name` is main, temp, the name in ATTACH, or `None` to search all databases. pubfn column_exists<N: Name>(
&self,
db_name: Option<N>,
table_name: N,
column_name: N,
) -> Result<bool> { self.exists(db_name, table_name, Some(column_name))
}
/// Check if `table_name` exists. /// /// `db_name` is main, temp, the name in ATTACH, or `None` to search all databases. pubfn table_exists<N: Name>(&self, db_name: Option<N>, table_name: N) -> Result<bool> { self.exists(db_name, table_name, None)
}
/// Extract metadata of column at specified index /// /// Returns: /// - declared data type /// - name of default collation sequence /// - True if column has a NOT NULL constraint /// - True if column is part of the PRIMARY KEY /// - True if column is AUTOINCREMENT #[allow(clippy::type_complexity)] pubfn column_metadata<N: Name>(
&self,
db_name: Option<N>,
table_name: N,
column_name: N,
) -> Result<(Option<&CStr>, Option<&CStr>, bool, bool, bool)> { let cs = db_name.as_ref().map(N::as_cstr).transpose()?; let db_name = cs.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()); let table_name = table_name.as_cstr()?; let column_name = column_name.as_cstr()?;
#[test] fn test_column_name_in_error() -> Result<()> { usecrate::{types::Type, Error}; let db = Connection::open_in_memory()?;
db.execute_batch( "BEGIN;
CREATE TABLE foo(x INTEGER, y TEXT);
INSERT INTO foo VALUES(4, NULL);
END;",
)?; letmut stmt = db.prepare("SELECT x as renamed, y FROM foo")?; letmut rows = stmt.query([])?; let row = rows.next()?.unwrap(); match row.get::<_, String>(0).unwrap_err() {
Error::InvalidColumnType(idx, name, ty) => {
assert_eq!(idx, 0);
assert_eq!(name, "renamed");
assert_eq!(ty, Type::Integer);
}
e => {
panic!("Unexpected error type: {e:?}");
}
} match row.get::<_, String>("y").unwrap_err() {
Error::InvalidColumnType(idx, name, ty) => {
assert_eq!(idx, 1);
assert_eq!(name, "y");
assert_eq!(ty, Type::Null);
}
e => {
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(())
}
#[test] #[cfg(feature = "column_metadata")] fn stmt_column_metadata() -> Result<()> { let db = Connection::open_in_memory()?; let query = db.prepare("SELECT *, 1 FROM sqlite_schema")?; let (db_name, table_name, col_name, data_type, coll_seq, not_null, primary_key, auto_inc) =
query.column_metadata(0)?.unwrap();
assert_eq!(db_name, crate::MAIN_DB);
assert_eq!(table_name, c"sqlite_master");
assert_eq!(col_name, c"type");
assert_eq!(data_type, Some(c"TEXT"));
assert_eq!(coll_seq, Some(c"BINARY"));
assert!(!not_null);
assert!(!primary_key);
assert!(!auto_inc);
assert!(query.column_metadata(5)?.is_none());
Ok(())
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.15Bemerkung:
(vorverarbeitet am 2026-08-24)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.