1use serde_json::Value;
7use sqlx_core::row::Row;
8use sqlx_core::sql_str::AssertSqlSafe;
9use sqlx_postgres::{PgPool, PgRow};
10
11use crate::column::{ColumnInfo, ColumnType};
12use crate::sql_generator::{GeneratedColumn, SqlGenerator};
13use crate::view_definition::ViewDefinition;
14use crate::{Error, Result};
15
16pub struct ViewRunner {
18 pool: PgPool,
19 generator: SqlGenerator,
20}
21
22impl ViewRunner {
23 pub fn new(pool: PgPool) -> Self {
25 Self {
26 pool,
27 generator: SqlGenerator::new(),
28 }
29 }
30
31 pub fn with_generator(pool: PgPool, generator: SqlGenerator) -> Self {
33 Self { pool, generator }
34 }
35
36 pub async fn run(&self, view: &ViewDefinition) -> Result<ViewResult> {
42 let generated = self.generator.generate(view)?;
43
44 tracing::debug!(sql = %generated.sql, "Executing view");
45
46 let rows = sqlx_core::query::query(AssertSqlSafe(generated.sql.clone()))
47 .fetch_all(&self.pool)
48 .await
49 .map_err(Error::Sql)?;
50
51 let columns: Vec<ColumnInfo> = generated
52 .columns
53 .iter()
54 .map(|c| ColumnInfo::new(c.alias.clone(), c.col_type))
55 .collect();
56
57 let data: Vec<Vec<Value>> = rows
58 .iter()
59 .map(|row| extract_row_values(row, &generated.columns))
60 .collect();
61
62 Ok(ViewResult {
63 columns,
64 data,
65 row_count: rows.len(),
66 })
67 }
68
69 pub fn explain(&self, view: &ViewDefinition) -> Result<String> {
73 let generated = self.generator.generate(view)?;
74 Ok(generated.sql)
75 }
76
77 pub fn pool(&self) -> &PgPool {
79 &self.pool
80 }
81}
82
83#[async_trait::async_trait]
92pub trait SqlExecutor {
93 async fn fetch(&self, sql: &str, columns: &[GeneratedColumn]) -> Result<Vec<Vec<Value>>>;
95}
96
97pub async fn run_with<E: SqlExecutor + ?Sized>(
106 executor: &E,
107 generator: &SqlGenerator,
108 view: &ViewDefinition,
109) -> Result<ViewResult> {
110 let generated = generator.generate(view)?;
111 let data = executor.fetch(&generated.sql, &generated.columns).await?;
112 let columns: Vec<ColumnInfo> = generated
113 .columns
114 .iter()
115 .map(|c| ColumnInfo::new(c.alias.clone(), c.col_type))
116 .collect();
117 let row_count = data.len();
118 Ok(ViewResult {
119 columns,
120 data,
121 row_count,
122 })
123}
124
125fn extract_row_values(row: &PgRow, columns: &[GeneratedColumn]) -> Vec<Value> {
127 columns
128 .iter()
129 .enumerate()
130 .map(|(i, col)| extract_column_value(row, i, &col.col_type))
131 .collect()
132}
133
134fn extract_column_value(row: &PgRow, index: usize, col_type: &ColumnType) -> Value {
136 match col_type {
137 ColumnType::Integer | ColumnType::Integer64 => row
138 .try_get::<Option<i64>, _>(index)
139 .ok()
140 .flatten()
141 .map(|v| Value::Number(v.into()))
142 .unwrap_or(Value::Null),
143
144 ColumnType::Decimal => row
145 .try_get::<Option<f64>, _>(index)
146 .ok()
147 .flatten()
148 .and_then(|v| serde_json::Number::from_f64(v).map(Value::Number))
149 .unwrap_or(Value::Null),
150
151 ColumnType::Boolean => row
152 .try_get::<Option<bool>, _>(index)
153 .ok()
154 .flatten()
155 .map(Value::Bool)
156 .unwrap_or(Value::Null),
157
158 ColumnType::Json => row
159 .try_get::<Option<Value>, _>(index)
160 .ok()
161 .flatten()
162 .unwrap_or(Value::Null),
163
164 ColumnType::String
166 | ColumnType::Date
167 | ColumnType::DateTime
168 | ColumnType::Instant
169 | ColumnType::Time
170 | ColumnType::Base64Binary => row
171 .try_get::<Option<String>, _>(index)
172 .ok()
173 .flatten()
174 .map(Value::String)
175 .unwrap_or(Value::Null),
176 }
177}
178
179#[derive(Debug, Clone)]
181pub struct ViewResult {
182 pub columns: Vec<ColumnInfo>,
184
185 pub data: Vec<Vec<Value>>,
187
188 pub row_count: usize,
190}
191
192impl ViewResult {
193 pub fn is_empty(&self) -> bool {
195 self.data.is_empty()
196 }
197
198 pub fn column_count(&self) -> usize {
200 self.columns.len()
201 }
202
203 pub fn to_json_array(&self) -> Vec<Value> {
205 self.data
206 .iter()
207 .map(|row| {
208 let mut obj = serde_json::Map::new();
209 for (i, col) in self.columns.iter().enumerate() {
210 if let Some(value) = row.get(i) {
211 obj.insert(col.name.clone(), value.clone());
212 }
213 }
214 Value::Object(obj)
215 })
216 .collect()
217 }
218
219 pub fn row_as_object(&self, index: usize) -> Option<Value> {
221 self.data.get(index).map(|row| {
222 let mut obj = serde_json::Map::new();
223 for (i, col) in self.columns.iter().enumerate() {
224 if let Some(value) = row.get(i) {
225 obj.insert(col.name.clone(), value.clone());
226 }
227 }
228 Value::Object(obj)
229 })
230 }
231
232 pub fn column_values(&self, name: &str) -> Option<Vec<&Value>> {
234 let col_index = self.columns.iter().position(|c| c.name == name)?;
235 Some(
236 self.data
237 .iter()
238 .filter_map(|row| row.get(col_index))
239 .collect(),
240 )
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 use serde_json::json;
248
249 #[test]
250 fn test_view_result_to_json_array() {
251 let result = ViewResult {
252 columns: vec![
253 ColumnInfo::new("id", ColumnType::String),
254 ColumnInfo::new("name", ColumnType::String),
255 ],
256 data: vec![
257 vec![json!("1"), json!("Alice")],
258 vec![json!("2"), json!("Bob")],
259 ],
260 row_count: 2,
261 };
262
263 let json_array = result.to_json_array();
264 assert_eq!(json_array.len(), 2);
265 assert_eq!(json_array[0]["id"], "1");
266 assert_eq!(json_array[0]["name"], "Alice");
267 assert_eq!(json_array[1]["id"], "2");
268 assert_eq!(json_array[1]["name"], "Bob");
269 }
270
271 #[test]
272 fn test_view_result_row_as_object() {
273 let result = ViewResult {
274 columns: vec![
275 ColumnInfo::new("id", ColumnType::String),
276 ColumnInfo::new("active", ColumnType::Boolean),
277 ],
278 data: vec![vec![json!("123"), json!(true)]],
279 row_count: 1,
280 };
281
282 let row = result.row_as_object(0).unwrap();
283 assert_eq!(row["id"], "123");
284 assert_eq!(row["active"], true);
285
286 assert!(result.row_as_object(1).is_none());
287 }
288
289 #[test]
290 fn test_view_result_column_values() {
291 let result = ViewResult {
292 columns: vec![
293 ColumnInfo::new("id", ColumnType::String),
294 ColumnInfo::new("value", ColumnType::Integer),
295 ],
296 data: vec![
297 vec![json!("1"), json!(10)],
298 vec![json!("2"), json!(20)],
299 vec![json!("3"), json!(30)],
300 ],
301 row_count: 3,
302 };
303
304 let ids = result.column_values("id").unwrap();
305 assert_eq!(ids.len(), 3);
306 assert_eq!(*ids[0], json!("1"));
307
308 let values = result.column_values("value").unwrap();
309 assert_eq!(values.len(), 3);
310 assert_eq!(*values[0], json!(10));
311
312 assert!(result.column_values("nonexistent").is_none());
313 }
314
315 #[test]
316 fn test_view_result_is_empty() {
317 let empty_result = ViewResult {
318 columns: vec![ColumnInfo::new("id", ColumnType::String)],
319 data: vec![],
320 row_count: 0,
321 };
322 assert!(empty_result.is_empty());
323
324 let non_empty = ViewResult {
325 columns: vec![ColumnInfo::new("id", ColumnType::String)],
326 data: vec![vec![json!("1")]],
327 row_count: 1,
328 };
329 assert!(!non_empty.is_empty());
330 }
331}