sql/
statements.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
15pub mod admin;
16pub mod alter;
17pub mod copy;
18pub mod create;
19pub mod cursor;
20pub mod delete;
21pub mod describe;
22pub mod drop;
23pub mod explain;
24pub mod insert;
25pub mod kill;
26mod option_map;
27pub mod query;
28pub mod set_variables;
29pub mod show;
30pub mod statement;
31pub mod tql;
32pub(crate) mod transform;
33pub mod truncate;
34
35use std::sync::Arc;
36
37use api::helper::ColumnDataTypeWrapper;
38use api::v1::SemanticType;
39use common_sql::default_constraint::parse_column_default_constraint;
40use common_time::timezone::Timezone;
41use datatypes::extension::json::{JsonExtensionType, JsonMetadata};
42use datatypes::prelude::ConcreteDataType;
43use datatypes::schema::{COMMENT_KEY, ColumnDefaultConstraint, ColumnSchema};
44use datatypes::types::json_type::JsonNativeType;
45use datatypes::types::{JsonFormat, JsonType, TimestampType};
46use datatypes::value::Value;
47use snafu::ResultExt;
48use sqlparser::ast::{ExactNumberInfo, Ident};
49
50use crate::ast::{
51    ColumnDef, ColumnOption, DataType as SqlDataType, ObjectNamePartExt, TimezoneInfo,
52    Value as SqlValue,
53};
54use crate::error::{
55    self, ConvertToGrpcDataTypeSnafu, ConvertValueSnafu, Result,
56    SerializeColumnDefaultConstraintSnafu, SetFulltextOptionSnafu, SetJsonStructureSettingsSnafu,
57    SetSkippingIndexOptionSnafu, SqlCommonSnafu,
58};
59use crate::statements::create::{Column, ColumnExtensions};
60pub use crate::statements::option_map::OptionMap;
61pub(crate) use crate::statements::transform::transform_statements;
62
63const VECTOR_TYPE_NAME: &str = "VECTOR";
64
65pub fn value_to_sql_value(val: &Value) -> Result<SqlValue> {
66    Ok(match val {
67        Value::Int8(v) => SqlValue::Number(v.to_string(), false),
68        Value::UInt8(v) => SqlValue::Number(v.to_string(), false),
69        Value::Int16(v) => SqlValue::Number(v.to_string(), false),
70        Value::UInt16(v) => SqlValue::Number(v.to_string(), false),
71        Value::Int32(v) => SqlValue::Number(v.to_string(), false),
72        Value::UInt32(v) => SqlValue::Number(v.to_string(), false),
73        Value::Int64(v) => SqlValue::Number(v.to_string(), false),
74        Value::UInt64(v) => SqlValue::Number(v.to_string(), false),
75        Value::Float32(v) => SqlValue::Number(v.to_string(), false),
76        Value::Float64(v) => SqlValue::Number(v.to_string(), false),
77        Value::Boolean(b) => SqlValue::Boolean(*b),
78        Value::Date(d) => SqlValue::SingleQuotedString(d.to_string()),
79        Value::Timestamp(ts) => SqlValue::SingleQuotedString(ts.to_iso8601_string()),
80        Value::String(s) => SqlValue::SingleQuotedString(s.as_utf8().to_string()),
81        Value::Null => SqlValue::Null,
82        // TODO(dennis): supports binary
83        _ => return ConvertValueSnafu { value: val.clone() }.fail(),
84    })
85}
86
87/// Return true when the `ColumnDef` options contain primary key
88pub fn has_primary_key_option(column_def: &ColumnDef) -> bool {
89    column_def
90        .options
91        .iter()
92        .any(|options| match options.option {
93            ColumnOption::Unique { is_primary, .. } => is_primary,
94            _ => false,
95        })
96}
97
98/// Create a `ColumnSchema` from `Column`.
99pub fn column_to_schema(
100    column: &Column,
101    time_index: &str,
102    timezone: Option<&Timezone>,
103) -> Result<ColumnSchema> {
104    let is_time_index = column.name().value == time_index;
105
106    let is_nullable = column
107        .options()
108        .iter()
109        .all(|o| !matches!(o.option, ColumnOption::NotNull))
110        && !is_time_index;
111
112    let name = column.name().value.clone();
113    let data_type = sql_data_type_to_concrete_data_type(column.data_type(), &column.extensions)?;
114    let default_constraint =
115        parse_column_default_constraint(&name, &data_type, column.options(), timezone)
116            .context(SqlCommonSnafu)?;
117
118    let mut column_schema = ColumnSchema::new(name, data_type, is_nullable)
119        .with_time_index(is_time_index)
120        .with_default_constraint(default_constraint)
121        .context(error::InvalidDefaultSnafu {
122            column: &column.name().value,
123        })?;
124
125    if let Some(ColumnOption::Comment(c)) = column.options().iter().find_map(|o| {
126        if matches!(o.option, ColumnOption::Comment(_)) {
127            Some(&o.option)
128        } else {
129            None
130        }
131    }) {
132        let _ = column_schema
133            .mut_metadata()
134            .insert(COMMENT_KEY.to_string(), c.clone());
135    }
136
137    if let Some(options) = column.extensions.build_fulltext_options()? {
138        column_schema = column_schema
139            .with_fulltext_options(options)
140            .context(SetFulltextOptionSnafu)?;
141    }
142
143    if let Some(options) = column.extensions.build_skipping_index_options()? {
144        column_schema = column_schema
145            .with_skipping_options(options)
146            .context(SetSkippingIndexOptionSnafu)?;
147    }
148
149    column_schema.set_inverted_index(column.extensions.inverted_index_options.is_some());
150
151    if matches!(column.data_type(), SqlDataType::JSON) {
152        let settings = column
153            .extensions
154            .build_json_structure_settings()?
155            .unwrap_or_default();
156        let extension = JsonExtensionType::new(Arc::new(JsonMetadata {
157            json_structure_settings: Some(settings.clone()),
158        }));
159        column_schema
160            .with_extension_type(&extension)
161            .with_context(|_| SetJsonStructureSettingsSnafu {
162                value: format!("{settings:?}"),
163            })?;
164    }
165
166    Ok(column_schema)
167}
168
169/// Convert `ColumnDef` in sqlparser to `ColumnDef` in gRPC proto.
170pub fn sql_column_def_to_grpc_column_def(
171    col: &ColumnDef,
172    timezone: Option<&Timezone>,
173) -> Result<api::v1::ColumnDef> {
174    let name = col.name.value.clone();
175    let data_type = sql_data_type_to_concrete_data_type(&col.data_type, &Default::default())?;
176
177    let is_nullable = col
178        .options
179        .iter()
180        .all(|o| !matches!(o.option, ColumnOption::NotNull));
181
182    let default_constraint =
183        parse_column_default_constraint(&name, &data_type, &col.options, timezone)
184            .context(SqlCommonSnafu)?
185            .map(ColumnDefaultConstraint::try_into) // serialize default constraint to bytes
186            .transpose()
187            .context(SerializeColumnDefaultConstraintSnafu)?;
188    // convert ConcreteDataType to grpc ColumnDataTypeWrapper
189    let (datatype, datatype_ext) = ColumnDataTypeWrapper::try_from(data_type.clone())
190        .context(ConvertToGrpcDataTypeSnafu)?
191        .to_parts();
192
193    let is_primary_key = col.options.iter().any(|o| {
194        matches!(
195            o.option,
196            ColumnOption::Unique {
197                is_primary: true,
198                ..
199            }
200        )
201    });
202
203    let semantic_type = if is_primary_key {
204        SemanticType::Tag
205    } else {
206        SemanticType::Field
207    };
208
209    Ok(api::v1::ColumnDef {
210        name,
211        data_type: datatype as i32,
212        is_nullable,
213        default_constraint: default_constraint.unwrap_or_default(),
214        semantic_type: semantic_type as _,
215        comment: String::new(),
216        datatype_extension: datatype_ext,
217        options: None,
218    })
219}
220
221pub fn sql_data_type_to_concrete_data_type(
222    data_type: &SqlDataType,
223    column_extensions: &ColumnExtensions,
224) -> Result<ConcreteDataType> {
225    match data_type {
226        SqlDataType::BigInt(_) | SqlDataType::Int64 => Ok(ConcreteDataType::int64_datatype()),
227        SqlDataType::BigIntUnsigned(_) => Ok(ConcreteDataType::uint64_datatype()),
228        SqlDataType::Int(_) | SqlDataType::Integer(_) => Ok(ConcreteDataType::int32_datatype()),
229        SqlDataType::IntUnsigned(_) | SqlDataType::UnsignedInteger => {
230            Ok(ConcreteDataType::uint32_datatype())
231        }
232        SqlDataType::SmallInt(_) => Ok(ConcreteDataType::int16_datatype()),
233        SqlDataType::SmallIntUnsigned(_) => Ok(ConcreteDataType::uint16_datatype()),
234        SqlDataType::TinyInt(_) | SqlDataType::Int8(_) => Ok(ConcreteDataType::int8_datatype()),
235        SqlDataType::TinyIntUnsigned(_) | SqlDataType::Int8Unsigned(_) => {
236            Ok(ConcreteDataType::uint8_datatype())
237        }
238        SqlDataType::Char(_)
239        | SqlDataType::Varchar(_)
240        | SqlDataType::Text
241        | SqlDataType::TinyText
242        | SqlDataType::MediumText
243        | SqlDataType::LongText
244        | SqlDataType::String(_) => Ok(ConcreteDataType::string_datatype()),
245        SqlDataType::Float(_) => Ok(ConcreteDataType::float32_datatype()),
246        SqlDataType::Double(_) | SqlDataType::Float64 => Ok(ConcreteDataType::float64_datatype()),
247        SqlDataType::Boolean => Ok(ConcreteDataType::boolean_datatype()),
248        SqlDataType::Date => Ok(ConcreteDataType::date_datatype()),
249        SqlDataType::Binary(_)
250        | SqlDataType::Blob(_)
251        | SqlDataType::Bytea
252        | SqlDataType::Varbinary(_) => Ok(ConcreteDataType::binary_datatype()),
253        SqlDataType::Datetime(_) => Ok(ConcreteDataType::timestamp_microsecond_datatype()),
254        SqlDataType::Timestamp(precision, _) => Ok(precision
255            .as_ref()
256            .map(|v| TimestampType::try_from(*v))
257            .transpose()
258            .map_err(|_| {
259                error::SqlTypeNotSupportedSnafu {
260                    t: data_type.clone(),
261                }
262                .build()
263            })?
264            .map(|t| ConcreteDataType::timestamp_datatype(t.unit()))
265            .unwrap_or(ConcreteDataType::timestamp_millisecond_datatype())),
266        SqlDataType::Interval => Ok(ConcreteDataType::interval_month_day_nano_datatype()),
267        SqlDataType::Decimal(exact_info) => match exact_info {
268            ExactNumberInfo::None => Ok(ConcreteDataType::decimal128_default_datatype()),
269            // refer to https://dev.mysql.com/doc/refman/8.0/en/fixed-point-types.html
270            // In standard SQL, the syntax DECIMAL(M) is equivalent to DECIMAL(M,0).
271            ExactNumberInfo::Precision(p) => Ok(ConcreteDataType::decimal128_datatype(*p as u8, 0)),
272            ExactNumberInfo::PrecisionAndScale(p, s) => {
273                Ok(ConcreteDataType::decimal128_datatype(*p as u8, *s as i8))
274            }
275        },
276        SqlDataType::JSON => {
277            let format = if column_extensions.json_datatype_options.is_some() {
278                JsonFormat::Native(Box::new(JsonNativeType::Null))
279            } else {
280                JsonFormat::Jsonb
281            };
282            Ok(ConcreteDataType::Json(JsonType::new(format)))
283        }
284        // Vector type
285        SqlDataType::Custom(name, d)
286            if name.0.as_slice().len() == 1
287                && name.0.as_slice()[0]
288                    .to_string_unquoted()
289                    .to_ascii_uppercase()
290                    == VECTOR_TYPE_NAME
291                && d.len() == 1 =>
292        {
293            let dim = d[0].parse().map_err(|e| {
294                error::ParseSqlValueSnafu {
295                    msg: format!("Failed to parse vector dimension: {}", e),
296                }
297                .build()
298            })?;
299            Ok(ConcreteDataType::vector_datatype(dim))
300        }
301        _ => error::SqlTypeNotSupportedSnafu {
302            t: data_type.clone(),
303        }
304        .fail(),
305    }
306}
307
308pub fn concrete_data_type_to_sql_data_type(data_type: &ConcreteDataType) -> Result<SqlDataType> {
309    match data_type {
310        ConcreteDataType::Int64(_) => Ok(SqlDataType::BigInt(None)),
311        ConcreteDataType::UInt64(_) => Ok(SqlDataType::BigIntUnsigned(None)),
312        ConcreteDataType::Int32(_) => Ok(SqlDataType::Int(None)),
313        ConcreteDataType::UInt32(_) => Ok(SqlDataType::IntUnsigned(None)),
314        ConcreteDataType::Int16(_) => Ok(SqlDataType::SmallInt(None)),
315        ConcreteDataType::UInt16(_) => Ok(SqlDataType::SmallIntUnsigned(None)),
316        ConcreteDataType::Int8(_) => Ok(SqlDataType::TinyInt(None)),
317        ConcreteDataType::UInt8(_) => Ok(SqlDataType::TinyIntUnsigned(None)),
318        ConcreteDataType::String(_) => Ok(SqlDataType::String(None)),
319        ConcreteDataType::Float32(_) => Ok(SqlDataType::Float(None)),
320        ConcreteDataType::Float64(_) => Ok(SqlDataType::Double(ExactNumberInfo::None)),
321        ConcreteDataType::Boolean(_) => Ok(SqlDataType::Boolean),
322        ConcreteDataType::Date(_) => Ok(SqlDataType::Date),
323        ConcreteDataType::Timestamp(ts_type) => Ok(SqlDataType::Timestamp(
324            Some(ts_type.precision()),
325            TimezoneInfo::None,
326        )),
327        ConcreteDataType::Time(time_type) => Ok(SqlDataType::Time(
328            Some(time_type.precision()),
329            TimezoneInfo::None,
330        )),
331        ConcreteDataType::Interval(_) => Ok(SqlDataType::Interval),
332        ConcreteDataType::Binary(_) => Ok(SqlDataType::Varbinary(None)),
333        ConcreteDataType::Decimal128(d) => Ok(SqlDataType::Decimal(
334            ExactNumberInfo::PrecisionAndScale(d.precision() as u64, d.scale() as u64),
335        )),
336        ConcreteDataType::Json(_) => Ok(SqlDataType::JSON),
337        ConcreteDataType::Vector(v) => Ok(SqlDataType::Custom(
338            vec![Ident::new(VECTOR_TYPE_NAME)].into(),
339            vec![v.dim.to_string()],
340        )),
341        ConcreteDataType::Duration(_)
342        | ConcreteDataType::Null(_)
343        | ConcreteDataType::List(_)
344        | ConcreteDataType::Struct(_)
345        | ConcreteDataType::Dictionary(_) => error::ConcreteTypeNotSupportedSnafu {
346            t: data_type.clone(),
347        }
348        .fail(),
349    }
350}
351
352#[cfg(test)]
353mod tests {
354    use api::v1::ColumnDataType;
355    use datatypes::schema::{
356        COLUMN_FULLTEXT_OPT_KEY_ANALYZER, COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, FulltextAnalyzer,
357    };
358    use sqlparser::ast::{ColumnOptionDef, Expr};
359
360    use super::*;
361    use crate::ast::TimezoneInfo;
362    use crate::statements::ColumnOption;
363    use crate::statements::create::ColumnExtensions;
364
365    fn check_type(sql_type: SqlDataType, data_type: ConcreteDataType) {
366        assert_eq!(
367            data_type,
368            sql_data_type_to_concrete_data_type(&sql_type, &Default::default()).unwrap()
369        );
370    }
371
372    #[test]
373    pub fn test_sql_data_type_to_concrete_data_type() {
374        check_type(
375            SqlDataType::BigInt(None),
376            ConcreteDataType::int64_datatype(),
377        );
378        check_type(SqlDataType::Int(None), ConcreteDataType::int32_datatype());
379        check_type(
380            SqlDataType::Integer(None),
381            ConcreteDataType::int32_datatype(),
382        );
383        check_type(
384            SqlDataType::SmallInt(None),
385            ConcreteDataType::int16_datatype(),
386        );
387        check_type(SqlDataType::Char(None), ConcreteDataType::string_datatype());
388        check_type(
389            SqlDataType::Varchar(None),
390            ConcreteDataType::string_datatype(),
391        );
392        check_type(SqlDataType::Text, ConcreteDataType::string_datatype());
393        check_type(
394            SqlDataType::String(None),
395            ConcreteDataType::string_datatype(),
396        );
397        check_type(
398            SqlDataType::Float(None),
399            ConcreteDataType::float32_datatype(),
400        );
401        check_type(
402            SqlDataType::Double(ExactNumberInfo::None),
403            ConcreteDataType::float64_datatype(),
404        );
405        check_type(SqlDataType::Boolean, ConcreteDataType::boolean_datatype());
406        check_type(SqlDataType::Date, ConcreteDataType::date_datatype());
407        check_type(
408            SqlDataType::Timestamp(None, TimezoneInfo::None),
409            ConcreteDataType::timestamp_millisecond_datatype(),
410        );
411        check_type(
412            SqlDataType::Varbinary(None),
413            ConcreteDataType::binary_datatype(),
414        );
415        check_type(
416            SqlDataType::BigIntUnsigned(None),
417            ConcreteDataType::uint64_datatype(),
418        );
419        check_type(
420            SqlDataType::IntUnsigned(None),
421            ConcreteDataType::uint32_datatype(),
422        );
423        check_type(
424            SqlDataType::SmallIntUnsigned(None),
425            ConcreteDataType::uint16_datatype(),
426        );
427        check_type(
428            SqlDataType::TinyIntUnsigned(None),
429            ConcreteDataType::uint8_datatype(),
430        );
431        check_type(
432            SqlDataType::Datetime(None),
433            ConcreteDataType::timestamp_microsecond_datatype(),
434        );
435        check_type(
436            SqlDataType::Interval,
437            ConcreteDataType::interval_month_day_nano_datatype(),
438        );
439        check_type(SqlDataType::JSON, ConcreteDataType::json_datatype());
440        check_type(
441            SqlDataType::Custom(
442                vec![Ident::new(VECTOR_TYPE_NAME)].into(),
443                vec!["3".to_string()],
444            ),
445            ConcreteDataType::vector_datatype(3),
446        );
447    }
448
449    #[test]
450    pub fn test_sql_column_def_to_grpc_column_def() {
451        // test basic
452        let column_def = ColumnDef {
453            name: "col".into(),
454            data_type: SqlDataType::Double(ExactNumberInfo::None),
455            options: vec![],
456        };
457
458        let grpc_column_def = sql_column_def_to_grpc_column_def(&column_def, None).unwrap();
459
460        assert_eq!("col", grpc_column_def.name);
461        assert!(grpc_column_def.is_nullable); // nullable when options are empty
462        assert_eq!(ColumnDataType::Float64 as i32, grpc_column_def.data_type);
463        assert!(grpc_column_def.default_constraint.is_empty());
464        assert_eq!(grpc_column_def.semantic_type, SemanticType::Field as i32);
465
466        // test not null
467        let column_def = ColumnDef {
468            name: "col".into(),
469            data_type: SqlDataType::Double(ExactNumberInfo::None),
470            options: vec![ColumnOptionDef {
471                name: None,
472                option: ColumnOption::NotNull,
473            }],
474        };
475
476        let grpc_column_def = sql_column_def_to_grpc_column_def(&column_def, None).unwrap();
477        assert!(!grpc_column_def.is_nullable);
478
479        // test primary key
480        let column_def = ColumnDef {
481            name: "col".into(),
482            data_type: SqlDataType::Double(ExactNumberInfo::None),
483            options: vec![ColumnOptionDef {
484                name: None,
485                option: ColumnOption::Unique {
486                    is_primary: true,
487                    characteristics: None,
488                },
489            }],
490        };
491
492        let grpc_column_def = sql_column_def_to_grpc_column_def(&column_def, None).unwrap();
493        assert_eq!(grpc_column_def.semantic_type, SemanticType::Tag as i32);
494    }
495
496    #[test]
497    pub fn test_sql_column_def_to_grpc_column_def_with_timezone() {
498        let column_def = ColumnDef {
499            name: "col".into(),
500            // MILLISECOND
501            data_type: SqlDataType::Timestamp(Some(3), TimezoneInfo::None),
502            options: vec![ColumnOptionDef {
503                name: None,
504                option: ColumnOption::Default(Expr::Value(
505                    SqlValue::SingleQuotedString("2024-01-30T00:01:01".to_string()).into(),
506                )),
507            }],
508        };
509
510        // with timezone "Asia/Shanghai"
511        let grpc_column_def = sql_column_def_to_grpc_column_def(
512            &column_def,
513            Some(&Timezone::from_tz_string("Asia/Shanghai").unwrap()),
514        )
515        .unwrap();
516        assert_eq!("col", grpc_column_def.name);
517        assert!(grpc_column_def.is_nullable); // nullable when options are empty
518        assert_eq!(
519            ColumnDataType::TimestampMillisecond as i32,
520            grpc_column_def.data_type
521        );
522        assert!(!grpc_column_def.default_constraint.is_empty());
523
524        let constraint =
525            ColumnDefaultConstraint::try_from(&grpc_column_def.default_constraint[..]).unwrap();
526        assert!(
527            matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
528                         if ts.to_iso8601_string() == "2024-01-29 16:01:01+0000")
529        );
530
531        // without timezone
532        let grpc_column_def = sql_column_def_to_grpc_column_def(&column_def, None).unwrap();
533        assert_eq!("col", grpc_column_def.name);
534        assert!(grpc_column_def.is_nullable); // nullable when options are empty
535        assert_eq!(
536            ColumnDataType::TimestampMillisecond as i32,
537            grpc_column_def.data_type
538        );
539        assert!(!grpc_column_def.default_constraint.is_empty());
540
541        let constraint =
542            ColumnDefaultConstraint::try_from(&grpc_column_def.default_constraint[..]).unwrap();
543        assert!(
544            matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
545                         if ts.to_iso8601_string() == "2024-01-30 00:01:01+0000")
546        );
547    }
548
549    #[test]
550    pub fn test_has_primary_key_option() {
551        let column_def = ColumnDef {
552            name: "col".into(),
553            data_type: SqlDataType::Double(ExactNumberInfo::None),
554            options: vec![],
555        };
556        assert!(!has_primary_key_option(&column_def));
557
558        let column_def = ColumnDef {
559            name: "col".into(),
560            data_type: SqlDataType::Double(ExactNumberInfo::None),
561            options: vec![ColumnOptionDef {
562                name: None,
563                option: ColumnOption::Unique {
564                    is_primary: true,
565                    characteristics: None,
566                },
567            }],
568        };
569        assert!(has_primary_key_option(&column_def));
570    }
571
572    #[test]
573    pub fn test_column_to_schema() {
574        let column_def = Column {
575            column_def: ColumnDef {
576                name: "col".into(),
577                data_type: SqlDataType::Double(ExactNumberInfo::None),
578                options: vec![],
579            },
580            extensions: ColumnExtensions::default(),
581        };
582
583        let column_schema = column_to_schema(&column_def, "ts", None).unwrap();
584
585        assert_eq!("col", column_schema.name);
586        assert_eq!(
587            ConcreteDataType::float64_datatype(),
588            column_schema.data_type
589        );
590        assert!(column_schema.is_nullable());
591        assert!(!column_schema.is_time_index());
592
593        let column_schema = column_to_schema(&column_def, "col", None).unwrap();
594
595        assert_eq!("col", column_schema.name);
596        assert_eq!(
597            ConcreteDataType::float64_datatype(),
598            column_schema.data_type
599        );
600        assert!(!column_schema.is_nullable());
601        assert!(column_schema.is_time_index());
602
603        let column_def = Column {
604            column_def: ColumnDef {
605                name: "col2".into(),
606                data_type: SqlDataType::String(None),
607                options: vec![
608                    ColumnOptionDef {
609                        name: None,
610                        option: ColumnOption::NotNull,
611                    },
612                    ColumnOptionDef {
613                        name: None,
614                        option: ColumnOption::Comment("test comment".to_string()),
615                    },
616                ],
617            },
618            extensions: ColumnExtensions::default(),
619        };
620
621        let column_schema = column_to_schema(&column_def, "ts", None).unwrap();
622
623        assert_eq!("col2", column_schema.name);
624        assert_eq!(ConcreteDataType::string_datatype(), column_schema.data_type);
625        assert!(!column_schema.is_nullable());
626        assert!(!column_schema.is_time_index());
627        assert_eq!(
628            column_schema.metadata().get(COMMENT_KEY),
629            Some(&"test comment".to_string())
630        );
631    }
632
633    #[test]
634    pub fn test_column_to_schema_timestamp_with_timezone() {
635        let column = Column {
636            column_def: ColumnDef {
637                name: "col".into(),
638                // MILLISECOND
639                data_type: SqlDataType::Timestamp(Some(3), TimezoneInfo::None),
640                options: vec![ColumnOptionDef {
641                    name: None,
642                    option: ColumnOption::Default(Expr::Value(
643                        SqlValue::SingleQuotedString("2024-01-30T00:01:01".to_string()).into(),
644                    )),
645                }],
646            },
647            extensions: ColumnExtensions::default(),
648        };
649
650        // with timezone "Asia/Shanghai"
651
652        let column_schema = column_to_schema(
653            &column,
654            "ts",
655            Some(&Timezone::from_tz_string("Asia/Shanghai").unwrap()),
656        )
657        .unwrap();
658
659        assert_eq!("col", column_schema.name);
660        assert_eq!(
661            ConcreteDataType::timestamp_millisecond_datatype(),
662            column_schema.data_type
663        );
664        assert!(column_schema.is_nullable());
665
666        let constraint = column_schema.default_constraint().unwrap();
667        assert!(
668            matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
669                         if ts.to_iso8601_string() == "2024-01-29 16:01:01+0000")
670        );
671
672        // without timezone
673        let column_schema = column_to_schema(&column, "ts", None).unwrap();
674
675        assert_eq!("col", column_schema.name);
676        assert_eq!(
677            ConcreteDataType::timestamp_millisecond_datatype(),
678            column_schema.data_type
679        );
680        assert!(column_schema.is_nullable());
681
682        let constraint = column_schema.default_constraint().unwrap();
683        assert!(
684            matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
685                         if ts.to_iso8601_string() == "2024-01-30 00:01:01+0000")
686        );
687    }
688
689    #[test]
690    fn test_column_to_schema_with_fulltext() {
691        let column = Column {
692            column_def: ColumnDef {
693                name: "col".into(),
694                data_type: SqlDataType::Text,
695                options: vec![],
696            },
697            extensions: ColumnExtensions {
698                fulltext_index_options: Some(OptionMap::from([
699                    (
700                        COLUMN_FULLTEXT_OPT_KEY_ANALYZER.to_string(),
701                        "English".to_string(),
702                    ),
703                    (
704                        COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE.to_string(),
705                        "true".to_string(),
706                    ),
707                ])),
708                vector_options: None,
709                skipping_index_options: None,
710                inverted_index_options: None,
711                json_datatype_options: None,
712            },
713        };
714
715        let column_schema = column_to_schema(&column, "ts", None).unwrap();
716        assert_eq!("col", column_schema.name);
717        assert_eq!(ConcreteDataType::string_datatype(), column_schema.data_type);
718        let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
719        assert_eq!(fulltext_options.analyzer, FulltextAnalyzer::English);
720        assert!(fulltext_options.case_sensitive);
721    }
722}