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>
203 where
204 T: 'static,
205 E: 'static,
206 T::Column<T::AnyType>: Expressive<T::Value>,
207 T::Select: Expressive<T::Value>,
208 {
209 for &col in cols {
210 let parts: Vec<&str> = col.split('.').collect();
211 if parts.iter().any(|p| p.is_empty()) {
212 return Err(error!("invalid active column name", column = col));
213 }
214
215 if parts.len() >= 2 {
216 let column = parts[parts.len() - 1];
217 let hops = &parts[..parts.len() - 1];
218
219 if !self.data_source().supports_traversal() {
220 return Err(error!(
221 "backend does not support implicit-reference traversal in columns",
222 column = col
223 ));
224 }
225
226 let target = self.resolve_has_one_target(hops)?;
228 if !target.columns().contains_key(column) {
229 return Err(error!(
230 "implicit reference target has no such column",
231 column = column
232 ));
233 }
234
235 let expr = match self.data_source().traversal_path_expr(hops, column) {
238 Some(e) => e,
239 None => self.traverse_rest_generic(hops, column)?,
240 };
241
242 let dotted = col.to_string();
243 if !self.columns.contains_key(&dotted) {
244 let column_def = self.data_source.create_column::<T::AnyType>(&dotted);
245 self.add_column(column_def);
246 }
247 self = self.with_expression(&dotted, move |_| expr.clone());
248 self.imported_columns.insert(dotted.clone());
249 self.active_columns
250 .get_or_insert_with(Default::default)
251 .insert(dotted);
252 } else {
253 if !self.columns.contains_key(col) {
254 return Err(error!("unknown active column", column = col));
255 }
256 self.active_columns
257 .get_or_insert_with(Default::default)
258 .insert(col.to_string());
259 }
260 }
261 Ok(self)
262 }
263
264 fn traverse_rest_generic(&self, hops: &[&str], column: &str) -> Result<Expression<T::Value>>
270 where
271 T: 'static,
272 E: 'static,
273 T::Column<T::AnyType>: Expressive<T::Value>,
274 T::Select: Expressive<T::Value>,
275 {
276 match hops.split_first() {
277 None => self.get_column_expr(column).ok_or_else(|| {
278 error!(
279 "implicit reference target has no such column",
280 column = column
281 )
282 }),
283 Some((head, tail)) => {
284 let target: Table<T, EmptyEntity> = self.get_subquery_erased(head)?;
285 let inner = target.traverse_rest_generic(tail, column)?;
286 let inner = if tail.is_empty() {
290 inner
291 } else {
292 expr_any!("({})", (inner))
293 };
294 Ok(target.select_expression(inner))
295 }
296 }
297 }
298
299 fn resolve_has_one_target(&self, hops: &[&str]) -> Result<Table<T, EmptyEntity>>
302 where
303 T: 'static,
304 E: 'static,
305 {
306 let (head, tail) = hops
307 .split_first()
308 .ok_or_else(|| error!("empty implicit reference path"))?;
309 if self.ref_cardinality(head)? != vantage_vista::ReferenceKind::HasOne {
310 return Err(error!(
311 "implicit reference hop must traverse a has_one relation",
312 relation = *head
313 ));
314 }
315 let target: Table<T, EmptyEntity> = self.get_ref_target_erased(head)?;
316 if tail.is_empty() {
317 Ok(target)
318 } else {
319 target.resolve_has_one_target(tail)
320 }
321 }
322}
323
324impl<T, E, V, C, S> Table<T, E>
329where
330 T: SelectableDataSource<V, C, Select = S>
331 + TableSource<Value = V, Condition = C, Source = SelectSource<S>>,
332 V: Clone + Send + Sync + 'static + From<String>,
333 C: Clone + Send + Sync + 'static,
334 S: Expressive<V> + Clone,
335 E: Entity<V>,
336{
337 pub fn from_select(data_source: T, alias: impl Into<String>, select: S) -> Self {
340 let alias = alias.into();
341 let mut table = Table::new(alias.clone(), data_source);
342 table.source = SelectSource::query(select, alias);
343 table
344 }
345
346 pub fn derive_from<E2: Entity<V> + 'static>(
355 source: &Table<T, E2>,
356 alias: impl Into<String>,
357 modifier: impl FnOnce(S) -> S,
358 columns: &[&str],
359 relations: &[&str],
360 ) -> Self
361 where
362 T: 'static,
363 E: 'static,
364 {
365 let alias = alias.into();
366 let select = modifier(source.select());
367 let mut table = Table::new(alias.clone(), source.data_source().clone());
368 table.source = SelectSource::query(select, alias);
369 table.copy_columns_from(source, Some(columns));
370 table.copy_relations_from(source, Some(relations));
371 table.id_field = source.id_field.clone();
372 table.title_field = source.title_field.clone();
373 table.title_fields = source.title_fields.clone();
374 table
375 }
376}
377
378impl<T, E> Table<T, E>
380where
381 T: SelectableDataSource<serde_json::Value, T::Condition>
382 + TableSource<Value = serde_json::Value>
383 + vantage_expressions::traits::datasource::ExprDataSource<serde_json::Value>,
384 T::Source: SelectSeed<T::Select, serde_json::Value, T::Condition>,
385 T::Value: From<String>,
386 E: Entity<serde_json::Value>,
387{
388 pub async fn get_count_via_query(&self) -> Result<i64> {
390 let count_query = self.get_count_query();
391 let result = self.data_source.execute(&count_query).await?;
392
393 let result = match result.as_array().map(Vec::as_slice) {
396 Some([single]) => single,
397 _ => &result,
398 };
399
400 if let Some(count) = result.get("count").and_then(|v| v.as_i64()) {
404 Ok(count)
405 } else if let Some(count) = result.as_i64() {
406 Ok(count)
407 } else {
408 Err(vantage_core::util::error::vantage_error!(
409 "count query returned an unexpected result shape: {result}"
410 ))
411 }
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418 use crate::mocks::mock_table_source::MockTableSource;
419 use serde_json::json;
420 use vantage_expressions::mocks::datasource::MockSelectableDataSource;
421 use vantage_expressions::traits::datasource::ExprDataSource;
422
423 #[tokio::test]
424 async fn test_selectable_functionality() {
425 let mock_select_source = MockSelectableDataSource::new(json!([
426 {"id": "1", "name": "Alice", "age": 30},
427 {"id": "2", "name": "Bob", "age": 25}
428 ]));
429
430 let mock_query_source = vantage_expressions::mocks::mock_builder::new()
431 .on_exact_select("(SELECT COUNT(*) FROM \"users\")", json!(42));
432
433 let table = MockTableSource::new()
434 .with_data(
435 "users",
436 vec![
437 json!({"id": "1", "name": "Alice", "age": 30}),
438 json!({"id": "2", "name": "Bob", "age": 25}),
439 ],
440 )
441 .await
442 .with_select_source(mock_select_source)
443 .with_query_source(mock_query_source);
444 let table = Table::<_, vantage_types::EmptyEntity>::new("users", table);
445
446 let select = table.select();
448 assert_eq!(select.source(), Some("users"));
449
450 let query_expr: vantage_expressions::Expression<serde_json::Value> = select.into();
452 assert_eq!(query_expr.preview(), "SELECT * FROM users");
453
454 let count_query = table.get_count_query();
456 assert_eq!(count_query.preview(), "(SELECT COUNT(*) FROM \"users\")");
457
458 let count = table.get_count_via_query().await.unwrap();
466 assert_eq!(count, 42);
467 }
468
469 async fn count_table_returning(
470 count_result: serde_json::Value,
471 ) -> Table<MockTableSource, vantage_types::EmptyEntity> {
472 let mock_select_source = MockSelectableDataSource::new(json!([]));
473 let mock_query_source = vantage_expressions::mocks::mock_builder::new()
474 .on_exact_select("(SELECT COUNT(*) FROM \"users\")", count_result);
475 let source = MockTableSource::new()
476 .with_select_source(mock_select_source)
477 .with_query_source(mock_query_source);
478 Table::<_, vantage_types::EmptyEntity>::new("users", source)
479 }
480
481 #[tokio::test]
482 async fn test_count_unwraps_single_element_array() {
483 let table = count_table_returning(json!([{"count": 7}])).await;
485 assert_eq!(table.get_count_via_query().await.unwrap(), 7);
486 }
487
488 #[tokio::test]
489 async fn test_count_errors_on_unexpected_shape() {
490 let table = count_table_returning(json!({"total": 5})).await;
492 assert!(table.get_count_via_query().await.is_err());
493 }
494
495 #[tokio::test]
496 #[should_panic(expected = "MockTableSource select source not set")]
497 async fn test_panics_without_select_source() {
498 let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
499 let _select = table.select();
500 }
501
502 #[tokio::test]
503 #[should_panic(expected = "MockTableSource query source not set")]
504 async fn test_panics_without_query_source() {
505 let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
506 let query = table.data_source().expr("SELECT COUNT(*)", vec![]);
507 let _result = table.data_source().execute(&query).await;
508 }
509}