1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Partitioning IR — partition-by clauses on partitioned parents and
//! partition-of declarations on partition children.
use serde::{Deserialize, Serialize};
use crate::identifier::{Identifier, QualifiedName};
use crate::ir::default_expr::NormalizedExpr;
/// Describes the partitioning strategy and key of a partitioned parent table.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PartitionBy {
/// The partitioning strategy (`RANGE`, `LIST`, or `HASH`).
pub strategy: PartitionStrategy,
/// The partition key columns (or expressions).
pub columns: Vec<PartitionColumn>,
}
/// The partitioning strategy declared by `PARTITION BY <strategy>`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PartitionStrategy {
/// `PARTITION BY RANGE (...)`.
Range,
/// `PARTITION BY LIST (...)`.
List,
/// `PARTITION BY HASH (...)`.
Hash,
}
/// A single element of the partition key.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PartitionColumn {
/// The key element — a plain column reference or an arbitrary expression.
pub kind: PartitionColumnKind,
/// Optional collation override.
pub collation: Option<QualifiedName>,
/// Optional operator class override.
pub opclass: Option<QualifiedName>,
}
/// Whether the partition key element is a column reference or an expression.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PartitionColumnKind {
/// A plain column reference.
Column(Identifier),
/// A parenthesized expression.
Expr(NormalizedExpr),
}
/// Declares that this table is itself a partition of a parent table.
///
/// Corresponds to `PARTITION OF <parent> <bounds>` in the DDL.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PartitionOf {
/// The schema-qualified parent table.
pub parent: QualifiedName,
/// The partition bounds clause.
pub bounds: PartitionBounds,
}
/// The bounds of a partition child table.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "kind")]
pub enum PartitionBounds {
/// `FOR VALUES FROM (...) TO (...)` — range partition.
Range {
/// The `FROM` bound datums (one per key column).
from: Vec<BoundDatum>,
/// The `TO` bound datums (one per key column).
to: Vec<BoundDatum>,
},
/// `FOR VALUES IN (...)` — list partition.
List {
/// The listed bound datums.
values: Vec<BoundDatum>,
},
/// `FOR VALUES WITH (MODULUS m, REMAINDER r)` — hash partition.
Hash {
/// Hash modulus.
modulus: u32,
/// Hash remainder.
remainder: u32,
},
/// `DEFAULT` — catches rows not matched by any other partition.
Default,
}
/// A single datum in a partition bound clause.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BoundDatum {
/// A concrete literal expression (e.g., `'2024-01-01'`, `42`).
Literal(NormalizedExpr),
/// The pseudo-datum `MINVALUE`.
MinValue,
/// The pseudo-datum `MAXVALUE`.
MaxValue,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn partition_strategy_round_trip() {
let s = PartitionStrategy::Range;
let json = serde_json::to_string(&s).unwrap();
let back: PartitionStrategy = serde_json::from_str(&json).unwrap();
assert_eq!(s, back);
}
#[test]
fn hash_bounds_round_trip() {
let b = PartitionBounds::Hash {
modulus: 4,
remainder: 1,
};
let json = serde_json::to_string(&b).unwrap();
let back: PartitionBounds = serde_json::from_str(&json).unwrap();
assert_eq!(b, back);
}
#[test]
fn default_bounds_round_trip() {
let b = PartitionBounds::Default;
let json = serde_json::to_string(&b).unwrap();
let back: PartitionBounds = serde_json::from_str(&json).unwrap();
assert_eq!(b, back);
}
}