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;
19use sqlparser::ast::ValueWithSpan;
20pub use sqlparser::ast::{
21    visit_expressions_mut, visit_statements_mut, BinaryOperator, ColumnDef, ColumnOption,
22    ColumnOptionDef, DataType, Expr, Function, FunctionArg, FunctionArgExpr, FunctionArguments,
23    Ident, ObjectName, SqlOption, TableConstraint, TimezoneInfo, UnaryOperator, Value as SqlValue,
24    Visit, VisitMut, Visitor, VisitorMut,
25};
26
27use crate::convert::{sql_number_to_value, sql_value_to_value};
28use crate::error::{Result, UnsupportedDefaultValueSnafu};
29
30pub fn parse_column_default_constraint(
31    column_name: &str,
32    data_type: &ConcreteDataType,
33    opts: &[ColumnOptionDef],
34    timezone: Option<&Timezone>,
35) -> Result<Option<ColumnDefaultConstraint>> {
36    if let Some(opt) = opts
37        .iter()
38        .find(|o| matches!(o.option, ColumnOption::Default(_)))
39    {
40        let default_constraint = match &opt.option {
41            ColumnOption::Default(Expr::Value(v)) => ColumnDefaultConstraint::Value(
42                sql_value_to_value(column_name, data_type, &v.value, timezone, None, false)?,
43            ),
44            ColumnOption::Default(Expr::Function(func)) => {
45                let mut func = format!("{func}").to_lowercase();
46                // normalize CURRENT_TIMESTAMP to CURRENT_TIMESTAMP()
47                if func == CURRENT_TIMESTAMP {
48                    func = CURRENT_TIMESTAMP_FN.to_string();
49                }
50                // Always use lowercase for function expression
51                ColumnDefaultConstraint::Function(func.to_lowercase())
52            }
53
54            ColumnOption::Default(Expr::UnaryOp { op, expr }) => {
55                // Specialized process for handling numerical inputs to prevent
56                // overflow errors during the parsing of negative numbers,
57                // See https://github.com/GreptimeTeam/greptimedb/issues/4351
58                if let (
59                    UnaryOperator::Minus,
60                    Expr::Value(ValueWithSpan {
61                        value: SqlValue::Number(n, _),
62                        span: _,
63                    }),
64                ) = (op, expr.as_ref())
65                {
66                    return Ok(Some(ColumnDefaultConstraint::Value(sql_number_to_value(
67                        data_type,
68                        &format!("-{n}"),
69                    )?)));
70                }
71
72                if let Expr::Value(v) = &**expr {
73                    let value = sql_value_to_value(
74                        column_name,
75                        data_type,
76                        &v.value,
77                        timezone,
78                        Some(*op),
79                        false,
80                    )?;
81                    ColumnDefaultConstraint::Value(value)
82                } else {
83                    return UnsupportedDefaultValueSnafu {
84                        column_name,
85                        expr: *expr.clone(),
86                    }
87                    .fail();
88                }
89            }
90            ColumnOption::Default(others) => {
91                return UnsupportedDefaultValueSnafu {
92                    column_name,
93                    expr: others.clone(),
94                }
95                .fail();
96            }
97            _ => {
98                return UnsupportedDefaultValueSnafu {
99                    column_name,
100                    expr: Expr::Value(SqlValue::Null.into()),
101                }
102                .fail();
103            }
104        };
105
106        Ok(Some(default_constraint))
107    } else {
108        Ok(None)
109    }
110}
111
112#[cfg(test)]
113mod test {
114    use std::assert_matches::assert_matches;
115
116    use datatypes::prelude::{ConcreteDataType, Value};
117    use datatypes::types::BooleanType;
118
119    use super::*;
120
121    #[test]
122    pub fn test_parse_column_default_constraint() {
123        let bool_value = sqlparser::ast::Value::Boolean(true);
124
125        let opts = vec![
126            ColumnOptionDef {
127                name: None,
128                option: ColumnOption::Default(Expr::Value(bool_value.into())),
129            },
130            ColumnOptionDef {
131                name: None,
132                option: ColumnOption::NotNull,
133            },
134        ];
135
136        let constraint = parse_column_default_constraint(
137            "coll",
138            &ConcreteDataType::Boolean(BooleanType),
139            &opts,
140            None,
141        )
142        .unwrap();
143
144        assert_matches!(
145            constraint,
146            Some(ColumnDefaultConstraint::Value(Value::Boolean(true)))
147        );
148
149        // Test negative number
150        let opts = vec![ColumnOptionDef {
151            name: None,
152            option: ColumnOption::Default(Expr::UnaryOp {
153                op: UnaryOperator::Minus,
154                expr: Box::new(Expr::Value(
155                    SqlValue::Number("32768".to_string(), false).into(),
156                )),
157            }),
158        }];
159
160        let constraint = parse_column_default_constraint(
161            "coll",
162            &ConcreteDataType::int16_datatype(),
163            &opts,
164            None,
165        )
166        .unwrap();
167
168        assert_matches!(
169            constraint,
170            Some(ColumnDefaultConstraint::Value(Value::Int16(-32768)))
171        );
172    }
173
174    #[test]
175    fn test_incorrect_default_value_issue_3479() {
176        let opts = vec![ColumnOptionDef {
177            name: None,
178            option: ColumnOption::Default(Expr::Value(
179                SqlValue::Number("0.047318541668048164".into(), false).into(),
180            )),
181        }];
182        let constraint = parse_column_default_constraint(
183            "coll",
184            &ConcreteDataType::float64_datatype(),
185            &opts,
186            None,
187        )
188        .unwrap()
189        .unwrap();
190        assert_eq!("0.047318541668048164", constraint.to_string());
191        let encoded: Vec<u8> = constraint.clone().try_into().unwrap();
192        let decoded = ColumnDefaultConstraint::try_from(encoded.as_ref()).unwrap();
193        assert_eq!(decoded, constraint);
194    }
195}