1use std::sync::Arc;
2
3use vantage_core::{Result, error};
4use vantage_expressions::traits::selectable::Selectable;
5use vantage_expressions::{Expression, Expressive, SelectableDataSource, expr_any};
6use vantage_types::{EmptyEntity, Entity};
7
8use crate::{
9 column::core::ColumnType,
10 source::{SelectSeed, SelectSource},
11 table::Table,
12 traits::column_like::ColumnLike,
13 traits::table_source::TableSource,
14};
15
16impl<T, E> Table<T, E>
17where
18 T: SelectableDataSource<T::Value, T::Condition> + TableSource,
19 T::Source: SelectSeed<T::Select, T::Value, T::Condition>,
20 T::Value: From<String>, E: Entity<T::Value>,
22{
23 pub fn select_empty(&self) -> T::Select {
27 let mut select = self.data_source.select();
28 self.source.seed(&mut select);
29
30 for condition in self.conditions.values() {
31 select.add_where_condition(condition.clone());
32 }
33
34 for (expr, direction) in self.order_by.values() {
35 let order = match direction {
36 crate::sorting::SortDirection::Ascending => vantage_expressions::Order::Asc,
37 crate::sorting::SortDirection::Descending => vantage_expressions::Order::Desc,
38 };
39 select.add_order_by(expr.clone(), order);
40 }
41
42 if let Some(pagination) = &self.pagination {
43 select.set_limit(Some(pagination.limit()), Some(pagination.skip()));
44 }
45
46 select
47 }
48
49 pub fn select(&self) -> T::Select {
51 let mut select = self.select_empty();
52
53 for column in self.columns.values() {
55 if self.lazy_expressions.contains_key(column.name()) {
60 continue;
61 }
62 if !self.is_active(column.name()) {
65 continue;
66 }
67 if let Some(expr_fn) = self.expressions.get(column.name()) {
68 let expr = expr_fn(self.as_entity_erased());
69 self.data_source.add_select_column(
70 &mut select,
71 expr_any!("({})", (expr)),
72 Some(column.name()),
73 );
74 } else if let Some(alias) = column.alias() {
75 let expr = self.data_source.expr(column.name(), vec![]);
76 self.data_source
77 .add_select_column(&mut select, expr, Some(alias));
78 } else {
79 select.add_field(column.name());
80 }
81 }
82
83 for (name, expr_fn) in &self.expressions {
85 if !self.columns.contains_key(name) && self.is_active(name) {
86 let expr = expr_fn(self.as_entity_erased());
87 self.data_source.add_select_column(
88 &mut select,
89 expr_any!("({})", (expr)),
90 Some(name),
91 );
92 }
93 }
94
95 select
96 }
97 pub async fn get_count(&self) -> Result<i64> {
99 self.data_source.get_table_count(self).await
100 }
101
102 pub async fn get_sum(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
104 self.data_source.get_table_sum(self, column).await
105 }
106
107 pub async fn get_max(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
109 self.data_source.get_table_max(self, column).await
110 }
111
112 pub async fn get_min(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
114 self.data_source.get_table_min(self, column).await
115 }
116
117 pub fn get_count_query(&self) -> Expression<T::Value> {
120 expr_any!("({})", (self.select_empty().as_count()))
121 }
122
123 pub fn get_sum_query<Type>(&self, column: &T::Column<Type>) -> Expression<T::Value>
126 where
127 Type: ColumnType,
128 T::Column<Type>: Expressive<T::Value>,
129 {
130 expr_any!("({})", (self.select_empty().as_sum(column.expr())))
131 }
132
133 pub fn select_column(&self, field: &str) -> Option<Expression<T::Value>>
148 where
149 T::Column<T::AnyType>: Expressive<T::Value>,
150 T::Select: Expressive<T::Value>,
151 {
152 Some(self.select_expression(self.get_column_expr(field)?))
153 }
154
155 pub fn select_expression(&self, expr: Expression<T::Value>) -> Expression<T::Value>
162 where
163 T::Select: Expressive<T::Value>,
164 {
165 let mut select = self.select_empty();
166 select.clear_fields();
167 select.clear_order_by();
168 select.add_expression(expr);
169 select.expr()
170 }
171
172 fn is_active(&self, name: &str) -> bool {
176 match &self.active_columns {
177 None => true,
178 Some(set) => set.contains(name) || self.id_field.as_deref() == Some(name),
179 }
180 }
181
182 pub fn with_active_columns(mut self, cols: &[&str]) -> Result<Self>
206 where
207 T: 'static,
208 E: 'static,
209 T::Column<T::AnyType>: Expressive<T::Value>,
210 T::Select: Expressive<T::Value>,
211 {
212 for &col in cols {
213 let parts: Vec<&str> = col.split('.').collect();
214 if parts.iter().any(|p| p.is_empty()) {
215 return Err(error!("invalid active column name", column = col));
216 }
217
218 if parts.len() >= 2 {
219 self.import_dotted_column(col)?;
220 self.active_columns
221 .get_or_insert_with(Default::default)
222 .insert(col.to_string());
223 } else {
224 if !self.columns.contains_key(col) && !self.expressions.contains_key(col) {
228 return Err(error!("unknown active column", column = col));
229 }
230 self.active_columns
231 .get_or_insert_with(Default::default)
232 .insert(col.to_string());
233 }
234 }
235 Ok(self)
236 }
237
238 pub fn with_imported_column(mut self, col: &str) -> Result<Self>
244 where
245 T: 'static,
246 E: 'static,
247 T::Column<T::AnyType>: Expressive<T::Value>,
248 T::Select: Expressive<T::Value>,
249 {
250 self.import_dotted_column(col)?;
251 Ok(self)
252 }
253
254 pub fn add_imported_column(&mut self, col: &str) -> Result<()>
256 where
257 T: 'static,
258 E: 'static,
259 T::Column<T::AnyType>: Expressive<T::Value>,
260 T::Select: Expressive<T::Value>,
261 {
262 self.import_dotted_column(col)
263 }
264
265 fn import_dotted_column(&mut self, col: &str) -> Result<()>
271 where
272 T: 'static,
273 E: 'static,
274 T::Column<T::AnyType>: Expressive<T::Value>,
275 T::Select: Expressive<T::Value>,
276 {
277 let parts: Vec<&str> = col.split('.').collect();
278 if parts.len() < 2 || parts.iter().any(|p| p.is_empty()) {
279 return Err(error!("invalid dotted column name", column = col));
280 }
281 let column = parts[parts.len() - 1];
282 let hops = &parts[..parts.len() - 1];
283
284 if !self.data_source().supports_traversal() {
285 return Err(error!(
286 "backend does not support implicit-reference traversal in columns",
287 column = col
288 ));
289 }
290
291 let (target, fk_hops) = self.resolve_has_one_target(hops)?;
293 if !target.columns().contains_key(column) {
294 return Err(error!(
295 "implicit reference target has no such column",
296 column = column
297 ));
298 }
299
300 let fk_refs: Vec<&str> = fk_hops.iter().map(String::as_str).collect();
308 let expr = match self.data_source().traversal_path_expr(&fk_refs, column) {
309 Some(e) => e,
310 None => self.traverse_rest_generic(hops, column)?,
311 };
312
313 let dotted = col.to_string();
314 if !self.columns.contains_key(&dotted) {
315 let column_def = self.data_source.create_column::<T::AnyType>(&dotted);
316 self.add_column(column_def);
317 }
318 let wrapped: crate::table::base::ExpressionFn<T> = Arc::new(move |_| expr.clone());
319 self.expressions.insert(dotted.clone(), wrapped);
320 self.imported_columns.insert(dotted.clone());
321 Ok(())
322 }
323
324 fn traverse_rest_generic(&self, hops: &[&str], column: &str) -> Result<Expression<T::Value>>
330 where
331 T: 'static,
332 E: 'static,
333 T::Column<T::AnyType>: Expressive<T::Value>,
334 T::Select: Expressive<T::Value>,
335 {
336 match hops.split_first() {
337 None => self.get_column_expr(column).ok_or_else(|| {
338 error!(
339 "implicit reference target has no such column",
340 column = column
341 )
342 }),
343 Some((head, tail)) => {
344 let target: Table<T, EmptyEntity> = self.get_subquery_erased(head)?;
345 let inner = target.traverse_rest_generic(tail, column)?;
346 let inner = if tail.is_empty() {
350 inner
351 } else {
352 expr_any!("({})", (inner))
353 };
354 Ok(target.select_expression(inner))
355 }
356 }
357 }
358
359 fn resolve_has_one_target(&self, hops: &[&str]) -> Result<(Table<T, EmptyEntity>, Vec<String>)>
365 where
366 T: 'static,
367 E: 'static,
368 {
369 let (head, tail) = hops
370 .split_first()
371 .ok_or_else(|| error!("empty implicit reference path"))?;
372 if self.ref_cardinality(head)? != vantage_vista::ReferenceKind::HasOne {
373 return Err(error!(
374 "implicit reference hop must traverse a has_one relation",
375 relation = *head
376 ));
377 }
378 let fk = self.ref_foreign_key(head)?;
379 let target: Table<T, EmptyEntity> = self.get_ref_target_erased(head)?;
380 if tail.is_empty() {
381 Ok((target, vec![fk]))
382 } else {
383 let (final_target, mut fks) = target.resolve_has_one_target(tail)?;
384 fks.insert(0, fk);
385 Ok((final_target, fks))
386 }
387 }
388}
389
390impl<T, E, V, C, S> Table<T, E>
395where
396 T: SelectableDataSource<V, C, Select = S>
397 + TableSource<Value = V, Condition = C, Source = SelectSource<S>>,
398 V: Clone + Send + Sync + 'static + From<String>,
399 C: Clone + Send + Sync + 'static,
400 S: Expressive<V> + Clone,
401 E: Entity<V>,
402{
403 pub fn from_select(data_source: T, alias: impl Into<String>, select: S) -> Self {
406 let alias = alias.into();
407 let mut table = Table::new(alias.clone(), data_source);
408 table.source = SelectSource::query(select, alias);
409 table
410 }
411
412 pub fn derive_from<E2: Entity<V> + 'static>(
426 source: &Table<T, E2>,
427 alias: impl Into<String>,
428 modifier: impl FnOnce(S) -> S,
429 columns: &[&str],
430 relations: &[&str],
431 ) -> Self
432 where
433 T: 'static,
434 E: 'static,
435 {
436 let alias = alias.into();
437 let select = modifier(source.select());
438 let mut table = Table::new(alias.clone(), source.data_source().clone());
439 table.source = SelectSource::query(select, alias);
440 table.copy_columns_from(source, Some(columns));
441 table.copy_relations_from(source, Some(relations));
442 table.id_field = source.id_field.clone();
443 table.title_field = source.title_field.clone();
444 table.title_fields = source.title_fields.clone();
445 table
446 }
447}
448
449impl<T, E> Table<T, E>
451where
452 T: SelectableDataSource<serde_json::Value, T::Condition>
453 + TableSource<Value = serde_json::Value>
454 + vantage_expressions::traits::datasource::ExprDataSource<serde_json::Value>,
455 T::Source: SelectSeed<T::Select, serde_json::Value, T::Condition>,
456 T::Value: From<String>,
457 E: Entity<serde_json::Value>,
458{
459 pub async fn get_count_via_query(&self) -> Result<i64> {
461 let count_query = self.get_count_query();
462 let result = self.data_source.execute(&count_query).await?;
463
464 let result = match result.as_array().map(Vec::as_slice) {
467 Some([single]) => single,
468 _ => &result,
469 };
470
471 if let Some(count) = result.get("count").and_then(|v| v.as_i64()) {
475 Ok(count)
476 } else if let Some(count) = result.as_i64() {
477 Ok(count)
478 } else {
479 Err(vantage_core::util::error::vantage_error!(
480 "count query returned an unexpected result shape: {result}"
481 ))
482 }
483 }
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489 use crate::mocks::mock_table_source::MockTableSource;
490 use serde_json::json;
491 use vantage_expressions::mocks::datasource::MockSelectableDataSource;
492 use vantage_expressions::traits::datasource::ExprDataSource;
493
494 #[tokio::test]
495 async fn test_selectable_functionality() {
496 let mock_select_source = MockSelectableDataSource::new(json!([
497 {"id": "1", "name": "Alice", "age": 30},
498 {"id": "2", "name": "Bob", "age": 25}
499 ]));
500
501 let mock_query_source = vantage_expressions::mocks::mock_builder::new()
502 .on_exact_select("(SELECT COUNT(*) FROM \"users\")", json!(42));
503
504 let table = MockTableSource::new()
505 .with_data(
506 "users",
507 vec![
508 json!({"id": "1", "name": "Alice", "age": 30}),
509 json!({"id": "2", "name": "Bob", "age": 25}),
510 ],
511 )
512 .await
513 .with_select_source(mock_select_source)
514 .with_query_source(mock_query_source);
515 let table = Table::<_, vantage_types::EmptyEntity>::new("users", table);
516
517 let select = table.select();
519 assert_eq!(select.source(), Some("users"));
520
521 let query_expr: vantage_expressions::Expression<serde_json::Value> = select.into();
523 assert_eq!(query_expr.preview(), "SELECT * FROM users");
524
525 let count_query = table.get_count_query();
527 assert_eq!(count_query.preview(), "(SELECT COUNT(*) FROM \"users\")");
528
529 let count = table.get_count_via_query().await.unwrap();
537 assert_eq!(count, 42);
538 }
539
540 async fn count_table_returning(
541 count_result: serde_json::Value,
542 ) -> Table<MockTableSource, vantage_types::EmptyEntity> {
543 let mock_select_source = MockSelectableDataSource::new(json!([]));
544 let mock_query_source = vantage_expressions::mocks::mock_builder::new()
545 .on_exact_select("(SELECT COUNT(*) FROM \"users\")", count_result);
546 let source = MockTableSource::new()
547 .with_select_source(mock_select_source)
548 .with_query_source(mock_query_source);
549 Table::<_, vantage_types::EmptyEntity>::new("users", source)
550 }
551
552 #[tokio::test]
553 async fn test_count_unwraps_single_element_array() {
554 let table = count_table_returning(json!([{"count": 7}])).await;
556 assert_eq!(table.get_count_via_query().await.unwrap(), 7);
557 }
558
559 #[tokio::test]
560 async fn test_count_errors_on_unexpected_shape() {
561 let table = count_table_returning(json!({"total": 5})).await;
563 assert!(table.get_count_via_query().await.is_err());
564 }
565
566 #[tokio::test]
567 #[should_panic(expected = "MockTableSource select source not set")]
568 async fn test_panics_without_select_source() {
569 let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
570 let _select = table.select();
571 }
572
573 #[tokio::test]
574 #[should_panic(expected = "MockTableSource query source not set")]
575 async fn test_panics_without_query_source() {
576 let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
577 let query = table.data_source().expr("SELECT COUNT(*)", vec![]);
578 let _result = table.data_source().execute(&query).await;
579 }
580}