1mod column_schema;
16pub mod constraint;
17pub mod ext;
18#[cfg(feature = "enterprise")]
19mod sketch;
20
21use std::collections::HashMap;
22use std::sync::Arc;
23use std::{fmt, mem};
24
25use arrow::datatypes::{Field, Schema as ArrowSchema};
26use datafusion_common::DFSchemaRef;
27use serde::{Deserialize, Deserializer, Serialize};
28use snafu::{ResultExt, ensure};
29
30use crate::error::{self, DuplicateColumnSnafu, Error, ProjectArrowSchemaSnafu, Result};
31use crate::prelude::ConcreteDataType;
32pub use crate::schema::column_schema::{
33 COLUMN_COMPRESSION_OPT_KEY_ENCODING, COLUMN_COMPRESSION_OPT_KEY_LEVEL,
34 COLUMN_COMPRESSION_OPT_KEY_PAGE_SIZE, COLUMN_COMPRESSION_OPT_KEY_TYPE,
35 COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE, COLUMN_FULLTEXT_OPT_KEY_ANALYZER,
36 COLUMN_FULLTEXT_OPT_KEY_BACKEND, COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE,
37 COLUMN_FULLTEXT_OPT_KEY_FALSE_POSITIVE_RATE, COLUMN_FULLTEXT_OPT_KEY_GRANULARITY,
38 COLUMN_SKIPPING_INDEX_OPT_KEY_FALSE_POSITIVE_RATE, COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY,
39 COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE, COMMENT_KEY, COMPRESSION_KEY, ColumnEncoding,
40 ColumnExtType, ColumnSchema, CompressionOptions, CompressionType, FULLTEXT_KEY,
41 FulltextAnalyzer, FulltextBackend, FulltextOptions, HLL_KEY, INVERTED_INDEX_KEY, KLL_KEY,
42 MG_KEY, Metadata, SKIPPING_INDEX_KEY, SkippingIndexOptions, SkippingIndexType, TIME_INDEX_KEY,
43};
44pub use crate::schema::constraint::ColumnDefaultConstraint;
45#[cfg(feature = "enterprise")]
46pub use crate::schema::sketch::{HllOptions, KllOptions, MgOptions};
47
48pub const VERSION_KEY: &str = "greptime:version";
50pub const TYPE_KEY: &str = "greptime:type";
52
53#[derive(Clone, PartialEq, Eq, Serialize)]
55pub struct Schema {
56 column_schemas: Vec<ColumnSchema>,
57 #[serde(skip_serializing)]
58 name_to_index: HashMap<String, usize>,
59 #[serde(skip_serializing)]
60 arrow_schema: Arc<ArrowSchema>,
61 #[serde(skip_serializing)]
66 timestamp_index: Option<usize>,
67 version: u32,
71}
72
73impl<'de> Deserialize<'de> for Schema {
74 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
75 where
76 D: Deserializer<'de>,
77 {
78 use serde::de::Error;
79
80 #[derive(Deserialize)]
81 struct RawSchema {
82 column_schemas: Vec<ColumnSchema>,
83 version: u32,
84 }
85 let raw = RawSchema::deserialize(deserializer)?;
86
87 SchemaBuilder::try_from(raw.column_schemas)
88 .and_then(|x| x.version(raw.version).build())
89 .map_err(D::Error::custom)
90 }
91}
92
93impl fmt::Debug for Schema {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 f.debug_struct("Schema")
96 .field("column_schemas", &self.column_schemas)
97 .field("name_to_index", &self.name_to_index)
98 .field("timestamp_index", &self.timestamp_index)
99 .field("version", &self.version)
100 .finish()
101 }
102}
103
104impl Schema {
105 pub const INITIAL_VERSION: u32 = 0;
107
108 pub fn new(column_schemas: Vec<ColumnSchema>) -> Schema {
113 Self::new_with_version(column_schemas, Self::INITIAL_VERSION)
115 }
116
117 pub fn new_with_version(column_schemas: Vec<ColumnSchema>, version: u32) -> Schema {
118 SchemaBuilder::try_from(column_schemas)
119 .unwrap()
120 .version(version)
121 .build()
122 .unwrap()
123 }
124
125 pub fn try_new(column_schemas: Vec<ColumnSchema>) -> Result<Schema> {
127 SchemaBuilder::try_from(column_schemas)?.build()
128 }
129
130 pub fn arrow_schema(&self) -> &Arc<ArrowSchema> {
131 &self.arrow_schema
132 }
133
134 pub fn column_schemas(&self) -> &[ColumnSchema] {
135 &self.column_schemas
136 }
137
138 pub fn column_schema_by_name(&self, name: &str) -> Option<&ColumnSchema> {
139 self.name_to_index
140 .get(name)
141 .map(|index| &self.column_schemas[*index])
142 }
143
144 pub fn column_name_by_index(&self, idx: usize) -> &str {
148 &self.column_schemas[idx].name
149 }
150
151 pub fn column_index_by_name(&self, name: &str) -> Option<usize> {
152 self.name_to_index.get(name).copied()
153 }
154
155 pub fn contains_column(&self, name: &str) -> bool {
156 self.name_to_index.contains_key(name)
157 }
158
159 pub fn num_columns(&self) -> usize {
160 self.column_schemas.len()
161 }
162
163 pub fn is_empty(&self) -> bool {
164 self.column_schemas.is_empty()
165 }
166
167 pub fn timestamp_index(&self) -> Option<usize> {
169 self.timestamp_index
170 }
171
172 pub fn timestamp_column(&self) -> Option<&ColumnSchema> {
173 self.timestamp_index.map(|idx| &self.column_schemas[idx])
174 }
175
176 pub fn version(&self) -> u32 {
177 self.version
178 }
179
180 pub fn metadata(&self) -> &HashMap<String, String> {
181 &self.arrow_schema.metadata
182 }
183
184 pub fn estimated_size(&self) -> usize {
186 mem::size_of_val(self)
187 + mem::size_of::<ColumnSchema>() * self.column_schemas.capacity()
188 + self
189 .column_schemas
190 .iter()
191 .map(|column_schema| {
192 column_schema.estimated_size() - mem::size_of::<ColumnSchema>()
193 })
194 .sum::<usize>()
195 + mem::size_of::<(String, usize)>() * self.name_to_index.capacity()
196 + self
197 .name_to_index
198 .keys()
199 .map(|name| name.capacity())
200 .sum::<usize>()
201 + arrow_schema_size(self.arrow_schema.as_ref())
202 }
203
204 pub fn try_project(&self, indices: &[usize]) -> Result<Self> {
210 let mut column_schemas = Vec::with_capacity(indices.len());
211 let mut timestamp_index = None;
212 for index in indices {
213 if let Some(ts_index) = self.timestamp_index
214 && ts_index == *index
215 {
216 timestamp_index = Some(column_schemas.len());
217 }
218 column_schemas.push(self.column_schemas[*index].clone());
219 }
220 let arrow_schema = self
221 .arrow_schema
222 .project(indices)
223 .context(ProjectArrowSchemaSnafu)?;
224 let name_to_index = column_schemas
225 .iter()
226 .enumerate()
227 .map(|(pos, column_schema)| (column_schema.name.clone(), pos))
228 .collect();
229
230 Ok(Self {
231 column_schemas,
232 name_to_index,
233 arrow_schema: Arc::new(arrow_schema),
234 timestamp_index,
235 version: self.version,
236 })
237 }
238}
239
240fn arrow_schema_size(schema: &ArrowSchema) -> usize {
241 mem::size_of_val(schema)
242 + schema.fields.size()
243 + mem::size_of::<(String, String)>() * schema.metadata.capacity()
244 + schema
245 .metadata
246 .iter()
247 .map(|(key, value)| key.capacity() + value.capacity())
248 .sum::<usize>()
249}
250
251#[derive(Default)]
252pub struct SchemaBuilder {
253 column_schemas: Vec<ColumnSchema>,
254 name_to_index: HashMap<String, usize>,
255 fields: Vec<Field>,
256 timestamp_index: Option<usize>,
257 version: u32,
258 metadata: HashMap<String, String>,
259}
260
261impl TryFrom<Vec<ColumnSchema>> for SchemaBuilder {
262 type Error = Error;
263
264 fn try_from(column_schemas: Vec<ColumnSchema>) -> Result<SchemaBuilder> {
265 SchemaBuilder::try_from_columns(column_schemas)
266 }
267}
268
269impl SchemaBuilder {
270 pub fn try_from_columns(column_schemas: Vec<ColumnSchema>) -> Result<Self> {
271 let FieldsAndIndices {
272 fields,
273 name_to_index,
274 timestamp_index,
275 } = collect_fields(&column_schemas)?;
276
277 Ok(Self {
278 column_schemas,
279 name_to_index,
280 fields,
281 timestamp_index,
282 ..Default::default()
283 })
284 }
285
286 pub fn version(mut self, version: u32) -> Self {
287 self.version = version;
288 self
289 }
290
291 pub fn add_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
295 let _ = self.metadata.insert(key.into(), value.into());
296 self
297 }
298
299 pub fn build(mut self) -> Result<Schema> {
300 if let Some(timestamp_index) = self.timestamp_index {
301 validate_timestamp_index(&self.column_schemas, timestamp_index)?;
302 }
303
304 self.metadata
305 .insert(VERSION_KEY.to_string(), self.version.to_string());
306
307 let arrow_schema = ArrowSchema::new(self.fields).with_metadata(self.metadata);
308
309 Ok(Schema {
310 column_schemas: self.column_schemas,
311 name_to_index: self.name_to_index,
312 arrow_schema: Arc::new(arrow_schema),
313 timestamp_index: self.timestamp_index,
314 version: self.version,
315 })
316 }
317}
318
319struct FieldsAndIndices {
320 fields: Vec<Field>,
321 name_to_index: HashMap<String, usize>,
322 timestamp_index: Option<usize>,
323}
324
325fn collect_fields(column_schemas: &[ColumnSchema]) -> Result<FieldsAndIndices> {
326 let mut fields = Vec::with_capacity(column_schemas.len());
327 let mut name_to_index = HashMap::with_capacity(column_schemas.len());
328 let mut timestamp_index = None;
329 for (index, column_schema) in column_schemas.iter().enumerate() {
330 if column_schema.is_time_index() && timestamp_index.is_none() {
331 timestamp_index = Some(index);
332 }
333 let mut field = Field::try_from(column_schema)?;
334
335 let extype = match column_schema.data_type {
337 ConcreteDataType::Json(_) => Some(ColumnExtType::Json),
338 ConcreteDataType::Vector(d) => Some(ColumnExtType::Vector(d.dim)),
339 _ => None,
340 };
341 if let Some(extype) = extype {
342 field
343 .metadata_mut()
344 .insert(TYPE_KEY.to_string(), extype.to_string());
345 }
346 fields.push(field);
347 ensure!(
348 name_to_index
349 .insert(column_schema.name.clone(), index)
350 .is_none(),
351 DuplicateColumnSnafu {
352 column: &column_schema.name,
353 }
354 );
355 }
356
357 Ok(FieldsAndIndices {
358 fields,
359 name_to_index,
360 timestamp_index,
361 })
362}
363
364fn validate_timestamp_index(column_schemas: &[ColumnSchema], timestamp_index: usize) -> Result<()> {
365 ensure!(
366 timestamp_index < column_schemas.len(),
367 error::InvalidTimestampIndexSnafu {
368 index: timestamp_index,
369 }
370 );
371
372 let column_schema = &column_schemas[timestamp_index];
373 ensure!(
374 column_schema.data_type.is_timestamp(),
375 error::InvalidTimestampIndexSnafu {
376 index: timestamp_index,
377 }
378 );
379 ensure!(
380 column_schema.is_time_index(),
381 error::InvalidTimestampIndexSnafu {
382 index: timestamp_index,
383 }
384 );
385
386 Ok(())
387}
388
389pub type SchemaRef = Arc<Schema>;
390
391impl TryFrom<Arc<ArrowSchema>> for Schema {
392 type Error = Error;
393
394 fn try_from(arrow_schema: Arc<ArrowSchema>) -> Result<Schema> {
395 let mut column_schemas = Vec::with_capacity(arrow_schema.fields.len());
396 let mut name_to_index = HashMap::with_capacity(arrow_schema.fields.len());
397 for field in &arrow_schema.fields {
398 let column_schema = ColumnSchema::try_from(field.as_ref())?;
399 let _ = name_to_index.insert(field.name().clone(), column_schemas.len());
400 column_schemas.push(column_schema);
401 }
402
403 let mut timestamp_index = None;
404 for (index, column_schema) in column_schemas.iter().enumerate() {
405 if column_schema.is_time_index() {
406 validate_timestamp_index(&column_schemas, index)?;
407 timestamp_index = Some(index);
408 break;
409 }
410 }
411
412 let version = try_parse_version(&arrow_schema.metadata, VERSION_KEY)?;
413
414 Ok(Self {
415 column_schemas,
416 name_to_index,
417 arrow_schema,
418 timestamp_index,
419 version,
420 })
421 }
422}
423
424impl TryFrom<ArrowSchema> for Schema {
425 type Error = Error;
426
427 fn try_from(arrow_schema: ArrowSchema) -> Result<Schema> {
428 let arrow_schema = Arc::new(arrow_schema);
429
430 Schema::try_from(arrow_schema)
431 }
432}
433
434impl TryFrom<DFSchemaRef> for Schema {
435 type Error = Error;
436
437 fn try_from(value: DFSchemaRef) -> Result<Self> {
438 value.inner().clone().try_into()
439 }
440}
441
442fn try_parse_version(metadata: &HashMap<String, String>, key: &str) -> Result<u32> {
443 if let Some(value) = metadata.get(key) {
444 let version = value
445 .parse()
446 .context(error::ParseSchemaVersionSnafu { value })?;
447
448 Ok(version)
449 } else {
450 Ok(Schema::INITIAL_VERSION)
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457 use crate::data_type::ConcreteDataType;
458
459 #[test]
460 fn test_build_empty_schema() {
461 let schema = SchemaBuilder::default().build().unwrap();
462 assert_eq!(0, schema.num_columns());
463 assert!(schema.is_empty());
464 }
465
466 #[test]
467 fn test_schema_no_timestamp() {
468 let column_schemas = vec![
469 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), false),
470 ColumnSchema::new("col2", ConcreteDataType::float64_datatype(), true),
471 ];
472 let schema = Schema::new(column_schemas.clone());
473
474 assert_eq!(2, schema.num_columns());
475 assert!(!schema.is_empty());
476 assert!(schema.timestamp_index().is_none());
477 assert!(schema.timestamp_column().is_none());
478 assert_eq!(Schema::INITIAL_VERSION, schema.version());
479
480 for column_schema in &column_schemas {
481 let found = schema.column_schema_by_name(&column_schema.name).unwrap();
482 assert_eq!(column_schema, found);
483 }
484 assert!(schema.column_schema_by_name("col3").is_none());
485
486 let new_schema = Schema::try_from(schema.arrow_schema().clone()).unwrap();
487
488 assert_eq!(schema, new_schema);
489 assert_eq!(column_schemas, schema.column_schemas());
490 }
491
492 #[test]
493 fn test_schema_duplicate_column() {
494 let column_schemas = vec![
495 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), false),
496 ColumnSchema::new("col1", ConcreteDataType::float64_datatype(), true),
497 ];
498 let err = Schema::try_new(column_schemas).unwrap_err();
499
500 assert!(
501 matches!(err, Error::DuplicateColumn { .. }),
502 "expect DuplicateColumn, found {}",
503 err
504 );
505 }
506
507 #[test]
508 fn test_metadata() {
509 let column_schemas = vec![ColumnSchema::new(
510 "col1",
511 ConcreteDataType::int32_datatype(),
512 false,
513 )];
514 let schema = SchemaBuilder::try_from(column_schemas)
515 .unwrap()
516 .add_metadata("k1", "v1")
517 .build()
518 .unwrap();
519
520 assert_eq!("v1", schema.metadata().get("k1").unwrap());
521 }
522
523 #[test]
524 fn test_schema_with_timestamp() {
525 let column_schemas = vec![
526 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
527 ColumnSchema::new(
528 "ts",
529 ConcreteDataType::timestamp_millisecond_datatype(),
530 false,
531 )
532 .with_time_index(true),
533 ];
534 let schema = SchemaBuilder::try_from(column_schemas.clone())
535 .unwrap()
536 .version(123)
537 .build()
538 .unwrap();
539
540 assert_eq!(1, schema.timestamp_index().unwrap());
541 assert_eq!(&column_schemas[1], schema.timestamp_column().unwrap());
542 assert_eq!(123, schema.version());
543
544 let new_schema = Schema::try_from(schema.arrow_schema().clone()).unwrap();
545 assert_eq!(1, schema.timestamp_index().unwrap());
546 assert_eq!(schema, new_schema);
547 }
548
549 #[test]
550 fn test_schema_wrong_timestamp() {
551 let column_schemas = vec![
552 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true)
553 .with_time_index(true),
554 ColumnSchema::new("col2", ConcreteDataType::float64_datatype(), false),
555 ];
556 assert!(
557 SchemaBuilder::try_from(column_schemas)
558 .unwrap()
559 .build()
560 .is_err()
561 );
562
563 let column_schemas = vec![
564 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
565 ColumnSchema::new("col2", ConcreteDataType::float64_datatype(), false)
566 .with_time_index(true),
567 ];
568
569 assert!(
570 SchemaBuilder::try_from(column_schemas)
571 .unwrap()
572 .build()
573 .is_err()
574 );
575 }
576}