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 composite_source: Option<Box<crate::expr::composites::CompositeConstantSource>>,
288 },
289 Param(usize),
291 Func {
293 name: String,
294 #[serde(default, skip_serializing_if = "Option::is_none")]
295 binding: Option<FunctionBinding>,
296 args: Vec<Expr>,
297 distinct: bool,
300 order_by: Vec<OrderBy>,
302 #[serde(default, skip_serializing_if = "FunctionOrderSyntax::is_legacy")]
303 order_syntax: FunctionOrderSyntax,
304 filter: Option<Box<Expr>>,
306 },
307 Array(Vec<Expr>),
309 Row(Vec<Expr>),
311 CompositeRow {
313 items: Vec<Expr>,
314 binding: super::CompositeRowBinding,
315 },
316 Binary {
318 op: BinaryOp,
319 lhs: Box<Expr>,
320 rhs: Box<Expr>,
321 },
322 UnaryMinus(Box<Expr>),
325 Not(Box<Expr>),
327 And(Vec<Expr>),
329 Or(Vec<Expr>),
331 IsNull {
333 expr: Box<Expr>,
334 negated: bool,
335 },
336 Between {
338 expr: Box<Expr>,
339 low: Box<Expr>,
340 high: Box<Expr>,
341 },
342 InList {
344 expr: Box<Expr>,
345 list: Vec<Expr>,
346 negated: bool,
347 },
348 WindowCall {
350 name: String,
351 args: Vec<Expr>,
352 spec: Box<WindowSpec>,
353 #[serde(default, skip_serializing_if = "Option::is_none")]
354 filter: Option<Box<Expr>>,
355 #[serde(default, skip_serializing_if = "WindowCallModifiers::is_empty")]
356 modifiers: WindowCallModifiers,
357 },
358 Case {
363 base: Option<Box<Expr>>,
364 when: Vec<(Expr, Expr)>,
365 else_branch: Option<Box<Expr>>,
366 },
367 Cast {
370 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
372 implicit: bool,
373 expr: Box<Expr>,
374 ty: String,
375 },
376 ScalarSubquery(Box<SelectStmt>),
379 Exists {
382 body: Box<SelectStmt>,
383 negated: bool,
384 },
385 InSubquery {
389 expr: Box<Expr>,
390 body: Box<SelectStmt>,
391 negated: bool,
392 },
393}
394
395impl Expr {
396 pub fn qualified_column(qualifier: impl Into<String>, column: impl Into<String>) -> Self {
397 Self::QualifiedColumn {
398 qualifier: qualifier.into(),
399 column: column.into(),
400 }
401 }
402
403 #[doc(hidden)]
405 #[expect(
406 clippy::too_many_lines,
407 reason = "exhaustive AST migration preserves every serialized variant"
408 )]
409 pub fn upgrade_legacy_serialized_dispatches(&mut self) -> bool {
410 let mut changed = false;
411 match self {
412 Self::Func {
413 name,
414 binding,
415 args,
416 order_by,
417 filter,
418 ..
419 } => {
420 for argument in args {
421 changed |= argument.upgrade_legacy_serialized_dispatches();
422 }
423 for order in order_by {
424 changed |= order.expr.upgrade_legacy_serialized_dispatches();
425 }
426 if let Some(filter) = filter {
427 changed |= filter.upgrade_legacy_serialized_dispatches();
428 }
429 changed |=
430 super::FunctionBinding::upgrade_legacy_serialized_dispatch(name, binding);
431 }
432 Self::Array(items)
433 | Self::Row(items)
434 | Self::CompositeRow { items, .. }
435 | Self::And(items)
436 | Self::Or(items) => {
437 for item in items {
438 changed |= item.upgrade_legacy_serialized_dispatches();
439 }
440 }
441 Self::Binary { lhs, rhs, .. } => {
442 changed |= lhs.upgrade_legacy_serialized_dispatches();
443 changed |= rhs.upgrade_legacy_serialized_dispatches();
444 }
445 Self::UnaryMinus(inner)
446 | Self::Not(inner)
447 | Self::IsNull { expr: inner, .. }
448 | Self::Cast { expr: inner, .. } => {
449 changed |= inner.upgrade_legacy_serialized_dispatches();
450 }
451 Self::Between { expr, low, high } => {
452 changed |= expr.upgrade_legacy_serialized_dispatches();
453 changed |= low.upgrade_legacy_serialized_dispatches();
454 changed |= high.upgrade_legacy_serialized_dispatches();
455 }
456 Self::InList { expr, list, .. } => {
457 changed |= expr.upgrade_legacy_serialized_dispatches();
458 for item in list {
459 changed |= item.upgrade_legacy_serialized_dispatches();
460 }
461 }
462 Self::WindowCall {
463 args, spec, filter, ..
464 } => {
465 for argument in args {
466 changed |= argument.upgrade_legacy_serialized_dispatches();
467 }
468 if let Some(filter) = filter {
469 changed |= filter.upgrade_legacy_serialized_dispatches();
470 }
471 for partition in &mut spec.partition_by {
472 changed |= partition.upgrade_legacy_serialized_dispatches();
473 }
474 for order in &mut spec.order_by {
475 changed |= order.expr.upgrade_legacy_serialized_dispatches();
476 }
477 if let Some(frame) = &mut spec.frame {
478 for bound in [&mut frame.start, &mut frame.end] {
479 match bound {
480 FrameBound::Preceding(expression)
481 | FrameBound::Following(expression) => {
482 changed |= expression.upgrade_legacy_serialized_dispatches();
483 }
484 FrameBound::UnboundedPreceding
485 | FrameBound::UnboundedFollowing
486 | FrameBound::CurrentRow => {}
487 }
488 }
489 }
490 }
491 Self::Case {
492 base,
493 when,
494 else_branch,
495 } => {
496 if let Some(base) = base {
497 changed |= base.upgrade_legacy_serialized_dispatches();
498 }
499 for (condition, result) in when {
500 changed |= condition.upgrade_legacy_serialized_dispatches();
501 changed |= result.upgrade_legacy_serialized_dispatches();
502 }
503 if let Some(branch) = else_branch {
504 changed |= branch.upgrade_legacy_serialized_dispatches();
505 }
506 }
507 Self::InSubquery { expr, body, .. } => {
508 changed |= expr.upgrade_legacy_serialized_dispatches();
509 changed |= body.upgrade_legacy_serialized_dispatches();
510 }
511 Self::ScalarSubquery(body) | Self::Exists { body, .. } => {
512 changed |= body.upgrade_legacy_serialized_dispatches();
513 }
514 Self::Default
515 | Self::Star
516 | Self::QualifiedStar(_)
517 | Self::Column(_)
518 | Self::QualifiedColumn { .. }
519 | Self::InternalColumn(_)
520 | Self::Literal(_)
521 | Self::TypedLiteral { .. }
522 | Self::Param(_) => {}
523 }
524 crate::type_resolution::between::restore_ast_node(self) | changed
525 }
526
527 #[must_use]
529 pub fn contains_window(&self) -> bool {
530 self.any_node(&|node| matches!(node, Self::WindowCall { .. }))
531 }
532
533 #[must_use]
535 pub fn contains_aggregate(&self) -> bool {
536 self.any_node(
537 &|node| matches!(node, Self::Func { name, .. } if is_builtin_aggregate_function(name)),
538 )
539 }
540
541 #[must_use]
543 pub fn contains_unqualified_column(&self) -> bool {
544 self.any_node(&|node| matches!(node, Self::Column(_)))
545 }
546
547 #[must_use]
549 pub fn contains_function_with_unknown_strictness(&self) -> bool {
550 self.any_node(&|node| {
551 matches!(
552 node,
553 Self::Func {
554 name,
555 args,
556 binding,
557 ..
558 } if crate::expr::bound_scalar_function_strictness(
559 name,
560 binding.as_ref(),
561 args.len(),
562 )
563 .is_none()
564 )
565 })
566 }
567
568 #[must_use]
570 pub fn any_node(&self, hit: &dyn Fn(&Self) -> bool) -> bool {
571 if hit(self) {
572 return true;
573 }
574 match self {
575 Self::Func {
576 args,
577 order_by,
578 filter,
579 ..
580 } => {
581 args.iter().any(|arg| arg.any_node(hit))
582 || order_by.iter().any(|order| order.expr.any_node(hit))
583 || filter.as_deref().is_some_and(|filter| filter.any_node(hit))
584 }
585 Self::Array(items)
586 | Self::Row(items)
587 | Self::CompositeRow { items, .. }
588 | Self::And(items)
589 | Self::Or(items) => items.iter().any(|item| item.any_node(hit)),
590 Self::UnaryMinus(expr) | Self::Not(expr) | Self::Cast { expr, .. } => {
591 expr.any_node(hit)
592 }
593 Self::Binary { lhs, rhs, .. } => lhs.any_node(hit) || rhs.any_node(hit),
594 Self::IsNull { expr, .. } | Self::InSubquery { expr, .. } => expr.any_node(hit),
595 Self::Between { expr, low, high } => {
596 expr.any_node(hit) || low.any_node(hit) || high.any_node(hit)
597 }
598 Self::InList { expr, list, .. } => {
599 expr.any_node(hit) || list.iter().any(|item| item.any_node(hit))
600 }
601 Self::Case {
602 base,
603 when,
604 else_branch,
605 } => {
606 base.as_deref().is_some_and(|base| base.any_node(hit))
607 || when
608 .iter()
609 .any(|(condition, result)| condition.any_node(hit) || result.any_node(hit))
610 || else_branch
611 .as_deref()
612 .is_some_and(|branch| branch.any_node(hit))
613 }
614 Self::WindowCall { .. }
615 | Self::Star
616 | Self::QualifiedStar(_)
617 | Self::Default
618 | Self::Column(_)
619 | Self::QualifiedColumn { .. }
620 | Self::InternalColumn(_)
621 | Self::Literal(_)
622 | Self::TypedLiteral { .. }
623 | Self::Param(_)
624 | Self::ScalarSubquery(_)
625 | Self::Exists { .. } => false,
626 }
627 }
628}
629
630#[must_use]
632pub fn is_builtin_aggregate_function(name: &str) -> bool {
633 matches!(
634 name.to_ascii_lowercase().as_str(),
635 "count"
636 | "sum"
637 | "avg"
638 | "min"
639 | "max"
640 | "string_agg"
641 | "array_agg"
642 | "bool_and"
643 | "bool_or"
644 | "stddev"
645 | "stddev_samp"
646 | "stddev_pop"
647 | "variance"
648 | "var_samp"
649 | "var_pop"
650 | "percentile_cont"
651 | "percentile_disc"
652 | "mode"
653 | "json_agg"
654 | "jsonb_agg"
655 | "json_object_agg"
656 | "jsonb_object_agg"
657 )
658}
659
660#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
661pub enum BinaryOp {
662 Equal,
663 NotEqual,
664 Less,
665 LessEqual,
666 Greater,
667 GreaterEqual,
668 Add,
669 Subtract,
670 Multiply,
671 Divide,
672}
673
674pub type ValueExpr = Expr;