jsonb_object_agg(key, value) - Aggregate key-value pairs into a JSON object
(conn: &Connection)
| 1074 | |
| 1075 | /// jsonb_object_agg(key, value) - Aggregate key-value pairs into a JSON object |
| 1076 | fn register_jsonb_object_agg(conn: &Connection) -> Result<()> { |
| 1077 | use rusqlite::functions::Aggregate; |
| 1078 | use std::collections::HashMap; |
| 1079 | |
| 1080 | #[derive(Default)] |
| 1081 | struct JsonbObjectAgg; |
| 1082 | |
| 1083 | impl Aggregate<HashMap<String, JsonValue>, Option<String>> for JsonbObjectAgg { |
| 1084 | fn init(&self, _: &mut rusqlite::functions::Context<'_>) -> Result<HashMap<String, JsonValue>> { |
| 1085 | Ok(HashMap::new()) |
| 1086 | } |
| 1087 | |
| 1088 | fn step(&self, ctx: &mut rusqlite::functions::Context<'_>, agg: &mut HashMap<String, JsonValue>) -> Result<()> { |
| 1089 | // Get the key (first argument) |
| 1090 | let key_value = ctx.get_raw(0); |
| 1091 | let key = match key_value { |
| 1092 | rusqlite::types::ValueRef::Text(s) => std::str::from_utf8(s).unwrap_or("").to_string(), |
| 1093 | rusqlite::types::ValueRef::Integer(i) => i.to_string(), |
| 1094 | rusqlite::types::ValueRef::Real(f) => f.to_string(), |
| 1095 | rusqlite::types::ValueRef::Null => "null".to_string(), |
| 1096 | rusqlite::types::ValueRef::Blob(_) => return Ok(()), // Skip blob keys |
| 1097 | }; |
| 1098 | |
| 1099 | // Get the value (second argument) |
| 1100 | let value_raw = ctx.get_raw(1); |
| 1101 | let json_value = match value_raw { |
| 1102 | rusqlite::types::ValueRef::Null => JsonValue::Null, |
| 1103 | rusqlite::types::ValueRef::Integer(i) => JsonValue::Number(serde_json::Number::from(i)), |
| 1104 | rusqlite::types::ValueRef::Real(f) => { |
| 1105 | if let Some(num) = serde_json::Number::from_f64(f) { |
| 1106 | JsonValue::Number(num) |
| 1107 | } else { |
| 1108 | JsonValue::Null |
| 1109 | } |
| 1110 | } |
| 1111 | rusqlite::types::ValueRef::Text(s) => { |
| 1112 | let text = std::str::from_utf8(s).unwrap_or(""); |
| 1113 | // For jsonb_object_agg, try to parse as JSON first, if it fails treat as string |
| 1114 | serde_json::from_str(text) |
| 1115 | .unwrap_or_else(|_| JsonValue::String(text.to_string())) |
| 1116 | } |
| 1117 | rusqlite::types::ValueRef::Blob(b) => { |
| 1118 | // Convert blob to hex string |
| 1119 | JsonValue::String(format!("\\x{}", hex::encode(b))) |
| 1120 | } |
| 1121 | }; |
| 1122 | |
| 1123 | agg.insert(key, json_value); |
| 1124 | Ok(()) |
| 1125 | } |
| 1126 | |
| 1127 | fn finalize(&self, _: &mut rusqlite::functions::Context<'_>, agg: Option<HashMap<String, JsonValue>>) -> Result<Option<String>> { |
| 1128 | match agg { |
| 1129 | Some(map) => { |
| 1130 | let json_map: serde_json::Map<String, JsonValue> = map.into_iter().collect(); |
| 1131 | let json_object = JsonValue::Object(json_map); |
| 1132 | Ok(Some(serde_json::to_string(&json_object).unwrap_or_else(|_| "{}".to_string()))) |
| 1133 | } |
no outgoing calls
no test coverage detected