use std::collections::HashMap;
use std::sync::Arc;
use crate::catalog::{Catalog, CatalogProvider, Column, HamelinType};
use crate::tree::ast::dataset_identifier::QualifiedDatasetIdentifier;
use crate::tree::ast::identifier::SimpleIdentifier;
use crate::tree::ast::query::Query;
use crate::tree::options::{TemplateParameterKind, TypeCheckOptions};
use crate::tree::typed_ast::query::TypedStatement;
use crate::types::INTERVAL;
use crate::{parse, type_check_with_options};
#[test]
fn templated_from_intersects_row_schemas() {
let mut catalog = Catalog::default();
catalog.0.insert(
QualifiedDatasetIdentifier::from_canonical_str("db:t_alice").unwrap(),
vec![
Column::new("id", HamelinType::Int),
Column::new("x", HamelinType::String),
],
);
catalog.0.insert(
QualifiedDatasetIdentifier::from_canonical_str("db:t_bob").unwrap(),
vec![Column::new("id", HamelinType::Int)],
);
let provider = Arc::new(CatalogProvider::try_from(catalog).unwrap());
let mut tmpl = HashMap::new();
tmpl.insert(
"which".to_string(),
TemplateParameterKind::IdentifierFragment(vec!["t_alice".to_string(), "t_bob".to_string()]),
);
let q = parse(r#"FROM db:${which}"#).into_result().expect("parse");
let typed: TypedStatement = type_check_with_options::<Query>(
Arc::new(q),
TypeCheckOptions::builder()
.provider(provider)
.maybe_template_parameters(Some(Arc::new(tmpl)))
.build(),
)
.into_result()
.expect("typecheck");
let schema = typed.schema();
assert!(schema.lookup(&SimpleIdentifier::new("id")).is_some());
assert!(schema.lookup(&SimpleIdentifier::new("x")).is_none());
}
#[test]
fn templated_join_requires_explicit_alias() {
let mut catalog = Catalog::default();
catalog.0.insert(
QualifiedDatasetIdentifier::from_canonical_str("db:base").unwrap(),
vec![Column::new("id", HamelinType::Int)],
);
catalog.0.insert(
QualifiedDatasetIdentifier::from_canonical_str("db:t_alice").unwrap(),
vec![Column::new("id", HamelinType::Int)],
);
let provider = Arc::new(CatalogProvider::try_from(catalog).unwrap());
let mut tmpl = HashMap::new();
tmpl.insert(
"which".to_string(),
TemplateParameterKind::IdentifierFragment(vec!["t_alice".to_string()]),
);
let q = parse(r#"FROM db:base | JOIN db:${which} ON true"#)
.into_result()
.expect("parse");
let err = type_check_with_options::<Query>(
Arc::new(q),
TypeCheckOptions::builder()
.provider(provider)
.maybe_template_parameters(Some(Arc::new(tmpl)))
.build(),
)
.into_result()
.expect_err("templated JOIN without alias should fail");
assert!(err.to_string().contains("require an explicit alias"));
}
#[test]
fn templated_match_requires_explicit_alias() {
let mut catalog = Catalog::default();
catalog.0.insert(
QualifiedDatasetIdentifier::from_canonical_str("db:t_alice").unwrap(),
vec![Column::new("id", HamelinType::Int)],
);
let provider = Arc::new(CatalogProvider::try_from(catalog).unwrap());
let mut tmpl = HashMap::new();
tmpl.insert(
"which".to_string(),
TemplateParameterKind::IdentifierFragment(vec!["t_alice".to_string()]),
);
let q = parse(r#"MATCH db:${which}"#).into_result().expect("parse");
let err = type_check_with_options::<Query>(
Arc::new(q),
TypeCheckOptions::builder()
.provider(provider)
.maybe_template_parameters(Some(Arc::new(tmpl)))
.build(),
)
.into_result()
.expect_err("templated MATCH without alias should fail");
assert!(err.to_string().contains("require an explicit alias"));
}
#[test]
fn templated_ts_trunc_typechecks_with_interval_primitive() {
let mut catalog = Catalog::default();
catalog.0.insert(
QualifiedDatasetIdentifier::from_canonical_str("db:events").unwrap(),
vec![Column::new("timestamp", HamelinType::Timestamp)],
);
let provider = Arc::new(CatalogProvider::try_from(catalog).unwrap());
let mut tmpl = HashMap::new();
tmpl.insert(
"timeslice".to_string(),
TemplateParameterKind::Primitive(INTERVAL),
);
let q = parse("FROM db:events | SELECT bucket = timestamp @${timeslice}")
.into_result()
.expect("parse");
type_check_with_options::<Query>(
Arc::new(q),
TypeCheckOptions::builder()
.provider(provider)
.maybe_template_parameters(Some(Arc::new(tmpl)))
.build(),
)
.into_result()
.expect("typecheck");
}
#[test]
fn interval_primitive_works_as_leaf_expression() {
let mut catalog = Catalog::default();
catalog.0.insert(
QualifiedDatasetIdentifier::from_canonical_str("db:events").unwrap(),
vec![Column::new("timestamp", HamelinType::Timestamp)],
);
let provider = Arc::new(CatalogProvider::try_from(catalog).unwrap());
let mut tmpl = HashMap::new();
tmpl.insert(
"timeslice".to_string(),
TemplateParameterKind::Primitive(INTERVAL),
);
let q = parse("FROM db:events | WHERE timestamp > now() - ${timeslice}")
.into_result()
.expect("parse");
type_check_with_options::<Query>(
Arc::new(q),
TypeCheckOptions::builder()
.provider(provider)
.maybe_template_parameters(Some(Arc::new(tmpl)))
.build(),
)
.into_result()
.expect("typecheck");
}