mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 09:09:19 +08:00
Merge pull request #534 from gwenn/collation
Add binding to `sqlite3_create_collation_v2`
This commit is contained in:
commit
4988715932
@ -30,6 +30,7 @@ script:
|
|||||||
- cargo test
|
- cargo test
|
||||||
- cargo test --features backup
|
- cargo test --features backup
|
||||||
- cargo test --features blob
|
- cargo test --features blob
|
||||||
|
- cargo test --features collation
|
||||||
- cargo test --features functions
|
- cargo test --features functions
|
||||||
- cargo test --features hooks
|
- cargo test --features hooks
|
||||||
- cargo test --features limits
|
- cargo test --features limits
|
||||||
@ -44,7 +45,7 @@ script:
|
|||||||
- cargo test --features uuid
|
- cargo test --features uuid
|
||||||
- cargo test --features "unlock_notify bundled"
|
- cargo test --features "unlock_notify bundled"
|
||||||
- cargo test --features "array bundled csvtab vtab"
|
- cargo test --features "array bundled csvtab vtab"
|
||||||
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace url uuid vtab"
|
- cargo test --features "backup blob chrono collation csvtab functions hooks limits load_extension serde_json trace url uuid vtab"
|
||||||
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace url uuid vtab buildtime_bindgen"
|
- cargo test --features "backup blob chrono collation csvtab functions hooks limits load_extension serde_json trace url uuid vtab buildtime_bindgen"
|
||||||
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace url uuid vtab bundled"
|
- cargo test --features "backup blob chrono collation csvtab functions hooks limits load_extension serde_json trace url uuid vtab bundled"
|
||||||
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace url uuid vtab bundled buildtime_bindgen"
|
- cargo test --features "backup blob chrono collation csvtab functions hooks limits load_extension serde_json trace url uuid vtab bundled buildtime_bindgen"
|
||||||
|
@ -28,6 +28,7 @@ load_extension = []
|
|||||||
backup = ["libsqlite3-sys/min_sqlite_version_3_6_23"]
|
backup = ["libsqlite3-sys/min_sqlite_version_3_6_23"]
|
||||||
# sqlite3_blob_reopen: 3.7.4
|
# sqlite3_blob_reopen: 3.7.4
|
||||||
blob = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
|
blob = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
|
||||||
|
collation = []
|
||||||
# sqlite3_create_function_v2: 3.7.3 (2010-10-08)
|
# sqlite3_create_function_v2: 3.7.3 (2010-10-08)
|
||||||
functions = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
|
functions = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
|
||||||
# sqlite3_log: 3.6.23 (2010-03-09)
|
# sqlite3_log: 3.6.23 (2010-03-09)
|
||||||
@ -69,6 +70,7 @@ tempdir = "0.3"
|
|||||||
lazy_static = "1.0"
|
lazy_static = "1.0"
|
||||||
regex = "1.0"
|
regex = "1.0"
|
||||||
uuid = { version = "0.7", features = ["v4"] }
|
uuid = { version = "0.7", features = ["v4"] }
|
||||||
|
unicase = "2.4.0"
|
||||||
|
|
||||||
[dependencies.libsqlite3-sys]
|
[dependencies.libsqlite3-sys]
|
||||||
path = "libsqlite3-sys"
|
path = "libsqlite3-sys"
|
||||||
|
@ -33,7 +33,7 @@ build: false
|
|||||||
test_script:
|
test_script:
|
||||||
- cargo test --lib --verbose
|
- cargo test --lib --verbose
|
||||||
- cargo test --lib --verbose --features bundled
|
- cargo test --lib --verbose --features bundled
|
||||||
- cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace"
|
- cargo test --lib --features "backup blob chrono collation functions hooks limits load_extension serde_json trace"
|
||||||
- cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace buildtime_bindgen"
|
- cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace buildtime_bindgen"
|
||||||
- cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled"
|
- cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled"
|
||||||
- cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled buildtime_bindgen"
|
- cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled buildtime_bindgen"
|
||||||
|
140
src/collation.rs
Normal file
140
src/collation.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
//! Add, remove, or modify a collation
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::os::raw::{c_int, c_void};
|
||||||
|
use std::panic::{catch_unwind, UnwindSafe};
|
||||||
|
use std::ptr;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
use crate::ffi;
|
||||||
|
use crate::{str_to_cstring, Connection, InnerConnection, Result};
|
||||||
|
|
||||||
|
// TODO sqlite3_collation_needed https://sqlite.org/c3ref/collation_needed.html
|
||||||
|
|
||||||
|
// FIXME copy/paste from function.rs
|
||||||
|
unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
|
||||||
|
drop(Box::from_raw(p as *mut T));
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connection {
|
||||||
|
/// Add or modify a collation.
|
||||||
|
pub fn create_collation<C>(&self, collation_name: &str, x_compare: C) -> Result<()>
|
||||||
|
where
|
||||||
|
C: Fn(&str, &str) -> Ordering + Send + UnwindSafe + 'static,
|
||||||
|
{
|
||||||
|
self.db
|
||||||
|
.borrow_mut()
|
||||||
|
.create_collation(collation_name, x_compare)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove collation.
|
||||||
|
pub fn remove_collation(&self, collation_name: &str) -> Result<()> {
|
||||||
|
self.db.borrow_mut().remove_collation(collation_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InnerConnection {
|
||||||
|
fn create_collation<C>(&mut self, collation_name: &str, x_compare: C) -> Result<()>
|
||||||
|
where
|
||||||
|
C: Fn(&str, &str) -> Ordering + Send + UnwindSafe + 'static,
|
||||||
|
{
|
||||||
|
unsafe extern "C" fn call_boxed_closure<F>(
|
||||||
|
arg1: *mut c_void,
|
||||||
|
arg2: c_int,
|
||||||
|
arg3: *const c_void,
|
||||||
|
arg4: c_int,
|
||||||
|
arg5: *const c_void,
|
||||||
|
) -> c_int
|
||||||
|
where
|
||||||
|
F: Fn(&str, &str) -> Ordering,
|
||||||
|
{
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
let r = catch_unwind(|| {
|
||||||
|
let boxed_f: *mut F = arg1 as *mut F;
|
||||||
|
assert!(!boxed_f.is_null(), "Internal error - null function pointer");
|
||||||
|
let s1 = {
|
||||||
|
let c_slice = slice::from_raw_parts(arg3 as *const u8, arg2 as usize);
|
||||||
|
str::from_utf8_unchecked(c_slice)
|
||||||
|
};
|
||||||
|
let s2 = {
|
||||||
|
let c_slice = slice::from_raw_parts(arg5 as *const u8, arg4 as usize);
|
||||||
|
str::from_utf8_unchecked(c_slice)
|
||||||
|
};
|
||||||
|
(*boxed_f)(s1, s2)
|
||||||
|
});
|
||||||
|
let t = match r {
|
||||||
|
Err(_) => {
|
||||||
|
return -1; // FIXME How ?
|
||||||
|
}
|
||||||
|
Ok(r) => r,
|
||||||
|
};
|
||||||
|
|
||||||
|
match t {
|
||||||
|
Ordering::Less => -1,
|
||||||
|
Ordering::Equal => 0,
|
||||||
|
Ordering::Greater => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let boxed_f: *mut C = Box::into_raw(Box::new(x_compare));
|
||||||
|
let c_name = str_to_cstring(collation_name)?;
|
||||||
|
let flags = ffi::SQLITE_UTF8;
|
||||||
|
let r = unsafe {
|
||||||
|
ffi::sqlite3_create_collation_v2(
|
||||||
|
self.db(),
|
||||||
|
c_name.as_ptr(),
|
||||||
|
flags,
|
||||||
|
boxed_f as *mut c_void,
|
||||||
|
Some(call_boxed_closure::<C>),
|
||||||
|
Some(free_boxed_value::<C>),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
self.decode_result(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_collation(&mut self, collation_name: &str) -> Result<()> {
|
||||||
|
let c_name = str_to_cstring(collation_name)?;
|
||||||
|
let r = unsafe {
|
||||||
|
ffi::sqlite3_create_collation_v2(
|
||||||
|
self.db(),
|
||||||
|
c_name.as_ptr(),
|
||||||
|
ffi::SQLITE_UTF8,
|
||||||
|
ptr::null_mut(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
self.decode_result(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{Connection, NO_PARAMS};
|
||||||
|
use fallible_streaming_iterator::FallibleStreamingIterator;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use unicase::UniCase;
|
||||||
|
|
||||||
|
fn unicase_compare(s1: &str, s2: &str) -> Ordering {
|
||||||
|
UniCase::new(s1).cmp(&UniCase::new(s2))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unicase() {
|
||||||
|
let db = Connection::open_in_memory().unwrap();
|
||||||
|
|
||||||
|
db.create_collation("unicase", unicase_compare).unwrap();
|
||||||
|
|
||||||
|
db.execute_batch(
|
||||||
|
"CREATE TABLE foo (bar);
|
||||||
|
INSERT INTO foo (bar) VALUES ('Maße');
|
||||||
|
INSERT INTO foo (bar) VALUES ('MASSE');",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut stmt = db
|
||||||
|
.prepare("SELECT DISTINCT bar COLLATE unicase FROM foo ORDER BY 1")
|
||||||
|
.unwrap();
|
||||||
|
let rows = stmt.query(NO_PARAMS).unwrap();
|
||||||
|
assert_eq!(rows.count().unwrap(), 1);
|
||||||
|
}
|
||||||
|
}
|
@ -105,6 +105,8 @@ pub mod backup;
|
|||||||
pub mod blob;
|
pub mod blob;
|
||||||
mod busy;
|
mod busy;
|
||||||
mod cache;
|
mod cache;
|
||||||
|
#[cfg(feature = "collation")]
|
||||||
|
mod collation;
|
||||||
mod column;
|
mod column;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
#[cfg(any(feature = "functions", feature = "vtab"))]
|
#[cfg(any(feature = "functions", feature = "vtab"))]
|
||||||
|
Loading…
Reference in New Issue
Block a user