1#![deny(missing_docs)]
2use std::marker::PhantomData;
6use std::sync::Arc;
7use std::sync::atomic::{AtomicU64, Ordering};
8
9use type_bridge_contract::codec::from_canonical_json;
10use type_bridge_contract::decimal::parse_decimal;
11use type_bridge_contract::id::TypeId;
12use type_bridge_contract::query_plan::CompatibilityValueV2;
13use type_bridge_contract::temporal::{
14 CanonicalDate, CanonicalDateTime, CanonicalDateTimeTz, CanonicalDuration,
15};
16use type_bridge_orm::_descriptor::TypeDescriptorRef;
17use type_bridge_orm::_registry::DescriptorRegistry;
18use type_bridge_orm::match_request::handles::{
19 BindingHandle as OrmBindingHandle, FieldHandle as OrmFieldHandle,
20 OrderHandle as OrmOrderHandle, PredicateHandle as OrmPredicateHandle,
21 QueryHandle as OrmQueryHandle, SelectionHandle as OrmSelectionHandle,
22 SessionHandle as OrmSessionHandle, ShapeHandle as OrmShapeHandle,
23};
24use type_bridge_orm::match_request::model::{
25 ComparisonOp, MissingOrder, RowCardinality, SortDirection, Window,
26};
27use type_bridge_orm::match_request::result::{
28 HydratedThing, MatchResult, MatchRow, SlotValue, ValidatedMatchResult,
29};
30use type_bridge_orm::match_request::validation::ValidatedMatchRequest;
31use type_bridge_orm::{
32 AttributeValue, DynamicEntityRow, DynamicRelationRow, DynamicRolePlayer,
33 InstalledRuntimeProjection,
34};
35
36use crate::__codegen::{
37 CompleteModel, EncodedScalar, FieldToken, GroupedQueryValue, HydratedRow, HydrationCapability,
38 Model, QueryValued, RelationModel, RolePlayerBinding, RoleToken, RoleTokenCompatible,
39 SubtypeRootModel, ThingModel, TypeToken, ValidationError,
40};
41use crate::entity_codec::{hydrate_entity, map_validation_error};
42use crate::error::{Error, ModelValidationPhase};
43use crate::relation_codec::hydrate_relation;
44use crate::schema::Schema;
45use crate::{Database, Result};
46
47#[cfg(test)]
48mod tests;
49
50static QUERY_SESSION_NONCE: AtomicU64 = AtomicU64::new(1);
51
52fn schema_not_bound() -> Error {
53 Error::model_validation(
54 ModelValidationPhase::Input,
55 "schema_not_bound",
56 vec![],
57 "database is not schema-bound",
58 None,
59 )
60}
61
62fn cross_session_handle() -> Error {
63 Error::model_validation(
64 ModelValidationPhase::Input,
65 "cross_session_handle",
66 vec![],
67 "binding belongs to a different query session",
68 None,
69 )
70}
71
72fn model_label(type_id_json: &'static str) -> Result<String> {
73 let id = from_canonical_json::<TypeId>(type_id_json.as_bytes()).map_err(|source| {
74 Error::model_validation(
75 ModelValidationPhase::Input,
76 "invalid_model_identity",
77 vec!["type".into()],
78 "generated model identity is not canonical",
79 Some(Box::new(source)),
80 )
81 })?;
82 Ok(id.label().as_str().to_owned())
83}
84
85fn parse_owns_identity(owns_id_json: &'static str) -> Result<(String, String)> {
86 let invalid = || {
87 Error::model_validation(
88 ModelValidationPhase::Input,
89 "invalid_field_identity",
90 vec!["type".into()],
91 "generated field identity is not canonical",
92 None,
93 )
94 };
95 let value: serde_json::Value = serde_json::from_str(owns_id_json).map_err(|_| invalid())?;
96 let attribute = value
97 .get("attribute")
98 .and_then(serde_json::Value::as_str)
99 .ok_or_else(invalid)?;
100 let owner = value
101 .get("owner")
102 .and_then(|owner| owner.get("label"))
103 .and_then(serde_json::Value::as_str)
104 .ok_or_else(invalid)?;
105 Ok((owner.to_owned(), attribute.to_owned()))
106}
107
108fn parse_role_identity(role_id_json: &'static str) -> Result<type_bridge_contract::id::RoleId> {
109 from_canonical_json::<type_bridge_contract::id::RoleId>(role_id_json.as_bytes()).map_err(
110 |source| {
111 Error::model_validation(
112 ModelValidationPhase::Input,
113 "invalid_role_identity",
114 vec!["type".into()],
115 "generated role identity is not canonical",
116 Some(Box::new(source)),
117 )
118 },
119 )
120}
121
122mod mode_sealed {
123 pub trait Sealed {}
124}
125
126pub trait SelectionMode: mode_sealed::Sealed + 'static {}
128
129#[derive(Clone, Copy, Debug)]
131pub struct Exact;
132impl mode_sealed::Sealed for Exact {}
133impl SelectionMode for Exact {}
134
135#[derive(Clone, Copy, Debug)]
138pub struct Subtypes;
139impl mode_sealed::Sealed for Subtypes {}
140impl SelectionMode for Subtypes {}
141
142pub struct QuerySession<'db, S: Schema> {
149 installed: &'db InstalledRuntimeProjection,
150 execution: QueryExecution<'db, S>,
151 session: OrmSessionHandle,
152 registry: Arc<DescriptorRegistry>,
153 nonce: u64,
154 bindings: Vec<OrmBindingHandle>,
155 marker: PhantomData<fn() -> S>,
156}
157
158enum QueryExecution<'db, S: Schema> {
159 Local(&'db Database<S>),
160 Borrowed(&'db type_bridge_orm::session::context::TransactionContext),
161 Remote(&'db crate::remote::RemoteDatabase<S>),
162}
163
164impl<S: Schema> std::fmt::Debug for QuerySession<'_, S> {
165 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 formatter
167 .debug_struct("QuerySession")
168 .field("session", &self.nonce)
169 .field("bindings", &self.bindings.len())
170 .finish_non_exhaustive()
171 }
172}
173
174#[doc(hidden)]
176#[derive(Clone, Copy, Debug, PartialEq, Eq)]
177pub struct BindingKey {
178 pub(crate) nonce: u64,
179 pub(crate) index: u32,
180}
181
182pub struct Binding<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode = Exact> {
186 key: BindingKey,
187 #[allow(clippy::type_complexity)]
188 marker: PhantomData<fn() -> (S, M, Mode)>,
189}
190
191impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Copy for Binding<S, M, Mode> {}
192impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Clone for Binding<S, M, Mode> {
193 fn clone(&self) -> Self {
194 *self
195 }
196}
197impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> PartialEq for Binding<S, M, Mode> {
198 fn eq(&self, other: &Self) -> bool {
199 self.key == other.key
200 }
201}
202impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Eq for Binding<S, M, Mode> {}
203impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> std::fmt::Debug
204 for Binding<S, M, Mode>
205{
206 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 formatter
208 .debug_struct("Binding")
209 .field("session", &self.key.nonce)
210 .field("index", &self.key.index)
211 .finish()
212 }
213}
214
215impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Binding<S, M, Mode> {
216 pub(crate) fn key(self) -> BindingKey {
217 self.key
218 }
219
220 #[must_use]
222 pub fn iid(self, iid: impl Into<String>) -> Predicate<S> {
223 Predicate::new(PredicateExpr::BindingIid {
224 binding: self.key,
225 iid: iid.into(),
226 })
227 }
228
229 #[must_use]
231 pub fn iid_in(self, iids: impl IntoIterator<Item = impl Into<String>>) -> Predicate<S> {
232 Predicate::new(PredicateExpr::BindingIidIn {
233 binding: self.key,
234 iids: iids.into_iter().map(Into::into).collect(),
235 })
236 }
237
238 #[must_use]
241 pub fn collect(self) -> Collected<S, Self>
242 where
243 Self: Selectable<S>,
244 {
245 Collected {
246 selection: self,
247 distinct: false,
248 order: Vec::new(),
249 }
250 }
251}
252
253impl<S: Schema> Database<S> {
254 pub fn query(&self) -> Result<QuerySession<'_, S>> {
257 let registry = self.match_registry().ok_or_else(schema_not_bound)?;
258 let installed = self
259 .installed_schema()
260 .map(Arc::as_ref)
261 .ok_or_else(schema_not_bound)?;
262 Ok(QuerySession::new(
263 installed,
264 Arc::clone(registry),
265 QueryExecution::Local(self),
266 ))
267 }
268}
269
270impl<'db, S: Schema> QuerySession<'db, S> {
271 fn new(
272 installed: &'db InstalledRuntimeProjection,
273 registry: Arc<DescriptorRegistry>,
274 execution: QueryExecution<'db, S>,
275 ) -> Self {
276 Self {
277 installed,
278 execution,
279 session: OrmSessionHandle::new(Arc::clone(®istry)),
280 registry,
281 nonce: QUERY_SESSION_NONCE.fetch_add(1, Ordering::Relaxed),
282 bindings: Vec::new(),
283 marker: PhantomData,
284 }
285 }
286
287 pub(crate) fn borrowed(
288 installed: &'db InstalledRuntimeProjection,
289 registry: Arc<DescriptorRegistry>,
290 transaction: &'db type_bridge_orm::session::context::TransactionContext,
291 ) -> Self {
292 Self::new(installed, registry, QueryExecution::Borrowed(transaction))
293 }
294
295 pub(crate) fn remote(
296 installed: &'db InstalledRuntimeProjection,
297 registry: Arc<DescriptorRegistry>,
298 remote: &'db crate::remote::RemoteDatabase<S>,
299 ) -> Self {
300 Self::new(installed, registry, QueryExecution::Remote(remote))
301 }
302}
303
304impl<'db, S: Schema> QuerySession<'db, S> {
305 fn push_binding<M: ThingModel<Schema = S>, Mode: SelectionMode>(
306 &mut self,
307 handle: OrmBindingHandle,
308 ) -> Result<Binding<S, M, Mode>> {
309 let index = u32::try_from(self.bindings.len()).map_err(|source| {
310 Error::model_validation(
311 ModelValidationPhase::Input,
312 "too_many_bindings",
313 vec![],
314 "query session binding capacity exceeded",
315 Some(Box::new(source)),
316 )
317 })?;
318 self.bindings.push(handle);
319 Ok(Binding {
320 key: BindingKey {
321 nonce: self.nonce,
322 index,
323 },
324 marker: PhantomData,
325 })
326 }
327
328 pub fn exact<M>(&mut self) -> Result<Binding<S, M, Exact>>
331 where
332 M: ThingModel<Schema = S> + CompleteModel,
333 {
334 let label = model_label(M::TYPE_ID_JSON)?;
335 let handle = self.session.exact(&label).map_err(Error::from_orm)?;
336 self.push_binding(handle)
337 }
338
339 pub fn subtypes<M>(&mut self) -> Result<Binding<S, M, Subtypes>>
342 where
343 M: ThingModel<Schema = S> + SubtypeRootModel,
344 {
345 let label = model_label(M::TYPE_ID_JSON)?;
346 let handle = self.session.subtypes(&label).map_err(Error::from_orm)?;
347 self.push_binding(handle)
348 }
349
350 pub(crate) fn handle_by_key(&self, key: BindingKey) -> Result<&OrmBindingHandle> {
351 if key.nonce != self.nonce {
352 return Err(cross_session_handle());
353 }
354 self.bindings
355 .get(key.index as usize)
356 .ok_or_else(cross_session_handle)
357 }
358
359 fn installed(&self) -> Result<&InstalledRuntimeProjection> {
360 Ok(self.installed)
361 }
362
363 fn field_name_for(&self, owner_label: &str, attribute_label: &str) -> Result<String> {
364 let descriptor = self.registry.get(owner_label).ok_or_else(|| {
365 Error::model_validation(
366 ModelValidationPhase::Input,
367 "unknown_field_owner",
368 vec!["type".into()],
369 format!("field owner '{owner_label}' is not registered in this session"),
370 None,
371 )
372 })?;
373 let found = match &descriptor {
374 TypeDescriptorRef::Entity(entity) => entity
375 .owned_attributes
376 .iter()
377 .find(|attribute| attribute.attr_name == attribute_label)
378 .map(|attribute| attribute.field_name.clone()),
379 TypeDescriptorRef::Relation(relation) => relation
380 .owned_attributes
381 .iter()
382 .find(|attribute| attribute.attr_name == attribute_label)
383 .map(|attribute| attribute.field_name.clone()),
384 };
385 found.ok_or_else(|| {
386 Error::model_validation(
387 ModelValidationPhase::Input,
388 "unknown_field",
389 vec!["type".into()],
390 format!("owner '{owner_label}' has no field for attribute '{attribute_label}'"),
391 None,
392 )
393 })
394 }
395
396 pub(crate) fn lower_field(
397 &self,
398 key: BindingKey,
399 owns_id_json: &'static str,
400 ) -> Result<OrmFieldHandle> {
401 let handle = self.handle_by_key(key)?;
402 let (owner_label, attribute_label) = parse_owns_identity(owns_id_json)?;
403 let field_name = self.field_name_for(&owner_label, &attribute_label)?;
404 handle
405 .field_owned_by(&owner_label, &field_name)
406 .map_err(Error::from_orm)
407 }
408
409 fn lower_order(&self, order: &Order<S>) -> Result<OrmOrderHandle> {
410 let field = self.lower_field(order.key, order.owns_id_json)?;
411 Ok(field.order(order.direction, order.missing))
412 }
413
414 fn lower_predicate(&self, expr: &PredicateExpr) -> Result<OrmPredicateHandle> {
415 match expr {
416 PredicateExpr::FieldValue {
417 binding,
418 owns_id_json,
419 operator,
420 value,
421 } => {
422 let field = self.lower_field(*binding, owns_id_json)?;
423 Ok(field.compare_value(*operator, value.clone()))
424 }
425 PredicateExpr::FieldField {
426 left_binding,
427 left_owns_id_json,
428 operator,
429 right_binding,
430 right_owns_id_json,
431 } => {
432 let left = self.lower_field(*left_binding, left_owns_id_json)?;
433 let right = self.lower_field(*right_binding, right_owns_id_json)?;
434 left.compare_field(*operator, &right)
435 .map_err(Error::from_orm)
436 }
437 PredicateExpr::FieldPresence {
438 binding,
439 owns_id_json,
440 present,
441 } => Ok(self.lower_field(*binding, owns_id_json)?.presence(*present)),
442 PredicateExpr::BindingIid { binding, iid } => self
443 .handle_by_key(*binding)?
444 .iid(iid.clone())
445 .map_err(Error::from_orm),
446 PredicateExpr::BindingIidIn { binding, iids } => self
447 .handle_by_key(*binding)?
448 .iid_in(iids.clone())
449 .map_err(Error::from_orm),
450 PredicateExpr::Connects {
451 relation,
452 role_id_json,
453 player,
454 } => {
455 let relation_handle = self.handle_by_key(*relation)?;
456 let role = parse_role_identity(role_id_json)?;
457 let role_handle = relation_handle
458 .role_owned_by(role.declaring_relation().as_str(), role.label().as_str())
459 .map_err(Error::from_orm)?;
460 let player_handle = self.handle_by_key(*player)?;
461 role_handle.connects(player_handle).map_err(Error::from_orm)
462 }
463 PredicateExpr::Reachable {
464 relation_type_id_json,
465 role_from_id_json,
466 role_to_id_json,
467 source,
468 target,
469 min_depth,
470 max_depth,
471 } => {
472 let relation = model_label(relation_type_id_json)?;
473 let role_from = parse_role_identity(role_from_id_json)?;
474 let role_to = parse_role_identity(role_to_id_json)?;
475 self.session
476 .reachable(
477 &relation,
478 role_from.label().as_str(),
479 role_to.label().as_str(),
480 self.handle_by_key(*source)?,
481 self.handle_by_key(*target)?,
482 *min_depth,
483 *max_depth,
484 )
485 .map_err(Error::from_orm)
486 }
487 PredicateExpr::And(terms) => self.lower_composed(terms, |left, right| {
488 left.and(right).map_err(Error::from_orm)
489 }),
490 PredicateExpr::Or(terms) => {
491 self.lower_composed(terms, |left, right| left.or(right).map_err(Error::from_orm))
492 }
493 PredicateExpr::Not(inner) => Ok(self.lower_predicate(inner)?.not()),
494 }
495 }
496
497 fn lower_composed(
498 &self,
499 terms: &[PredicateExpr],
500 combine: impl Fn(&OrmPredicateHandle, &OrmPredicateHandle) -> Result<OrmPredicateHandle>,
501 ) -> Result<OrmPredicateHandle> {
502 let mut lowered = terms.iter().map(|term| self.lower_predicate(term));
503 let mut combined = lowered.next().ok_or_else(|| {
504 Error::model_validation(
505 ModelValidationPhase::Input,
506 "empty_predicate",
507 vec![],
508 "boolean composition requires at least one predicate",
509 None,
510 )
511 })??;
512 for term in lowered {
513 combined = combine(&combined, &term?)?;
514 }
515 Ok(combined)
516 }
517
518 fn client_row_for(&self, thing: &HydratedThing) -> Result<HydratedRow> {
519 use type_bridge_orm::match_request::model::ThingKind as OrmThingKind;
520 let installed = self.installed()?;
521 let type_name = self
522 .registry
523 .descriptor_type_name(thing.concrete_descriptor())
524 .ok_or_else(|| {
525 Error::model_validation(
526 ModelValidationPhase::Hydration,
527 "invalid_installed_projection",
528 vec!["type".into()],
529 "selected concrete descriptor is absent from the installed registry",
530 None,
531 )
532 })?;
533 let mut attributes = Vec::new();
534 for attribute in thing.attributes() {
535 let provider_name = self
536 .registry
537 .provider_attribute_name(attribute.field())
538 .ok_or_else(|| {
539 Error::model_validation(
540 ModelValidationPhase::Hydration,
541 "invalid_installed_projection",
542 vec![],
543 "selected field identity has no provider attribute name",
544 None,
545 )
546 })?;
547 for value in attribute.values() {
548 attributes.push((
549 provider_name.clone(),
550 canonicalize_selected_value(value.clone())?,
551 ));
552 }
553 }
554 match thing.kind() {
555 OrmThingKind::Entity => {
556 let id = TypeId::new(type_bridge_contract::id::TypeKind::Entity, &type_name)
557 .map_err(|source| {
558 Error::model_validation(
559 ModelValidationPhase::Hydration,
560 "invalid_discovered_type",
561 vec!["type".into()],
562 "selected entity type label is invalid",
563 Some(Box::new(source)),
564 )
565 })?;
566 hydrate_entity(
567 DynamicEntityRow {
568 iid: Some(thing.concept_id().as_str().to_owned()),
569 type_name: Some(type_name),
570 attributes,
571 },
572 &id,
573 installed,
574 )
575 }
576 OrmThingKind::Relation => {
577 let id = TypeId::new(type_bridge_contract::id::TypeKind::Relation, &type_name)
578 .map_err(|source| {
579 Error::model_validation(
580 ModelValidationPhase::Hydration,
581 "invalid_discovered_type",
582 vec!["type".into()],
583 "selected relation type label is invalid",
584 Some(Box::new(source)),
585 )
586 })?;
587 let mut role_players = Vec::new();
588 for role in thing.roles() {
589 for player in role.players() {
590 let mut raw = Vec::new();
591 for attribute in player.attributes() {
592 let provider_name = self
593 .registry
594 .provider_attribute_name(attribute.field())
595 .ok_or_else(|| {
596 Error::model_validation(
597 ModelValidationPhase::Hydration,
598 "invalid_installed_projection",
599 vec![],
600 "player field identity has no provider attribute name",
601 None,
602 )
603 })?;
604 for value in attribute.values() {
605 raw.push((
606 provider_name.clone(),
607 plain_json(&canonicalize_selected_value(value.clone())?),
608 ));
609 }
610 }
611 let player_type_name = self
612 .registry
613 .descriptor_type_name(player.concrete_descriptor())
614 .ok_or_else(|| {
615 Error::model_validation(
616 ModelValidationPhase::Hydration,
617 "invalid_installed_projection",
618 vec!["roles".into(), role.role().name.clone()],
619 "selected role-player descriptor is absent from the installed registry",
620 None,
621 )
622 })?;
623 role_players.push(DynamicRolePlayer {
624 role_name: role.role().name.clone(),
625 player_iid: Some(player.concept_id().as_str().to_owned()),
626 player_type_name: Some(player_type_name),
627 attributes: raw,
628 });
629 }
630 }
631 hydrate_relation(
632 DynamicRelationRow {
633 iid: Some(thing.concept_id().as_str().to_owned()),
634 type_name: Some(type_name),
635 attributes,
636 role_players,
637 },
638 &id,
639 installed,
640 )
641 }
642 }
643 }
644}
645
646fn canonicalize_selected_value(value: AttributeValue) -> Result<AttributeValue> {
647 let malformed = || {
648 Error::model_validation(
649 ModelValidationPhase::Hydration,
650 "hydrated_attribute_value_type",
651 vec![],
652 "selected attribute value is outside its canonical scalar domain",
653 None,
654 )
655 };
656 match value {
657 AttributeValue::Date(value) => value
658 .parse::<CanonicalDate>()
659 .map(|value| AttributeValue::Date(value.to_string()))
660 .map_err(|_| malformed()),
661 AttributeValue::DateTime(value) => normalize_provider_fraction(value)
662 .parse::<CanonicalDateTime>()
663 .map(|value| AttributeValue::DateTime(value.to_string()))
664 .map_err(|_| malformed()),
665 AttributeValue::DateTimeTZ(value) => normalize_provider_datetime_tz(value)
666 .parse::<CanonicalDateTimeTz>()
667 .map(|value| AttributeValue::DateTimeTZ(value.to_string()))
668 .map_err(|_| malformed()),
669 AttributeValue::Decimal(value) => parse_decimal(&value)
670 .map(|value| AttributeValue::Decimal(value.canonical_string()))
671 .ok_or_else(malformed),
672 AttributeValue::Duration(value) => {
673 let value = normalize_provider_fraction(value);
674 match value.parse::<CanonicalDuration>() {
675 Ok(value) => Ok(AttributeValue::Duration(value.to_string())),
676 Err(_) => CompatibilityValueV2::released_duration(value.clone())
677 .map(|_| AttributeValue::Duration(value))
678 .map_err(|_| malformed()),
679 }
680 }
681 value => Ok(value),
682 }
683}
684
685fn encoded_group_scalar(value: &AttributeValue) -> Result<EncodedScalar> {
686 let value = canonicalize_selected_value(value.clone())?;
687 let map = |error| map_validation_error(error, ModelValidationPhase::Hydration);
688 match value {
689 AttributeValue::String(value) => Ok(EncodedScalar::String(value)),
690 AttributeValue::Long(value) => Ok(EncodedScalar::Long(value)),
691 AttributeValue::Double(value) => crate::__codegen::CanonicalDouble::try_new(value)
692 .map(EncodedScalar::Double)
693 .map_err(map),
694 AttributeValue::Boolean(value) => Ok(EncodedScalar::Boolean(value)),
695 AttributeValue::Date(value) => crate::__codegen::Date::try_new(value)
696 .map(EncodedScalar::Date)
697 .map_err(map),
698 AttributeValue::DateTime(value) => crate::__codegen::DateTime::try_new(value)
699 .map(EncodedScalar::DateTime)
700 .map_err(map),
701 AttributeValue::DateTimeTZ(value) => crate::__codegen::DateTimeTz::try_new(value)
702 .map(EncodedScalar::DateTimeTz)
703 .map_err(map),
704 AttributeValue::Decimal(value) => crate::__codegen::Decimal::try_new(value)
705 .map(EncodedScalar::Decimal)
706 .map_err(map),
707 AttributeValue::Duration(value) => crate::__codegen::Duration::try_new(value)
708 .map(EncodedScalar::Duration)
709 .map_err(map),
710 }
711}
712
713fn normalize_provider_datetime_tz(value: String) -> String {
714 let mut normalized = normalize_provider_fraction(value);
715 for zero_offset in ["+00:00:00", "-00:00:00", "+00:00", "-00:00"] {
716 if normalized.ends_with(zero_offset) {
717 normalized.truncate(normalized.len() - zero_offset.len());
718 normalized.push('Z');
719 break;
720 }
721 }
722 normalized
723}
724
725fn normalize_provider_fraction(value: String) -> String {
726 let Some(dot) = value.find('.') else {
727 return value;
728 };
729 let fraction_end = value[dot + 1..]
730 .find(|character: char| !character.is_ascii_digit())
731 .map_or(value.len(), |offset| dot + 1 + offset);
732 let trimmed_end = value[dot + 1..fraction_end].trim_end_matches('0').len() + dot + 1;
733 if trimmed_end == fraction_end {
734 return value;
735 }
736 let mut normalized = String::with_capacity(value.len());
737 normalized.push_str(
738 &value[..if trimmed_end == dot + 1 {
739 dot
740 } else {
741 trimmed_end
742 }],
743 );
744 normalized.push_str(&value[fraction_end..]);
745 normalized
746}
747
748fn plain_json(value: &AttributeValue) -> serde_json::Value {
749 match value {
750 AttributeValue::String(value) => serde_json::Value::String(value.clone()),
751 AttributeValue::Long(value) => serde_json::Value::from(*value),
752 AttributeValue::Double(value) => {
753 serde_json::Number::from_f64(*value).map_or(serde_json::Value::Null, Into::into)
754 }
755 AttributeValue::Boolean(value) => serde_json::Value::Bool(*value),
756 AttributeValue::Date(value)
757 | AttributeValue::DateTime(value)
758 | AttributeValue::DateTimeTZ(value)
759 | AttributeValue::Decimal(value)
760 | AttributeValue::Duration(value) => serde_json::Value::String(value.clone()),
761 }
762}
763
764pub trait QueryOperand: operand_sealed::Sealed {
767 #[doc(hidden)]
768 type Domain;
769
770 #[doc(hidden)]
771 fn into_operand(self) -> AttributeValue;
772}
773
774pub trait OrderedOperand: QueryOperand {}
776
777mod operand_sealed {
778 pub trait Sealed {}
779}
780
781macro_rules! operand {
782 ($ty:ty, $domain:ty, $self_:ident => $convert:expr, ordered: $ordered:tt) => {
783 impl operand_sealed::Sealed for $ty {}
784 impl QueryOperand for $ty {
785 type Domain = $domain;
786
787 fn into_operand($self_) -> AttributeValue {
788 $convert
789 }
790 }
791 operand!(@ordered $ty, $ordered);
792 };
793 (@ordered $ty:ty, true) => {
794 impl OrderedOperand for $ty {}
795 };
796 (@ordered $ty:ty, false) => {};
797}
798
799fn encoded_query_operand(value: EncodedScalar) -> AttributeValue {
800 match value {
801 EncodedScalar::String(value) => AttributeValue::String(value),
802 EncodedScalar::Long(value) => AttributeValue::Long(value),
803 EncodedScalar::Double(value) => AttributeValue::Double(value.get()),
804 EncodedScalar::Boolean(value) => AttributeValue::Boolean(value),
805 EncodedScalar::Date(value) => AttributeValue::Date(value.as_str().to_owned()),
806 EncodedScalar::DateTime(value) => AttributeValue::DateTime(value.as_str().to_owned()),
807 EncodedScalar::DateTimeTz(value) => AttributeValue::DateTimeTZ(value.as_str().to_owned()),
808 EncodedScalar::Decimal(value) => AttributeValue::Decimal(value.as_str().to_owned()),
809 EncodedScalar::Duration(value) => AttributeValue::Duration(value.as_str().to_owned()),
810 }
811}
812
813impl<T: QueryValued> operand_sealed::Sealed for T {}
814impl<T: QueryValued> QueryOperand for T {
815 type Domain = T::Domain;
816
817 fn into_operand(self) -> AttributeValue {
818 encoded_query_operand(self.into_encoded_scalar())
819 }
820}
821
822impl OrderedOperand for i64 {}
823
824operand!(
825 crate::value::Text,
826 String,
827 self => AttributeValue::String(self.into_string()),
828 ordered: false
829);
830operand!(
831 crate::value::Double,
832 crate::__codegen::CanonicalDouble,
833 self => AttributeValue::Double(self.get()),
834 ordered: true
835);
836operand!(
837 crate::value::Decimal,
838 crate::__codegen::Decimal,
839 self => AttributeValue::Decimal(self.into_string()),
840 ordered: true
841);
842operand!(
843 crate::value::Date,
844 crate::__codegen::Date,
845 self => AttributeValue::Date(self.into_string()),
846 ordered: true
847);
848operand!(
849 crate::value::DateTime,
850 crate::__codegen::DateTime,
851 self => AttributeValue::DateTime(self.into_string()),
852 ordered: true
853);
854operand!(
855 crate::value::DateTimeTz,
856 crate::__codegen::DateTimeTz,
857 self => AttributeValue::DateTimeTZ(self.into_string()),
858 ordered: true
859);
860operand!(
861 crate::value::Duration,
862 crate::__codegen::Duration,
863 self => AttributeValue::Duration(self.into_string()),
864 ordered: true
865);
866
867#[derive(Clone, Debug, PartialEq)]
868pub(crate) enum PredicateExpr {
869 FieldValue {
870 binding: BindingKey,
871 owns_id_json: &'static str,
872 operator: ComparisonOp,
873 value: AttributeValue,
874 },
875 FieldField {
876 left_binding: BindingKey,
877 left_owns_id_json: &'static str,
878 operator: ComparisonOp,
879 right_binding: BindingKey,
880 right_owns_id_json: &'static str,
881 },
882 FieldPresence {
883 binding: BindingKey,
884 owns_id_json: &'static str,
885 present: bool,
886 },
887 BindingIid {
888 binding: BindingKey,
889 iid: String,
890 },
891 BindingIidIn {
892 binding: BindingKey,
893 iids: Vec<String>,
894 },
895 Connects {
896 relation: BindingKey,
897 role_id_json: &'static str,
898 player: BindingKey,
899 },
900 Reachable {
901 relation_type_id_json: &'static str,
902 role_from_id_json: &'static str,
903 role_to_id_json: &'static str,
904 source: BindingKey,
905 target: BindingKey,
906 min_depth: u8,
907 max_depth: u8,
908 },
909 And(Vec<PredicateExpr>),
910 Or(Vec<PredicateExpr>),
911 Not(Box<PredicateExpr>),
912}
913
914#[derive(Debug, PartialEq)]
921pub struct Predicate<S: Schema> {
922 pub(crate) expr: PredicateExpr,
923 marker: PhantomData<fn() -> S>,
924}
925
926impl<S: Schema> Clone for Predicate<S> {
927 fn clone(&self) -> Self {
928 Self {
929 expr: self.expr.clone(),
930 marker: PhantomData,
931 }
932 }
933}
934
935impl<S: Schema> Predicate<S> {
936 fn new(expr: PredicateExpr) -> Self {
937 Self {
938 expr,
939 marker: PhantomData,
940 }
941 }
942
943 #[must_use]
945 pub fn and(self, other: Predicate<S>) -> Predicate<S> {
946 self & other
947 }
948
949 #[must_use]
951 pub fn or(self, other: Predicate<S>) -> Predicate<S> {
952 self | other
953 }
954
955 #[must_use]
957 #[allow(clippy::should_implement_trait)]
958 pub fn not(self) -> Predicate<S> {
959 !self
960 }
961}
962
963impl<'db, S: Schema> QuerySession<'db, S> {
964 #[allow(clippy::too_many_arguments)]
973 pub fn reachable<
974 R,
975 FromOwner,
976 FromPlayers,
977 ToOwner,
978 ToPlayers,
979 Source,
980 SourceMode,
981 Target,
982 TargetMode,
983 >(
984 &self,
985 relation: TypeToken<R>,
986 role_from: RoleToken<FromOwner, FromPlayers>,
987 role_to: RoleToken<ToOwner, ToPlayers>,
988 source: Binding<S, Source, SourceMode>,
989 target: Binding<S, Target, TargetMode>,
990 min_depth: u8,
991 max_depth: u8,
992 ) -> Result<Predicate<S>>
993 where
994 R: RelationModel<Schema = S>
995 + CompleteModel
996 + RoleTokenCompatible<FromOwner, FromPlayers>
997 + RoleTokenCompatible<ToOwner, ToPlayers>,
998 FromOwner: RelationModel<Schema = S>,
999 ToOwner: RelationModel<Schema = S>,
1000 FromPlayers: RolePlayerBinding<Source, SourceMode>,
1001 ToPlayers: RolePlayerBinding<Target, TargetMode>,
1002 Source: ThingModel<Schema = S>,
1003 SourceMode: SelectionMode,
1004 Target: ThingModel<Schema = S>,
1005 TargetMode: SelectionMode,
1006 {
1007 let predicate = Predicate::new(PredicateExpr::Reachable {
1008 relation_type_id_json: relation.type_id_json(),
1009 role_from_id_json: role_from.role_id_json(),
1010 role_to_id_json: role_to.role_id_json(),
1011 source: source.key,
1012 target: target.key,
1013 min_depth,
1014 max_depth,
1015 });
1016 self.lower_predicate(&predicate.expr)?;
1017 Ok(predicate)
1018 }
1019}
1020
1021impl<S: Schema> std::ops::BitAnd for Predicate<S> {
1022 type Output = Predicate<S>;
1023 fn bitand(self, other: Predicate<S>) -> Predicate<S> {
1024 let mut terms = match self.expr {
1025 PredicateExpr::And(terms) => terms,
1026 expr => vec![expr],
1027 };
1028 match other.expr {
1029 PredicateExpr::And(more) => terms.extend(more),
1030 expr => terms.push(expr),
1031 }
1032 Predicate::new(PredicateExpr::And(terms))
1033 }
1034}
1035
1036impl<S: Schema> std::ops::BitOr for Predicate<S> {
1037 type Output = Predicate<S>;
1038 fn bitor(self, other: Predicate<S>) -> Predicate<S> {
1039 let mut terms = match self.expr {
1040 PredicateExpr::Or(terms) => terms,
1041 expr => vec![expr],
1042 };
1043 match other.expr {
1044 PredicateExpr::Or(more) => terms.extend(more),
1045 expr => terms.push(expr),
1046 }
1047 Predicate::new(PredicateExpr::Or(terms))
1048 }
1049}
1050
1051impl<S: Schema> std::ops::Not for Predicate<S> {
1052 type Output = Predicate<S>;
1053 fn not(self) -> Predicate<S> {
1054 Predicate::new(PredicateExpr::Not(Box::new(self.expr)))
1055 }
1056}
1057
1058pub struct BoundField<S: Schema, Owner: Model<Schema = S>, V> {
1064 key: BindingKey,
1065 owns_id_json: &'static str,
1066 marker: PhantomData<fn() -> (Owner, V)>,
1067}
1068
1069impl<S: Schema, Owner: Model<Schema = S>, V> Copy for BoundField<S, Owner, V> {}
1070impl<S: Schema, Owner: Model<Schema = S>, V> Clone for BoundField<S, Owner, V> {
1071 fn clone(&self) -> Self {
1072 *self
1073 }
1074}
1075
1076impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Binding<S, M, Mode> {
1077 #[must_use]
1083 pub fn field<Owner, V>(self, token: FieldToken<Owner, V>) -> BoundField<S, Owner, V>
1084 where
1085 Owner: Model<Schema = S>,
1086 M: crate::__codegen::NominalUpcast<Owner>,
1087 {
1088 BoundField {
1089 key: self.key,
1090 owns_id_json: token.owns_id_json(),
1091 marker: PhantomData,
1092 }
1093 }
1094}
1095
1096impl<S: Schema, M: RelationModel<Schema = S> + ThingModel<Schema = S>, Mode: SelectionMode>
1097 Binding<S, M, Mode>
1098{
1099 #[must_use]
1104 pub fn role<Owner, Players>(
1105 self,
1106 token: RoleToken<Owner, Players>,
1107 ) -> BoundRole<S, Owner, Players>
1108 where
1109 Owner: RelationModel<Schema = S>,
1110 M: RoleTokenCompatible<Owner, Players>,
1111 {
1112 BoundRole {
1113 key: self.key,
1114 role_id_json: token.role_id_json(),
1115 marker: PhantomData,
1116 }
1117 }
1118}
1119
1120impl<S: Schema, Owner: Model<Schema = S>, V> BoundField<S, Owner, V> {
1121 pub(crate) fn reduction_input(self) -> (BindingKey, &'static str) {
1122 (self.key, self.owns_id_json)
1123 }
1124
1125 fn value_predicate(self, operator: ComparisonOp, value: AttributeValue) -> Predicate<S> {
1126 Predicate::new(PredicateExpr::FieldValue {
1127 binding: self.key,
1128 owns_id_json: self.owns_id_json,
1129 operator,
1130 value,
1131 })
1132 }
1133
1134 #[must_use]
1136 pub fn eq<O>(self, operand: O) -> Predicate<S>
1137 where
1138 V: QueryValued,
1139 O: QueryOperand<Domain = V::Domain>,
1140 {
1141 self.value_predicate(ComparisonOp::Equal, operand.into_operand())
1142 }
1143
1144 #[must_use]
1146 pub fn ne<O>(self, operand: O) -> Predicate<S>
1147 where
1148 V: QueryValued,
1149 O: QueryOperand<Domain = V::Domain>,
1150 {
1151 self.value_predicate(ComparisonOp::NotEqual, operand.into_operand())
1152 }
1153
1154 #[must_use]
1157 pub fn lt(self, operand: impl OrderedOperand) -> Predicate<S>
1158 where
1159 V: crate::__codegen::OrderedValued,
1160 {
1161 self.value_predicate(ComparisonOp::LessThan, operand.into_operand())
1162 }
1163
1164 #[must_use]
1167 pub fn le(self, operand: impl OrderedOperand) -> Predicate<S>
1168 where
1169 V: crate::__codegen::OrderedValued,
1170 {
1171 self.value_predicate(ComparisonOp::LessThanOrEqual, operand.into_operand())
1172 }
1173
1174 #[must_use]
1177 pub fn gt(self, operand: impl OrderedOperand) -> Predicate<S>
1178 where
1179 V: crate::__codegen::OrderedValued,
1180 {
1181 self.value_predicate(ComparisonOp::GreaterThan, operand.into_operand())
1182 }
1183
1184 #[must_use]
1187 pub fn ge(self, operand: impl OrderedOperand) -> Predicate<S>
1188 where
1189 V: crate::__codegen::OrderedValued,
1190 {
1191 self.value_predicate(ComparisonOp::GreaterThanOrEqual, operand.into_operand())
1192 }
1193
1194 #[must_use]
1197 pub fn contains(self, text: crate::value::Text) -> Predicate<S>
1198 where
1199 V: crate::__codegen::TextValued,
1200 {
1201 self.value_predicate(
1202 ComparisonOp::Contains,
1203 AttributeValue::String(text.into_string()),
1204 )
1205 }
1206
1207 #[must_use]
1210 pub fn starts_with(self, text: crate::value::Text) -> Predicate<S>
1211 where
1212 V: crate::__codegen::TextValued,
1213 {
1214 self.value_predicate(
1215 ComparisonOp::StartsWith,
1216 AttributeValue::String(text.into_string()),
1217 )
1218 }
1219
1220 #[must_use]
1223 pub fn ends_with(self, text: crate::value::Text) -> Predicate<S>
1224 where
1225 V: crate::__codegen::TextValued,
1226 {
1227 self.value_predicate(
1228 ComparisonOp::EndsWith,
1229 AttributeValue::String(text.into_string()),
1230 )
1231 }
1232
1233 #[must_use]
1236 pub fn regex(self, pattern: crate::value::Regex) -> Predicate<S>
1237 where
1238 V: crate::__codegen::TextValued,
1239 {
1240 self.value_predicate(
1241 ComparisonOp::Regex,
1242 AttributeValue::String(pattern.into_string()),
1243 )
1244 }
1245
1246 #[must_use]
1248 pub fn is_present(self) -> Predicate<S> {
1249 Predicate::new(PredicateExpr::FieldPresence {
1250 binding: self.key,
1251 owns_id_json: self.owns_id_json,
1252 present: true,
1253 })
1254 }
1255
1256 #[must_use]
1258 pub fn is_missing(self) -> Predicate<S> {
1259 Predicate::new(PredicateExpr::FieldPresence {
1260 binding: self.key,
1261 owns_id_json: self.owns_id_json,
1262 present: false,
1263 })
1264 }
1265
1266 #[must_use]
1269 pub fn eq_field<Owner2>(self, other: BoundField<S, Owner2, V>) -> Predicate<S>
1270 where
1271 Owner2: Model<Schema = S>,
1272 {
1273 Predicate::new(PredicateExpr::FieldField {
1274 left_binding: self.key,
1275 left_owns_id_json: self.owns_id_json,
1276 operator: ComparisonOp::Equal,
1277 right_binding: other.key,
1278 right_owns_id_json: other.owns_id_json,
1279 })
1280 }
1281
1282 #[must_use]
1285 pub fn asc(self) -> Order<S> {
1286 Order {
1287 key: self.key,
1288 owns_id_json: self.owns_id_json,
1289 direction: SortDirection::Ascending,
1290 missing: MissingOrder::Reject,
1291 marker: PhantomData,
1292 }
1293 }
1294
1295 #[must_use]
1298 pub fn desc(self) -> Order<S> {
1299 Order {
1300 key: self.key,
1301 owns_id_json: self.owns_id_json,
1302 direction: SortDirection::Descending,
1303 missing: MissingOrder::Reject,
1304 marker: PhantomData,
1305 }
1306 }
1307}
1308
1309#[derive(Debug)]
1311pub struct Order<S: Schema> {
1312 key: BindingKey,
1313 owns_id_json: &'static str,
1314 direction: SortDirection,
1315 missing: MissingOrder,
1316 marker: PhantomData<fn() -> S>,
1317}
1318
1319impl<S: Schema> Copy for Order<S> {}
1320impl<S: Schema> Clone for Order<S> {
1321 fn clone(&self) -> Self {
1322 *self
1323 }
1324}
1325
1326impl<S: Schema> Order<S> {
1327 #[must_use]
1329 pub fn missing_first(mut self) -> Self {
1330 self.missing = MissingOrder::First;
1331 self
1332 }
1333
1334 #[must_use]
1336 pub fn missing_last(mut self) -> Self {
1337 self.missing = MissingOrder::Last;
1338 self
1339 }
1340}
1341
1342pub struct Collected<S: Schema, B: Selectable<S>> {
1344 selection: B,
1345 distinct: bool,
1346 order: Vec<Order<S>>,
1347}
1348
1349impl<S: Schema, B: Selectable<S>> Clone for Collected<S, B> {
1350 fn clone(&self) -> Self {
1351 Self {
1352 selection: self.selection,
1353 distinct: self.distinct,
1354 order: self.order.clone(),
1355 }
1356 }
1357}
1358
1359impl<S: Schema, B: Selectable<S>> Collected<S, B> {
1360 #[must_use]
1362 pub fn distinct(mut self) -> Self {
1363 self.distinct = true;
1364 self
1365 }
1366
1367 pub fn order_by(mut self, order: Order<S>) -> Result<Self> {
1369 if order.key != self.selection.binding_key() {
1370 return Err(Error::model_validation(
1371 ModelValidationPhase::Input,
1372 "collection_order_binding_mismatch",
1373 vec![],
1374 "collection ordering must reference the collected binding",
1375 None,
1376 ));
1377 }
1378 self.order.push(order);
1379 Ok(self)
1380 }
1381}
1382
1383pub struct BoundRole<S: Schema, Owner: Model<Schema = S>, Players> {
1386 key: BindingKey,
1387 role_id_json: &'static str,
1388 marker: PhantomData<fn() -> (Owner, Players)>,
1389}
1390
1391impl<S: Schema, Owner: Model<Schema = S>, Players> Copy for BoundRole<S, Owner, Players> {}
1392impl<S: Schema, Owner: Model<Schema = S>, Players> Clone for BoundRole<S, Owner, Players> {
1393 fn clone(&self) -> Self {
1394 *self
1395 }
1396}
1397
1398impl<S: Schema, Owner: Model<Schema = S>, Players> BoundRole<S, Owner, Players> {
1399 #[must_use]
1402 pub fn connects<MP: ThingModel<Schema = S>, ModeP: SelectionMode>(
1403 self,
1404 player: Binding<S, MP, ModeP>,
1405 ) -> Predicate<S>
1406 where
1407 Players: RolePlayerBinding<MP, ModeP>,
1408 {
1409 Predicate::new(PredicateExpr::Connects {
1410 relation: self.key,
1411 role_id_json: self.role_id_json,
1412 player: player.key,
1413 })
1414 }
1415}
1416
1417mod selectable_sealed {
1418 pub trait Sealed {}
1419}
1420
1421pub trait Selectable<S: Schema>: selectable_sealed::Sealed + Copy {
1423 type Output;
1425 #[doc(hidden)]
1426 fn binding_key(self) -> BindingKey;
1427 #[doc(hidden)]
1428 fn materialize_output(row: &HydratedRow) -> std::result::Result<Self::Output, ValidationError>;
1429
1430 #[doc(hidden)]
1431 fn __selection_handle(self, session: &QuerySession<'_, S>) -> Result<OrmSelectionHandle> {
1432 Ok(session.handle_by_key(self.binding_key())?.one())
1433 }
1434
1435 #[doc(hidden)]
1436 fn __materialize_slot(
1437 self,
1438 session: &QuerySession<'_, S>,
1439 slot: &SlotValue,
1440 ) -> Result<Self::Output> {
1441 let SlotValue::One(thing) = slot else {
1442 return Err(Error::model_validation(
1443 ModelValidationPhase::Hydration,
1444 "wrong_result_shape",
1445 vec![],
1446 "provider returned a collection slot for a singular selection",
1447 None,
1448 ));
1449 };
1450 let row = session.client_row_for(thing)?;
1451 Self::materialize_output(&row)
1452 .map_err(|error| map_validation_error(error, ModelValidationPhase::Hydration))
1453 }
1454}
1455
1456impl<S: Schema, M: ThingModel<Schema = S> + CompleteModel> selectable_sealed::Sealed
1457 for Binding<S, M, Exact>
1458{
1459}
1460impl<S: Schema, M: ThingModel<Schema = S> + CompleteModel> Selectable<S> for Binding<S, M, Exact> {
1461 type Output = M;
1462 fn binding_key(self) -> BindingKey {
1463 self.key()
1464 }
1465 fn materialize_output(row: &HydratedRow) -> std::result::Result<M, ValidationError> {
1466 M::materialize(row, &HydrationCapability::new())
1467 }
1468}
1469
1470impl<S: Schema, M: ThingModel<Schema = S> + SubtypeRootModel> selectable_sealed::Sealed
1471 for Binding<S, M, Subtypes>
1472{
1473}
1474impl<S: Schema, M: ThingModel<Schema = S> + SubtypeRootModel> Selectable<S>
1475 for Binding<S, M, Subtypes>
1476{
1477 type Output = M::Subtypes;
1478 fn binding_key(self) -> BindingKey {
1479 self.key()
1480 }
1481 fn materialize_output(row: &HydratedRow) -> std::result::Result<M::Subtypes, ValidationError> {
1482 M::__tb_dispatch_subtype(row, &HydrationCapability::new())
1483 }
1484}
1485
1486mod selected_slot_sealed {
1487 pub trait Sealed<S> {}
1488}
1489
1490#[doc(hidden)]
1493pub trait SelectedSlot<S: Schema>: selected_slot_sealed::Sealed<S> + Clone {
1494 type Output;
1495
1496 fn __selection_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmSelectionHandle>;
1497
1498 fn __materialize_slot(
1499 &self,
1500 session: &QuerySession<'_, S>,
1501 slot: &SlotValue,
1502 ) -> Result<Self::Output>;
1503}
1504
1505impl<S: Schema, B: Selectable<S>> selected_slot_sealed::Sealed<S> for B {}
1506impl<S: Schema, B: Selectable<S>> SelectedSlot<S> for B {
1507 type Output = B::Output;
1508
1509 fn __selection_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmSelectionHandle> {
1510 (*self).__selection_handle(session)
1511 }
1512
1513 fn __materialize_slot(
1514 &self,
1515 session: &QuerySession<'_, S>,
1516 slot: &SlotValue,
1517 ) -> Result<Self::Output> {
1518 (*self).__materialize_slot(session, slot)
1519 }
1520}
1521
1522impl<S: Schema, B: Selectable<S>> selected_slot_sealed::Sealed<S> for Collected<S, B> {}
1523impl<S: Schema, B: Selectable<S>> SelectedSlot<S> for Collected<S, B> {
1524 type Output = Vec<B::Output>;
1525
1526 fn __selection_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmSelectionHandle> {
1527 let binding = session.handle_by_key(self.selection.binding_key())?;
1528 let mut selection = binding
1529 .collect()
1530 .distinct(self.distinct)
1531 .map_err(Error::from_orm)?;
1532 for order in &self.order {
1533 selection = selection
1534 .order_by(session.lower_order(order)?)
1535 .map_err(Error::from_orm)?;
1536 }
1537 Ok(selection)
1538 }
1539
1540 fn __materialize_slot(
1541 &self,
1542 session: &QuerySession<'_, S>,
1543 slot: &SlotValue,
1544 ) -> Result<Self::Output> {
1545 let SlotValue::Many(things) = slot else {
1546 return Err(Error::model_validation(
1547 ModelValidationPhase::Hydration,
1548 "wrong_result_shape",
1549 vec![],
1550 "provider returned a singular slot for a collection selection",
1551 None,
1552 ));
1553 };
1554 let mut outputs = Vec::with_capacity(things.len());
1555 for thing in things {
1556 let row = session.client_row_for(thing)?;
1557 outputs.push(
1558 B::materialize_output(&row).map_err(|error| {
1559 map_validation_error(error, ModelValidationPhase::Hydration)
1560 })?,
1561 );
1562 }
1563 Ok(outputs)
1564 }
1565}
1566
1567mod selected_shape_sealed {
1568 pub trait Sealed<S> {}
1569}
1570
1571pub trait SelectedShape<S: Schema>: selected_shape_sealed::Sealed<S> + Clone {
1576 type Output;
1578
1579 #[doc(hidden)]
1580 fn __shape_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmShapeHandle>;
1581
1582 #[doc(hidden)]
1583 fn __materialize_row(
1584 &self,
1585 session: &QuerySession<'_, S>,
1586 row: &MatchRow,
1587 ) -> Result<Self::Output>;
1588}
1589
1590impl<S: Schema, B: Selectable<S>> selected_shape_sealed::Sealed<S> for B {}
1591impl<S: Schema, B: Selectable<S>> SelectedShape<S> for B {
1592 type Output = B::Output;
1593
1594 fn __shape_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmShapeHandle> {
1595 session
1596 .session
1597 .positional([SelectedSlot::__selection_handle(self, session)?])
1598 .map_err(Error::from_orm)
1599 }
1600
1601 fn __materialize_row(
1602 &self,
1603 session: &QuerySession<'_, S>,
1604 row: &MatchRow,
1605 ) -> Result<Self::Output> {
1606 let [slot] = row.slots() else {
1607 return Err(selected_shape_arity_error(1, row.slots().len()));
1608 };
1609 SelectedSlot::__materialize_slot(self, session, slot)
1610 }
1611}
1612
1613impl<S: Schema, B: Selectable<S>> selected_shape_sealed::Sealed<S> for Collected<S, B> {}
1614impl<S: Schema, B: Selectable<S>> SelectedShape<S> for Collected<S, B> {
1615 type Output = Vec<B::Output>;
1616
1617 fn __shape_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmShapeHandle> {
1618 session
1619 .session
1620 .positional([SelectedSlot::__selection_handle(self, session)?])
1621 .map_err(Error::from_orm)
1622 }
1623
1624 fn __materialize_row(
1625 &self,
1626 session: &QuerySession<'_, S>,
1627 row: &MatchRow,
1628 ) -> Result<Self::Output> {
1629 let [slot] = row.slots() else {
1630 return Err(selected_shape_arity_error(1, row.slots().len()));
1631 };
1632 SelectedSlot::__materialize_slot(self, session, slot)
1633 }
1634}
1635
1636mod singular_selected_shape_sealed {
1637 pub trait Sealed<S> {}
1638}
1639
1640pub trait SingularSelectedShape<S: Schema>:
1642 SelectedShape<S> + singular_selected_shape_sealed::Sealed<S>
1643{
1644}
1645
1646impl<S: Schema, B: Selectable<S>> singular_selected_shape_sealed::Sealed<S> for B {}
1647impl<S: Schema, B: Selectable<S>> SingularSelectedShape<S> for B {}
1648
1649#[doc(hidden)]
1650pub trait SelectedTuple<S: Schema>: Clone {
1651 const ARITY: usize;
1652
1653 type Output;
1654
1655 fn __selection_handles(&self, session: &QuerySession<'_, S>)
1656 -> Result<Vec<OrmSelectionHandle>>;
1657
1658 fn __materialize_slots(
1659 &self,
1660 session: &QuerySession<'_, S>,
1661 slots: &[SlotValue],
1662 ) -> Result<Self::Output>;
1663}
1664
1665#[doc(hidden)]
1666pub trait SingularSelectedTuple<S: Schema>: SelectedTuple<S> {}
1667
1668macro_rules! selected_tuple {
1669 ($length:literal; $(($type:ident, $index:tt)),+ $(,)?) => {
1670 impl<S: Schema, $($type: SelectedSlot<S>),+> SelectedTuple<S> for ($($type,)+) {
1671 const ARITY: usize = $length;
1672
1673 type Output = ($(<$type as SelectedSlot<S>>::Output,)+);
1674
1675 fn __selection_handles(
1676 &self,
1677 session: &QuerySession<'_, S>,
1678 ) -> Result<Vec<OrmSelectionHandle>> {
1679 Ok(vec![$(SelectedSlot::__selection_handle(&self.$index, session)?),+])
1680 }
1681
1682 fn __materialize_slots(
1683 &self,
1684 session: &QuerySession<'_, S>,
1685 slots: &[SlotValue],
1686 ) -> Result<Self::Output> {
1687 let slots: &[SlotValue; $length] = slots
1688 .try_into()
1689 .map_err(|_| selected_shape_arity_error($length, slots.len()))?;
1690 Ok(($(SelectedSlot::__materialize_slot(
1691 &self.$index,
1692 session,
1693 &slots[$index],
1694 )?,)+))
1695 }
1696 }
1697
1698 impl<S: Schema, $($type: SelectedSlot<S>),+> selected_shape_sealed::Sealed<S>
1699 for ($($type,)+)
1700 {
1701 }
1702
1703 impl<S: Schema, $($type: SelectedSlot<S>),+> SelectedShape<S> for ($($type,)+) {
1704 type Output = <Self as SelectedTuple<S>>::Output;
1705
1706 fn __shape_handle(
1707 &self,
1708 session: &QuerySession<'_, S>,
1709 ) -> Result<OrmShapeHandle> {
1710 session
1711 .session
1712 .positional(self.__selection_handles(session)?)
1713 .map_err(Error::from_orm)
1714 }
1715
1716 fn __materialize_row(
1717 &self,
1718 session: &QuerySession<'_, S>,
1719 row: &MatchRow,
1720 ) -> Result<Self::Output> {
1721 self.__materialize_slots(session, row.slots())
1722 }
1723 }
1724
1725 impl<S: Schema, $($type: Selectable<S>),+> singular_selected_shape_sealed::Sealed<S>
1726 for ($($type,)+)
1727 {
1728 }
1729
1730 impl<S: Schema, $($type: Selectable<S>),+> SingularSelectedShape<S>
1731 for ($($type,)+)
1732 {
1733 }
1734
1735 impl<S: Schema, $($type: Selectable<S>),+> SingularSelectedTuple<S>
1736 for ($($type,)+)
1737 {
1738 }
1739 };
1740}
1741
1742selected_tuple!(1; (A, 0));
1743selected_tuple!(2; (A, 0), (B, 1));
1744selected_tuple!(3; (A, 0), (B, 1), (C, 2));
1745selected_tuple!(4; (A, 0), (B, 1), (C, 2), (D, 3));
1746selected_tuple!(5; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4));
1747selected_tuple!(6; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5));
1748selected_tuple!(7; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6));
1749selected_tuple!(8; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7));
1750selected_tuple!(9; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8));
1751selected_tuple!(10; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9));
1752selected_tuple!(11; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9), (K, 10));
1753selected_tuple!(12; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9), (K, 10), (L, 11));
1754selected_tuple!(13; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9), (K, 10), (L, 11), (M, 12));
1755selected_tuple!(14; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9), (K, 10), (L, 11), (M, 12), (N, 13));
1756selected_tuple!(15; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9), (K, 10), (L, 11), (M, 12), (N, 13), (O, 14));
1757selected_tuple!(16; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9), (K, 10), (L, 11), (M, 12), (N, 13), (O, 14), (P, 15));
1758
1759#[doc(hidden)]
1761pub trait SelectedRowSpec<Outputs>: Sized {
1762 fn __from_selected_outputs(outputs: Outputs) -> Self;
1763}
1764
1765pub struct NamedSelection<S: Schema, Row, Slots> {
1767 slots: Slots,
1768 names: &'static [&'static str],
1769 marker: PhantomData<fn() -> (S, Row)>,
1770}
1771
1772impl<S: Schema, Row, Slots: Clone> Clone for NamedSelection<S, Row, Slots> {
1773 fn clone(&self) -> Self {
1774 Self {
1775 slots: self.slots.clone(),
1776 names: self.names,
1777 marker: PhantomData,
1778 }
1779 }
1780}
1781
1782impl<S: Schema, Row, Slots: SelectedTuple<S>> NamedSelection<S, Row, Slots> {
1783 #[doc(hidden)]
1784 pub fn __new(slots: Slots, names: &'static [&'static str]) -> Result<Self> {
1785 if names.len() != Slots::ARITY {
1786 return Err(Error::model_validation(
1787 ModelValidationPhase::Input,
1788 "invalid_selected_shape",
1789 vec![],
1790 format!(
1791 "named selected shape has {} names for {} slots",
1792 names.len(),
1793 Slots::ARITY
1794 ),
1795 None,
1796 ));
1797 }
1798 Ok(Self {
1799 slots,
1800 names,
1801 marker: PhantomData,
1802 })
1803 }
1804}
1805
1806impl<S: Schema, Row, Slots> selected_shape_sealed::Sealed<S> for NamedSelection<S, Row, Slots> {}
1807
1808impl<S, Row, Slots> SelectedShape<S> for NamedSelection<S, Row, Slots>
1809where
1810 S: Schema,
1811 Slots: SelectedTuple<S>,
1812 Row: SelectedRowSpec<Slots::Output>,
1813{
1814 type Output = Row;
1815
1816 fn __shape_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmShapeHandle> {
1817 let selections = self.slots.__selection_handles(session)?;
1818 session
1819 .session
1820 .named(
1821 self.names
1822 .iter()
1823 .copied()
1824 .zip(selections)
1825 .map(|(name, selection)| (name.to_owned(), selection)),
1826 )
1827 .map_err(Error::from_orm)
1828 }
1829
1830 fn __materialize_row(
1831 &self,
1832 session: &QuerySession<'_, S>,
1833 row: &MatchRow,
1834 ) -> Result<Self::Output> {
1835 Ok(Row::__from_selected_outputs(
1836 self.slots.__materialize_slots(session, row.slots())?,
1837 ))
1838 }
1839}
1840
1841impl<S, Row, Slots> singular_selected_shape_sealed::Sealed<S> for NamedSelection<S, Row, Slots>
1842where
1843 S: Schema,
1844 Slots: SingularSelectedTuple<S>,
1845 Row: SelectedRowSpec<Slots::Output>,
1846{
1847}
1848
1849impl<S, Row, Slots> SingularSelectedShape<S> for NamedSelection<S, Row, Slots>
1850where
1851 S: Schema,
1852 Slots: SingularSelectedTuple<S>,
1853 Row: SelectedRowSpec<Slots::Output>,
1854{
1855}
1856
1857fn selected_shape_arity_error(expected: usize, actual: usize) -> Error {
1858 Error::model_validation(
1859 ModelValidationPhase::Hydration,
1860 "wrong_result_shape",
1861 vec![],
1862 format!("selected row has {actual} slots; expected {expected}"),
1863 None,
1864 )
1865}
1866
1867#[derive(Debug)]
1869pub struct RowsOptions<S: Schema> {
1870 limit: u64,
1871 offset: u64,
1872 order: Vec<Order<S>>,
1873}
1874
1875impl<S: Schema> Clone for RowsOptions<S> {
1876 fn clone(&self) -> Self {
1877 Self {
1878 limit: self.limit,
1879 offset: self.offset,
1880 order: self.order.clone(),
1881 }
1882 }
1883}
1884
1885impl<S: Schema> RowsOptions<S> {
1886 #[must_use]
1888 pub fn new(limit: u64) -> Self {
1889 Self {
1890 limit,
1891 offset: 0,
1892 order: Vec::new(),
1893 }
1894 }
1895
1896 #[must_use]
1898 pub fn offset(mut self, offset: u64) -> Self {
1899 self.offset = offset;
1900 self
1901 }
1902
1903 #[must_use]
1905 pub fn order_by(mut self, order: Order<S>) -> Self {
1906 self.order.push(order);
1907 self
1908 }
1909}
1910
1911#[derive(Debug)]
1913pub struct PageOptions<S: Schema> {
1914 limit: u64,
1915 offset: u64,
1916 include_total: bool,
1917 order: Vec<Order<S>>,
1918}
1919
1920impl<S: Schema> Clone for PageOptions<S> {
1921 fn clone(&self) -> Self {
1922 Self {
1923 limit: self.limit,
1924 offset: self.offset,
1925 include_total: self.include_total,
1926 order: self.order.clone(),
1927 }
1928 }
1929}
1930
1931impl<S: Schema> PageOptions<S> {
1932 #[must_use]
1934 pub fn new(limit: u64) -> Self {
1935 Self {
1936 limit,
1937 offset: 0,
1938 include_total: false,
1939 order: Vec::new(),
1940 }
1941 }
1942
1943 #[must_use]
1945 pub fn offset(mut self, offset: u64) -> Self {
1946 self.offset = offset;
1947 self
1948 }
1949
1950 #[must_use]
1952 pub fn include_total(mut self, include_total: bool) -> Self {
1953 self.include_total = include_total;
1954 self
1955 }
1956
1957 #[must_use]
1959 pub fn order_by(mut self, order: Order<S>) -> Self {
1960 self.order.push(order);
1961 self
1962 }
1963}
1964
1965#[derive(Clone, Debug)]
1967pub struct Page<T> {
1968 items: Vec<T>,
1969 offset: u64,
1970 limit: u64,
1971 total: Option<u64>,
1972}
1973
1974impl<T> Page<T> {
1975 #[must_use]
1977 pub fn items(&self) -> &[T] {
1978 &self.items
1979 }
1980
1981 #[must_use]
1983 pub const fn offset(&self) -> u64 {
1984 self.offset
1985 }
1986
1987 #[must_use]
1989 pub const fn limit(&self) -> u64 {
1990 self.limit
1991 }
1992
1993 #[must_use]
1995 pub const fn total(&self) -> Option<u64> {
1996 self.total
1997 }
1998
1999 #[must_use]
2001 pub fn into_items(self) -> Vec<T> {
2002 self.items
2003 }
2004}
2005
2006pub struct Query<'s, 'db, S: Schema, Shape: SelectedShape<S>> {
2011 session: &'s QuerySession<'db, S>,
2012 selection: Shape,
2013 hidden: Vec<BindingKey>,
2014 predicates: Vec<Predicate<S>>,
2015 allowed_cross_joins: Vec<(BindingKey, BindingKey)>,
2016}
2017
2018impl<'s, 'db, S: Schema, Shape: SelectedShape<S>> Clone for Query<'s, 'db, S, Shape> {
2019 fn clone(&self) -> Self {
2020 Self {
2021 session: self.session,
2022 selection: self.selection.clone(),
2023 hidden: self.hidden.clone(),
2024 predicates: self.predicates.clone(),
2025 allowed_cross_joins: self.allowed_cross_joins.clone(),
2026 }
2027 }
2028}
2029
2030impl<'db, S: Schema> QuerySession<'db, S> {
2031 pub fn query<Shape: SelectedShape<S>>(
2033 &self,
2034 selection: Shape,
2035 ) -> Result<Query<'_, 'db, S, Shape>> {
2036 selection.__shape_handle(self)?;
2037 Ok(Query {
2038 session: self,
2039 selection,
2040 hidden: Vec::new(),
2041 predicates: Vec::new(),
2042 allowed_cross_joins: Vec::new(),
2043 })
2044 }
2045}
2046
2047impl<'s, 'db, S: Schema, Shape: SelectedShape<S>> Query<'s, 'db, S, Shape> {
2048 pub fn match_<M: ThingModel<Schema = S>, Mode: SelectionMode>(
2050 &self,
2051 binding: Binding<S, M, Mode>,
2052 ) -> Result<Self> {
2053 self.session.handle_by_key(binding.key())?;
2054 let mut next = self.clone();
2055 if !next.hidden.contains(&binding.key()) {
2056 next.hidden.push(binding.key());
2057 }
2058 Ok(next)
2059 }
2060
2061 pub fn where_(&self, predicate: Predicate<S>) -> Result<Self> {
2063 let mut next = self.clone();
2064 next.predicates.push(predicate);
2065 Ok(next)
2066 }
2067
2068 pub fn where_all(&self, predicates: impl IntoIterator<Item = Predicate<S>>) -> Result<Self> {
2070 let mut next = self.clone();
2071 next.predicates.extend(predicates);
2072 Ok(next)
2073 }
2074
2075 pub fn allow_cross_join<L: Selectable<S>, R: Selectable<S>>(
2078 &self,
2079 left: L,
2080 right: R,
2081 ) -> Result<Self> {
2082 let left = left.binding_key();
2083 let right = right.binding_key();
2084 self.session.handle_by_key(left)?;
2085 self.session.handle_by_key(right)?;
2086 if left == right {
2087 return Err(Error::model_validation(
2088 ModelValidationPhase::Input,
2089 "self_cross_join",
2090 vec![],
2091 "cross-join permission requires two distinct generated bindings",
2092 None,
2093 ));
2094 }
2095 let pair = if left.index < right.index {
2096 (left, right)
2097 } else {
2098 (right, left)
2099 };
2100 let mut next = self.clone();
2101 if !next.allowed_cross_joins.contains(&pair) {
2102 next.allowed_cross_joins.push(pair);
2103 }
2104 Ok(next)
2105 }
2106
2107 fn lineage(&self) -> Result<OrmQueryHandle> {
2108 self.lineage_with_hidden(&[])
2109 }
2110
2111 fn lineage_with_hidden(&self, hidden: &[BindingKey]) -> Result<OrmQueryHandle> {
2112 let shape = self.selection.__shape_handle(self.session)?;
2113 let mut query = self.session.session.query(shape).map_err(Error::from_orm)?;
2114 let mut hidden_keys = self.hidden.clone();
2115 for key in hidden {
2116 if !hidden_keys.contains(key) {
2117 hidden_keys.push(*key);
2118 }
2119 }
2120 for key in hidden_keys {
2121 let hidden = self.session.handle_by_key(key)?;
2122 query = query.add_hidden(hidden.clone()).map_err(Error::from_orm)?;
2123 }
2124 for (left, right) in &self.allowed_cross_joins {
2125 let left = self.session.handle_by_key(*left)?;
2126 let right = self.session.handle_by_key(*right)?;
2127 query = query
2128 .allow_cross_join(left, right)
2129 .map_err(Error::from_orm)?;
2130 }
2131 for predicate in &self.predicates {
2132 let lowered = self.session.lower_predicate(&predicate.expr)?;
2133 query = query.where_predicate(lowered).map_err(Error::from_orm)?;
2134 }
2135 Ok(query)
2136 }
2137
2138 pub(crate) fn validated_rows(
2139 &self,
2140 order: &[Order<S>],
2141 window: Window,
2142 ) -> Result<ValidatedMatchRequest> {
2143 let lineage = self.lineage()?;
2144 let mut lowered_orders = Vec::with_capacity(order.len());
2145 for term in order {
2146 lowered_orders.push(self.session.lower_order(term)?);
2147 }
2148 lineage
2149 .validate_fetch_rows(&lowered_orders, window, RowCardinality::BoundedMany)
2150 .map_err(Error::from_orm)
2151 }
2152
2153 pub(crate) fn validated_one(&self) -> Result<ValidatedMatchRequest> {
2154 self.lineage()?
2155 .validate_fetch_rows(
2156 &[],
2157 Window {
2158 offset: 0,
2159 limit: 1,
2160 },
2161 RowCardinality::ExactlyOne,
2162 )
2163 .map_err(Error::from_orm)
2164 }
2165
2166 pub(crate) fn validated_page<R: Selectable<S>>(
2167 &self,
2168 root: R,
2169 order: &[Order<S>],
2170 window: Window,
2171 include_total: bool,
2172 ) -> Result<ValidatedMatchRequest> {
2173 let root = self.session.handle_by_key(root.binding_key())?;
2174 let lineage = self.lineage()?;
2175 let mut lowered_orders = Vec::with_capacity(order.len());
2176 for term in order {
2177 lowered_orders.push(self.session.lower_order(term)?);
2178 }
2179 lineage
2180 .validate_page_by(root, &lowered_orders, window, include_total)
2181 .map_err(Error::from_orm)
2182 }
2183
2184 pub(crate) fn validated_count_by<R: Selectable<S>>(
2185 &self,
2186 root: R,
2187 ) -> Result<ValidatedMatchRequest> {
2188 let root = self.session.handle_by_key(root.binding_key())?;
2189 self.lineage()?
2190 .validate_count_by(root)
2191 .map_err(Error::from_orm)
2192 }
2193
2194 pub(crate) fn validated_exists_by<R: Selectable<S>>(
2195 &self,
2196 root: R,
2197 ) -> Result<ValidatedMatchRequest> {
2198 let root = self.session.handle_by_key(root.binding_key())?;
2199 self.lineage()?
2200 .validate_exists_by(root)
2201 .map_err(Error::from_orm)
2202 }
2203
2204 fn materialize_rows(&self, rows: &[MatchRow]) -> Result<Vec<Shape::Output>> {
2205 let mut outputs = Vec::with_capacity(rows.len());
2206 for row in rows {
2207 outputs.push(self.selection.__materialize_row(self.session, row)?);
2208 }
2209 Ok(outputs)
2210 }
2211
2212 pub(crate) fn outputs_from_rows(
2213 &self,
2214 validated: &ValidatedMatchRequest,
2215 result: &ValidatedMatchResult,
2216 ) -> Result<Vec<Shape::Output>> {
2217 let rows = match result
2218 .for_request(validated)
2219 .map_err(|error| Error::from_orm_hydration(error.into()))?
2220 {
2221 MatchResult::Rows { rows } => rows,
2222 _ => {
2223 return Err(Error::model_validation(
2224 ModelValidationPhase::Hydration,
2225 "wrong_result_shape",
2226 vec![],
2227 "provider returned a non-row result for a row fetch",
2228 None,
2229 ));
2230 }
2231 };
2232 self.materialize_rows(rows)
2233 }
2234
2235 pub(crate) fn output_page(
2236 &self,
2237 validated: &ValidatedMatchRequest,
2238 result: &ValidatedMatchResult,
2239 ) -> Result<Page<Shape::Output>> {
2240 let (entries, window, total) = match result
2241 .for_request(validated)
2242 .map_err(|error| Error::from_orm_hydration(error.into()))?
2243 {
2244 MatchResult::Page {
2245 entries,
2246 window,
2247 total,
2248 ..
2249 } => (entries, *window, *total),
2250 _ => {
2251 return Err(Error::model_validation(
2252 ModelValidationPhase::Hydration,
2253 "wrong_result_shape",
2254 vec![],
2255 "provider returned a non-page result for a page fetch",
2256 None,
2257 ));
2258 }
2259 };
2260 Ok(Page {
2261 items: self.materialize_rows(entries)?,
2262 offset: window.offset,
2263 limit: window.limit,
2264 total,
2265 })
2266 }
2267
2268 async fn execute(
2269 &self,
2270 validated: ValidatedMatchRequest,
2271 ) -> Result<(ValidatedMatchRequest, ValidatedMatchResult)> {
2272 let result = match &self.session.execution {
2273 QueryExecution::Borrowed(transaction) => {
2274 transaction
2275 .execute_match(&self.session.registry, &validated)
2276 .await
2277 }
2278 QueryExecution::Local(database) => {
2279 database
2280 .inner_orm()
2281 .execute_match(&self.session.registry, &validated)
2282 .await
2283 }
2284 QueryExecution::Remote(remote) => {
2285 return remote
2286 .execute_match(&self.session.registry, validated)
2287 .await;
2288 }
2289 };
2290 Ok((validated, result.map_err(Error::from_orm_hydration)?))
2291 }
2292}
2293
2294impl<'s, 'db, S, Shape> Query<'s, 'db, S, Shape>
2295where
2296 S: Schema,
2297 Shape: SingularSelectedShape<S>,
2298{
2299 pub async fn one(&self) -> Result<Shape::Output> {
2302 let validated = self.validated_one()?;
2303 let (validated, result) = self.execute(validated).await?;
2304 let mut outputs = self.outputs_from_rows(&validated, &result)?;
2305 match outputs.len() {
2306 0 => Err(Error::model_validation(
2307 ModelValidationPhase::Hydration,
2308 "no_result",
2309 vec![],
2310 "query selected no distinct identity",
2311 None,
2312 )),
2313 1 => Ok(outputs.remove(0)),
2314 _ => Err(Error::model_validation(
2315 ModelValidationPhase::Hydration,
2316 "not_unique",
2317 vec![],
2318 "query selected more than one distinct identity",
2319 None,
2320 )),
2321 }
2322 }
2323
2324 pub async fn rows(&self, options: RowsOptions<S>) -> Result<Vec<Shape::Output>> {
2327 if options.limit == 0 {
2328 return Err(Error::model_validation(
2329 ModelValidationPhase::Input,
2330 "zero_limit",
2331 vec![],
2332 "row fetches require a nonzero limit",
2333 None,
2334 ));
2335 }
2336 let validated = self.validated_rows(
2337 &options.order,
2338 Window {
2339 offset: options.offset,
2340 limit: options.limit,
2341 },
2342 )?;
2343 let (validated, result) = self.execute(validated).await?;
2344 self.outputs_from_rows(&validated, &result)
2345 }
2346
2347 pub async fn first(&self, order: Order<S>) -> Result<Option<Shape::Output>> {
2349 let validated = self.validated_rows(
2350 &[order],
2351 Window {
2352 offset: 0,
2353 limit: 1,
2354 },
2355 )?;
2356 let (validated, result) = self.execute(validated).await?;
2357 Ok(self.outputs_from_rows(&validated, &result)?.pop())
2358 }
2359}
2360
2361impl<'s, 'db, S: Schema, Shape: SelectedShape<S>> Query<'s, 'db, S, Shape> {
2362 pub async fn page_by<R: Selectable<S>>(
2364 &self,
2365 root: R,
2366 options: PageOptions<S>,
2367 ) -> Result<Page<Shape::Output>> {
2368 if options.limit == 0 {
2369 return Err(Error::model_validation(
2370 ModelValidationPhase::Input,
2371 "zero_limit",
2372 vec![],
2373 "page fetches require a nonzero limit",
2374 None,
2375 ));
2376 }
2377 let validated = self.validated_page(
2378 root,
2379 &options.order,
2380 Window {
2381 offset: options.offset,
2382 limit: options.limit,
2383 },
2384 options.include_total,
2385 )?;
2386 let (validated, result) = self.execute(validated).await?;
2387 self.output_page(&validated, &result)
2388 }
2389
2390 pub async fn count_by<R: Selectable<S>>(&self, root: R) -> Result<u64> {
2392 let validated = self.validated_count_by(root)?;
2393 let (validated, result) = self.execute(validated).await?;
2394 match result
2395 .for_request(&validated)
2396 .map_err(|error| Error::from_orm_hydration(error.into()))?
2397 {
2398 MatchResult::Count { value, .. } => Ok(*value),
2399 _ => Err(Error::model_validation(
2400 ModelValidationPhase::Hydration,
2401 "wrong_result_shape",
2402 vec![],
2403 "provider returned a non-count result for a count",
2404 None,
2405 )),
2406 }
2407 }
2408
2409 pub async fn exists_by<R: Selectable<S>>(&self, root: R) -> Result<bool> {
2412 let validated = self.validated_exists_by(root)?;
2413 let (validated, result) = self.execute(validated).await?;
2414 match result
2415 .for_request(&validated)
2416 .map_err(|error| Error::from_orm_hydration(error.into()))?
2417 {
2418 MatchResult::Exists { value, .. } => Ok(*value),
2419 _ => Err(Error::model_validation(
2420 ModelValidationPhase::Hydration,
2421 "wrong_result_shape",
2422 vec![],
2423 "provider returned a non-existence result for an existence test",
2424 None,
2425 )),
2426 }
2427 }
2428}
2429
2430impl<'s, 'db, S: Schema, B: Selectable<S>> Query<'s, 'db, S, B> {
2431 pub async fn count(&self) -> Result<u64> {
2433 self.count_by(self.selection).await
2434 }
2435
2436 pub async fn exists(&self) -> Result<bool> {
2438 self.exists_by(self.selection).await
2439 }
2440
2441 fn lowered_reduce_terms(
2442 &self,
2443 terms: &[(
2444 type_bridge_orm::match_request::Reduction,
2445 Option<(BindingKey, &'static str)>,
2446 )],
2447 ) -> Result<Vec<OrmFieldHandle>> {
2448 let mut lowered = Vec::new();
2449 for (_, input) in terms {
2450 if let Some((key, owns_id_json)) = input {
2451 lowered.push(self.session.lower_field(*key, owns_id_json)?);
2452 }
2453 }
2454 Ok(lowered)
2455 }
2456
2457 pub(crate) fn validated_reduce(
2458 &self,
2459 group: Option<BindingKey>,
2460 terms: &[(
2461 type_bridge_orm::match_request::Reduction,
2462 Option<(BindingKey, &'static str)>,
2463 )],
2464 ) -> Result<ValidatedMatchRequest> {
2465 let root = self.session.handle_by_key(self.selection.binding_key())?;
2466 let hidden_groups = group
2467 .filter(|key| *key != self.selection.binding_key())
2468 .into_iter()
2469 .collect::<Vec<_>>();
2470 let lineage = self.lineage_with_hidden(&hidden_groups)?;
2471 let group_handle = group
2472 .map(|key| self.session.handle_by_key(key))
2473 .transpose()?;
2474 let lowered_inputs = self.lowered_reduce_terms(terms)?;
2475 let mut inputs = lowered_inputs.iter();
2476 let mut pairs = Vec::with_capacity(terms.len());
2477 for (reduction, input) in terms {
2478 let handle = if input.is_some() {
2479 Some(inputs.next().expect("one lowered handle per input"))
2480 } else {
2481 None
2482 };
2483 pairs.push((*reduction, handle));
2484 }
2485 lineage
2486 .validate_reduce_by(root, group_handle, &pairs)
2487 .map_err(Error::from_orm)
2488 }
2489
2490 fn validated_reduce_by_field<Owner: Model<Schema = S>, V>(
2491 &self,
2492 group: BoundField<S, Owner, V>,
2493 terms: &[(
2494 type_bridge_orm::match_request::Reduction,
2495 Option<(BindingKey, &'static str)>,
2496 )],
2497 ) -> Result<ValidatedMatchRequest> {
2498 let root = self.session.handle_by_key(self.selection.binding_key())?;
2499 let hidden_groups = (group.key != self.selection.binding_key())
2500 .then_some(group.key)
2501 .into_iter()
2502 .collect::<Vec<_>>();
2503 let lineage = self.lineage_with_hidden(&hidden_groups)?;
2504 let group = self.session.lower_field(group.key, group.owns_id_json)?;
2505 let lowered_inputs = self.lowered_reduce_terms(terms)?;
2506 let mut inputs = lowered_inputs.iter();
2507 let mut pairs = Vec::with_capacity(terms.len());
2508 for (reduction, input) in terms {
2509 let handle = if input.is_some() {
2510 Some(inputs.next().expect("one lowered handle per input"))
2511 } else {
2512 None
2513 };
2514 pairs.push((*reduction, handle));
2515 }
2516 lineage
2517 .validate_reduce_by_field(root, &group, &pairs)
2518 .map_err(Error::from_orm)
2519 }
2520
2521 fn validated_reduce_by_fields(
2522 &self,
2523 groups: &[(BindingKey, &'static str)],
2524 terms: &[(
2525 type_bridge_orm::match_request::Reduction,
2526 Option<(BindingKey, &'static str)>,
2527 )],
2528 ) -> Result<ValidatedMatchRequest> {
2529 let root = self.session.handle_by_key(self.selection.binding_key())?;
2530 let mut hidden_keys = Vec::new();
2531 for (key, _) in groups {
2532 if *key != self.selection.binding_key() && !hidden_keys.contains(key) {
2533 hidden_keys.push(*key);
2534 }
2535 }
2536 let lineage = self.lineage_with_hidden(&hidden_keys)?;
2537 let lowered_groups = groups
2538 .iter()
2539 .map(|(key, owns_id_json)| self.session.lower_field(*key, owns_id_json))
2540 .collect::<Result<Vec<_>>>()?;
2541 let group_refs = lowered_groups.iter().collect::<Vec<_>>();
2542 let lowered_inputs = self.lowered_reduce_terms(terms)?;
2543 let mut inputs = lowered_inputs.iter();
2544 let mut pairs = Vec::with_capacity(terms.len());
2545 for (reduction, input) in terms {
2546 let handle = if input.is_some() {
2547 Some(inputs.next().expect("one lowered handle per input"))
2548 } else {
2549 None
2550 };
2551 pairs.push((*reduction, handle));
2552 }
2553 lineage
2554 .validate_reduce_by_fields(root, &group_refs, &pairs)
2555 .map_err(Error::from_orm)
2556 }
2557
2558 fn decoded_reduction_rows(
2559 validated: &ValidatedMatchRequest,
2560 result: &ValidatedMatchResult,
2561 ) -> Result<Vec<type_bridge_orm::match_request::ReductionRow>> {
2562 match result
2563 .for_request(validated)
2564 .map_err(|error| Error::from_orm_hydration(error.into()))?
2565 {
2566 MatchResult::Reduction { rows, .. }
2567 | MatchResult::FieldReduction { rows, .. }
2568 | MatchResult::FieldTupleReduction { rows, .. } => Ok(rows.clone()),
2569 _ => Err(Error::model_validation(
2570 ModelValidationPhase::Hydration,
2571 "wrong_result_shape",
2572 vec![],
2573 "provider returned a non-reduction result for an aggregate",
2574 None,
2575 )),
2576 }
2577 }
2578
2579 pub async fn aggregate<T: crate::aggregate::AggregateTuple<S>>(
2582 &self,
2583 terms: T,
2584 ) -> Result<T::Output> {
2585 let term_list = terms.terms();
2586 let validated = self.validated_reduce(None, &term_list)?;
2587 let (validated, result) = self.execute(validated).await?;
2588 let rows = Self::decoded_reduction_rows(&validated, &result)?;
2589 let [row] = rows.as_slice() else {
2590 return Err(Error::model_validation(
2591 ModelValidationPhase::Hydration,
2592 "wrong_result_shape",
2593 vec![],
2594 "ungrouped aggregates require exactly one reduction row",
2595 None,
2596 ));
2597 };
2598 T::decode(row.values())
2599 }
2600
2601 pub fn group_by<G: Selectable<S>>(&self, group: G) -> Result<GroupedQuery<'s, 'db, S, B, G>> {
2604 self.session.handle_by_key(group.binding_key())?;
2605 Ok(GroupedQuery {
2606 query: self.clone(),
2607 group,
2608 })
2609 }
2610
2611 pub fn group_by_field<Owner, V>(
2614 &self,
2615 group: BoundField<S, Owner, V>,
2616 ) -> Result<FieldGroupedQuery<'s, 'db, S, B, Owner, V>>
2617 where
2618 Owner: Model<Schema = S>,
2619 V: GroupedQueryValue,
2620 {
2621 self.session.lower_field(group.key, group.owns_id_json)?;
2622 Ok(FieldGroupedQuery {
2623 query: self.clone(),
2624 group,
2625 })
2626 }
2627
2628 pub fn group_by_fields<G>(&self, groups: G) -> Result<FieldTupleGroupedQuery<'s, 'db, S, B, G>>
2631 where
2632 G: FieldGroupTuple<S>,
2633 {
2634 for (key, owns_id_json) in groups.fields() {
2635 self.session.lower_field(key, owns_id_json)?;
2636 }
2637 Ok(FieldTupleGroupedQuery {
2638 query: self.clone(),
2639 groups,
2640 })
2641 }
2642}
2643
2644pub struct GroupedQuery<'s, 'db, S: Schema, B: Selectable<S>, G: Selectable<S>> {
2646 query: Query<'s, 'db, S, B>,
2647 group: G,
2648}
2649
2650impl<'s, 'db, S: Schema, B: Selectable<S>, G: Selectable<S>> GroupedQuery<'s, 'db, S, B, G> {
2651 pub async fn aggregate<T: crate::aggregate::AggregateTuple<S>>(
2654 &self,
2655 terms: T,
2656 ) -> Result<Vec<(G::Output, T::Output)>> {
2657 let term_list = terms.terms();
2658 let validated = self
2659 .query
2660 .validated_reduce(Some(self.group.binding_key()), &term_list)?;
2661 let (validated, result) = self.query.execute(validated).await?;
2662 let rows = Query::<S, B>::decoded_reduction_rows(&validated, &result)?;
2663 let mut outputs = Vec::with_capacity(rows.len());
2664 for row in &rows {
2665 let thing = row.group().ok_or_else(|| {
2666 Error::model_validation(
2667 ModelValidationPhase::Hydration,
2668 "wrong_result_shape",
2669 vec![],
2670 "grouped aggregates require group evidence per row",
2671 None,
2672 )
2673 })?;
2674 let client_row = self.query.session.client_row_for(thing)?;
2675 let key = G::materialize_output(&client_row).map_err(|error| {
2676 crate::entity_codec::map_validation_error(error, ModelValidationPhase::Hydration)
2677 })?;
2678 outputs.push((key, T::decode(row.values())?));
2679 }
2680 Ok(outputs)
2681 }
2682}
2683
2684pub struct FieldGroupedQuery<
2687 's,
2688 'db,
2689 S: Schema,
2690 B: Selectable<S>,
2691 Owner: Model<Schema = S>,
2692 V: GroupedQueryValue,
2693> {
2694 query: Query<'s, 'db, S, B>,
2695 group: BoundField<S, Owner, V>,
2696}
2697
2698impl<'s, 'db, S, B, Owner, V> FieldGroupedQuery<'s, 'db, S, B, Owner, V>
2699where
2700 S: Schema,
2701 B: Selectable<S>,
2702 Owner: Model<Schema = S>,
2703 V: GroupedQueryValue,
2704{
2705 pub async fn aggregate<T: crate::aggregate::AggregateTuple<S>>(
2708 &self,
2709 terms: T,
2710 ) -> Result<Vec<(V, T::Output)>> {
2711 let term_list = terms.terms();
2712 let validated = self
2713 .query
2714 .validated_reduce_by_field(self.group, &term_list)?;
2715 let (validated, result) = self.query.execute(validated).await?;
2716 let rows = Query::<S, B>::decoded_reduction_rows(&validated, &result)?;
2717 let mut outputs = Vec::with_capacity(rows.len());
2718 for row in &rows {
2719 let value = row.field_group().ok_or_else(|| {
2720 Error::model_validation(
2721 ModelValidationPhase::Hydration,
2722 "wrong_result_shape",
2723 vec![],
2724 "field-grouped aggregates require scalar group evidence per row",
2725 None,
2726 )
2727 })?;
2728 let key = V::from_group_scalar(encoded_group_scalar(value)?)
2729 .map_err(|error| map_validation_error(error, ModelValidationPhase::Hydration))?;
2730 outputs.push((key, T::decode(row.values())?));
2731 }
2732 Ok(outputs)
2733 }
2734}
2735
2736pub trait FieldGroupTuple<S: Schema>: field_group_tuple_sealed::Sealed<S> + Copy {
2739 type Output;
2741
2742 #[doc(hidden)]
2743 fn fields(&self) -> Vec<(BindingKey, &'static str)>;
2744
2745 #[doc(hidden)]
2746 fn decode(values: &[AttributeValue]) -> Result<Self::Output>;
2747}
2748
2749mod field_group_tuple_sealed {
2750 pub trait Sealed<S> {}
2751}
2752
2753macro_rules! field_group_tuple {
2754 ($(($owner:ident, $value:ident, $index:tt)),+) => {
2755 impl<S, $($owner, $value),+> field_group_tuple_sealed::Sealed<S>
2756 for ($(BoundField<S, $owner, $value>,)+)
2757 where
2758 S: Schema,
2759 $($owner: Model<Schema = S>, $value: GroupedQueryValue),+
2760 {
2761 }
2762
2763 impl<S, $($owner, $value),+> FieldGroupTuple<S>
2764 for ($(BoundField<S, $owner, $value>,)+)
2765 where
2766 S: Schema,
2767 $($owner: Model<Schema = S>, $value: GroupedQueryValue),+
2768 {
2769 type Output = ($($value,)+);
2770
2771 fn fields(&self) -> Vec<(BindingKey, &'static str)> {
2772 vec![$((self.$index.key, self.$index.owns_id_json)),+]
2773 }
2774
2775 fn decode(values: &[AttributeValue]) -> Result<Self::Output> {
2776 let expected = [$(stringify!($owner)),+].len();
2777 if values.len() != expected {
2778 return Err(Error::model_validation(
2779 ModelValidationPhase::Hydration,
2780 "wrong_result_shape",
2781 vec![],
2782 "tuple-field-grouped aggregate key has the wrong arity",
2783 None,
2784 ));
2785 }
2786 Ok(($(
2787 $value::from_group_scalar(encoded_group_scalar(&values[$index])?)
2788 .map_err(|error| {
2789 map_validation_error(error, ModelValidationPhase::Hydration)
2790 })?,
2791 )+))
2792 }
2793 }
2794 };
2795}
2796
2797field_group_tuple!((O1, V1, 0), (O2, V2, 1));
2798field_group_tuple!((O1, V1, 0), (O2, V2, 1), (O3, V3, 2));
2799field_group_tuple!((O1, V1, 0), (O2, V2, 1), (O3, V3, 2), (O4, V4, 3));
2800field_group_tuple!(
2801 (O1, V1, 0),
2802 (O2, V2, 1),
2803 (O3, V3, 2),
2804 (O4, V4, 3),
2805 (O5, V5, 4)
2806);
2807field_group_tuple!(
2808 (O1, V1, 0),
2809 (O2, V2, 1),
2810 (O3, V3, 2),
2811 (O4, V4, 3),
2812 (O5, V5, 4),
2813 (O6, V6, 5)
2814);
2815field_group_tuple!(
2816 (O1, V1, 0),
2817 (O2, V2, 1),
2818 (O3, V3, 2),
2819 (O4, V4, 3),
2820 (O5, V5, 4),
2821 (O6, V6, 5),
2822 (O7, V7, 6)
2823);
2824field_group_tuple!(
2825 (O1, V1, 0),
2826 (O2, V2, 1),
2827 (O3, V3, 2),
2828 (O4, V4, 3),
2829 (O5, V5, 4),
2830 (O6, V6, 5),
2831 (O7, V7, 6),
2832 (O8, V8, 7)
2833);
2834field_group_tuple!(
2835 (O1, V1, 0),
2836 (O2, V2, 1),
2837 (O3, V3, 2),
2838 (O4, V4, 3),
2839 (O5, V5, 4),
2840 (O6, V6, 5),
2841 (O7, V7, 6),
2842 (O8, V8, 7),
2843 (O9, V9, 8)
2844);
2845field_group_tuple!(
2846 (O1, V1, 0),
2847 (O2, V2, 1),
2848 (O3, V3, 2),
2849 (O4, V4, 3),
2850 (O5, V5, 4),
2851 (O6, V6, 5),
2852 (O7, V7, 6),
2853 (O8, V8, 7),
2854 (O9, V9, 8),
2855 (O10, V10, 9)
2856);
2857field_group_tuple!(
2858 (O1, V1, 0),
2859 (O2, V2, 1),
2860 (O3, V3, 2),
2861 (O4, V4, 3),
2862 (O5, V5, 4),
2863 (O6, V6, 5),
2864 (O7, V7, 6),
2865 (O8, V8, 7),
2866 (O9, V9, 8),
2867 (O10, V10, 9),
2868 (O11, V11, 10)
2869);
2870field_group_tuple!(
2871 (O1, V1, 0),
2872 (O2, V2, 1),
2873 (O3, V3, 2),
2874 (O4, V4, 3),
2875 (O5, V5, 4),
2876 (O6, V6, 5),
2877 (O7, V7, 6),
2878 (O8, V8, 7),
2879 (O9, V9, 8),
2880 (O10, V10, 9),
2881 (O11, V11, 10),
2882 (O12, V12, 11)
2883);
2884field_group_tuple!(
2885 (O1, V1, 0),
2886 (O2, V2, 1),
2887 (O3, V3, 2),
2888 (O4, V4, 3),
2889 (O5, V5, 4),
2890 (O6, V6, 5),
2891 (O7, V7, 6),
2892 (O8, V8, 7),
2893 (O9, V9, 8),
2894 (O10, V10, 9),
2895 (O11, V11, 10),
2896 (O12, V12, 11),
2897 (O13, V13, 12)
2898);
2899field_group_tuple!(
2900 (O1, V1, 0),
2901 (O2, V2, 1),
2902 (O3, V3, 2),
2903 (O4, V4, 3),
2904 (O5, V5, 4),
2905 (O6, V6, 5),
2906 (O7, V7, 6),
2907 (O8, V8, 7),
2908 (O9, V9, 8),
2909 (O10, V10, 9),
2910 (O11, V11, 10),
2911 (O12, V12, 11),
2912 (O13, V13, 12),
2913 (O14, V14, 13)
2914);
2915field_group_tuple!(
2916 (O1, V1, 0),
2917 (O2, V2, 1),
2918 (O3, V3, 2),
2919 (O4, V4, 3),
2920 (O5, V5, 4),
2921 (O6, V6, 5),
2922 (O7, V7, 6),
2923 (O8, V8, 7),
2924 (O9, V9, 8),
2925 (O10, V10, 9),
2926 (O11, V11, 10),
2927 (O12, V12, 11),
2928 (O13, V13, 12),
2929 (O14, V14, 13),
2930 (O15, V15, 14)
2931);
2932field_group_tuple!(
2933 (O1, V1, 0),
2934 (O2, V2, 1),
2935 (O3, V3, 2),
2936 (O4, V4, 3),
2937 (O5, V5, 4),
2938 (O6, V6, 5),
2939 (O7, V7, 6),
2940 (O8, V8, 7),
2941 (O9, V9, 8),
2942 (O10, V10, 9),
2943 (O11, V11, 10),
2944 (O12, V12, 11),
2945 (O13, V13, 12),
2946 (O14, V14, 13),
2947 (O15, V15, 14),
2948 (O16, V16, 15)
2949);
2950
2951pub struct FieldTupleGroupedQuery<'s, 'db, S: Schema, B: Selectable<S>, G: FieldGroupTuple<S>> {
2954 query: Query<'s, 'db, S, B>,
2955 groups: G,
2956}
2957
2958impl<'s, 'db, S, B, G> FieldTupleGroupedQuery<'s, 'db, S, B, G>
2959where
2960 S: Schema,
2961 B: Selectable<S>,
2962 G: FieldGroupTuple<S>,
2963{
2964 pub async fn aggregate<T: crate::aggregate::AggregateTuple<S>>(
2967 &self,
2968 terms: T,
2969 ) -> Result<Vec<(G::Output, T::Output)>> {
2970 let term_list = terms.terms();
2971 let group_fields = self.groups.fields();
2972 let validated = self
2973 .query
2974 .validated_reduce_by_fields(&group_fields, &term_list)?;
2975 let (validated, result) = self.query.execute(validated).await?;
2976 let rows = Query::<S, B>::decoded_reduction_rows(&validated, &result)?;
2977 let mut outputs = Vec::with_capacity(rows.len());
2978 for row in &rows {
2979 let values = row.field_groups().ok_or_else(|| {
2980 Error::model_validation(
2981 ModelValidationPhase::Hydration,
2982 "wrong_result_shape",
2983 vec![],
2984 "tuple-field-grouped aggregates require tuple group evidence per row",
2985 None,
2986 )
2987 })?;
2988 outputs.push((G::decode(values)?, T::decode(row.values())?));
2989 }
2990 Ok(outputs)
2991 }
2992}