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