otter_sql/
value.rs

1//! Values contained in a table cell, its operations and errors.
2use std::{
3    fmt::Display,
4    ops::{Add, Div, Mul, Neg, Not, Rem, Sub},
5};
6
7use ordered_float::OrderedFloat;
8use sqlparser::ast::{self, DataType};
9
10use crate::{
11    expr::{BinOp, UnOp},
12    vm::RuntimeError,
13};
14
15/// A value contained within a table's cell.
16///
17/// One or more [`DataType`] variants may be mapped to a single variant of `Value`.
18#[derive(Debug, PartialEq, PartialOrd, Clone, Eq, Ord)]
19pub enum Value {
20    Null,
21
22    Bool(bool),
23
24    // integer types
25    // reference: https://dev.mysql.com/doc/refman/8.0/en/integer-types.html
26    // TODO: other integer types. Currently, all integers are casted to Int64.
27    Int64(i64),
28
29    // TODO: exact value fixed point types - Decimal and Numeric
30
31    // floating point types
32    // reference: https://dev.mysql.com/doc/refman/8.0/en/floating-point-types.html
33    // note: specifying exact precision and digits is not supported yet
34    // TODO: Float32
35    Float64(OrderedFloat<f64>),
36
37    // TODO: date and timestamp
38
39    // string types
40    // reference: https://dev.mysql.com/doc/refman/8.0/en/string-types.html
41    String(String),
42
43    Binary(Vec<u8>),
44}
45
46impl Display for Value {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        match self {
49            Self::Null => write!(f, "NULL"),
50            Self::Bool(v) => write!(f, "{}", v),
51            Self::Int64(v) => write!(f, "{}", v),
52            Self::Float64(v) => write!(f, "{}", v),
53            Self::String(v) => write!(f, "{}", v),
54            Self::Binary(v) => write!(f, "{:?}", v),
55        }
56    }
57}
58
59impl Value {
60    pub fn is_true(self) -> Result<Value, ValueUnaryOpError> {
61        match self {
62            Value::Bool(lhs) => Ok(Value::Bool(lhs)),
63            _ => Err(ValueUnaryOpError {
64                operator: UnOp::IsTrue,
65                value: self,
66            }),
67        }
68    }
69
70    pub fn is_false(self) -> Result<Value, ValueUnaryOpError> {
71        match self {
72            Value::Bool(lhs) => Ok(Value::Bool(!lhs)),
73            _ => Err(ValueUnaryOpError {
74                operator: UnOp::IsFalse,
75                value: self,
76            }),
77        }
78    }
79
80    pub fn is_null(self) -> Result<Value, ValueUnaryOpError> {
81        match self {
82            Value::Null => Ok(Value::Bool(true)),
83            _ => Ok(Value::Bool(false)),
84        }
85    }
86
87    pub fn is_not_null(self) -> Result<Value, ValueUnaryOpError> {
88        match self {
89            Value::Null => Ok(Value::Bool(false)),
90            _ => Ok(Value::Bool(true)),
91        }
92    }
93
94    pub fn like(self, rhs: Value) -> Result<Value, ValueBinaryOpError> {
95        match (&self, &rhs) {
96            // TODO: implement proper pattern matching
97            (Value::String(lhs), Value::String(rhs)) => Ok(Value::Bool(lhs.contains(rhs))),
98            _ => Err(ValueBinaryOpError {
99                operator: BinOp::Like,
100                values: (self, rhs),
101            }),
102        }
103    }
104
105    pub fn ilike(self, rhs: Value) -> Result<Value, ValueBinaryOpError> {
106        match (&self, &rhs) {
107            // TODO: implement proper pattern matching
108            (Value::String(lhs), Value::String(rhs)) => Ok(Value::Bool(
109                lhs.to_lowercase().contains(&rhs.to_lowercase()),
110            )),
111            _ => Err(ValueBinaryOpError {
112                operator: BinOp::Like,
113                values: (self, rhs),
114            }),
115        }
116    }
117
118    /// Type of data this value is.
119    pub fn data_type(&self) -> DataType {
120        match self {
121            Self::Null => DataType::Int(None),
122            Self::Bool(_) => DataType::Boolean,
123            Self::Int64(_) => DataType::Int(None),
124            Self::Float64(_) => DataType::Float(None),
125            Self::String(_) => DataType::String,
126            Self::Binary(_) => DataType::Bytea,
127        }
128    }
129
130    /// Create a new sentinel value of given type.
131    pub(crate) fn sentinel_value(data_type: &DataType) -> Result<Self, RuntimeError> {
132        Ok(match data_type {
133            DataType::Boolean => Self::Bool(false),
134            DataType::Int(_) => Self::Int64(0),
135            DataType::UnsignedInt(_) => Self::Int64(0),
136            DataType::Float(_) => Self::Float64(0.0.into()),
137            DataType::String => Self::String("".to_owned()),
138            DataType::Bytea => Self::Binary(vec![]),
139            _ => return Err(RuntimeError::UnsupportedType(data_type.clone())),
140        })
141    }
142}
143
144impl TryFrom<ast::Value> for Value {
145    type Error = ValueError;
146
147    fn try_from(val: ast::Value) -> Result<Self, Self::Error> {
148        match val {
149            ast::Value::Null => Ok(Value::Null),
150            ast::Value::Boolean(b) => Ok(Value::Bool(b)),
151            ast::Value::SingleQuotedString(s) => Ok(Value::String(s)),
152            ast::Value::DoubleQuotedString(s) => Ok(Value::String(s)),
153            ast::Value::Number(ref s, _long) => {
154                if let Ok(int) = s.parse::<i64>() {
155                    Ok(Value::Int64(int))
156                } else {
157                    if let Ok(float) = s.parse::<f64>() {
158                        Ok(Value::Float64(float.into()))
159                    } else {
160                        Err(ValueError {
161                            reason: "Unsupported number format",
162                            value: val.clone(),
163                        })
164                    }
165                }
166            }
167            _ => Err(ValueError {
168                reason: "Unsupported value format",
169                value: val,
170            }),
171        }
172    }
173}
174
175impl Add for Value {
176    type Output = Result<Value, ValueBinaryOpError>;
177
178    fn add(self, rhs: Self) -> Self::Output {
179        match self {
180            Value::Null | Value::Bool(_) | Value::String(_) | Value::Binary(_) => {
181                Err(ValueBinaryOpError {
182                    operator: BinOp::Plus,
183                    values: (self, rhs),
184                })
185            }
186            Value::Int64(lhs) => match rhs {
187                Value::Int64(rhs) => Ok(Value::Int64(lhs + rhs)),
188                _ => Err(ValueBinaryOpError {
189                    operator: BinOp::Plus,
190                    values: (self, rhs),
191                }),
192            },
193            Value::Float64(lhs) => match rhs {
194                Value::Float64(rhs) => Ok(Value::Float64(lhs + rhs)),
195                _ => Err(ValueBinaryOpError {
196                    operator: BinOp::Plus,
197                    values: (self, rhs),
198                }),
199            },
200        }
201    }
202}
203
204impl Sub for Value {
205    type Output = Result<Value, ValueBinaryOpError>;
206
207    fn sub(self, rhs: Self) -> Self::Output {
208        match self {
209            Value::Null | Value::Bool(_) | Value::String(_) | Value::Binary(_) => {
210                Err(ValueBinaryOpError {
211                    operator: BinOp::Minus,
212                    values: (self, rhs),
213                })
214            }
215            Value::Int64(lhs) => match rhs {
216                Value::Int64(rhs) => Ok(Value::Int64(lhs - rhs)),
217                _ => Err(ValueBinaryOpError {
218                    operator: BinOp::Minus,
219                    values: (self, rhs),
220                }),
221            },
222            Value::Float64(lhs) => match rhs {
223                Value::Float64(rhs) => Ok(Value::Float64(lhs - rhs)),
224                _ => Err(ValueBinaryOpError {
225                    operator: BinOp::Minus,
226                    values: (self, rhs),
227                }),
228            },
229        }
230    }
231}
232
233impl Mul for Value {
234    type Output = Result<Value, ValueBinaryOpError>;
235
236    fn mul(self, rhs: Self) -> Self::Output {
237        match self {
238            Value::Null | Value::Bool(_) | Value::String(_) | Value::Binary(_) => {
239                Err(ValueBinaryOpError {
240                    operator: BinOp::Multiply,
241                    values: (self, rhs),
242                })
243            }
244            Value::Int64(lhs) => match rhs {
245                Value::Int64(rhs) => Ok(Value::Int64(lhs * rhs)),
246                _ => Err(ValueBinaryOpError {
247                    operator: BinOp::Multiply,
248                    values: (self, rhs),
249                }),
250            },
251            Value::Float64(lhs) => match rhs {
252                Value::Float64(rhs) => Ok(Value::Float64(lhs * rhs)),
253                _ => Err(ValueBinaryOpError {
254                    operator: BinOp::Multiply,
255                    values: (self, rhs),
256                }),
257            },
258        }
259    }
260}
261
262impl Div for Value {
263    type Output = Result<Value, ValueBinaryOpError>;
264
265    fn div(self, rhs: Self) -> Self::Output {
266        match self {
267            Value::Null | Value::Bool(_) | Value::String(_) | Value::Binary(_) => {
268                Err(ValueBinaryOpError {
269                    operator: BinOp::Divide,
270                    values: (self, rhs),
271                })
272            }
273            Value::Int64(lhs) => match rhs {
274                Value::Int64(rhs) => Ok(Value::Int64(lhs / rhs)),
275                _ => Err(ValueBinaryOpError {
276                    operator: BinOp::Divide,
277                    values: (self, rhs),
278                }),
279            },
280            Value::Float64(lhs) => match rhs {
281                Value::Float64(rhs) => Ok(Value::Float64(lhs / rhs)),
282                _ => Err(ValueBinaryOpError {
283                    operator: BinOp::Divide,
284                    values: (self, rhs),
285                }),
286            },
287        }
288    }
289}
290
291impl Rem for Value {
292    type Output = Result<Value, ValueBinaryOpError>;
293
294    fn rem(self, rhs: Self) -> Self::Output {
295        match self {
296            Value::Null | Value::Bool(_) | Value::String(_) | Value::Binary(_) => {
297                Err(ValueBinaryOpError {
298                    operator: BinOp::Modulo,
299                    values: (self, rhs),
300                })
301            }
302            Value::Int64(lhs) => match rhs {
303                Value::Int64(rhs) => Ok(Value::Int64(lhs % rhs)),
304                _ => Err(ValueBinaryOpError {
305                    operator: BinOp::Modulo,
306                    values: (self, rhs),
307                }),
308            },
309            Value::Float64(lhs) => match rhs {
310                Value::Float64(rhs) => Ok(Value::Float64(lhs % rhs)),
311                _ => Err(ValueBinaryOpError {
312                    operator: BinOp::Modulo,
313                    values: (self, rhs),
314                }),
315            },
316        }
317    }
318}
319
320impl Neg for Value {
321    type Output = Result<Value, ValueUnaryOpError>;
322
323    fn neg(self) -> Self::Output {
324        match self {
325            Value::Null | Value::Bool(_) | Value::String(_) | Value::Binary(_) => {
326                Err(ValueUnaryOpError {
327                    operator: UnOp::Minus,
328                    value: self,
329                })
330            }
331            Value::Int64(lhs) => Ok(Value::Int64(-lhs)),
332            Value::Float64(lhs) => Ok(Value::Float64(-lhs)),
333        }
334    }
335}
336
337impl Not for Value {
338    type Output = Result<Value, ValueUnaryOpError>;
339
340    fn not(self) -> Self::Output {
341        match self {
342            Value::Bool(lhs) => Ok(Value::Bool(!lhs)),
343            _ => Err(ValueUnaryOpError {
344                operator: UnOp::Not,
345                value: self,
346            }),
347        }
348    }
349}
350
351/// Error performing a binary operation on two [`Value`]s.
352#[derive(Debug, PartialEq)]
353pub struct ValueBinaryOpError {
354    pub operator: BinOp,
355    pub values: (Value, Value),
356}
357
358impl Display for ValueBinaryOpError {
359    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
360        write!(
361            f,
362            "ValueBinaryOpError: unsupported operation '{}' between '{:?}' and '{:?}'",
363            self.operator, self.values.0, self.values.1
364        )
365    }
366}
367
368/// Error performing a unary operation on a [`Value`].
369#[derive(Debug, PartialEq)]
370pub struct ValueUnaryOpError {
371    pub operator: UnOp,
372    pub value: Value,
373}
374
375impl Display for ValueUnaryOpError {
376    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
377        write!(
378            f,
379            "ValueUnaryOpError: unsupported operation '{}' for '{:?}'",
380            self.operator, self.value
381        )
382    }
383}
384
385/// Error in the value of a [`Value`].
386#[derive(Debug, PartialEq)]
387pub struct ValueError {
388    pub reason: &'static str,
389    pub value: ast::Value,
390}
391
392impl Display for ValueError {
393    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
394        write!(f, "ValueError: {}: {}", self.reason, self.value)
395    }
396}
397
398#[cfg(test)]
399mod tests {
400    use sqlparser::ast;
401
402    use crate::value::ValueError;
403
404    use super::Value;
405
406    #[test]
407    fn create_value() {
408        let value = Value::Null;
409        assert_eq!(value, Value::Null);
410        assert!(value != Value::String("test".to_owned()));
411    }
412
413    #[test]
414    fn conversion_from_ast() {
415        assert_eq!(Value::try_from(ast::Value::Null), Ok(Value::Null));
416
417        assert_eq!(
418            Value::try_from(ast::Value::Number("1000".to_owned(), false)),
419            Ok(Value::Int64(1000))
420        );
421
422        assert_eq!(
423            Value::try_from(ast::Value::Number("1000".to_owned(), true)),
424            Ok(Value::Int64(1000))
425        );
426
427        assert_eq!(
428            Value::try_from(ast::Value::Number("1000.0".to_owned(), false)),
429            Ok(Value::Float64(1000.0.into()))
430        );
431
432        assert_eq!(
433            Value::try_from(ast::Value::Number("0.300000000000000004".to_owned(), false)),
434            Ok(Value::Float64(0.300000000000000004.into()))
435        );
436
437        assert_eq!(
438            Value::try_from(ast::Value::Number("-1".to_owned(), false)),
439            Ok(Value::Int64(-1))
440        );
441
442        assert_eq!(
443            Value::try_from(ast::Value::Number("9223372036854775807".to_owned(), false)),
444            Ok(Value::Int64(9223372036854775807))
445        );
446
447        assert_eq!(
448            Value::try_from(ast::Value::HexStringLiteral("brr".to_owned())),
449            Err(ValueError {
450                reason: "Unsupported value format",
451                value: ast::Value::HexStringLiteral("brr".to_owned())
452            })
453        )
454    }
455}