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