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