mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 11:31:37 +08:00
Merge pull request #411 from thomcc/i128_blob
Add a feature for storing i128 as blobs.
This commit is contained in:
commit
0d08bf5e05
@ -37,6 +37,7 @@ script:
|
||||
- cargo test --features serde_json
|
||||
- cargo test --features bundled
|
||||
- cargo test --features sqlcipher
|
||||
- cargo test --features i128_blob
|
||||
- cargo test --features "unlock_notify bundled"
|
||||
- cargo test --features "array bundled csvtab vtab"
|
||||
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab"
|
||||
|
@ -32,6 +32,7 @@ bundled = ["libsqlite3-sys/bundled"]
|
||||
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
|
||||
limits = []
|
||||
hooks = []
|
||||
i128_blob = ["byteorder"]
|
||||
sqlcipher = ["libsqlite3-sys/sqlcipher"]
|
||||
unlock_notify = ["libsqlite3-sys/unlock_notify"]
|
||||
# xSavepoint, xRelease and xRollbackTo: 3.7.7 (2011-06-23)
|
||||
@ -48,6 +49,7 @@ chrono = { version = "0.4", optional = true }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
csv = { version = "1.0", optional = true }
|
||||
lazy_static = { version = "1.0", optional = true }
|
||||
byteorder = { version = "1.2", features = ["i128"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
||||
|
@ -98,6 +98,7 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
|
||||
* `vtab` for [virtual table](https://sqlite.org/vtab.html) support (allows you to write virtual table implemntations in Rust). Currently, only read-only virtual tables are supported.
|
||||
* [`csvtab`](https://sqlite.org/csv.html), CSV virtual table written in Rust.
|
||||
* [`array`](https://sqlite.org/carray.html), The `rarray()` Table-Valued Function.
|
||||
* `i128_blob` allows storing values of type `i128` type in SQLite databases. Internally, the data is stored as a 16 byte big-endian blob, with the most significant bit flipped, which allows ordering and comparison between different blobs storing i128s to work as expected.
|
||||
|
||||
## Notes on building rusqlite and libsqlite3-sys
|
||||
|
||||
|
@ -67,6 +67,9 @@ extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[cfg(feature = "i128_blob")]
|
||||
extern crate byteorder;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::convert;
|
||||
use std::default::Default;
|
||||
|
@ -161,6 +161,10 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
|
||||
///
|
||||
/// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
|
||||
/// name for this row.
|
||||
///
|
||||
/// If the result type is i128 (which requires the `i128_blob` feature to be
|
||||
/// enabled), and the underlying SQLite column is a blob whose size is not
|
||||
/// 16 bytes, `Error::InvalidColumnType` will also be returned.
|
||||
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
|
||||
let idx = try!(idx.idx(self.stmt));
|
||||
let value = self.stmt.value_ref(idx);
|
||||
@ -170,6 +174,10 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
|
||||
FromSqlError::Other(err) => {
|
||||
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
|
||||
}
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(_) => {
|
||||
Error::InvalidColumnType(idx, value.data_type())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,11 @@ pub enum FromSqlError {
|
||||
/// requested type.
|
||||
OutOfRange(i64),
|
||||
|
||||
/// Error returned when reading an `i128` from a blob with a size
|
||||
/// other than 16. Only available when the `i128_blob` feature is enabled.
|
||||
#[cfg(feature = "i128_blob")]
|
||||
InvalidI128Size(usize),
|
||||
|
||||
/// An error case available for implementors of the `FromSql` trait.
|
||||
Other(Box<Error + Send + Sync>),
|
||||
}
|
||||
@ -22,6 +27,9 @@ impl fmt::Display for FromSqlError {
|
||||
match *self {
|
||||
FromSqlError::InvalidType => write!(f, "Invalid type"),
|
||||
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(s) =>
|
||||
write!(f, "Cannot read 128bit value out of {} byte blob", s),
|
||||
FromSqlError::Other(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
@ -32,6 +40,9 @@ impl Error for FromSqlError {
|
||||
match *self {
|
||||
FromSqlError::InvalidType => "invalid type",
|
||||
FromSqlError::OutOfRange(_) => "value out of range",
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(_) =>
|
||||
"unexpected blob size for 128bit value",
|
||||
FromSqlError::Other(ref err) => err.description(),
|
||||
}
|
||||
}
|
||||
@ -41,6 +52,8 @@ impl Error for FromSqlError {
|
||||
match *self {
|
||||
FromSqlError::Other(ref err) => err.cause(),
|
||||
FromSqlError::InvalidType | FromSqlError::OutOfRange(_) => None,
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -135,6 +148,21 @@ impl FromSql for Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "i128_blob")]
|
||||
impl FromSql for i128 {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
|
||||
value.as_blob().and_then(|bytes| {
|
||||
if bytes.len() == 16 {
|
||||
Ok(BigEndian::read_i128(bytes) ^ (1i128 << 127))
|
||||
} else {
|
||||
Err(FromSqlError::InvalidI128Size(bytes.len()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromSql> FromSql for Option<T> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
match value {
|
||||
|
@ -59,6 +59,12 @@ from_value!(u32);
|
||||
from_value!(f64);
|
||||
from_value!(Vec<u8>);
|
||||
|
||||
// It would be nice if we could avoid the heap allocation (of the `Vec`) that
|
||||
// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
|
||||
// worth adding another case to Value.
|
||||
#[cfg(feature = "i128_blob")]
|
||||
from_value!(i128);
|
||||
|
||||
impl<'a> ToSql for ToSqlOutput<'a> {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(match *self {
|
||||
@ -112,6 +118,9 @@ to_sql_self!(u16);
|
||||
to_sql_self!(u32);
|
||||
to_sql_self!(f64);
|
||||
|
||||
#[cfg(feature = "i128_blob")]
|
||||
to_sql_self!(i128);
|
||||
|
||||
impl<'a, T: ?Sized> ToSql for &'a T
|
||||
where
|
||||
T: ToSql,
|
||||
@ -194,4 +203,38 @@ mod test {
|
||||
let r = cow.to_sql();
|
||||
assert!(r.is_ok());
|
||||
}
|
||||
|
||||
#[cfg(feature = "i128_blob")]
|
||||
#[test]
|
||||
fn test_i128() {
|
||||
use {Connection, NO_PARAMS};
|
||||
use std::i128;
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)").unwrap();
|
||||
db.execute("
|
||||
INSERT INTO foo(i128, desc) VALUES
|
||||
(?, 'zero'),
|
||||
(?, 'neg one'), (?, 'neg two'),
|
||||
(?, 'pos one'), (?, 'pos two'),
|
||||
(?, 'min'), (?, 'max')",
|
||||
&[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX]
|
||||
).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC").unwrap();
|
||||
|
||||
let res = stmt.query_map(
|
||||
NO_PARAMS,
|
||||
|row| (row.get::<_, i128>(0), row.get::<_, String>(1))
|
||||
).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
|
||||
|
||||
assert_eq!(res, &[
|
||||
(i128::MIN, "min".to_owned()),
|
||||
(-2, "neg two".to_owned()),
|
||||
(-1, "neg one".to_owned()),
|
||||
(0, "zero".to_owned()),
|
||||
(1, "pos one".to_owned()),
|
||||
(2, "pos two".to_owned()),
|
||||
(i128::MAX, "max".to_owned()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,18 @@ impl From<isize> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "i128_blob")]
|
||||
impl From<i128> for Value {
|
||||
fn from(i: i128) -> Value {
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
let mut buf = vec![0u8; 16];
|
||||
// We store these biased (e.g. with the most significant bit flipped)
|
||||
// so that comparisons with negative numbers work properly.
|
||||
BigEndian::write_i128(&mut buf, i ^ (1i128 << 127));
|
||||
Value::Blob(buf)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_i64(
|
||||
($t:ty) => (
|
||||
impl From<$t> for Value {
|
||||
|
Loading…
Reference in New Issue
Block a user