common_sql/
default_constraint.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use common_time::timezone::Timezone;
16use datatypes::prelude::ConcreteDataType;
17use datatypes::schema::constraint::{CURRENT_TIMESTAMP, CURRENT_TIMESTAMP_FN};
18use datatypes::schema::ColumnDefaultConstraint;
19pub use sqlparser::ast::{
20    visit_expressions_mut, visit_statements_mut, BinaryOperator, ColumnDef, ColumnOption,
21    ColumnOptionDef, DataType, Expr, Function, FunctionArg, FunctionArgExpr, FunctionArguments,
22    Ident, ObjectName, SqlOption, TableConstraint, TimezoneInfo, UnaryOperator, Value as SqlValue,
23    Visit, VisitMut, Visitor, VisitorMut,
24};
25
26use crate::convert::{sql_number_to_value, sql_value_to_value};
27use crate::error::{Result, UnsupportedDefaultValueSnafu};
28
29pub fn parse_column_default_constraint(
30    column_name: &str,
31    data_type: &ConcreteDataType,
32    opts: &[ColumnOptionDef],
33    timezone: Option<&Timezone>,
34) -> Result<Option<ColumnDefaultConstraint>> {
35    if let Some(opt) = opts
36        .iter()
37        .find(|o| matches!(o.option, ColumnOption::Default(_)))
38    {
39        let default_constraint = match &opt.option {
40            ColumnOption::Default(Expr::Value(v)) => ColumnDefaultConstraint::Value(
41                sql_value_to_value(column_name, data_type, v, timezone, None, false)?,
42            ),
43            ColumnOption::Default(Expr::Function(func)) => {
44                let mut func = format!("{func}").to_lowercase();
45                // normalize CURRENT_TIMESTAMP to CURRENT_TIMESTAMP()
46                if func == CURRENT_TIMESTAMP {
47                    func = CURRENT_TIMESTAMP_FN.to_string();
48                }
49                // Always use lowercase for function expression
50                ColumnDefaultConstraint::Function(func.to_lowercase())
51            }
52
53            ColumnOption::Default(Expr::UnaryOp { op, expr }) => {
54                // Specialized process for handling numerical inputs to prevent
55                // overflow errors during the parsing of negative numbers,
56                // See https://github.com/GreptimeTeam/greptimedb/issues/4351
57                if let (UnaryOperator::Minus, Expr::Value(SqlValue::Number(n, _))) =
58                    (op, expr.as_ref())
59                {
60                    return Ok(Some(ColumnDefaultConstraint::Value(sql_number_to_value(
61                        data_type,
62                        &format!("-{n}"),
63                    )?)));
64                }
65
66                if let Expr::Value(v) = &**expr {
67                    let value =
68                        sql_value_to_value(column_name, data_type, v, timezone, Some(*op), false)?;
69                    ColumnDefaultConstraint::Value(value)
70                } else {
71                    return UnsupportedDefaultValueSnafu {
72                        column_name,
73                        expr: *expr.clone(),
74                    }
75                    .fail();
76                }
77            }
78            ColumnOption::Default(others) => {
79                return UnsupportedDefaultValueSnafu {
80                    column_name,
81                    expr: others.clone(),
82                }
83                .fail();
84            }
85            _ => {
86                return UnsupportedDefaultValueSnafu {
87                    column_name,
88                    expr: Expr::Value(SqlValue::Null),
89                }
90                .fail();
91            }
92        };
93
94        Ok(Some(default_constraint))
95    } else {
96        Ok(None)
97    }
98}
99
100#[cfg(test)]
101mod test {
102    use std::assert_matches::assert_matches;
103
104    use datatypes::prelude::{ConcreteDataType, Value};
105    use datatypes::types::BooleanType;
106
107    use super::*;
108
109    #[test]
110    pub fn test_parse_column_default_constraint() {
111        let bool_value = sqlparser::ast::Value::Boolean(true);
112
113        let opts = vec![
114            ColumnOptionDef {
115                name: None,
116                option: ColumnOption::Default(Expr::Value(bool_value)),
117            },
118            ColumnOptionDef {
119                name: None,
120                option: ColumnOption::NotNull,
121            },
122        ];
123
124        let constraint = parse_column_default_constraint(
125            "coll",
126            &ConcreteDataType::Boolean(BooleanType),
127            &opts,
128            None,
129        )
130        .unwrap();
131
132        assert_matches!(
133            constraint,
134            Some(ColumnDefaultConstraint::Value(Value::Boolean(true)))
135        );
136
137        // Test negative number
138        let opts = vec![ColumnOptionDef {
139            name: None,
140            option: ColumnOption::Default(Expr::UnaryOp {
141                op: UnaryOperator::Minus,
142                expr: Box::new(Expr::Value(SqlValue::Number("32768".to_string(), false))),
143            }),
144        }];
145
146        let constraint = parse_column_default_constraint(
147            "coll",
148            &ConcreteDataType::int16_datatype(),
149            &opts,
150            None,
151        )
152        .unwrap();
153
154        assert_matches!(
155            constraint,
156            Some(ColumnDefaultConstraint::Value(Value::Int16(-32768)))
157        );
158    }
159
160    #[test]
161    fn test_incorrect_default_value_issue_3479() {
162        let opts = vec![ColumnOptionDef {
163            name: None,
164            option: ColumnOption::Default(Expr::Value(SqlValue::Number(
165                "0.047318541668048164".into(),
166                false,
167            ))),
168        }];
169        let constraint = parse_column_default_constraint(
170            "coll",
171            &ConcreteDataType::float64_datatype(),
172            &opts,
173            None,
174        )
175        .unwrap()
176        .unwrap();
177        assert_eq!("0.047318541668048164", constraint.to_string());
178        let encoded: Vec<u8> = constraint.clone().try_into().unwrap();
179        let decoded = ColumnDefaultConstraint::try_from(encoded.as_ref()).unwrap();
180        assert_eq!(decoded, constraint);
181    }
182}