Helper function to get a numeric value from context, handling both numeric and text inputs
(ctx: &Context<'_>, idx: usize)
| 4 | |
| 5 | /// Helper function to get a numeric value from context, handling both numeric and text inputs |
| 6 | fn get_numeric_value(ctx: &Context<'_>, idx: usize) -> Result<f64> { |
| 7 | match ctx.get_raw(idx) { |
| 8 | rusqlite::types::ValueRef::Real(f) => Ok(f), |
| 9 | rusqlite::types::ValueRef::Integer(i) => Ok(i as f64), |
| 10 | rusqlite::types::ValueRef::Text(s) => { |
| 11 | let text = std::str::from_utf8(s).map_err(|e| rusqlite::Error::UserFunctionError(Box::new(e)))?; |
| 12 | text.trim().parse::<f64>() |
| 13 | .map_err(|e| rusqlite::Error::UserFunctionError(format!("Failed to parse '{text}' as number: {e}").into())) |
| 14 | } |
| 15 | rusqlite::types::ValueRef::Null => { |
| 16 | // For NULL input, we should propagate it (most SQL functions return NULL for NULL input) |
| 17 | Err(rusqlite::Error::UserFunctionError("NULL input".into())) |
| 18 | } |
| 19 | rusqlite::types::ValueRef::Blob(b) => { |
| 20 | // Decimal values are stored as 16-byte blobs |
| 21 | if b.len() == 16 { |
| 22 | let mut array = [0u8; 16]; |
| 23 | array.copy_from_slice(b); |
| 24 | let decimal = Decimal::deserialize(array); |
| 25 | // Convert to f64 - note this may lose precision for very large decimals |
| 26 | use std::str::FromStr; |
| 27 | let decimal_str = decimal.to_string(); |
| 28 | f64::from_str(&decimal_str) |
| 29 | .map_err(|e| rusqlite::Error::UserFunctionError( |
| 30 | format!("Failed to convert decimal to f64: {e}").into() |
| 31 | )) |
| 32 | } else { |
| 33 | // Try to parse blob as UTF-8 text as fallback |
| 34 | match std::str::from_utf8(b) { |
| 35 | Ok(text) => { |
| 36 | text.trim().parse::<f64>() |
| 37 | .map_err(|e| rusqlite::Error::UserFunctionError( |
| 38 | format!("Failed to parse blob as number: {e}").into() |
| 39 | )) |
| 40 | } |
| 41 | Err(_) => { |
| 42 | let len = b.len(); |
| 43 | Err(rusqlite::Error::UserFunctionError( |
| 44 | format!("Invalid blob size for numeric function: {len} bytes").into() |
| 45 | )) |
| 46 | } |
| 47 | } |
| 48 | } |
| 49 | } |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | /// Register all PostgreSQL math functions |
| 54 | pub fn register_math_functions(conn: &Connection) -> Result<()> { |
no test coverage detected