use std::sync::Arc;
use crate::tree::ast::identifier::{Identifier, ParsedIdentifier};
use crate::tree::ast::node::Span;
use crate::tree::ast::pipeline::Pipeline;
use crate::tree::ast::query::{Query, QueryKind, ValidQuery, WithClause};
use crate::tree::typed_ast::pipeline::TypedPipeline;
use super::pipeline::PipelineBuilder;
pub trait IntoPipelineRc {
fn into_pipeline_rc(self) -> Arc<Pipeline>;
}
impl IntoPipelineRc for Pipeline {
fn into_pipeline_rc(self) -> Arc<Pipeline> {
Arc::new(self)
}
}
impl IntoPipelineRc for Arc<Pipeline> {
fn into_pipeline_rc(self) -> Arc<Pipeline> {
self
}
}
impl IntoPipelineRc for PipelineBuilder {
fn into_pipeline_rc(self) -> Arc<Pipeline> {
Arc::new(self.build())
}
}
impl IntoPipelineRc for Arc<TypedPipeline> {
fn into_pipeline_rc(self) -> Arc<Pipeline> {
self.ast.clone()
}
}
impl IntoPipelineRc for &Arc<TypedPipeline> {
fn into_pipeline_rc(self) -> Arc<Pipeline> {
self.ast.clone()
}
}
#[derive(Clone)]
pub struct QueryBuilder {
with_clauses: Vec<WithClause>,
}
impl QueryBuilder {
pub fn new() -> Self {
Self {
with_clauses: Vec::new(),
}
}
pub fn main(self, pipeline: impl IntoPipelineRc) -> QueryBuilderWithMain {
QueryBuilderWithMain {
with_clauses: self.with_clauses,
main_pipeline: pipeline.into_pipeline_rc(),
}
}
pub fn with(mut self, name: impl Into<Identifier>, pipeline: impl IntoPipelineRc) -> Self {
self.with_clauses.push(WithClause {
span: Span::NONE,
name: ParsedIdentifier::Valid(name.into()),
pipeline: pipeline.into_pipeline_rc(),
});
self
}
pub fn merge_as_cte(mut self, query: Query, name: impl Into<Identifier>) -> Self {
if let QueryKind::Valid(valid) = query.kind {
self.with_clauses.extend(valid.with_clauses);
self.with_clauses.push(WithClause {
span: Span::NONE,
name: ParsedIdentifier::Valid(name.into()),
pipeline: valid.main_pipeline,
});
}
self
}
pub fn merge_as_main(mut self, query: Query) -> Query {
if let QueryKind::Valid(valid) = query.kind {
self.with_clauses.extend(valid.with_clauses);
Query {
span: Span::NONE,
kind: ValidQuery {
span: Span::NONE,
with_clauses: self.with_clauses,
main_pipeline: valid.main_pipeline,
}
.into(),
}
} else {
query
}
}
}
impl Default for QueryBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct QueryBuilderWithMain {
with_clauses: Vec<WithClause>,
main_pipeline: Arc<Pipeline>,
}
impl QueryBuilderWithMain {
pub fn with(mut self, name: impl Into<Identifier>, pipeline: impl IntoPipelineRc) -> Self {
self.with_clauses.push(WithClause {
span: Span::NONE,
name: ParsedIdentifier::Valid(name.into()),
pipeline: pipeline.into_pipeline_rc(),
});
self
}
pub fn build(self) -> Query {
Query {
span: Span::NONE,
kind: ValidQuery {
span: Span::NONE,
with_clauses: self.with_clauses,
main_pipeline: self.main_pipeline,
}
.into(),
}
}
}
pub fn query() -> QueryBuilder {
QueryBuilder::new()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tree::builder::pipeline;
#[test]
fn test_query_builder_main_then_build() {
let q = query()
.main(pipeline().from(|f| f.table_reference("events")))
.build();
match &q.kind {
crate::tree::ast::query::QueryKind::Valid(valid) => {
assert!(valid.with_clauses.is_empty());
}
_ => panic!("Expected valid query"),
}
}
#[test]
fn test_query_builder_with_then_main() {
let q = query()
.with("cte1", pipeline().from(|f| f.table_reference("events")))
.with("cte2", pipeline().from(|f| f.table_reference("logs")))
.main(pipeline().from(|f| f.table_reference("cte1")))
.build();
match &q.kind {
crate::tree::ast::query::QueryKind::Valid(valid) => {
assert_eq!(valid.with_clauses.len(), 2);
}
_ => panic!("Expected valid query"),
}
}
#[test]
fn test_query_builder_main_then_with() {
let q = query()
.main(pipeline().from(|f| f.table_reference("cte1")))
.with("cte1", pipeline().from(|f| f.table_reference("events")))
.build();
match &q.kind {
QueryKind::Valid(valid) => {
assert_eq!(valid.with_clauses.len(), 1);
}
_ => panic!("Expected valid query"),
}
}
#[test]
fn test_query_with_main_shorthand() {
let q = query()
.main(pipeline().from(|f| f.table_reference("events")))
.with("cte1", pipeline().from(|f| f.table_reference("logs")))
.build();
match &q.kind {
QueryKind::Valid(valid) => {
assert_eq!(valid.with_clauses.len(), 1);
}
_ => panic!("Expected valid query"),
}
}
}