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};
22
23/// Default time index column name.
24static GREPTIME_TIMESTAMP_CELL: OnceCell<String> = OnceCell::new();
25
26/// Default value column name.
27static GREPTIME_VALUE_CELL: OnceCell<String> = OnceCell::new();
28
29pub fn set_default_prefix(prefix: Option<&str>) -> Result<()> {
30    // Strip surrounding double quotes as a defensive measure against upstream
31    // sources (scripts, CI, template engines, incorrect shell escaping) that may
32    // pass literal `""` as the value instead of an empty string.
33    let stripped = prefix.map(|s| {
34        s.strip_prefix('"')
35            .and_then(|s| s.strip_suffix('"'))
36            .unwrap_or(s)
37    });
38
39    match stripped {
40        None => {
41            // use default greptime prefix
42            GREPTIME_TIMESTAMP_CELL.get_or_init(|| GREPTIME_TIMESTAMP.to_string());
43            GREPTIME_VALUE_CELL.get_or_init(|| GREPTIME_VALUE.to_string());
44        }
45        Some(s) if s.trim().is_empty() => {
46            // use "" to disable prefix
47            GREPTIME_TIMESTAMP_CELL.get_or_init(|| "timestamp".to_string());
48            GREPTIME_VALUE_CELL.get_or_init(|| "value".to_string());
49        }
50        Some(x) => {
51            ensure!(
52                NAME_PATTERN_REG.is_match(x),
53                InvalidColumnPrefixSnafu { prefix: x }
54            );
55            GREPTIME_TIMESTAMP_CELL.get_or_init(|| format!("{}_timestamp", x));
56            GREPTIME_VALUE_CELL.get_or_init(|| format!("{}_value", x));
57        }
58    }
59    Ok(())
60}
61
62/// Get the default timestamp column name.
63/// Returns the configured value, or `greptime_timestamp` if not set.
64pub fn greptime_timestamp() -> &'static str {
65    GREPTIME_TIMESTAMP_CELL.get_or_init(|| GREPTIME_TIMESTAMP.to_string())
66}
67
68/// Get the default value column name.
69/// Returns the configured value, or `greptime_value` if not set.
70pub fn greptime_value() -> &'static str {
71    GREPTIME_VALUE_CELL.get_or_init(|| GREPTIME_VALUE.to_string())
72}
73
74/// Default timestamp column name constant for backward compatibility.
75const GREPTIME_TIMESTAMP: &str = "greptime_timestamp";
76/// Default value column name constant for backward compatibility.
77const GREPTIME_VALUE: &str = "greptime_value";
78/// Default counter column name for OTLP metrics (legacy mode).
79pub const GREPTIME_COUNT: &str = "greptime_count";
80/// Default physical table name
81pub const GREPTIME_PHYSICAL_TABLE: &str = "greptime_physical_table";
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    // Each test runs in a separate process via `cargo nextest`, so OnceCell
88    // state does not leak between tests.
89
90    #[test]
91    fn test_set_default_prefix_none() {
92        set_default_prefix(None).unwrap();
93        assert_eq!(greptime_timestamp(), "greptime_timestamp");
94        assert_eq!(greptime_value(), "greptime_value");
95    }
96
97    #[test]
98    fn test_set_default_prefix_empty_string() {
99        set_default_prefix(Some("")).unwrap();
100        assert_eq!(greptime_timestamp(), "timestamp");
101        assert_eq!(greptime_value(), "value");
102    }
103
104    #[test]
105    fn test_set_default_prefix_quoted_empty() {
106        // Handles upstream sources that pass literal `""` instead of an empty string
107        set_default_prefix(Some("\"\"")).unwrap();
108        assert_eq!(greptime_timestamp(), "timestamp");
109        assert_eq!(greptime_value(), "value");
110    }
111
112    #[test]
113    fn test_set_default_prefix_custom() {
114        set_default_prefix(Some("mydb")).unwrap();
115        assert_eq!(greptime_timestamp(), "mydb_timestamp");
116        assert_eq!(greptime_value(), "mydb_value");
117    }
118
119    #[test]
120    fn test_set_default_prefix_invalid() {
121        assert!(set_default_prefix(Some("invalid prefix!")).is_err());
122    }
123}