1use featherdb_core::{Error, Result, Value};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum BinaryOp {
12 Add,
14 Sub,
15 Mul,
16 Div,
17 Mod,
18
19 Eq,
21 Ne,
22 Lt,
23 Le,
24 Gt,
25 Ge,
26
27 And,
29 Or,
30
31 Like,
33 Concat,
34}
35
36impl BinaryOp {
37 pub fn eval(&self, left: &Value, right: &Value) -> Result<Value> {
39 use Value::*;
40
41 match (left, right) {
45 (Null, _) | (_, Null) => match self {
46 BinaryOp::Add
47 | BinaryOp::Sub
48 | BinaryOp::Mul
49 | BinaryOp::Div
50 | BinaryOp::Mod
51 | BinaryOp::Concat => return Ok(Null),
52 BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge | BinaryOp::Like => {
53 return Ok(Null)
54 }
55 BinaryOp::Eq => return Ok(Boolean(matches!((left, right), (Null, Null)))),
56 BinaryOp::Ne => return Ok(Boolean(!matches!((left, right), (Null, Null)))),
57 BinaryOp::And => match (left, right) {
58 (Boolean(false), _) | (_, Boolean(false)) => return Ok(Boolean(false)),
59 _ => return Ok(Null),
60 },
61 BinaryOp::Or => match (left, right) {
62 (Boolean(true), _) | (_, Boolean(true)) => return Ok(Boolean(true)),
63 _ => return Ok(Null),
64 },
65 },
66 _ => {}
67 }
68
69 match self {
70 BinaryOp::Add => match (left, right) {
72 (Integer(a), Integer(b)) => Ok(Integer(a + b)),
73 (Real(a), Real(b)) => Ok(Real(a + b)),
74 (Integer(a), Real(b)) => Ok(Real(*a as f64 + b)),
75 (Real(a), Integer(b)) => Ok(Real(a + *b as f64)),
76 (Text(date), Text(interval)) if Self::looks_like_interval(interval) => {
78 Self::date_add(date, interval)
79 }
80 _ => Self::type_error("add", left, right),
81 },
82 BinaryOp::Sub => match (left, right) {
83 (Integer(a), Integer(b)) => Ok(Integer(a - b)),
84 (Real(a), Real(b)) => Ok(Real(a - b)),
85 (Integer(a), Real(b)) => Ok(Real(*a as f64 - b)),
86 (Real(a), Integer(b)) => Ok(Real(a - *b as f64)),
87 (Text(date), Text(interval)) if Self::looks_like_interval(interval) => {
89 Self::date_sub(date, interval)
90 }
91 _ => Self::type_error("subtract", left, right),
92 },
93 BinaryOp::Mul => match (left, right) {
94 (Integer(a), Integer(b)) => Ok(Integer(a * b)),
95 (Real(a), Real(b)) => Ok(Real(a * b)),
96 (Integer(a), Real(b)) => Ok(Real(*a as f64 * b)),
97 (Real(a), Integer(b)) => Ok(Real(a * *b as f64)),
98 _ => Self::type_error("multiply", left, right),
99 },
100 BinaryOp::Div => match (left, right) {
101 (Integer(a), Integer(b)) => {
102 if *b == 0 {
103 Err(Error::Internal("Division by zero".into()))
104 } else {
105 Ok(Integer(a / b))
106 }
107 }
108 (Real(a), Real(b)) => {
109 if *b == 0.0 {
110 Err(Error::Internal("Division by zero".into()))
111 } else {
112 Ok(Real(a / b))
113 }
114 }
115 (Integer(a), Real(b)) => {
116 if *b == 0.0 {
117 Err(Error::Internal("Division by zero".into()))
118 } else {
119 Ok(Real(*a as f64 / b))
120 }
121 }
122 (Real(a), Integer(b)) => {
123 if *b == 0 {
124 Err(Error::Internal("Division by zero".into()))
125 } else {
126 Ok(Real(a / *b as f64))
127 }
128 }
129 _ => Self::type_error("divide", left, right),
130 },
131 BinaryOp::Mod => match (left, right) {
132 (Integer(a), Integer(b)) => {
133 if *b == 0 {
134 Err(Error::Internal("Modulo by zero".into()))
135 } else {
136 Ok(Integer(a % b))
137 }
138 }
139 _ => Self::type_error("modulo", left, right),
140 },
141
142 BinaryOp::Eq => Ok(Boolean(left == right)),
144 BinaryOp::Ne => Ok(Boolean(left != right)),
145 BinaryOp::Lt => Ok(Boolean(left < right)),
146 BinaryOp::Le => Ok(Boolean(left <= right)),
147 BinaryOp::Gt => Ok(Boolean(left > right)),
148 BinaryOp::Ge => Ok(Boolean(left >= right)),
149
150 BinaryOp::And => match (left, right) {
152 (Boolean(a), Boolean(b)) => Ok(Boolean(*a && *b)),
153 _ => Self::type_error("AND", left, right),
154 },
155 BinaryOp::Or => match (left, right) {
156 (Boolean(a), Boolean(b)) => Ok(Boolean(*a || *b)),
157 _ => Self::type_error("OR", left, right),
158 },
159
160 BinaryOp::Like => match (left, right) {
162 (Text(text), Text(pattern)) => {
163 Ok(Boolean(super::helpers::like_match(text, pattern)))
164 }
165 _ => Self::type_error("LIKE", left, right),
166 },
167 BinaryOp::Concat => match (left, right) {
168 (Text(a), Text(b)) => Ok(Text(format!("{}{}", a, b))),
169 _ => Self::type_error("CONCAT", left, right),
170 },
171 }
172 }
173
174 fn type_error<T>(op: &str, left: &Value, right: &Value) -> Result<T> {
175 Err(Error::Internal(format!(
176 "Type mismatch for {}: cannot apply to {:?} and {:?}",
177 op, left, right
178 )))
179 }
180
181 fn looks_like_interval(s: &str) -> bool {
183 let parts: Vec<&str> = s.split_whitespace().collect();
184 if parts.len() != 2 {
185 return false;
186 }
187 parts[0].parse::<i64>().is_ok()
188 && matches!(
189 parts[1].to_uppercase().as_str(),
190 "DAY"
191 | "DAYS"
192 | "MONTH"
193 | "MONTHS"
194 | "YEAR"
195 | "YEARS"
196 | "HOUR"
197 | "HOURS"
198 | "MINUTE"
199 | "MINUTES"
200 | "SECOND"
201 | "SECONDS"
202 )
203 }
204
205 fn parse_interval(interval: &str) -> Result<(i64, String)> {
207 let parts: Vec<&str> = interval.split_whitespace().collect();
208 let n = parts[0]
209 .parse::<i64>()
210 .map_err(|_| Error::Internal(format!("Invalid interval number: {}", parts[0])))?;
211 Ok((n, parts[1].to_uppercase()))
212 }
213
214 fn parse_date(date: &str) -> Result<(i32, u32, u32)> {
216 let date_part = date.split_whitespace().next().unwrap_or(date);
217 let parts: Vec<&str> = date_part.split('-').collect();
218 if parts.len() < 3 {
219 return Err(Error::Internal(format!("Invalid date format: {}", date)));
220 }
221 let y = parts[0]
222 .parse::<i32>()
223 .map_err(|_| Error::Internal(format!("Invalid year: {}", parts[0])))?;
224 let m = parts[1]
225 .parse::<u32>()
226 .map_err(|_| Error::Internal(format!("Invalid month: {}", parts[1])))?;
227 let d = parts[2]
228 .parse::<u32>()
229 .map_err(|_| Error::Internal(format!("Invalid day: {}", parts[2])))?;
230 Ok((y, m, d))
231 }
232
233 fn days_in_month(year: i32, month: u32) -> u32 {
235 match month {
236 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
237 4 | 6 | 9 | 11 => 30,
238 2 => {
239 if (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 {
240 29
241 } else {
242 28
243 }
244 }
245 _ => 30,
246 }
247 }
248
249 fn date_add(date: &str, interval: &str) -> Result<Value> {
251 let (n, unit) = Self::parse_interval(interval)?;
252 let (mut y, mut m, mut d) = Self::parse_date(date)?;
253
254 match unit.as_str() {
255 "YEAR" | "YEARS" => {
256 y += n as i32;
257 d = d.min(Self::days_in_month(y, m));
258 }
259 "MONTH" | "MONTHS" => {
260 let total_months = (y as i64) * 12 + (m as i64 - 1) + n;
261 y = (total_months / 12) as i32;
262 m = (total_months % 12 + 1) as u32;
263 if m == 0 {
264 m = 12;
265 y -= 1;
266 }
267 d = d.min(Self::days_in_month(y, m));
268 }
269 "DAY" | "DAYS" => {
270 let days = Self::date_to_days(y, m, d) + n;
272 let (ny, nm, nd) = Self::days_to_date(days);
273 y = ny;
274 m = nm;
275 d = nd;
276 let _ = days;
277 }
278 _ => {
279 return Err(Error::Unsupported {
280 feature: format!("Interval unit: {}", unit),
281 })
282 }
283 }
284
285 Ok(Value::Text(format!("{:04}-{:02}-{:02}", y, m, d)))
286 }
287
288 fn date_sub(date: &str, interval: &str) -> Result<Value> {
290 let (n, unit) = Self::parse_interval(interval)?;
291 let negated = format!("{} {}", -n, unit);
293 Self::date_add(date, &negated)
294 }
295
296 fn date_to_days(y: i32, m: u32, d: u32) -> i64 {
299 let y = y as i64;
300 let m = m as i64;
301 let d = d as i64;
302 let yr = if m <= 2 { y - 1 } else { y };
304 let era = if yr >= 0 { yr } else { yr - 399 } / 400;
305 let yoe = yr - era * 400; let mo = if m > 2 { m - 3 } else { m + 9 }; let doy = (153 * mo + 2) / 5 + d - 1; let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; era * 146097 + doe - 719468 }
311
312 fn days_to_date(days: i64) -> (i32, u32, u32) {
315 let z = days + 719468;
316 let era = if z >= 0 { z } else { z - 146096 } / 146097;
317 let doe = z - era * 146097; let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
319 let y = yoe + era * 400;
320 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
321 let mp = (5 * doy + 2) / 153;
322 let d = doy - (153 * mp + 2) / 5 + 1;
323 let m = if mp < 10 { mp + 3 } else { mp - 9 };
324 let y = if m <= 2 { y + 1 } else { y };
325 (y as i32, m as u32, d as u32)
326 }
327}
328
329#[derive(Debug, Clone, Copy, PartialEq, Eq)]
335pub enum UnaryOp {
336 Negate,
338 Not,
340 IsNull,
342 IsNotNull,
344}
345
346impl UnaryOp {
347 pub fn eval(&self, value: &Value) -> Result<Value> {
349 match self {
350 UnaryOp::Negate => match value {
351 Value::Null => Ok(Value::Null),
352 Value::Integer(n) => Ok(Value::Integer(-n)),
353 Value::Real(f) => Ok(Value::Real(-f)),
354 _ => Err(Error::Internal(format!(
355 "Cannot negate non-numeric value: {:?}",
356 value
357 ))),
358 },
359 UnaryOp::Not => match value {
360 Value::Null => Ok(Value::Null),
361 Value::Boolean(b) => Ok(Value::Boolean(!b)),
362 _ => Err(Error::Internal(format!(
363 "Cannot apply NOT to non-boolean value: {:?}",
364 value
365 ))),
366 },
367 UnaryOp::IsNull => Ok(Value::Boolean(matches!(value, Value::Null))),
368 UnaryOp::IsNotNull => Ok(Value::Boolean(!matches!(value, Value::Null))),
369 }
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
379 fn test_binary_op_add() {
380 assert_eq!(
381 BinaryOp::Add
382 .eval(&Value::Integer(2), &Value::Integer(3))
383 .unwrap(),
384 Value::Integer(5)
385 );
386 assert_eq!(
387 BinaryOp::Add
388 .eval(&Value::Real(2.5), &Value::Real(3.5))
389 .unwrap(),
390 Value::Real(6.0)
391 );
392 assert_eq!(
393 BinaryOp::Add
394 .eval(&Value::Integer(2), &Value::Real(3.5))
395 .unwrap(),
396 Value::Real(5.5)
397 );
398 }
399
400 #[test]
401 fn test_binary_op_subtract() {
402 assert_eq!(
403 BinaryOp::Sub
404 .eval(&Value::Integer(5), &Value::Integer(3))
405 .unwrap(),
406 Value::Integer(2)
407 );
408 assert_eq!(
409 BinaryOp::Sub
410 .eval(&Value::Real(5.5), &Value::Real(2.5))
411 .unwrap(),
412 Value::Real(3.0)
413 );
414 }
415
416 #[test]
417 fn test_binary_op_multiply() {
418 assert_eq!(
419 BinaryOp::Mul
420 .eval(&Value::Integer(2), &Value::Integer(3))
421 .unwrap(),
422 Value::Integer(6)
423 );
424 assert_eq!(
425 BinaryOp::Mul
426 .eval(&Value::Real(2.5), &Value::Real(2.0))
427 .unwrap(),
428 Value::Real(5.0)
429 );
430 }
431
432 #[test]
433 fn test_binary_op_divide() {
434 assert_eq!(
435 BinaryOp::Div
436 .eval(&Value::Integer(6), &Value::Integer(3))
437 .unwrap(),
438 Value::Integer(2)
439 );
440 assert_eq!(
441 BinaryOp::Div
442 .eval(&Value::Real(5.0), &Value::Real(2.0))
443 .unwrap(),
444 Value::Real(2.5)
445 );
446
447 assert!(BinaryOp::Div
449 .eval(&Value::Integer(5), &Value::Integer(0))
450 .is_err());
451 assert!(BinaryOp::Div
452 .eval(&Value::Real(5.0), &Value::Real(0.0))
453 .is_err());
454 }
455
456 #[test]
457 fn test_binary_op_modulo() {
458 assert_eq!(
459 BinaryOp::Mod
460 .eval(&Value::Integer(7), &Value::Integer(3))
461 .unwrap(),
462 Value::Integer(1)
463 );
464
465 assert!(BinaryOp::Mod
467 .eval(&Value::Integer(5), &Value::Integer(0))
468 .is_err());
469 }
470
471 #[test]
472 fn test_binary_op_comparison() {
473 assert_eq!(
474 BinaryOp::Eq
475 .eval(&Value::Integer(5), &Value::Integer(5))
476 .unwrap(),
477 Value::Boolean(true)
478 );
479 assert_eq!(
480 BinaryOp::Ne
481 .eval(&Value::Integer(5), &Value::Integer(3))
482 .unwrap(),
483 Value::Boolean(true)
484 );
485 assert_eq!(
486 BinaryOp::Lt
487 .eval(&Value::Integer(3), &Value::Integer(5))
488 .unwrap(),
489 Value::Boolean(true)
490 );
491 assert_eq!(
492 BinaryOp::Le
493 .eval(&Value::Integer(5), &Value::Integer(5))
494 .unwrap(),
495 Value::Boolean(true)
496 );
497 assert_eq!(
498 BinaryOp::Gt
499 .eval(&Value::Integer(7), &Value::Integer(5))
500 .unwrap(),
501 Value::Boolean(true)
502 );
503 assert_eq!(
504 BinaryOp::Ge
505 .eval(&Value::Integer(5), &Value::Integer(5))
506 .unwrap(),
507 Value::Boolean(true)
508 );
509 }
510
511 #[test]
512 fn test_binary_op_logical() {
513 assert_eq!(
514 BinaryOp::And
515 .eval(&Value::Boolean(true), &Value::Boolean(true))
516 .unwrap(),
517 Value::Boolean(true)
518 );
519 assert_eq!(
520 BinaryOp::And
521 .eval(&Value::Boolean(true), &Value::Boolean(false))
522 .unwrap(),
523 Value::Boolean(false)
524 );
525 assert_eq!(
526 BinaryOp::Or
527 .eval(&Value::Boolean(true), &Value::Boolean(false))
528 .unwrap(),
529 Value::Boolean(true)
530 );
531 assert_eq!(
532 BinaryOp::Or
533 .eval(&Value::Boolean(false), &Value::Boolean(false))
534 .unwrap(),
535 Value::Boolean(false)
536 );
537 }
538
539 #[test]
540 fn test_binary_op_like() {
541 assert_eq!(
542 BinaryOp::Like
543 .eval(&Value::Text("hello".into()), &Value::Text("h%".into()))
544 .unwrap(),
545 Value::Boolean(true)
546 );
547 assert_eq!(
548 BinaryOp::Like
549 .eval(&Value::Text("hello".into()), &Value::Text("%lo".into()))
550 .unwrap(),
551 Value::Boolean(true)
552 );
553 assert_eq!(
554 BinaryOp::Like
555 .eval(&Value::Text("hello".into()), &Value::Text("h_llo".into()))
556 .unwrap(),
557 Value::Boolean(true)
558 );
559 }
560
561 #[test]
563 fn test_unary_op_negate() {
564 assert_eq!(
565 UnaryOp::Negate.eval(&Value::Integer(5)).unwrap(),
566 Value::Integer(-5)
567 );
568 assert_eq!(
569 UnaryOp::Negate.eval(&Value::Real(2.5)).unwrap(),
570 Value::Real(-2.5)
571 );
572 assert!(UnaryOp::Negate.eval(&Value::Text("hello".into())).is_err());
573 }
574
575 #[test]
576 fn test_unary_op_not() {
577 assert_eq!(
578 UnaryOp::Not.eval(&Value::Boolean(true)).unwrap(),
579 Value::Boolean(false)
580 );
581 assert_eq!(
582 UnaryOp::Not.eval(&Value::Boolean(false)).unwrap(),
583 Value::Boolean(true)
584 );
585 assert!(UnaryOp::Not.eval(&Value::Integer(5)).is_err());
586 }
587
588 #[test]
589 fn test_unary_op_is_null() {
590 assert_eq!(
591 UnaryOp::IsNull.eval(&Value::Null).unwrap(),
592 Value::Boolean(true)
593 );
594 assert_eq!(
595 UnaryOp::IsNull.eval(&Value::Integer(5)).unwrap(),
596 Value::Boolean(false)
597 );
598 }
599
600 #[test]
601 fn test_unary_op_is_not_null() {
602 assert_eq!(
603 UnaryOp::IsNotNull.eval(&Value::Null).unwrap(),
604 Value::Boolean(false)
605 );
606 assert_eq!(
607 UnaryOp::IsNotNull.eval(&Value::Integer(5)).unwrap(),
608 Value::Boolean(true)
609 );
610 }
611}