Skip to main content

servers/
influxdb.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 api::v1::value::ValueData;
16use api::v1::{ColumnDataType, RowInsertRequests};
17use common_grpc::precision::Precision;
18use common_query::prelude::greptime_timestamp;
19use hyper::Request;
20use influxdb_line_protocol::{FieldValue, parse_lines};
21use snafu::ResultExt;
22
23use crate::error::{Error, InfluxdbLineProtocolSnafu, TimePrecisionSnafu};
24use crate::row_writer::{self, MultiTableData};
25
26const INFLUXDB_API_PATH_NAME: &str = "influxdb";
27const INFLUXDB_API_V2_PATH_NAME: &str = "influxdb/api/v2";
28const DEFAULT_TIME_PRECISION: Precision = Precision::Nanosecond;
29
30#[inline]
31pub(crate) fn is_influxdb_request<T>(req: &Request<T>) -> bool {
32    req.uri().path().contains(INFLUXDB_API_PATH_NAME)
33}
34
35#[inline]
36pub(crate) fn is_influxdb_v2_request<T>(req: &Request<T>) -> bool {
37    req.uri().path().contains(INFLUXDB_API_V2_PATH_NAME)
38}
39
40#[derive(Debug)]
41pub struct InfluxdbRequest {
42    pub precision: Option<Precision>,
43    pub lines: String,
44}
45
46impl TryFrom<InfluxdbRequest> for RowInsertRequests {
47    type Error = Error;
48
49    fn try_from(value: InfluxdbRequest) -> Result<Self, Self::Error> {
50        let lines = parse_lines(&value.lines)
51            .collect::<influxdb_line_protocol::Result<Vec<_>>>()
52            .context(InfluxdbLineProtocolSnafu)?;
53
54        let mut multi_table_data = MultiTableData::new();
55        let precision = unwrap_or_default_precision(value.precision);
56        for line in &lines {
57            let table_name = line.series.measurement.as_str();
58            let tags = &line.series.tag_set;
59            let fields = &line.field_set;
60            let ts = line.timestamp;
61            // tags.len + fields.len + timestamp(+1)
62            let num_columns = tags.as_ref().map(|x| x.len()).unwrap_or(0) + fields.len() + 1;
63
64            let table_data = multi_table_data.get_or_default_table_data(table_name, num_columns, 0);
65            let mut one_row = table_data.alloc_one_row();
66
67            // tags
68            if let Some(tags) = tags {
69                let kvs = tags.iter().map(|(k, v)| (k.to_string(), v.to_string()));
70                row_writer::write_tags(table_data, kvs, &mut one_row)?;
71            }
72
73            // fields
74            let fields = fields.iter().map(|(k, v)| {
75                let (datatype, value) = match v {
76                    FieldValue::I64(v) => (ColumnDataType::Int64, Some(ValueData::I64Value(*v))),
77                    FieldValue::U64(v) => (ColumnDataType::Uint64, Some(ValueData::U64Value(*v))),
78                    FieldValue::F64(v) => (ColumnDataType::Float64, Some(ValueData::F64Value(*v))),
79                    FieldValue::String(v) => (
80                        ColumnDataType::String,
81                        Some(ValueData::StringValue(v.to_string())),
82                    ),
83                    FieldValue::Boolean(v) => {
84                        (ColumnDataType::Boolean, Some(ValueData::BoolValue(*v)))
85                    }
86                };
87                (k.to_string(), datatype, value)
88            });
89            row_writer::write_fields(table_data, fields, &mut one_row)?;
90
91            // timestamp
92            row_writer::write_ts_to_nanos(
93                table_data,
94                greptime_timestamp(),
95                ts,
96                precision,
97                &mut one_row,
98            )?;
99
100            table_data.add_row(one_row);
101        }
102
103        Ok(multi_table_data.into_row_insert_requests().0)
104    }
105}
106
107#[inline]
108fn unwrap_or_default_precision(precision: Option<Precision>) -> Precision {
109    if let Some(val) = precision {
110        val
111    } else {
112        DEFAULT_TIME_PRECISION
113    }
114}
115
116/// Parses an InfluxDB time-precision name shared by the HTTP write
117/// endpoints (v1/v2) and the `precision` option of COPY FROM STDIN.
118pub(crate) fn parse_time_precision(value: &str) -> crate::error::Result<Precision> {
119    // Precision conversion needs to be compatible with influxdb v1 v2 api.
120    // For details, see the Influxdb documents.
121    // https://docs.influxdata.com/influxdb/v1.8/tools/api/#apiv2write-http-endpoint
122    // https://docs.influxdata.com/influxdb/v1.8/tools/api/#write-http-endpoint
123    match value {
124        "n" | "ns" => Ok(Precision::Nanosecond),
125        "u" | "us" => Ok(Precision::Microsecond),
126        "ms" => Ok(Precision::Millisecond),
127        "s" => Ok(Precision::Second),
128        "m" => Ok(Precision::Minute),
129        "h" => Ok(Precision::Hour),
130        unknown => TimePrecisionSnafu {
131            name: unknown.to_string(),
132        }
133        .fail(),
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use api::v1::value::ValueData;
140    use api::v1::{ColumnDataType, RowInsertRequests, Rows, SemanticType};
141    use common_query::prelude::greptime_timestamp;
142
143    use crate::influxdb::InfluxdbRequest;
144
145    #[test]
146    fn test_convert_influxdb_lines_to_rows() {
147        let lines = r"
148monitor1,host=host1 cpu=66.6,memory=1024 1663840496100023100
149monitor1,host=host2 memory=1027 1663840496400340001
150monitor2,host=host3 cpu=66.5 1663840496100023102
151monitor2,host=host4 cpu=66.3,memory=1029 1663840496400340003";
152
153        let influxdb_req = InfluxdbRequest {
154            precision: None,
155            lines: lines.to_string(),
156        };
157
158        let requests: RowInsertRequests = influxdb_req.try_into().unwrap();
159        assert_eq!(2, requests.inserts.len());
160
161        for request in requests.inserts {
162            match &request.table_name[..] {
163                "monitor1" => assert_monitor1_rows(&request.rows),
164                "monitor2" => assert_monitor2_rows(&request.rows),
165                _ => panic!(),
166            }
167        }
168    }
169
170    fn assert_monitor1_rows(rows: &Option<Rows>) {
171        let rows = rows.as_ref().unwrap();
172        let schema = &rows.schema;
173        let rows = &rows.rows;
174        assert_eq!(4, schema.len());
175        assert_eq!(2, rows.len());
176
177        for (i, column_schema) in schema.iter().enumerate() {
178            match &column_schema.column_name[..] {
179                "host" => {
180                    assert_eq!(ColumnDataType::String as i32, column_schema.datatype);
181                    assert_eq!(SemanticType::Tag as i32, column_schema.semantic_type);
182
183                    for (j, row) in rows.iter().enumerate() {
184                        let v = row.values[i].value_data.as_ref().unwrap();
185                        match j {
186                            0 => assert_eq!("host1", extract_string_value(v)),
187                            1 => assert_eq!("host2", extract_string_value(v)),
188                            _ => panic!(),
189                        }
190                    }
191                }
192                "cpu" => {
193                    assert_eq!(ColumnDataType::Float64 as i32, column_schema.datatype);
194                    assert_eq!(SemanticType::Field as i32, column_schema.semantic_type);
195
196                    for (j, row) in rows.iter().enumerate() {
197                        let v = row.values[i].value_data.as_ref();
198                        match j {
199                            0 => assert_eq!(66.6f64, extract_f64_value(v.as_ref().unwrap())),
200                            1 => assert_eq!(None, v),
201                            _ => panic!(),
202                        }
203                    }
204                }
205                "memory" => {
206                    assert_eq!(ColumnDataType::Float64 as i32, column_schema.datatype);
207                    assert_eq!(SemanticType::Field as i32, column_schema.semantic_type);
208
209                    for (j, row) in rows.iter().enumerate() {
210                        let v = row.values[i].value_data.as_ref();
211                        match j {
212                            0 => assert_eq!(1024f64, extract_f64_value(v.as_ref().unwrap())),
213                            1 => assert_eq!(1027f64, extract_f64_value(v.as_ref().unwrap())),
214                            _ => panic!(),
215                        }
216                    }
217                }
218                _ if column_schema.column_name == greptime_timestamp() => {
219                    assert_eq!(
220                        ColumnDataType::TimestampNanosecond as i32,
221                        column_schema.datatype
222                    );
223                    assert_eq!(SemanticType::Timestamp as i32, column_schema.semantic_type);
224
225                    for (j, row) in rows.iter().enumerate() {
226                        let v = row.values[i].value_data.as_ref();
227                        match j {
228                            0 => assert_eq!(
229                                1663840496100023100,
230                                extract_ts_nanos_value(v.as_ref().unwrap())
231                            ),
232                            1 => assert_eq!(
233                                1663840496400340001,
234                                extract_ts_nanos_value(v.as_ref().unwrap())
235                            ),
236                            _ => panic!(),
237                        }
238                    }
239                }
240                _ => panic!(),
241            }
242        }
243    }
244
245    fn assert_monitor2_rows(rows: &Option<Rows>) {
246        let rows = rows.as_ref().unwrap();
247        let schema = &rows.schema;
248        let rows = &rows.rows;
249        assert_eq!(4, schema.len());
250        assert_eq!(2, rows.len());
251
252        for (i, column_schema) in schema.iter().enumerate() {
253            match &column_schema.column_name[..] {
254                "host" => {
255                    assert_eq!(ColumnDataType::String as i32, column_schema.datatype);
256                    assert_eq!(SemanticType::Tag as i32, column_schema.semantic_type);
257
258                    for (j, row) in rows.iter().enumerate() {
259                        let v = row.values[i].value_data.as_ref().unwrap();
260                        match j {
261                            0 => assert_eq!("host3", extract_string_value(v)),
262                            1 => assert_eq!("host4", extract_string_value(v)),
263                            _ => panic!(),
264                        }
265                    }
266                }
267                "cpu" => {
268                    assert_eq!(ColumnDataType::Float64 as i32, column_schema.datatype);
269                    assert_eq!(SemanticType::Field as i32, column_schema.semantic_type);
270
271                    for (j, row) in rows.iter().enumerate() {
272                        let v = row.values[i].value_data.as_ref();
273                        match j {
274                            0 => assert_eq!(66.5f64, extract_f64_value(v.as_ref().unwrap())),
275                            1 => assert_eq!(66.3f64, extract_f64_value(v.as_ref().unwrap())),
276                            _ => panic!(),
277                        }
278                    }
279                }
280                "memory" => {
281                    assert_eq!(ColumnDataType::Float64 as i32, column_schema.datatype);
282                    assert_eq!(SemanticType::Field as i32, column_schema.semantic_type);
283
284                    for (j, row) in rows.iter().enumerate() {
285                        let v = row.values[i].value_data.as_ref();
286                        match j {
287                            0 => assert_eq!(None, v),
288                            1 => assert_eq!(1029f64, extract_f64_value(v.as_ref().unwrap())),
289                            _ => panic!(),
290                        }
291                    }
292                }
293                _ if column_schema.column_name == greptime_timestamp() => {
294                    assert_eq!(
295                        ColumnDataType::TimestampNanosecond as i32,
296                        column_schema.datatype
297                    );
298                    assert_eq!(SemanticType::Timestamp as i32, column_schema.semantic_type);
299
300                    for (j, row) in rows.iter().enumerate() {
301                        let v = row.values[i].value_data.as_ref();
302                        match j {
303                            0 => assert_eq!(
304                                1663840496100023102,
305                                extract_ts_nanos_value(v.as_ref().unwrap())
306                            ),
307                            1 => assert_eq!(
308                                1663840496400340003,
309                                extract_ts_nanos_value(v.as_ref().unwrap())
310                            ),
311                            _ => panic!(),
312                        }
313                    }
314                }
315                _ => panic!(),
316            }
317        }
318    }
319
320    fn extract_string_value(value: &ValueData) -> &str {
321        match value {
322            ValueData::StringValue(v) => v,
323            _ => unreachable!(),
324        }
325    }
326
327    fn extract_f64_value(value: &ValueData) -> f64 {
328        match value {
329            ValueData::F64Value(v) => *v,
330            _ => unreachable!(),
331        }
332    }
333
334    fn extract_ts_nanos_value(value: &ValueData) -> i64 {
335        match value {
336            ValueData::TimestampNanosecondValue(v) => *v,
337            _ => unreachable!(),
338        }
339    }
340}