Skip to main content

servers/
row_writer.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::HashMap;
16
17use ahash::{HashMap as AHashMap, HashMapExt};
18use api::v1::column_data_type_extension::TypeExt;
19use api::v1::helper::time_index_column_schema;
20use api::v1::value::ValueData;
21use api::v1::{
22    ColumnDataType, ColumnDataTypeExtension, ColumnSchema, JsonTypeExtension, Row,
23    RowInsertRequest, RowInsertRequests, Rows, SemanticType, Value,
24};
25use common_grpc::precision::Precision;
26use common_time::Timestamp;
27use common_time::timestamp::TimeUnit;
28use common_time::timestamp::TimeUnit::Nanosecond;
29use snafu::{OptionExt, ResultExt, ensure};
30
31use crate::error::{
32    IncompatibleSchemaSnafu, Result, RowWriterSnafu, TimePrecisionSnafu, TimestampOverflowSnafu,
33};
34
35/// The intermediate data structure for building the write request.
36/// It constructs the `schema` and `rows` as all input data row
37/// parsing is completed.
38pub struct TableData {
39    schema: Vec<ColumnSchema>,
40    rows: Vec<Row>,
41    column_indexes: AHashMap<String, usize>,
42}
43
44impl TableData {
45    pub fn new(num_columns: usize, num_rows: usize) -> Self {
46        Self {
47            schema: Vec::with_capacity(num_columns),
48            rows: Vec::with_capacity(num_rows),
49            column_indexes: AHashMap::with_capacity(num_columns),
50        }
51    }
52
53    #[inline]
54    pub fn num_columns(&self) -> usize {
55        self.schema.len()
56    }
57
58    #[inline]
59    pub fn num_rows(&self) -> usize {
60        self.rows.len()
61    }
62
63    #[inline]
64    pub fn alloc_one_row(&self) -> Vec<Value> {
65        vec![Value { value_data: None }; self.num_columns()]
66    }
67
68    #[inline]
69    pub fn add_row(&mut self, values: Vec<Value>) {
70        self.rows.push(Row { values })
71    }
72
73    #[inline]
74    pub fn reserve_rows(&mut self, additional: usize) {
75        self.rows.reserve(additional);
76    }
77
78    pub(crate) fn ensure_column(&mut self, column_schema: ColumnSchema) -> Result<usize> {
79        if let Some(index) = self.column_indexes.get(&column_schema.column_name).copied() {
80            check_schema_number(
81                column_schema.datatype,
82                column_schema.semantic_type,
83                &self.schema[index],
84            )?;
85            return Ok(index);
86        }
87
88        let index = self.schema.len();
89        let name = column_schema.column_name.clone();
90        self.schema.push(column_schema);
91        self.column_indexes.insert(name, index);
92        Ok(index)
93    }
94
95    /// Ensures a default column schema without allocating its name when it already exists.
96    pub(crate) fn ensure_column_by_name(
97        &mut self,
98        name: &str,
99        datatype: ColumnDataType,
100        semantic_type: SemanticType,
101    ) -> Result<usize> {
102        if let Some(index) = self.column_indexes.get(name).copied() {
103            check_schema(datatype, semantic_type, &self.schema[index])?;
104            return Ok(index);
105        }
106
107        self.ensure_column(ColumnSchema {
108            column_name: name.to_string(),
109            datatype: datatype as i32,
110            semantic_type: semantic_type as i32,
111            ..Default::default()
112        })
113    }
114
115    #[allow(dead_code)]
116    pub fn columns(&self) -> &Vec<ColumnSchema> {
117        &self.schema
118    }
119
120    pub fn into_schema_and_rows(self) -> (Vec<ColumnSchema>, Vec<Row>) {
121        (self.schema, self.rows)
122    }
123
124    /// Writes a field value without enforcing that later writes use the same datatype
125    /// as the first-seen schema entry.
126    ///
127    /// The OTLP trace v1 path uses this to preserve raw mixed values inside one request
128    /// so the frontend can reconcile them later against both the full batch and the
129    /// existing table schema.
130    pub fn write_field_unchecked(
131        &mut self,
132        name: impl ToString,
133        datatype: ColumnDataType,
134        value: Option<ValueData>,
135        one_row: &mut Vec<Value>,
136    ) {
137        self.write_column_unchecked(
138            ColumnSchema {
139                column_name: name.to_string(),
140                datatype: datatype as i32,
141                semantic_type: SemanticType::Field as i32,
142                ..Default::default()
143            },
144            value,
145            one_row,
146        );
147    }
148
149    pub fn write_column_unchecked(
150        &mut self,
151        column_schema: ColumnSchema,
152        value: Option<ValueData>,
153        one_row: &mut Vec<Value>,
154    ) {
155        if let Some(index) = self.column_indexes.get(&column_schema.column_name).copied() {
156            one_row[index].value_data = value;
157        } else {
158            let index = self.schema.len();
159            let name = column_schema.column_name.clone();
160            self.schema.push(column_schema);
161            self.column_indexes.insert(name, index);
162            one_row.push(Value { value_data: value });
163        }
164    }
165}
166
167pub struct MultiTableData {
168    table_data_map: HashMap<String, TableData>,
169}
170
171impl Default for MultiTableData {
172    fn default() -> Self {
173        Self::new()
174    }
175}
176
177impl MultiTableData {
178    pub fn new() -> Self {
179        Self {
180            table_data_map: HashMap::new(),
181        }
182    }
183
184    pub fn get_or_default_table_data(
185        &mut self,
186        table_name: impl ToString,
187        num_columns: usize,
188        num_rows: usize,
189    ) -> &mut TableData {
190        self.table_data_map
191            .entry(table_name.to_string())
192            .or_insert_with(|| TableData::new(num_columns, num_rows))
193    }
194
195    pub fn add_table_data(&mut self, table_name: impl ToString, table_data: TableData) {
196        self.table_data_map
197            .insert(table_name.to_string(), table_data);
198    }
199
200    #[allow(dead_code)]
201    pub fn num_tables(&self) -> usize {
202        self.table_data_map.len()
203    }
204
205    /// Returns the request and number of rows in it.
206    pub fn into_row_insert_requests(self) -> (RowInsertRequests, usize) {
207        let mut total_rows = 0;
208        let inserts = self
209            .table_data_map
210            .into_iter()
211            .map(|(table_name, table_data)| {
212                total_rows += table_data.num_rows();
213                let num_columns = table_data.num_columns();
214                let (schema, mut rows) = table_data.into_schema_and_rows();
215                for row in &mut rows {
216                    if num_columns > row.values.len() {
217                        row.values.resize(num_columns, Value { value_data: None });
218                    }
219                }
220
221                RowInsertRequest {
222                    table_name,
223                    rows: Some(Rows { schema, rows }),
224                }
225            })
226            .collect::<Vec<_>>();
227        let row_insert_requests = RowInsertRequests { inserts };
228
229        (row_insert_requests, total_rows)
230    }
231}
232
233/// Write data as tags into the table data.
234pub fn write_tags<K>(
235    table_data: &mut TableData,
236    tags: impl Iterator<Item = (K, String)>,
237    one_row: &mut Vec<Value>,
238) -> Result<()>
239where
240    K: AsRef<str> + Into<String>,
241{
242    let ktv_iter = tags.map(|(k, v)| (k, ColumnDataType::String, Some(ValueData::StringValue(v))));
243    write_by_semantic_type(table_data, SemanticType::Tag, ktv_iter, one_row)
244}
245
246/// Write data as fields into the table data.
247pub fn write_fields(
248    table_data: &mut TableData,
249    fields: impl Iterator<Item = (String, ColumnDataType, Option<ValueData>)>,
250    one_row: &mut Vec<Value>,
251) -> Result<()> {
252    write_by_semantic_type(table_data, SemanticType::Field, fields, one_row)
253}
254
255/// Write data as a tag into the table data.
256pub fn write_tag(
257    table_data: &mut TableData,
258    name: impl ToString,
259    value: impl ToString,
260    one_row: &mut Vec<Value>,
261) -> Result<()> {
262    write_by_semantic_type(
263        table_data,
264        SemanticType::Tag,
265        std::iter::once((
266            name.to_string(),
267            ColumnDataType::String,
268            Some(ValueData::StringValue(value.to_string())),
269        )),
270        one_row,
271    )
272}
273
274/// Write float64 data as a field into the table data.
275pub fn write_f64(
276    table_data: &mut TableData,
277    name: impl ToString,
278    value: f64,
279    one_row: &mut Vec<Value>,
280) -> Result<()> {
281    write_fields(
282        table_data,
283        std::iter::once((
284            name.to_string(),
285            ColumnDataType::Float64,
286            Some(ValueData::F64Value(value)),
287        )),
288        one_row,
289    )
290}
291
292pub(crate) fn build_json_column_schema(name: impl ToString) -> ColumnSchema {
293    ColumnSchema {
294        column_name: name.to_string(),
295        datatype: ColumnDataType::Binary as i32,
296        semantic_type: SemanticType::Field as i32,
297        datatype_extension: Some(ColumnDataTypeExtension {
298            type_ext: Some(TypeExt::JsonType(JsonTypeExtension::JsonBinary.into())),
299        }),
300        ..Default::default()
301    }
302}
303
304pub fn write_json(
305    table_data: &mut TableData,
306    name: impl ToString,
307    value: jsonb::Value,
308    one_row: &mut Vec<Value>,
309) -> Result<()> {
310    write_by_schema(
311        table_data,
312        std::iter::once((
313            build_json_column_schema(name),
314            Some(ValueData::BinaryValue(value.to_vec())),
315        )),
316        one_row,
317    )
318}
319
320pub(crate) fn write_by_schema(
321    table_data: &mut TableData,
322    kv_iter: impl Iterator<Item = (ColumnSchema, Option<ValueData>)>,
323    one_row: &mut Vec<Value>,
324) -> Result<()> {
325    let TableData {
326        schema,
327        column_indexes,
328        ..
329    } = table_data;
330
331    for (column_schema, value) in kv_iter {
332        let index = column_indexes.get(&column_schema.column_name);
333        if let Some(index) = index {
334            check_schema_number(
335                column_schema.datatype,
336                column_schema.semantic_type,
337                &schema[*index],
338            )?;
339            one_row[*index].value_data = value;
340        } else {
341            let index = schema.len();
342            let key = column_schema.column_name.clone();
343            schema.push(column_schema);
344            column_indexes.insert(key, index);
345            one_row.push(Value { value_data: value });
346        }
347    }
348
349    Ok(())
350}
351
352fn write_by_semantic_type<K>(
353    table_data: &mut TableData,
354    semantic_type: SemanticType,
355    ktv_iter: impl Iterator<Item = (K, ColumnDataType, Option<ValueData>)>,
356    one_row: &mut Vec<Value>,
357) -> Result<()>
358where
359    K: AsRef<str> + Into<String>,
360{
361    let TableData {
362        schema,
363        column_indexes,
364        ..
365    } = table_data;
366
367    for (name, datatype, value) in ktv_iter {
368        let index = column_indexes.get(name.as_ref()).copied();
369        if let Some(index) = index {
370            check_schema(datatype, semantic_type, &schema[index])?;
371            one_row[index].value_data = value;
372        } else {
373            let index = schema.len();
374            let name = name.into();
375            schema.push(ColumnSchema {
376                column_name: name.clone(),
377                datatype: datatype as i32,
378                semantic_type: semantic_type as i32,
379                ..Default::default()
380            });
381            column_indexes.insert(name, index);
382            one_row.push(Value { value_data: value });
383        }
384    }
385
386    Ok(())
387}
388
389/// Write timestamp data as milliseconds into the table data.
390pub fn write_ts_to_millis(
391    table_data: &mut TableData,
392    name: impl ToString,
393    ts: Option<i64>,
394    precision: Precision,
395    one_row: &mut Vec<Value>,
396) -> Result<()> {
397    write_ts_to(
398        table_data,
399        name,
400        ts,
401        precision,
402        TimestampType::Millis,
403        one_row,
404    )
405}
406
407/// Write timestamp data as nanoseconds into the table data.
408pub fn write_ts_to_nanos(
409    table_data: &mut TableData,
410    name: impl ToString,
411    ts: Option<i64>,
412    precision: Precision,
413    one_row: &mut Vec<Value>,
414) -> Result<()> {
415    write_ts_to(
416        table_data,
417        name,
418        ts,
419        precision,
420        TimestampType::Nanos,
421        one_row,
422    )
423}
424
425enum TimestampType {
426    Millis,
427    Nanos,
428}
429
430fn write_ts_to(
431    table_data: &mut TableData,
432    name: impl ToString,
433    ts: Option<i64>,
434    precision: Precision,
435    ts_type: TimestampType,
436    one_row: &mut Vec<Value>,
437) -> Result<()> {
438    let TableData {
439        schema,
440        column_indexes,
441        ..
442    } = table_data;
443    let name = name.to_string();
444
445    let ts = match ts {
446        Some(timestamp) => match ts_type {
447            TimestampType::Millis => precision.to_millis(timestamp),
448            TimestampType::Nanos => precision.to_nanos(timestamp),
449        }
450        .with_context(|| TimestampOverflowSnafu {
451            error: format!(
452                "timestamp {} overflow with precision {}",
453                timestamp, precision
454            ),
455        })?,
456        None => {
457            let timestamp = Timestamp::current_time(Nanosecond);
458            let unit: TimeUnit = precision.try_into().context(RowWriterSnafu)?;
459            let timestamp = timestamp
460                .convert_to(unit)
461                .with_context(|| TimePrecisionSnafu {
462                    name: precision.to_string(),
463                })?
464                .into();
465            match ts_type {
466                TimestampType::Millis => precision.to_millis(timestamp),
467                TimestampType::Nanos => precision.to_nanos(timestamp),
468            }
469            .with_context(|| TimestampOverflowSnafu {
470                error: format!(
471                    "timestamp {} overflow with precision {}",
472                    timestamp, precision
473                ),
474            })?
475        }
476    };
477
478    let (datatype, ts) = match ts_type {
479        TimestampType::Millis => (
480            ColumnDataType::TimestampMillisecond,
481            ValueData::TimestampMillisecondValue(ts),
482        ),
483        TimestampType::Nanos => (
484            ColumnDataType::TimestampNanosecond,
485            ValueData::TimestampNanosecondValue(ts),
486        ),
487    };
488
489    let index = column_indexes.get(&name);
490    if let Some(index) = index {
491        check_schema(datatype, SemanticType::Timestamp, &schema[*index])?;
492        one_row[*index].value_data = Some(ts);
493    } else {
494        let index = schema.len();
495        schema.push(time_index_column_schema(&name, datatype));
496        column_indexes.insert(name, index);
497        one_row.push(ts.into())
498    }
499
500    Ok(())
501}
502
503fn check_schema(
504    datatype: ColumnDataType,
505    semantic_type: SemanticType,
506    schema: &ColumnSchema,
507) -> Result<()> {
508    check_schema_number(datatype as i32, semantic_type as i32, schema)
509}
510
511fn check_schema_number(datatype: i32, semantic_type: i32, schema: &ColumnSchema) -> Result<()> {
512    ensure!(
513        schema.datatype == datatype,
514        IncompatibleSchemaSnafu {
515            column_name: &schema.column_name,
516            datatype: "datatype",
517            expected: schema.datatype,
518            actual: datatype,
519        }
520    );
521
522    ensure!(
523        schema.semantic_type == semantic_type,
524        IncompatibleSchemaSnafu {
525            column_name: &schema.column_name,
526            datatype: "semantic_type",
527            expected: schema.semantic_type,
528            actual: semantic_type,
529        }
530    );
531
532    Ok(())
533}