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