use serde::{Deserialize, Serialize};
use crate::{
backend::where_clause::WhereClause,
compiler::{
aggregation::{OrderByClause, OrderDirection},
fact_table::FactTableMetadata,
},
error::{FraiseQLError, Result},
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WindowRequest {
pub table_name: String,
pub select: Vec<WindowSelectColumn>,
pub windows: Vec<WindowFunctionRequest>,
pub where_clause: Option<WhereClause>,
pub order_by: Vec<WindowOrderBy>,
pub limit: Option<u32>,
pub offset: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum WindowSelectColumn {
Measure {
name: String,
alias: String,
},
Dimension {
path: String,
alias: String,
},
Filter {
name: String,
alias: String,
},
}
impl WindowRequest {
#[must_use]
pub fn decimal_aliases(&self, metadata: &FactTableMetadata) -> Vec<String> {
let is_decimal = |measure: &str| {
metadata.measures.iter().any(|m| {
m.name == measure && m.sql_type == crate::compiler::fact_table::SqlType::Decimal
})
};
let selected = self.select.iter().filter_map(|column| match column {
WindowSelectColumn::Measure { name, alias } if is_decimal(name) => Some(alias),
_ => None,
});
let windowed = self.windows.iter().filter_map(|window| match &window.function {
WindowFunctionSpec::Lag { field: measure, .. }
| WindowFunctionSpec::Lead { field: measure, .. }
| WindowFunctionSpec::FirstValue { field: measure }
| WindowFunctionSpec::LastValue { field: measure }
| WindowFunctionSpec::NthValue { field: measure, .. }
| WindowFunctionSpec::RunningSum { measure }
| WindowFunctionSpec::RunningAvg { measure }
| WindowFunctionSpec::RunningMin { measure }
| WindowFunctionSpec::RunningMax { measure }
| WindowFunctionSpec::RunningStddev { measure }
| WindowFunctionSpec::RunningVariance { measure }
if is_decimal(measure) =>
{
Some(&window.alias)
},
_ => None,
});
selected.chain(windowed).cloned().collect()
}
}
impl WindowSelectColumn {
#[must_use]
pub fn alias(&self) -> &str {
match self {
Self::Measure { alias, .. }
| Self::Dimension { alias, .. }
| Self::Filter { alias, .. } => alias,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WindowFunctionRequest {
pub function: WindowFunctionSpec,
pub alias: String,
pub partition_by: Vec<PartitionByColumn>,
pub order_by: Vec<WindowOrderBy>,
pub frame: Option<WindowFrame>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum WindowFunctionSpec {
RowNumber,
Rank,
DenseRank,
Ntile {
n: u32,
},
PercentRank,
CumeDist,
Lag {
field: String,
offset: i32,
default: Option<serde_json::Value>,
},
Lead {
field: String,
offset: i32,
default: Option<serde_json::Value>,
},
FirstValue {
field: String,
},
LastValue {
field: String,
},
NthValue {
field: String,
n: u32,
},
RunningSum {
measure: String,
},
RunningAvg {
measure: String,
},
RunningCount,
RunningCountField {
field: String,
},
RunningMin {
measure: String,
},
RunningMax {
measure: String,
},
RunningStddev {
measure: String,
},
RunningVariance {
measure: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum PartitionByColumn {
Dimension {
path: String,
},
Filter {
name: String,
},
Measure {
name: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WindowOrderBy {
pub field: String,
pub direction: OrderDirection,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WindowExecutionPlan {
pub table: String,
pub select: Vec<SelectColumn>,
pub windows: Vec<WindowFunction>,
pub where_clause: Option<WhereClause>,
pub order_by: Vec<OrderByClause>,
pub limit: Option<u32>,
pub offset: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelectColumn {
pub expression: String,
pub alias: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WindowFunction {
pub function: WindowFunctionType,
pub alias: String,
pub partition_by: Vec<String>,
pub order_by: Vec<OrderByClause>,
pub frame: Option<WindowFrame>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum WindowFunctionType {
RowNumber,
Rank,
DenseRank,
Ntile {
n: u32,
},
PercentRank,
CumeDist,
Lag {
field: String,
offset: i32,
default: Option<serde_json::Value>,
},
Lead {
field: String,
offset: i32,
default: Option<serde_json::Value>,
},
FirstValue {
field: String,
},
LastValue {
field: String,
},
NthValue {
field: String,
n: u32,
},
Sum {
field: String,
},
Avg {
field: String,
},
Count {
field: Option<String>,
},
Min {
field: String,
},
Max {
field: String,
},
Stddev {
field: String,
},
Variance {
field: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WindowFrame {
pub frame_type: FrameType,
pub start: FrameBoundary,
pub end: FrameBoundary,
pub exclusion: Option<FrameExclusion>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[non_exhaustive]
pub enum FrameType {
Rows,
Range,
Groups,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum FrameBoundary {
UnboundedPreceding,
NPreceding {
n: u32,
},
CurrentRow,
NFollowing {
n: u32,
},
UnboundedFollowing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum FrameExclusion {
CurrentRow,
Group,
Ties,
NoOthers,
}
mod codegen;
pub use self::codegen::{WindowLocale, WindowPlanner};
#[cfg(test)]
mod tests;