1use super::intermediate_ast::{OrderBy, SetExpression, Slice, TableExpression};
2use crate::{sql::SelectStatementParser, Identifier, ParseError, ParseResult, ResourceId};
3use alloc::{boxed::Box, string::ToString, vec::Vec};
4use core::{fmt, str::FromStr};
5use serde::{Deserialize, Serialize};
67/// Representation of a select statement, that is, the only type of queries allowed.
8#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
9pub struct SelectStatement {
10/// the query expression
11pub expr: Box<SetExpression>,
1213/// if non-empty, an sort-order that is applied to the rows returned as result
14pub order_by: Vec<OrderBy>,
1516/// an optional slice clause, which can restrict the rows returned to a window within the
17 /// set of rows as generated by `expr` and `order_by`.
18pub slice: Option<Slice>,
19}
2021impl fmt::Debug for SelectStatement {
22fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23write!(
24 f,
25"SelectStatement \n[{:#?},\n{:#?},\n{:#?}\n]",
26self.expr, self.order_by, self.slice
27 )
28 }
29}
3031impl SelectStatement {
32/// This function returns the referenced tables in the provided `intermediate_ast`
33 ///
34 /// Note that we provide a `default_schema` in case the table expression
35 /// does not have any associated schema. This `default_schema` is
36 /// used to construct the `resource_id`, as we cannot have this field empty.
37 /// In case the table expression already has an associated schema,
38 /// then it's used instead of `default_schema`. Although the DQL endpoint
39 /// would require both to be equal, we have chosen to not fail here
40 /// as this would imply the caller to always know beforehand the referenced
41 /// schemas.
42 ///
43 /// Return:
44 /// - The vector with all tables referenced by the intermediate ast, encoded as resource ids.
45#[must_use]
46pub fn get_table_references(&self, default_schema: Identifier) -> Vec<ResourceId> {
47let set_expression: &SetExpression = &(self.expr);
4849match set_expression {
50 SetExpression::Query {
51 result_exprs: _,
52 from,
53 where_expr: _,
54 group_by: _,
55 } => convert_table_expr_to_resource_id_vector(&from[..], default_schema),
56 }
57 }
58}
5960impl FromStr for SelectStatement {
61type Err = crate::ParseError;
6263fn from_str(query: &str) -> ParseResult<Self> {
64 SelectStatementParser::new()
65 .parse(query)
66 .map_err(|e| ParseError::QueryParseError {
67 error: e.to_string(),
68 })
69 }
70}
7172/// # Panics
73///
74/// This function will panic in the following cases:
75/// - If `ResourceId::try_new` fails to create a valid `ResourceId`,
76/// the `.unwrap()` call will cause a panic.
77fn convert_table_expr_to_resource_id_vector(
78 table_expressions: &[Box<TableExpression>],
79 default_schema: Identifier,
80) -> Vec<ResourceId> {
81let mut tables = Vec::new();
8283for table_expression in table_expressions {
84let table_ref: &TableExpression = table_expression;
8586match table_ref {
87 TableExpression::Named { table, schema } => {
88let schema = schema.as_ref().map_or_else(
89 || default_schema.name(),
90super::identifier::Identifier::as_str,
91 );
9293 tables.push(ResourceId::try_new(schema, table.as_str()).unwrap());
94 }
95 }
96 }
9798 tables
99}
100101#[cfg(test)]
102mod tests {
103use super::*;
104use crate::sql::SelectStatementParser;
105106#[test]
107fn we_can_get_the_correct_table_references_using_a_default_schema() {
108let parsed_query_ast = SelectStatementParser::new()
109 .parse("SELECT A FROM TAB WHERE C = 3")
110 .unwrap();
111let default_schema = Identifier::try_new("ETH").unwrap();
112let ref_tables = parsed_query_ast.get_table_references(default_schema);
113114// note: the parsed table is always lower case
115assert_eq!(ref_tables, [ResourceId::try_new("eth", "tab").unwrap()]);
116 }
117118#[test]
119fn we_can_get_the_correct_table_references_in_case_the_default_schema_equals_the_original_schema(
120 ) {
121let parsed_query_ast = SelectStatementParser::new()
122 .parse("SELECT A FROM SCHEMA.TAB WHERE C = 3")
123 .unwrap();
124let default_schema = Identifier::try_new("SCHEMA").unwrap();
125let ref_tables = parsed_query_ast.get_table_references(default_schema);
126127assert_eq!(ref_tables, [ResourceId::try_new("schema", "tab").unwrap()]);
128 }
129130#[test]
131fn we_can_get_the_correct_table_references_in_case_the_default_schema_differs_from_the_original_schema(
132 ) {
133let parsed_query_ast = SelectStatementParser::new()
134 .parse("SELECT A FROM SCHEMA.TAB WHERE C = 3")
135 .unwrap();
136let default_schema = Identifier::try_new(" ETH ").unwrap();
137let ref_tables = parsed_query_ast.get_table_references(default_schema);
138139assert_eq!(ref_tables, [ResourceId::try_new("schema", "tab").unwrap()]);
140 }
141}