mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 11:31:37 +08:00
Merge pull request #495 from gwenn/decltype
Introduce Statement::columns
This commit is contained in:
commit
4ef5bcaf55
@ -151,9 +151,9 @@ impl StatementCache {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use fallible_iterator::FallibleIterator;
|
|
||||||
use super::StatementCache;
|
use super::StatementCache;
|
||||||
use crate::{Connection, NO_PARAMS};
|
use crate::{Connection, NO_PARAMS};
|
||||||
|
use fallible_iterator::FallibleIterator;
|
||||||
|
|
||||||
impl StatementCache {
|
impl StatementCache {
|
||||||
fn clear(&self) {
|
fn clear(&self) {
|
||||||
@ -279,8 +279,7 @@ mod test {
|
|||||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(Some(1i32)),
|
Ok(Some(1i32)),
|
||||||
stmt.query(NO_PARAMS).unwrap().map(|r| r.get(0))
|
stmt.query(NO_PARAMS).unwrap().map(|r| r.get(0)).next()
|
||||||
.next()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +295,9 @@ mod test {
|
|||||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(Some((1i32, 2i32))),
|
Ok(Some((1i32, 2i32))),
|
||||||
stmt.query(NO_PARAMS).unwrap().map(|r| Ok((r.get(0)?, r.get(1)?)))
|
stmt.query(NO_PARAMS)
|
||||||
|
.unwrap()
|
||||||
|
.map(|r| Ok((r.get(0)?, r.get(1)?)))
|
||||||
.next()
|
.next()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
132
src/column.rs
Normal file
132
src/column.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use std::str;
|
||||||
|
|
||||||
|
use crate::{Error, Result, Row, Rows, Statement};
|
||||||
|
|
||||||
|
/// Information about a column of a SQLite query.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Column<'stmt> {
|
||||||
|
name: &'stmt str,
|
||||||
|
decl_type: Option<&'stmt str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Column<'_> {
|
||||||
|
/// Returns the name of the column.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the type of the column (`None` for expression).
|
||||||
|
pub fn decl_type(&self) -> Option<&str> {
|
||||||
|
self.decl_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Statement<'_> {
|
||||||
|
/// Get all the column names in the result set of the prepared statement.
|
||||||
|
pub fn column_names(&self) -> Vec<&str> {
|
||||||
|
let n = self.column_count();
|
||||||
|
let mut cols = Vec::with_capacity(n as usize);
|
||||||
|
for i in 0..n {
|
||||||
|
let slice = self.stmt.column_name(i);
|
||||||
|
let s = str::from_utf8(slice.to_bytes()).unwrap();
|
||||||
|
cols.push(s);
|
||||||
|
}
|
||||||
|
cols
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of columns in the result set returned by the prepared
|
||||||
|
/// statement.
|
||||||
|
pub fn column_count(&self) -> usize {
|
||||||
|
self.stmt.column_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// # Failure
|
||||||
|
///
|
||||||
|
/// Will return an `Error::InvalidColumnName` when there is no column with
|
||||||
|
/// the specified `name`.
|
||||||
|
pub fn column_index(&self, name: &str) -> Result<usize> {
|
||||||
|
let bytes = name.as_bytes();
|
||||||
|
let n = self.column_count();
|
||||||
|
for i in 0..n {
|
||||||
|
if bytes.eq_ignore_ascii_case(self.stmt.column_name(i).to_bytes()) {
|
||||||
|
return Ok(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::InvalidColumnName(String::from(name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice describing the columns of the result of the query.
|
||||||
|
pub fn columns<'stmt>(&'stmt self) -> Vec<Column<'stmt>> {
|
||||||
|
let n = self.column_count();
|
||||||
|
let mut cols = Vec::with_capacity(n as usize);
|
||||||
|
for i in 0..n {
|
||||||
|
let slice = self.stmt.column_name(i);
|
||||||
|
let name = str::from_utf8(slice.to_bytes()).unwrap();
|
||||||
|
let slice = self.stmt.column_decltype(i);
|
||||||
|
let decl_type = slice.map(|s| str::from_utf8(s.to_bytes()).unwrap());
|
||||||
|
cols.push(Column { name, decl_type });
|
||||||
|
}
|
||||||
|
cols
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'stmt> Rows<'stmt> {
|
||||||
|
/// Get all the column names.
|
||||||
|
pub fn column_names(&self) -> Option<Vec<&str>> {
|
||||||
|
self.stmt.map(Statement::column_names)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of columns.
|
||||||
|
pub fn column_count(&self) -> Option<usize> {
|
||||||
|
self.stmt.map(Statement::column_count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice describing the columns of the Rows.
|
||||||
|
pub fn columns(&self) -> Option<Vec<Column<'stmt>>> {
|
||||||
|
self.stmt.map(Statement::columns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'stmt> Row<'stmt> {
|
||||||
|
/// Return the number of columns in the current row.
|
||||||
|
pub fn column_count(&self) -> usize {
|
||||||
|
self.stmt.column_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice describing the columns of the Row.
|
||||||
|
pub fn columns(&self) -> Vec<Column<'stmt>> {
|
||||||
|
self.stmt.columns()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Column;
|
||||||
|
use crate::Connection;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_columns() {
|
||||||
|
let db = Connection::open_in_memory().unwrap();
|
||||||
|
let query = db.prepare("SELECT * FROM sqlite_master").unwrap();
|
||||||
|
let columns = query.columns();
|
||||||
|
let column_names: Vec<&str> = columns.iter().map(Column::name).collect();
|
||||||
|
assert_eq!(
|
||||||
|
column_names.as_slice(),
|
||||||
|
&["type", "name", "tbl_name", "rootpage", "sql"]
|
||||||
|
);
|
||||||
|
let column_types: Vec<Option<&str>> = columns.iter().map(Column::decl_type).collect();
|
||||||
|
assert_eq!(
|
||||||
|
&column_types[..3],
|
||||||
|
&[
|
||||||
|
Some("text"),
|
||||||
|
Some("text"),
|
||||||
|
Some("text"),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -83,6 +83,7 @@ use crate::raw_statement::RawStatement;
|
|||||||
use crate::types::ValueRef;
|
use crate::types::ValueRef;
|
||||||
|
|
||||||
pub use crate::cache::CachedStatement;
|
pub use crate::cache::CachedStatement;
|
||||||
|
pub use crate::column::Column;
|
||||||
pub use crate::error::Error;
|
pub use crate::error::Error;
|
||||||
pub use crate::ffi::ErrorCode;
|
pub use crate::ffi::ErrorCode;
|
||||||
#[cfg(feature = "hooks")]
|
#[cfg(feature = "hooks")]
|
||||||
@ -104,6 +105,7 @@ pub mod backup;
|
|||||||
pub mod blob;
|
pub mod blob;
|
||||||
mod busy;
|
mod busy;
|
||||||
mod cache;
|
mod cache;
|
||||||
|
mod column;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
#[cfg(any(feature = "functions", feature = "vtab"))]
|
#[cfg(any(feature = "functions", feature = "vtab"))]
|
||||||
mod context;
|
mod context;
|
||||||
@ -844,10 +846,10 @@ unsafe fn db_filename(_: *mut ffi::sqlite3) -> Option<PathBuf> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use fallible_iterator::FallibleIterator;
|
|
||||||
use self::tempdir::TempDir;
|
use self::tempdir::TempDir;
|
||||||
pub use super::*;
|
pub use super::*;
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
|
use fallible_iterator::FallibleIterator;
|
||||||
pub use std::error::Error as StdError;
|
pub use std::error::Error as StdError;
|
||||||
pub use std::fmt;
|
pub use std::fmt;
|
||||||
use tempdir;
|
use tempdir;
|
||||||
@ -1131,7 +1133,9 @@ mod test {
|
|||||||
|
|
||||||
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
||||||
let results: Result<Vec<String>> = query
|
let results: Result<Vec<String>> = query
|
||||||
.query(NO_PARAMS).unwrap().map(|row| row.get(1))
|
.query(NO_PARAMS)
|
||||||
|
.unwrap()
|
||||||
|
.map(|row| row.get(1))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
assert_eq!(results.unwrap().concat(), "hello, world!");
|
assert_eq!(results.unwrap().concat(), "hello, world!");
|
||||||
|
@ -26,6 +26,17 @@ impl RawStatement {
|
|||||||
unsafe { ffi::sqlite3_column_type(self.0, idx as c_int) }
|
unsafe { ffi::sqlite3_column_type(self.0, idx as c_int) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn column_decltype(&self, idx: usize) -> Option<&CStr> {
|
||||||
|
unsafe {
|
||||||
|
let decltype = ffi::sqlite3_column_decltype(self.0, idx as c_int);
|
||||||
|
if decltype.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(CStr::from_ptr(decltype))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn column_name(&self, idx: usize) -> &CStr {
|
pub fn column_name(&self, idx: usize) -> &CStr {
|
||||||
unsafe { CStr::from_ptr(ffi::sqlite3_column_name(self.0, idx as c_int)) }
|
unsafe { CStr::from_ptr(ffi::sqlite3_column_name(self.0, idx as c_int)) }
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use crate::types::{FromSql, FromSqlError, ValueRef};
|
|||||||
|
|
||||||
/// An handle for the resulting rows of a query.
|
/// An handle for the resulting rows of a query.
|
||||||
pub struct Rows<'stmt> {
|
pub struct Rows<'stmt> {
|
||||||
stmt: Option<&'stmt Statement<'stmt>>,
|
pub(crate) stmt: Option<&'stmt Statement<'stmt>>,
|
||||||
row: Option<Row<'stmt>>,
|
row: Option<Row<'stmt>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ impl<'stmt> FallibleStreamingIterator for Rows<'stmt> {
|
|||||||
|
|
||||||
/// A single result row of a query.
|
/// A single result row of a query.
|
||||||
pub struct Row<'stmt> {
|
pub struct Row<'stmt> {
|
||||||
stmt: &'stmt Statement<'stmt>,
|
pub(crate) stmt: &'stmt Statement<'stmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'stmt> Row<'stmt> {
|
impl<'stmt> Row<'stmt> {
|
||||||
@ -275,11 +275,6 @@ impl<'stmt> Row<'stmt> {
|
|||||||
pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
|
pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
|
||||||
self.get_raw_checked(idx).unwrap()
|
self.get_raw_checked(idx).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the number of columns in the current row.
|
|
||||||
pub fn column_count(&self) -> usize {
|
|
||||||
self.stmt.column_count()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait implemented by types that can index into columns of a row.
|
/// A trait implemented by types that can index into columns of a row.
|
||||||
|
@ -719,8 +719,8 @@ unsafe extern "C" fn x_output(p_out: *mut c_void, data: *const c_void, len: c_in
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use fallible_streaming_iterator::FallibleStreamingIterator;
|
use fallible_streaming_iterator::FallibleStreamingIterator;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use super::{Changeset, ChangesetIter, ConflictAction, ConflictType, Session};
|
use super::{Changeset, ChangesetIter, ConflictAction, ConflictType, Session};
|
||||||
use crate::hooks::Action;
|
use crate::hooks::Action;
|
||||||
|
@ -18,48 +18,10 @@ use crate::vtab::array::{free_array, ARRAY_TYPE};
|
|||||||
/// A prepared statement.
|
/// A prepared statement.
|
||||||
pub struct Statement<'conn> {
|
pub struct Statement<'conn> {
|
||||||
conn: &'conn Connection,
|
conn: &'conn Connection,
|
||||||
stmt: RawStatement,
|
pub(crate) stmt: RawStatement,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Statement<'_> {
|
impl Statement<'_> {
|
||||||
/// Get all the column names in the result set of the prepared statement.
|
|
||||||
pub fn column_names(&self) -> Vec<&str> {
|
|
||||||
let n = self.column_count();
|
|
||||||
let mut cols = Vec::with_capacity(n as usize);
|
|
||||||
for i in 0..n {
|
|
||||||
let slice = self.stmt.column_name(i);
|
|
||||||
let s = str::from_utf8(slice.to_bytes()).unwrap();
|
|
||||||
cols.push(s);
|
|
||||||
}
|
|
||||||
cols
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the number of columns in the result set returned by the prepared
|
|
||||||
/// statement.
|
|
||||||
pub fn column_count(&self) -> usize {
|
|
||||||
self.stmt.column_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// # Failure
|
|
||||||
///
|
|
||||||
/// Will return an `Error::InvalidColumnName` when there is no column with
|
|
||||||
/// the specified `name`.
|
|
||||||
pub fn column_index(&self, name: &str) -> Result<usize> {
|
|
||||||
let bytes = name.as_bytes();
|
|
||||||
let n = self.column_count();
|
|
||||||
for i in 0..n {
|
|
||||||
if bytes.eq_ignore_ascii_case(self.stmt.column_name(i).to_bytes()) {
|
|
||||||
return Ok(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(Error::InvalidColumnName(String::from(name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute the prepared statement.
|
/// Execute the prepared statement.
|
||||||
///
|
///
|
||||||
/// On success, returns the number of rows that were changed or inserted or
|
/// On success, returns the number of rows that were changed or inserted or
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! `ToSql` and `FromSql` implementation for [`url::Url`].
|
//! `ToSql` and `FromSql` implementation for [`url::Url`].
|
||||||
use url::Url;
|
|
||||||
use crate::Result;
|
|
||||||
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||||
|
use crate::Result;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
/// Serialize `Url` to text.
|
/// Serialize `Url` to text.
|
||||||
impl ToSql for Url {
|
impl ToSql for Url {
|
||||||
@ -23,8 +23,8 @@ impl FromSql for Url {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use url::{Url, ParseError};
|
use crate::{params, Connection, Error, Result};
|
||||||
use crate::{Connection, params, Error, Result};
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
fn checked_memory_handle() -> Connection {
|
fn checked_memory_handle() -> Connection {
|
||||||
let db = Connection::open_in_memory().unwrap();
|
let db = Connection::open_in_memory().unwrap();
|
||||||
@ -34,11 +34,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_url(db: &Connection, id: i64) -> Result<Url> {
|
fn get_url(db: &Connection, id: i64) -> Result<Url> {
|
||||||
db.query_row(
|
db.query_row("SELECT v FROM urls WHERE i = ?", params![id], |r| r.get(0))
|
||||||
"SELECT v FROM urls WHERE i = ?",
|
|
||||||
params![id],
|
|
||||||
|r| r.get(0),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -345,9 +345,9 @@ impl From<csv::Error> for Error {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use fallible_iterator::FallibleIterator;
|
|
||||||
use crate::vtab::csvtab;
|
use crate::vtab::csvtab;
|
||||||
use crate::{Connection, Result, NO_PARAMS};
|
use crate::{Connection, Result, NO_PARAMS};
|
||||||
|
use fallible_iterator::FallibleIterator;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_csv_module() {
|
fn test_csv_module() {
|
||||||
@ -364,7 +364,9 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ids: Result<Vec<i32>> = s
|
let ids: Result<Vec<i32>> = s
|
||||||
.query(NO_PARAMS).unwrap().map(|row| row.get::<_, i32>(0))
|
.query(NO_PARAMS)
|
||||||
|
.unwrap()
|
||||||
|
.map(|row| row.get::<_, i32>(0))
|
||||||
.collect();
|
.collect();
|
||||||
let sum = ids.unwrap().iter().sum::<i32>();
|
let sum = ids.unwrap().iter().sum::<i32>();
|
||||||
assert_eq!(sum, 15);
|
assert_eq!(sum, 15);
|
||||||
|
Loading…
Reference in New Issue
Block a user