iceberg/transaction/
sort_order.rs1use std::sync::Arc;
19
20use async_trait::async_trait;
21
22use crate::error::Result;
23use crate::spec::{NullOrder, SchemaRef, SortDirection, SortField, SortOrder, Transform};
24use crate::table::Table;
25use crate::transaction::{ActionCommit, TransactionAction};
26use crate::{Error, ErrorKind, TableRequirement, TableUpdate};
27
28#[derive(Debug, PartialEq, Eq, Clone)]
32struct PendingSortField {
33 name: String,
34 direction: SortDirection,
35 null_order: NullOrder,
36}
37
38impl PendingSortField {
39 fn to_sort_field(&self, schema: &SchemaRef) -> Result<SortField> {
40 let field_id = schema.field_id_by_name(self.name.as_str()).ok_or_else(|| {
41 Error::new(
42 ErrorKind::DataInvalid,
43 format!("Cannot find field {} in table schema", self.name),
44 )
45 })?;
46
47 Ok(SortField::builder()
48 .source_id(field_id)
49 .transform(Transform::Identity)
50 .direction(self.direction)
51 .null_order(self.null_order)
52 .build())
53 }
54}
55
56pub struct ReplaceSortOrderAction {
58 pending_sort_fields: Vec<PendingSortField>,
59}
60
61impl ReplaceSortOrderAction {
62 pub(crate) fn new() -> Self {
63 ReplaceSortOrderAction {
64 pending_sort_fields: vec![],
65 }
66 }
67
68 pub fn asc(self, name: &str, null_order: NullOrder) -> Self {
70 self.add_sort_field(name, SortDirection::Ascending, null_order)
71 }
72
73 pub fn desc(self, name: &str, null_order: NullOrder) -> Self {
75 self.add_sort_field(name, SortDirection::Descending, null_order)
76 }
77
78 fn add_sort_field(
79 mut self,
80 name: &str,
81 sort_direction: SortDirection,
82 null_order: NullOrder,
83 ) -> Self {
84 self.pending_sort_fields.push(PendingSortField {
85 name: name.to_string(),
86 direction: sort_direction,
87 null_order,
88 });
89
90 self
91 }
92}
93
94#[async_trait]
95impl TransactionAction for ReplaceSortOrderAction {
96 async fn commit(self: Arc<Self>, table: &Table) -> Result<ActionCommit> {
97 let current_schema = table.metadata().current_schema();
98 let sort_fields: Result<Vec<SortField>> = self
99 .pending_sort_fields
100 .iter()
101 .map(|p| p.to_sort_field(current_schema))
102 .collect();
103
104 let bound_sort_order = SortOrder::builder()
105 .with_fields(sort_fields?)
106 .build(current_schema)?;
107
108 let updates = vec![
109 TableUpdate::AddSortOrder {
110 sort_order: bound_sort_order,
111 },
112 TableUpdate::SetDefaultSortOrder { sort_order_id: -1 },
113 ];
114
115 let requirements = vec![
116 TableRequirement::CurrentSchemaIdMatch {
117 current_schema_id: current_schema.schema_id(),
118 },
119 TableRequirement::DefaultSortOrderIdMatch {
120 default_sort_order_id: table.metadata().default_sort_order().order_id,
121 },
122 ];
123
124 Ok(ActionCommit::new(updates, requirements))
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use as_any::Downcast;
131
132 use crate::spec::{NullOrder, SortDirection};
133 use crate::transaction::sort_order::{PendingSortField, ReplaceSortOrderAction};
134 use crate::transaction::tests::make_v2_table;
135 use crate::transaction::{ApplyTransactionAction, Transaction};
136
137 #[test]
138 fn test_replace_sort_order() {
139 let table = make_v2_table();
140 let tx = Transaction::new(&table);
141 let replace_sort_order = tx.replace_sort_order();
142
143 let tx = replace_sort_order
144 .asc("x", NullOrder::First)
145 .desc("y", NullOrder::Last)
146 .apply(tx)
147 .unwrap();
148
149 let replace_sort_order = (*tx.actions[0])
150 .downcast_ref::<ReplaceSortOrderAction>()
151 .unwrap();
152
153 assert_eq!(replace_sort_order.pending_sort_fields, vec![
154 PendingSortField {
155 name: String::from("x"),
156 direction: SortDirection::Ascending,
157 null_order: NullOrder::First,
158 },
159 PendingSortField {
160 name: String::from("y"),
161 direction: SortDirection::Descending,
162 null_order: NullOrder::Last,
163 }
164 ]);
165 }
166}