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