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