Skip to main content

common_query/
prelude.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 common_base::regex_pattern::NAME_PATTERN_REG;
16pub use datafusion_common::ScalarValue;
17use once_cell::sync::OnceCell;
18use snafu::ensure;
19
20pub use crate::columnar_value::ColumnarValue;
21use crate::error::{InvalidColumnPrefixSnafu, Result};
22use crate::native_histogram::NATIVE_HISTOGRAM_FIELD;
23
24/// Default time index column name.
25static GREPTIME_TIMESTAMP_CELL: OnceCell<String> = OnceCell::new();
26
27/// Default value column name.
28static GREPTIME_VALUE_CELL: OnceCell<String> = OnceCell::new();
29
30/// Default native histogram column name.
31static GREPTIME_NATIVE_HISTOGRAM_CELL: OnceCell<String> = OnceCell::new();
32
33pub fn set_default_prefix(prefix: Option<&str>) -> Result<()> {
34    // Strip surrounding double quotes as a defensive measure against upstream
35    // sources (scripts, CI, template engines, incorrect shell escaping) that may
36    // pass literal `""` as the value instead of an empty string.
37    let stripped = prefix.map(|s| {
38        s.strip_prefix('"')
39            .and_then(|s| s.strip_suffix('"'))
40            .unwrap_or(s)
41    });
42
43    match stripped {
44        None => {
45            // use default greptime prefix
46            GREPTIME_TIMESTAMP_CELL.get_or_init(|| GREPTIME_TIMESTAMP.to_string());
47            GREPTIME_VALUE_CELL.get_or_init(|| GREPTIME_VALUE.to_string());
48            GREPTIME_NATIVE_HISTOGRAM_CELL.get_or_init(|| NATIVE_HISTOGRAM_FIELD.to_string());
49        }
50        Some(s) if s.trim().is_empty() => {
51            // use "" to disable prefix
52            GREPTIME_TIMESTAMP_CELL.get_or_init(|| "timestamp".to_string());
53            GREPTIME_VALUE_CELL.get_or_init(|| "value".to_string());
54            GREPTIME_NATIVE_HISTOGRAM_CELL.get_or_init(|| "native_histogram".to_string());
55        }
56        Some(x) => {
57            ensure!(
58                NAME_PATTERN_REG.is_match(x),
59                InvalidColumnPrefixSnafu { prefix: x }
60            );
61            GREPTIME_TIMESTAMP_CELL.get_or_init(|| format!("{}_timestamp", x));
62            GREPTIME_VALUE_CELL.get_or_init(|| format!("{}_value", x));
63            GREPTIME_NATIVE_HISTOGRAM_CELL.get_or_init(|| format!("{}_native_histogram", x));
64        }
65    }
66    Ok(())
67}
68
69/// Get the default timestamp column name.
70/// Returns the configured value, or `greptime_timestamp` if not set.
71pub fn greptime_timestamp() -> &'static str {
72    GREPTIME_TIMESTAMP_CELL.get_or_init(|| GREPTIME_TIMESTAMP.to_string())
73}
74
75/// Get the default value column name.
76/// Returns the configured value, or `greptime_value` if not set.
77pub fn greptime_value() -> &'static str {
78    GREPTIME_VALUE_CELL.get_or_init(|| GREPTIME_VALUE.to_string())
79}
80
81/// Get the default native histogram column name.
82/// Returns the configured value, or `greptime_native_histogram` if not set.
83#[inline]
84pub fn greptime_native_histogram() -> &'static str {
85    GREPTIME_NATIVE_HISTOGRAM_CELL
86        .get()
87        .map_or(NATIVE_HISTOGRAM_FIELD, String::as_str)
88}
89
90/// Default timestamp column name constant for backward compatibility.
91const GREPTIME_TIMESTAMP: &str = "greptime_timestamp";
92/// Default value column name constant for backward compatibility.
93const GREPTIME_VALUE: &str = "greptime_value";
94/// Stored series label that opts ordinary float samples into raw-delta math.
95pub const OTLP_AGGREGATION_TEMPORALITY_LABEL: &str = "otlp_aggregation_temporality";
96/// Authoritative raw-delta value for [`OTLP_AGGREGATION_TEMPORALITY_LABEL`].
97pub const GREPTIME_TEMPORALITY_DELTA: &str = "delta";
98/// Default physical table name
99pub const GREPTIME_PHYSICAL_TABLE: &str = "greptime_physical_table";
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    // Each test runs in a separate process via `cargo nextest`, so OnceCell
106    // state does not leak between tests.
107
108    #[test]
109    fn test_set_default_prefix_none() {
110        set_default_prefix(None).unwrap();
111        assert_eq!(greptime_timestamp(), "greptime_timestamp");
112        assert_eq!(greptime_value(), "greptime_value");
113        assert_eq!(greptime_native_histogram(), "greptime_native_histogram");
114    }
115
116    #[test]
117    fn test_set_default_prefix_empty_string() {
118        set_default_prefix(Some("")).unwrap();
119        assert_eq!(greptime_timestamp(), "timestamp");
120        assert_eq!(greptime_value(), "value");
121        assert_eq!(greptime_native_histogram(), "native_histogram");
122    }
123
124    #[test]
125    fn test_set_default_prefix_quoted_empty() {
126        // Handles upstream sources that pass literal `""` instead of an empty string
127        set_default_prefix(Some("\"\"")).unwrap();
128        assert_eq!(greptime_timestamp(), "timestamp");
129        assert_eq!(greptime_value(), "value");
130        assert_eq!(greptime_native_histogram(), "native_histogram");
131    }
132
133    #[test]
134    fn test_set_default_prefix_custom() {
135        set_default_prefix(Some("mydb")).unwrap();
136        assert_eq!(greptime_timestamp(), "mydb_timestamp");
137        assert_eq!(greptime_value(), "mydb_value");
138        assert_eq!(greptime_native_histogram(), "mydb_native_histogram");
139    }
140
141    #[test]
142    fn test_set_default_prefix_invalid() {
143        assert!(set_default_prefix(Some("invalid prefix!")).is_err());
144    }
145}