Skip to main content

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