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