1#![deny(missing_docs)]
2use std::marker::PhantomData;
6use std::sync::Arc;
7use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
8
9use type_bridge_contract::codec::from_canonical_json;
10use type_bridge_contract::decimal::parse_decimal;
11use type_bridge_contract::id::{FunctionId, TypeId, TypeKind};
12use type_bridge_contract::projection::ProjectionHandler;
13use type_bridge_contract::query_plan::CompatibilityValueV2;
14use type_bridge_contract::temporal::{
15 CanonicalDate, CanonicalDateTime, CanonicalDateTimeTz, CanonicalDuration,
16};
17use type_bridge_orm::_descriptor::TypeDescriptorRef;
18use type_bridge_orm::_registry::DescriptorRegistry;
19use type_bridge_orm::match_request::handles::{
20 BindingHandle as OrmBindingHandle, FieldHandle as OrmFieldHandle,
21 FunctionArgumentHandle as OrmFunctionArgumentHandle,
22 FunctionCallHandle as OrmFunctionCallHandle, OrderHandle as OrmOrderHandle,
23 PredicateHandle as OrmPredicateHandle, QueryHandle as OrmQueryHandle,
24 SelectionHandle as OrmSelectionHandle, SessionHandle as OrmSessionHandle,
25 ShapeHandle as OrmShapeHandle,
26};
27use type_bridge_orm::match_request::model::{
28 ComparisonOp, MissingOrder, RowCardinality, SortDirection, Window,
29};
30use type_bridge_orm::match_request::result::{
31 HydratedThing, MatchResult, MatchRow, ReducedValue, SlotValue, ValidatedMatchResult,
32};
33use type_bridge_orm::match_request::validation::ValidatedMatchRequest;
34use type_bridge_orm::{
35 AnswerCancellation, AttributeValue, DynamicEntityRow, DynamicRelationRow, DynamicRolePlayer,
36 InstalledRuntimeProjection, ProjectedAttributeValue, ProjectedQueryOrigin, ProjectedQueryRow,
37 ProjectedQuerySlotValue, ProjectedQueryValue, ProjectedReducedValue, ProjectedReductionGroup,
38 ProjectedThing, QueryExecutionDeadline, QueryExecutionResourceLimits,
39};
40
41use crate::__codegen::{
42 CompleteModel, EncodedScalar, FieldToken, FunctionToken, GroupedQueryValue, HydratedRow,
43 HydrationCapability, Model, QueryValued, RelationModel, RolePlayerBinding, RoleToken,
44 RoleTokenCompatible, SubtypeRootModel, ThingModel, TypeToken, ValidationError,
45};
46use crate::entity_codec::{hydrate_entity, map_validation_error};
47use crate::error::{Error, ModelValidationPhase};
48use crate::projected_codec::projected_to_hydrated_row;
49use crate::relation_codec::hydrate_relation;
50use crate::schema::Schema;
51use crate::{Database, Result};
52
53#[cfg(test)]
54mod tests;
55
56static QUERY_SESSION_NONCE: AtomicU64 = AtomicU64::new(1);
57
58fn schema_not_bound() -> Error {
59 Error::model_validation(
60 ModelValidationPhase::Input,
61 "schema_not_bound",
62 vec![],
63 "database is not schema-bound",
64 None,
65 )
66}
67
68fn cross_session_handle() -> Error {
69 Error::model_validation(
70 ModelValidationPhase::Input,
71 "cross_session_handle",
72 vec![],
73 "binding belongs to a different query session",
74 None,
75 )
76}
77
78fn model_label(type_id_json: &'static str) -> Result<String> {
79 let id = from_canonical_json::<TypeId>(type_id_json.as_bytes()).map_err(|source| {
80 Error::model_validation(
81 ModelValidationPhase::Input,
82 "invalid_model_identity",
83 vec!["type".into()],
84 "generated model identity is not canonical",
85 Some(Box::new(source)),
86 )
87 })?;
88 Ok(id.label().as_str().to_owned())
89}
90
91fn parse_owns_identity(owns_id_json: &'static str) -> Result<(String, String)> {
92 let invalid = || {
93 Error::model_validation(
94 ModelValidationPhase::Input,
95 "invalid_field_identity",
96 vec!["type".into()],
97 "generated field identity is not canonical",
98 None,
99 )
100 };
101 let value: serde_json::Value = serde_json::from_str(owns_id_json).map_err(|_| invalid())?;
102 let attribute = value
103 .get("attribute")
104 .and_then(serde_json::Value::as_str)
105 .ok_or_else(invalid)?;
106 let owner = value
107 .get("owner")
108 .and_then(|owner| owner.get("label"))
109 .and_then(serde_json::Value::as_str)
110 .ok_or_else(invalid)?;
111 Ok((owner.to_owned(), attribute.to_owned()))
112}
113
114fn parse_role_identity(role_id_json: &'static str) -> Result<type_bridge_contract::id::RoleId> {
115 from_canonical_json::<type_bridge_contract::id::RoleId>(role_id_json.as_bytes()).map_err(
116 |source| {
117 Error::model_validation(
118 ModelValidationPhase::Input,
119 "invalid_role_identity",
120 vec!["type".into()],
121 "generated role identity is not canonical",
122 Some(Box::new(source)),
123 )
124 },
125 )
126}
127
128mod mode_sealed {
129 pub trait Sealed {}
130}
131
132pub trait SelectionMode: mode_sealed::Sealed + 'static {}
134
135#[derive(Clone, Copy, Debug)]
137pub struct Exact;
138impl mode_sealed::Sealed for Exact {}
139impl SelectionMode for Exact {}
140
141#[derive(Clone, Copy, Debug)]
144pub struct Subtypes;
145impl mode_sealed::Sealed for Subtypes {}
146impl SelectionMode for Subtypes {}
147
148pub struct QuerySession<'db, S: Schema> {
155 cancellation: AnswerCancellation,
156 installed: &'db InstalledRuntimeProjection,
157 execution: QueryExecution<'db, S>,
158 resources: QueryExecutionResourceLimits,
159 session: OrmSessionHandle,
160 registry: Arc<DescriptorRegistry>,
161 nonce: u64,
162 bindings: Vec<OrmBindingHandle>,
163 closed: AtomicBool,
164 marker: PhantomData<fn() -> S>,
165}
166
167enum QueryExecution<'db, S: Schema> {
168 Local(&'db Database<S>),
169 Borrowed(&'db type_bridge_orm::session::context::TransactionContext),
170 Remote(&'db crate::remote::RemoteDatabase<S>),
171}
172
173impl<S: Schema> std::fmt::Debug for QuerySession<'_, S> {
174 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 formatter
176 .debug_struct("QuerySession")
177 .field("session", &self.nonce)
178 .field("bindings", &self.bindings.len())
179 .field("closed", &self.closed.load(Ordering::Acquire))
180 .finish_non_exhaustive()
181 }
182}
183
184#[doc(hidden)]
186#[derive(Clone, Copy, Debug, PartialEq, Eq)]
187pub struct BindingKey {
188 pub(crate) nonce: u64,
189 pub(crate) index: u32,
190}
191
192pub struct Binding<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode = Exact> {
196 key: BindingKey,
197 #[allow(clippy::type_complexity)]
198 marker: PhantomData<fn() -> (S, M, Mode)>,
199}
200
201impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Copy for Binding<S, M, Mode> {}
202impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Clone for Binding<S, M, Mode> {
203 fn clone(&self) -> Self {
204 *self
205 }
206}
207impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> PartialEq for Binding<S, M, Mode> {
208 fn eq(&self, other: &Self) -> bool {
209 self.key == other.key
210 }
211}
212impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Eq for Binding<S, M, Mode> {}
213impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> std::fmt::Debug
214 for Binding<S, M, Mode>
215{
216 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 formatter
218 .debug_struct("Binding")
219 .field("session", &self.key.nonce)
220 .field("index", &self.key.index)
221 .finish()
222 }
223}
224
225impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Binding<S, M, Mode> {
226 pub(crate) fn key(self) -> BindingKey {
227 self.key
228 }
229
230 #[must_use]
232 pub fn iid(self, iid: impl Into<String>) -> Predicate<S> {
233 Predicate::new(PredicateExpr::BindingIid {
234 binding: self.key,
235 iid: iid.into(),
236 })
237 }
238
239 #[must_use]
241 pub fn iid_in(self, iids: impl IntoIterator<Item = impl Into<String>>) -> Predicate<S> {
242 Predicate::new(PredicateExpr::BindingIidIn {
243 binding: self.key,
244 iids: iids.into_iter().map(Into::into).collect(),
245 })
246 }
247
248 #[must_use]
251 pub fn collect(self) -> Collected<S, Self>
252 where
253 Self: Selectable<S>,
254 {
255 Collected {
256 selection: self,
257 distinct: false,
258 order: Vec::new(),
259 }
260 }
261
262 #[doc(hidden)]
266 #[must_use]
267 pub fn __function_argument(self) -> FunctionArgument<S> {
268 FunctionArgument {
269 nonce: self.key.nonce,
270 expression: FunctionArgumentExpr::Binding(self.key),
271 marker: PhantomData,
272 }
273 }
274}
275
276#[derive(Clone, Debug, PartialEq)]
277pub(crate) struct FunctionInputExpr {
278 attribute_type: TypeId,
279 value: AttributeValue,
280}
281
282#[derive(Clone, Debug, PartialEq)]
283pub(crate) enum FunctionArgumentExpr {
284 Binding(BindingKey),
285 Value(FunctionInputExpr),
286 Call(FunctionCallExpr),
287}
288
289#[derive(Clone, Debug, PartialEq)]
290pub(crate) struct FunctionCallExpr {
291 function_id: String,
292 arguments: Vec<FunctionArgumentExpr>,
293}
294
295#[doc(hidden)]
300#[derive(Clone, Debug, PartialEq)]
301pub struct FunctionInput<S: Schema, Domain> {
302 nonce: u64,
303 expression: FunctionInputExpr,
304 marker: PhantomData<fn() -> (S, Domain)>,
305}
306
307impl<S: Schema, Domain> FunctionInput<S, Domain> {
308 #[doc(hidden)]
310 #[must_use]
311 pub fn __function_argument(&self) -> FunctionArgument<S> {
312 FunctionArgument {
313 nonce: self.nonce,
314 expression: FunctionArgumentExpr::Value(self.expression.clone()),
315 marker: PhantomData,
316 }
317 }
318}
319
320#[doc(hidden)]
324#[derive(Clone, Debug, PartialEq)]
325pub struct FunctionCall<S: Schema, Domain> {
326 nonce: u64,
327 expression: FunctionCallExpr,
328 marker: PhantomData<fn() -> (S, Domain)>,
329}
330
331impl<S: Schema, Domain> FunctionCall<S, Domain> {
332 #[doc(hidden)]
334 #[must_use]
335 pub fn __function_argument(&self) -> FunctionArgument<S> {
336 FunctionArgument {
337 nonce: self.nonce,
338 expression: FunctionArgumentExpr::Call(self.expression.clone()),
339 marker: PhantomData,
340 }
341 }
342
343 fn field_predicate<Owner, Value>(
344 &self,
345 operator: ComparisonOp,
346 field: BoundField<S, Owner, Value>,
347 ) -> Predicate<S>
348 where
349 Owner: Model<Schema = S>,
350 Value: QueryValued<Domain = Domain>,
351 {
352 Predicate::new(PredicateExpr::FunctionField {
353 call: self.expression.clone(),
354 operator,
355 binding: field.key,
356 owns_id_json: field.owns_id_json,
357 })
358 }
359
360 fn value_predicate(
361 &self,
362 operator: ComparisonOp,
363 value: &FunctionInput<S, Domain>,
364 ) -> Result<Predicate<S>> {
365 if self.nonce != value.nonce {
366 return Err(cross_session_handle());
367 }
368 Ok(Predicate::new(PredicateExpr::FunctionValue {
369 call: self.expression.clone(),
370 operator,
371 value: value.expression.clone(),
372 }))
373 }
374
375 fn call_predicate(&self, operator: ComparisonOp, other: &Self) -> Result<Predicate<S>> {
376 if self.nonce != other.nonce {
377 return Err(cross_session_handle());
378 }
379 Ok(Predicate::new(PredicateExpr::FunctionCall {
380 left: self.expression.clone(),
381 operator,
382 right: other.expression.clone(),
383 }))
384 }
385
386 #[must_use]
388 pub fn eq_field<Owner, Value>(&self, field: BoundField<S, Owner, Value>) -> Predicate<S>
389 where
390 Owner: Model<Schema = S>,
391 Value: QueryValued<Domain = Domain>,
392 {
393 self.field_predicate(ComparisonOp::Equal, field)
394 }
395
396 #[must_use]
398 pub fn ne_field<Owner, Value>(&self, field: BoundField<S, Owner, Value>) -> Predicate<S>
399 where
400 Owner: Model<Schema = S>,
401 Value: QueryValued<Domain = Domain>,
402 {
403 self.field_predicate(ComparisonOp::NotEqual, field)
404 }
405
406 #[must_use]
408 pub fn lt_field<Owner, Value>(&self, field: BoundField<S, Owner, Value>) -> Predicate<S>
409 where
410 Owner: Model<Schema = S>,
411 Value: QueryValued<Domain = Domain>,
412 {
413 self.field_predicate(ComparisonOp::LessThan, field)
414 }
415
416 #[must_use]
418 pub fn le_field<Owner, Value>(&self, field: BoundField<S, Owner, Value>) -> Predicate<S>
419 where
420 Owner: Model<Schema = S>,
421 Value: QueryValued<Domain = Domain>,
422 {
423 self.field_predicate(ComparisonOp::LessThanOrEqual, field)
424 }
425
426 #[must_use]
428 pub fn gt_field<Owner, Value>(&self, field: BoundField<S, Owner, Value>) -> Predicate<S>
429 where
430 Owner: Model<Schema = S>,
431 Value: QueryValued<Domain = Domain>,
432 {
433 self.field_predicate(ComparisonOp::GreaterThan, field)
434 }
435
436 #[must_use]
438 pub fn ge_field<Owner, Value>(&self, field: BoundField<S, Owner, Value>) -> Predicate<S>
439 where
440 Owner: Model<Schema = S>,
441 Value: QueryValued<Domain = Domain>,
442 {
443 self.field_predicate(ComparisonOp::GreaterThanOrEqual, field)
444 }
445
446 pub fn eq_value(&self, value: &FunctionInput<S, Domain>) -> Result<Predicate<S>> {
448 self.value_predicate(ComparisonOp::Equal, value)
449 }
450
451 pub fn ne_value(&self, value: &FunctionInput<S, Domain>) -> Result<Predicate<S>> {
453 self.value_predicate(ComparisonOp::NotEqual, value)
454 }
455
456 pub fn lt_value(&self, value: &FunctionInput<S, Domain>) -> Result<Predicate<S>> {
458 self.value_predicate(ComparisonOp::LessThan, value)
459 }
460
461 pub fn le_value(&self, value: &FunctionInput<S, Domain>) -> Result<Predicate<S>> {
463 self.value_predicate(ComparisonOp::LessThanOrEqual, value)
464 }
465
466 pub fn gt_value(&self, value: &FunctionInput<S, Domain>) -> Result<Predicate<S>> {
468 self.value_predicate(ComparisonOp::GreaterThan, value)
469 }
470
471 pub fn ge_value(&self, value: &FunctionInput<S, Domain>) -> Result<Predicate<S>> {
473 self.value_predicate(ComparisonOp::GreaterThanOrEqual, value)
474 }
475
476 pub fn eq_call(&self, other: &Self) -> Result<Predicate<S>> {
478 self.call_predicate(ComparisonOp::Equal, other)
479 }
480
481 pub fn ne_call(&self, other: &Self) -> Result<Predicate<S>> {
483 self.call_predicate(ComparisonOp::NotEqual, other)
484 }
485
486 pub fn lt_call(&self, other: &Self) -> Result<Predicate<S>> {
488 self.call_predicate(ComparisonOp::LessThan, other)
489 }
490
491 pub fn le_call(&self, other: &Self) -> Result<Predicate<S>> {
493 self.call_predicate(ComparisonOp::LessThanOrEqual, other)
494 }
495
496 pub fn gt_call(&self, other: &Self) -> Result<Predicate<S>> {
498 self.call_predicate(ComparisonOp::GreaterThan, other)
499 }
500
501 pub fn ge_call(&self, other: &Self) -> Result<Predicate<S>> {
503 self.call_predicate(ComparisonOp::GreaterThanOrEqual, other)
504 }
505}
506
507mod function_scalar_argument_sealed {
508 pub trait Sealed {}
509}
510
511#[doc(hidden)]
519pub trait FunctionScalarArgument<S: Schema, Domain>:
520 function_scalar_argument_sealed::Sealed
521{
522 #[doc(hidden)]
524 fn __function_argument(&self) -> FunctionArgument<S>;
525}
526
527impl<S: Schema, Domain> function_scalar_argument_sealed::Sealed for FunctionInput<S, Domain> {}
528
529impl<S: Schema, Domain> FunctionScalarArgument<S, Domain> for FunctionInput<S, Domain> {
530 fn __function_argument(&self) -> FunctionArgument<S> {
531 FunctionInput::__function_argument(self)
532 }
533}
534
535impl<S: Schema, Domain> function_scalar_argument_sealed::Sealed for FunctionCall<S, Domain> {}
536
537impl<S: Schema, Domain> FunctionScalarArgument<S, Domain> for FunctionCall<S, Domain> {
538 fn __function_argument(&self) -> FunctionArgument<S> {
539 FunctionCall::__function_argument(self)
540 }
541}
542
543#[doc(hidden)]
545#[derive(Clone, Debug, PartialEq)]
546pub struct FunctionArgument<S: Schema> {
547 nonce: u64,
548 expression: FunctionArgumentExpr,
549 marker: PhantomData<fn() -> S>,
550}
551
552impl<S: Schema> Database<S> {
553 pub fn query(&self) -> Result<QuerySession<'_, S>> {
556 self.query_with_resources(
557 QueryExecutionResourceLimits::default(),
558 AnswerCancellation::default(),
559 )
560 }
561
562 pub fn query_with_resources(
565 &self,
566 resources: QueryExecutionResourceLimits,
567 cancellation: AnswerCancellation,
568 ) -> Result<QuerySession<'_, S>> {
569 let registry = self.match_registry().ok_or_else(schema_not_bound)?;
570 let installed = self
571 .installed_schema()
572 .map(Arc::as_ref)
573 .ok_or_else(schema_not_bound)?;
574 Ok(QuerySession::new(
575 installed,
576 Arc::clone(registry),
577 QueryExecution::Local(self),
578 self.operation_limits(resources),
579 cancellation,
580 ))
581 }
582}
583
584impl<'db, S: Schema> QuerySession<'db, S> {
585 fn new(
586 installed: &'db InstalledRuntimeProjection,
587 registry: Arc<DescriptorRegistry>,
588 execution: QueryExecution<'db, S>,
589 resources: QueryExecutionResourceLimits,
590 cancellation: AnswerCancellation,
591 ) -> Self {
592 Self {
593 cancellation,
594 installed,
595 execution,
596 resources: resources.effective(),
597 session: OrmSessionHandle::new(Arc::clone(®istry)),
598 registry,
599 nonce: QUERY_SESSION_NONCE.fetch_add(1, Ordering::Relaxed),
600 bindings: Vec::new(),
601 closed: AtomicBool::new(false),
602 marker: PhantomData,
603 }
604 }
605
606 pub(crate) fn borrowed(
607 installed: &'db InstalledRuntimeProjection,
608 registry: Arc<DescriptorRegistry>,
609 transaction: &'db type_bridge_orm::session::context::TransactionContext,
610 resources: QueryExecutionResourceLimits,
611 cancellation: AnswerCancellation,
612 ) -> Self {
613 Self::new(
614 installed,
615 registry,
616 QueryExecution::Borrowed(transaction),
617 resources,
618 cancellation,
619 )
620 }
621
622 pub(crate) fn remote(
623 installed: &'db InstalledRuntimeProjection,
624 registry: Arc<DescriptorRegistry>,
625 remote: &'db crate::remote::RemoteDatabase<S>,
626 resources: QueryExecutionResourceLimits,
627 cancellation: AnswerCancellation,
628 ) -> Self {
629 Self::new(
630 installed,
631 registry,
632 QueryExecution::Remote(remote),
633 resources,
634 cancellation,
635 )
636 }
637
638 fn begin_invocation(&self) -> Result<QueryExecutionDeadline> {
639 self.ensure_open(ModelValidationPhase::Input)?;
640 let deadline = QueryExecutionDeadline::for_limits(self.resources);
641 self.check_invocation(deadline, ModelValidationPhase::Input)?;
642 Ok(deadline)
643 }
644
645 fn check_invocation(
646 &self,
647 deadline: QueryExecutionDeadline,
648 phase: ModelValidationPhase,
649 ) -> Result<()> {
650 self.ensure_open(phase)?;
651 deadline
652 .check(&self.cancellation)
653 .map_err(|error| Error::from_sdk_execution(error, phase))
654 }
655
656 fn ensure_open(&self, phase: ModelValidationPhase) -> Result<()> {
657 if self.closed.load(Ordering::Acquire) {
658 Err(Error::from_sdk_execution(
659 type_bridge_orm::query_resource_closed_diagnostic(),
660 phase,
661 ))
662 } else {
663 Ok(())
664 }
665 }
666
667 fn uses_successor_projected_materialization(&self) -> bool {
668 self.installed.projection().generator_handlers() == [ProjectionHandler::rust_v2()]
669 }
670
671 fn projected_query_origin(&self) -> Result<ProjectedQueryOrigin> {
672 match &self.execution {
673 QueryExecution::Local(database) => {
674 Ok(ProjectedQueryOrigin::for_database(database.inner_orm()))
675 }
676 QueryExecution::Borrowed(transaction) => {
677 ProjectedQueryOrigin::for_transaction(transaction).map_err(|error| {
678 Error::from_sdk_execution(error, ModelValidationPhase::Hydration)
679 })
680 }
681 QueryExecution::Remote(_) => Ok(ProjectedQueryOrigin::remote_unbound()),
682 }
683 }
684
685 fn materialize_projected_query_value(
686 &self,
687 validated: &ValidatedMatchRequest,
688 result: &ValidatedMatchResult,
689 deadline: QueryExecutionDeadline,
690 ) -> Result<ProjectedQueryValue> {
691 self.check_invocation(deadline, ModelValidationPhase::Hydration)?;
692 self.projected_query_origin()?
693 .materialize_borrowed_with_budget(
694 self.installed,
695 &self.registry,
696 validated,
697 result,
698 self.resources.projected(),
699 &self.cancellation,
700 Some(deadline),
701 )
702 .map(|(value, _measure)| value)
703 .map_err(|error| Error::from_sdk_execution(error, ModelValidationPhase::Hydration))
704 }
705}
706
707impl<'db, S: Schema> QuerySession<'db, S> {
708 pub fn close(&self) {
713 self.closed.store(true, Ordering::Release);
714 }
715
716 #[must_use]
718 pub fn is_closed(&self) -> bool {
719 self.closed.load(Ordering::Acquire)
720 }
721
722 fn push_binding<M: ThingModel<Schema = S>, Mode: SelectionMode>(
723 &mut self,
724 handle: OrmBindingHandle,
725 ) -> Result<Binding<S, M, Mode>> {
726 let index = u32::try_from(self.bindings.len()).map_err(|source| {
727 Error::model_validation(
728 ModelValidationPhase::Input,
729 "too_many_bindings",
730 vec![],
731 "query session binding capacity exceeded",
732 Some(Box::new(source)),
733 )
734 })?;
735 self.bindings.push(handle);
736 Ok(Binding {
737 key: BindingKey {
738 nonce: self.nonce,
739 index,
740 },
741 marker: PhantomData,
742 })
743 }
744
745 pub fn exact<M>(&mut self) -> Result<Binding<S, M, Exact>>
748 where
749 M: ThingModel<Schema = S> + CompleteModel,
750 {
751 self.ensure_open(ModelValidationPhase::Input)?;
752 let label = model_label(M::TYPE_ID_JSON)?;
753 let handle = self.session.exact(&label).map_err(Error::from_orm)?;
754 self.push_binding(handle)
755 }
756
757 pub fn subtypes<M>(&mut self) -> Result<Binding<S, M, Subtypes>>
760 where
761 M: ThingModel<Schema = S> + SubtypeRootModel,
762 {
763 self.ensure_open(ModelValidationPhase::Input)?;
764 let label = model_label(M::TYPE_ID_JSON)?;
765 let handle = self.session.subtypes(&label).map_err(Error::from_orm)?;
766 self.push_binding(handle)
767 }
768
769 pub(crate) fn handle_by_key(&self, key: BindingKey) -> Result<&OrmBindingHandle> {
770 self.ensure_open(ModelValidationPhase::Input)?;
771 if key.nonce != self.nonce {
772 return Err(cross_session_handle());
773 }
774 self.bindings
775 .get(key.index as usize)
776 .ok_or_else(cross_session_handle)
777 }
778
779 #[doc(hidden)]
783 pub fn __function_input<Value>(&self, value: &Value) -> Result<FunctionInput<S, Value::Domain>>
784 where
785 Value: Model<Schema = S> + QueryValued,
786 {
787 self.ensure_open(ModelValidationPhase::Input)?;
788 let attribute_type = from_canonical_json::<TypeId>(Value::TYPE_ID_JSON.as_bytes())
789 .map_err(|source| {
790 Error::model_validation(
791 ModelValidationPhase::Input,
792 "invalid_function_attribute_identity",
793 vec!["function".into()],
794 "generated function input attribute identity is not canonical",
795 Some(Box::new(source)),
796 )
797 })?;
798 if attribute_type.kind() != TypeKind::Attribute {
799 return Err(Error::model_validation(
800 ModelValidationPhase::Input,
801 "function_input_not_attribute",
802 vec!["function".into()],
803 "generated scalar function input must be a projected attribute wrapper",
804 None,
805 ));
806 }
807 let expression = FunctionInputExpr {
808 attribute_type,
809 value: encoded_query_operand(value.into_encoded_scalar()),
810 };
811 self.lower_function_input(&expression)?;
812 Ok(FunctionInput {
813 nonce: self.nonce,
814 expression,
815 marker: PhantomData,
816 })
817 }
818
819 #[doc(hidden)]
822 pub fn __call_function<Arguments, Output>(
823 &self,
824 function: FunctionToken<S, Arguments, Output>,
825 arguments: impl IntoIterator<Item = FunctionArgument<S>>,
826 ) -> Result<FunctionCall<S, Output>> {
827 self.ensure_open(ModelValidationPhase::Input)?;
828 let arguments = arguments.into_iter().collect::<Vec<_>>();
829 if arguments
830 .iter()
831 .any(|argument| argument.nonce != self.nonce)
832 {
833 return Err(cross_session_handle());
834 }
835 let expression = FunctionCallExpr {
836 function_id: function.__function_id().to_owned(),
837 arguments: arguments
838 .into_iter()
839 .map(|argument| argument.expression)
840 .collect(),
841 };
842 self.lower_function_call(&expression)?;
843 Ok(FunctionCall {
844 nonce: self.nonce,
845 expression,
846 marker: PhantomData,
847 })
848 }
849
850 fn lower_function_input(
851 &self,
852 input: &FunctionInputExpr,
853 ) -> Result<type_bridge_orm::FunctionValueHandle> {
854 let projected = ProjectedAttributeValue::try_from_attribute_value(
855 self.installed,
856 input.attribute_type.clone(),
857 &input.value,
858 )
859 .map_err(|error| Error::from_sdk_execution(error, ModelValidationPhase::Input))?;
860 self.session
861 .function_value(&projected)
862 .map_err(Error::from_orm)
863 }
864
865 fn lower_function_argument(
866 &self,
867 argument: &FunctionArgumentExpr,
868 ) -> Result<OrmFunctionArgumentHandle> {
869 match argument {
870 FunctionArgumentExpr::Binding(binding) => {
871 Ok(self.handle_by_key(*binding)?.function_argument())
872 }
873 FunctionArgumentExpr::Value(value) => {
874 Ok(self.lower_function_input(value)?.function_argument())
875 }
876 FunctionArgumentExpr::Call(call) => {
877 Ok(self.lower_function_call(call)?.function_argument())
878 }
879 }
880 }
881
882 fn lower_function_call(&self, call: &FunctionCallExpr) -> Result<OrmFunctionCallHandle> {
883 let id = FunctionId::new(call.function_id.clone()).map_err(|source| {
884 Error::model_validation(
885 ModelValidationPhase::Input,
886 "invalid_function_identity",
887 vec!["function".into()],
888 "generated function identity is not canonical",
889 Some(Box::new(source)),
890 )
891 })?;
892 let function = self.session.function(&id).map_err(Error::from_orm)?;
893 let arguments = call
894 .arguments
895 .iter()
896 .map(|argument| self.lower_function_argument(argument))
897 .collect::<Result<Vec<_>>>()?;
898 function.call(arguments).map_err(Error::from_orm)
899 }
900
901 fn installed(&self) -> Result<&InstalledRuntimeProjection> {
902 Ok(self.installed)
903 }
904
905 fn field_name_for(&self, owner_label: &str, attribute_label: &str) -> Result<String> {
906 let descriptor = self.registry.get(owner_label).ok_or_else(|| {
907 Error::model_validation(
908 ModelValidationPhase::Input,
909 "unknown_field_owner",
910 vec!["type".into()],
911 format!("field owner '{owner_label}' is not registered in this session"),
912 None,
913 )
914 })?;
915 let found = match &descriptor {
916 TypeDescriptorRef::Entity(entity) => entity
917 .owned_attributes
918 .iter()
919 .find(|attribute| attribute.attr_name == attribute_label)
920 .map(|attribute| attribute.field_name.clone()),
921 TypeDescriptorRef::Relation(relation) => relation
922 .owned_attributes
923 .iter()
924 .find(|attribute| attribute.attr_name == attribute_label)
925 .map(|attribute| attribute.field_name.clone()),
926 };
927 found.ok_or_else(|| {
928 Error::model_validation(
929 ModelValidationPhase::Input,
930 "unknown_field",
931 vec!["type".into()],
932 format!("owner '{owner_label}' has no field for attribute '{attribute_label}'"),
933 None,
934 )
935 })
936 }
937
938 pub(crate) fn lower_field(
939 &self,
940 key: BindingKey,
941 owns_id_json: &'static str,
942 ) -> Result<OrmFieldHandle> {
943 let handle = self.handle_by_key(key)?;
944 let (owner_label, attribute_label) = parse_owns_identity(owns_id_json)?;
945 let field_name = self.field_name_for(&owner_label, &attribute_label)?;
946 handle
947 .field_owned_by(&owner_label, &field_name)
948 .map_err(Error::from_orm)
949 }
950
951 fn lower_order(&self, order: &Order<S>) -> Result<OrmOrderHandle> {
952 let field = self.lower_field(order.key, order.owns_id_json)?;
953 Ok(field.order(order.direction, order.missing))
954 }
955
956 fn lower_predicate(&self, expr: &PredicateExpr) -> Result<OrmPredicateHandle> {
957 match expr {
958 PredicateExpr::FieldValue {
959 binding,
960 owns_id_json,
961 operator,
962 value,
963 } => {
964 let field = self.lower_field(*binding, owns_id_json)?;
965 Ok(field.compare_value(*operator, value.clone()))
966 }
967 PredicateExpr::FieldField {
968 left_binding,
969 left_owns_id_json,
970 operator,
971 right_binding,
972 right_owns_id_json,
973 } => {
974 let left = self.lower_field(*left_binding, left_owns_id_json)?;
975 let right = self.lower_field(*right_binding, right_owns_id_json)?;
976 left.compare_field(*operator, &right)
977 .map_err(Error::from_orm)
978 }
979 PredicateExpr::FieldPresence {
980 binding,
981 owns_id_json,
982 present,
983 } => Ok(self.lower_field(*binding, owns_id_json)?.presence(*present)),
984 PredicateExpr::FunctionField {
985 call,
986 operator,
987 binding,
988 owns_id_json,
989 } => self
990 .lower_function_call(call)?
991 .compare_field(*operator, &self.lower_field(*binding, owns_id_json)?)
992 .map_err(Error::from_orm),
993 PredicateExpr::FunctionValue {
994 call,
995 operator,
996 value,
997 } => self
998 .lower_function_call(call)?
999 .compare_value(*operator, &self.lower_function_input(value)?)
1000 .map_err(Error::from_orm),
1001 PredicateExpr::FunctionCall {
1002 left,
1003 operator,
1004 right,
1005 } => self
1006 .lower_function_call(left)?
1007 .compare_call(*operator, &self.lower_function_call(right)?)
1008 .map_err(Error::from_orm),
1009 PredicateExpr::BindingIid { binding, iid } => self
1010 .handle_by_key(*binding)?
1011 .iid(iid.clone())
1012 .map_err(Error::from_orm),
1013 PredicateExpr::BindingIidIn { binding, iids } => self
1014 .handle_by_key(*binding)?
1015 .iid_in(iids.clone())
1016 .map_err(Error::from_orm),
1017 PredicateExpr::Connects {
1018 relation,
1019 role_id_json,
1020 player,
1021 } => {
1022 let relation_handle = self.handle_by_key(*relation)?;
1023 let role = parse_role_identity(role_id_json)?;
1024 let role_handle = relation_handle
1025 .role_owned_by(role.declaring_relation().as_str(), role.label().as_str())
1026 .map_err(Error::from_orm)?;
1027 let player_handle = self.handle_by_key(*player)?;
1028 role_handle.connects(player_handle).map_err(Error::from_orm)
1029 }
1030 PredicateExpr::Reachable {
1031 relation_type_id_json,
1032 role_from_id_json,
1033 role_to_id_json,
1034 source,
1035 target,
1036 min_depth,
1037 max_depth,
1038 } => {
1039 let relation = model_label(relation_type_id_json)?;
1040 let role_from = parse_role_identity(role_from_id_json)?;
1041 let role_to = parse_role_identity(role_to_id_json)?;
1042 self.session
1043 .reachable(
1044 &relation,
1045 role_from.label().as_str(),
1046 role_to.label().as_str(),
1047 self.handle_by_key(*source)?,
1048 self.handle_by_key(*target)?,
1049 *min_depth,
1050 *max_depth,
1051 )
1052 .map_err(Error::from_orm)
1053 }
1054 PredicateExpr::And(terms) => self.lower_composed(terms, |left, right| {
1055 left.and(right).map_err(Error::from_orm)
1056 }),
1057 PredicateExpr::Or(terms) => {
1058 self.lower_composed(terms, |left, right| left.or(right).map_err(Error::from_orm))
1059 }
1060 PredicateExpr::Not(inner) => Ok(self.lower_predicate(inner)?.not()),
1061 }
1062 }
1063
1064 fn lower_composed(
1065 &self,
1066 terms: &[PredicateExpr],
1067 combine: impl Fn(&OrmPredicateHandle, &OrmPredicateHandle) -> Result<OrmPredicateHandle>,
1068 ) -> Result<OrmPredicateHandle> {
1069 let mut lowered = terms.iter().map(|term| self.lower_predicate(term));
1070 let mut combined = lowered.next().ok_or_else(|| {
1071 Error::model_validation(
1072 ModelValidationPhase::Input,
1073 "empty_predicate",
1074 vec![],
1075 "boolean composition requires at least one predicate",
1076 None,
1077 )
1078 })??;
1079 for term in lowered {
1080 combined = combine(&combined, &term?)?;
1081 }
1082 Ok(combined)
1083 }
1084
1085 fn client_row_for(&self, thing: &HydratedThing) -> Result<HydratedRow> {
1086 use type_bridge_orm::match_request::model::ThingKind as OrmThingKind;
1087 let installed = self.installed()?;
1088 let type_name = self
1089 .registry
1090 .descriptor_type_name(thing.concrete_descriptor())
1091 .ok_or_else(|| {
1092 Error::model_validation(
1093 ModelValidationPhase::Hydration,
1094 "invalid_installed_projection",
1095 vec!["type".into()],
1096 "selected concrete descriptor is absent from the installed registry",
1097 None,
1098 )
1099 })?;
1100 let mut attributes = Vec::new();
1101 for attribute in thing.attributes() {
1102 let provider_name = self
1103 .registry
1104 .provider_attribute_name(attribute.field())
1105 .ok_or_else(|| {
1106 Error::model_validation(
1107 ModelValidationPhase::Hydration,
1108 "invalid_installed_projection",
1109 vec![],
1110 "selected field identity has no provider attribute name",
1111 None,
1112 )
1113 })?;
1114 for value in attribute.values() {
1115 attributes.push((
1116 provider_name.clone(),
1117 canonicalize_selected_value(value.clone())?,
1118 ));
1119 }
1120 }
1121 match thing.kind() {
1122 OrmThingKind::Entity => {
1123 let id = TypeId::new(type_bridge_contract::id::TypeKind::Entity, &type_name)
1124 .map_err(|source| {
1125 Error::model_validation(
1126 ModelValidationPhase::Hydration,
1127 "invalid_discovered_type",
1128 vec!["type".into()],
1129 "selected entity type label is invalid",
1130 Some(Box::new(source)),
1131 )
1132 })?;
1133 hydrate_entity(
1134 DynamicEntityRow {
1135 iid: Some(thing.concept_id().as_str().to_owned()),
1136 type_name: Some(type_name),
1137 attributes,
1138 },
1139 &id,
1140 installed,
1141 )
1142 }
1143 OrmThingKind::Relation => {
1144 let id = TypeId::new(type_bridge_contract::id::TypeKind::Relation, &type_name)
1145 .map_err(|source| {
1146 Error::model_validation(
1147 ModelValidationPhase::Hydration,
1148 "invalid_discovered_type",
1149 vec!["type".into()],
1150 "selected relation type label is invalid",
1151 Some(Box::new(source)),
1152 )
1153 })?;
1154 let mut role_players = Vec::new();
1155 for role in thing.roles() {
1156 for player in role.players() {
1157 let mut raw = Vec::new();
1158 for attribute in player.attributes() {
1159 let provider_name = self
1160 .registry
1161 .provider_attribute_name(attribute.field())
1162 .ok_or_else(|| {
1163 Error::model_validation(
1164 ModelValidationPhase::Hydration,
1165 "invalid_installed_projection",
1166 vec![],
1167 "player field identity has no provider attribute name",
1168 None,
1169 )
1170 })?;
1171 for value in attribute.values() {
1172 raw.push((
1173 provider_name.clone(),
1174 plain_json(&canonicalize_selected_value(value.clone())?),
1175 ));
1176 }
1177 }
1178 let player_type_name = self
1179 .registry
1180 .descriptor_type_name(player.concrete_descriptor())
1181 .ok_or_else(|| {
1182 Error::model_validation(
1183 ModelValidationPhase::Hydration,
1184 "invalid_installed_projection",
1185 vec!["roles".into(), role.role().name.clone()],
1186 "selected role-player descriptor is absent from the installed registry",
1187 None,
1188 )
1189 })?;
1190 role_players.push(DynamicRolePlayer {
1191 role_name: role.role().name.clone(),
1192 player_iid: Some(player.concept_id().as_str().to_owned()),
1193 player_type_name: Some(player_type_name),
1194 attributes: raw,
1195 });
1196 }
1197 }
1198 hydrate_relation(
1199 DynamicRelationRow {
1200 iid: Some(thing.concept_id().as_str().to_owned()),
1201 type_name: Some(type_name),
1202 attributes,
1203 role_players,
1204 },
1205 &id,
1206 installed,
1207 )
1208 }
1209 }
1210 }
1211
1212 fn client_row_for_projected(&self, thing: &ProjectedThing) -> Result<HydratedRow> {
1213 projected_to_hydrated_row(thing, self.installed()?)
1214 }
1215}
1216
1217fn canonicalize_selected_value(value: AttributeValue) -> Result<AttributeValue> {
1218 let malformed = || {
1219 Error::model_validation(
1220 ModelValidationPhase::Hydration,
1221 "hydrated_attribute_value_type",
1222 vec![],
1223 "selected attribute value is outside its canonical scalar domain",
1224 None,
1225 )
1226 };
1227 match value {
1228 AttributeValue::Date(value) => value
1229 .parse::<CanonicalDate>()
1230 .map(|value| AttributeValue::Date(value.to_string()))
1231 .map_err(|_| malformed()),
1232 AttributeValue::DateTime(value) => normalize_provider_fraction(value)
1233 .parse::<CanonicalDateTime>()
1234 .map(|value| AttributeValue::DateTime(value.to_string()))
1235 .map_err(|_| malformed()),
1236 AttributeValue::DateTimeTZ(value) => normalize_provider_datetime_tz(value)
1237 .parse::<CanonicalDateTimeTz>()
1238 .map(|value| AttributeValue::DateTimeTZ(value.to_string()))
1239 .map_err(|_| malformed()),
1240 AttributeValue::Decimal(value) => parse_decimal(&value)
1241 .map(|value| AttributeValue::Decimal(value.canonical_string()))
1242 .ok_or_else(malformed),
1243 AttributeValue::Duration(value) => {
1244 let value = normalize_provider_fraction(value);
1245 match value.parse::<CanonicalDuration>() {
1246 Ok(value) => Ok(AttributeValue::Duration(value.to_string())),
1247 Err(_) => CompatibilityValueV2::released_duration(value.clone())
1248 .map(|_| AttributeValue::Duration(value))
1249 .map_err(|_| malformed()),
1250 }
1251 }
1252 value => Ok(value),
1253 }
1254}
1255
1256fn encoded_group_scalar(value: &AttributeValue) -> Result<EncodedScalar> {
1257 let value = canonicalize_selected_value(value.clone())?;
1258 let map = |error| map_validation_error(error, ModelValidationPhase::Hydration);
1259 match value {
1260 AttributeValue::String(value) => Ok(EncodedScalar::String(value)),
1261 AttributeValue::Long(value) => Ok(EncodedScalar::Long(value)),
1262 AttributeValue::Double(value) => crate::__codegen::CanonicalDouble::try_new(value)
1263 .map(EncodedScalar::Double)
1264 .map_err(map),
1265 AttributeValue::Boolean(value) => Ok(EncodedScalar::Boolean(value)),
1266 AttributeValue::Date(value) => crate::__codegen::Date::try_new(value)
1267 .map(EncodedScalar::Date)
1268 .map_err(map),
1269 AttributeValue::DateTime(value) => crate::__codegen::DateTime::try_new(value)
1270 .map(EncodedScalar::DateTime)
1271 .map_err(map),
1272 AttributeValue::DateTimeTZ(value) => crate::__codegen::DateTimeTz::try_new(value)
1273 .map(EncodedScalar::DateTimeTz)
1274 .map_err(map),
1275 AttributeValue::Decimal(value) => crate::__codegen::Decimal::try_new(value)
1276 .map(EncodedScalar::Decimal)
1277 .map_err(map),
1278 AttributeValue::Duration(value) => crate::__codegen::Duration::try_new(value)
1279 .map(EncodedScalar::Duration)
1280 .map_err(map),
1281 }
1282}
1283
1284fn decoded_projected_reduction_values(values: &[ProjectedReducedValue]) -> Vec<ReducedValue> {
1285 values
1286 .iter()
1287 .map(|value| match value {
1288 ProjectedReducedValue::Count(value) => ReducedValue::Count(*value),
1289 ProjectedReducedValue::Long(value) => ReducedValue::Long(*value),
1290 ProjectedReducedValue::Double(value) => {
1291 ReducedValue::Double(value.map(type_bridge_contract::value::CanonicalDouble::get))
1292 }
1293 })
1294 .collect()
1295}
1296
1297fn normalize_provider_datetime_tz(value: String) -> String {
1298 let mut normalized = normalize_provider_fraction(value);
1299 for zero_offset in ["+00:00:00", "-00:00:00", "+00:00", "-00:00"] {
1300 if normalized.ends_with(zero_offset) {
1301 normalized.truncate(normalized.len() - zero_offset.len());
1302 normalized.push('Z');
1303 break;
1304 }
1305 }
1306 normalized
1307}
1308
1309fn normalize_provider_fraction(value: String) -> String {
1310 let Some(dot) = value.find('.') else {
1311 return value;
1312 };
1313 let fraction_end = value[dot + 1..]
1314 .find(|character: char| !character.is_ascii_digit())
1315 .map_or(value.len(), |offset| dot + 1 + offset);
1316 let trimmed_end = value[dot + 1..fraction_end].trim_end_matches('0').len() + dot + 1;
1317 if trimmed_end == fraction_end {
1318 return value;
1319 }
1320 let mut normalized = String::with_capacity(value.len());
1321 normalized.push_str(
1322 &value[..if trimmed_end == dot + 1 {
1323 dot
1324 } else {
1325 trimmed_end
1326 }],
1327 );
1328 normalized.push_str(&value[fraction_end..]);
1329 normalized
1330}
1331
1332fn plain_json(value: &AttributeValue) -> serde_json::Value {
1333 match value {
1334 AttributeValue::String(value) => serde_json::Value::String(value.clone()),
1335 AttributeValue::Long(value) => serde_json::Value::from(*value),
1336 AttributeValue::Double(value) => {
1337 serde_json::Number::from_f64(*value).map_or(serde_json::Value::Null, Into::into)
1338 }
1339 AttributeValue::Boolean(value) => serde_json::Value::Bool(*value),
1340 AttributeValue::Date(value)
1341 | AttributeValue::DateTime(value)
1342 | AttributeValue::DateTimeTZ(value)
1343 | AttributeValue::Decimal(value)
1344 | AttributeValue::Duration(value) => serde_json::Value::String(value.clone()),
1345 }
1346}
1347
1348pub trait QueryOperand: operand_sealed::Sealed {
1351 #[doc(hidden)]
1352 type Domain;
1353
1354 #[doc(hidden)]
1355 fn into_operand(self) -> AttributeValue;
1356}
1357
1358pub trait OrderedOperand: QueryOperand {}
1360
1361mod operand_sealed {
1362 pub trait Sealed {}
1363}
1364
1365macro_rules! operand {
1366 ($ty:ty, $domain:ty, $self_:ident => $convert:expr, ordered: $ordered:tt) => {
1367 impl operand_sealed::Sealed for $ty {}
1368 impl QueryOperand for $ty {
1369 type Domain = $domain;
1370
1371 fn into_operand($self_) -> AttributeValue {
1372 $convert
1373 }
1374 }
1375 operand!(@ordered $ty, $ordered);
1376 };
1377 (@ordered $ty:ty, true) => {
1378 impl OrderedOperand for $ty {}
1379 };
1380 (@ordered $ty:ty, false) => {};
1381}
1382
1383fn encoded_query_operand(value: EncodedScalar) -> AttributeValue {
1384 match value {
1385 EncodedScalar::String(value) => AttributeValue::String(value),
1386 EncodedScalar::Long(value) => AttributeValue::Long(value),
1387 EncodedScalar::Double(value) => AttributeValue::Double(value.get()),
1388 EncodedScalar::Boolean(value) => AttributeValue::Boolean(value),
1389 EncodedScalar::Date(value) => AttributeValue::Date(value.as_str().to_owned()),
1390 EncodedScalar::DateTime(value) => AttributeValue::DateTime(value.as_str().to_owned()),
1391 EncodedScalar::DateTimeTz(value) => AttributeValue::DateTimeTZ(value.as_str().to_owned()),
1392 EncodedScalar::Decimal(value) => AttributeValue::Decimal(value.as_str().to_owned()),
1393 EncodedScalar::Duration(value) => AttributeValue::Duration(value.as_str().to_owned()),
1394 }
1395}
1396
1397impl<T: QueryValued> operand_sealed::Sealed for T {}
1398impl<T: QueryValued> QueryOperand for T {
1399 type Domain = T::Domain;
1400
1401 fn into_operand(self) -> AttributeValue {
1402 encoded_query_operand(self.into_encoded_scalar())
1403 }
1404}
1405
1406impl OrderedOperand for i64 {}
1407
1408operand!(
1409 crate::value::Text,
1410 String,
1411 self => AttributeValue::String(self.into_string()),
1412 ordered: false
1413);
1414operand!(
1415 crate::value::Double,
1416 crate::__codegen::CanonicalDouble,
1417 self => AttributeValue::Double(self.get()),
1418 ordered: true
1419);
1420operand!(
1421 crate::value::Decimal,
1422 crate::__codegen::Decimal,
1423 self => AttributeValue::Decimal(self.into_string()),
1424 ordered: true
1425);
1426operand!(
1427 crate::value::Date,
1428 crate::__codegen::Date,
1429 self => AttributeValue::Date(self.into_string()),
1430 ordered: true
1431);
1432operand!(
1433 crate::value::DateTime,
1434 crate::__codegen::DateTime,
1435 self => AttributeValue::DateTime(self.into_string()),
1436 ordered: true
1437);
1438operand!(
1439 crate::value::DateTimeTz,
1440 crate::__codegen::DateTimeTz,
1441 self => AttributeValue::DateTimeTZ(self.into_string()),
1442 ordered: true
1443);
1444operand!(
1445 crate::value::Duration,
1446 crate::__codegen::Duration,
1447 self => AttributeValue::Duration(self.into_string()),
1448 ordered: true
1449);
1450
1451#[derive(Clone, Debug, PartialEq)]
1452pub(crate) enum PredicateExpr {
1453 FieldValue {
1454 binding: BindingKey,
1455 owns_id_json: &'static str,
1456 operator: ComparisonOp,
1457 value: AttributeValue,
1458 },
1459 FieldField {
1460 left_binding: BindingKey,
1461 left_owns_id_json: &'static str,
1462 operator: ComparisonOp,
1463 right_binding: BindingKey,
1464 right_owns_id_json: &'static str,
1465 },
1466 FieldPresence {
1467 binding: BindingKey,
1468 owns_id_json: &'static str,
1469 present: bool,
1470 },
1471 FunctionField {
1472 call: FunctionCallExpr,
1473 operator: ComparisonOp,
1474 binding: BindingKey,
1475 owns_id_json: &'static str,
1476 },
1477 FunctionValue {
1478 call: FunctionCallExpr,
1479 operator: ComparisonOp,
1480 value: FunctionInputExpr,
1481 },
1482 FunctionCall {
1483 left: FunctionCallExpr,
1484 operator: ComparisonOp,
1485 right: FunctionCallExpr,
1486 },
1487 BindingIid {
1488 binding: BindingKey,
1489 iid: String,
1490 },
1491 BindingIidIn {
1492 binding: BindingKey,
1493 iids: Vec<String>,
1494 },
1495 Connects {
1496 relation: BindingKey,
1497 role_id_json: &'static str,
1498 player: BindingKey,
1499 },
1500 Reachable {
1501 relation_type_id_json: &'static str,
1502 role_from_id_json: &'static str,
1503 role_to_id_json: &'static str,
1504 source: BindingKey,
1505 target: BindingKey,
1506 min_depth: u8,
1507 max_depth: u8,
1508 },
1509 And(Vec<PredicateExpr>),
1510 Or(Vec<PredicateExpr>),
1511 Not(Box<PredicateExpr>),
1512}
1513
1514#[derive(Debug, PartialEq)]
1521pub struct Predicate<S: Schema> {
1522 pub(crate) expr: PredicateExpr,
1523 marker: PhantomData<fn() -> S>,
1524}
1525
1526impl<S: Schema> Clone for Predicate<S> {
1527 fn clone(&self) -> Self {
1528 Self {
1529 expr: self.expr.clone(),
1530 marker: PhantomData,
1531 }
1532 }
1533}
1534
1535impl<S: Schema> Predicate<S> {
1536 fn new(expr: PredicateExpr) -> Self {
1537 Self {
1538 expr,
1539 marker: PhantomData,
1540 }
1541 }
1542
1543 #[must_use]
1545 pub fn and(self, other: Predicate<S>) -> Predicate<S> {
1546 self & other
1547 }
1548
1549 #[must_use]
1551 pub fn or(self, other: Predicate<S>) -> Predicate<S> {
1552 self | other
1553 }
1554
1555 #[must_use]
1557 #[allow(clippy::should_implement_trait)]
1558 pub fn not(self) -> Predicate<S> {
1559 !self
1560 }
1561}
1562
1563impl<'db, S: Schema> QuerySession<'db, S> {
1564 #[allow(clippy::too_many_arguments)]
1573 pub fn reachable<
1574 R,
1575 FromOwner,
1576 FromPlayers,
1577 ToOwner,
1578 ToPlayers,
1579 Source,
1580 SourceMode,
1581 Target,
1582 TargetMode,
1583 >(
1584 &self,
1585 relation: TypeToken<R>,
1586 role_from: RoleToken<FromOwner, FromPlayers>,
1587 role_to: RoleToken<ToOwner, ToPlayers>,
1588 source: Binding<S, Source, SourceMode>,
1589 target: Binding<S, Target, TargetMode>,
1590 min_depth: u8,
1591 max_depth: u8,
1592 ) -> Result<Predicate<S>>
1593 where
1594 R: RelationModel<Schema = S>
1595 + CompleteModel
1596 + RoleTokenCompatible<FromOwner, FromPlayers>
1597 + RoleTokenCompatible<ToOwner, ToPlayers>,
1598 FromOwner: RelationModel<Schema = S>,
1599 ToOwner: RelationModel<Schema = S>,
1600 FromPlayers: RolePlayerBinding<Source, SourceMode>,
1601 ToPlayers: RolePlayerBinding<Target, TargetMode>,
1602 Source: ThingModel<Schema = S>,
1603 SourceMode: SelectionMode,
1604 Target: ThingModel<Schema = S>,
1605 TargetMode: SelectionMode,
1606 {
1607 let predicate = Predicate::new(PredicateExpr::Reachable {
1608 relation_type_id_json: relation.type_id_json(),
1609 role_from_id_json: role_from.role_id_json(),
1610 role_to_id_json: role_to.role_id_json(),
1611 source: source.key,
1612 target: target.key,
1613 min_depth,
1614 max_depth,
1615 });
1616 self.lower_predicate(&predicate.expr)?;
1617 Ok(predicate)
1618 }
1619}
1620
1621impl<S: Schema> std::ops::BitAnd for Predicate<S> {
1622 type Output = Predicate<S>;
1623 fn bitand(self, other: Predicate<S>) -> Predicate<S> {
1624 let mut terms = match self.expr {
1625 PredicateExpr::And(terms) => terms,
1626 expr => vec![expr],
1627 };
1628 match other.expr {
1629 PredicateExpr::And(more) => terms.extend(more),
1630 expr => terms.push(expr),
1631 }
1632 Predicate::new(PredicateExpr::And(terms))
1633 }
1634}
1635
1636impl<S: Schema> std::ops::BitOr for Predicate<S> {
1637 type Output = Predicate<S>;
1638 fn bitor(self, other: Predicate<S>) -> Predicate<S> {
1639 let mut terms = match self.expr {
1640 PredicateExpr::Or(terms) => terms,
1641 expr => vec![expr],
1642 };
1643 match other.expr {
1644 PredicateExpr::Or(more) => terms.extend(more),
1645 expr => terms.push(expr),
1646 }
1647 Predicate::new(PredicateExpr::Or(terms))
1648 }
1649}
1650
1651impl<S: Schema> std::ops::Not for Predicate<S> {
1652 type Output = Predicate<S>;
1653 fn not(self) -> Predicate<S> {
1654 Predicate::new(PredicateExpr::Not(Box::new(self.expr)))
1655 }
1656}
1657
1658pub struct BoundField<S: Schema, Owner: Model<Schema = S>, V> {
1664 key: BindingKey,
1665 owns_id_json: &'static str,
1666 marker: PhantomData<fn() -> (Owner, V)>,
1667}
1668
1669impl<S: Schema, Owner: Model<Schema = S>, V> Copy for BoundField<S, Owner, V> {}
1670impl<S: Schema, Owner: Model<Schema = S>, V> Clone for BoundField<S, Owner, V> {
1671 fn clone(&self) -> Self {
1672 *self
1673 }
1674}
1675
1676impl<S: Schema, M: ThingModel<Schema = S>, Mode: SelectionMode> Binding<S, M, Mode> {
1677 #[must_use]
1683 pub fn field<Owner, V>(self, token: FieldToken<Owner, V>) -> BoundField<S, Owner, V>
1684 where
1685 Owner: Model<Schema = S>,
1686 M: crate::__codegen::NominalUpcast<Owner>,
1687 {
1688 BoundField {
1689 key: self.key,
1690 owns_id_json: token.owns_id_json(),
1691 marker: PhantomData,
1692 }
1693 }
1694}
1695
1696impl<S: Schema, M: RelationModel<Schema = S> + ThingModel<Schema = S>, Mode: SelectionMode>
1697 Binding<S, M, Mode>
1698{
1699 #[must_use]
1704 pub fn role<Owner, Players>(
1705 self,
1706 token: RoleToken<Owner, Players>,
1707 ) -> BoundRole<S, Owner, Players>
1708 where
1709 Owner: RelationModel<Schema = S>,
1710 M: RoleTokenCompatible<Owner, Players>,
1711 {
1712 BoundRole {
1713 key: self.key,
1714 role_id_json: token.role_id_json(),
1715 marker: PhantomData,
1716 }
1717 }
1718}
1719
1720impl<S: Schema, Owner: Model<Schema = S>, V> BoundField<S, Owner, V> {
1721 pub(crate) fn reduction_input(self) -> (BindingKey, &'static str) {
1722 (self.key, self.owns_id_json)
1723 }
1724
1725 fn value_predicate(self, operator: ComparisonOp, value: AttributeValue) -> Predicate<S> {
1726 Predicate::new(PredicateExpr::FieldValue {
1727 binding: self.key,
1728 owns_id_json: self.owns_id_json,
1729 operator,
1730 value,
1731 })
1732 }
1733
1734 #[must_use]
1736 pub fn eq<O>(self, operand: O) -> Predicate<S>
1737 where
1738 V: QueryValued,
1739 O: QueryOperand<Domain = V::Domain>,
1740 {
1741 self.value_predicate(ComparisonOp::Equal, operand.into_operand())
1742 }
1743
1744 #[must_use]
1746 pub fn ne<O>(self, operand: O) -> Predicate<S>
1747 where
1748 V: QueryValued,
1749 O: QueryOperand<Domain = V::Domain>,
1750 {
1751 self.value_predicate(ComparisonOp::NotEqual, operand.into_operand())
1752 }
1753
1754 #[must_use]
1757 pub fn lt(self, operand: impl OrderedOperand) -> Predicate<S>
1758 where
1759 V: crate::__codegen::OrderedValued,
1760 {
1761 self.value_predicate(ComparisonOp::LessThan, operand.into_operand())
1762 }
1763
1764 #[must_use]
1767 pub fn le(self, operand: impl OrderedOperand) -> Predicate<S>
1768 where
1769 V: crate::__codegen::OrderedValued,
1770 {
1771 self.value_predicate(ComparisonOp::LessThanOrEqual, operand.into_operand())
1772 }
1773
1774 #[must_use]
1777 pub fn gt(self, operand: impl OrderedOperand) -> Predicate<S>
1778 where
1779 V: crate::__codegen::OrderedValued,
1780 {
1781 self.value_predicate(ComparisonOp::GreaterThan, operand.into_operand())
1782 }
1783
1784 #[must_use]
1787 pub fn ge(self, operand: impl OrderedOperand) -> Predicate<S>
1788 where
1789 V: crate::__codegen::OrderedValued,
1790 {
1791 self.value_predicate(ComparisonOp::GreaterThanOrEqual, operand.into_operand())
1792 }
1793
1794 #[must_use]
1797 pub fn contains(self, text: crate::value::Text) -> Predicate<S>
1798 where
1799 V: crate::__codegen::TextValued,
1800 {
1801 self.value_predicate(
1802 ComparisonOp::Contains,
1803 AttributeValue::String(text.into_string()),
1804 )
1805 }
1806
1807 #[must_use]
1810 pub fn starts_with(self, text: crate::value::Text) -> Predicate<S>
1811 where
1812 V: crate::__codegen::TextValued,
1813 {
1814 self.value_predicate(
1815 ComparisonOp::StartsWith,
1816 AttributeValue::String(text.into_string()),
1817 )
1818 }
1819
1820 #[must_use]
1823 pub fn ends_with(self, text: crate::value::Text) -> Predicate<S>
1824 where
1825 V: crate::__codegen::TextValued,
1826 {
1827 self.value_predicate(
1828 ComparisonOp::EndsWith,
1829 AttributeValue::String(text.into_string()),
1830 )
1831 }
1832
1833 #[must_use]
1836 pub fn regex(self, pattern: crate::value::Regex) -> Predicate<S>
1837 where
1838 V: crate::__codegen::TextValued,
1839 {
1840 self.value_predicate(
1841 ComparisonOp::Regex,
1842 AttributeValue::String(pattern.into_string()),
1843 )
1844 }
1845
1846 #[must_use]
1848 pub fn is_present(self) -> Predicate<S> {
1849 Predicate::new(PredicateExpr::FieldPresence {
1850 binding: self.key,
1851 owns_id_json: self.owns_id_json,
1852 present: true,
1853 })
1854 }
1855
1856 #[must_use]
1858 pub fn is_missing(self) -> Predicate<S> {
1859 Predicate::new(PredicateExpr::FieldPresence {
1860 binding: self.key,
1861 owns_id_json: self.owns_id_json,
1862 present: false,
1863 })
1864 }
1865
1866 fn field_predicate<Owner2, V2>(
1867 self,
1868 operator: ComparisonOp,
1869 other: BoundField<S, Owner2, V2>,
1870 ) -> Predicate<S>
1871 where
1872 Owner2: Model<Schema = S>,
1873 {
1874 Predicate::new(PredicateExpr::FieldField {
1875 left_binding: self.key,
1876 left_owns_id_json: self.owns_id_json,
1877 operator,
1878 right_binding: other.key,
1879 right_owns_id_json: other.owns_id_json,
1880 })
1881 }
1882
1883 #[must_use]
1886 pub fn eq_field<Owner2, V2>(self, other: BoundField<S, Owner2, V2>) -> Predicate<S>
1887 where
1888 Owner2: Model<Schema = S>,
1889 V: QueryValued,
1890 V2: QueryValued<Domain = V::Domain>,
1891 {
1892 self.field_predicate(ComparisonOp::Equal, other)
1893 }
1894
1895 #[must_use]
1898 pub fn ne_field<Owner2, V2>(self, other: BoundField<S, Owner2, V2>) -> Predicate<S>
1899 where
1900 Owner2: Model<Schema = S>,
1901 V: QueryValued,
1902 V2: QueryValued<Domain = V::Domain>,
1903 {
1904 self.field_predicate(ComparisonOp::NotEqual, other)
1905 }
1906
1907 #[must_use]
1910 pub fn lt_field<Owner2, V2>(self, other: BoundField<S, Owner2, V2>) -> Predicate<S>
1911 where
1912 Owner2: Model<Schema = S>,
1913 V: QueryValued + crate::__codegen::OrderedValued,
1914 V2: QueryValued<Domain = V::Domain> + crate::__codegen::OrderedValued,
1915 {
1916 self.field_predicate(ComparisonOp::LessThan, other)
1917 }
1918
1919 #[must_use]
1922 pub fn le_field<Owner2, V2>(self, other: BoundField<S, Owner2, V2>) -> Predicate<S>
1923 where
1924 Owner2: Model<Schema = S>,
1925 V: QueryValued + crate::__codegen::OrderedValued,
1926 V2: QueryValued<Domain = V::Domain> + crate::__codegen::OrderedValued,
1927 {
1928 self.field_predicate(ComparisonOp::LessThanOrEqual, other)
1929 }
1930
1931 #[must_use]
1934 pub fn gt_field<Owner2, V2>(self, other: BoundField<S, Owner2, V2>) -> Predicate<S>
1935 where
1936 Owner2: Model<Schema = S>,
1937 V: QueryValued + crate::__codegen::OrderedValued,
1938 V2: QueryValued<Domain = V::Domain> + crate::__codegen::OrderedValued,
1939 {
1940 self.field_predicate(ComparisonOp::GreaterThan, other)
1941 }
1942
1943 #[must_use]
1946 pub fn ge_field<Owner2, V2>(self, other: BoundField<S, Owner2, V2>) -> Predicate<S>
1947 where
1948 Owner2: Model<Schema = S>,
1949 V: QueryValued + crate::__codegen::OrderedValued,
1950 V2: QueryValued<Domain = V::Domain> + crate::__codegen::OrderedValued,
1951 {
1952 self.field_predicate(ComparisonOp::GreaterThanOrEqual, other)
1953 }
1954
1955 #[must_use]
1958 pub fn asc(self) -> Order<S> {
1959 Order {
1960 key: self.key,
1961 owns_id_json: self.owns_id_json,
1962 direction: SortDirection::Ascending,
1963 missing: MissingOrder::Reject,
1964 marker: PhantomData,
1965 }
1966 }
1967
1968 #[must_use]
1971 pub fn desc(self) -> Order<S> {
1972 Order {
1973 key: self.key,
1974 owns_id_json: self.owns_id_json,
1975 direction: SortDirection::Descending,
1976 missing: MissingOrder::Reject,
1977 marker: PhantomData,
1978 }
1979 }
1980}
1981
1982#[derive(Debug)]
1984pub struct Order<S: Schema> {
1985 key: BindingKey,
1986 owns_id_json: &'static str,
1987 direction: SortDirection,
1988 missing: MissingOrder,
1989 marker: PhantomData<fn() -> S>,
1990}
1991
1992impl<S: Schema> Copy for Order<S> {}
1993impl<S: Schema> Clone for Order<S> {
1994 fn clone(&self) -> Self {
1995 *self
1996 }
1997}
1998
1999impl<S: Schema> Order<S> {
2000 #[must_use]
2002 pub fn missing_first(mut self) -> Self {
2003 self.missing = MissingOrder::First;
2004 self
2005 }
2006
2007 #[must_use]
2009 pub fn missing_last(mut self) -> Self {
2010 self.missing = MissingOrder::Last;
2011 self
2012 }
2013}
2014
2015pub struct Collected<S: Schema, B: Selectable<S>> {
2017 selection: B,
2018 distinct: bool,
2019 order: Vec<Order<S>>,
2020}
2021
2022impl<S: Schema, B: Selectable<S>> Clone for Collected<S, B> {
2023 fn clone(&self) -> Self {
2024 Self {
2025 selection: self.selection,
2026 distinct: self.distinct,
2027 order: self.order.clone(),
2028 }
2029 }
2030}
2031
2032impl<S: Schema, B: Selectable<S>> Collected<S, B> {
2033 #[must_use]
2035 pub fn distinct(mut self) -> Self {
2036 self.distinct = true;
2037 self
2038 }
2039
2040 pub fn order_by(mut self, order: Order<S>) -> Result<Self> {
2042 if order.key != self.selection.binding_key() {
2043 return Err(Error::model_validation(
2044 ModelValidationPhase::Input,
2045 "collection_order_binding_mismatch",
2046 vec![],
2047 "collection ordering must reference the collected binding",
2048 None,
2049 ));
2050 }
2051 self.order.push(order);
2052 Ok(self)
2053 }
2054}
2055
2056pub struct BoundRole<S: Schema, Owner: Model<Schema = S>, Players> {
2059 key: BindingKey,
2060 role_id_json: &'static str,
2061 marker: PhantomData<fn() -> (Owner, Players)>,
2062}
2063
2064impl<S: Schema, Owner: Model<Schema = S>, Players> Copy for BoundRole<S, Owner, Players> {}
2065impl<S: Schema, Owner: Model<Schema = S>, Players> Clone for BoundRole<S, Owner, Players> {
2066 fn clone(&self) -> Self {
2067 *self
2068 }
2069}
2070
2071impl<S: Schema, Owner: Model<Schema = S>, Players> BoundRole<S, Owner, Players> {
2072 #[must_use]
2075 pub fn connects<MP: ThingModel<Schema = S>, ModeP: SelectionMode>(
2076 self,
2077 player: Binding<S, MP, ModeP>,
2078 ) -> Predicate<S>
2079 where
2080 Players: RolePlayerBinding<MP, ModeP>,
2081 {
2082 Predicate::new(PredicateExpr::Connects {
2083 relation: self.key,
2084 role_id_json: self.role_id_json,
2085 player: player.key,
2086 })
2087 }
2088}
2089
2090mod selectable_sealed {
2091 pub trait Sealed {}
2092}
2093
2094pub trait Selectable<S: Schema>: selectable_sealed::Sealed + Copy {
2096 type Output;
2098 #[doc(hidden)]
2099 fn binding_key(self) -> BindingKey;
2100 #[doc(hidden)]
2101 fn materialize_output(row: &HydratedRow) -> std::result::Result<Self::Output, ValidationError>;
2102
2103 #[doc(hidden)]
2104 fn __materialize_projected_output(
2105 session: &QuerySession<'_, S>,
2106 thing: &ProjectedThing,
2107 ) -> Result<Self::Output> {
2108 let row = session.client_row_for_projected(thing)?;
2109 Self::materialize_output(&row)
2110 .map_err(|error| map_validation_error(error, ModelValidationPhase::Hydration))
2111 }
2112
2113 #[doc(hidden)]
2114 fn __selection_handle(self, session: &QuerySession<'_, S>) -> Result<OrmSelectionHandle> {
2115 Ok(session.handle_by_key(self.binding_key())?.one())
2116 }
2117
2118 #[doc(hidden)]
2119 fn __materialize_slot(
2120 self,
2121 session: &QuerySession<'_, S>,
2122 slot: &SlotValue,
2123 ) -> Result<Self::Output> {
2124 let SlotValue::One(thing) = slot else {
2125 return Err(Error::model_validation(
2126 ModelValidationPhase::Hydration,
2127 "wrong_result_shape",
2128 vec![],
2129 "provider returned a collection slot for a singular selection",
2130 None,
2131 ));
2132 };
2133 let row = session.client_row_for(thing)?;
2134 Self::materialize_output(&row)
2135 .map_err(|error| map_validation_error(error, ModelValidationPhase::Hydration))
2136 }
2137
2138 #[doc(hidden)]
2139 fn __materialize_slot_with_checkpoint(
2140 self,
2141 session: &QuerySession<'_, S>,
2142 slot: &SlotValue,
2143 checkpoint: &dyn Fn() -> Result<()>,
2144 ) -> Result<Self::Output> {
2145 checkpoint()?;
2146 let output = self.__materialize_slot(session, slot)?;
2147 checkpoint()?;
2148 Ok(output)
2149 }
2150}
2151
2152impl<S: Schema, M: ThingModel<Schema = S> + CompleteModel> selectable_sealed::Sealed
2153 for Binding<S, M, Exact>
2154{
2155}
2156impl<S: Schema, M: ThingModel<Schema = S> + CompleteModel> Selectable<S> for Binding<S, M, Exact> {
2157 type Output = M;
2158 fn binding_key(self) -> BindingKey {
2159 self.key()
2160 }
2161 fn materialize_output(row: &HydratedRow) -> std::result::Result<M, ValidationError> {
2162 M::materialize(row, &HydrationCapability::new())
2163 }
2164}
2165
2166impl<S: Schema, M: ThingModel<Schema = S> + SubtypeRootModel> selectable_sealed::Sealed
2167 for Binding<S, M, Subtypes>
2168{
2169}
2170impl<S: Schema, M: ThingModel<Schema = S> + SubtypeRootModel> Selectable<S>
2171 for Binding<S, M, Subtypes>
2172{
2173 type Output = M::Subtypes;
2174 fn binding_key(self) -> BindingKey {
2175 self.key()
2176 }
2177 fn materialize_output(row: &HydratedRow) -> std::result::Result<M::Subtypes, ValidationError> {
2178 M::__tb_dispatch_subtype(row, &HydrationCapability::new())
2179 }
2180}
2181
2182mod selected_slot_sealed {
2183 pub trait Sealed<S> {}
2184}
2185
2186#[doc(hidden)]
2189pub trait SelectedSlot<S: Schema>: selected_slot_sealed::Sealed<S> + Clone {
2190 type Output;
2191
2192 fn __selection_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmSelectionHandle>;
2193
2194 fn __materialize_slot(
2195 &self,
2196 session: &QuerySession<'_, S>,
2197 slot: &SlotValue,
2198 ) -> Result<Self::Output>;
2199
2200 #[doc(hidden)]
2201 fn __materialize_projected_slot(
2202 &self,
2203 session: &QuerySession<'_, S>,
2204 slot: &ProjectedQuerySlotValue,
2205 ) -> Result<Self::Output>;
2206
2207 #[doc(hidden)]
2208 fn __materialize_slot_with_checkpoint(
2209 &self,
2210 session: &QuerySession<'_, S>,
2211 slot: &SlotValue,
2212 checkpoint: &dyn Fn() -> Result<()>,
2213 ) -> Result<Self::Output> {
2214 checkpoint()?;
2215 let output = self.__materialize_slot(session, slot)?;
2216 checkpoint()?;
2217 Ok(output)
2218 }
2219
2220 #[doc(hidden)]
2221 fn __materialize_projected_slot_with_checkpoint(
2222 &self,
2223 session: &QuerySession<'_, S>,
2224 slot: &ProjectedQuerySlotValue,
2225 checkpoint: &dyn Fn() -> Result<()>,
2226 ) -> Result<Self::Output> {
2227 checkpoint()?;
2228 let output = self.__materialize_projected_slot(session, slot)?;
2229 checkpoint()?;
2230 Ok(output)
2231 }
2232}
2233
2234impl<S: Schema, B: Selectable<S>> selected_slot_sealed::Sealed<S> for B {}
2235impl<S: Schema, B: Selectable<S>> SelectedSlot<S> for B {
2236 type Output = B::Output;
2237
2238 fn __selection_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmSelectionHandle> {
2239 (*self).__selection_handle(session)
2240 }
2241
2242 fn __materialize_slot(
2243 &self,
2244 session: &QuerySession<'_, S>,
2245 slot: &SlotValue,
2246 ) -> Result<Self::Output> {
2247 (*self).__materialize_slot(session, slot)
2248 }
2249
2250 fn __materialize_projected_slot(
2251 &self,
2252 session: &QuerySession<'_, S>,
2253 slot: &ProjectedQuerySlotValue,
2254 ) -> Result<Self::Output> {
2255 let ProjectedQuerySlotValue::One(thing) = slot else {
2256 return Err(Error::model_validation(
2257 ModelValidationPhase::Hydration,
2258 "wrong_result_shape",
2259 vec![],
2260 "provider returned a collection slot for a singular selection",
2261 None,
2262 ));
2263 };
2264 B::__materialize_projected_output(session, thing)
2265 }
2266
2267 fn __materialize_slot_with_checkpoint(
2268 &self,
2269 session: &QuerySession<'_, S>,
2270 slot: &SlotValue,
2271 checkpoint: &dyn Fn() -> Result<()>,
2272 ) -> Result<Self::Output> {
2273 (*self).__materialize_slot_with_checkpoint(session, slot, checkpoint)
2274 }
2275
2276 fn __materialize_projected_slot_with_checkpoint(
2277 &self,
2278 session: &QuerySession<'_, S>,
2279 slot: &ProjectedQuerySlotValue,
2280 checkpoint: &dyn Fn() -> Result<()>,
2281 ) -> Result<Self::Output> {
2282 checkpoint()?;
2283 let output = self.__materialize_projected_slot(session, slot)?;
2284 checkpoint()?;
2285 Ok(output)
2286 }
2287}
2288
2289impl<S: Schema, B: Selectable<S>> selected_slot_sealed::Sealed<S> for Collected<S, B> {}
2290impl<S: Schema, B: Selectable<S>> SelectedSlot<S> for Collected<S, B> {
2291 type Output = Vec<B::Output>;
2292
2293 fn __selection_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmSelectionHandle> {
2294 let binding = session.handle_by_key(self.selection.binding_key())?;
2295 let mut selection = binding
2296 .collect()
2297 .distinct(self.distinct)
2298 .map_err(Error::from_orm)?;
2299 for order in &self.order {
2300 selection = selection
2301 .order_by(session.lower_order(order)?)
2302 .map_err(Error::from_orm)?;
2303 }
2304 Ok(selection)
2305 }
2306
2307 fn __materialize_slot(
2308 &self,
2309 session: &QuerySession<'_, S>,
2310 slot: &SlotValue,
2311 ) -> Result<Self::Output> {
2312 let SlotValue::Many(things) = slot else {
2313 return Err(Error::model_validation(
2314 ModelValidationPhase::Hydration,
2315 "wrong_result_shape",
2316 vec![],
2317 "provider returned a singular slot for a collection selection",
2318 None,
2319 ));
2320 };
2321 let mut outputs = Vec::with_capacity(things.len());
2322 for thing in things {
2323 let row = session.client_row_for(thing)?;
2324 outputs.push(
2325 B::materialize_output(&row).map_err(|error| {
2326 map_validation_error(error, ModelValidationPhase::Hydration)
2327 })?,
2328 );
2329 }
2330 Ok(outputs)
2331 }
2332
2333 fn __materialize_projected_slot(
2334 &self,
2335 session: &QuerySession<'_, S>,
2336 slot: &ProjectedQuerySlotValue,
2337 ) -> Result<Self::Output> {
2338 let ProjectedQuerySlotValue::Many(things) = slot else {
2339 return Err(Error::model_validation(
2340 ModelValidationPhase::Hydration,
2341 "wrong_result_shape",
2342 vec![],
2343 "provider returned a singular slot for a collection selection",
2344 None,
2345 ));
2346 };
2347 things
2348 .iter()
2349 .map(|thing| B::__materialize_projected_output(session, thing))
2350 .collect()
2351 }
2352
2353 fn __materialize_slot_with_checkpoint(
2354 &self,
2355 session: &QuerySession<'_, S>,
2356 slot: &SlotValue,
2357 checkpoint: &dyn Fn() -> Result<()>,
2358 ) -> Result<Self::Output> {
2359 let SlotValue::Many(things) = slot else {
2360 return Err(Error::model_validation(
2361 ModelValidationPhase::Hydration,
2362 "wrong_result_shape",
2363 vec![],
2364 "provider returned a singular slot for a collection selection",
2365 None,
2366 ));
2367 };
2368 checkpoint()?;
2369 let mut outputs = Vec::with_capacity(things.len());
2370 for thing in things {
2371 checkpoint()?;
2372 let row = session.client_row_for(thing)?;
2373 outputs.push(
2374 B::materialize_output(&row).map_err(|error| {
2375 map_validation_error(error, ModelValidationPhase::Hydration)
2376 })?,
2377 );
2378 }
2379 checkpoint()?;
2380 Ok(outputs)
2381 }
2382
2383 fn __materialize_projected_slot_with_checkpoint(
2384 &self,
2385 session: &QuerySession<'_, S>,
2386 slot: &ProjectedQuerySlotValue,
2387 checkpoint: &dyn Fn() -> Result<()>,
2388 ) -> Result<Self::Output> {
2389 let ProjectedQuerySlotValue::Many(things) = slot else {
2390 return Err(Error::model_validation(
2391 ModelValidationPhase::Hydration,
2392 "wrong_result_shape",
2393 vec![],
2394 "provider returned a singular slot for a collection selection",
2395 None,
2396 ));
2397 };
2398 checkpoint()?;
2399 let mut outputs = Vec::with_capacity(things.len());
2400 for thing in things {
2401 checkpoint()?;
2402 outputs.push(B::__materialize_projected_output(session, thing)?);
2403 }
2404 checkpoint()?;
2405 Ok(outputs)
2406 }
2407}
2408
2409mod selected_shape_sealed {
2410 pub trait Sealed<S> {}
2411}
2412
2413pub trait SelectedShape<S: Schema>: selected_shape_sealed::Sealed<S> + Clone {
2418 type Output;
2420
2421 #[doc(hidden)]
2422 fn __shape_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmShapeHandle>;
2423
2424 #[doc(hidden)]
2425 fn __materialize_row(
2426 &self,
2427 session: &QuerySession<'_, S>,
2428 row: &MatchRow,
2429 ) -> Result<Self::Output>;
2430
2431 #[doc(hidden)]
2432 fn __materialize_projected_row(
2433 &self,
2434 session: &QuerySession<'_, S>,
2435 row: &ProjectedQueryRow,
2436 ) -> Result<Self::Output>;
2437
2438 #[doc(hidden)]
2439 fn __materialize_row_with_checkpoint(
2440 &self,
2441 session: &QuerySession<'_, S>,
2442 row: &MatchRow,
2443 checkpoint: &dyn Fn() -> Result<()>,
2444 ) -> Result<Self::Output> {
2445 checkpoint()?;
2446 let output = self.__materialize_row(session, row)?;
2447 checkpoint()?;
2448 Ok(output)
2449 }
2450
2451 #[doc(hidden)]
2452 fn __materialize_projected_row_with_checkpoint(
2453 &self,
2454 session: &QuerySession<'_, S>,
2455 row: &ProjectedQueryRow,
2456 checkpoint: &dyn Fn() -> Result<()>,
2457 ) -> Result<Self::Output> {
2458 checkpoint()?;
2459 let output = self.__materialize_projected_row(session, row)?;
2460 checkpoint()?;
2461 Ok(output)
2462 }
2463}
2464
2465impl<S: Schema, B: Selectable<S>> selected_shape_sealed::Sealed<S> for B {}
2466impl<S: Schema, B: Selectable<S>> SelectedShape<S> for B {
2467 type Output = B::Output;
2468
2469 fn __shape_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmShapeHandle> {
2470 session
2471 .session
2472 .positional([SelectedSlot::__selection_handle(self, session)?])
2473 .map_err(Error::from_orm)
2474 }
2475
2476 fn __materialize_row(
2477 &self,
2478 session: &QuerySession<'_, S>,
2479 row: &MatchRow,
2480 ) -> Result<Self::Output> {
2481 let [slot] = row.slots() else {
2482 return Err(selected_shape_arity_error(1, row.slots().len()));
2483 };
2484 SelectedSlot::__materialize_slot(self, session, slot)
2485 }
2486
2487 fn __materialize_projected_row(
2488 &self,
2489 session: &QuerySession<'_, S>,
2490 row: &ProjectedQueryRow,
2491 ) -> Result<Self::Output> {
2492 let [slot] = row.slots() else {
2493 return Err(selected_shape_arity_error(1, row.slots().len()));
2494 };
2495 SelectedSlot::__materialize_projected_slot(self, session, slot.value())
2496 }
2497}
2498
2499impl<S: Schema, B: Selectable<S>> selected_shape_sealed::Sealed<S> for Collected<S, B> {}
2500impl<S: Schema, B: Selectable<S>> SelectedShape<S> for Collected<S, B> {
2501 type Output = Vec<B::Output>;
2502
2503 fn __shape_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmShapeHandle> {
2504 session
2505 .session
2506 .positional([SelectedSlot::__selection_handle(self, session)?])
2507 .map_err(Error::from_orm)
2508 }
2509
2510 fn __materialize_row(
2511 &self,
2512 session: &QuerySession<'_, S>,
2513 row: &MatchRow,
2514 ) -> Result<Self::Output> {
2515 let [slot] = row.slots() else {
2516 return Err(selected_shape_arity_error(1, row.slots().len()));
2517 };
2518 SelectedSlot::__materialize_slot(self, session, slot)
2519 }
2520
2521 fn __materialize_projected_row(
2522 &self,
2523 session: &QuerySession<'_, S>,
2524 row: &ProjectedQueryRow,
2525 ) -> Result<Self::Output> {
2526 let [slot] = row.slots() else {
2527 return Err(selected_shape_arity_error(1, row.slots().len()));
2528 };
2529 SelectedSlot::__materialize_projected_slot(self, session, slot.value())
2530 }
2531
2532 fn __materialize_row_with_checkpoint(
2533 &self,
2534 session: &QuerySession<'_, S>,
2535 row: &MatchRow,
2536 checkpoint: &dyn Fn() -> Result<()>,
2537 ) -> Result<Self::Output> {
2538 let [slot] = row.slots() else {
2539 return Err(selected_shape_arity_error(1, row.slots().len()));
2540 };
2541 SelectedSlot::__materialize_slot_with_checkpoint(self, session, slot, checkpoint)
2542 }
2543
2544 fn __materialize_projected_row_with_checkpoint(
2545 &self,
2546 session: &QuerySession<'_, S>,
2547 row: &ProjectedQueryRow,
2548 checkpoint: &dyn Fn() -> Result<()>,
2549 ) -> Result<Self::Output> {
2550 let [slot] = row.slots() else {
2551 return Err(selected_shape_arity_error(1, row.slots().len()));
2552 };
2553 SelectedSlot::__materialize_projected_slot_with_checkpoint(
2554 self,
2555 session,
2556 slot.value(),
2557 checkpoint,
2558 )
2559 }
2560}
2561
2562mod singular_selected_shape_sealed {
2563 pub trait Sealed<S> {}
2564}
2565
2566pub trait SingularSelectedShape<S: Schema>:
2568 SelectedShape<S> + singular_selected_shape_sealed::Sealed<S>
2569{
2570}
2571
2572impl<S: Schema, B: Selectable<S>> singular_selected_shape_sealed::Sealed<S> for B {}
2573impl<S: Schema, B: Selectable<S>> SingularSelectedShape<S> for B {}
2574
2575#[doc(hidden)]
2576pub trait SelectedTuple<S: Schema>: Clone {
2577 const ARITY: usize;
2578
2579 type Output;
2580
2581 fn __selection_handles(&self, session: &QuerySession<'_, S>)
2582 -> Result<Vec<OrmSelectionHandle>>;
2583
2584 fn __materialize_slots(
2585 &self,
2586 session: &QuerySession<'_, S>,
2587 slots: &[SlotValue],
2588 ) -> Result<Self::Output>;
2589
2590 #[doc(hidden)]
2591 fn __materialize_projected_slots(
2592 &self,
2593 session: &QuerySession<'_, S>,
2594 slots: &[type_bridge_orm::ProjectedQuerySlot],
2595 ) -> Result<Self::Output>;
2596
2597 #[doc(hidden)]
2598 fn __materialize_slots_with_checkpoint(
2599 &self,
2600 session: &QuerySession<'_, S>,
2601 slots: &[SlotValue],
2602 checkpoint: &dyn Fn() -> Result<()>,
2603 ) -> Result<Self::Output> {
2604 checkpoint()?;
2605 let output = self.__materialize_slots(session, slots)?;
2606 checkpoint()?;
2607 Ok(output)
2608 }
2609
2610 #[doc(hidden)]
2611 fn __materialize_projected_slots_with_checkpoint(
2612 &self,
2613 session: &QuerySession<'_, S>,
2614 slots: &[type_bridge_orm::ProjectedQuerySlot],
2615 checkpoint: &dyn Fn() -> Result<()>,
2616 ) -> Result<Self::Output> {
2617 checkpoint()?;
2618 let output = self.__materialize_projected_slots(session, slots)?;
2619 checkpoint()?;
2620 Ok(output)
2621 }
2622}
2623
2624#[doc(hidden)]
2625pub trait SingularSelectedTuple<S: Schema>: SelectedTuple<S> {}
2626
2627macro_rules! selected_tuple {
2628 ($length:literal; $(($type:ident, $index:tt)),+ $(,)?) => {
2629 impl<S: Schema, $($type: SelectedSlot<S>),+> SelectedTuple<S> for ($($type,)+) {
2630 const ARITY: usize = $length;
2631
2632 type Output = ($(<$type as SelectedSlot<S>>::Output,)+);
2633
2634 fn __selection_handles(
2635 &self,
2636 session: &QuerySession<'_, S>,
2637 ) -> Result<Vec<OrmSelectionHandle>> {
2638 Ok(vec![$(SelectedSlot::__selection_handle(&self.$index, session)?),+])
2639 }
2640
2641 fn __materialize_slots(
2642 &self,
2643 session: &QuerySession<'_, S>,
2644 slots: &[SlotValue],
2645 ) -> Result<Self::Output> {
2646 let slots: &[SlotValue; $length] = slots
2647 .try_into()
2648 .map_err(|_| selected_shape_arity_error($length, slots.len()))?;
2649 Ok(($(SelectedSlot::__materialize_slot(
2650 &self.$index,
2651 session,
2652 &slots[$index],
2653 )?,)+))
2654 }
2655
2656 fn __materialize_projected_slots(
2657 &self,
2658 session: &QuerySession<'_, S>,
2659 slots: &[type_bridge_orm::ProjectedQuerySlot],
2660 ) -> Result<Self::Output> {
2661 let slots: &[type_bridge_orm::ProjectedQuerySlot; $length] = slots
2662 .try_into()
2663 .map_err(|_| selected_shape_arity_error($length, slots.len()))?;
2664 Ok(($(SelectedSlot::__materialize_projected_slot(
2665 &self.$index,
2666 session,
2667 slots[$index].value(),
2668 )?,)+))
2669 }
2670
2671 fn __materialize_slots_with_checkpoint(
2672 &self,
2673 session: &QuerySession<'_, S>,
2674 slots: &[SlotValue],
2675 checkpoint: &dyn Fn() -> Result<()>,
2676 ) -> Result<Self::Output> {
2677 let slots: &[SlotValue; $length] = slots
2678 .try_into()
2679 .map_err(|_| selected_shape_arity_error($length, slots.len()))?;
2680 Ok(($(SelectedSlot::__materialize_slot_with_checkpoint(
2681 &self.$index,
2682 session,
2683 &slots[$index],
2684 checkpoint,
2685 )?,)+))
2686 }
2687
2688
2689 fn __materialize_projected_slots_with_checkpoint(
2690 &self,
2691 session: &QuerySession<'_, S>,
2692 slots: &[type_bridge_orm::ProjectedQuerySlot],
2693 checkpoint: &dyn Fn() -> Result<()>,
2694 ) -> Result<Self::Output> {
2695 let slots: &[type_bridge_orm::ProjectedQuerySlot; $length] = slots
2696 .try_into()
2697 .map_err(|_| selected_shape_arity_error($length, slots.len()))?;
2698 Ok(($(SelectedSlot::__materialize_projected_slot_with_checkpoint(
2699 &self.$index,
2700 session,
2701 slots[$index].value(),
2702 checkpoint,
2703 )?,)+))
2704 }
2705 }
2706
2707 impl<S: Schema, $($type: SelectedSlot<S>),+> selected_shape_sealed::Sealed<S>
2708 for ($($type,)+)
2709 {
2710 }
2711
2712 impl<S: Schema, $($type: SelectedSlot<S>),+> SelectedShape<S> for ($($type,)+) {
2713 type Output = <Self as SelectedTuple<S>>::Output;
2714
2715 fn __shape_handle(
2716 &self,
2717 session: &QuerySession<'_, S>,
2718 ) -> Result<OrmShapeHandle> {
2719 session
2720 .session
2721 .positional(self.__selection_handles(session)?)
2722 .map_err(Error::from_orm)
2723 }
2724
2725 fn __materialize_row(
2726 &self,
2727 session: &QuerySession<'_, S>,
2728 row: &MatchRow,
2729 ) -> Result<Self::Output> {
2730 self.__materialize_slots(session, row.slots())
2731 }
2732
2733 fn __materialize_projected_row(
2734 &self,
2735 session: &QuerySession<'_, S>,
2736 row: &ProjectedQueryRow,
2737 ) -> Result<Self::Output> {
2738 self.__materialize_projected_slots(session, row.slots())
2739 }
2740
2741
2742 fn __materialize_row_with_checkpoint(
2743 &self,
2744 session: &QuerySession<'_, S>,
2745 row: &MatchRow,
2746 checkpoint: &dyn Fn() -> Result<()>,
2747 ) -> Result<Self::Output> {
2748 self.__materialize_slots_with_checkpoint(session, row.slots(), checkpoint)
2749 }
2750
2751
2752 fn __materialize_projected_row_with_checkpoint(
2753 &self,
2754 session: &QuerySession<'_, S>,
2755 row: &ProjectedQueryRow,
2756 checkpoint: &dyn Fn() -> Result<()>,
2757 ) -> Result<Self::Output> {
2758 self.__materialize_projected_slots_with_checkpoint(
2759 session,
2760 row.slots(),
2761 checkpoint,
2762 )
2763 }
2764 }
2765
2766 impl<S: Schema, $($type: Selectable<S>),+> singular_selected_shape_sealed::Sealed<S>
2767 for ($($type,)+)
2768 {
2769 }
2770
2771 impl<S: Schema, $($type: Selectable<S>),+> SingularSelectedShape<S>
2772 for ($($type,)+)
2773 {
2774 }
2775
2776 impl<S: Schema, $($type: Selectable<S>),+> SingularSelectedTuple<S>
2777 for ($($type,)+)
2778 {
2779 }
2780 };
2781}
2782
2783selected_tuple!(1; (A, 0));
2784selected_tuple!(2; (A, 0), (B, 1));
2785selected_tuple!(3; (A, 0), (B, 1), (C, 2));
2786selected_tuple!(4; (A, 0), (B, 1), (C, 2), (D, 3));
2787selected_tuple!(5; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4));
2788selected_tuple!(6; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5));
2789selected_tuple!(7; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6));
2790selected_tuple!(8; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7));
2791selected_tuple!(9; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8));
2792selected_tuple!(10; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9));
2793selected_tuple!(11; (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8), (J, 9), (K, 10));
2794selected_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));
2795selected_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));
2796selected_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));
2797selected_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));
2798selected_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));
2799
2800#[doc(hidden)]
2802pub trait SelectedRowSpec<Outputs>: Sized {
2803 fn __from_selected_outputs(outputs: Outputs) -> Self;
2804}
2805
2806pub struct NamedSelection<S: Schema, Row, Slots> {
2808 slots: Slots,
2809 names: &'static [&'static str],
2810 marker: PhantomData<fn() -> (S, Row)>,
2811}
2812
2813impl<S: Schema, Row, Slots: Clone> Clone for NamedSelection<S, Row, Slots> {
2814 fn clone(&self) -> Self {
2815 Self {
2816 slots: self.slots.clone(),
2817 names: self.names,
2818 marker: PhantomData,
2819 }
2820 }
2821}
2822
2823impl<S: Schema, Row, Slots: SelectedTuple<S>> NamedSelection<S, Row, Slots> {
2824 #[doc(hidden)]
2825 pub fn __new(slots: Slots, names: &'static [&'static str]) -> Result<Self> {
2826 if names.len() != Slots::ARITY {
2827 return Err(Error::model_validation(
2828 ModelValidationPhase::Input,
2829 "invalid_selected_shape",
2830 vec![],
2831 format!(
2832 "named selected shape has {} names for {} slots",
2833 names.len(),
2834 Slots::ARITY
2835 ),
2836 None,
2837 ));
2838 }
2839 Ok(Self {
2840 slots,
2841 names,
2842 marker: PhantomData,
2843 })
2844 }
2845}
2846
2847impl<S: Schema, Row, Slots> selected_shape_sealed::Sealed<S> for NamedSelection<S, Row, Slots> {}
2848
2849impl<S, Row, Slots> SelectedShape<S> for NamedSelection<S, Row, Slots>
2850where
2851 S: Schema,
2852 Slots: SelectedTuple<S>,
2853 Row: SelectedRowSpec<Slots::Output>,
2854{
2855 type Output = Row;
2856
2857 fn __shape_handle(&self, session: &QuerySession<'_, S>) -> Result<OrmShapeHandle> {
2858 let selections = self.slots.__selection_handles(session)?;
2859 session
2860 .session
2861 .named(
2862 self.names
2863 .iter()
2864 .copied()
2865 .zip(selections)
2866 .map(|(name, selection)| (name.to_owned(), selection)),
2867 )
2868 .map_err(Error::from_orm)
2869 }
2870
2871 fn __materialize_row(
2872 &self,
2873 session: &QuerySession<'_, S>,
2874 row: &MatchRow,
2875 ) -> Result<Self::Output> {
2876 Ok(Row::__from_selected_outputs(
2877 self.slots.__materialize_slots(session, row.slots())?,
2878 ))
2879 }
2880
2881 fn __materialize_projected_row(
2882 &self,
2883 session: &QuerySession<'_, S>,
2884 row: &ProjectedQueryRow,
2885 ) -> Result<Self::Output> {
2886 Ok(Row::__from_selected_outputs(
2887 self.slots
2888 .__materialize_projected_slots(session, row.slots())?,
2889 ))
2890 }
2891
2892 fn __materialize_row_with_checkpoint(
2893 &self,
2894 session: &QuerySession<'_, S>,
2895 row: &MatchRow,
2896 checkpoint: &dyn Fn() -> Result<()>,
2897 ) -> Result<Self::Output> {
2898 Ok(Row::__from_selected_outputs(
2899 self.slots
2900 .__materialize_slots_with_checkpoint(session, row.slots(), checkpoint)?,
2901 ))
2902 }
2903
2904 fn __materialize_projected_row_with_checkpoint(
2905 &self,
2906 session: &QuerySession<'_, S>,
2907 row: &ProjectedQueryRow,
2908 checkpoint: &dyn Fn() -> Result<()>,
2909 ) -> Result<Self::Output> {
2910 Ok(Row::__from_selected_outputs(
2911 self.slots.__materialize_projected_slots_with_checkpoint(
2912 session,
2913 row.slots(),
2914 checkpoint,
2915 )?,
2916 ))
2917 }
2918}
2919
2920impl<S, Row, Slots> singular_selected_shape_sealed::Sealed<S> for NamedSelection<S, Row, Slots>
2921where
2922 S: Schema,
2923 Slots: SingularSelectedTuple<S>,
2924 Row: SelectedRowSpec<Slots::Output>,
2925{
2926}
2927
2928impl<S, Row, Slots> SingularSelectedShape<S> for NamedSelection<S, Row, Slots>
2929where
2930 S: Schema,
2931 Slots: SingularSelectedTuple<S>,
2932 Row: SelectedRowSpec<Slots::Output>,
2933{
2934}
2935
2936fn selected_shape_arity_error(expected: usize, actual: usize) -> Error {
2937 Error::model_validation(
2938 ModelValidationPhase::Hydration,
2939 "wrong_result_shape",
2940 vec![],
2941 format!("selected row has {actual} slots; expected {expected}"),
2942 None,
2943 )
2944}
2945
2946#[derive(Debug)]
2948pub struct RowsOptions<S: Schema> {
2949 limit: u64,
2950 offset: u64,
2951 order: Vec<Order<S>>,
2952}
2953
2954impl<S: Schema> Clone for RowsOptions<S> {
2955 fn clone(&self) -> Self {
2956 Self {
2957 limit: self.limit,
2958 offset: self.offset,
2959 order: self.order.clone(),
2960 }
2961 }
2962}
2963
2964impl<S: Schema> RowsOptions<S> {
2965 #[must_use]
2967 pub fn new(limit: u64) -> Self {
2968 Self {
2969 limit,
2970 offset: 0,
2971 order: Vec::new(),
2972 }
2973 }
2974
2975 #[must_use]
2977 pub fn offset(mut self, offset: u64) -> Self {
2978 self.offset = offset;
2979 self
2980 }
2981
2982 #[must_use]
2984 pub fn order_by(mut self, order: Order<S>) -> Self {
2985 self.order.push(order);
2986 self
2987 }
2988}
2989
2990#[derive(Debug)]
2992pub struct PageOptions<S: Schema> {
2993 limit: u64,
2994 offset: u64,
2995 include_total: bool,
2996 order: Vec<Order<S>>,
2997}
2998
2999impl<S: Schema> Clone for PageOptions<S> {
3000 fn clone(&self) -> Self {
3001 Self {
3002 limit: self.limit,
3003 offset: self.offset,
3004 include_total: self.include_total,
3005 order: self.order.clone(),
3006 }
3007 }
3008}
3009
3010impl<S: Schema> PageOptions<S> {
3011 #[must_use]
3013 pub fn new(limit: u64) -> Self {
3014 Self {
3015 limit,
3016 offset: 0,
3017 include_total: false,
3018 order: Vec::new(),
3019 }
3020 }
3021
3022 #[must_use]
3024 pub fn offset(mut self, offset: u64) -> Self {
3025 self.offset = offset;
3026 self
3027 }
3028
3029 #[must_use]
3031 pub fn include_total(mut self, include_total: bool) -> Self {
3032 self.include_total = include_total;
3033 self
3034 }
3035
3036 #[must_use]
3038 pub fn order_by(mut self, order: Order<S>) -> Self {
3039 self.order.push(order);
3040 self
3041 }
3042}
3043
3044#[derive(Clone, Debug)]
3046pub struct Page<T> {
3047 items: Vec<T>,
3048 offset: u64,
3049 limit: u64,
3050 total: Option<u64>,
3051}
3052
3053impl<T> Page<T> {
3054 #[must_use]
3056 pub fn items(&self) -> &[T] {
3057 &self.items
3058 }
3059
3060 #[must_use]
3062 pub const fn offset(&self) -> u64 {
3063 self.offset
3064 }
3065
3066 #[must_use]
3068 pub const fn limit(&self) -> u64 {
3069 self.limit
3070 }
3071
3072 #[must_use]
3074 pub const fn total(&self) -> Option<u64> {
3075 self.total
3076 }
3077
3078 #[must_use]
3080 pub fn into_items(self) -> Vec<T> {
3081 self.items
3082 }
3083}
3084
3085pub struct Query<'s, 'db, S: Schema, Shape: SelectedShape<S>> {
3090 session: &'s QuerySession<'db, S>,
3091 selection: Shape,
3092 hidden: Vec<BindingKey>,
3093 predicates: Vec<Predicate<S>>,
3094 allowed_cross_joins: Vec<(BindingKey, BindingKey)>,
3095 closed: AtomicBool,
3096}
3097
3098impl<'s, 'db, S: Schema, Shape: SelectedShape<S>> Clone for Query<'s, 'db, S, Shape> {
3099 fn clone(&self) -> Self {
3100 Self {
3101 session: self.session,
3102 selection: self.selection.clone(),
3103 hidden: self.hidden.clone(),
3104 predicates: self.predicates.clone(),
3105 allowed_cross_joins: self.allowed_cross_joins.clone(),
3106 closed: AtomicBool::new(self.closed.load(Ordering::Acquire)),
3107 }
3108 }
3109}
3110
3111impl<'db, S: Schema> QuerySession<'db, S> {
3112 pub fn query<Shape: SelectedShape<S>>(
3114 &self,
3115 selection: Shape,
3116 ) -> Result<Query<'_, 'db, S, Shape>> {
3117 selection.__shape_handle(self)?;
3118 Ok(Query {
3119 session: self,
3120 selection,
3121 hidden: Vec::new(),
3122 predicates: Vec::new(),
3123 allowed_cross_joins: Vec::new(),
3124 closed: AtomicBool::new(false),
3125 })
3126 }
3127}
3128
3129impl<'s, 'db, S: Schema, Shape: SelectedShape<S>> Query<'s, 'db, S, Shape> {
3130 pub fn close(&self) {
3136 self.closed.store(true, Ordering::Release);
3137 }
3138
3139 #[must_use]
3141 pub fn is_closed(&self) -> bool {
3142 self.closed.load(Ordering::Acquire)
3143 }
3144
3145 fn ensure_open(&self, phase: ModelValidationPhase) -> Result<()> {
3146 self.session.ensure_open(phase)?;
3147 if self.closed.load(Ordering::Acquire) {
3148 Err(Error::from_sdk_execution(
3149 type_bridge_orm::query_resource_closed_diagnostic(),
3150 phase,
3151 ))
3152 } else {
3153 Ok(())
3154 }
3155 }
3156
3157 pub fn match_<M: ThingModel<Schema = S>, Mode: SelectionMode>(
3159 &self,
3160 binding: Binding<S, M, Mode>,
3161 ) -> Result<Self> {
3162 self.ensure_open(ModelValidationPhase::Input)?;
3163 self.session.handle_by_key(binding.key())?;
3164 let mut next = self.clone();
3165 if !next.hidden.contains(&binding.key()) {
3166 next.hidden.push(binding.key());
3167 }
3168 Ok(next)
3169 }
3170
3171 pub fn where_(&self, predicate: Predicate<S>) -> Result<Self> {
3173 self.ensure_open(ModelValidationPhase::Input)?;
3174 let mut next = self.clone();
3175 next.predicates.push(predicate);
3176 Ok(next)
3177 }
3178
3179 pub fn where_all(&self, predicates: impl IntoIterator<Item = Predicate<S>>) -> Result<Self> {
3181 self.ensure_open(ModelValidationPhase::Input)?;
3182 let mut next = self.clone();
3183 next.predicates.extend(predicates);
3184 Ok(next)
3185 }
3186
3187 pub fn allow_cross_join<L: Selectable<S>, R: Selectable<S>>(
3190 &self,
3191 left: L,
3192 right: R,
3193 ) -> Result<Self> {
3194 self.ensure_open(ModelValidationPhase::Input)?;
3195 let left = left.binding_key();
3196 let right = right.binding_key();
3197 self.session.handle_by_key(left)?;
3198 self.session.handle_by_key(right)?;
3199 if left == right {
3200 return Err(Error::model_validation(
3201 ModelValidationPhase::Input,
3202 "self_cross_join",
3203 vec![],
3204 "cross-join permission requires two distinct generated bindings",
3205 None,
3206 ));
3207 }
3208 let pair = if left.index < right.index {
3209 (left, right)
3210 } else {
3211 (right, left)
3212 };
3213 let mut next = self.clone();
3214 if !next.allowed_cross_joins.contains(&pair) {
3215 next.allowed_cross_joins.push(pair);
3216 }
3217 Ok(next)
3218 }
3219
3220 fn lineage(&self) -> Result<OrmQueryHandle> {
3221 self.lineage_with_hidden(&[])
3222 }
3223
3224 fn lineage_with_hidden(&self, hidden: &[BindingKey]) -> Result<OrmQueryHandle> {
3225 self.ensure_open(ModelValidationPhase::Input)?;
3226 let shape = self.selection.__shape_handle(self.session)?;
3227 let mut query = self.session.session.query(shape).map_err(Error::from_orm)?;
3228 let mut hidden_keys = self.hidden.clone();
3229 for key in hidden {
3230 if !hidden_keys.contains(key) {
3231 hidden_keys.push(*key);
3232 }
3233 }
3234 for key in hidden_keys {
3235 let hidden = self.session.handle_by_key(key)?;
3236 query = query.add_hidden(hidden.clone()).map_err(Error::from_orm)?;
3237 }
3238 for (left, right) in &self.allowed_cross_joins {
3239 let left = self.session.handle_by_key(*left)?;
3240 let right = self.session.handle_by_key(*right)?;
3241 query = query
3242 .allow_cross_join(left, right)
3243 .map_err(Error::from_orm)?;
3244 }
3245 for predicate in &self.predicates {
3246 let lowered = self.session.lower_predicate(&predicate.expr)?;
3247 query = query.where_predicate(lowered).map_err(Error::from_orm)?;
3248 }
3249 Ok(query)
3250 }
3251
3252 pub(crate) fn validated_rows(
3253 &self,
3254 order: &[Order<S>],
3255 window: Window,
3256 ) -> Result<ValidatedMatchRequest> {
3257 let lineage = self.lineage()?;
3258 let mut lowered_orders = Vec::with_capacity(order.len());
3259 for term in order {
3260 lowered_orders.push(self.session.lower_order(term)?);
3261 }
3262 lineage
3263 .validate_fetch_rows(&lowered_orders, window, RowCardinality::BoundedMany)
3264 .map_err(Error::from_orm)
3265 }
3266
3267 pub(crate) fn validated_one(&self) -> Result<ValidatedMatchRequest> {
3268 self.lineage()?
3269 .validate_fetch_rows(
3270 &[],
3271 Window {
3272 offset: 0,
3273 limit: 1,
3274 },
3275 RowCardinality::ExactlyOne,
3276 )
3277 .map_err(Error::from_orm)
3278 }
3279
3280 pub(crate) fn validated_page<R: Selectable<S>>(
3281 &self,
3282 root: R,
3283 order: &[Order<S>],
3284 window: Window,
3285 include_total: bool,
3286 ) -> Result<ValidatedMatchRequest> {
3287 let root = self.session.handle_by_key(root.binding_key())?;
3288 let lineage = self.lineage()?;
3289 let mut lowered_orders = Vec::with_capacity(order.len());
3290 for term in order {
3291 lowered_orders.push(self.session.lower_order(term)?);
3292 }
3293 lineage
3294 .validate_page_by(root, &lowered_orders, window, include_total)
3295 .map_err(Error::from_orm)
3296 }
3297
3298 pub(crate) fn validated_count_by<R: Selectable<S>>(
3299 &self,
3300 root: R,
3301 ) -> Result<ValidatedMatchRequest> {
3302 let root = self.session.handle_by_key(root.binding_key())?;
3303 self.lineage()?
3304 .validate_count_by(root)
3305 .map_err(Error::from_orm)
3306 }
3307
3308 pub(crate) fn validated_exists_by<R: Selectable<S>>(
3309 &self,
3310 root: R,
3311 ) -> Result<ValidatedMatchRequest> {
3312 let root = self.session.handle_by_key(root.binding_key())?;
3313 self.lineage()?
3314 .validate_exists_by(root)
3315 .map_err(Error::from_orm)
3316 }
3317
3318 fn materialize_rows(
3319 &self,
3320 rows: &[MatchRow],
3321 deadline: QueryExecutionDeadline,
3322 ) -> Result<Vec<Shape::Output>> {
3323 let checkpoint = || {
3324 self.session
3325 .check_invocation(deadline, ModelValidationPhase::Hydration)
3326 };
3327 checkpoint()?;
3328 let mut outputs = Vec::with_capacity(rows.len());
3329 for row in rows {
3330 checkpoint()?;
3331 outputs.push(self.selection.__materialize_row_with_checkpoint(
3332 self.session,
3333 row,
3334 &checkpoint,
3335 )?);
3336 }
3337 checkpoint()?;
3338 Ok(outputs)
3339 }
3340
3341 fn materialize_projected_rows(
3342 &self,
3343 rows: &[ProjectedQueryRow],
3344 deadline: QueryExecutionDeadline,
3345 ) -> Result<Vec<Shape::Output>> {
3346 let checkpoint = || {
3347 self.session
3348 .check_invocation(deadline, ModelValidationPhase::Hydration)
3349 };
3350 checkpoint()?;
3351 let mut outputs = Vec::with_capacity(rows.len());
3352 for row in rows {
3353 checkpoint()?;
3354 outputs.push(self.selection.__materialize_projected_row_with_checkpoint(
3355 self.session,
3356 row,
3357 &checkpoint,
3358 )?);
3359 }
3360 checkpoint()?;
3361 Ok(outputs)
3362 }
3363
3364 pub(crate) fn outputs_from_rows(
3365 &self,
3366 validated: &ValidatedMatchRequest,
3367 result: &ValidatedMatchResult,
3368 deadline: QueryExecutionDeadline,
3369 ) -> Result<Vec<Shape::Output>> {
3370 self.session
3371 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3372 if self.session.uses_successor_projected_materialization() {
3373 let projected = self
3374 .session
3375 .materialize_projected_query_value(validated, result, deadline)?;
3376 let ProjectedQueryValue::Rows { rows } = projected else {
3377 return Err(Error::model_validation(
3378 ModelValidationPhase::Hydration,
3379 "wrong_result_shape",
3380 vec![],
3381 "provider returned a non-row result for a row fetch",
3382 None,
3383 ));
3384 };
3385 return self.materialize_projected_rows(&rows, deadline);
3386 }
3387 let rows = match result
3388 .for_request(validated)
3389 .map_err(|error| Error::from_orm_hydration(error.into()))?
3390 {
3391 MatchResult::Rows { rows } => rows,
3392 _ => {
3393 return Err(Error::model_validation(
3394 ModelValidationPhase::Hydration,
3395 "wrong_result_shape",
3396 vec![],
3397 "provider returned a non-row result for a row fetch",
3398 None,
3399 ));
3400 }
3401 };
3402 self.materialize_rows(rows, deadline)
3403 }
3404
3405 pub(crate) fn output_page(
3406 &self,
3407 validated: &ValidatedMatchRequest,
3408 result: &ValidatedMatchResult,
3409 deadline: QueryExecutionDeadline,
3410 ) -> Result<Page<Shape::Output>> {
3411 self.session
3412 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3413 if self.session.uses_successor_projected_materialization() {
3414 let projected = self
3415 .session
3416 .materialize_projected_query_value(validated, result, deadline)?;
3417 let ProjectedQueryValue::Page {
3418 entries,
3419 window,
3420 total,
3421 ..
3422 } = projected
3423 else {
3424 return Err(Error::model_validation(
3425 ModelValidationPhase::Hydration,
3426 "wrong_result_shape",
3427 vec![],
3428 "provider returned a non-page result for a page fetch",
3429 None,
3430 ));
3431 };
3432 return Ok(Page {
3433 items: self.materialize_projected_rows(&entries, deadline)?,
3434 offset: window.offset,
3435 limit: window.limit,
3436 total,
3437 });
3438 }
3439 let (entries, window, total) = match result
3440 .for_request(validated)
3441 .map_err(|error| Error::from_orm_hydration(error.into()))?
3442 {
3443 MatchResult::Page {
3444 entries,
3445 window,
3446 total,
3447 ..
3448 } => (entries, *window, *total),
3449 _ => {
3450 return Err(Error::model_validation(
3451 ModelValidationPhase::Hydration,
3452 "wrong_result_shape",
3453 vec![],
3454 "provider returned a non-page result for a page fetch",
3455 None,
3456 ));
3457 }
3458 };
3459 Ok(Page {
3460 items: self.materialize_rows(entries, deadline)?,
3461 offset: window.offset,
3462 limit: window.limit,
3463 total,
3464 })
3465 }
3466
3467 async fn execute(
3468 &self,
3469 validated: ValidatedMatchRequest,
3470 deadline: QueryExecutionDeadline,
3471 ) -> Result<(ValidatedMatchRequest, ValidatedMatchResult)> {
3472 self.ensure_open(ModelValidationPhase::Input)?;
3473 self.session
3474 .check_invocation(deadline, ModelValidationPhase::Input)?;
3475 let result = match &self.session.execution {
3476 QueryExecution::Borrowed(transaction) => {
3477 transaction
3478 .execute_match_with_limits(
3479 &self.session.registry,
3480 &validated,
3481 self.session
3482 .resources
3483 .direct_with_deadline(self.session.cancellation.clone(), deadline),
3484 )
3485 .await
3486 }
3487 QueryExecution::Local(database) => {
3488 database
3489 .inner_orm()
3490 .execute_match_with_limits(
3491 &self.session.registry,
3492 &validated,
3493 self.session
3494 .resources
3495 .direct_with_deadline(self.session.cancellation.clone(), deadline),
3496 )
3497 .await
3498 }
3499 QueryExecution::Remote(remote) => {
3500 let result = remote
3501 .execute_match(
3502 &self.session.registry,
3503 validated,
3504 self.session.resources,
3505 self.session.cancellation.clone(),
3506 deadline,
3507 )
3508 .await?;
3509 self.ensure_open(ModelValidationPhase::Hydration)?;
3510 return Ok(result);
3511 }
3512 };
3513 self.session
3514 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3515 self.ensure_open(ModelValidationPhase::Hydration)?;
3516 Ok((validated, result.map_err(Error::from_orm_hydration)?))
3517 }
3518}
3519
3520impl<'s, 'db, S, Shape> Query<'s, 'db, S, Shape>
3521where
3522 S: Schema,
3523 Shape: SingularSelectedShape<S>,
3524{
3525 pub async fn one(&self) -> Result<Shape::Output> {
3528 let deadline = self.session.begin_invocation()?;
3529 let validated = self.validated_one()?;
3530 let (validated, result) = self.execute(validated, deadline).await?;
3531 let mut outputs = self.outputs_from_rows(&validated, &result, deadline)?;
3532 match outputs.len() {
3533 0 => Err(Error::model_validation(
3534 ModelValidationPhase::Hydration,
3535 "no_result",
3536 vec![],
3537 "query selected no distinct identity",
3538 None,
3539 )),
3540 1 => Ok(outputs.remove(0)),
3541 _ => Err(Error::model_validation(
3542 ModelValidationPhase::Hydration,
3543 "not_unique",
3544 vec![],
3545 "query selected more than one distinct identity",
3546 None,
3547 )),
3548 }
3549 }
3550
3551 pub async fn rows(&self, options: RowsOptions<S>) -> Result<Vec<Shape::Output>> {
3554 let deadline = self.session.begin_invocation()?;
3555 if options.limit == 0 {
3556 return Err(Error::model_validation(
3557 ModelValidationPhase::Input,
3558 "zero_limit",
3559 vec![],
3560 "row fetches require a nonzero limit",
3561 None,
3562 ));
3563 }
3564 let validated = self.validated_rows(
3565 &options.order,
3566 Window {
3567 offset: options.offset,
3568 limit: options.limit,
3569 },
3570 )?;
3571 let (validated, result) = self.execute(validated, deadline).await?;
3572 self.outputs_from_rows(&validated, &result, deadline)
3573 }
3574
3575 pub async fn first(&self, order: Order<S>) -> Result<Option<Shape::Output>> {
3577 let deadline = self.session.begin_invocation()?;
3578 let validated = self.validated_rows(
3579 &[order],
3580 Window {
3581 offset: 0,
3582 limit: 1,
3583 },
3584 )?;
3585 let (validated, result) = self.execute(validated, deadline).await?;
3586 Ok(self.outputs_from_rows(&validated, &result, deadline)?.pop())
3587 }
3588}
3589
3590impl<'s, 'db, S: Schema, Shape: SelectedShape<S>> Query<'s, 'db, S, Shape> {
3591 pub async fn page_by<R: Selectable<S>>(
3593 &self,
3594 root: R,
3595 options: PageOptions<S>,
3596 ) -> Result<Page<Shape::Output>> {
3597 let deadline = self.session.begin_invocation()?;
3598 if options.limit == 0 {
3599 return Err(Error::model_validation(
3600 ModelValidationPhase::Input,
3601 "zero_limit",
3602 vec![],
3603 "page fetches require a nonzero limit",
3604 None,
3605 ));
3606 }
3607 let validated = self.validated_page(
3608 root,
3609 &options.order,
3610 Window {
3611 offset: options.offset,
3612 limit: options.limit,
3613 },
3614 options.include_total,
3615 )?;
3616 let (validated, result) = self.execute(validated, deadline).await?;
3617 self.output_page(&validated, &result, deadline)
3618 }
3619
3620 pub async fn count_by<R: Selectable<S>>(&self, root: R) -> Result<u64> {
3622 let deadline = self.session.begin_invocation()?;
3623 let validated = self.validated_count_by(root)?;
3624 let (validated, result) = self.execute(validated, deadline).await?;
3625 self.session
3626 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3627 match result
3628 .for_request(&validated)
3629 .map_err(|error| Error::from_orm_hydration(error.into()))?
3630 {
3631 MatchResult::Count { value, .. } => Ok(*value),
3632 _ => Err(Error::model_validation(
3633 ModelValidationPhase::Hydration,
3634 "wrong_result_shape",
3635 vec![],
3636 "provider returned a non-count result for a count",
3637 None,
3638 )),
3639 }
3640 }
3641
3642 pub async fn exists_by<R: Selectable<S>>(&self, root: R) -> Result<bool> {
3645 let deadline = self.session.begin_invocation()?;
3646 let validated = self.validated_exists_by(root)?;
3647 let (validated, result) = self.execute(validated, deadline).await?;
3648 self.session
3649 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3650 match result
3651 .for_request(&validated)
3652 .map_err(|error| Error::from_orm_hydration(error.into()))?
3653 {
3654 MatchResult::Exists { value, .. } => Ok(*value),
3655 _ => Err(Error::model_validation(
3656 ModelValidationPhase::Hydration,
3657 "wrong_result_shape",
3658 vec![],
3659 "provider returned a non-existence result for an existence test",
3660 None,
3661 )),
3662 }
3663 }
3664}
3665
3666impl<'s, 'db, S: Schema, B: Selectable<S>> Query<'s, 'db, S, B> {
3667 pub async fn count(&self) -> Result<u64> {
3669 self.count_by(self.selection).await
3670 }
3671
3672 pub async fn exists(&self) -> Result<bool> {
3674 self.exists_by(self.selection).await
3675 }
3676
3677 fn lowered_reduce_terms(
3678 &self,
3679 terms: &[(
3680 type_bridge_orm::match_request::Reduction,
3681 Option<(BindingKey, &'static str)>,
3682 )],
3683 ) -> Result<Vec<OrmFieldHandle>> {
3684 let mut lowered = Vec::new();
3685 for (_, input) in terms {
3686 if let Some((key, owns_id_json)) = input {
3687 lowered.push(self.session.lower_field(*key, owns_id_json)?);
3688 }
3689 }
3690 Ok(lowered)
3691 }
3692
3693 pub(crate) fn validated_reduce(
3694 &self,
3695 group: Option<BindingKey>,
3696 terms: &[(
3697 type_bridge_orm::match_request::Reduction,
3698 Option<(BindingKey, &'static str)>,
3699 )],
3700 ) -> Result<ValidatedMatchRequest> {
3701 let root = self.session.handle_by_key(self.selection.binding_key())?;
3702 let hidden_groups = group
3703 .filter(|key| *key != self.selection.binding_key())
3704 .into_iter()
3705 .collect::<Vec<_>>();
3706 let lineage = self.lineage_with_hidden(&hidden_groups)?;
3707 let group_handle = group
3708 .map(|key| self.session.handle_by_key(key))
3709 .transpose()?;
3710 let lowered_inputs = self.lowered_reduce_terms(terms)?;
3711 let mut inputs = lowered_inputs.iter();
3712 let mut pairs = Vec::with_capacity(terms.len());
3713 for (reduction, input) in terms {
3714 let handle = if input.is_some() {
3715 Some(inputs.next().expect("one lowered handle per input"))
3716 } else {
3717 None
3718 };
3719 pairs.push((*reduction, handle));
3720 }
3721 lineage
3722 .validate_reduce_by(root, group_handle, &pairs)
3723 .map_err(Error::from_orm)
3724 }
3725
3726 fn validated_reduce_by_field<Owner: Model<Schema = S>, V>(
3727 &self,
3728 group: BoundField<S, Owner, V>,
3729 terms: &[(
3730 type_bridge_orm::match_request::Reduction,
3731 Option<(BindingKey, &'static str)>,
3732 )],
3733 ) -> Result<ValidatedMatchRequest> {
3734 let root = self.session.handle_by_key(self.selection.binding_key())?;
3735 let hidden_groups = (group.key != self.selection.binding_key())
3736 .then_some(group.key)
3737 .into_iter()
3738 .collect::<Vec<_>>();
3739 let lineage = self.lineage_with_hidden(&hidden_groups)?;
3740 let group = self.session.lower_field(group.key, group.owns_id_json)?;
3741 let lowered_inputs = self.lowered_reduce_terms(terms)?;
3742 let mut inputs = lowered_inputs.iter();
3743 let mut pairs = Vec::with_capacity(terms.len());
3744 for (reduction, input) in terms {
3745 let handle = if input.is_some() {
3746 Some(inputs.next().expect("one lowered handle per input"))
3747 } else {
3748 None
3749 };
3750 pairs.push((*reduction, handle));
3751 }
3752 lineage
3753 .validate_reduce_by_field(root, &group, &pairs)
3754 .map_err(Error::from_orm)
3755 }
3756
3757 fn validated_reduce_by_fields(
3758 &self,
3759 groups: &[(BindingKey, &'static str)],
3760 terms: &[(
3761 type_bridge_orm::match_request::Reduction,
3762 Option<(BindingKey, &'static str)>,
3763 )],
3764 ) -> Result<ValidatedMatchRequest> {
3765 let root = self.session.handle_by_key(self.selection.binding_key())?;
3766 let mut hidden_keys = Vec::new();
3767 for (key, _) in groups {
3768 if *key != self.selection.binding_key() && !hidden_keys.contains(key) {
3769 hidden_keys.push(*key);
3770 }
3771 }
3772 let lineage = self.lineage_with_hidden(&hidden_keys)?;
3773 let lowered_groups = groups
3774 .iter()
3775 .map(|(key, owns_id_json)| self.session.lower_field(*key, owns_id_json))
3776 .collect::<Result<Vec<_>>>()?;
3777 let group_refs = lowered_groups.iter().collect::<Vec<_>>();
3778 let lowered_inputs = self.lowered_reduce_terms(terms)?;
3779 let mut inputs = lowered_inputs.iter();
3780 let mut pairs = Vec::with_capacity(terms.len());
3781 for (reduction, input) in terms {
3782 let handle = if input.is_some() {
3783 Some(inputs.next().expect("one lowered handle per input"))
3784 } else {
3785 None
3786 };
3787 pairs.push((*reduction, handle));
3788 }
3789 lineage
3790 .validate_reduce_by_fields(root, &group_refs, &pairs)
3791 .map_err(Error::from_orm)
3792 }
3793
3794 fn decoded_reduction_rows<'result>(
3795 &self,
3796 validated: &ValidatedMatchRequest,
3797 result: &'result ValidatedMatchResult,
3798 deadline: QueryExecutionDeadline,
3799 ) -> Result<&'result [type_bridge_orm::match_request::ReductionRow]> {
3800 self.session
3801 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3802 match result
3803 .for_request(validated)
3804 .map_err(|error| Error::from_orm_hydration(error.into()))?
3805 {
3806 MatchResult::Reduction { rows, .. }
3807 | MatchResult::FieldReduction { rows, .. }
3808 | MatchResult::FieldTupleReduction { rows, .. } => Ok(rows),
3809 _ => Err(Error::model_validation(
3810 ModelValidationPhase::Hydration,
3811 "wrong_result_shape",
3812 vec![],
3813 "provider returned a non-reduction result for an aggregate",
3814 None,
3815 )),
3816 }
3817 }
3818
3819 pub async fn aggregate<T: crate::aggregate::AggregateTuple<S>>(
3822 &self,
3823 terms: T,
3824 ) -> Result<T::Output> {
3825 let deadline = self.session.begin_invocation()?;
3826 let term_list = terms.terms();
3827 let validated = self.validated_reduce(None, &term_list)?;
3828 let (validated, result) = self.execute(validated, deadline).await?;
3829 let rows = self.decoded_reduction_rows(&validated, &result, deadline)?;
3830 let [row] = rows else {
3831 return Err(Error::model_validation(
3832 ModelValidationPhase::Hydration,
3833 "wrong_result_shape",
3834 vec![],
3835 "ungrouped aggregates require exactly one reduction row",
3836 None,
3837 ));
3838 };
3839 self.session
3840 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3841 let output = T::decode(row.values())?;
3842 self.session
3843 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3844 Ok(output)
3845 }
3846
3847 pub fn group_by<G: Selectable<S>>(&self, group: G) -> Result<GroupedQuery<'s, 'db, S, B, G>> {
3850 self.ensure_open(ModelValidationPhase::Input)?;
3851 self.session.handle_by_key(group.binding_key())?;
3852 Ok(GroupedQuery {
3853 query: self.clone(),
3854 group,
3855 })
3856 }
3857
3858 pub fn group_by_field<Owner, V>(
3861 &self,
3862 group: BoundField<S, Owner, V>,
3863 ) -> Result<FieldGroupedQuery<'s, 'db, S, B, Owner, V>>
3864 where
3865 Owner: Model<Schema = S>,
3866 V: GroupedQueryValue,
3867 {
3868 self.ensure_open(ModelValidationPhase::Input)?;
3869 self.session.lower_field(group.key, group.owns_id_json)?;
3870 Ok(FieldGroupedQuery {
3871 query: self.clone(),
3872 group,
3873 })
3874 }
3875
3876 pub fn group_by_fields<G>(&self, groups: G) -> Result<FieldTupleGroupedQuery<'s, 'db, S, B, G>>
3879 where
3880 G: FieldGroupTuple<S>,
3881 {
3882 self.ensure_open(ModelValidationPhase::Input)?;
3883 for (key, owns_id_json) in groups.fields() {
3884 self.session.lower_field(key, owns_id_json)?;
3885 }
3886 Ok(FieldTupleGroupedQuery {
3887 query: self.clone(),
3888 groups,
3889 })
3890 }
3891}
3892
3893pub struct GroupedQuery<'s, 'db, S: Schema, B: Selectable<S>, G: Selectable<S>> {
3895 query: Query<'s, 'db, S, B>,
3896 group: G,
3897}
3898
3899impl<'s, 'db, S: Schema, B: Selectable<S>, G: Selectable<S>> GroupedQuery<'s, 'db, S, B, G> {
3900 pub fn close(&self) {
3902 self.query.close();
3903 }
3904
3905 #[must_use]
3907 pub fn is_closed(&self) -> bool {
3908 self.query.is_closed()
3909 }
3910
3911 pub async fn aggregate<T: crate::aggregate::AggregateTuple<S>>(
3914 &self,
3915 terms: T,
3916 ) -> Result<Vec<(G::Output, T::Output)>> {
3917 let deadline = self.query.session.begin_invocation()?;
3918 let term_list = terms.terms();
3919 let validated = self
3920 .query
3921 .validated_reduce(Some(self.group.binding_key()), &term_list)?;
3922 let (validated, result) = self.query.execute(validated, deadline).await?;
3923 if self
3924 .query
3925 .session
3926 .uses_successor_projected_materialization()
3927 {
3928 let projected = self
3929 .query
3930 .session
3931 .materialize_projected_query_value(&validated, &result, deadline)?;
3932 let ProjectedQueryValue::Reduction { rows, .. } = projected else {
3933 return Err(Error::model_validation(
3934 ModelValidationPhase::Hydration,
3935 "wrong_result_shape",
3936 vec![],
3937 "provider returned a non-reduction result for an aggregate",
3938 None,
3939 ));
3940 };
3941 let mut outputs = Vec::with_capacity(rows.len());
3942 for row in rows {
3943 self.query
3944 .session
3945 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3946 let Some(ProjectedReductionGroup::Thing(thing)) = row.group() else {
3947 return Err(Error::model_validation(
3948 ModelValidationPhase::Hydration,
3949 "wrong_result_shape",
3950 vec![],
3951 "grouped aggregates require group evidence per row",
3952 None,
3953 ));
3954 };
3955 let key = G::__materialize_projected_output(self.query.session, thing)?;
3956 let values = decoded_projected_reduction_values(row.values());
3957 outputs.push((key, T::decode(&values)?));
3958 }
3959 self.query
3960 .session
3961 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3962 return Ok(outputs);
3963 }
3964 let rows = self
3965 .query
3966 .decoded_reduction_rows(&validated, &result, deadline)?;
3967 let mut outputs = Vec::with_capacity(rows.len());
3968 for row in rows {
3969 self.query
3970 .session
3971 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3972 let thing = row.group().ok_or_else(|| {
3973 Error::model_validation(
3974 ModelValidationPhase::Hydration,
3975 "wrong_result_shape",
3976 vec![],
3977 "grouped aggregates require group evidence per row",
3978 None,
3979 )
3980 })?;
3981 let client_row = self.query.session.client_row_for(thing)?;
3982 let key = G::materialize_output(&client_row).map_err(|error| {
3983 crate::entity_codec::map_validation_error(error, ModelValidationPhase::Hydration)
3984 })?;
3985 outputs.push((key, T::decode(row.values())?));
3986 }
3987 self.query
3988 .session
3989 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
3990 Ok(outputs)
3991 }
3992}
3993
3994pub struct FieldGroupedQuery<
3997 's,
3998 'db,
3999 S: Schema,
4000 B: Selectable<S>,
4001 Owner: Model<Schema = S>,
4002 V: GroupedQueryValue,
4003> {
4004 query: Query<'s, 'db, S, B>,
4005 group: BoundField<S, Owner, V>,
4006}
4007
4008impl<'s, 'db, S, B, Owner, V> FieldGroupedQuery<'s, 'db, S, B, Owner, V>
4009where
4010 S: Schema,
4011 B: Selectable<S>,
4012 Owner: Model<Schema = S>,
4013 V: GroupedQueryValue,
4014{
4015 pub fn close(&self) {
4017 self.query.close();
4018 }
4019
4020 #[must_use]
4022 pub fn is_closed(&self) -> bool {
4023 self.query.is_closed()
4024 }
4025
4026 pub async fn aggregate<T: crate::aggregate::AggregateTuple<S>>(
4029 &self,
4030 terms: T,
4031 ) -> Result<Vec<(V, T::Output)>> {
4032 let deadline = self.query.session.begin_invocation()?;
4033 let term_list = terms.terms();
4034 let validated = self
4035 .query
4036 .validated_reduce_by_field(self.group, &term_list)?;
4037 let (validated, result) = self.query.execute(validated, deadline).await?;
4038 let rows = self
4039 .query
4040 .decoded_reduction_rows(&validated, &result, deadline)?;
4041 let mut outputs = Vec::with_capacity(rows.len());
4042 for row in rows {
4043 self.query
4044 .session
4045 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
4046 let value = row.field_group().ok_or_else(|| {
4047 Error::model_validation(
4048 ModelValidationPhase::Hydration,
4049 "wrong_result_shape",
4050 vec![],
4051 "field-grouped aggregates require scalar group evidence per row",
4052 None,
4053 )
4054 })?;
4055 let key = V::from_group_scalar(encoded_group_scalar(value)?)
4056 .map_err(|error| map_validation_error(error, ModelValidationPhase::Hydration))?;
4057 outputs.push((key, T::decode(row.values())?));
4058 }
4059 self.query
4060 .session
4061 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
4062 Ok(outputs)
4063 }
4064}
4065
4066pub trait FieldGroupTuple<S: Schema>: field_group_tuple_sealed::Sealed<S> + Copy {
4069 type Output;
4071
4072 #[doc(hidden)]
4073 fn fields(&self) -> Vec<(BindingKey, &'static str)>;
4074
4075 #[doc(hidden)]
4076 fn decode(values: &[AttributeValue]) -> Result<Self::Output>;
4077}
4078
4079mod field_group_tuple_sealed {
4080 pub trait Sealed<S> {}
4081}
4082
4083macro_rules! field_group_tuple {
4084 ($(($owner:ident, $value:ident, $index:tt)),+) => {
4085 impl<S, $($owner, $value),+> field_group_tuple_sealed::Sealed<S>
4086 for ($(BoundField<S, $owner, $value>,)+)
4087 where
4088 S: Schema,
4089 $($owner: Model<Schema = S>, $value: GroupedQueryValue),+
4090 {
4091 }
4092
4093 impl<S, $($owner, $value),+> FieldGroupTuple<S>
4094 for ($(BoundField<S, $owner, $value>,)+)
4095 where
4096 S: Schema,
4097 $($owner: Model<Schema = S>, $value: GroupedQueryValue),+
4098 {
4099 type Output = ($($value,)+);
4100
4101 fn fields(&self) -> Vec<(BindingKey, &'static str)> {
4102 vec![$((self.$index.key, self.$index.owns_id_json)),+]
4103 }
4104
4105 fn decode(values: &[AttributeValue]) -> Result<Self::Output> {
4106 let expected = [$(stringify!($owner)),+].len();
4107 if values.len() != expected {
4108 return Err(Error::model_validation(
4109 ModelValidationPhase::Hydration,
4110 "wrong_result_shape",
4111 vec![],
4112 "tuple-field-grouped aggregate key has the wrong arity",
4113 None,
4114 ));
4115 }
4116 Ok(($(
4117 $value::from_group_scalar(encoded_group_scalar(&values[$index])?)
4118 .map_err(|error| {
4119 map_validation_error(error, ModelValidationPhase::Hydration)
4120 })?,
4121 )+))
4122 }
4123 }
4124 };
4125}
4126
4127field_group_tuple!((O1, V1, 0), (O2, V2, 1));
4128field_group_tuple!((O1, V1, 0), (O2, V2, 1), (O3, V3, 2));
4129field_group_tuple!((O1, V1, 0), (O2, V2, 1), (O3, V3, 2), (O4, V4, 3));
4130field_group_tuple!(
4131 (O1, V1, 0),
4132 (O2, V2, 1),
4133 (O3, V3, 2),
4134 (O4, V4, 3),
4135 (O5, V5, 4)
4136);
4137field_group_tuple!(
4138 (O1, V1, 0),
4139 (O2, V2, 1),
4140 (O3, V3, 2),
4141 (O4, V4, 3),
4142 (O5, V5, 4),
4143 (O6, V6, 5)
4144);
4145field_group_tuple!(
4146 (O1, V1, 0),
4147 (O2, V2, 1),
4148 (O3, V3, 2),
4149 (O4, V4, 3),
4150 (O5, V5, 4),
4151 (O6, V6, 5),
4152 (O7, V7, 6)
4153);
4154field_group_tuple!(
4155 (O1, V1, 0),
4156 (O2, V2, 1),
4157 (O3, V3, 2),
4158 (O4, V4, 3),
4159 (O5, V5, 4),
4160 (O6, V6, 5),
4161 (O7, V7, 6),
4162 (O8, V8, 7)
4163);
4164field_group_tuple!(
4165 (O1, V1, 0),
4166 (O2, V2, 1),
4167 (O3, V3, 2),
4168 (O4, V4, 3),
4169 (O5, V5, 4),
4170 (O6, V6, 5),
4171 (O7, V7, 6),
4172 (O8, V8, 7),
4173 (O9, V9, 8)
4174);
4175field_group_tuple!(
4176 (O1, V1, 0),
4177 (O2, V2, 1),
4178 (O3, V3, 2),
4179 (O4, V4, 3),
4180 (O5, V5, 4),
4181 (O6, V6, 5),
4182 (O7, V7, 6),
4183 (O8, V8, 7),
4184 (O9, V9, 8),
4185 (O10, V10, 9)
4186);
4187field_group_tuple!(
4188 (O1, V1, 0),
4189 (O2, V2, 1),
4190 (O3, V3, 2),
4191 (O4, V4, 3),
4192 (O5, V5, 4),
4193 (O6, V6, 5),
4194 (O7, V7, 6),
4195 (O8, V8, 7),
4196 (O9, V9, 8),
4197 (O10, V10, 9),
4198 (O11, V11, 10)
4199);
4200field_group_tuple!(
4201 (O1, V1, 0),
4202 (O2, V2, 1),
4203 (O3, V3, 2),
4204 (O4, V4, 3),
4205 (O5, V5, 4),
4206 (O6, V6, 5),
4207 (O7, V7, 6),
4208 (O8, V8, 7),
4209 (O9, V9, 8),
4210 (O10, V10, 9),
4211 (O11, V11, 10),
4212 (O12, V12, 11)
4213);
4214field_group_tuple!(
4215 (O1, V1, 0),
4216 (O2, V2, 1),
4217 (O3, V3, 2),
4218 (O4, V4, 3),
4219 (O5, V5, 4),
4220 (O6, V6, 5),
4221 (O7, V7, 6),
4222 (O8, V8, 7),
4223 (O9, V9, 8),
4224 (O10, V10, 9),
4225 (O11, V11, 10),
4226 (O12, V12, 11),
4227 (O13, V13, 12)
4228);
4229field_group_tuple!(
4230 (O1, V1, 0),
4231 (O2, V2, 1),
4232 (O3, V3, 2),
4233 (O4, V4, 3),
4234 (O5, V5, 4),
4235 (O6, V6, 5),
4236 (O7, V7, 6),
4237 (O8, V8, 7),
4238 (O9, V9, 8),
4239 (O10, V10, 9),
4240 (O11, V11, 10),
4241 (O12, V12, 11),
4242 (O13, V13, 12),
4243 (O14, V14, 13)
4244);
4245field_group_tuple!(
4246 (O1, V1, 0),
4247 (O2, V2, 1),
4248 (O3, V3, 2),
4249 (O4, V4, 3),
4250 (O5, V5, 4),
4251 (O6, V6, 5),
4252 (O7, V7, 6),
4253 (O8, V8, 7),
4254 (O9, V9, 8),
4255 (O10, V10, 9),
4256 (O11, V11, 10),
4257 (O12, V12, 11),
4258 (O13, V13, 12),
4259 (O14, V14, 13),
4260 (O15, V15, 14)
4261);
4262field_group_tuple!(
4263 (O1, V1, 0),
4264 (O2, V2, 1),
4265 (O3, V3, 2),
4266 (O4, V4, 3),
4267 (O5, V5, 4),
4268 (O6, V6, 5),
4269 (O7, V7, 6),
4270 (O8, V8, 7),
4271 (O9, V9, 8),
4272 (O10, V10, 9),
4273 (O11, V11, 10),
4274 (O12, V12, 11),
4275 (O13, V13, 12),
4276 (O14, V14, 13),
4277 (O15, V15, 14),
4278 (O16, V16, 15)
4279);
4280
4281pub struct FieldTupleGroupedQuery<'s, 'db, S: Schema, B: Selectable<S>, G: FieldGroupTuple<S>> {
4284 query: Query<'s, 'db, S, B>,
4285 groups: G,
4286}
4287
4288impl<'s, 'db, S, B, G> FieldTupleGroupedQuery<'s, 'db, S, B, G>
4289where
4290 S: Schema,
4291 B: Selectable<S>,
4292 G: FieldGroupTuple<S>,
4293{
4294 pub fn close(&self) {
4296 self.query.close();
4297 }
4298
4299 #[must_use]
4301 pub fn is_closed(&self) -> bool {
4302 self.query.is_closed()
4303 }
4304
4305 pub async fn aggregate<T: crate::aggregate::AggregateTuple<S>>(
4308 &self,
4309 terms: T,
4310 ) -> Result<Vec<(G::Output, T::Output)>> {
4311 let deadline = self.query.session.begin_invocation()?;
4312 let term_list = terms.terms();
4313 let group_fields = self.groups.fields();
4314 let validated = self
4315 .query
4316 .validated_reduce_by_fields(&group_fields, &term_list)?;
4317 let (validated, result) = self.query.execute(validated, deadline).await?;
4318 let rows = self
4319 .query
4320 .decoded_reduction_rows(&validated, &result, deadline)?;
4321 let mut outputs = Vec::with_capacity(rows.len());
4322 for row in rows {
4323 self.query
4324 .session
4325 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
4326 let values = row.field_groups().ok_or_else(|| {
4327 Error::model_validation(
4328 ModelValidationPhase::Hydration,
4329 "wrong_result_shape",
4330 vec![],
4331 "tuple-field-grouped aggregates require tuple group evidence per row",
4332 None,
4333 )
4334 })?;
4335 outputs.push((G::decode(values)?, T::decode(row.values())?));
4336 }
4337 self.query
4338 .session
4339 .check_invocation(deadline, ModelValidationPhase::Hydration)?;
4340 Ok(outputs)
4341 }
4342}