1use serde::{Deserialize, Serialize};
8use std::sync::atomic::{AtomicU64, Ordering};
9use uqa_core::Value;
10
11use super::{FunctionBinding, SelectStmt};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[doc(hidden)]
16pub struct InternalRelationId(u64);
17
18impl InternalRelationId {
19 #[must_use]
21 pub fn allocate() -> Self {
22 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
23 let id = uqa_core::atomic::try_update_u64(
24 &NEXT_ID,
25 Ordering::Relaxed,
26 Ordering::Relaxed,
27 |current| current.checked_add(1),
28 )
29 .expect("internal relation identity space exhausted");
30 Self(id)
31 }
32
33 #[must_use]
35 pub fn column(self, attribute: usize) -> InternalColumnRef {
36 InternalColumnRef {
37 relation: self,
38 attribute: u32::try_from(attribute).expect("internal relation attribute exceeds u32"),
39 }
40 }
41
42 #[must_use]
43 pub const fn raw(self) -> u64 {
44 self.0
45 }
46
47 #[must_use]
48 pub const fn from_raw(raw: u64) -> Self {
49 Self(raw)
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
55#[doc(hidden)]
56pub struct InternalColumnRef {
57 relation: InternalRelationId,
58 attribute: u32,
59}
60
61impl InternalColumnRef {
62 #[must_use]
63 pub const fn relation(self) -> InternalRelationId {
64 self.relation
65 }
66
67 #[must_use]
68 pub const fn attribute(self) -> usize {
69 self.attribute as usize
70 }
71
72 #[must_use]
73 pub const fn from_raw(relation: u64, attribute: u32) -> Self {
74 Self {
75 relation: InternalRelationId::from_raw(relation),
76 attribute,
77 }
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub struct Projection {
83 pub expr: Expr,
84 pub alias: Option<String>,
85}
86
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88pub struct OrderBy {
89 pub expr: Expr,
90 pub descending: bool,
91 pub nulls: Option<NullsOrder>,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
98pub enum NullsOrder {
99 First,
100 Last,
101}
102
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105pub struct WindowDefinition<S = WindowSpec> {
106 pub name: Option<String>,
107 pub inherited: Option<usize>,
108 pub spec: S,
109}
110
111#[derive(Debug, Clone, PartialEq)]
113pub struct WindowDefinitionSyntax(pub(crate) serde_json::Value);
114
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct WindowSpec {
117 #[serde(skip)]
119 pub raw_definition: Option<WindowDefinitionSyntax>,
120 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub definition: Option<usize>,
123 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub reference: Option<WindowReference>,
126 pub partition_by: Vec<Expr>,
127 pub order_by: Vec<OrderBy>,
128 pub frame: Option<WindowFrame>,
131}
132
133impl WindowSpec {
134 pub fn expressions_mut(&mut self) -> impl Iterator<Item = &mut Expr> {
136 self.partition_by
137 .iter_mut()
138 .chain(self.order_by.iter_mut().map(|order| &mut order.expr))
139 .chain(
140 self.frame
141 .iter_mut()
142 .flat_map(|frame| [&mut frame.start, &mut frame.end])
143 .filter_map(|bound| match bound {
144 FrameBound::Preceding(value) | FrameBound::Following(value) => {
145 Some(value.as_mut())
146 }
147 _ => None,
148 }),
149 )
150 }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub struct WindowReference {
155 pub name: String,
156 pub kind: WindowReferenceKind,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
160pub enum WindowReferenceKind {
161 Direct,
163 Copy,
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
168pub struct WindowFrame {
169 pub mode: FrameMode,
170 pub start: FrameBound,
171 pub end: FrameBound,
172 #[serde(default = "super::default_true")]
174 pub between: bool,
175 #[serde(default, skip_serializing_if = "FrameExclusion::is_no_others")]
176 pub exclusion: FrameExclusion,
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
180pub enum FrameMode {
181 Rows,
182 Range,
183 Groups,
184}
185
186#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
188pub enum FunctionCallSyntax {
189 #[default]
190 Legacy,
191 Ordinary,
192 WithinGroup,
193 Extract,
194}
195
196pub type FunctionOrderSyntax = FunctionCallSyntax;
198
199impl FunctionCallSyntax {
200 #[must_use]
201 pub const fn is_legacy(&self) -> bool {
202 matches!(self, Self::Legacy)
203 }
204}
205
206#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
208pub struct WindowCallModifiers {
209 pub distinct: bool,
210 pub ordered: bool,
212 pub within_group: bool,
213}
214
215impl WindowCallModifiers {
216 #[must_use]
217 pub const fn is_empty(&self) -> bool {
218 !self.distinct && !self.ordered && !self.within_group
219 }
220}
221
222#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
224pub enum FrameExclusion {
225 #[default]
226 NoOthers,
227 CurrentRow,
228 Group,
230 Ties,
232}
233
234impl FrameExclusion {
235 #[must_use]
236 pub const fn is_no_others(&self) -> bool {
237 matches!(self, Self::NoOthers)
238 }
239
240 #[must_use]
242 pub const fn sql(self) -> Option<&'static str> {
243 match self {
244 Self::NoOthers => None,
245 Self::CurrentRow => Some("EXCLUDE CURRENT ROW"),
246 Self::Group => Some("EXCLUDE GROUP"),
247 Self::Ties => Some("EXCLUDE TIES"),
248 }
249 }
250}
251
252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
253pub enum FrameBound {
254 UnboundedPreceding,
255 UnboundedFollowing,
256 CurrentRow,
257 Preceding(Box<Expr>),
258 Following(Box<Expr>),
259}
260
261#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
263pub enum Expr {
264 Star,
265 QualifiedStar(String),
267 Default,
269 Column(String),
271 QualifiedColumn {
273 qualifier: String,
274 column: String,
275 },
276 #[doc(hidden)]
278 InternalColumn(InternalColumnRef),
279 Literal(Value),
280 #[doc(hidden)]
282 TypedLiteral {
283 value: Value,
284 ty: String,
285 #[serde(default, skip_serializing_if = "Option::is_none")]
287 collation_oid: Option<u32>,
288 #[serde(default, skip_serializing_if = "Option::is_none")]
290 composite_source: Option<Box<crate::expr::composites::CompositeConstantSource>>,
291 },
292 Param(usize),
294 Func {
296 name: String,
297 #[serde(default, skip_serializing_if = "Option::is_none")]
298 binding: Option<FunctionBinding>,
299 args: Vec<Expr>,
300 distinct: bool,
303 order_by: Vec<OrderBy>,
305 #[serde(default, skip_serializing_if = "FunctionOrderSyntax::is_legacy")]
306 order_syntax: FunctionOrderSyntax,
307 filter: Option<Box<Expr>>,
309 },
310 Array(Vec<Expr>),
312 Row(Vec<Expr>),
314 CompositeRow {
316 items: Vec<Expr>,
317 binding: super::CompositeRowBinding,
318 },
319 Binary {
321 op: BinaryOp,
322 lhs: Box<Expr>,
323 rhs: Box<Expr>,
324 },
325 UnaryMinus(Box<Expr>),
328 Not(Box<Expr>),
330 And(Vec<Expr>),
332 Or(Vec<Expr>),
334 IsNull {
336 expr: Box<Expr>,
337 negated: bool,
338 },
339 Between {
341 expr: Box<Expr>,
342 low: Box<Expr>,
343 high: Box<Expr>,
344 },
345 InList {
347 expr: Box<Expr>,
348 list: Vec<Expr>,
349 negated: bool,
350 },
351 WindowCall {
353 name: String,
354 args: Vec<Expr>,
355 spec: Box<WindowSpec>,
356 #[serde(default, skip_serializing_if = "Option::is_none")]
357 filter: Option<Box<Expr>>,
358 #[serde(default, skip_serializing_if = "WindowCallModifiers::is_empty")]
359 modifiers: WindowCallModifiers,
360 },
361 Case {
366 base: Option<Box<Expr>>,
367 when: Vec<(Expr, Expr)>,
368 else_branch: Option<Box<Expr>>,
369 },
370 Cast {
373 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
375 implicit: bool,
376 expr: Box<Expr>,
377 ty: String,
378 },
379 ScalarSubquery(Box<SelectStmt>),
382 Exists {
385 body: Box<SelectStmt>,
386 negated: bool,
387 },
388 InSubquery {
392 expr: Box<Expr>,
393 body: Box<SelectStmt>,
394 negated: bool,
395 },
396}
397
398impl Expr {
399 pub fn qualified_column(qualifier: impl Into<String>, column: impl Into<String>) -> Self {
400 Self::QualifiedColumn {
401 qualifier: qualifier.into(),
402 column: column.into(),
403 }
404 }
405
406 #[doc(hidden)]
408 #[expect(
409 clippy::too_many_lines,
410 reason = "exhaustive AST migration preserves every serialized variant"
411 )]
412 pub fn upgrade_legacy_serialized_dispatches(&mut self) -> bool {
413 let mut changed = false;
414 match self {
415 Self::Func {
416 name,
417 binding,
418 args,
419 order_by,
420 filter,
421 ..
422 } => {
423 for argument in args.iter_mut() {
424 changed |= argument.upgrade_legacy_serialized_dispatches();
425 }
426 for order in order_by {
427 changed |= order.expr.upgrade_legacy_serialized_dispatches();
428 }
429 if let Some(filter) = filter {
430 changed |= filter.upgrade_legacy_serialized_dispatches();
431 }
432 changed |=
433 super::FunctionBinding::upgrade_legacy_serialized_dispatch(name, binding);
434 changed |= super::FunctionBinding::upgrade_legacy_array_slices(
435 binding,
436 args.len(),
437 |index| matches!(args[index], Expr::Literal(Value::Null)),
438 );
439 }
440 Self::Array(items)
441 | Self::Row(items)
442 | Self::CompositeRow { items, .. }
443 | Self::And(items)
444 | Self::Or(items) => {
445 for item in items {
446 changed |= item.upgrade_legacy_serialized_dispatches();
447 }
448 }
449 Self::Binary { lhs, rhs, .. } => {
450 changed |= lhs.upgrade_legacy_serialized_dispatches();
451 changed |= rhs.upgrade_legacy_serialized_dispatches();
452 }
453 Self::UnaryMinus(inner)
454 | Self::Not(inner)
455 | Self::IsNull { expr: inner, .. }
456 | Self::Cast { expr: inner, .. } => {
457 changed |= inner.upgrade_legacy_serialized_dispatches();
458 }
459 Self::Between { expr, low, high } => {
460 changed |= expr.upgrade_legacy_serialized_dispatches();
461 changed |= low.upgrade_legacy_serialized_dispatches();
462 changed |= high.upgrade_legacy_serialized_dispatches();
463 }
464 Self::InList { expr, list, .. } => {
465 changed |= expr.upgrade_legacy_serialized_dispatches();
466 for item in list {
467 changed |= item.upgrade_legacy_serialized_dispatches();
468 }
469 }
470 Self::WindowCall {
471 args, spec, filter, ..
472 } => {
473 for argument in args {
474 changed |= argument.upgrade_legacy_serialized_dispatches();
475 }
476 if let Some(filter) = filter {
477 changed |= filter.upgrade_legacy_serialized_dispatches();
478 }
479 for partition in &mut spec.partition_by {
480 changed |= partition.upgrade_legacy_serialized_dispatches();
481 }
482 for order in &mut spec.order_by {
483 changed |= order.expr.upgrade_legacy_serialized_dispatches();
484 }
485 if let Some(frame) = &mut spec.frame {
486 for bound in [&mut frame.start, &mut frame.end] {
487 match bound {
488 FrameBound::Preceding(expression)
489 | FrameBound::Following(expression) => {
490 changed |= expression.upgrade_legacy_serialized_dispatches();
491 }
492 FrameBound::UnboundedPreceding
493 | FrameBound::UnboundedFollowing
494 | FrameBound::CurrentRow => {}
495 }
496 }
497 }
498 }
499 Self::Case {
500 base,
501 when,
502 else_branch,
503 } => {
504 if let Some(base) = base {
505 changed |= base.upgrade_legacy_serialized_dispatches();
506 }
507 for (condition, result) in when {
508 changed |= condition.upgrade_legacy_serialized_dispatches();
509 changed |= result.upgrade_legacy_serialized_dispatches();
510 }
511 if let Some(branch) = else_branch {
512 changed |= branch.upgrade_legacy_serialized_dispatches();
513 }
514 }
515 Self::InSubquery { expr, body, .. } => {
516 changed |= expr.upgrade_legacy_serialized_dispatches();
517 changed |= body.upgrade_legacy_serialized_dispatches();
518 }
519 Self::ScalarSubquery(body) | Self::Exists { body, .. } => {
520 changed |= body.upgrade_legacy_serialized_dispatches();
521 }
522 Self::Default
523 | Self::Star
524 | Self::QualifiedStar(_)
525 | Self::Column(_)
526 | Self::QualifiedColumn { .. }
527 | Self::InternalColumn(_)
528 | Self::Literal(_)
529 | Self::TypedLiteral { .. }
530 | Self::Param(_) => {}
531 }
532 crate::type_resolution::between::restore_ast_node(self) | changed
533 }
534
535 #[must_use]
537 pub fn contains_window(&self) -> bool {
538 self.any_node(&|node| matches!(node, Self::WindowCall { .. }))
539 }
540
541 #[must_use]
543 pub fn contains_aggregate(&self) -> bool {
544 self.any_node(
545 &|node| matches!(node, Self::Func { name, .. } if is_builtin_aggregate_function(name)),
546 )
547 }
548
549 #[must_use]
551 pub fn contains_unqualified_column(&self) -> bool {
552 self.any_node(&|node| matches!(node, Self::Column(_)))
553 }
554
555 #[must_use]
557 pub fn contains_function_with_unknown_strictness(&self) -> bool {
558 self.any_node(&|node| {
559 matches!(
560 node,
561 Self::Func {
562 name,
563 args,
564 binding,
565 ..
566 } if crate::expr::bound_scalar_function_strictness(
567 name,
568 binding.as_ref(),
569 args.len(),
570 )
571 .is_none()
572 )
573 })
574 }
575
576 #[must_use]
578 pub fn any_node(&self, hit: &dyn Fn(&Self) -> bool) -> bool {
579 if hit(self) {
580 return true;
581 }
582 match self {
583 Self::Func {
584 args,
585 order_by,
586 filter,
587 ..
588 } => {
589 args.iter().any(|arg| arg.any_node(hit))
590 || order_by.iter().any(|order| order.expr.any_node(hit))
591 || filter.as_deref().is_some_and(|filter| filter.any_node(hit))
592 }
593 Self::Array(items)
594 | Self::Row(items)
595 | Self::CompositeRow { items, .. }
596 | Self::And(items)
597 | Self::Or(items) => items.iter().any(|item| item.any_node(hit)),
598 Self::UnaryMinus(expr) | Self::Not(expr) | Self::Cast { expr, .. } => {
599 expr.any_node(hit)
600 }
601 Self::Binary { lhs, rhs, .. } => lhs.any_node(hit) || rhs.any_node(hit),
602 Self::IsNull { expr, .. } | Self::InSubquery { expr, .. } => expr.any_node(hit),
603 Self::Between { expr, low, high } => {
604 expr.any_node(hit) || low.any_node(hit) || high.any_node(hit)
605 }
606 Self::InList { expr, list, .. } => {
607 expr.any_node(hit) || list.iter().any(|item| item.any_node(hit))
608 }
609 Self::Case {
610 base,
611 when,
612 else_branch,
613 } => {
614 base.as_deref().is_some_and(|base| base.any_node(hit))
615 || when
616 .iter()
617 .any(|(condition, result)| condition.any_node(hit) || result.any_node(hit))
618 || else_branch
619 .as_deref()
620 .is_some_and(|branch| branch.any_node(hit))
621 }
622 Self::WindowCall { .. }
623 | Self::Star
624 | Self::QualifiedStar(_)
625 | Self::Default
626 | Self::Column(_)
627 | Self::QualifiedColumn { .. }
628 | Self::InternalColumn(_)
629 | Self::Literal(_)
630 | Self::TypedLiteral { .. }
631 | Self::Param(_)
632 | Self::ScalarSubquery(_)
633 | Self::Exists { .. } => false,
634 }
635 }
636}
637
638#[must_use]
640pub fn is_builtin_aggregate_function(name: &str) -> bool {
641 matches!(
642 name.to_ascii_lowercase().as_str(),
643 "count"
644 | "sum"
645 | "avg"
646 | "min"
647 | "max"
648 | "string_agg"
649 | "array_agg"
650 | "bool_and"
651 | "bool_or"
652 | "stddev"
653 | "stddev_samp"
654 | "stddev_pop"
655 | "variance"
656 | "var_samp"
657 | "var_pop"
658 | "percentile_cont"
659 | "percentile_disc"
660 | "mode"
661 | "json_agg"
662 | "jsonb_agg"
663 | "json_object_agg"
664 | "jsonb_object_agg"
665 )
666}
667
668#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
669pub enum BinaryOp {
670 Equal,
671 NotEqual,
672 Less,
673 LessEqual,
674 Greater,
675 GreaterEqual,
676 Add,
677 Subtract,
678 Multiply,
679 Divide,
680}
681
682pub type ValueExpr = Expr;