|
15 | 15 | // specific language governing permissions and limitations |
16 | 16 | // under the License. |
17 | 17 |
|
| 18 | +use std::sync::Arc; |
| 19 | + |
| 20 | +use async_trait::async_trait; |
| 21 | + |
18 | 22 | use crate::error::Result; |
19 | | -use crate::spec::{NullOrder, SortDirection, SortField, SortOrder, Transform}; |
20 | | -use crate::transaction::Transaction; |
| 23 | +use crate::spec::{NullOrder, SchemaRef, SortDirection, SortField, SortOrder, Transform}; |
| 24 | +use crate::table::Table; |
| 25 | +use crate::transaction::{ActionCommit, TransactionAction}; |
21 | 26 | use crate::{Error, ErrorKind, TableRequirement, TableUpdate}; |
22 | 27 |
|
| 28 | +/// Represents a sort field whose construction and validation are deferred until commit time. |
| 29 | +/// This avoids the need to pass a `Table` reference into methods like `asc` or `desc` when |
| 30 | +/// adding sort orders. |
| 31 | +#[derive(Debug, PartialEq, Eq, Clone)] |
| 32 | +struct PendingSortField { |
| 33 | + name: String, |
| 34 | + direction: SortDirection, |
| 35 | + null_order: NullOrder, |
| 36 | +} |
| 37 | + |
| 38 | +impl 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 | + |
23 | 56 | /// Transaction action for replacing sort order. |
24 | 57 | pub struct ReplaceSortOrderAction { |
25 | | - pub tx: Transaction, |
26 | | - pub sort_fields: Vec<SortField>, |
| 58 | + pending_sort_fields: Vec<PendingSortField>, |
27 | 59 | } |
28 | 60 |
|
29 | 61 | impl ReplaceSortOrderAction { |
| 62 | + pub fn new() -> Self { |
| 63 | + ReplaceSortOrderAction { |
| 64 | + pending_sort_fields: vec![], |
| 65 | + } |
| 66 | + } |
| 67 | + |
30 | 68 | /// Adds a field for sorting in ascending order. |
31 | | - pub fn asc(self, name: &str, null_order: NullOrder) -> Result<Self> { |
| 69 | + pub fn asc(self, name: &str, null_order: NullOrder) -> Self { |
32 | 70 | self.add_sort_field(name, SortDirection::Ascending, null_order) |
33 | 71 | } |
34 | 72 |
|
35 | 73 | /// Adds a field for sorting in descending order. |
36 | | - pub fn desc(self, name: &str, null_order: NullOrder) -> Result<Self> { |
| 74 | + pub fn desc(self, name: &str, null_order: NullOrder) -> Self { |
37 | 75 | self.add_sort_field(name, SortDirection::Descending, null_order) |
38 | 76 | } |
39 | 77 |
|
40 | | - /// Finished building the action and apply it to the transaction. |
41 | | - pub fn apply(mut self) -> Result<Transaction> { |
42 | | - let unbound_sort_order = SortOrder::builder() |
43 | | - .with_fields(self.sort_fields) |
44 | | - .build_unbound()?; |
| 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 | +impl Default for ReplaceSortOrderAction { |
| 95 | + fn default() -> Self { |
| 96 | + Self::new() |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +#[async_trait] |
| 101 | +impl TransactionAction for ReplaceSortOrderAction { |
| 102 | + async fn commit(self: Arc<Self>, table: &Table) -> Result<ActionCommit> { |
| 103 | + let current_schema = table.metadata().current_schema(); |
| 104 | + let sort_fields: Result<Vec<SortField>> = self |
| 105 | + .pending_sort_fields |
| 106 | + .iter() |
| 107 | + .map(|p| p.to_sort_field(current_schema)) |
| 108 | + .collect(); |
| 109 | + |
| 110 | + let bound_sort_order = SortOrder::builder() |
| 111 | + .with_fields(sort_fields?) |
| 112 | + .build(current_schema)?; |
45 | 113 |
|
46 | 114 | let updates = vec![ |
47 | 115 | TableUpdate::AddSortOrder { |
48 | | - sort_order: unbound_sort_order, |
| 116 | + sort_order: bound_sort_order, |
49 | 117 | }, |
50 | 118 | TableUpdate::SetDefaultSortOrder { sort_order_id: -1 }, |
51 | 119 | ]; |
52 | 120 |
|
53 | 121 | let requirements = vec![ |
54 | 122 | TableRequirement::CurrentSchemaIdMatch { |
55 | | - current_schema_id: self |
56 | | - .tx |
57 | | - .current_table |
58 | | - .metadata() |
59 | | - .current_schema() |
60 | | - .schema_id(), |
| 123 | + current_schema_id: current_schema.schema_id(), |
61 | 124 | }, |
62 | 125 | TableRequirement::DefaultSortOrderIdMatch { |
63 | | - default_sort_order_id: self |
64 | | - .tx |
65 | | - .current_table |
66 | | - .metadata() |
67 | | - .default_sort_order() |
68 | | - .order_id, |
| 126 | + default_sort_order_id: table.metadata().default_sort_order().order_id, |
69 | 127 | }, |
70 | 128 | ]; |
71 | 129 |
|
72 | | - self.tx.apply(updates, requirements)?; |
73 | | - |
74 | | - Ok(self.tx) |
75 | | - } |
76 | | - |
77 | | - fn add_sort_field( |
78 | | - mut self, |
79 | | - name: &str, |
80 | | - sort_direction: SortDirection, |
81 | | - null_order: NullOrder, |
82 | | - ) -> Result<Self> { |
83 | | - let field_id = self |
84 | | - .tx |
85 | | - .current_table |
86 | | - .metadata() |
87 | | - .current_schema() |
88 | | - .field_id_by_name(name) |
89 | | - .ok_or_else(|| { |
90 | | - Error::new( |
91 | | - ErrorKind::DataInvalid, |
92 | | - format!("Cannot find field {} in table schema", name), |
93 | | - ) |
94 | | - })?; |
95 | | - |
96 | | - let sort_field = SortField::builder() |
97 | | - .source_id(field_id) |
98 | | - .transform(Transform::Identity) |
99 | | - .direction(sort_direction) |
100 | | - .null_order(null_order) |
101 | | - .build(); |
102 | | - |
103 | | - self.sort_fields.push(sort_field); |
104 | | - Ok(self) |
| 130 | + Ok(ActionCommit::new(updates, requirements)) |
105 | 131 | } |
106 | 132 | } |
107 | 133 |
|
108 | 134 | #[cfg(test)] |
109 | 135 | mod tests { |
110 | | - use crate::transaction::Transaction; |
| 136 | + use as_any::Downcast; |
| 137 | + |
| 138 | + use crate::spec::{NullOrder, SortDirection}; |
| 139 | + use crate::transaction::sort_order::{PendingSortField, ReplaceSortOrderAction}; |
111 | 140 | use crate::transaction::tests::make_v2_table; |
112 | | - use crate::{TableRequirement, TableUpdate}; |
| 141 | + use crate::transaction::{ApplyTransactionAction, Transaction}; |
113 | 142 |
|
114 | 143 | #[test] |
115 | 144 | fn test_replace_sort_order() { |
116 | 145 | let table = make_v2_table(); |
117 | 146 | let tx = Transaction::new(&table); |
118 | | - let tx = tx.replace_sort_order().apply().unwrap(); |
119 | | - |
120 | | - assert_eq!( |
121 | | - vec![ |
122 | | - TableUpdate::AddSortOrder { |
123 | | - sort_order: Default::default() |
124 | | - }, |
125 | | - TableUpdate::SetDefaultSortOrder { sort_order_id: -1 } |
126 | | - ], |
127 | | - tx.updates |
128 | | - ); |
129 | | - |
130 | | - assert_eq!( |
131 | | - vec![ |
132 | | - TableRequirement::CurrentSchemaIdMatch { |
133 | | - current_schema_id: 1 |
134 | | - }, |
135 | | - TableRequirement::DefaultSortOrderIdMatch { |
136 | | - default_sort_order_id: 3 |
137 | | - } |
138 | | - ], |
139 | | - tx.requirements |
140 | | - ); |
| 147 | + let replace_sort_order = tx.replace_sort_order(); |
| 148 | + |
| 149 | + let tx = replace_sort_order |
| 150 | + .asc("x", NullOrder::First) |
| 151 | + .desc("y", NullOrder::Last) |
| 152 | + .apply(tx) |
| 153 | + .unwrap(); |
| 154 | + |
| 155 | + let replace_sort_order = (*tx.actions[0]) |
| 156 | + .downcast_ref::<ReplaceSortOrderAction>() |
| 157 | + .unwrap(); |
| 158 | + |
| 159 | + assert_eq!(replace_sort_order.pending_sort_fields, vec![ |
| 160 | + PendingSortField { |
| 161 | + name: String::from("x"), |
| 162 | + direction: SortDirection::Ascending, |
| 163 | + null_order: NullOrder::First, |
| 164 | + }, |
| 165 | + PendingSortField { |
| 166 | + name: String::from("y"), |
| 167 | + direction: SortDirection::Descending, |
| 168 | + null_order: NullOrder::Last, |
| 169 | + } |
| 170 | + ]); |
141 | 171 | } |
142 | 172 | } |
0 commit comments