1use anyhow::{anyhow, Result};
2use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
3
4use crate::data::datatable::DataValue;
5use crate::sql::functions::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
6
7pub struct FormatNumberFunction;
9
10impl SqlFunction for FormatNumberFunction {
11 fn signature(&self) -> FunctionSignature {
12 FunctionSignature {
13 name: "FORMAT_NUMBER",
14 category: FunctionCategory::String,
15 arg_count: ArgCount::Range(1, 3),
16 description: "Format a number with decimal places and thousand separators",
17 returns: "STRING",
18 examples: vec![
19 "SELECT FORMAT_NUMBER(1234567.89, 2)", "SELECT FORMAT_NUMBER(1234.5, 2, false)", "SELECT FORMAT_NUMBER(1234567)", ],
23 }
24 }
25
26 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
27 self.validate_args(args)?;
28
29 let value = match &args[0] {
30 DataValue::Integer(n) => *n as f64,
31 DataValue::Float(f) => *f,
32 DataValue::Null => return Ok(DataValue::Null),
33 _ => return Err(anyhow!("FORMAT_NUMBER requires a numeric argument")),
34 };
35
36 let decimals = if args.len() >= 2 {
37 match &args[1] {
38 DataValue::Integer(n) => *n as usize,
39 DataValue::Float(f) => *f as usize,
40 _ => 2,
41 }
42 } else {
43 0
44 };
45
46 let use_separator = if args.len() >= 3 {
47 match &args[2] {
48 DataValue::Boolean(b) => *b,
49 DataValue::Integer(n) => *n != 0,
50 _ => true,
51 }
52 } else {
53 true
54 };
55
56 let formatted = if decimals > 0 {
58 format!("{:.prec$}", value, prec = decimals)
59 } else {
60 format!("{:.0}", value)
61 };
62
63 let result = if use_separator {
65 add_thousand_separators(&formatted)
66 } else {
67 formatted
68 };
69
70 Ok(DataValue::String(result))
71 }
72}
73
74fn add_thousand_separators(s: &str) -> String {
75 let parts: Vec<&str> = s.split('.').collect();
76 let integer_part = parts[0];
77 let decimal_part = parts.get(1);
78
79 let mut result = String::new();
80 let mut count = 0;
81
82 for ch in integer_part.chars().rev() {
83 if count > 0 && count % 3 == 0 && ch != '-' {
84 result.push(',');
85 }
86 result.push(ch);
87 if ch != '-' {
88 count += 1;
89 }
90 }
91
92 let formatted_integer: String = result.chars().rev().collect();
93
94 if let Some(dec) = decimal_part {
95 format!("{}.{}", formatted_integer, dec)
96 } else {
97 formatted_integer
98 }
99}
100
101pub struct LPadFunction;
103
104impl SqlFunction for LPadFunction {
105 fn signature(&self) -> FunctionSignature {
106 FunctionSignature {
107 name: "LPAD",
108 category: FunctionCategory::String,
109 arg_count: ArgCount::Range(2, 3),
110 description: "Left pad a string to a specified length with a fill character",
111 returns: "STRING",
112 examples: vec![
113 "SELECT LPAD('123', 5)", "SELECT LPAD('123', 5, '0')", "SELECT LPAD('hello', 10, '.')", ],
117 }
118 }
119
120 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
121 self.validate_args(args)?;
122
123 let text = match &args[0] {
124 DataValue::String(s) => s.clone(),
125 DataValue::Integer(n) => n.to_string(),
126 DataValue::Float(f) => f.to_string(),
127 DataValue::Null => return Ok(DataValue::Null),
128 _ => return Err(anyhow!("LPAD requires a string or numeric argument")),
129 };
130
131 let length = match &args[1] {
132 DataValue::Integer(n) if *n >= 0 => *n as usize,
133 _ => return Err(anyhow!("LPAD length must be a non-negative integer")),
134 };
135
136 let pad_char = if args.len() >= 3 {
137 match &args[2] {
138 DataValue::String(s) if !s.is_empty() => s.chars().next().unwrap(),
139 _ => ' ',
140 }
141 } else {
142 ' '
143 };
144
145 if text.len() >= length {
146 Ok(DataValue::String(text[..length].to_string()))
147 } else {
148 let padding = pad_char.to_string().repeat(length - text.len());
149 Ok(DataValue::String(format!("{}{}", padding, text)))
150 }
151 }
152}
153
154pub struct RPadFunction;
156
157impl SqlFunction for RPadFunction {
158 fn signature(&self) -> FunctionSignature {
159 FunctionSignature {
160 name: "RPAD",
161 category: FunctionCategory::String,
162 arg_count: ArgCount::Range(2, 3),
163 description: "Right pad a string to a specified length with a fill character",
164 returns: "STRING",
165 examples: vec![
166 "SELECT RPAD('123', 5)", "SELECT RPAD('123', 5, '0')", "SELECT RPAD('hello', 10, '.')", ],
170 }
171 }
172
173 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
174 self.validate_args(args)?;
175
176 let text = match &args[0] {
177 DataValue::String(s) => s.clone(),
178 DataValue::Integer(n) => n.to_string(),
179 DataValue::Float(f) => f.to_string(),
180 DataValue::Null => return Ok(DataValue::Null),
181 _ => return Err(anyhow!("RPAD requires a string or numeric argument")),
182 };
183
184 let length = match &args[1] {
185 DataValue::Integer(n) if *n >= 0 => *n as usize,
186 _ => return Err(anyhow!("RPAD length must be a non-negative integer")),
187 };
188
189 let pad_char = if args.len() >= 3 {
190 match &args[2] {
191 DataValue::String(s) if !s.is_empty() => s.chars().next().unwrap(),
192 _ => ' ',
193 }
194 } else {
195 ' '
196 };
197
198 if text.len() >= length {
199 Ok(DataValue::String(text[..length].to_string()))
200 } else {
201 let padding = pad_char.to_string().repeat(length - text.len());
202 Ok(DataValue::String(format!("{}{}", text, padding)))
203 }
204 }
205}
206
207pub struct CenterFunction;
209
210impl SqlFunction for CenterFunction {
211 fn signature(&self) -> FunctionSignature {
212 FunctionSignature {
213 name: "CENTER",
214 category: FunctionCategory::String,
215 arg_count: ArgCount::Range(2, 3),
216 description: "Center a string within a specified width",
217 returns: "STRING",
218 examples: vec![
219 "SELECT CENTER('hello', 11)", "SELECT CENTER('test', 10, '.')", "SELECT CENTER('SQL', 7, '-')", ],
223 }
224 }
225
226 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
227 self.validate_args(args)?;
228
229 let text = match &args[0] {
230 DataValue::String(s) => s.clone(),
231 DataValue::Integer(n) => n.to_string(),
232 DataValue::Float(f) => f.to_string(),
233 DataValue::Null => return Ok(DataValue::Null),
234 _ => return Err(anyhow!("CENTER requires a string or numeric argument")),
235 };
236
237 let width = match &args[1] {
238 DataValue::Integer(n) if *n >= 0 => *n as usize,
239 _ => return Err(anyhow!("CENTER width must be a non-negative integer")),
240 };
241
242 let pad_char = if args.len() >= 3 {
243 match &args[2] {
244 DataValue::String(s) if !s.is_empty() => s.chars().next().unwrap(),
245 _ => ' ',
246 }
247 } else {
248 ' '
249 };
250
251 if text.len() >= width {
252 Ok(DataValue::String(text[..width].to_string()))
253 } else {
254 let total_padding = width - text.len();
255 let left_padding = total_padding / 2;
256 let right_padding = total_padding - left_padding;
257
258 let left = pad_char.to_string().repeat(left_padding);
259 let right = pad_char.to_string().repeat(right_padding);
260
261 Ok(DataValue::String(format!("{}{}{}", left, text, right)))
262 }
263 }
264}
265
266pub struct FormatDateFunction;
268
269impl SqlFunction for FormatDateFunction {
270 fn signature(&self) -> FunctionSignature {
271 FunctionSignature {
272 name: "FORMAT_DATE",
273 category: FunctionCategory::Date,
274 arg_count: ArgCount::Fixed(2),
275 description: "Format a date using a format string",
276 returns: "STRING",
277 examples: vec![
278 "SELECT FORMAT_DATE(NOW(), '%Y-%m-%d')", "SELECT FORMAT_DATE(NOW(), '%B %d, %Y')", "SELECT FORMAT_DATE(NOW(), '%Y%m%d_%H%M%S')", ],
282 }
283 }
284
285 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
286 self.validate_args(args)?;
287
288 let datetime_str = match &args[0] {
289 DataValue::DateTime(dt) => dt,
290 DataValue::String(s) => s, DataValue::Null => return Ok(DataValue::Null),
292 _ => {
293 return Err(anyhow!(
294 "FORMAT_DATE requires a datetime or string argument"
295 ))
296 }
297 };
298
299 let format_str = match &args[1] {
300 DataValue::String(s) => s,
301 _ => return Err(anyhow!("FORMAT_DATE format must be a string")),
302 };
303
304 let formatted = if let Ok(dt) = datetime_str.parse::<DateTime<Utc>>() {
307 dt.format(format_str).to_string()
308 } else if let Ok(dt) = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%d %H:%M:%S") {
309 dt.format(format_str).to_string()
310 } else if let Ok(dt) = NaiveDate::parse_from_str(datetime_str, "%Y-%m-%d") {
311 dt.format(format_str).to_string()
312 } else if let Ok(dt) = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%dT%H:%M:%S") {
313 dt.format(format_str).to_string()
314 } else {
315 return Err(anyhow!("Cannot parse datetime: {}", datetime_str));
316 };
317
318 Ok(DataValue::String(formatted))
319 }
320}