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