1use reifydb_core::value::column::data::ColumnData;
5use reifydb_type::value::{container::temporal::TemporalContainer, time::Time, r#type::Type};
6
7use crate::{
8 ScalarFunction, ScalarFunctionContext,
9 error::{ScalarFunctionError, ScalarFunctionResult},
10 propagate_options,
11};
12
13pub struct TimeNew;
14
15impl TimeNew {
16 pub fn new() -> Self {
17 Self
18 }
19}
20
21fn extract_i32(data: &ColumnData, i: usize) -> Option<i32> {
22 match data {
23 ColumnData::Int1(c) => c.get(i).map(|&v| v as i32),
24 ColumnData::Int2(c) => c.get(i).map(|&v| v as i32),
25 ColumnData::Int4(c) => c.get(i).copied(),
26 ColumnData::Int8(c) => c.get(i).map(|&v| v as i32),
27 ColumnData::Int16(c) => c.get(i).map(|&v| v as i32),
28 ColumnData::Uint1(c) => c.get(i).map(|&v| v as i32),
29 ColumnData::Uint2(c) => c.get(i).map(|&v| v as i32),
30 ColumnData::Uint4(c) => c.get(i).map(|&v| v as i32),
31 ColumnData::Uint8(c) => c.get(i).map(|&v| v as i32),
32 ColumnData::Uint16(c) => c.get(i).map(|&v| v as i32),
33 _ => None,
34 }
35}
36
37fn is_integer_type(data: &ColumnData) -> bool {
38 matches!(
39 data,
40 ColumnData::Int1(_)
41 | ColumnData::Int2(_) | ColumnData::Int4(_)
42 | ColumnData::Int8(_) | ColumnData::Int16(_)
43 | ColumnData::Uint1(_)
44 | ColumnData::Uint2(_)
45 | ColumnData::Uint4(_)
46 | ColumnData::Uint8(_)
47 | ColumnData::Uint16(_)
48 )
49}
50
51impl ScalarFunction for TimeNew {
52 fn scalar(&self, ctx: ScalarFunctionContext) -> ScalarFunctionResult<ColumnData> {
53 if let Some(result) = propagate_options(self, &ctx) {
54 return result;
55 }
56 let columns = ctx.columns;
57 let row_count = ctx.row_count;
58
59 if columns.len() != 3 && columns.len() != 4 {
60 return Err(ScalarFunctionError::ArityMismatch {
61 function: ctx.fragment.clone(),
62 expected: 3,
63 actual: columns.len(),
64 });
65 }
66
67 let hour_col = columns.get(0).unwrap();
68 let min_col = columns.get(1).unwrap();
69 let sec_col = columns.get(2).unwrap();
70 let nano_col = if columns.len() == 4 {
71 Some(columns.get(3).unwrap())
72 } else {
73 None
74 };
75
76 if !is_integer_type(hour_col.data()) {
77 return Err(ScalarFunctionError::InvalidArgumentType {
78 function: ctx.fragment.clone(),
79 argument_index: 0,
80 expected: vec![
81 Type::Int1,
82 Type::Int2,
83 Type::Int4,
84 Type::Int8,
85 Type::Int16,
86 Type::Uint1,
87 Type::Uint2,
88 Type::Uint4,
89 Type::Uint8,
90 Type::Uint16,
91 ],
92 actual: hour_col.data().get_type(),
93 });
94 }
95 if !is_integer_type(min_col.data()) {
96 return Err(ScalarFunctionError::InvalidArgumentType {
97 function: ctx.fragment.clone(),
98 argument_index: 1,
99 expected: vec![
100 Type::Int1,
101 Type::Int2,
102 Type::Int4,
103 Type::Int8,
104 Type::Int16,
105 Type::Uint1,
106 Type::Uint2,
107 Type::Uint4,
108 Type::Uint8,
109 Type::Uint16,
110 ],
111 actual: min_col.data().get_type(),
112 });
113 }
114 if !is_integer_type(sec_col.data()) {
115 return Err(ScalarFunctionError::InvalidArgumentType {
116 function: ctx.fragment.clone(),
117 argument_index: 2,
118 expected: vec![
119 Type::Int1,
120 Type::Int2,
121 Type::Int4,
122 Type::Int8,
123 Type::Int16,
124 Type::Uint1,
125 Type::Uint2,
126 Type::Uint4,
127 Type::Uint8,
128 Type::Uint16,
129 ],
130 actual: sec_col.data().get_type(),
131 });
132 }
133 if let Some(nc) = &nano_col {
134 if !is_integer_type(nc.data()) {
135 return Err(ScalarFunctionError::InvalidArgumentType {
136 function: ctx.fragment.clone(),
137 argument_index: 3,
138 expected: vec![
139 Type::Int1,
140 Type::Int2,
141 Type::Int4,
142 Type::Int8,
143 Type::Uint1,
144 Type::Uint2,
145 Type::Uint4,
146 ],
147 actual: nc.data().get_type(),
148 });
149 }
150 }
151
152 let mut container = TemporalContainer::with_capacity(row_count);
153
154 for i in 0..row_count {
155 let hour = extract_i32(hour_col.data(), i);
156 let min = extract_i32(min_col.data(), i);
157 let sec = extract_i32(sec_col.data(), i);
158 let nano = if let Some(nc) = &nano_col {
159 extract_i32(nc.data(), i)
160 } else {
161 Some(0)
162 };
163
164 match (hour, min, sec, nano) {
165 (Some(h), Some(m), Some(s), Some(n)) => {
166 if h >= 0 && m >= 0 && s >= 0 && n >= 0 {
167 match Time::new(h as u32, m as u32, s as u32, n as u32) {
168 Some(time) => container.push(time),
169 None => container.push_default(),
170 }
171 } else {
172 container.push_default();
173 }
174 }
175 _ => container.push_default(),
176 }
177 }
178
179 Ok(ColumnData::Time(container))
180 }
181
182 fn return_type(&self, _input_types: &[Type]) -> Type {
183 Type::Time
184 }
185}