1use serde::{Deserialize, Serialize};
15
16use crate::backend::{StorageBackendError, StorageBackendResult};
17
18mod cache_revisions;
19mod graph_access;
20mod graph_snapshot;
21mod relation;
22mod schema;
23
24pub use cache_revisions::CatalogCacheRevisions;
25pub(crate) use graph_access::validate_graph_page;
26pub use graph_access::{GraphEntityFilter, GraphEntityKind, MAX_GRAPH_ID_PAGE};
27pub use relation::RelationIdentity;
28mod table;
29
30pub use schema::{SchemaAclEntry, SchemaPrivileges, SchemaRow};
31pub use table::{TableAclEntry, TablePrivileges};
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(rename_all = "snake_case")]
35pub enum RelationKind {
36 Table,
37 View,
38 Sequence,
39 ForeignTable,
40 Index,
41}
42
43impl RelationKind {
44 pub fn as_str(self) -> &'static str {
45 match self {
46 Self::Table => "table",
47 Self::View => "view",
48 Self::Sequence => "sequence",
49 Self::ForeignTable => "foreign_table",
50 Self::Index => "index",
51 }
52 }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct TableSchema {
57 pub relation: RelationIdentity,
58 #[serde(default = "legacy_table_role_owner")]
60 pub role_owner: String,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub acl: Option<Vec<TableAclEntry>>,
64 #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
66 pub column_acls: std::collections::BTreeMap<String, Vec<TableAclEntry>>,
67 #[serde(default)]
69 pub object_id: [u8; 16],
70 #[serde(default)]
72 pub storage_generation: [u8; 16],
73 pub analyzer_json: String,
74 pub fts_fields: Vec<String>,
75 pub vector_fields: Vec<VectorFieldSchema>,
76 #[serde(default)]
81 pub columns_json: String,
82 #[serde(default)]
85 pub constraints_json: String,
86}
87
88fn legacy_table_role_owner() -> String {
89 "uqa".into()
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct VectorFieldSchema {
94 pub field: String,
95 pub dimensions: u32,
96}
97
98#[derive(Debug, Clone)]
101pub struct EdgeRow {
102 pub edge_id: u64,
103 pub source_id: u64,
104 pub target_id: u64,
105 pub label: String,
106 pub properties_json: String,
107}
108
109#[derive(Debug, Clone)]
111pub struct GraphVertexRow {
112 pub vertex_id: u64,
113 pub label: String,
114 pub properties_json: String,
115}
116
117#[derive(Debug, Clone)]
120pub struct GraphSnapshot {
121 pub vertices: Vec<GraphVertexRow>,
122 pub edges: Vec<EdgeRow>,
123 pub label_registry_json: String,
124}
125
126#[derive(Debug, Clone)]
128pub struct ForeignTableRow {
129 pub relation: RelationIdentity,
130 pub role_owner: String,
132 pub acl: Option<Vec<TableAclEntry>>,
134 pub column_acls: std::collections::BTreeMap<String, Vec<TableAclEntry>>,
136 pub server_name: String,
137 pub columns_json: String,
138 pub options_json: String,
139}
140
141#[derive(Debug, Clone)]
144pub struct ViewRow {
145 pub relation: RelationIdentity,
146 pub role_owner: String,
148 pub acl: Option<Vec<TableAclEntry>>,
150 pub column_acls: std::collections::BTreeMap<String, Vec<TableAclEntry>>,
152 pub definition_json: String,
153}
154
155#[derive(Debug, Clone)]
157pub struct CatalogIndexRow {
158 pub relation: RelationIdentity,
159 pub index_type: String,
160 pub table_name: String,
161 pub columns_json: String,
162 pub parameters_json: String,
163 pub definition_json: Option<String>,
165}
166
167#[derive(Debug, Clone, Copy)]
169pub struct ColumnStatsInput<'a> {
170 pub table_name: &'a str,
171 pub column_name: &'a str,
172 pub distinct_count: i64,
173 pub null_count: i64,
174 pub min_value: Option<&'a str>,
175 pub max_value: Option<&'a str>,
176 pub row_count: i64,
177 pub histogram_json: &'a str,
178 pub mcv_values_json: &'a str,
179 pub mcv_frequencies_json: &'a str,
180}
181
182impl<'a> ColumnStatsInput<'a> {
183 pub fn basic(
184 table_name: &'a str,
185 column_name: &'a str,
186 distinct_count: i64,
187 null_count: i64,
188 min_value: Option<&'a str>,
189 max_value: Option<&'a str>,
190 row_count: i64,
191 ) -> Self {
192 Self {
193 table_name,
194 column_name,
195 distinct_count,
196 null_count,
197 min_value,
198 max_value,
199 row_count,
200 histogram_json: "[]",
201 mcv_values_json: "[]",
202 mcv_frequencies_json: "[]",
203 }
204 }
205}
206
207#[derive(Debug, Clone, PartialEq)]
209pub struct ColumnStatsRow {
210 pub column_name: String,
211 pub distinct_count: i64,
212 pub null_count: i64,
213 pub min_value: Option<String>,
214 pub max_value: Option<String>,
215 pub row_count: i64,
216 pub histogram_json: String,
217 pub mcv_values_json: String,
218 pub mcv_frequencies_json: String,
219}
220
221#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
225pub struct SequenceOptions {
226 pub data_type: String,
227 pub min_value: Option<i64>,
229 pub max_value: Option<i64>,
231 pub cycle: bool,
232 #[serde(default = "default_sequence_cache_size")]
233 pub cache_size: i64,
234}
235
236const fn default_sequence_cache_size() -> i64 {
237 1
238}
239
240impl Default for SequenceOptions {
241 fn default() -> Self {
242 Self {
243 data_type: "bigint".into(),
244 min_value: None,
245 max_value: None,
246 cycle: false,
247 cache_size: default_sequence_cache_size(),
248 }
249 }
250}
251
252#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
254#[serde(rename_all = "snake_case")]
255pub enum SequenceOwnerDependency {
256 #[default]
257 Automatic,
258 Internal,
259}
260
261impl SequenceOwnerDependency {
262 #[must_use]
263 pub const fn catalog_code(self) -> &'static str {
264 match self {
265 Self::Automatic => "a",
266 Self::Internal => "i",
267 }
268 }
269}
270
271#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
273pub struct SequenceOwner {
274 pub table_object_id: [u8; 16],
275 pub column_object_id: [u8; 16],
276 #[serde(default)]
277 pub dependency: SequenceOwnerDependency,
278}
279
280#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
282pub struct SequencePrivileges {
283 #[serde(default)]
284 pub select: bool,
285 #[serde(default)]
286 pub update: bool,
287 #[serde(default)]
288 pub usage: bool,
289}
290
291impl SequencePrivileges {
292 pub const ALL: Self = Self {
293 select: true,
294 update: true,
295 usage: true,
296 };
297
298 #[must_use]
299 pub const fn is_empty(self) -> bool {
300 !self.select && !self.update && !self.usage
301 }
302
303 #[must_use]
304 pub const fn intersects(self, other: Self) -> bool {
305 self.select && other.select || self.update && other.update || self.usage && other.usage
306 }
307
308 pub fn insert(&mut self, other: Self) {
309 self.select |= other.select;
310 self.update |= other.update;
311 self.usage |= other.usage;
312 }
313
314 pub fn remove(&mut self, other: Self) {
315 self.select &= !other.select;
316 self.update &= !other.update;
317 self.usage &= !other.usage;
318 }
319}
320
321#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
323pub struct SequenceAclEntry {
324 pub role: String,
325 #[serde(default, skip_serializing_if = "Option::is_none")]
327 pub grantor: Option<String>,
328 #[serde(default)]
329 pub privileges: SequencePrivileges,
330 #[serde(default)]
331 pub grant_options: SequencePrivileges,
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
335pub struct SequenceRow {
336 pub relation: RelationIdentity,
337 #[serde(default = "default_sequence_role_owner")]
339 pub role_owner: String,
340 #[serde(default, skip_serializing_if = "Option::is_none")]
342 pub acl: Option<Vec<SequenceAclEntry>>,
343 #[serde(default)]
345 pub object_id: [u8; 16],
346 #[serde(default)]
348 pub definition_generation: [u8; 16],
349 pub start: i64,
350 pub increment: i64,
351 pub current: i64,
352 pub called: bool,
354 #[serde(default)]
356 pub log_count: i64,
357 pub persistence: String,
359 #[serde(default)]
360 pub owner: Option<SequenceOwner>,
361 #[serde(default)]
362 pub options: SequenceOptions,
363}
364
365fn default_sequence_role_owner() -> String {
366 "uqa".into()
367}
368
369#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub struct SequenceValuePosition {
372 pub current: i64,
373 pub called: bool,
374 pub log_count: i64,
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq)]
379pub struct SequenceValueReservation {
380 pub first_value: i64,
381 pub last_value: i64,
382 pub count: i64,
383 pub log_count: i64,
384}
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387pub enum SequenceReservationResult {
388 Missing,
389 DefinitionChanged,
390 Exhausted,
391 Reserved(SequenceValueReservation),
392}
393
394#[must_use]
396pub fn sequence_value_reservation(
397 position: SequenceValuePosition,
398 increment: i64,
399 min_value: i64,
400 max_value: i64,
401 cycle: bool,
402 cache_size: i64,
403) -> Option<SequenceValueReservation> {
404 let SequenceValuePosition {
405 current,
406 called,
407 log_count,
408 } = position;
409 debug_assert_ne!(increment, 0);
410 debug_assert!(cache_size > 0);
411 let first_value = if called {
412 match current
413 .checked_add(increment)
414 .filter(|value| (min_value..=max_value).contains(value))
415 {
416 Some(value) => value,
417 None if cycle && increment > 0 => min_value,
418 None if cycle => max_value,
419 None => return None,
420 }
421 } else {
422 current
423 };
424 let distance = if increment > 0 {
425 i128::from(max_value) - i128::from(first_value)
426 } else {
427 i128::from(first_value) - i128::from(min_value)
428 };
429 let step = i128::from(increment).abs();
430 let available = distance / step + 1;
431 let count = available.min(i128::from(cache_size));
432 let last_value = i128::from(first_value) + i128::from(increment) * (count - 1);
433 let initial_count = i128::from(!called);
434 let cache_fetch = i128::from(cache_size) - initial_count;
435 let mut fetch = cache_fetch;
436 let mut next_log_count = i128::from(log_count);
437 if i128::from(log_count) < cache_fetch || !called {
438 fetch += 32;
439 next_log_count = fetch;
440 }
441 let fetched = fetch.min(available - initial_count);
442 next_log_count -= fetched.min(cache_fetch);
443 next_log_count -= fetch - fetched;
444 Some(SequenceValueReservation {
445 first_value,
446 last_value: i64::try_from(last_value).expect("reserved sequence value stays in bounds"),
447 count: i64::try_from(count).expect("reservation count cannot exceed cache size"),
448 log_count: i64::try_from(next_log_count)
449 .expect("persisted sequence log count cannot exceed the cache request"),
450 })
451}
452
453pub trait CatalogFacade: Send + Sync {
455 fn cache_revisions(&self) -> StorageBackendResult<Option<CatalogCacheRevisions>> {
458 Ok(None)
459 }
460
461 fn set_metadata(&self, key: &str, value: &str) -> StorageBackendResult<()>;
462 fn get_metadata(&self, key: &str) -> StorageBackendResult<Option<String>>;
463 fn fts_storage_was_reset(&self) -> bool {
464 false
465 }
466
467 fn migrate_relation_namespace(&self) -> StorageBackendResult<()>;
471
472 fn save_schema_row(&self, schema: &SchemaRow) -> StorageBackendResult<()>;
473 fn drop_schema(&self, name: &str) -> StorageBackendResult<()>;
474 fn load_schema_rows(&self) -> StorageBackendResult<Vec<SchemaRow>>;
475
476 fn save_schema(&self, name: &str) -> StorageBackendResult<()> {
477 self.save_schema_row(&SchemaRow::legacy(name))
478 }
479
480 fn load_schemas(&self) -> StorageBackendResult<Vec<String>> {
481 Ok(self
482 .load_schema_rows()?
483 .into_iter()
484 .map(|schema| schema.name)
485 .collect())
486 }
487
488 fn save_table(&self, schema: &TableSchema) -> StorageBackendResult<()>;
489 fn load_tables(&self) -> StorageBackendResult<Vec<TableSchema>>;
490 fn drop_table(&self, name: &str) -> StorageBackendResult<()>;
491 fn drop_table_and_data(&self, name: &str) -> StorageBackendResult<()>;
494 fn purge_table_data(&self, name: &str) -> StorageBackendResult<()>;
495 fn rename_table_data(&self, from: &str, to: &str) -> StorageBackendResult<()>;
496 fn drop_column_data(&self, table_name: &str, column_name: &str) -> StorageBackendResult<()>;
497 fn rename_column_data(
498 &self,
499 table_name: &str,
500 from: &str,
501 to: &str,
502 ) -> StorageBackendResult<()>;
503
504 fn save_model(&self, name: &str, json: &str) -> StorageBackendResult<()>;
505 fn load_models(&self) -> StorageBackendResult<Vec<(String, String)>>;
506 fn load_model(&self, name: &str) -> StorageBackendResult<Option<String>>;
507 fn drop_model(&self, name: &str) -> StorageBackendResult<()>;
508
509 fn save_scoring_params(&self, name: &str, params_json: &str) -> StorageBackendResult<()>;
510 fn load_scoring_params(&self, name: &str) -> StorageBackendResult<Option<String>>;
511 fn load_all_scoring_params(&self) -> StorageBackendResult<Vec<(String, String)>>;
512 fn drop_scoring_params(&self, name: &str) -> StorageBackendResult<()>;
513
514 fn create_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
515 fn replace_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
516 fn rename_sequence_row(&self, from: &str, to: &str) -> StorageBackendResult<bool>;
518 fn drop_sequence_row(&self, name: &str) -> StorageBackendResult<bool>;
519 fn load_sequence_rows(&self) -> StorageBackendResult<Vec<SequenceRow>>;
520 fn reserve_sequence_values(
521 &self,
522 name: &str,
523 object_id: [u8; 16],
524 definition_generation: [u8; 16],
525 ) -> StorageBackendResult<SequenceReservationResult>;
526 fn next_sequence_value(
528 &self,
529 name: &str,
530 object_id: [u8; 16],
531 ) -> StorageBackendResult<Option<i64>> {
532 loop {
533 let relation =
534 RelationIdentity::from_legacy_name(name).map_err(StorageBackendError::Other)?;
535 let Some(row) = self
536 .load_sequence_rows()?
537 .into_iter()
538 .find(|row| row.relation == relation && row.object_id == object_id)
539 else {
540 return Ok(None);
541 };
542 match self.reserve_sequence_values(name, object_id, row.definition_generation)? {
543 SequenceReservationResult::Reserved(reservation) => {
544 return Ok(Some(reservation.first_value));
545 }
546 SequenceReservationResult::DefinitionChanged => {}
547 SequenceReservationResult::Missing => return Ok(None),
548 SequenceReservationResult::Exhausted => {
549 return Err(StorageBackendError::Other(format!(
550 "sequence `{name}` exhausted"
551 )));
552 }
553 }
554 }
555 }
556 fn set_sequence_value(
557 &self,
558 name: &str,
559 object_id: [u8; 16],
560 value: i64,
561 called: bool,
562 log_count: i64,
563 ) -> StorageBackendResult<Option<i64>>;
564
565 fn save_view(&self, view: &ViewRow) -> StorageBackendResult<()>;
566 fn rename_view(
568 &self,
569 from: &RelationIdentity,
570 to: &RelationIdentity,
571 ) -> StorageBackendResult<bool>;
572 fn drop_view(&self, relation: &RelationIdentity) -> StorageBackendResult<bool>;
573 fn load_views(&self) -> StorageBackendResult<Vec<ViewRow>>;
574
575 fn save_named_graph(&self, name: &str) -> StorageBackendResult<()>;
576 fn drop_named_graph(&self, name: &str) -> StorageBackendResult<()>;
577 fn load_named_graphs(&self) -> StorageBackendResult<Vec<String>>;
578 fn named_graph_exists(&self, name: &str) -> StorageBackendResult<bool>;
579
580 fn graph_vertex(&self, id: u64) -> StorageBackendResult<Option<GraphVertexRow>>;
582 fn graph_edge(&self, id: u64) -> StorageBackendResult<Option<EdgeRow>>;
583
584 fn graph_entity_ids(
587 &self,
588 filter: GraphEntityFilter<'_>,
589 after: Option<u64>,
590 limit: usize,
591 ) -> StorageBackendResult<Vec<u64>>;
592
593 fn graph_entity_count(&self, filter: GraphEntityFilter<'_>) -> StorageBackendResult<u64>;
594 fn graph_entity_max_id(&self, kind: GraphEntityKind) -> StorageBackendResult<Option<u64>>;
595 fn graph_entity_memberships(
596 &self,
597 kind: GraphEntityKind,
598 id: u64,
599 ) -> StorageBackendResult<Vec<String>>;
600 fn graph_has_membership(
601 &self,
602 kind: GraphEntityKind,
603 id: u64,
604 graph: &str,
605 ) -> StorageBackendResult<bool>;
606 fn load_named_graph_snapshot(&self, name: &str) -> StorageBackendResult<Option<GraphSnapshot>> {
610 graph_snapshot::load(self, name)
611 }
612 fn save_vertex(
613 &self,
614 vertex_id: u64,
615 label: &str,
616 properties_json: &str,
617 ) -> StorageBackendResult<()>;
618 fn delete_vertex(&self, vertex_id: u64) -> StorageBackendResult<()>;
619 fn load_vertices(&self) -> StorageBackendResult<Vec<(u64, String, String)>>;
620 fn save_edge(
621 &self,
622 edge_id: u64,
623 source_id: u64,
624 target_id: u64,
625 label: &str,
626 properties_json: &str,
627 ) -> StorageBackendResult<()>;
628 fn delete_edge(&self, edge_id: u64) -> StorageBackendResult<()>;
629 fn load_edges(&self) -> StorageBackendResult<Vec<EdgeRow>>;
630 fn save_graph_membership(
631 &self,
632 entity_type: &str,
633 entity_id: u64,
634 graph_name: &str,
635 ) -> StorageBackendResult<()>;
636 fn delete_graph_membership(
637 &self,
638 entity_type: &str,
639 entity_id: u64,
640 graph_name: &str,
641 ) -> StorageBackendResult<()>;
642 fn delete_graph_membership_for_graph(&self, graph_name: &str) -> StorageBackendResult<()>;
643 fn load_graph_memberships(&self) -> StorageBackendResult<Vec<(String, u64, String)>>;
644 fn purge_orphan_graph_entities(&self) -> StorageBackendResult<()>;
645 fn replace_named_graph(
646 &self,
647 graph_name: &str,
648 snapshot: &GraphSnapshot,
649 ) -> StorageBackendResult<()>;
650 fn drop_named_graph_data(&self, graph_name: &str) -> StorageBackendResult<()>;
651
652 fn save_analyzer(&self, name: &str, config_json: &str) -> StorageBackendResult<()>;
653 fn drop_analyzer(&self, name: &str) -> StorageBackendResult<()>;
654 fn load_analyzers(&self) -> StorageBackendResult<Vec<(String, String)>>;
655
656 fn save_table_field_analyzer(
657 &self,
658 table_name: &str,
659 field: &str,
660 phase: &str,
661 analyzer_name: &str,
662 ) -> StorageBackendResult<()>;
663 fn replace_table_field_analyzer(
664 &self,
665 table_name: &str,
666 field: &str,
667 phase: &str,
668 analyzer_name: &str,
669 ) -> StorageBackendResult<()>;
670 fn drop_table_field_analyzer_field(
671 &self,
672 table_name: &str,
673 field: &str,
674 ) -> StorageBackendResult<()>;
675 fn drop_table_field_analyzers(&self, table_name: &str) -> StorageBackendResult<()>;
676 fn load_table_field_analyzers(
677 &self,
678 ) -> StorageBackendResult<Vec<(String, String, String, String)>>;
679
680 fn save_foreign_server(
681 &self,
682 name: &str,
683 fdw_type: &str,
684 options_json: &str,
685 ) -> StorageBackendResult<()>;
686 fn drop_foreign_server(&self, name: &str) -> StorageBackendResult<()>;
687 fn load_foreign_servers(&self) -> StorageBackendResult<Vec<(String, String, String)>>;
688
689 fn save_foreign_table(&self, row: &ForeignTableRow) -> StorageBackendResult<()>;
690 fn rename_foreign_table(
692 &self,
693 from: &RelationIdentity,
694 to: &RelationIdentity,
695 ) -> StorageBackendResult<bool>;
696 fn update_foreign_table_security(
697 &self,
698 relation: &RelationIdentity,
699 role_owner: &str,
700 acl: Option<&[TableAclEntry]>,
701 column_acls: &std::collections::BTreeMap<String, Vec<TableAclEntry>>,
702 ) -> StorageBackendResult<bool>;
703 fn drop_foreign_table(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
704 fn load_foreign_tables(&self) -> StorageBackendResult<Vec<ForeignTableRow>>;
705
706 fn save_catalog_index(
707 &self,
708 relation: &RelationIdentity,
709 index_type: &str,
710 table_name: &str,
711 columns_json: &str,
712 parameters_json: &str,
713 ) -> StorageBackendResult<()> {
714 self.save_catalog_index_row(&CatalogIndexRow {
715 relation: relation.clone(),
716 index_type: index_type.to_string(),
717 table_name: table_name.to_string(),
718 columns_json: columns_json.to_string(),
719 parameters_json: parameters_json.to_string(),
720 definition_json: None,
721 })
722 }
723 fn save_catalog_index_row(&self, index: &CatalogIndexRow) -> StorageBackendResult<()>;
724 fn drop_catalog_index(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
725 fn drop_catalog_indexes_for_table(&self, table_name: &str) -> StorageBackendResult<()>;
726 fn load_catalog_indexes(&self) -> StorageBackendResult<Vec<CatalogIndexRow>>;
727
728 fn save_path_index(
729 &self,
730 graph_name: &str,
731 label_sequences_json: &str,
732 ) -> StorageBackendResult<()>;
733 fn drop_path_index(&self, graph_name: &str) -> StorageBackendResult<()>;
734 fn load_path_indexes(&self) -> StorageBackendResult<Vec<(String, String)>>;
735
736 fn clear_path_index_data(&self, index: &str) -> StorageBackendResult<()>;
740 fn save_path_index_pairs(
741 &self,
742 index: &str,
743 sequence: &str,
744 pairs: &[(u64, u64)],
745 ) -> StorageBackendResult<()>;
746 fn finish_path_index_data(
747 &self,
748 index: &str,
749 graph: &str,
750 definition: &str,
751 ) -> StorageBackendResult<()>;
752 fn path_index_data_is_current(
753 &self,
754 index: &str,
755 definition: &str,
756 ) -> StorageBackendResult<bool>;
757 fn path_index_pairs(
758 &self,
759 index: &str,
760 sequence: &str,
761 after: Option<(u64, u64)>,
762 limit: usize,
763 ) -> StorageBackendResult<Vec<(u64, u64)>>;
764
765 fn save_column_stats(&self, stats: ColumnStatsInput<'_>) -> StorageBackendResult<()>;
766 fn replace_column_stats(
769 &self,
770 table_name: &str,
771 stats: &[ColumnStatsInput<'_>],
772 ) -> StorageBackendResult<()>;
773 fn load_column_stats(&self, table_name: &str) -> StorageBackendResult<Vec<ColumnStatsRow>>;
774 fn delete_column_stats(&self, table_name: &str) -> StorageBackendResult<()>;
775}
776
777#[cfg(test)]
778mod tests {
779 use super::{
780 sequence_value_reservation, RelationIdentity, SequenceValuePosition,
781 SequenceValueReservation,
782 };
783
784 const fn sequence_position(
785 current: i64,
786 called: bool,
787 log_count: i64,
788 ) -> SequenceValuePosition {
789 SequenceValuePosition {
790 current,
791 called,
792 log_count,
793 }
794 }
795
796 #[test]
797 fn relation_identity_rendering_is_reversible_and_collision_free() {
798 let left = RelationIdentity::new("a.b", "c");
799 let right = RelationIdentity::new("a", "b.c");
800 assert_eq!(left.qualified_name(), "\"a.b\".c");
801 assert_eq!(right.qualified_name(), "a.\"b.c\"");
802 assert_ne!(left.qualified_name(), right.qualified_name());
803 assert_eq!(
804 RelationIdentity::from_legacy_name(&left.qualified_name()).unwrap(),
805 left
806 );
807 assert_eq!(
808 RelationIdentity::from_legacy_name(&right.qualified_name()).unwrap(),
809 right
810 );
811 }
812
813 #[test]
814 fn relation_identity_preserves_quotes_and_unqualified_public_alias() {
815 let quoted = RelationIdentity::new("public", "a\"b.c");
816 assert_eq!(quoted.qualified_name(), "public.\"a\"\"b.c\"");
817 assert_eq!(
818 quoted.canonical_and_legacy_public_names(),
819 vec![
820 "public.\"a\"\"b.c\"".to_string(),
821 "\"a\"\"b.c\"".to_string()
822 ]
823 );
824 assert_eq!(
825 RelationIdentity::from_legacy_name("ed.qualified_name()).unwrap(),
826 quoted
827 );
828 assert_eq!(
829 RelationIdentity::from_legacy_name("plain").unwrap(),
830 RelationIdentity::new("public", "plain")
831 );
832 assert_eq!(
833 RelationIdentity::new("app", "plain").canonical_and_legacy_public_names(),
834 vec!["app.plain".to_string()]
835 );
836 assert_eq!(
837 RelationIdentity::new("public", "Upper").canonical_and_legacy_public_names(),
838 vec![
839 "public.\"Upper\"".to_string(),
840 "\"Upper\"".to_string(),
841 "Upper".to_string()
842 ]
843 );
844 }
845
846 #[test]
847 fn sequence_reservations_track_postgresql_log_counts() {
848 assert_eq!(
849 sequence_value_reservation(sequence_position(1, false, 0), 1, 1, i64::MAX, false, 1),
850 Some(SequenceValueReservation {
851 first_value: 1,
852 last_value: 1,
853 count: 1,
854 log_count: 32,
855 })
856 );
857 assert_eq!(
858 sequence_value_reservation(sequence_position(1, true, 32), 1, 1, i64::MAX, false, 1),
859 Some(SequenceValueReservation {
860 first_value: 2,
861 last_value: 2,
862 count: 1,
863 log_count: 31,
864 })
865 );
866 assert_eq!(
867 sequence_value_reservation(sequence_position(1, false, 0), 1, 1, i64::MAX, false, 10),
868 Some(SequenceValueReservation {
869 first_value: 1,
870 last_value: 10,
871 count: 10,
872 log_count: 32,
873 })
874 );
875 assert_eq!(
876 sequence_value_reservation(sequence_position(5, false, 0), 2, 3, 9, true, 3),
877 Some(SequenceValueReservation {
878 first_value: 5,
879 last_value: 9,
880 count: 3,
881 log_count: 0,
882 })
883 );
884 assert_eq!(
885 sequence_value_reservation(
886 sequence_position(1, false, 0),
887 1,
888 1,
889 i64::MAX,
890 false,
891 i64::MAX,
892 ),
893 Some(SequenceValueReservation {
894 first_value: 1,
895 last_value: i64::MAX,
896 count: i64::MAX,
897 log_count: 0,
898 })
899 );
900 }
901}