json_object_agg(key, value) - Aggregate key-value pairs into a JSON object
(conn: &Connection)
| 1001 | |
| 1002 | /// json_object_agg(key, value) - Aggregate key-value pairs into a JSON object |
| 1003 | fn register_json_object_agg(conn: &Connection) -> Result<()> { |
| 1004 | use rusqlite::functions::Aggregate; |
| 1005 | use std::collections::HashMap; |
| 1006 | |
| 1007 | #[derive(Default)] |
| 1008 | struct JsonObjectAgg; |
| 1009 | |
| 1010 | impl Aggregate<HashMap<String, JsonValue>, Option<String>> for JsonObjectAgg { |
| 1011 | fn init(&self, _: &mut rusqlite::functions::Context<'_>) -> Result<HashMap<String, JsonValue>> { |
| 1012 | Ok(HashMap::new()) |
| 1013 | } |
| 1014 | |
| 1015 | fn step(&self, ctx: &mut rusqlite::functions::Context<'_>, agg: &mut HashMap<String, JsonValue>) -> Result<()> { |
| 1016 | // Get the key (first argument) |
| 1017 | let key_value = ctx.get_raw(0); |
| 1018 | let key = match key_value { |
| 1019 | rusqlite::types::ValueRef::Text(s) => std::str::from_utf8(s).unwrap_or("").to_string(), |
| 1020 | rusqlite::types::ValueRef::Integer(i) => i.to_string(), |
| 1021 | rusqlite::types::ValueRef::Real(f) => f.to_string(), |
| 1022 | rusqlite::types::ValueRef::Null => "null".to_string(), |
| 1023 | rusqlite::types::ValueRef::Blob(_) => return Ok(()), // Skip blob keys |
| 1024 | }; |
| 1025 | |
| 1026 | // Get the value (second argument) |
| 1027 | let value_raw = ctx.get_raw(1); |
| 1028 | let json_value = match value_raw { |
| 1029 | rusqlite::types::ValueRef::Null => JsonValue::Null, |
| 1030 | rusqlite::types::ValueRef::Integer(i) => JsonValue::Number(serde_json::Number::from(i)), |
| 1031 | rusqlite::types::ValueRef::Real(f) => { |
| 1032 | if let Some(num) = serde_json::Number::from_f64(f) { |
| 1033 | JsonValue::Number(num) |
| 1034 | } else { |
| 1035 | JsonValue::Null |
| 1036 | } |
| 1037 | } |
| 1038 | rusqlite::types::ValueRef::Text(s) => { |
| 1039 | let text = std::str::from_utf8(s).unwrap_or(""); |
| 1040 | // For json_object_agg, treat text as literal strings (not JSON) |
| 1041 | JsonValue::String(text.to_string()) |
| 1042 | } |
| 1043 | rusqlite::types::ValueRef::Blob(b) => { |
| 1044 | // Convert blob to hex string |
| 1045 | JsonValue::String(format!("\\x{}", hex::encode(b))) |
| 1046 | } |
| 1047 | }; |
| 1048 | |
| 1049 | agg.insert(key, json_value); |
| 1050 | Ok(()) |
| 1051 | } |
| 1052 | |
| 1053 | fn finalize(&self, _: &mut rusqlite::functions::Context<'_>, agg: Option<HashMap<String, JsonValue>>) -> Result<Option<String>> { |
| 1054 | match agg { |
| 1055 | Some(map) => { |
| 1056 | let json_map: serde_json::Map<String, JsonValue> = map.into_iter().collect(); |
| 1057 | let json_object = JsonValue::Object(json_map); |
| 1058 | Ok(Some(serde_json::to_string(&json_object).unwrap_or_else(|_| "{}".to_string()))) |
| 1059 | } |
| 1060 | None => Ok(Some("{}".to_string())), // Return empty object for no rows |
no outgoing calls
no test coverage detected