use crate::sql::expression::identifier::SimpleIdentifier;
use crate::sql::expression::{OrderByExpression, SQLExpression};
use crate::write_utils::{maybe_newline, maybe_pad, newline_or_space, Indent};
use derive_more::From;
use ordermap::OrderMap;
use std::fmt;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Windows {
pub windows: OrderMap<SimpleIdentifier, WindowSpecification>,
}
impl Windows {
pub fn with(mut self, identifier: SimpleIdentifier, window: WindowSpecification) -> Self {
self.windows.insert(identifier, window);
self
}
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> fmt::Result {
if self.windows.is_empty() {
Ok(())
} else {
newline_or_space(f, indentation)?;
maybe_pad(f, indentation)?;
write!(f, "WINDOW")?;
for (i, (si, window_spec)) in self.windows.iter().enumerate() {
if i != 0 {
write!(f, ",")?;
}
write!(f, " {} AS ", si.to_string())?;
window_spec.fmt_indented(f, indentation.nested())?;
}
Ok(())
}
}
}
impl Display for Windows {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct WindowSpecification {
pub partition_by: Vec<SQLExpression>,
pub order_by: Vec<OrderByExpression>,
pub frame: Option<WindowFrame>,
}
impl WindowSpecification {
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> fmt::Result {
write!(f, "(")?;
let spec_indentation = indentation.nested();
if !self.partition_by.is_empty() {
newline_or_space(f, indentation)?;
maybe_pad(f, spec_indentation)?;
write!(f, "PARTITION BY ")?;
let partition_count = self.partition_by.len();
for (i, expr) in self.partition_by.iter().enumerate() {
write!(f, "{}", expr)?;
if i != partition_count - 1 {
write!(f, ", ")?;
}
}
};
if !self.order_by.is_empty() {
newline_or_space(f, indentation)?;
maybe_pad(f, spec_indentation)?;
write!(f, "ORDER BY ")?;
let order_count = self.order_by.len();
for (i, expr) in self.order_by.iter().enumerate() {
write!(f, "{}", expr)?;
if i != order_count - 1 {
write!(f, ", ")?;
}
}
};
if let Some(frame) = &self.frame {
newline_or_space(f, indentation)?;
frame.fmt_indented(f, spec_indentation)?;
}
maybe_newline(f, indentation)?;
maybe_pad(f, indentation)?;
write!(f, ")")
}
fn subelements(&self) -> usize {
self.partition_by
.iter()
.map(|e| e.subelements() + 1)
.sum::<usize>()
+ self
.order_by
.iter()
.map(|e| e.expression.subelements() + 1)
.sum::<usize>()
+ 1 }
}
impl Display for WindowSpecification {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
#[derive(Debug, Clone)]
pub enum Direction {
Asc,
Desc,
}
impl Direction {
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> fmt::Result {
maybe_pad(f, indentation)?;
match self {
Direction::Asc => write!(f, "ASC"),
Direction::Desc => write!(f, "DESC"),
}
}
}
impl Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WindowFrame {
pub frame_type: FrameType,
pub preceding: FrameBoundary,
pub following: FrameBoundary,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FrameType {
RANGE,
ROWS,
}
impl FrameType {
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> fmt::Result {
maybe_pad(f, indentation)?;
match self {
FrameType::RANGE => write!(f, "RANGE"),
FrameType::ROWS => write!(f, "ROWS"),
}
}
}
impl Display for FrameType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FrameBoundary {
Unbounded,
CurrentRowBoundary,
BoundaryExpression(Box<SQLExpression>),
}
impl WindowFrame {
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> fmt::Result {
self.frame_type.fmt_indented(f, indentation)?;
write!(f, " BETWEEN ")?;
self.render_frame_boundary(f, &self.preceding, "PRECEDING")?;
write!(f, " AND ")?;
self.render_frame_boundary(f, &self.following, "FOLLOWING")
}
fn render_frame_boundary(
&self,
f: &mut Formatter<'_>,
boundary: &FrameBoundary,
position: &str,
) -> fmt::Result {
match boundary {
FrameBoundary::Unbounded => write!(f, "UNBOUNDED {}", position),
FrameBoundary::CurrentRowBoundary => write!(f, "CURRENT ROW"),
FrameBoundary::BoundaryExpression(expr) => {
write!(f, "{} {}", expr.to_string(), position)
}
}
}
}
impl Display for WindowFrame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
#[derive(Debug, Clone, From, PartialEq, Eq)]
pub enum WindowReference {
NamedWindowReference(NamedWindowReference),
WindowSpecification(WindowSpecification),
}
impl WindowReference {
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> fmt::Result {
match self {
WindowReference::NamedWindowReference(l) => l.fmt_indented(f, indentation),
WindowReference::WindowSpecification(l) => l.fmt_indented(f, indentation),
}
}
pub fn subelements(&self) -> usize {
match self {
WindowReference::NamedWindowReference(_) => 0,
WindowReference::WindowSpecification(l) => l.subelements(),
}
}
}
impl Display for WindowReference {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NamedWindowReference {
pub name: SimpleIdentifier,
}
impl NamedWindowReference {
pub fn new(name: SimpleIdentifier) -> Self {
Self { name }
}
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> fmt::Result {
self.name.fmt_indented(f, indentation)
}
}
impl Display for NamedWindowReference {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_indented(f, Indent::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WindowExpression {
pub expression: Box<SQLExpression>,
pub window_reference: WindowReference,
}
impl WindowExpression {
pub fn new(expression: SQLExpression, window_reference: WindowReference) -> Self {
Self {
expression: Box::new(expression),
window_reference,
}
}
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: Indent) -> fmt::Result {
self.expression.fmt_indented(f, indentation)?;
write!(f, " OVER ")?;
self.window_reference.fmt_indented(f, indentation)
}
pub(crate) fn subelements(&self) -> usize {
self.expression.subelements() + self.window_reference.subelements() + 1
}
}
impl Display for WindowExpression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_indented(f, Indent::default())
}
}