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