risingwave_expr/window_function/
session.rs1use std::fmt::Display;
16use std::ops::Deref;
17use std::sync::Arc;
18
19use anyhow::Context;
20use educe::Educe;
21use risingwave_common::bail;
22use risingwave_common::row::OwnedRow;
23use risingwave_common::types::{
24 DataType, Datum, IsNegative, ScalarImpl, ScalarRefImpl, ToOwnedDatum, ToText,
25};
26use risingwave_common::util::sort_util::OrderType;
27use risingwave_common::util::value_encoding::{DatumFromProtoExt, DatumToProtoExt};
28use risingwave_pb::expr::window_frame::PbSessionFrameBounds;
29
30use super::FrameBoundsImpl;
31use crate::Result;
32use crate::expr::{
33 InputRefExpression, LiteralExpression, SyncExpression, SyncExpressionBoxExt, build_func,
34};
35
36#[derive(Debug, Clone, Eq, PartialEq, Hash)]
40pub struct SessionFrameBounds {
41 pub order_data_type: DataType,
42 pub order_type: OrderType,
43 pub gap_data_type: DataType,
44 pub gap: SessionFrameGap,
45}
46
47impl SessionFrameBounds {
48 pub(super) fn from_protobuf(bounds: &PbSessionFrameBounds) -> Result<Self> {
49 let order_data_type = DataType::from(bounds.get_order_data_type()?);
50 let order_type = OrderType::from_protobuf(bounds.get_order_type()?);
51 let gap_data_type = DataType::from(bounds.get_gap_data_type()?);
52 let gap_value = Datum::from_protobuf(bounds.get_gap()?, &gap_data_type)
53 .context("gap `Datum` is not decodable")?
54 .context("gap of session frame must be non-NULL")?;
55 let mut gap = SessionFrameGap::new(gap_value);
56 gap.prepare(&order_data_type, &gap_data_type)?;
57 Ok(Self {
58 order_data_type,
59 order_type,
60 gap_data_type,
61 gap,
62 })
63 }
64
65 pub(super) fn to_protobuf(&self) -> PbSessionFrameBounds {
66 PbSessionFrameBounds {
67 gap: Some(Some(self.gap.as_scalar_ref_impl()).to_protobuf()),
68 order_data_type: Some(self.order_data_type.to_protobuf()),
69 order_type: Some(self.order_type.to_protobuf()),
70 gap_data_type: Some(self.gap_data_type.to_protobuf()),
71 }
72 }
73}
74
75impl Display for SessionFrameBounds {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 write!(
78 f,
79 "SESSION WITH GAP {}",
80 self.gap.as_scalar_ref_impl().to_text()
81 )
82 }
83}
84
85impl FrameBoundsImpl for SessionFrameBounds {
86 fn validate(&self) -> Result<()> {
87 fn validate_non_negative(val: impl IsNegative + Display) -> Result<()> {
90 if val.is_negative() {
91 bail!("session gap should be non-negative, but {} is given", val);
92 }
93 Ok(())
94 }
95
96 match self.gap.as_scalar_ref_impl() {
97 ScalarRefImpl::Int16(val) => validate_non_negative(val)?,
98 ScalarRefImpl::Int32(val) => validate_non_negative(val)?,
99 ScalarRefImpl::Int64(val) => validate_non_negative(val)?,
100 ScalarRefImpl::Float32(val) => validate_non_negative(val)?,
101 ScalarRefImpl::Float64(val) => validate_non_negative(val)?,
102 ScalarRefImpl::Decimal(val) => validate_non_negative(val)?,
103 ScalarRefImpl::Interval(val) => {
104 if !val.is_never_negative() {
105 bail!(
106 "for session gap of type `interval`, each field should be non-negative, but {} is given",
107 val
108 );
109 }
110 if matches!(self.order_data_type, DataType::Timestamptz) {
111 if val.months() != 0 || val.days() != 0 {
113 bail!(
114 "for session order column of type `timestamptz`, gap should not have non-zero `month` and `day`",
115 );
116 }
117 }
118 }
119 _ => unreachable!(
120 "other order column data types are not supported and should be banned in frontend"
121 ),
122 }
123 Ok(())
124 }
125}
126
127impl SessionFrameBounds {
128 pub fn minimal_next_start_of(&self, end_order_value: impl ToOwnedDatum) -> Datum {
129 self.gap.for_calc().minimal_next_start_of(end_order_value)
130 }
131}
132
133#[derive(Debug, Clone, Educe)]
136#[educe(PartialEq, Eq, Hash)]
137pub struct SessionFrameGap {
138 gap: ScalarImpl,
140 #[educe(PartialEq(ignore), Hash(ignore))]
142 add_expr: Option<Arc<dyn SyncExpression>>,
143}
144
145impl Deref for SessionFrameGap {
146 type Target = ScalarImpl;
147
148 fn deref(&self) -> &Self::Target {
149 &self.gap
150 }
151}
152
153impl SessionFrameGap {
154 pub fn new(gap: ScalarImpl) -> Self {
155 Self {
156 gap,
157 add_expr: None,
158 }
159 }
160
161 fn prepare(&mut self, order_data_type: &DataType, gap_data_type: &DataType) -> Result<()> {
162 use risingwave_pb::expr::expr_node::PbType as PbExprType;
163
164 let input_expr = InputRefExpression::new(order_data_type.clone(), 0);
165 let gap_expr = LiteralExpression::new(gap_data_type.clone(), Some(self.gap.clone()));
166 let add_expr = build_func(
167 PbExprType::Add,
168 order_data_type.clone(),
169 vec![input_expr.boxed(), gap_expr.boxed()],
170 )?;
171 let crate::expr::BoxedExpression::Sync(add_expr) = add_expr else {
172 bail!("session frame gap add expression must be sync");
173 };
174 self.add_expr = Some(add_expr);
175 Ok(())
176 }
177
178 pub fn new_for_test(
179 gap: ScalarImpl,
180 order_data_type: &DataType,
181 gap_data_type: &DataType,
182 ) -> Self {
183 let mut gap = Self::new(gap);
184 gap.prepare(order_data_type, gap_data_type).unwrap();
185 gap
186 }
187
188 fn for_calc(&self) -> SessionFrameGapRef<'_> {
189 SessionFrameGapRef {
190 add_expr: self.add_expr.as_ref().unwrap().as_ref(),
191 }
192 }
193}
194
195#[derive(Debug, Educe)]
196#[educe(Clone, Copy)]
197struct SessionFrameGapRef<'a> {
198 add_expr: &'a dyn SyncExpression,
199}
200
201impl SessionFrameGapRef<'_> {
202 fn minimal_next_start_of(&self, end_order_value: impl ToOwnedDatum) -> Datum {
203 let row = OwnedRow::new(vec![end_order_value.to_owned_datum()]);
204 self.add_expr
205 .eval_row(&row)
206 .expect("just simple calculation, should succeed") }
208}