1use serde::{Deserialize, Serialize};
5
6use crate::identifier::{Identifier, QualifiedName};
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct RoutineSignature(String);
15
16impl RoutineSignature {
17 #[must_use]
21 pub const fn new(s: String) -> Self {
22 Self(s)
23 }
24
25 #[must_use]
27 pub const fn as_str(&self) -> &str {
28 self.0.as_str()
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(tag = "kind", rename_all = "snake_case")]
45pub enum CatalogObjectRef {
46 Schema(Identifier),
48 Sequence(QualifiedName),
50 Table(QualifiedName),
52 View(QualifiedName),
54 MaterializedView(QualifiedName),
56 UserType(QualifiedName),
58 Function {
60 name: QualifiedName,
62 signature: RoutineSignature,
64 },
65 Procedure {
67 name: QualifiedName,
69 signature: RoutineSignature,
71 },
72 Statistic(QualifiedName),
74 Collation(QualifiedName),
76 Publication(Identifier),
78 Subscription(Identifier),
80}
81
82impl CatalogObjectRef {
83 #[must_use]
86 pub const fn sql_keyword(&self) -> &'static str {
87 match self {
88 Self::Schema(_) => "SCHEMA",
89 Self::Sequence(_) => "SEQUENCE",
90 Self::Table(_) => "TABLE",
91 Self::View(_) => "VIEW",
92 Self::MaterializedView(_) => "MATERIALIZED VIEW",
93 Self::UserType(_) => "TYPE",
94 Self::Function { .. } => "FUNCTION",
95 Self::Procedure { .. } => "PROCEDURE",
96 Self::Statistic(_) => "STATISTICS",
97 Self::Collation(_) => "COLLATION",
98 Self::Publication(_) => "PUBLICATION",
99 Self::Subscription(_) => "SUBSCRIPTION",
100 }
101 }
102
103 #[must_use]
111 pub fn render_target(&self) -> String {
112 match self {
113 Self::Schema(name) | Self::Publication(name) | Self::Subscription(name) => {
114 name.render_sql()
115 }
116 Self::Sequence(q)
117 | Self::Table(q)
118 | Self::View(q)
119 | Self::MaterializedView(q)
120 | Self::UserType(q)
121 | Self::Statistic(q)
122 | Self::Collation(q) => q.render_sql(),
123 Self::Function { name, signature } | Self::Procedure { name, signature } => {
124 format!("{}{}", name.render_sql(), signature.as_str())
125 }
126 }
127 }
128
129 #[must_use]
139 pub fn observation_label(&self) -> String {
140 let kind = self.sql_keyword().to_lowercase();
141 match self {
142 Self::Schema(name) | Self::Publication(name) | Self::Subscription(name) => {
143 format!("{kind} {name}")
144 }
145 Self::Sequence(q)
146 | Self::Table(q)
147 | Self::View(q)
148 | Self::MaterializedView(q)
149 | Self::UserType(q)
150 | Self::Statistic(q)
151 | Self::Collation(q) => format!("{kind} {q}"),
152 Self::Function { name, signature } | Self::Procedure { name, signature } => {
153 format!("{kind} {name}{}", signature.as_str())
154 }
155 }
156 }
157}
158
159impl std::fmt::Display for CatalogObjectRef {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 f.write_str(&self.render_target())
162 }
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
168pub struct AlterObjectOwner {
169 pub object: CatalogObjectRef,
171 pub from: Option<Identifier>,
174 pub to: Identifier,
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 fn id(s: &str) -> Identifier {
183 Identifier::from_unquoted(s).unwrap()
184 }
185
186 fn qn(schema: &str, name: &str) -> QualifiedName {
187 QualifiedName::new(id(schema), id(name))
188 }
189
190 #[test]
191 fn sql_keywords_match_pg() {
192 assert_eq!(CatalogObjectRef::Schema(id("s")).sql_keyword(), "SCHEMA");
193 assert_eq!(
194 CatalogObjectRef::Sequence(qn("s", "x")).sql_keyword(),
195 "SEQUENCE"
196 );
197 assert_eq!(CatalogObjectRef::Table(qn("s", "x")).sql_keyword(), "TABLE");
198 assert_eq!(CatalogObjectRef::View(qn("s", "x")).sql_keyword(), "VIEW");
199 assert_eq!(
200 CatalogObjectRef::MaterializedView(qn("s", "x")).sql_keyword(),
201 "MATERIALIZED VIEW"
202 );
203 assert_eq!(
204 CatalogObjectRef::Function {
205 name: qn("s", "x"),
206 signature: RoutineSignature::new("()".to_string()),
207 }
208 .sql_keyword(),
209 "FUNCTION"
210 );
211 assert_eq!(
212 CatalogObjectRef::Procedure {
213 name: qn("s", "x"),
214 signature: RoutineSignature::new("()".to_string()),
215 }
216 .sql_keyword(),
217 "PROCEDURE"
218 );
219 assert_eq!(
220 CatalogObjectRef::UserType(qn("s", "x")).sql_keyword(),
221 "TYPE"
222 );
223 assert_eq!(
224 CatalogObjectRef::Publication(id("p")).sql_keyword(),
225 "PUBLICATION"
226 );
227 assert_eq!(
228 CatalogObjectRef::Subscription(id("p")).sql_keyword(),
229 "SUBSCRIPTION"
230 );
231 assert_eq!(
232 CatalogObjectRef::Statistic(qn("s", "x")).sql_keyword(),
233 "STATISTICS"
234 );
235 assert_eq!(
236 CatalogObjectRef::Collation(qn("s", "x")).sql_keyword(),
237 "COLLATION"
238 );
239 }
240
241 #[test]
242 fn render_target_each_shape() {
243 assert_eq!(
244 CatalogObjectRef::Table(qn("app", "users")).render_target(),
245 "app.users"
246 );
247 assert_eq!(
248 CatalogObjectRef::Schema(id("billing")).render_target(),
249 "billing"
250 );
251 assert_eq!(
252 CatalogObjectRef::Publication(id("my_pub")).render_target(),
253 "my_pub"
254 );
255 assert_eq!(
256 CatalogObjectRef::Function {
257 name: qn("app", "do_thing"),
258 signature: RoutineSignature::new("(integer, text)".to_string()),
259 }
260 .render_target(),
261 "app.do_thing(integer, text)"
262 );
263 assert_eq!(
264 CatalogObjectRef::Procedure {
265 name: qn("app", "do_work"),
266 signature: RoutineSignature::new("(integer)".to_string()),
267 }
268 .render_target(),
269 "app.do_work(integer)"
270 );
271 }
272
273 #[test]
274 fn observation_label_each_shape() {
275 assert_eq!(
276 CatalogObjectRef::Table(qn("app", "users")).observation_label(),
277 "table app.users"
278 );
279 assert_eq!(
280 CatalogObjectRef::MaterializedView(qn("app", "mv")).observation_label(),
281 "materialized view app.mv"
282 );
283 assert_eq!(
284 CatalogObjectRef::Schema(id("billing")).observation_label(),
285 "schema billing"
286 );
287 assert_eq!(
288 CatalogObjectRef::UserType(qn("app", "status")).observation_label(),
289 "type app.status"
290 );
291 assert_eq!(
292 CatalogObjectRef::Function {
293 name: qn("app", "do_thing"),
294 signature: RoutineSignature::new("(integer, text)".to_string()),
295 }
296 .observation_label(),
297 "function app.do_thing(integer, text)"
298 );
299 }
300
301 #[test]
306 fn observation_label_is_unquoted_unlike_render_target() {
307 let q = qn("app", "select");
309 let obj = CatalogObjectRef::Table(q);
310 assert_eq!(obj.render_target(), "app.\"select\"");
311 assert_eq!(obj.observation_label(), "table app.select");
312 }
313}