icydb_core/db/query/expr/
order.rs1use crate::db::{
7 QueryError,
8 query::{
9 builder::{
10 AggregateExpr, FieldRef, NumericProjectionExpr, RoundProjectionExpr, TextProjectionExpr,
11 },
12 plan::{
13 OrderDirection, OrderTerm as PlannedOrderTerm,
14 expr::{Expr, FieldId},
15 },
16 preparation::PreparationWork,
17 },
18};
19
20#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct OrderExpr {
30 expr: Expr,
31}
32
33impl OrderExpr {
34 #[must_use]
36 pub fn field(field: impl Into<String>) -> Self {
37 let field = field.into();
38
39 Self {
40 expr: Expr::Field(FieldId::new(field)),
41 }
42 }
43
44 const fn new(expr: Expr) -> Self {
48 Self { expr }
49 }
50}
51
52impl From<&str> for OrderExpr {
53 fn from(value: &str) -> Self {
54 Self::field(value)
55 }
56}
57
58impl From<String> for OrderExpr {
59 fn from(value: String) -> Self {
60 Self::field(value)
61 }
62}
63
64impl From<FieldRef> for OrderExpr {
65 fn from(value: FieldRef) -> Self {
66 Self::field(value.as_str())
67 }
68}
69
70impl From<TextProjectionExpr> for OrderExpr {
71 fn from(value: TextProjectionExpr) -> Self {
72 Self::new(value.expr().clone())
73 }
74}
75
76impl From<NumericProjectionExpr> for OrderExpr {
77 fn from(value: NumericProjectionExpr) -> Self {
78 Self::new(value.expr().clone())
79 }
80}
81
82impl From<RoundProjectionExpr> for OrderExpr {
83 fn from(value: RoundProjectionExpr) -> Self {
84 Self::new(value.expr().clone())
85 }
86}
87
88impl From<AggregateExpr> for OrderExpr {
89 fn from(value: AggregateExpr) -> Self {
90 Self::new(Expr::Aggregate(value))
91 }
92}
93
94#[derive(Clone, Debug, Eq, PartialEq)]
103pub struct OrderTerm {
104 expr: OrderExpr,
105 direction: OrderDirection,
106}
107
108impl OrderTerm {
109 pub(in crate::db) fn copy_for_preparation(
111 &self,
112 work: &PreparationWork<'_>,
113 ) -> Result<Self, QueryError> {
114 Ok(Self {
115 expr: OrderExpr::new(work.copy_expr(self.expression())?),
116 direction: self.direction,
117 })
118 }
119
120 pub(in crate::db) const fn expression(&self) -> &Expr {
122 &self.expr.expr
123 }
124
125 #[must_use]
127 pub fn asc(expr: impl Into<OrderExpr>) -> Self {
128 Self {
129 expr: expr.into(),
130 direction: OrderDirection::Asc,
131 }
132 }
133
134 #[must_use]
136 pub fn desc(expr: impl Into<OrderExpr>) -> Self {
137 Self {
138 expr: expr.into(),
139 direction: OrderDirection::Desc,
140 }
141 }
142
143 pub(in crate::db) fn lower(self) -> PlannedOrderTerm {
146 PlannedOrderTerm::new(self.expr.expr, self.direction)
147 }
148}
149
150#[must_use]
152pub fn field(field: impl Into<String>) -> OrderExpr {
153 OrderExpr::field(field)
154}
155
156#[must_use]
158pub fn asc(expr: impl Into<OrderExpr>) -> OrderTerm {
159 OrderTerm::asc(expr)
160}
161
162#[must_use]
164pub fn desc(expr: impl Into<OrderExpr>) -> OrderTerm {
165 OrderTerm::desc(expr)
166}
167
168#[cfg(test)]
169mod tests {
170 use super::{OrderExpr, OrderTerm};
171 use crate::{
172 db::query::{
173 builder::sum,
174 plan::{OrderDirection, expr::Expr},
175 },
176 value::Value,
177 };
178
179 #[test]
180 fn owned_order_lowering_preserves_operands_and_direction() {
181 for direction in [OrderDirection::Asc, OrderDirection::Desc] {
182 let aggregate = sum("amount")
183 .with_filter_expr(Expr::Literal(Value::Text("x".repeat(4096))))
184 .distinct();
185 let input_address = std::ptr::from_ref(aggregate.input_expr().expect("input"));
186 let filter_address = std::ptr::from_ref(aggregate.filter_expr().expect("filter"));
187 let expected = Expr::Aggregate(aggregate.clone());
188 let term = OrderTerm {
189 expr: OrderExpr::from(aggregate),
190 direction,
191 };
192
193 let lowered = term.lower();
194 assert_eq!(lowered.direction(), direction);
195 assert_eq!(lowered.expr(), &expected);
196 let Expr::Aggregate(aggregate) = lowered.expr() else {
197 panic!("aggregate order expression");
198 };
199 assert_eq!(
200 std::ptr::from_ref(aggregate.input_expr().expect("input")),
201 input_address
202 );
203 assert_eq!(
204 std::ptr::from_ref(aggregate.filter_expr().expect("filter")),
205 filter_address
206 );
207 }
208 }
209}