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