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