1use crate::data::datatable::DataValue;
2use crate::sql::functions::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
3use anyhow::{anyhow, Result};
4
5pub struct ToBase;
7
8impl SqlFunction for ToBase {
9 fn signature(&self) -> FunctionSignature {
10 FunctionSignature {
11 name: "TO_BASE",
12 category: FunctionCategory::Mathematical,
13 arg_count: ArgCount::Fixed(2),
14 description: "Convert a number to a string in the specified base (2-36)",
15 returns: "String representation in the specified base",
16 examples: vec![
17 "SELECT TO_BASE(255, 16) -- Returns 'ff'",
18 "SELECT TO_BASE(42, 2) -- Returns '101010'",
19 "SELECT TO_BASE(100, 8) -- Returns '144'",
20 ],
21 }
22 }
23
24 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
25 if args.len() != 2 {
26 return Err(anyhow!("TO_BASE requires exactly 2 arguments"));
27 }
28
29 let number = match &args[0] {
30 DataValue::Integer(i) => *i,
31 DataValue::Float(f) => *f as i64,
32 DataValue::Null => return Ok(DataValue::Null),
33 _ => {
34 return Err(anyhow!(
35 "TO_BASE requires a numeric value as first argument"
36 ))
37 }
38 };
39
40 let base = match &args[1] {
41 DataValue::Integer(i) => *i as i32,
42 DataValue::Float(f) => *f as i32,
43 DataValue::Null => return Ok(DataValue::Null),
44 _ => {
45 return Err(anyhow!(
46 "TO_BASE requires an integer base as second argument"
47 ))
48 }
49 };
50
51 if base < 2 || base > 36 {
52 return Err(anyhow!("Base must be between 2 and 36, got {}", base));
53 }
54
55 if number < 0 {
56 let result = format!("-{}", to_base_string((-number) as u64, base as u32));
58 Ok(DataValue::String(result))
59 } else {
60 let result = to_base_string(number as u64, base as u32);
61 Ok(DataValue::String(result))
62 }
63 }
64}
65
66pub struct FromBase;
68
69impl SqlFunction for FromBase {
70 fn signature(&self) -> FunctionSignature {
71 FunctionSignature {
72 name: "FROM_BASE",
73 category: FunctionCategory::Mathematical,
74 arg_count: ArgCount::Fixed(2),
75 description: "Convert a string in the specified base (2-36) to a number",
76 returns: "Numeric value",
77 examples: vec![
78 "SELECT FROM_BASE('ff', 16) -- Returns 255",
79 "SELECT FROM_BASE('101010', 2) -- Returns 42",
80 "SELECT FROM_BASE('144', 8) -- Returns 100",
81 ],
82 }
83 }
84
85 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
86 if args.len() != 2 {
87 return Err(anyhow!("FROM_BASE requires exactly 2 arguments"));
88 }
89
90 let string = match &args[0] {
91 DataValue::String(s) => s.trim(),
92 DataValue::Null => return Ok(DataValue::Null),
93 _ => return Err(anyhow!("FROM_BASE requires a string as first argument")),
94 };
95
96 let base = match &args[1] {
97 DataValue::Integer(i) => *i as u32,
98 DataValue::Float(f) => *f as u32,
99 DataValue::Null => return Ok(DataValue::Null),
100 _ => {
101 return Err(anyhow!(
102 "FROM_BASE requires an integer base as second argument"
103 ))
104 }
105 };
106
107 if base < 2 || base > 36 {
108 return Err(anyhow!("Base must be between 2 and 36, got {}", base));
109 }
110
111 let (is_negative, string) = if string.starts_with('-') {
113 (true, &string[1..])
114 } else {
115 (false, string)
116 };
117
118 match i64::from_str_radix(string, base) {
119 Ok(n) => {
120 let result = if is_negative { -n } else { n };
121 Ok(DataValue::Integer(result))
122 }
123 Err(e) => Err(anyhow!("Invalid {} base number '{}': {}", base, string, e)),
124 }
125 }
126}
127
128pub struct ToBinary;
130
131impl SqlFunction for ToBinary {
132 fn signature(&self) -> FunctionSignature {
133 FunctionSignature {
134 name: "TO_BINARY",
135 category: FunctionCategory::Mathematical,
136 arg_count: ArgCount::Fixed(1),
137 description: "Convert a number to binary string representation",
138 returns: "Binary string",
139 examples: vec![
140 "SELECT TO_BINARY(42) -- Returns '101010'",
141 "SELECT TO_BINARY(255) -- Returns '11111111'",
142 "SELECT TO_BINARY(-5) -- Returns '-101'",
143 ],
144 }
145 }
146
147 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
148 if args.len() != 1 {
149 return Err(anyhow!("TO_BINARY requires exactly 1 argument"));
150 }
151
152 let to_base = ToBase;
153 to_base.evaluate(&[args[0].clone(), DataValue::Integer(2)])
154 }
155}
156
157pub struct FromBinary;
159
160impl SqlFunction for FromBinary {
161 fn signature(&self) -> FunctionSignature {
162 FunctionSignature {
163 name: "FROM_BINARY",
164 category: FunctionCategory::Mathematical,
165 arg_count: ArgCount::Fixed(1),
166 description: "Convert a binary string to a number",
167 returns: "Numeric value",
168 examples: vec![
169 "SELECT FROM_BINARY('101010') -- Returns 42",
170 "SELECT FROM_BINARY('11111111') -- Returns 255",
171 "SELECT FROM_BINARY('-101') -- Returns -5",
172 ],
173 }
174 }
175
176 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
177 if args.len() != 1 {
178 return Err(anyhow!("FROM_BINARY requires exactly 1 argument"));
179 }
180
181 let from_base = FromBase;
182 from_base.evaluate(&[args[0].clone(), DataValue::Integer(2)])
183 }
184}
185
186pub struct ToHex;
188
189impl SqlFunction for ToHex {
190 fn signature(&self) -> FunctionSignature {
191 FunctionSignature {
192 name: "TO_HEX",
193 category: FunctionCategory::Mathematical,
194 arg_count: ArgCount::Fixed(1),
195 description: "Convert a number to hexadecimal string representation",
196 returns: "Hexadecimal string",
197 examples: vec![
198 "SELECT TO_HEX(255) -- Returns 'ff'",
199 "SELECT TO_HEX(4095) -- Returns 'fff'",
200 "SELECT TO_HEX(16777215) -- Returns 'ffffff'",
201 ],
202 }
203 }
204
205 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
206 if args.len() != 1 {
207 return Err(anyhow!("TO_HEX requires exactly 1 argument"));
208 }
209
210 let to_base = ToBase;
211 to_base.evaluate(&[args[0].clone(), DataValue::Integer(16)])
212 }
213}
214
215pub struct FromHex;
217
218impl SqlFunction for FromHex {
219 fn signature(&self) -> FunctionSignature {
220 FunctionSignature {
221 name: "FROM_HEX",
222 category: FunctionCategory::Mathematical,
223 arg_count: ArgCount::Fixed(1),
224 description: "Convert a hexadecimal string to a number",
225 returns: "Numeric value",
226 examples: vec![
227 "SELECT FROM_HEX('ff') -- Returns 255",
228 "SELECT FROM_HEX('fff') -- Returns 4095",
229 "SELECT FROM_HEX('ffffff') -- Returns 16777215",
230 ],
231 }
232 }
233
234 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
235 if args.len() != 1 {
236 return Err(anyhow!("FROM_HEX requires exactly 1 argument"));
237 }
238
239 let from_base = FromBase;
240 from_base.evaluate(&[args[0].clone(), DataValue::Integer(16)])
241 }
242}
243
244pub struct ToOctal;
246
247impl SqlFunction for ToOctal {
248 fn signature(&self) -> FunctionSignature {
249 FunctionSignature {
250 name: "TO_OCTAL",
251 category: FunctionCategory::Mathematical,
252 arg_count: ArgCount::Fixed(1),
253 description: "Convert a number to octal string representation",
254 returns: "Octal string",
255 examples: vec![
256 "SELECT TO_OCTAL(8) -- Returns '10'",
257 "SELECT TO_OCTAL(64) -- Returns '100'",
258 "SELECT TO_OCTAL(511) -- Returns '777'",
259 ],
260 }
261 }
262
263 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
264 if args.len() != 1 {
265 return Err(anyhow!("TO_OCTAL requires exactly 1 argument"));
266 }
267
268 let to_base = ToBase;
269 to_base.evaluate(&[args[0].clone(), DataValue::Integer(8)])
270 }
271}
272
273pub struct FromOctal;
275
276impl SqlFunction for FromOctal {
277 fn signature(&self) -> FunctionSignature {
278 FunctionSignature {
279 name: "FROM_OCTAL",
280 category: FunctionCategory::Mathematical,
281 arg_count: ArgCount::Fixed(1),
282 description: "Convert an octal string to a number",
283 returns: "Numeric value",
284 examples: vec![
285 "SELECT FROM_OCTAL('10') -- Returns 8",
286 "SELECT FROM_OCTAL('100') -- Returns 64",
287 "SELECT FROM_OCTAL('777') -- Returns 511",
288 ],
289 }
290 }
291
292 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
293 if args.len() != 1 {
294 return Err(anyhow!("FROM_OCTAL requires exactly 1 argument"));
295 }
296
297 let from_base = FromBase;
298 from_base.evaluate(&[args[0].clone(), DataValue::Integer(8)])
299 }
300}
301
302fn to_base_string(mut num: u64, base: u32) -> String {
304 if num == 0 {
305 return "0".to_string();
306 }
307
308 const DIGITS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
309 let mut result = Vec::new();
310
311 while num > 0 {
312 let remainder = (num % base as u64) as usize;
313 result.push(DIGITS[remainder]);
314 num /= base as u64;
315 }
316
317 result.reverse();
318 String::from_utf8(result).unwrap()
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn test_to_base() {
327 let func = ToBase;
328
329 assert_eq!(
331 func.evaluate(&[DataValue::Integer(42), DataValue::Integer(2)])
332 .unwrap(),
333 DataValue::String("101010".to_string())
334 );
335
336 assert_eq!(
338 func.evaluate(&[DataValue::Integer(255), DataValue::Integer(16)])
339 .unwrap(),
340 DataValue::String("ff".to_string())
341 );
342
343 assert_eq!(
345 func.evaluate(&[DataValue::Integer(64), DataValue::Integer(8)])
346 .unwrap(),
347 DataValue::String("100".to_string())
348 );
349
350 assert_eq!(
352 func.evaluate(&[DataValue::Integer(1295), DataValue::Integer(36)])
353 .unwrap(),
354 DataValue::String("zz".to_string())
355 );
356 }
357
358 #[test]
359 fn test_from_base() {
360 let func = FromBase;
361
362 assert_eq!(
364 func.evaluate(&[
365 DataValue::String("101010".to_string()),
366 DataValue::Integer(2)
367 ])
368 .unwrap(),
369 DataValue::Integer(42)
370 );
371
372 assert_eq!(
374 func.evaluate(&[DataValue::String("ff".to_string()), DataValue::Integer(16)])
375 .unwrap(),
376 DataValue::Integer(255)
377 );
378
379 assert_eq!(
381 func.evaluate(&[DataValue::String("100".to_string()), DataValue::Integer(8)])
382 .unwrap(),
383 DataValue::Integer(64)
384 );
385 }
386
387 #[test]
388 fn test_binary_functions() {
389 let to_bin = ToBinary;
390 let from_bin = FromBinary;
391
392 assert_eq!(
393 to_bin.evaluate(&[DataValue::Integer(42)]).unwrap(),
394 DataValue::String("101010".to_string())
395 );
396
397 assert_eq!(
398 from_bin
399 .evaluate(&[DataValue::String("101010".to_string())])
400 .unwrap(),
401 DataValue::Integer(42)
402 );
403 }
404
405 #[test]
406 fn test_hex_functions() {
407 let to_hex = ToHex;
408 let from_hex = FromHex;
409
410 assert_eq!(
411 to_hex.evaluate(&[DataValue::Integer(255)]).unwrap(),
412 DataValue::String("ff".to_string())
413 );
414
415 assert_eq!(
416 from_hex
417 .evaluate(&[DataValue::String("ff".to_string())])
418 .unwrap(),
419 DataValue::Integer(255)
420 );
421 }
422}