taos_query/common/raw/
layout.rs

1use std::ops::{BitAnd, BitAndAssign, BitOrAssign};
2
3use bitflags::bitflags;
4
5use crate::common::Precision;
6
7/// Inlined data format in backup file.
8pub enum InlineFormat {
9    /// Only located at the exactly header of a file, contains basic information describing the data included.
10    ///
11    /// 1. database name
12    /// 2. database schema
13    /// 3. single table or not.
14    Header,
15    /// Raw meta message from TMQ, which could be write back to a TDengine cluster seamlessly.
16    RawMeta,
17    /// Raw data message from TMQ, contains data block of a child table or common table.
18    RawData,
19    /// For stable data not use TMQ, contains STable schema and table name - tags entry map.
20    ///
21    /// This type of data will be built from legacy whole backup strategy.
22    StbData,
23}
24
25bitflags! {
26    /// Inline memory layout for raw block.
27    #[repr(transparent)]
28    #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
29    pub struct Layout: u32 {
30        // Lowest 4 bits for layout components.
31
32        /// With table name in the block.
33        const WITH_TABLE_NAME = 0b_0001;
34        /// With field names.
35        const WITH_FIELD_NAMES = 0b_0010;
36        /// If raw data block contains group id.
37        const WITH_GROUP_ID = 0b_0100;
38        /// If raw data contains schema
39        const WITH_FIELD_SCHEMA = 0b_1000;
40
41        /// If NChar decoded or not in data block.
42        const NCHAR_IS_DECODED = 0b_0001_0000;
43        /// If Schema has been changed, eg. subset or added some columns.
44        const SCHEMA_CHANGED = 0b_0010_0000;
45
46        // Inline layout types.
47
48        /// Inline with full raw layout, this is the default behavior.
49        ///
50        ///
51        /// ```text
52        /// +-----+----+----+-----+------+---+---+------+-------+------------+
53        /// |flags|rows|cols|table|fields|len|gid|schema|lengths|    data    |
54        /// +-----+----+----+-----+------+---+---+------+-------+------------+
55        /// |2    |4   |4   | dyn | dyn  | 4 | 8 |cols*6|cols*4 |sum(lengths)|
56        /// +-----+----+----+-----+------+---+---+------+-------+------------+
57        /// ```
58        const INLINE_DEFAULT = Self::WITH_GROUP_ID.bits() | Self::WITH_FIELD_SCHEMA.bits();
59
60        /// Inline as raw block only, without table names and field names.
61        ///
62        /// ```text
63        /// +-----+----+----+---+---+------+-------+------------+
64        /// |flags|rows|cols|len|gid|schema|lengths|    data    |
65        /// +-----+----+----+---+---+------+-------+------------+
66        /// |2    |4   |4   |4  | 8 |cols*6|cols*4 |sum(lengths)|
67        /// +-----+----+----+---+---+------+-------+------------+
68        /// ```
69        const INLINE_AS_RAW_BLOCK = Self::WITH_GROUP_ID.bits() | Self::WITH_FIELD_SCHEMA.bits();
70
71        /// Inline as data only to save space.
72        ///
73        /// ```text
74        /// +-----+----+----+-------+------------|
75        /// |flags|rows|cols|lengths|    data    |
76        /// +-----+----+----+-------+------------|
77        /// |2    |4   |4   |cols*4 |sum(lengths)|
78        /// +-----+----+----+-------+------------|
79        /// ```
80        const INLINE_AS_DATA_ONLY = 0;
81
82        // Next 2 bits for precision
83
84        /// Precision mask.
85        const PRECISION_MASK = 0b11_0000_0000;
86        const PRECISION_CLEAR_MASK = 0b1111_1100_1111_1111;
87        const IS_MILLIS = 0b0_0000_0000;
88        const IS_MICROS = 0b1_0000_0000;
89        const IS_NANOS = 0b10_0000_0000;
90
91        // Top 4 bits for data block type.
92        //
93        //  0: table raw data
94        //  1: table raw meta
95        //  2: stable schema and name-tags data.
96        //  3: database schema
97        const FORMAT_MASK = 0b_0011_0000_0000_0000;
98
99        /// is raw data
100        const IS_RAW_DATA = 0b_0000_0000_0000_0000;
101
102        /// is raw meta
103        const IS_RAW_META = 0b_0001_0000_0000_0000;
104
105        /// Is STable data, contains sql schema and table name - tags map.
106        const IS_STB_DATA = 0b_0010_0000_0000_0000;
107
108        /// Is database schema, used for database rebuild.
109        const IS_HEADER = 0b_0011_0000_0000_0000;
110
111        // Non-exhausted.
112    }
113}
114
115// explicit `Default` implementation
116impl Default for Layout {
117    fn default() -> Layout {
118        Self::INLINE_DEFAULT
119    }
120}
121
122impl Layout {
123    pub fn precision(&self) -> Precision {
124        let layout = *self & Self::PRECISION_MASK;
125        match layout {
126            l if l == Self::IS_MILLIS => Precision::Millisecond,
127            l if l == Self::IS_MICROS => Precision::Microsecond,
128            Self::IS_NANOS => Precision::Nanosecond,
129            _ => unreachable!(),
130        }
131    }
132
133    pub fn expect_table_name(&self) -> bool {
134        self.contains(Self::WITH_TABLE_NAME)
135    }
136
137    pub fn with_table_name(&mut self) -> &mut Self {
138        self.set(Self::WITH_TABLE_NAME, true);
139        self
140    }
141
142    pub fn expect_field_names(&self) -> bool {
143        self.contains(Self::WITH_FIELD_NAMES)
144    }
145
146    pub fn with_field_names(&mut self) -> &mut Self {
147        self.set(Self::WITH_FIELD_NAMES, true);
148        self
149    }
150
151    pub fn nchar_is_decoded(&self) -> bool {
152        self.contains(Self::NCHAR_IS_DECODED)
153    }
154
155    pub fn with_nchar_decoded(&mut self) -> &mut Self {
156        self.set(Self::NCHAR_IS_DECODED, true);
157        self
158    }
159
160    pub fn with_precision(&mut self, precision: Precision) -> &mut Self {
161        self.bitand_assign(Self::PRECISION_CLEAR_MASK);
162        let precision = match precision {
163            Precision::Millisecond => Self::IS_MILLIS,
164            Precision::Microsecond => Self::IS_MICROS,
165            Precision::Nanosecond => Self::IS_NANOS,
166        };
167        self.bitor_assign(precision);
168        self
169    }
170
171    pub fn with_schema_changed(mut self) -> Self {
172        self.set(Self::SCHEMA_CHANGED, true);
173        self
174    }
175    pub fn set_schema_changed(&mut self, value: bool) -> &mut Self {
176        self.set(Self::SCHEMA_CHANGED, value);
177        self
178    }
179
180    pub fn schema_changed(&self) -> bool {
181        self.contains(Self::SCHEMA_CHANGED)
182    }
183
184    pub fn inline_format(&self) -> InlineFormat {
185        match self.bitand(Self::FORMAT_MASK) {
186            Self::IS_RAW_DATA => InlineFormat::RawData,
187            Self::IS_RAW_META => InlineFormat::RawMeta,
188            Self::IS_STB_DATA => InlineFormat::StbData,
189            Self::IS_HEADER => InlineFormat::Header,
190            _ => unreachable!(),
191        }
192    }
193
194    pub fn as_inner(&self) -> u32 {
195        self.bits()
196    }
197}
198
199#[test]
200fn test_layout() {
201    let mut default = Layout::default();
202    assert_eq!(default, Layout::INLINE_DEFAULT);
203
204    assert!(default.with_field_names().expect_field_names());
205
206    assert_eq!(default.precision(), Precision::Millisecond);
207    let precisions = [
208        Precision::Nanosecond,
209        Precision::Microsecond,
210        Precision::Millisecond,
211    ];
212    for precision in precisions {
213        assert_eq!(default.with_precision(precision).precision(), precision);
214    }
215
216    assert!(!default.nchar_is_decoded());
217    assert!(default.with_nchar_decoded().nchar_is_decoded());
218
219    let mut a = Layout::INLINE_AS_DATA_ONLY;
220    assert!(a.with_table_name().expect_table_name());
221    assert!(a.with_field_names().expect_field_names());
222
223    assert!(!a.schema_changed());
224    assert!(a.with_schema_changed().schema_changed());
225}