1use rupg_common::{Error, Result, SqlState};
4use rupg_pgcatalog::builtin::{self, ProcRow};
5use rupg_sql::nodes::{
6 Equal, FRAMEOPTION_DEFAULTS, FRAMEOPTION_END_OFFSET_PRECEDING, FRAMEOPTION_EXCLUDE_CURRENT_ROW,
7 FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE,
8 FRAMEOPTION_ROWS, FRAMEOPTION_START_OFFSET_FOLLOWING, FRAMEOPTION_START_OFFSET_PRECEDING,
9 FRAMEOPTION_START_UNBOUNDED_FOLLOWING, FRAMEOPTION_START_UNBOUNDED_PRECEDING, Node, WindowDef,
10};
11use rupg_types::oid;
12
13use crate::Analyzer;
14use crate::agg::{Kind, Parts, SRF_HINT};
15use crate::coerce::{AtOpt, Context, can_coerce};
16use crate::expr::{Expr, ExprKind, WindowFunc};
17use crate::resolve::FUNC_MAX_ARGS;
18use crate::select::Target;
19use crate::sort::{SortGroup, invalid_reference};
20use crate::typcache::ordering_properties;
21use crate::typename::place;
22use crate::types;
23
24pub const FRAMEOPTION_END_UNBOUNDED_FOLLOWING: i32 = FRAMEOPTION_START_UNBOUNDED_FOLLOWING << 1;
26pub const FRAMEOPTION_END_OFFSET_FOLLOWING: i32 = FRAMEOPTION_START_OFFSET_FOLLOWING << 1;
28pub const FRAMEOPTION_START_OFFSET: i32 =
30 FRAMEOPTION_START_OFFSET_PRECEDING | FRAMEOPTION_START_OFFSET_FOLLOWING;
31pub const FRAMEOPTION_END_OFFSET: i32 =
33 FRAMEOPTION_END_OFFSET_PRECEDING | FRAMEOPTION_END_OFFSET_FOLLOWING;
34
35const BTINRANGE_PROC: i16 = 3;
37
38#[derive(Clone, Debug, PartialEq)]
40pub struct Frame {
41 pub options: i32,
43 pub start: Option<Expr>,
45 pub end: Option<Expr>,
47 pub start_in_range: u32,
49 pub end_in_range: u32,
51 pub in_range_asc: bool,
53 pub in_range_nulls_first: bool,
55}
56
57impl Default for Frame {
58 fn default() -> Self {
59 Frame {
60 options: FRAMEOPTION_DEFAULTS,
61 start: None,
62 end: None,
63 start_in_range: 0,
64 end_in_range: 0,
65 in_range_asc: false,
66 in_range_nulls_first: false,
67 }
68 }
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73pub enum FrameMode {
74 Range,
75 Rows,
76 Groups,
77}
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub enum FrameBound {
82 UnboundedPreceding,
83 Preceding,
85 CurrentRow,
86 Following,
88 UnboundedFollowing,
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub enum FrameExclusion {
94 NoOthers,
95 CurrentRow,
96 Group,
98 Ties,
100}
101
102impl Frame {
103 pub fn mode(&self) -> FrameMode {
105 if self.options & FRAMEOPTION_ROWS != 0 {
106 FrameMode::Rows
107 } else if self.options & FRAMEOPTION_GROUPS != 0 {
108 FrameMode::Groups
109 } else {
110 FrameMode::Range
111 }
112 }
113
114 pub fn start_bound(&self) -> FrameBound {
116 self.bound(0)
117 }
118
119 pub fn end_bound(&self) -> FrameBound {
121 self.bound(1)
122 }
123
124 fn bound(&self, shift: i32) -> FrameBound {
126 let has = |bit: i32| self.options & (bit << shift) != 0;
127 if has(FRAMEOPTION_START_UNBOUNDED_PRECEDING) {
128 FrameBound::UnboundedPreceding
129 } else if has(FRAMEOPTION_START_OFFSET_PRECEDING) {
130 FrameBound::Preceding
131 } else if has(FRAMEOPTION_START_OFFSET_FOLLOWING) {
132 FrameBound::Following
133 } else if has(FRAMEOPTION_START_UNBOUNDED_FOLLOWING) {
134 FrameBound::UnboundedFollowing
135 } else {
136 FrameBound::CurrentRow
137 }
138 }
139
140 pub fn exclusion(&self) -> FrameExclusion {
142 if self.options & FRAMEOPTION_EXCLUDE_CURRENT_ROW != 0 {
143 FrameExclusion::CurrentRow
144 } else if self.options & FRAMEOPTION_EXCLUDE_GROUP != 0 {
145 FrameExclusion::Group
146 } else if self.options & FRAMEOPTION_EXCLUDE_TIES != 0 {
147 FrameExclusion::Ties
148 } else {
149 FrameExclusion::NoOthers
150 }
151 }
152}
153
154#[derive(Clone, Debug, PartialEq)]
156pub struct WindowClause {
157 pub name: Option<String>,
159 pub refname: Option<String>,
161 pub partition: Vec<SortGroup>,
163 pub order: Vec<SortGroup>,
165 pub copied_order: bool,
167 pub frame: Frame,
169}
170
171fn windowing(message: String, at: Option<usize>) -> Error {
173 Error::new(SqlState::WINDOWING_ERROR, message).at_opt(at)
174}
175
176fn not_yet(what: &str, at: Option<usize>) -> Error {
178 Error::new(SqlState::FEATURE_NOT_SUPPORTED, format!("{what} is not supported yet")).at_opt(at)
179}
180
181fn no_window(name: &str, at: Option<usize>) -> Error {
183 Error::new(SqlState::UNDEFINED_OBJECT, format!("window \"{name}\" does not exist")).at_opt(at)
184}
185
186impl Analyzer<'_> {
187 pub(crate) fn window_call(
189 &mut self,
190 proc: &ProcRow,
191 aggregate: Option<&str>,
192 args: Vec<Expr>,
193 parts: Parts<'_>,
194 ty: u32,
195 at: Option<usize>,
196 ) -> Result<Expr> {
197 let Some(over) = parts.over else {
198 return Err(Error::internal("a window function call with no OVER"));
199 };
200 let unsupported =
201 |message: &str| Error::new(SqlState::FEATURE_NOT_SUPPORTED, message).at_opt(at);
202 if parts.distinct {
203 return Err(unsupported("DISTINCT is not implemented for window functions"));
204 }
205 if let Some(name) = aggregate {
206 if args.len() > FUNC_MAX_ARGS - 1 {
207 return Err(Error::new(
208 SqlState::TOO_MANY_ARGUMENTS,
209 format!("aggregates cannot have more than {} arguments", FUNC_MAX_ARGS - 1),
210 )
211 .at_opt(at));
212 }
213 if args.is_empty() && !parts.star {
214 return Err(Error::new(
215 SqlState::WRONG_OBJECT_TYPE,
216 format!("{name}(*) must be used to call a parameterless aggregate function"),
217 )
218 .at_opt(at));
219 }
220 }
221 if !parts.order.is_empty() {
222 return Err(unsupported("aggregate ORDER BY is not implemented for window functions"));
223 }
224 if aggregate.is_none() && parts.filter.is_some() {
225 return Err(unsupported(
226 "FILTER is not implemented for non-aggregate window functions",
227 ));
228 }
229 if self.srfs.0 != parts.srfs {
230 return Err(Error::new(
231 SqlState::FEATURE_NOT_SUPPORTED,
232 "window function calls cannot contain set-returning function calls",
233 )
234 .with_hint(SRF_HINT)
235 .at_opt(self.srfs.1));
236 }
237 if proc.retset {
238 return Err(Error::new(
239 SqlState::INVALID_FUNCTION_DEFINITION,
240 "window functions cannot return sets",
241 )
242 .at_opt(at));
243 }
244 if self.has_windows
245 && let Some(inner) = args.iter().find_map(Expr::first_window)
246 {
247 return Err(windowing("window function calls cannot be nested".into(), inner.location));
248 }
249 let message = match self.kind {
250 Kind::Select | Kind::OrderBy | Kind::DistinctOn => None,
251 Kind::JoinOn => Some("window functions are not allowed in JOIN conditions".to_owned()),
252 Kind::FromFunction => {
253 Some("window functions are not allowed in functions in FROM".to_owned())
254 }
255 Kind::WindowPartition
256 | Kind::WindowOrder
257 | Kind::WindowRange
258 | Kind::WindowRows
259 | Kind::WindowGroups => {
260 Some("window functions are not allowed in window definitions".to_owned())
261 }
262 Kind::Check => Some("window functions are not allowed in check constraints".to_owned()),
263 Kind::Where
264 | Kind::Having
265 | Kind::Filter
266 | Kind::GroupBy
267 | Kind::Limit
268 | Kind::Offset
269 | Kind::Values
270 | Kind::ValuesSingle
271 | Kind::UpdateSource
272 | Kind::MergeWhen
273 | Kind::Returning
274 | Kind::ColumnDefault
275 | Kind::FunctionDefault
276 | Kind::IndexExpression
277 | Kind::StatsExpression
278 | Kind::IndexPredicate
279 | Kind::ExecuteParameter
280 | Kind::Transform
281 | Kind::TriggerWhen
282 | Kind::CallArgument
283 | Kind::PartitionBound
284 | Kind::PartitionExpression
285 | Kind::Generated => {
286 Some(format!("window functions are not allowed in {}", self.kind.name()))
287 }
288 Kind::Other => return Err(not_yet("a window function in this place", at)),
289 };
290 if let Some(message) = message {
291 return Err(windowing(message, at));
292 }
293 let winref = if let Some(name) = &over.name {
294 let found = self.windowdefs.iter().position(|d| d.name.as_ref() == Some(name));
295 match found {
296 Some(i) => i + 1,
297 None => return Err(no_window(name, place(over.location))),
298 }
299 } else {
300 let same = |d: &WindowDef| {
302 d.refname == over.refname
303 && d.partitionClause.equal(&over.partitionClause)
304 && d.orderClause.equal(&over.orderClause)
305 && d.frameOptions == over.frameOptions
306 && d.startOffset.equal(&over.startOffset)
307 && d.endOffset.equal(&over.endOffset)
308 };
309 match self.windowdefs.iter().position(same) {
310 Some(i) => i + 1,
311 None => {
312 self.windowdefs.push(over.clone());
313 self.windowdefs.len()
314 }
315 }
316 };
317 self.has_windows = true;
318 let call = WindowFunc {
319 oid: proc.oid,
320 args,
321 winref,
322 star: parts.star,
323 agg: aggregate.is_some(),
324 filter: parts.filter.map(Box::new),
325 nulls: parts.nulls,
326 };
327 Ok(Expr { kind: ExprKind::Window(Box::new(call)), ty, typmod: -1, location: at })
328 }
329
330 pub(crate) fn window_clauses(
332 &mut self,
333 targets: &mut Vec<Target>,
334 ) -> Result<Vec<WindowClause>> {
335 let defs = self.windowdefs.clone();
336 let mut result: Vec<WindowClause> = Vec::with_capacity(defs.len());
337 for def in &defs {
338 let at = place(def.location);
339 let named = |name: &str| result.iter().find(|w| w.name.as_deref() == Some(name));
340 if let Some(name) = &def.name
341 && named(name).is_some()
342 {
343 return Err(windowing(format!("window \"{name}\" is already defined"), at));
344 }
345 let base = match &def.refname {
346 Some(refname) => {
347 Some(named(refname).ok_or_else(|| no_window(refname, at))?.clone())
348 }
349 None => None,
350 };
351 let order = self.window_order(&def.orderClause, targets)?;
352 let partition = self.window_partition(&def.partitionClause, targets, &order)?;
353 let mut window = WindowClause {
354 name: def.name.as_deref().map(str::to_owned),
355 refname: def.refname.as_deref().map(str::to_owned),
356 partition,
357 order,
358 copied_order: false,
359 frame: Frame::default(),
360 };
361 if let Some(base) = base {
362 let refname = &base.name.unwrap_or_default();
363 if !window.partition.is_empty() {
364 return Err(windowing(
365 format!("cannot override PARTITION BY clause of window \"{refname}\""),
366 at,
367 ));
368 }
369 window.partition = base.partition;
370 if !window.order.is_empty() && !base.order.is_empty() {
371 return Err(windowing(
372 format!("cannot override ORDER BY clause of window \"{refname}\""),
373 at,
374 ));
375 }
376 if window.order.is_empty() {
377 window.order = base.order;
378 window.copied_order = true;
379 }
380 if base.frame.options != FRAMEOPTION_DEFAULTS {
381 let error = windowing(
382 format!("cannot copy window \"{refname}\" because it has a frame clause"),
383 at,
384 );
385 let bare = def.name.is_none()
387 && def.orderClause.is_empty()
388 && def.frameOptions == FRAMEOPTION_DEFAULTS;
389 return Err(if bare {
390 error.with_hint("Omit the parentheses in this OVER clause.")
391 } else {
392 error
393 });
394 }
395 }
396 let options = def.frameOptions;
397 window.frame.options = options;
398 let mut family = (0, 0);
400 if options & FRAMEOPTION_RANGE != 0
401 && options & (FRAMEOPTION_START_OFFSET | FRAMEOPTION_END_OFFSET) != 0
402 {
403 let [item] = window.order.as_slice() else {
404 return Err(windowing(
405 "RANGE with offset PRECEDING/FOLLOWING requires exactly one ORDER BY column"
406 .into(),
407 at,
408 ));
409 };
410 let (opfamily, intype, descending) = ordering_properties(item.operator)
411 .ok_or_else(|| {
412 Error::internal(format!(
413 "operator {} is not a valid ordering operator",
414 item.operator
415 ))
416 })?;
417 family = (opfamily, intype);
418 window.frame.in_range_asc = !descending;
419 window.frame.in_range_nulls_first = item.nulls_first;
420 }
421 if options & FRAMEOPTION_GROUPS != 0 && window.order.is_empty() {
422 return Err(windowing("GROUPS mode requires an ORDER BY clause".into(), at));
423 }
424 (window.frame.start, window.frame.start_in_range) =
425 self.frame_offset(options, family, def.startOffset.as_ref())?;
426 (window.frame.end, window.frame.end_in_range) =
427 self.frame_offset(options, family, def.endOffset.as_ref())?;
428 result.push(window);
429 }
430 Ok(result)
431 }
432
433 fn frame_offset(
435 &mut self,
436 options: i32,
437 (family, intype): (u32, u32),
438 node: Option<&Node>,
439 ) -> Result<(Option<Expr>, u32)> {
440 let Some(node) = node else { return Ok((None, 0)) };
441 let (construct, expr, func) = if options & FRAMEOPTION_ROWS != 0 {
442 let expr = self.with_kind(Kind::WindowRows, |a| a.transform(Some(node)))?;
443 ("ROWS", self.coerce_to_specific(expr, oid::INT8, "ROWS")?, 0)
444 } else if options & FRAMEOPTION_RANGE != 0 {
445 let expr = self.with_kind(Kind::WindowRange, |a| a.transform(Some(node)))?;
446 let ty = expr.ty;
447 let preferred = if ty == oid::UNKNOWN { intype } else { ty };
449 let (mut funcs, mut matches) = (0, 0);
450 let (mut selected, mut func) = (0, 0);
451 for proc in builtin::amprocs(family, intype) {
452 if proc.number != BTINRANGE_PROC {
453 continue;
454 }
455 funcs += 1;
456 if !can_coerce(&[ty], &[proc.right], Context::Implicit) {
457 continue;
458 }
459 matches += 1;
460 if selected != preferred {
461 (selected, func) = (proc.right, proc.proc);
462 }
463 }
464 let unsupported = |message: String| {
465 Error::new(SqlState::FEATURE_NOT_SUPPORTED, message).at_opt(expr.place())
466 };
467 let column = types::name(intype);
468 if funcs == 0 {
469 return Err(unsupported(format!(
470 "RANGE with offset PRECEDING/FOLLOWING is not supported for column type {column}"
471 )));
472 }
473 if matches == 0 {
474 return Err(unsupported(format!(
475 "RANGE with offset PRECEDING/FOLLOWING is not supported for column type {column} and offset type {}",
476 types::name(ty)
477 ))
478 .with_hint("Cast the offset value to an appropriate type."));
479 }
480 if matches != 1 && selected != preferred {
481 return Err(unsupported(format!(
482 "RANGE with offset PRECEDING/FOLLOWING has multiple interpretations for column type {column} and offset type {}",
483 types::name(ty)
484 ))
485 .with_hint("Cast the offset value to the exact intended type."));
486 }
487 ("RANGE", self.coerce_to_specific(expr, selected, "RANGE")?, func)
488 } else {
489 let expr = self.with_kind(Kind::WindowGroups, |a| a.transform(Some(node)))?;
490 ("GROUPS", self.coerce_to_specific(expr, oid::INT8, "GROUPS")?, 0)
491 };
492 if let Some(var) = expr.first_var() {
493 return Err(invalid_reference(
494 format!("argument of {construct} must not contain variables"),
495 var.location,
496 ));
497 }
498 Ok((Some(expr), func))
499 }
500}