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