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