1use 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 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 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 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 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
116pub(crate) fn parse_time_precision(value: &str) -> crate::error::Result<Precision> {
119 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}