1use vantage_core::{Result, error};
2use vantage_expressions::traits::selectable::Selectable;
3use vantage_expressions::{Expression, Expressive, SelectableDataSource, expr_any};
4use vantage_types::{EmptyEntity, Entity};
5
6use crate::{
7 column::core::ColumnType,
8 source::{SelectSeed, SelectSource},
9 table::Table,
10 traits::column_like::ColumnLike,
11 traits::table_source::TableSource,
12};
13
14impl<T, E> Table<T, E>
15where
16 T: SelectableDataSource<T::Value, T::Condition> + TableSource,
17 T::Source: SelectSeed<T::Select, T::Value, T::Condition>,
18 T::Value: From<String>, E: Entity<T::Value>,
20{
21 pub fn select_empty(&self) -> T::Select {
25 let mut select = self.data_source.select();
26 self.source.seed(&mut select);
27
28 for condition in self.conditions.values() {
29 select.add_where_condition(condition.clone());
30 }
31
32 for (expr, direction) in self.order_by.values() {
33 let order = match direction {
34 crate::sorting::SortDirection::Ascending => vantage_expressions::Order::Asc,
35 crate::sorting::SortDirection::Descending => vantage_expressions::Order::Desc,
36 };
37 select.add_order_by(expr.clone(), order);
38 }
39
40 if let Some(pagination) = &self.pagination {
41 select.set_limit(Some(pagination.limit()), Some(pagination.skip()));
42 }
43
44 select
45 }
46
47 pub fn select(&self) -> T::Select {
49 let mut select = self.select_empty();
50
51 for column in self.columns.values() {
53 if self.lazy_expressions.contains_key(column.name()) {
58 continue;
59 }
60 if !self.is_active(column.name()) {
63 continue;
64 }
65 if let Some(expr_fn) = self.expressions.get(column.name()) {
66 let expr = expr_fn(self.as_entity_erased());
67 self.data_source.add_select_column(
68 &mut select,
69 expr_any!("({})", (expr)),
70 Some(column.name()),
71 );
72 } else if let Some(alias) = column.alias() {
73 let expr = self.data_source.expr(column.name(), vec![]);
74 self.data_source
75 .add_select_column(&mut select, expr, Some(alias));
76 } else {
77 select.add_field(column.name());
78 }
79 }
80
81 for (name, expr_fn) in &self.expressions {
83 if !self.columns.contains_key(name) && self.is_active(name) {
84 let expr = expr_fn(self.as_entity_erased());
85 self.data_source.add_select_column(
86 &mut select,
87 expr_any!("({})", (expr)),
88 Some(name),
89 );
90 }
91 }
92
93 select
94 }
95 pub async fn get_count(&self) -> Result<i64> {
97 self.data_source.get_table_count(self).await
98 }
99
100 pub async fn get_sum(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
102 self.data_source.get_table_sum(self, column).await
103 }
104
105 pub async fn get_max(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
107 self.data_source.get_table_max(self, column).await
108 }
109
110 pub async fn get_min(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
112 self.data_source.get_table_min(self, column).await
113 }
114
115 pub fn get_count_query(&self) -> Expression<T::Value> {
118 expr_any!("({})", (self.select_empty().as_count()))
119 }
120
121 pub fn get_sum_query<Type>(&self, column: &T::Column<Type>) -> Expression<T::Value>
124 where
125 Type: ColumnType,
126 T::Column<Type>: Expressive<T::Value>,
127 {
128 expr_any!("({})", (self.select_empty().as_sum(column.expr())))
129 }
130
131 pub fn select_column(&self, field: &str) -> Option<Expression<T::Value>>
146 where
147 T::Column<T::AnyType>: Expressive<T::Value>,
148 T::Select: Expressive<T::Value>,
149 {
150 Some(self.select_expression(self.get_column_expr(field)?))
151 }
152
153 pub fn select_expression(&self, expr: Expression<T::Value>) -> Expression<T::Value>
160 where
161 T::Select: Expressive<T::Value>,
162 {
163 let mut select = self.select_empty();
164 select.clear_fields();
165 select.clear_order_by();
166 select.add_expression(expr);
167 select.expr()
168 }
169
170 fn is_active(&self, name: &str) -> bool {
174 match &self.active_columns {
175 None => true,
176 Some(set) => set.contains(name) || self.id_field.as_deref() == Some(name),
177 }
178 }
179
180 pub fn with_active_columns(mut self, cols: &[&str]) -> Result<Self>
204 where
205 T: 'static,
206 E: 'static,
207 T::Column<T::AnyType>: Expressive<T::Value>,
208 T::Select: Expressive<T::Value>,
209 {
210 for &col in cols {
211 let parts: Vec<&str> = col.split('.').collect();
212 if parts.iter().any(|p| p.is_empty()) {
213 return Err(error!("invalid active column name", column = col));
214 }
215
216 if parts.len() >= 2 {
217 let column = parts[parts.len() - 1];
218 let hops = &parts[..parts.len() - 1];
219
220 if !self.data_source().supports_traversal() {
221 return Err(error!(
222 "backend does not support implicit-reference traversal in columns",
223 column = col
224 ));
225 }
226
227 let (target, fk_hops) = self.resolve_has_one_target(hops)?;
229 if !target.columns().contains_key(column) {
230 return Err(error!(
231 "implicit reference target has no such column",
232 column = column
233 ));
234 }
235
236 let fk_refs: Vec<&str> = fk_hops.iter().map(String::as_str).collect();
244 let expr = match self.data_source().traversal_path_expr(&fk_refs, column) {
245 Some(e) => e,
246 None => self.traverse_rest_generic(hops, column)?,
247 };
248
249 let dotted = col.to_string();
250 if !self.columns.contains_key(&dotted) {
251 let column_def = self.data_source.create_column::<T::AnyType>(&dotted);
252 self.add_column(column_def);
253 }
254 self = self.with_expression(&dotted, move |_| expr.clone());
255 self.imported_columns.insert(dotted.clone());
256 self.active_columns
257 .get_or_insert_with(Default::default)
258 .insert(dotted);
259 } else {
260 if !self.columns.contains_key(col) && !self.expressions.contains_key(col) {
264 return Err(error!("unknown active column", column = col));
265 }
266 self.active_columns
267 .get_or_insert_with(Default::default)
268 .insert(col.to_string());
269 }
270 }
271 Ok(self)
272 }
273
274 fn traverse_rest_generic(&self, hops: &[&str], column: &str) -> Result<Expression<T::Value>>
280 where
281 T: 'static,
282 E: 'static,
283 T::Column<T::AnyType>: Expressive<T::Value>,
284 T::Select: Expressive<T::Value>,
285 {
286 match hops.split_first() {
287 None => self.get_column_expr(column).ok_or_else(|| {
288 error!(
289 "implicit reference target has no such column",
290 column = column
291 )
292 }),
293 Some((head, tail)) => {
294 let target: Table<T, EmptyEntity> = self.get_subquery_erased(head)?;
295 let inner = target.traverse_rest_generic(tail, column)?;
296 let inner = if tail.is_empty() {
300 inner
301 } else {
302 expr_any!("({})", (inner))
303 };
304 Ok(target.select_expression(inner))
305 }
306 }
307 }
308
309 fn resolve_has_one_target(&self, hops: &[&str]) -> Result<(Table<T, EmptyEntity>, Vec<String>)>
315 where
316 T: 'static,
317 E: 'static,
318 {
319 let (head, tail) = hops
320 .split_first()
321 .ok_or_else(|| error!("empty implicit reference path"))?;
322 if self.ref_cardinality(head)? != vantage_vista::ReferenceKind::HasOne {
323 return Err(error!(
324 "implicit reference hop must traverse a has_one relation",
325 relation = *head
326 ));
327 }
328 let fk = self.ref_foreign_key(head)?;
329 let target: Table<T, EmptyEntity> = self.get_ref_target_erased(head)?;
330 if tail.is_empty() {
331 Ok((target, vec![fk]))
332 } else {
333 let (final_target, mut fks) = target.resolve_has_one_target(tail)?;
334 fks.insert(0, fk);
335 Ok((final_target, fks))
336 }
337 }
338}
339
340impl<T, E, V, C, S> Table<T, E>
345where
346 T: SelectableDataSource<V, C, Select = S>
347 + TableSource<Value = V, Condition = C, Source = SelectSource<S>>,
348 V: Clone + Send + Sync + 'static + From<String>,
349 C: Clone + Send + Sync + 'static,
350 S: Expressive<V> + Clone,
351 E: Entity<V>,
352{
353 pub fn from_select(data_source: T, alias: impl Into<String>, select: S) -> Self {
356 let alias = alias.into();
357 let mut table = Table::new(alias.clone(), data_source);
358 table.source = SelectSource::query(select, alias);
359 table
360 }
361
362 pub fn derive_from<E2: Entity<V> + 'static>(
376 source: &Table<T, E2>,
377 alias: impl Into<String>,
378 modifier: impl FnOnce(S) -> S,
379 columns: &[&str],
380 relations: &[&str],
381 ) -> Self
382 where
383 T: 'static,
384 E: 'static,
385 {
386 let alias = alias.into();
387 let select = modifier(source.select());
388 let mut table = Table::new(alias.clone(), source.data_source().clone());
389 table.source = SelectSource::query(select, alias);
390 table.copy_columns_from(source, Some(columns));
391 table.copy_relations_from(source, Some(relations));
392 table.id_field = source.id_field.clone();
393 table.title_field = source.title_field.clone();
394 table.title_fields = source.title_fields.clone();
395 table
396 }
397}
398
399impl<T, E> Table<T, E>
401where
402 T: SelectableDataSource<serde_json::Value, T::Condition>
403 + TableSource<Value = serde_json::Value>
404 + vantage_expressions::traits::datasource::ExprDataSource<serde_json::Value>,
405 T::Source: SelectSeed<T::Select, serde_json::Value, T::Condition>,
406 T::Value: From<String>,
407 E: Entity<serde_json::Value>,
408{
409 pub async fn get_count_via_query(&self) -> Result<i64> {
411 let count_query = self.get_count_query();
412 let result = self.data_source.execute(&count_query).await?;
413
414 let result = match result.as_array().map(Vec::as_slice) {
417 Some([single]) => single,
418 _ => &result,
419 };
420
421 if let Some(count) = result.get("count").and_then(|v| v.as_i64()) {
425 Ok(count)
426 } else if let Some(count) = result.as_i64() {
427 Ok(count)
428 } else {
429 Err(vantage_core::util::error::vantage_error!(
430 "count query returned an unexpected result shape: {result}"
431 ))
432 }
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439 use crate::mocks::mock_table_source::MockTableSource;
440 use serde_json::json;
441 use vantage_expressions::mocks::datasource::MockSelectableDataSource;
442 use vantage_expressions::traits::datasource::ExprDataSource;
443
444 #[tokio::test]
445 async fn test_selectable_functionality() {
446 let mock_select_source = MockSelectableDataSource::new(json!([
447 {"id": "1", "name": "Alice", "age": 30},
448 {"id": "2", "name": "Bob", "age": 25}
449 ]));
450
451 let mock_query_source = vantage_expressions::mocks::mock_builder::new()
452 .on_exact_select("(SELECT COUNT(*) FROM \"users\")", json!(42));
453
454 let table = MockTableSource::new()
455 .with_data(
456 "users",
457 vec![
458 json!({"id": "1", "name": "Alice", "age": 30}),
459 json!({"id": "2", "name": "Bob", "age": 25}),
460 ],
461 )
462 .await
463 .with_select_source(mock_select_source)
464 .with_query_source(mock_query_source);
465 let table = Table::<_, vantage_types::EmptyEntity>::new("users", table);
466
467 let select = table.select();
469 assert_eq!(select.source(), Some("users"));
470
471 let query_expr: vantage_expressions::Expression<serde_json::Value> = select.into();
473 assert_eq!(query_expr.preview(), "SELECT * FROM users");
474
475 let count_query = table.get_count_query();
477 assert_eq!(count_query.preview(), "(SELECT COUNT(*) FROM \"users\")");
478
479 let count = table.get_count_via_query().await.unwrap();
487 assert_eq!(count, 42);
488 }
489
490 async fn count_table_returning(
491 count_result: serde_json::Value,
492 ) -> Table<MockTableSource, vantage_types::EmptyEntity> {
493 let mock_select_source = MockSelectableDataSource::new(json!([]));
494 let mock_query_source = vantage_expressions::mocks::mock_builder::new()
495 .on_exact_select("(SELECT COUNT(*) FROM \"users\")", count_result);
496 let source = MockTableSource::new()
497 .with_select_source(mock_select_source)
498 .with_query_source(mock_query_source);
499 Table::<_, vantage_types::EmptyEntity>::new("users", source)
500 }
501
502 #[tokio::test]
503 async fn test_count_unwraps_single_element_array() {
504 let table = count_table_returning(json!([{"count": 7}])).await;
506 assert_eq!(table.get_count_via_query().await.unwrap(), 7);
507 }
508
509 #[tokio::test]
510 async fn test_count_errors_on_unexpected_shape() {
511 let table = count_table_returning(json!({"total": 5})).await;
513 assert!(table.get_count_via_query().await.is_err());
514 }
515
516 #[tokio::test]
517 #[should_panic(expected = "MockTableSource select source not set")]
518 async fn test_panics_without_select_source() {
519 let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
520 let _select = table.select();
521 }
522
523 #[tokio::test]
524 #[should_panic(expected = "MockTableSource query source not set")]
525 async fn test_panics_without_query_source() {
526 let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
527 let query = table.data_source().expr("SELECT COUNT(*)", vec![]);
528 let _result = table.data_source().execute(&query).await;
529 }
530}