1#[cfg(feature = "std")]
8use std::borrow::Cow;
9
10#[cfg(all(feature = "alloc", not(feature = "std")))]
11use alloc::borrow::Cow;
12
13#[cfg(feature = "serde")]
14use crate::serde_helpers::{cow_from_string, cow_option_from_string};
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22pub struct ViewWithOptionDef {
23 pub check_option: Option<&'static str>,
25 pub security_barrier: bool,
27 pub security_invoker: bool,
29 pub fillfactor: Option<i32>,
31 pub toast_tuple_target: Option<i32>,
33 pub parallel_workers: Option<i32>,
35 pub autovacuum_enabled: Option<bool>,
37 pub vacuum_index_cleanup: Option<&'static str>,
39 pub vacuum_truncate: Option<bool>,
41 pub autovacuum_vacuum_threshold: Option<i32>,
43 pub autovacuum_vacuum_scale_factor: Option<i32>,
45 pub autovacuum_vacuum_cost_delay: Option<i32>,
47 pub autovacuum_vacuum_cost_limit: Option<i32>,
49 pub autovacuum_freeze_min_age: Option<i64>,
51 pub autovacuum_freeze_max_age: Option<i64>,
53 pub autovacuum_freeze_table_age: Option<i64>,
55 pub autovacuum_multixact_freeze_min_age: Option<i64>,
57 pub autovacuum_multixact_freeze_max_age: Option<i64>,
59 pub autovacuum_multixact_freeze_table_age: Option<i64>,
61 pub log_autovacuum_min_duration: Option<i32>,
63 pub user_catalog_table: Option<bool>,
65}
66
67impl ViewWithOptionDef {
68 #[must_use]
70 pub const fn new() -> Self {
71 Self {
72 check_option: None,
73 security_barrier: false,
74 security_invoker: false,
75 fillfactor: None,
76 toast_tuple_target: None,
77 parallel_workers: None,
78 autovacuum_enabled: None,
79 vacuum_index_cleanup: None,
80 vacuum_truncate: None,
81 autovacuum_vacuum_threshold: None,
82 autovacuum_vacuum_scale_factor: None,
83 autovacuum_vacuum_cost_delay: None,
84 autovacuum_vacuum_cost_limit: None,
85 autovacuum_freeze_min_age: None,
86 autovacuum_freeze_max_age: None,
87 autovacuum_freeze_table_age: None,
88 autovacuum_multixact_freeze_min_age: None,
89 autovacuum_multixact_freeze_max_age: None,
90 autovacuum_multixact_freeze_table_age: None,
91 log_autovacuum_min_duration: None,
92 user_catalog_table: None,
93 }
94 }
95
96 #[must_use]
98 pub const fn check_option(self, option: &'static str) -> Self {
99 Self {
100 check_option: Some(option),
101 ..self
102 }
103 }
104
105 #[must_use]
107 pub const fn security_barrier(self) -> Self {
108 Self {
109 security_barrier: true,
110 ..self
111 }
112 }
113
114 #[must_use]
116 pub const fn security_invoker(self) -> Self {
117 Self {
118 security_invoker: true,
119 ..self
120 }
121 }
122
123 #[must_use]
125 pub const fn into_view_with_option(self) -> ViewWithOption {
126 ViewWithOption {
127 check_option: match self.check_option {
128 Some(s) => Some(Cow::Borrowed(s)),
129 None => None,
130 },
131 security_barrier: if self.security_barrier {
132 Some(true)
133 } else {
134 None
135 },
136 security_invoker: if self.security_invoker {
137 Some(true)
138 } else {
139 None
140 },
141 fillfactor: self.fillfactor,
142 toast_tuple_target: self.toast_tuple_target,
143 parallel_workers: self.parallel_workers,
144 autovacuum_enabled: self.autovacuum_enabled,
145 vacuum_index_cleanup: match self.vacuum_index_cleanup {
146 Some(s) => Some(Cow::Borrowed(s)),
147 None => None,
148 },
149 vacuum_truncate: self.vacuum_truncate,
150 autovacuum_vacuum_threshold: self.autovacuum_vacuum_threshold,
151 autovacuum_vacuum_scale_factor: self.autovacuum_vacuum_scale_factor,
152 autovacuum_vacuum_cost_delay: self.autovacuum_vacuum_cost_delay,
153 autovacuum_vacuum_cost_limit: self.autovacuum_vacuum_cost_limit,
154 autovacuum_freeze_min_age: self.autovacuum_freeze_min_age,
155 autovacuum_freeze_max_age: self.autovacuum_freeze_max_age,
156 autovacuum_freeze_table_age: self.autovacuum_freeze_table_age,
157 autovacuum_multixact_freeze_min_age: self.autovacuum_multixact_freeze_min_age,
158 autovacuum_multixact_freeze_max_age: self.autovacuum_multixact_freeze_max_age,
159 autovacuum_multixact_freeze_table_age: self.autovacuum_multixact_freeze_table_age,
160 log_autovacuum_min_duration: self.log_autovacuum_min_duration,
161 user_catalog_table: self.user_catalog_table,
162 }
163 }
164}
165
166impl Default for ViewWithOptionDef {
167 fn default() -> Self {
168 Self::new()
169 }
170}
171
172#[derive(Clone, Debug, PartialEq, Eq)]
174#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
175#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
176pub struct ViewWithOption {
177 #[cfg_attr(
179 feature = "serde",
180 serde(
181 skip_serializing_if = "Option::is_none",
182 deserialize_with = "cow_option_from_string"
183 )
184 )]
185 pub check_option: Option<Cow<'static, str>>,
186
187 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
189 pub security_barrier: Option<bool>,
190
191 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
193 pub security_invoker: Option<bool>,
194
195 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
197 pub fillfactor: Option<i32>,
198
199 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
201 pub toast_tuple_target: Option<i32>,
202
203 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
205 pub parallel_workers: Option<i32>,
206
207 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
209 pub autovacuum_enabled: Option<bool>,
210
211 #[cfg_attr(
213 feature = "serde",
214 serde(
215 skip_serializing_if = "Option::is_none",
216 deserialize_with = "cow_option_from_string"
217 )
218 )]
219 pub vacuum_index_cleanup: Option<Cow<'static, str>>,
220
221 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
223 pub vacuum_truncate: Option<bool>,
224
225 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
227 pub autovacuum_vacuum_threshold: Option<i32>,
228
229 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
231 pub autovacuum_vacuum_scale_factor: Option<i32>,
232
233 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
235 pub autovacuum_vacuum_cost_delay: Option<i32>,
236
237 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
239 pub autovacuum_vacuum_cost_limit: Option<i32>,
240
241 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
243 pub autovacuum_freeze_min_age: Option<i64>,
244
245 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
247 pub autovacuum_freeze_max_age: Option<i64>,
248
249 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
251 pub autovacuum_freeze_table_age: Option<i64>,
252
253 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
255 pub autovacuum_multixact_freeze_min_age: Option<i64>,
256
257 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
259 pub autovacuum_multixact_freeze_max_age: Option<i64>,
260
261 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
263 pub autovacuum_multixact_freeze_table_age: Option<i64>,
264
265 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
267 pub log_autovacuum_min_duration: Option<i32>,
268
269 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
271 pub user_catalog_table: Option<bool>,
272}
273
274impl Default for ViewWithOption {
275 fn default() -> Self {
276 ViewWithOptionDef::new().into_view_with_option()
277 }
278}
279
280impl From<ViewWithOptionDef> for ViewWithOption {
281 fn from(def: ViewWithOptionDef) -> Self {
282 def.into_view_with_option()
283 }
284}
285
286#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
292pub struct ViewDef {
293 pub schema: &'static str,
295 pub name: &'static str,
297 pub definition: Option<&'static str>,
299 pub materialized: bool,
301 pub with: Option<ViewWithOptionDef>,
303 pub is_existing: bool,
305 pub with_no_data: bool,
307 pub using: Option<&'static str>,
309 pub tablespace: Option<&'static str>,
311}
312
313impl ViewDef {
314 #[must_use]
316 pub const fn new(schema: &'static str, name: &'static str) -> Self {
317 Self {
318 schema,
319 name,
320 definition: None,
321 materialized: false,
322 with: None,
323 is_existing: false,
324 with_no_data: false,
325 using: None,
326 tablespace: None,
327 }
328 }
329
330 #[must_use]
332 pub const fn definition(self, sql: &'static str) -> Self {
333 Self {
334 definition: Some(sql),
335 ..self
336 }
337 }
338
339 #[must_use]
341 pub const fn materialized(self) -> Self {
342 Self {
343 materialized: true,
344 ..self
345 }
346 }
347
348 #[must_use]
350 pub const fn with_options(self, options: ViewWithOptionDef) -> Self {
351 Self {
352 with: Some(options),
353 ..self
354 }
355 }
356
357 #[must_use]
359 pub const fn existing(self) -> Self {
360 Self {
361 is_existing: true,
362 ..self
363 }
364 }
365
366 #[must_use]
368 pub const fn with_no_data(self) -> Self {
369 Self {
370 with_no_data: true,
371 ..self
372 }
373 }
374
375 #[must_use]
377 pub const fn using(self, clause: &'static str) -> Self {
378 Self {
379 using: Some(clause),
380 ..self
381 }
382 }
383
384 #[must_use]
386 pub const fn tablespace(self, space: &'static str) -> Self {
387 Self {
388 tablespace: Some(space),
389 ..self
390 }
391 }
392
393 #[must_use]
398 pub fn into_view(self) -> View {
399 View {
400 schema: Cow::Borrowed(self.schema),
401 name: Cow::Borrowed(self.name),
402 definition: match self.definition {
403 Some(s) => Some(Cow::Borrowed(s)),
404 None => None,
405 },
406 materialized: self.materialized,
407 with: self.with.map(|w| w.into_view_with_option()),
408 is_existing: self.is_existing,
409 with_no_data: if self.with_no_data { Some(true) } else { None },
410 using: match self.using {
411 Some(s) => Some(Cow::Borrowed(s)),
412 None => None,
413 },
414 tablespace: match self.tablespace {
415 Some(s) => Some(Cow::Borrowed(s)),
416 None => None,
417 },
418 }
419 }
420}
421
422impl Default for ViewDef {
423 fn default() -> Self {
424 Self::new("public", "")
425 }
426}
427
428#[derive(Clone, Debug, PartialEq, Eq)]
434#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
435#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
436pub struct View {
437 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
439 pub schema: Cow<'static, str>,
440
441 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
443 pub name: Cow<'static, str>,
444
445 #[cfg_attr(
447 feature = "serde",
448 serde(
449 default,
450 skip_serializing_if = "Option::is_none",
451 deserialize_with = "cow_option_from_string"
452 )
453 )]
454 pub definition: Option<Cow<'static, str>>,
455
456 #[cfg_attr(feature = "serde", serde(default))]
458 pub materialized: bool,
459
460 #[cfg_attr(
462 feature = "serde",
463 serde(skip_serializing_if = "Option::is_none", rename = "with")
464 )]
465 pub with: Option<ViewWithOption>,
466
467 #[cfg_attr(feature = "serde", serde(default))]
469 pub is_existing: bool,
470
471 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
473 pub with_no_data: Option<bool>,
474
475 #[cfg_attr(
477 feature = "serde",
478 serde(
479 skip_serializing_if = "Option::is_none",
480 deserialize_with = "cow_option_from_string"
481 )
482 )]
483 pub using: Option<Cow<'static, str>>,
484
485 #[cfg_attr(
487 feature = "serde",
488 serde(
489 skip_serializing_if = "Option::is_none",
490 deserialize_with = "cow_option_from_string"
491 )
492 )]
493 pub tablespace: Option<Cow<'static, str>>,
494}
495
496impl View {
497 #[must_use]
499 pub fn new(schema: impl Into<Cow<'static, str>>, name: impl Into<Cow<'static, str>>) -> Self {
500 Self {
501 schema: schema.into(),
502 name: name.into(),
503 definition: None,
504 materialized: false,
505 with: None,
506 is_existing: false,
507 with_no_data: None,
508 using: None,
509 tablespace: None,
510 }
511 }
512
513 #[inline]
515 #[must_use]
516 pub fn schema(&self) -> &str {
517 &self.schema
518 }
519
520 #[inline]
522 #[must_use]
523 pub fn name(&self) -> &str {
524 &self.name
525 }
526}
527
528impl Default for View {
529 fn default() -> Self {
530 Self::new("public", "")
531 }
532}
533
534impl From<ViewDef> for View {
535 fn from(def: ViewDef) -> Self {
536 def.into_view()
537 }
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543
544 #[test]
545 fn test_const_view_def() {
546 const VIEW: ViewDef = ViewDef::new("public", "active_users")
547 .definition("SELECT * FROM users WHERE active = 1");
548
549 assert_eq!(VIEW.name, "active_users");
550 assert_eq!(VIEW.schema, "public");
551 }
552
553 #[test]
554 fn test_materialized_view_def() {
555 const MAT_VIEW: ViewDef = ViewDef::new("public", "user_stats")
556 .materialized()
557 .with_no_data();
558
559 assert!(MAT_VIEW.materialized);
560 }
561
562 #[test]
563 fn test_view_def_to_view() {
564 const DEF: ViewDef = ViewDef::new("public", "view").definition("SELECT 1");
565 let view = DEF.into_view();
566 assert_eq!(view.name(), "view");
567 assert_eq!(view.schema(), "public");
568 }
569}