sql/statements/
query.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::fmt;
16
17use serde::Serialize;
18use sqlparser::ast::Query as SpQuery;
19use sqlparser_derive::{Visit, VisitMut};
20
21use crate::error::Error;
22use crate::parsers::with_tql_parser::HybridCteWith;
23
24/// A wrapper around [`Query`] from sqlparser-rs to add support for hybrid CTEs
25#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
26pub struct Query {
27    pub inner: SpQuery,
28    /// Hybrid CTE containing both SQL and TQL CTEs
29    pub hybrid_cte: Option<HybridCteWith>,
30}
31
32impl TryFrom<SpQuery> for Query {
33    type Error = Error;
34
35    fn try_from(inner: SpQuery) -> Result<Self, Self::Error> {
36        Ok(Self {
37            inner,
38            hybrid_cte: None,
39        })
40    }
41}
42
43impl TryFrom<Query> for SpQuery {
44    type Error = Error;
45
46    fn try_from(value: Query) -> Result<Self, Self::Error> {
47        Ok(value.inner)
48    }
49}
50
51impl fmt::Display for Query {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        if let Some(hybrid_cte) = &self.hybrid_cte {
54            // Delegate the WITH clause rendering to `HybridCteWith`
55            write!(f, "{} ", hybrid_cte)?;
56
57            // Display the main query without its WITH clause since we handled it above
58            let mut main_query = self.inner.clone();
59            main_query.with = None;
60            write!(f, "{}", main_query)
61        } else {
62            write!(f, "{}", self.inner)
63        }
64    }
65}
66
67#[cfg(test)]
68mod test {
69
70    use super::Query;
71    use crate::dialect::GreptimeDbDialect;
72    use crate::parser::{ParseOptions, ParserContext};
73    use crate::statements::statement::Statement;
74
75    fn create_query(sql: &str) -> Option<Box<Query>> {
76        match ParserContext::create_with_dialect(
77            sql,
78            &GreptimeDbDialect {},
79            ParseOptions::default(),
80        )
81        .unwrap()
82        .remove(0)
83        {
84            Statement::Query(query) => Some(query),
85            _ => None,
86        }
87    }
88
89    #[test]
90    fn test_query_display() {
91        assert_eq!(
92            create_query("select * from abc where x = 1 and y = 7")
93                .unwrap()
94                .to_string(),
95            "SELECT * FROM abc WHERE x = 1 AND y = 7"
96        );
97        assert_eq!(
98            create_query(
99                "select * from abc left join bcd where abc.a = 1 and bcd.d = 7 and abc.id = bcd.id"
100            )
101            .unwrap()
102            .to_string(),
103            "SELECT * FROM abc LEFT JOIN bcd WHERE abc.a = 1 AND bcd.d = 7 AND abc.id = bcd.id"
104        );
105        assert_eq!(
106            create_query("WITH tql_cte AS (TQL EVAL (0, 100, '5s') up) SELECT * FROM tql_cte")
107                .unwrap()
108                .to_string(),
109            "WITH tql_cte AS (TQL EVAL (0, 100, '5s') up) SELECT * FROM tql_cte"
110        );
111    }
112}