1pub 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::SemanticType;
40use common_sql::default_constraint::parse_column_default_constraint;
41use common_time::timezone::Timezone;
42use datatypes::extension::json::{Json2ExtensionType, JsonMetadata};
43use datatypes::prelude::ConcreteDataType;
44use datatypes::schema::{COMMENT_KEY, ColumnDefaultConstraint, ColumnSchema};
45use datatypes::types::json_type::JsonNativeType;
46use datatypes::types::{JsonFormat, JsonType, TimestampType};
47use datatypes::value::Value;
48use snafu::ResultExt;
49use sqlparser::ast::{ExactNumberInfo, Ident};
50
51use crate::ast::{
52 ColumnDef, ColumnOption, DataType as SqlDataType, ObjectNamePartExt, TimezoneInfo,
53 Value as SqlValue,
54};
55use crate::error::{
56 self, ConvertToGrpcDataTypeSnafu, ConvertValueSnafu, Result,
57 SerializeColumnDefaultConstraintSnafu, SetFulltextOptionSnafu, SetSkippingIndexOptionSnafu,
58 SetVectorIndexOptionSnafu, SqlCommonSnafu,
59};
60use crate::statements::create::Column;
61pub use crate::statements::option_map::OptionMap;
62pub(crate) use crate::statements::transform::transform_statements;
63
64const VECTOR_TYPE_NAME: &str = "VECTOR";
65const JSON2_TYPE_NAME: &str = "JSON2";
66
67pub fn value_to_sql_value(val: &Value) -> Result<SqlValue> {
68 Ok(match val {
69 Value::Int8(v) => SqlValue::Number(v.to_string(), false),
70 Value::UInt8(v) => SqlValue::Number(v.to_string(), false),
71 Value::Int16(v) => SqlValue::Number(v.to_string(), false),
72 Value::UInt16(v) => SqlValue::Number(v.to_string(), false),
73 Value::Int32(v) => SqlValue::Number(v.to_string(), false),
74 Value::UInt32(v) => SqlValue::Number(v.to_string(), false),
75 Value::Int64(v) => SqlValue::Number(v.to_string(), false),
76 Value::UInt64(v) => SqlValue::Number(v.to_string(), false),
77 Value::Float32(v) => SqlValue::Number(v.to_string(), false),
78 Value::Float64(v) => SqlValue::Number(v.to_string(), false),
79 Value::Boolean(b) => SqlValue::Boolean(*b),
80 Value::Date(d) => SqlValue::SingleQuotedString(d.to_string()),
81 Value::Timestamp(ts) => SqlValue::SingleQuotedString(ts.to_iso8601_string()),
82 Value::String(s) => SqlValue::SingleQuotedString(s.as_utf8().to_string()),
83 Value::Null => SqlValue::Null,
84 _ => return ConvertValueSnafu { value: val.clone() }.fail(),
86 })
87}
88
89pub fn has_primary_key_option(column_def: &ColumnDef) -> bool {
91 column_def
92 .options
93 .iter()
94 .any(|options| matches!(options.option, ColumnOption::PrimaryKey(..)))
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 if let Some(options) = column.extensions.build_vector_index_options()? {
149 column_schema = column_schema
150 .with_vector_index_options(&options)
151 .context(SetVectorIndexOptionSnafu)?;
152 }
153
154 column_schema.set_inverted_index(column.extensions.inverted_index_options.is_some());
155
156 let is_json2_column = if let SqlDataType::Custom(object_name, _) = column.data_type() {
157 object_name
158 .0
159 .first()
160 .map(|x| x.to_string_unquoted().eq_ignore_ascii_case(JSON2_TYPE_NAME))
161 .unwrap_or_default()
162 } else {
163 false
164 };
165 if is_json2_column {
166 let settings = column.extensions.build_json_settings()?.unwrap_or_default();
167 let extension = Json2ExtensionType::new(Arc::new(JsonMetadata::new(settings)));
168 column_schema.with_extension_type(&extension);
169 }
170
171 Ok(column_schema)
172}
173
174pub fn sql_column_def_to_grpc_column_def(
176 col: &ColumnDef,
177 timezone: Option<&Timezone>,
178) -> Result<api::v1::ColumnDef> {
179 let name = col.name.value.clone();
180 let data_type = sql_data_type_to_concrete_data_type(&col.data_type)?;
181
182 let is_nullable = col
183 .options
184 .iter()
185 .all(|o| !matches!(o.option, ColumnOption::NotNull));
186
187 let default_constraint =
188 parse_column_default_constraint(&name, &data_type, &col.options, timezone)
189 .context(SqlCommonSnafu)?
190 .map(ColumnDefaultConstraint::try_into) .transpose()
192 .context(SerializeColumnDefaultConstraintSnafu)?;
193 let (datatype, datatype_ext) = ColumnDataTypeWrapper::try_from(data_type.clone())
195 .context(ConvertToGrpcDataTypeSnafu)?
196 .to_parts();
197
198 let is_primary_key = col
199 .options
200 .iter()
201 .any(|o| matches!(o.option, ColumnOption::PrimaryKey(..)));
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(data_type: &SqlDataType) -> Result<ConcreteDataType> {
222 match data_type {
223 SqlDataType::BigInt(_) | SqlDataType::Int64 => Ok(ConcreteDataType::int64_datatype()),
224 SqlDataType::BigIntUnsigned(_) => Ok(ConcreteDataType::uint64_datatype()),
225 SqlDataType::Int(_) | SqlDataType::Integer(_) => Ok(ConcreteDataType::int32_datatype()),
226 SqlDataType::IntUnsigned(_) | SqlDataType::UnsignedInteger => {
227 Ok(ConcreteDataType::uint32_datatype())
228 }
229 SqlDataType::SmallInt(_) => Ok(ConcreteDataType::int16_datatype()),
230 SqlDataType::SmallIntUnsigned(_) => Ok(ConcreteDataType::uint16_datatype()),
231 SqlDataType::TinyInt(_) | SqlDataType::Int8(_) => Ok(ConcreteDataType::int8_datatype()),
232 SqlDataType::TinyIntUnsigned(_) | SqlDataType::Int8Unsigned(_) => {
233 Ok(ConcreteDataType::uint8_datatype())
234 }
235 SqlDataType::Char(_)
236 | SqlDataType::Varchar(_)
237 | SqlDataType::Text
238 | SqlDataType::TinyText
239 | SqlDataType::MediumText
240 | SqlDataType::LongText
241 | SqlDataType::String(_) => Ok(ConcreteDataType::string_datatype()),
242 SqlDataType::Float(_) => Ok(ConcreteDataType::float32_datatype()),
243 SqlDataType::Double(_) | SqlDataType::Float64 => Ok(ConcreteDataType::float64_datatype()),
244 SqlDataType::Boolean => Ok(ConcreteDataType::boolean_datatype()),
245 SqlDataType::Date => Ok(ConcreteDataType::date_datatype()),
246 SqlDataType::Binary(_)
247 | SqlDataType::Blob(_)
248 | SqlDataType::Bytea
249 | SqlDataType::Varbinary(_) => Ok(ConcreteDataType::binary_datatype()),
250 SqlDataType::Datetime(_) => Ok(ConcreteDataType::timestamp_microsecond_datatype()),
251 SqlDataType::Timestamp(precision, _) => Ok(precision
252 .as_ref()
253 .map(|v| TimestampType::try_from(*v))
254 .transpose()
255 .map_err(|_| {
256 error::SqlTypeNotSupportedSnafu {
257 t: data_type.clone(),
258 }
259 .build()
260 })?
261 .map(|t| ConcreteDataType::timestamp_datatype(t.unit()))
262 .unwrap_or(ConcreteDataType::timestamp_millisecond_datatype())),
263 SqlDataType::Interval { .. } => Ok(ConcreteDataType::interval_month_day_nano_datatype()),
264 SqlDataType::Decimal(exact_info) => match exact_info {
265 ExactNumberInfo::None => Ok(ConcreteDataType::decimal128_default_datatype()),
266 ExactNumberInfo::Precision(p) => Ok(ConcreteDataType::decimal128_datatype(*p as u8, 0)),
269 ExactNumberInfo::PrecisionAndScale(p, s) => {
270 Ok(ConcreteDataType::decimal128_datatype(*p as u8, *s as i8))
271 }
272 },
273 SqlDataType::JSON => Ok(ConcreteDataType::Json(JsonType::new(JsonFormat::Jsonb))),
274 SqlDataType::Custom(name, args) if name.0.len() == 1 => {
276 let name = name.0[0].to_string_unquoted().to_ascii_uppercase();
277 match name.as_str() {
278 VECTOR_TYPE_NAME if args.len() == 1 => {
279 let dim = &args[0];
280 let dim = dim.parse().map_err(|e| {
281 error::ParseSqlValueSnafu {
282 msg: format!("Failed to parse vector dimension '{}': {}", dim, e),
283 }
284 .build()
285 })?;
286 Ok(ConcreteDataType::vector_datatype(dim))
287 }
288 JSON2_TYPE_NAME if args.is_empty() => {
289 let format = JsonFormat::Json2(Arc::new(JsonNativeType::Null));
292 Ok(ConcreteDataType::Json(JsonType::new(format)))
293 }
294 _ => error::SqlTypeNotSupportedSnafu {
295 t: data_type.clone(),
296 }
297 .fail(),
298 }
299 }
300 _ => error::SqlTypeNotSupportedSnafu {
301 t: data_type.clone(),
302 }
303 .fail(),
304 }
305}
306
307pub fn concrete_data_type_to_sql_data_type(data_type: &ConcreteDataType) -> Result<SqlDataType> {
308 match data_type {
309 ConcreteDataType::Int64(_) => Ok(SqlDataType::BigInt(None)),
310 ConcreteDataType::UInt64(_) => Ok(SqlDataType::BigIntUnsigned(None)),
311 ConcreteDataType::Int32(_) => Ok(SqlDataType::Int(None)),
312 ConcreteDataType::UInt32(_) => Ok(SqlDataType::IntUnsigned(None)),
313 ConcreteDataType::Int16(_) => Ok(SqlDataType::SmallInt(None)),
314 ConcreteDataType::UInt16(_) => Ok(SqlDataType::SmallIntUnsigned(None)),
315 ConcreteDataType::Int8(_) => Ok(SqlDataType::TinyInt(None)),
316 ConcreteDataType::UInt8(_) => Ok(SqlDataType::TinyIntUnsigned(None)),
317 ConcreteDataType::String(_) => Ok(SqlDataType::String(None)),
318 ConcreteDataType::Float32(_) => Ok(SqlDataType::Float(ExactNumberInfo::None)),
319 ConcreteDataType::Float64(_) => Ok(SqlDataType::Double(ExactNumberInfo::None)),
320 ConcreteDataType::Boolean(_) => Ok(SqlDataType::Boolean),
321 ConcreteDataType::Date(_) => Ok(SqlDataType::Date),
322 ConcreteDataType::Timestamp(ts_type) => Ok(SqlDataType::Timestamp(
323 Some(ts_type.precision()),
324 TimezoneInfo::None,
325 )),
326 ConcreteDataType::Time(time_type) => Ok(SqlDataType::Time(
327 Some(time_type.precision()),
328 TimezoneInfo::None,
329 )),
330 ConcreteDataType::Interval(_) => Ok(SqlDataType::Interval {
331 fields: None,
332 precision: None,
333 }),
334 ConcreteDataType::Binary(_) => Ok(SqlDataType::Varbinary(None)),
335 ConcreteDataType::Decimal128(d) => Ok(SqlDataType::Decimal(
336 ExactNumberInfo::PrecisionAndScale(d.precision() as u64, d.scale() as i64),
337 )),
338 ConcreteDataType::Json(_) => Ok(SqlDataType::JSON),
339 ConcreteDataType::Vector(v) => Ok(SqlDataType::Custom(
340 vec![Ident::new(VECTOR_TYPE_NAME)].into(),
341 vec![v.dim.to_string()],
342 )),
343 ConcreteDataType::Duration(_)
344 | ConcreteDataType::Null(_)
345 | ConcreteDataType::List(_)
346 | ConcreteDataType::Struct(_)
347 | ConcreteDataType::Dictionary(_) => error::ConcreteTypeNotSupportedSnafu {
348 t: data_type.clone(),
349 }
350 .fail(),
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use api::v1::ColumnDataType;
357 use datatypes::schema::{
358 COLUMN_FULLTEXT_OPT_KEY_ANALYZER, COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, FulltextAnalyzer,
359 };
360 use sqlparser::ast::{ColumnOptionDef, Expr, PrimaryKeyConstraint};
361
362 use super::*;
363 use crate::ast::TimezoneInfo;
364 use crate::statements::ColumnOption;
365 use crate::statements::create::ColumnExtensions;
366
367 fn check_type(sql_type: SqlDataType, data_type: ConcreteDataType) {
368 assert_eq!(
369 data_type,
370 sql_data_type_to_concrete_data_type(&sql_type).unwrap()
371 );
372 }
373
374 #[test]
375 pub fn test_sql_data_type_to_concrete_data_type() {
376 check_type(
377 SqlDataType::BigInt(None),
378 ConcreteDataType::int64_datatype(),
379 );
380 check_type(SqlDataType::Int(None), ConcreteDataType::int32_datatype());
381 check_type(
382 SqlDataType::Integer(None),
383 ConcreteDataType::int32_datatype(),
384 );
385 check_type(
386 SqlDataType::SmallInt(None),
387 ConcreteDataType::int16_datatype(),
388 );
389 check_type(SqlDataType::Char(None), ConcreteDataType::string_datatype());
390 check_type(
391 SqlDataType::Varchar(None),
392 ConcreteDataType::string_datatype(),
393 );
394 check_type(SqlDataType::Text, ConcreteDataType::string_datatype());
395 check_type(
396 SqlDataType::String(None),
397 ConcreteDataType::string_datatype(),
398 );
399 check_type(
400 SqlDataType::Float(ExactNumberInfo::None),
401 ConcreteDataType::float32_datatype(),
402 );
403 check_type(
404 SqlDataType::Double(ExactNumberInfo::None),
405 ConcreteDataType::float64_datatype(),
406 );
407 check_type(SqlDataType::Boolean, ConcreteDataType::boolean_datatype());
408 check_type(SqlDataType::Date, ConcreteDataType::date_datatype());
409 check_type(
410 SqlDataType::Timestamp(None, TimezoneInfo::None),
411 ConcreteDataType::timestamp_millisecond_datatype(),
412 );
413 check_type(
414 SqlDataType::Varbinary(None),
415 ConcreteDataType::binary_datatype(),
416 );
417 check_type(
418 SqlDataType::BigIntUnsigned(None),
419 ConcreteDataType::uint64_datatype(),
420 );
421 check_type(
422 SqlDataType::IntUnsigned(None),
423 ConcreteDataType::uint32_datatype(),
424 );
425 check_type(
426 SqlDataType::SmallIntUnsigned(None),
427 ConcreteDataType::uint16_datatype(),
428 );
429 check_type(
430 SqlDataType::TinyIntUnsigned(None),
431 ConcreteDataType::uint8_datatype(),
432 );
433 check_type(
434 SqlDataType::Datetime(None),
435 ConcreteDataType::timestamp_microsecond_datatype(),
436 );
437 check_type(
438 SqlDataType::Interval {
439 fields: None,
440 precision: None,
441 },
442 ConcreteDataType::interval_month_day_nano_datatype(),
443 );
444 check_type(SqlDataType::JSON, ConcreteDataType::json_datatype());
445 check_type(
446 SqlDataType::Custom(
447 vec![Ident::new(VECTOR_TYPE_NAME)].into(),
448 vec!["3".to_string()],
449 ),
450 ConcreteDataType::vector_datatype(3),
451 );
452 }
453
454 #[test]
455 pub fn test_sql_column_def_to_grpc_column_def() {
456 let column_def = ColumnDef {
458 name: "col".into(),
459 data_type: SqlDataType::Double(ExactNumberInfo::None),
460 options: vec![],
461 };
462
463 let grpc_column_def = sql_column_def_to_grpc_column_def(&column_def, None).unwrap();
464
465 assert_eq!("col", grpc_column_def.name);
466 assert!(grpc_column_def.is_nullable); assert_eq!(ColumnDataType::Float64 as i32, grpc_column_def.data_type);
468 assert!(grpc_column_def.default_constraint.is_empty());
469 assert_eq!(grpc_column_def.semantic_type, SemanticType::Field as i32);
470
471 let column_def = ColumnDef {
473 name: "col".into(),
474 data_type: SqlDataType::Double(ExactNumberInfo::None),
475 options: vec![ColumnOptionDef {
476 name: None,
477 option: ColumnOption::NotNull,
478 }],
479 };
480
481 let grpc_column_def = sql_column_def_to_grpc_column_def(&column_def, None).unwrap();
482 assert!(!grpc_column_def.is_nullable);
483
484 let column_def = ColumnDef {
486 name: "col".into(),
487 data_type: SqlDataType::Double(ExactNumberInfo::None),
488 options: vec![ColumnOptionDef {
489 name: None,
490 option: ColumnOption::PrimaryKey(PrimaryKeyConstraint {
491 name: None,
492 index_name: None,
493 index_type: None,
494 columns: vec![],
495 index_options: vec![],
496 characteristics: None,
497 }),
498 }],
499 };
500
501 let grpc_column_def = sql_column_def_to_grpc_column_def(&column_def, None).unwrap();
502 assert_eq!(grpc_column_def.semantic_type, SemanticType::Tag as i32);
503 }
504
505 #[test]
506 pub fn test_sql_column_def_to_grpc_column_def_with_timezone() {
507 let column_def = ColumnDef {
508 name: "col".into(),
509 data_type: SqlDataType::Timestamp(Some(3), TimezoneInfo::None),
511 options: vec![ColumnOptionDef {
512 name: None,
513 option: ColumnOption::Default(Expr::Value(
514 SqlValue::SingleQuotedString("2024-01-30T00:01:01".to_string()).into(),
515 )),
516 }],
517 };
518
519 let grpc_column_def = sql_column_def_to_grpc_column_def(
521 &column_def,
522 Some(&Timezone::from_tz_string("Asia/Shanghai").unwrap()),
523 )
524 .unwrap();
525 assert_eq!("col", grpc_column_def.name);
526 assert!(grpc_column_def.is_nullable); assert_eq!(
528 ColumnDataType::TimestampMillisecond as i32,
529 grpc_column_def.data_type
530 );
531 assert!(!grpc_column_def.default_constraint.is_empty());
532
533 let constraint =
534 ColumnDefaultConstraint::try_from(&grpc_column_def.default_constraint[..]).unwrap();
535 assert!(
536 matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
537 if ts.to_iso8601_string() == "2024-01-29 16:01:01+0000")
538 );
539
540 let grpc_column_def = sql_column_def_to_grpc_column_def(&column_def, None).unwrap();
542 assert_eq!("col", grpc_column_def.name);
543 assert!(grpc_column_def.is_nullable); assert_eq!(
545 ColumnDataType::TimestampMillisecond as i32,
546 grpc_column_def.data_type
547 );
548 assert!(!grpc_column_def.default_constraint.is_empty());
549
550 let constraint =
551 ColumnDefaultConstraint::try_from(&grpc_column_def.default_constraint[..]).unwrap();
552 assert!(
553 matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
554 if ts.to_iso8601_string() == "2024-01-30 00:01:01+0000")
555 );
556 }
557
558 #[test]
559 pub fn test_has_primary_key_option() {
560 let column_def = ColumnDef {
561 name: "col".into(),
562 data_type: SqlDataType::Double(ExactNumberInfo::None),
563 options: vec![],
564 };
565 assert!(!has_primary_key_option(&column_def));
566
567 let column_def = ColumnDef {
568 name: "col".into(),
569 data_type: SqlDataType::Double(ExactNumberInfo::None),
570 options: vec![ColumnOptionDef {
571 name: None,
572 option: ColumnOption::PrimaryKey(PrimaryKeyConstraint {
573 name: None,
574 index_name: None,
575 index_type: None,
576 columns: vec![],
577 index_options: vec![],
578 characteristics: None,
579 }),
580 }],
581 };
582 assert!(has_primary_key_option(&column_def));
583 }
584
585 #[test]
586 pub fn test_column_to_schema() {
587 let column_def = Column {
588 column_def: ColumnDef {
589 name: "col".into(),
590 data_type: SqlDataType::Double(ExactNumberInfo::None),
591 options: vec![],
592 },
593 extensions: ColumnExtensions::default(),
594 };
595
596 let column_schema = column_to_schema(&column_def, "ts", None).unwrap();
597
598 assert_eq!("col", column_schema.name);
599 assert_eq!(
600 ConcreteDataType::float64_datatype(),
601 column_schema.data_type
602 );
603 assert!(column_schema.is_nullable());
604 assert!(!column_schema.is_time_index());
605
606 let column_schema = column_to_schema(&column_def, "col", None).unwrap();
607
608 assert_eq!("col", column_schema.name);
609 assert_eq!(
610 ConcreteDataType::float64_datatype(),
611 column_schema.data_type
612 );
613 assert!(!column_schema.is_nullable());
614 assert!(column_schema.is_time_index());
615
616 let column_def = Column {
617 column_def: ColumnDef {
618 name: "col2".into(),
619 data_type: SqlDataType::String(None),
620 options: vec![
621 ColumnOptionDef {
622 name: None,
623 option: ColumnOption::NotNull,
624 },
625 ColumnOptionDef {
626 name: None,
627 option: ColumnOption::Comment("test comment".to_string()),
628 },
629 ],
630 },
631 extensions: ColumnExtensions::default(),
632 };
633
634 let column_schema = column_to_schema(&column_def, "ts", None).unwrap();
635
636 assert_eq!("col2", column_schema.name);
637 assert_eq!(ConcreteDataType::string_datatype(), column_schema.data_type);
638 assert!(!column_schema.is_nullable());
639 assert!(!column_schema.is_time_index());
640 assert_eq!(
641 column_schema.metadata().get(COMMENT_KEY),
642 Some(&"test comment".to_string())
643 );
644 }
645
646 #[test]
647 pub fn test_column_to_schema_timestamp_with_timezone() {
648 let column = Column {
649 column_def: ColumnDef {
650 name: "col".into(),
651 data_type: SqlDataType::Timestamp(Some(3), TimezoneInfo::None),
653 options: vec![ColumnOptionDef {
654 name: None,
655 option: ColumnOption::Default(Expr::Value(
656 SqlValue::SingleQuotedString("2024-01-30T00:01:01".to_string()).into(),
657 )),
658 }],
659 },
660 extensions: ColumnExtensions::default(),
661 };
662
663 let column_schema = column_to_schema(
666 &column,
667 "ts",
668 Some(&Timezone::from_tz_string("Asia/Shanghai").unwrap()),
669 )
670 .unwrap();
671
672 assert_eq!("col", column_schema.name);
673 assert_eq!(
674 ConcreteDataType::timestamp_millisecond_datatype(),
675 column_schema.data_type
676 );
677 assert!(column_schema.is_nullable());
678
679 let constraint = column_schema.default_constraint().unwrap();
680 assert!(
681 matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
682 if ts.to_iso8601_string() == "2024-01-29 16:01:01+0000")
683 );
684
685 let column_schema = column_to_schema(&column, "ts", None).unwrap();
687
688 assert_eq!("col", column_schema.name);
689 assert_eq!(
690 ConcreteDataType::timestamp_millisecond_datatype(),
691 column_schema.data_type
692 );
693 assert!(column_schema.is_nullable());
694
695 let constraint = column_schema.default_constraint().unwrap();
696 assert!(
697 matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
698 if ts.to_iso8601_string() == "2024-01-30 00:01:01+0000")
699 );
700 }
701
702 #[test]
703 fn test_column_to_schema_with_fulltext() {
704 let column = Column {
705 column_def: ColumnDef {
706 name: "col".into(),
707 data_type: SqlDataType::Text,
708 options: vec![],
709 },
710 extensions: ColumnExtensions {
711 fulltext_index_options: Some(OptionMap::from([
712 (
713 COLUMN_FULLTEXT_OPT_KEY_ANALYZER.to_string(),
714 "English".to_string(),
715 ),
716 (
717 COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE.to_string(),
718 "true".to_string(),
719 ),
720 ])),
721 ..Default::default()
722 },
723 };
724
725 let column_schema = column_to_schema(&column, "ts", None).unwrap();
726 assert_eq!("col", column_schema.name);
727 assert_eq!(ConcreteDataType::string_datatype(), column_schema.data_type);
728 let fulltext_options = column_schema.fulltext_options().unwrap().unwrap();
729 assert_eq!(fulltext_options.analyzer, FulltextAnalyzer::English);
730 assert!(fulltext_options.case_sensitive);
731 }
732
733 #[test]
734 fn test_column_to_schema_with_vector_index() {
735 use datatypes::schema::{VectorDistanceMetric, VectorIndexEngineType};
736
737 let column = Column {
739 column_def: ColumnDef {
740 name: "embedding".into(),
741 data_type: SqlDataType::Custom(
742 vec![Ident::new(VECTOR_TYPE_NAME)].into(),
743 vec!["128".to_string()],
744 ),
745 options: vec![],
746 },
747 extensions: ColumnExtensions {
748 vector_index_options: Some(OptionMap::from([
749 ("metric".to_string(), "cosine".to_string()),
750 ("connectivity".to_string(), "32".to_string()),
751 ("expansion_add".to_string(), "200".to_string()),
752 ("expansion_search".to_string(), "100".to_string()),
753 ])),
754 ..Default::default()
755 },
756 };
757
758 let column_schema = column_to_schema(&column, "ts", None).unwrap();
759 assert_eq!("embedding", column_schema.name);
760 assert!(column_schema.is_vector_indexed());
761
762 let vector_options = column_schema.vector_index_options().unwrap().unwrap();
763 assert_eq!(vector_options.engine, VectorIndexEngineType::Usearch);
764 assert_eq!(vector_options.metric, VectorDistanceMetric::Cosine);
765 assert_eq!(vector_options.connectivity, 32);
766 assert_eq!(vector_options.expansion_add, 200);
767 assert_eq!(vector_options.expansion_search, 100);
768 }
769
770 #[test]
771 fn test_column_to_schema_with_vector_index_defaults() {
772 use datatypes::schema::{VectorDistanceMetric, VectorIndexEngineType};
773
774 let column = Column {
776 column_def: ColumnDef {
777 name: "vec".into(),
778 data_type: SqlDataType::Custom(
779 vec![Ident::new(VECTOR_TYPE_NAME)].into(),
780 vec!["64".to_string()],
781 ),
782 options: vec![],
783 },
784 extensions: ColumnExtensions {
785 vector_index_options: Some(OptionMap::default()),
786 ..Default::default()
787 },
788 };
789
790 let column_schema = column_to_schema(&column, "ts", None).unwrap();
791 assert_eq!("vec", column_schema.name);
792 assert!(column_schema.is_vector_indexed());
793
794 let vector_options = column_schema.vector_index_options().unwrap().unwrap();
795 assert_eq!(vector_options.engine, VectorIndexEngineType::Usearch);
797 assert_eq!(vector_options.metric, VectorDistanceMetric::L2sq);
798 assert_eq!(vector_options.connectivity, 16);
799 assert_eq!(vector_options.expansion_add, 128);
800 assert_eq!(vector_options.expansion_search, 64);
801 }
802}