Index: .efiles ================================================================== --- .efiles +++ .efiles @@ -3,5 +3,6 @@ www/index.md www/changelog.md src/lib.rs src/hashing.rs src/text.rs +src/transcode.rs Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,8 +1,8 @@ [package] name = "sqlfuncs" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "0BSD" # https://crates.io/category_slugs categories = [ "database" ] keywords = [ "sqlite", "rusqlite", "functions" ] @@ -22,10 +22,11 @@ [badges] maintenance = { status = "actively-developed" } [dependencies] digest = { version = "0.10.7" } +hex = { version = "0.4.3" } orphanage = { version = "0.1.4" } rusqlite = { version = "0.34.0", features = ["functions"] } sha2 = { version = "0.10.8" } sha3 = { version = "0.10.8" } Index: src/hashing.rs ================================================================== --- src/hashing.rs +++ src/hashing.rs @@ -4,11 +4,11 @@ //! `sha2/224`, `sha2/256`, `sha2/384`, `sha2/512`, `sha3/224`, `sha3/256`, //! `sha3/384`, `sha3/512` use digest::Digest; -use rusqlite::{functions::FunctionFlags, Connection, Error}; +use rusqlite::{Connection, Error, functions::FunctionFlags}; fn mkhasher(algo: &str) -> Result, Error> { match algo { "sha2/224" => Ok(Box::new(sha2::Sha224::new())), @@ -47,10 +47,15 @@ /// row.get(0) /// } /// ).unwrap(); /// assert_eq!(&hstr[..8], "09ca7e4e"); /// ``` +/// +/// ## SQLite function properties +/// - Deterministic +/// - Innocuous +/// - UTF8 #[allow(clippy::missing_errors_doc)] pub fn hashstr(conn: &Connection) -> Result<(), Error> { conn.create_scalar_function( "hashstr", 2, @@ -94,10 +99,15 @@ /// row.get(0) /// } /// ).unwrap(); /// assert_eq!(&hstr[..8], "09ca7e4e"); /// ``` +/// +/// ## SQLite function properties +/// - Deterministic +/// - Innocuous +/// - UTF8 #[allow(clippy::missing_errors_doc)] pub fn hashblob(conn: &Connection) -> Result<(), Error> { conn.create_scalar_function( "hashblob", 2, Index: src/lib.rs ================================================================== --- src/lib.rs +++ src/lib.rs @@ -1,6 +1,7 @@ //! Collection of scalar functions for use with SQLite through rusqlite. pub mod hashing; pub mod text; +pub mod transcode; // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: src/text.rs ================================================================== --- src/text.rs +++ src/text.rs @@ -1,10 +1,10 @@ //! A collection of functions intended to work with text strings. -use rusqlite::{functions::FunctionFlags, Connection, Error}; +use rusqlite::{Connection, Error, functions::FunctionFlags}; -use orphanage::strx::{validate_objname, RndStr}; +use orphanage::strx::{RndStr, validate_objname}; /// Add a `randomstr()` SQL function to the connection object. /// /// The SQL function `randomstr()` takes a single argument: @@ -25,10 +25,14 @@ /// id INTEGER PRIMARY KEY, /// salt TEXT NOT NULL DEFAULT (randomstr(8)) /// ); /// "#, []); /// ``` +/// +/// ## SQLite function properties +/// - Innocuous +/// - UTF8 #[allow(clippy::missing_errors_doc)] pub fn rndstr_alphanum(db: &Connection) -> Result<(), Error> { db.create_scalar_function( "randomstr", 1, @@ -64,10 +68,14 @@ /// id INTEGER PRIMARY KEY, /// salt TEXT NOT NULL DEFAULT (randomstr(8, "abcdefABCD123456")) /// ); /// "#, []); /// ``` +/// +/// ## SQLite function properties +/// - Innocuous +/// - UTF8 #[allow(clippy::missing_errors_doc)] pub fn rndstr(db: &Connection) -> Result<(), Error> { db.create_scalar_function( "randomstr", 2, @@ -102,10 +110,15 @@ /// name TEXT UNIQUE NOT NULL, /// CHECK (isobjname(name) == 1) /// ); /// "#, []); /// ``` +/// +/// ## SQLite function properties +/// - Deterministic +/// - Innocuous +/// - UTF8 /// /// # Panics /// The number of input parameters must be exactly 1. #[allow(clippy::missing_errors_doc)] pub fn isobjname(conn: &Connection) -> Result<(), Error> { ADDED src/transcode.rs Index: src/transcode.rs ================================================================== --- /dev/null +++ src/transcode.rs @@ -0,0 +1,47 @@ +//! Functions for transcoding data. + +use rusqlite::{Connection, Error, functions::FunctionFlags}; + +/// Decode a hex encoded string +/// +/// The SQL function `dehex()` takes a single argument: +/// 1. The input hex string. +/// +/// Its output will be a blob of the decoded data. +/// +/// ``` +/// use rusqlite::Connection; +/// use sqlfuncs::transcode::dehex; +/// +/// let conn = Connection::open_in_memory().unwrap(); +/// dehex(&conn).unwrap(); +/// let instr = "48656c6c6f20776f726c6421"; +/// let buf: Vec = conn.query_row_and_then( +/// "SELECT dehex(?);", [instr], |row| { +/// row.get(0) +/// }).unwrap(); +/// assert_eq!(buf, "Hello world!".to_owned().into_bytes()); +/// ``` +/// +/// ## SQLite function properties +/// - Deterministic +/// - Innocuous +/// - UTF8 +#[allow(clippy::missing_errors_doc)] +pub fn dehex(db: &Connection) -> Result<(), Error> { + db.create_scalar_function( + "dehex", + 1, + FunctionFlags::SQLITE_UTF8 + | FunctionFlags::SQLITE_INNOCUOUS + | FunctionFlags::SQLITE_DETERMINISTIC, + move |ctx| { + let hex_input = ctx.get::(0)?; + let buf = hex::decode(hex_input) + .map_err(|_| Error::UserFunctionError("Invalid hex".into()))?; + Ok(buf) + } + ) +} + +// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: www/changelog.md ================================================================== --- www/changelog.md +++ www/changelog.md @@ -3,10 +3,12 @@ ## [Unreleased] [Details](/vdiff?from=sqlfuncs-0.1.0&to=trunk) ### Added + +- Add `transcode` module with `dehex()` sql function. ### Changed ### Removed