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