1use std::fmt::Debug;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::hash::Hasher;
8
9use prost::Message;
10use vortex_array::Array;
11use vortex_array::ArrayEq;
12use vortex_array::ArrayHash;
13use vortex_array::ArrayId;
14use vortex_array::ArrayParts;
15use vortex_array::ArrayRef;
16use vortex_array::ArrayView;
17use vortex_array::ExecutionCtx;
18use vortex_array::ExecutionResult;
19use vortex_array::IntoArray;
20use vortex_array::Precision;
21use vortex_array::TypedArrayRef;
22use vortex_array::arrays::TemporalArray;
23use vortex_array::buffer::BufferHandle;
24use vortex_array::dtype::DType;
25use vortex_array::dtype::Nullability;
26use vortex_array::dtype::PType;
27use vortex_array::serde::ArrayChildren;
28use vortex_array::vtable::VTable;
29use vortex_array::vtable::ValidityChild;
30use vortex_array::vtable::ValidityVTableFromChild;
31use vortex_error::VortexExpect;
32use vortex_error::VortexResult;
33use vortex_error::vortex_bail;
34use vortex_error::vortex_ensure;
35use vortex_error::vortex_err;
36use vortex_error::vortex_panic;
37use vortex_session::VortexSession;
38use vortex_session::registry::CachedId;
39
40use crate::TemporalParts;
41use crate::canonical::decode_to_temporal;
42use crate::compute::kernel::PARENT_KERNELS;
43use crate::compute::rules::PARENT_RULES;
44use crate::split_temporal;
45
46pub type DateTimePartsArray = Array<DateTimeParts>;
48
49impl ArrayHash for DateTimePartsData {
50 fn array_hash<H: Hasher>(&self, _state: &mut H, _precision: Precision) {}
51}
52
53impl ArrayEq for DateTimePartsData {
54 fn array_eq(&self, _other: &Self, _precision: Precision) -> bool {
55 true
56 }
57}
58
59#[derive(Clone, prost::Message)]
60#[repr(C)]
61pub struct DateTimePartsMetadata {
62 #[prost(enumeration = "PType", tag = "1")]
65 pub days_ptype: i32,
66 #[prost(enumeration = "PType", tag = "2")]
67 pub seconds_ptype: i32,
68 #[prost(enumeration = "PType", tag = "3")]
69 pub subseconds_ptype: i32,
70}
71
72impl DateTimePartsMetadata {
73 pub fn get_days_ptype(&self) -> VortexResult<PType> {
74 PType::try_from(self.days_ptype)
75 .map_err(|_| vortex_err!("Invalid PType {}", self.days_ptype))
76 }
77
78 pub fn get_seconds_ptype(&self) -> VortexResult<PType> {
79 PType::try_from(self.seconds_ptype)
80 .map_err(|_| vortex_err!("Invalid PType {}", self.seconds_ptype))
81 }
82
83 pub fn get_subseconds_ptype(&self) -> VortexResult<PType> {
84 PType::try_from(self.subseconds_ptype)
85 .map_err(|_| vortex_err!("Invalid PType {}", self.subseconds_ptype))
86 }
87}
88
89impl VTable for DateTimeParts {
90 type ArrayData = DateTimePartsData;
91
92 type OperationsVTable = Self;
93 type ValidityVTable = ValidityVTableFromChild;
94
95 fn id(&self) -> ArrayId {
96 static ID: CachedId = CachedId::new("vortex.datetimeparts");
97 *ID
98 }
99
100 fn validate(
101 &self,
102 _data: &Self::ArrayData,
103 dtype: &DType,
104 len: usize,
105 slots: &[Option<ArrayRef>],
106 ) -> VortexResult<()> {
107 let days = slots[DAYS_SLOT]
108 .as_ref()
109 .vortex_expect("DateTimePartsArray days slot");
110 let seconds = slots[SECONDS_SLOT]
111 .as_ref()
112 .vortex_expect("DateTimePartsArray seconds slot");
113 let subseconds = slots[SUBSECONDS_SLOT]
114 .as_ref()
115 .vortex_expect("DateTimePartsArray subseconds slot");
116 DateTimePartsData::validate(dtype, days, seconds, subseconds, len)
117 }
118
119 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
120 0
121 }
122
123 fn buffer(_array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
124 vortex_panic!("DateTimePartsArray buffer index {idx} out of bounds")
125 }
126
127 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
128 vortex_panic!("DateTimePartsArray buffer_name index {idx} out of bounds")
129 }
130
131 fn serialize(
132 array: ArrayView<'_, Self>,
133 _session: &VortexSession,
134 ) -> VortexResult<Option<Vec<u8>>> {
135 Ok(Some(
136 DateTimePartsMetadata {
137 days_ptype: PType::try_from(array.days().dtype())? as i32,
138 seconds_ptype: PType::try_from(array.seconds().dtype())? as i32,
139 subseconds_ptype: PType::try_from(array.subseconds().dtype())? as i32,
140 }
141 .encode_to_vec(),
142 ))
143 }
144
145 fn deserialize(
146 &self,
147 dtype: &DType,
148 len: usize,
149 metadata: &[u8],
150 _buffers: &[BufferHandle],
151 children: &dyn ArrayChildren,
152 _session: &VortexSession,
153 ) -> VortexResult<ArrayParts<Self>> {
154 let metadata = DateTimePartsMetadata::decode(metadata)?;
155 if children.len() != 3 {
156 vortex_bail!(
157 "Expected 3 children for datetime-parts encoding, found {}",
158 children.len()
159 )
160 }
161
162 let days = children.get(
163 0,
164 &DType::Primitive(metadata.get_days_ptype()?, dtype.nullability()),
165 len,
166 )?;
167 let seconds = children.get(
168 1,
169 &DType::Primitive(metadata.get_seconds_ptype()?, Nullability::NonNullable),
170 len,
171 )?;
172 let subseconds = children.get(
173 2,
174 &DType::Primitive(metadata.get_subseconds_ptype()?, Nullability::NonNullable),
175 len,
176 )?;
177
178 let slots = vec![Some(days), Some(seconds), Some(subseconds)];
179 let data = DateTimePartsData {};
180 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots))
181 }
182
183 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
184 SLOT_NAMES[idx].to_string()
185 }
186
187 fn execute(array: Array<Self>, ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
188 Ok(ExecutionResult::done(
189 decode_to_temporal(&array, ctx)?.into_array(),
190 ))
191 }
192
193 fn reduce_parent(
194 array: ArrayView<'_, Self>,
195 parent: &ArrayRef,
196 child_idx: usize,
197 ) -> VortexResult<Option<ArrayRef>> {
198 PARENT_RULES.evaluate(array, parent, child_idx)
199 }
200
201 fn execute_parent(
202 array: ArrayView<'_, Self>,
203 parent: &ArrayRef,
204 child_idx: usize,
205 ctx: &mut ExecutionCtx,
206 ) -> VortexResult<Option<ArrayRef>> {
207 PARENT_KERNELS.execute(array, parent, child_idx, ctx)
208 }
209}
210
211pub(super) const DAYS_SLOT: usize = 0;
213pub(super) const SECONDS_SLOT: usize = 1;
215pub(super) const SUBSECONDS_SLOT: usize = 2;
217pub(super) const NUM_SLOTS: usize = 3;
218pub(super) const SLOT_NAMES: [&str; NUM_SLOTS] = ["days", "seconds", "subseconds"];
219
220#[derive(Clone, Debug)]
221pub struct DateTimePartsData {}
222
223impl Display for DateTimePartsData {
224 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
225 Ok(())
226 }
227}
228
229pub trait DateTimePartsArrayExt: TypedArrayRef<DateTimeParts> {
230 fn days(&self) -> &ArrayRef {
231 self.as_ref().slots()[DAYS_SLOT]
232 .as_ref()
233 .vortex_expect("DateTimePartsArray days slot")
234 }
235
236 fn seconds(&self) -> &ArrayRef {
237 self.as_ref().slots()[SECONDS_SLOT]
238 .as_ref()
239 .vortex_expect("DateTimePartsArray seconds slot")
240 }
241
242 fn subseconds(&self) -> &ArrayRef {
243 self.as_ref().slots()[SUBSECONDS_SLOT]
244 .as_ref()
245 .vortex_expect("DateTimePartsArray subseconds slot")
246 }
247}
248
249impl<T: TypedArrayRef<DateTimeParts>> DateTimePartsArrayExt for T {}
250
251#[derive(Clone, Debug)]
252pub struct DateTimeParts;
253
254impl DateTimeParts {
255 pub fn try_new(
257 dtype: DType,
258 days: ArrayRef,
259 seconds: ArrayRef,
260 subseconds: ArrayRef,
261 ) -> VortexResult<DateTimePartsArray> {
262 let len = days.len();
263 DateTimePartsData::validate(&dtype, &days, &seconds, &subseconds, len)?;
264 let slots = vec![Some(days), Some(seconds), Some(subseconds)];
265 let data = DateTimePartsData {};
266 Ok(unsafe {
267 Array::from_parts_unchecked(
268 ArrayParts::new(DateTimeParts, dtype, len, data).with_slots(slots),
269 )
270 })
271 }
272
273 pub fn try_from_temporal(temporal: TemporalArray) -> VortexResult<DateTimePartsArray> {
275 let dtype = temporal.dtype().clone();
276 let TemporalParts {
277 days,
278 seconds,
279 subseconds,
280 } = split_temporal(temporal)?;
281 Self::try_new(dtype, days, seconds, subseconds)
282 }
283}
284
285impl DateTimePartsData {
286 pub fn validate(
287 dtype: &DType,
288 days: &ArrayRef,
289 seconds: &ArrayRef,
290 subseconds: &ArrayRef,
291 len: usize,
292 ) -> VortexResult<()> {
293 vortex_ensure!(days.len() == len, "expected len {len}, got {}", days.len());
294
295 if !days.dtype().is_int() || (dtype.is_nullable() != days.dtype().is_nullable()) {
296 vortex_bail!(
297 "Expected integer with nullability {}, got {}",
298 dtype.is_nullable(),
299 days.dtype()
300 );
301 }
302 if !seconds.dtype().is_int() || seconds.dtype().is_nullable() {
303 vortex_bail!(MismatchedTypes: "non-nullable integer", seconds.dtype());
304 }
305 if !subseconds.dtype().is_int() || subseconds.dtype().is_nullable() {
306 vortex_bail!(MismatchedTypes: "non-nullable integer", subseconds.dtype());
307 }
308
309 if len != seconds.len() || len != subseconds.len() {
310 vortex_bail!(
311 "Mismatched lengths {} {} {}",
312 days.len(),
313 seconds.len(),
314 subseconds.len()
315 );
316 }
317
318 Ok(())
319 }
320}
321
322impl ValidityChild<DateTimeParts> for DateTimeParts {
323 fn validity_child(array: ArrayView<'_, DateTimeParts>) -> ArrayRef {
324 array.days().clone()
325 }
326}