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 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
155pub use uqa_core::catalog_index::CatalogIndexRow;
156
157#[derive(Debug, Clone, Copy)]
159pub struct ColumnStatsInput<'a> {
160 pub table_name: &'a str,
161 pub column_name: &'a str,
162 pub distinct_count: i64,
163 pub null_count: i64,
164 pub min_value: Option<&'a str>,
165 pub max_value: Option<&'a str>,
166 pub row_count: i64,
167 pub histogram_json: &'a str,
168 pub mcv_values_json: &'a str,
169 pub mcv_frequencies_json: &'a str,
170}
171
172impl<'a> ColumnStatsInput<'a> {
173 pub fn basic(
174 table_name: &'a str,
175 column_name: &'a str,
176 distinct_count: i64,
177 null_count: i64,
178 min_value: Option<&'a str>,
179 max_value: Option<&'a str>,
180 row_count: i64,
181 ) -> Self {
182 Self {
183 table_name,
184 column_name,
185 distinct_count,
186 null_count,
187 min_value,
188 max_value,
189 row_count,
190 histogram_json: "[]",
191 mcv_values_json: "[]",
192 mcv_frequencies_json: "[]",
193 }
194 }
195}
196
197#[derive(Debug, Clone, PartialEq)]
199pub struct ColumnStatsRow {
200 pub column_name: String,
201 pub distinct_count: i64,
202 pub null_count: i64,
203 pub min_value: Option<String>,
204 pub max_value: Option<String>,
205 pub row_count: i64,
206 pub histogram_json: String,
207 pub mcv_values_json: String,
208 pub mcv_frequencies_json: String,
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
215pub struct SequenceOptions {
216 pub data_type: String,
217 pub min_value: Option<i64>,
219 pub max_value: Option<i64>,
221 pub cycle: bool,
222 #[serde(default = "default_sequence_cache_size")]
223 pub cache_size: i64,
224}
225
226const fn default_sequence_cache_size() -> i64 {
227 1
228}
229
230impl Default for SequenceOptions {
231 fn default() -> Self {
232 Self {
233 data_type: "bigint".into(),
234 min_value: None,
235 max_value: None,
236 cycle: false,
237 cache_size: default_sequence_cache_size(),
238 }
239 }
240}
241
242pub use uqa_core::catalog_sequence::{
243 SequenceAclEntry, SequenceOwner, SequenceOwnerDependency, SequencePrivileges,
244};
245
246#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
247pub struct SequenceRow {
248 pub relation: RelationIdentity,
249 #[serde(default = "default_sequence_role_owner")]
251 pub role_owner: String,
252 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub acl: Option<Vec<SequenceAclEntry>>,
255 #[serde(default)]
257 pub object_id: [u8; 16],
258 #[serde(default)]
260 pub definition_generation: [u8; 16],
261 pub start: i64,
262 pub increment: i64,
263 pub current: i64,
264 pub called: bool,
266 #[serde(default)]
268 pub log_count: i64,
269 pub persistence: String,
271 #[serde(default)]
272 pub owner: Option<SequenceOwner>,
273 #[serde(default)]
274 pub options: SequenceOptions,
275}
276
277fn default_sequence_role_owner() -> String {
278 "uqa".into()
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
283pub struct SequenceValuePosition {
284 pub current: i64,
285 pub called: bool,
286 pub log_count: i64,
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
291pub struct SequenceValueReservation {
292 pub first_value: i64,
293 pub last_value: i64,
294 pub count: i64,
295 pub log_count: i64,
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq)]
299pub enum SequenceReservationResult {
300 Missing,
301 DefinitionChanged,
302 Exhausted,
303 Reserved(SequenceValueReservation),
304}
305
306#[must_use]
308pub fn sequence_value_reservation(
309 position: SequenceValuePosition,
310 increment: i64,
311 min_value: i64,
312 max_value: i64,
313 cycle: bool,
314 cache_size: i64,
315) -> Option<SequenceValueReservation> {
316 let SequenceValuePosition {
317 current,
318 called,
319 log_count,
320 } = position;
321 debug_assert_ne!(increment, 0);
322 debug_assert!(cache_size > 0);
323 let first_value = if called {
324 match current
325 .checked_add(increment)
326 .filter(|value| (min_value..=max_value).contains(value))
327 {
328 Some(value) => value,
329 None if cycle && increment > 0 => min_value,
330 None if cycle => max_value,
331 None => return None,
332 }
333 } else {
334 current
335 };
336 let distance = if increment > 0 {
337 i128::from(max_value) - i128::from(first_value)
338 } else {
339 i128::from(first_value) - i128::from(min_value)
340 };
341 let step = i128::from(increment).abs();
342 let available = distance / step + 1;
343 let count = available.min(i128::from(cache_size));
344 let last_value = i128::from(first_value) + i128::from(increment) * (count - 1);
345 let initial_count = i128::from(!called);
346 let cache_fetch = i128::from(cache_size) - initial_count;
347 let mut fetch = cache_fetch;
348 let mut next_log_count = i128::from(log_count);
349 if i128::from(log_count) < cache_fetch || !called {
350 fetch += 32;
351 next_log_count = fetch;
352 }
353 let fetched = fetch.min(available - initial_count);
354 next_log_count -= fetched.min(cache_fetch);
355 next_log_count -= fetch - fetched;
356 Some(SequenceValueReservation {
357 first_value,
358 last_value: i64::try_from(last_value).expect("reserved sequence value stays in bounds"),
359 count: i64::try_from(count).expect("reservation count cannot exceed cache size"),
360 log_count: i64::try_from(next_log_count)
361 .expect("persisted sequence log count cannot exceed the cache request"),
362 })
363}
364
365pub trait CatalogFacade: Send + Sync {
367 fn initialize_storage(&self) -> StorageBackendResult<()> {
369 Ok(())
370 }
371
372 fn cache_revisions(&self) -> StorageBackendResult<Option<CatalogCacheRevisions>> {
375 Ok(None)
376 }
377
378 fn set_metadata(&self, key: &str, value: &str) -> StorageBackendResult<()>;
379 fn get_metadata(&self, key: &str) -> StorageBackendResult<Option<String>>;
380 fn fts_storage_was_reset(&self) -> bool {
381 false
382 }
383
384 fn migrate_relation_namespace(&self) -> StorageBackendResult<()>;
388
389 fn save_schema_row(&self, schema: &SchemaRow) -> StorageBackendResult<()>;
390 fn drop_schema(&self, name: &str) -> StorageBackendResult<()>;
391 fn load_schema_rows(&self) -> StorageBackendResult<Vec<SchemaRow>>;
392
393 fn save_schema(&self, name: &str) -> StorageBackendResult<()> {
394 self.save_schema_row(&SchemaRow::legacy(name))
395 }
396
397 fn load_schemas(&self) -> StorageBackendResult<Vec<String>> {
398 Ok(self
399 .load_schema_rows()?
400 .into_iter()
401 .map(|schema| schema.name)
402 .collect())
403 }
404
405 fn save_table(&self, schema: &TableSchema) -> StorageBackendResult<()>;
406 fn load_tables(&self) -> StorageBackendResult<Vec<TableSchema>>;
407 fn drop_table(&self, name: &str) -> StorageBackendResult<()>;
408 fn drop_table_and_data(&self, name: &str) -> StorageBackendResult<()>;
411 fn purge_table_data(&self, name: &str) -> StorageBackendResult<()>;
412 fn rename_table_data(&self, from: &str, to: &str) -> StorageBackendResult<()>;
413 fn drop_column_data(&self, table_name: &str, column_name: &str) -> StorageBackendResult<()>;
414 fn rename_column_data(
415 &self,
416 table_name: &str,
417 from: &str,
418 to: &str,
419 ) -> StorageBackendResult<()>;
420
421 fn save_model(&self, name: &str, json: &str) -> StorageBackendResult<()>;
422 fn load_models(&self) -> StorageBackendResult<Vec<(String, String)>>;
423 fn load_model(&self, name: &str) -> StorageBackendResult<Option<String>>;
424 fn drop_model(&self, name: &str) -> StorageBackendResult<()>;
425
426 fn save_scoring_params(&self, name: &str, params_json: &str) -> StorageBackendResult<()>;
427 fn load_scoring_params(&self, name: &str) -> StorageBackendResult<Option<String>>;
428 fn load_all_scoring_params(&self) -> StorageBackendResult<Vec<(String, String)>>;
429 fn drop_scoring_params(&self, name: &str) -> StorageBackendResult<()>;
430
431 fn create_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
432 fn replace_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
433 fn rename_sequence_row(&self, from: &str, to: &str) -> StorageBackendResult<bool>;
435 fn drop_sequence_row(&self, name: &str) -> StorageBackendResult<bool>;
436 fn load_sequence_rows(&self) -> StorageBackendResult<Vec<SequenceRow>>;
437 fn reserve_sequence_values(
438 &self,
439 name: &str,
440 object_id: [u8; 16],
441 definition_generation: [u8; 16],
442 ) -> StorageBackendResult<SequenceReservationResult>;
443 fn next_sequence_value(
445 &self,
446 name: &str,
447 object_id: [u8; 16],
448 ) -> StorageBackendResult<Option<i64>> {
449 loop {
450 let relation =
451 RelationIdentity::from_legacy_name(name).map_err(StorageBackendError::Other)?;
452 let Some(row) = self
453 .load_sequence_rows()?
454 .into_iter()
455 .find(|row| row.relation == relation && row.object_id == object_id)
456 else {
457 return Ok(None);
458 };
459 match self.reserve_sequence_values(name, object_id, row.definition_generation)? {
460 SequenceReservationResult::Reserved(reservation) => {
461 return Ok(Some(reservation.first_value));
462 }
463 SequenceReservationResult::DefinitionChanged => {}
464 SequenceReservationResult::Missing => return Ok(None),
465 SequenceReservationResult::Exhausted => {
466 return Err(StorageBackendError::Other(format!(
467 "sequence `{name}` exhausted"
468 )));
469 }
470 }
471 }
472 }
473 fn set_sequence_value(
474 &self,
475 name: &str,
476 object_id: [u8; 16],
477 value: i64,
478 called: bool,
479 log_count: i64,
480 ) -> StorageBackendResult<Option<i64>>;
481
482 fn save_view(&self, view: &ViewRow) -> StorageBackendResult<()>;
483 fn rename_view(
485 &self,
486 from: &RelationIdentity,
487 to: &RelationIdentity,
488 ) -> StorageBackendResult<bool>;
489 fn drop_view(&self, relation: &RelationIdentity) -> StorageBackendResult<bool>;
490 fn load_views(&self) -> StorageBackendResult<Vec<ViewRow>>;
491
492 fn save_named_graph(&self, name: &str) -> StorageBackendResult<()>;
493 fn drop_named_graph(&self, name: &str) -> StorageBackendResult<()>;
494 fn load_named_graphs(&self) -> StorageBackendResult<Vec<String>>;
495 fn named_graph_exists(&self, name: &str) -> StorageBackendResult<bool>;
496
497 fn graph_vertex(&self, id: u64) -> StorageBackendResult<Option<GraphVertexRow>>;
499 fn graph_edge(&self, id: u64) -> StorageBackendResult<Option<EdgeRow>>;
500
501 fn graph_entity_ids(
504 &self,
505 filter: GraphEntityFilter<'_>,
506 after: Option<u64>,
507 limit: usize,
508 ) -> StorageBackendResult<Vec<u64>>;
509
510 fn graph_entity_count(&self, filter: GraphEntityFilter<'_>) -> StorageBackendResult<u64>;
511 fn graph_entity_max_id(&self, kind: GraphEntityKind) -> StorageBackendResult<Option<u64>>;
512 fn graph_entity_memberships(
513 &self,
514 kind: GraphEntityKind,
515 id: u64,
516 ) -> StorageBackendResult<Vec<String>>;
517 fn graph_has_membership(
518 &self,
519 kind: GraphEntityKind,
520 id: u64,
521 graph: &str,
522 ) -> StorageBackendResult<bool>;
523 fn load_named_graph_snapshot(&self, name: &str) -> StorageBackendResult<Option<GraphSnapshot>> {
527 graph_snapshot::load(self, name)
528 }
529 fn save_vertex(
530 &self,
531 vertex_id: u64,
532 label: &str,
533 properties_json: &str,
534 ) -> StorageBackendResult<()>;
535 fn delete_vertex(&self, vertex_id: u64) -> StorageBackendResult<()>;
536 fn load_vertices(&self) -> StorageBackendResult<Vec<(u64, String, String)>>;
537 fn save_edge(
538 &self,
539 edge_id: u64,
540 source_id: u64,
541 target_id: u64,
542 label: &str,
543 properties_json: &str,
544 ) -> StorageBackendResult<()>;
545 fn delete_edge(&self, edge_id: u64) -> StorageBackendResult<()>;
546 fn load_edges(&self) -> StorageBackendResult<Vec<EdgeRow>>;
547 fn save_graph_membership(
548 &self,
549 entity_type: &str,
550 entity_id: u64,
551 graph_name: &str,
552 ) -> StorageBackendResult<()>;
553 fn delete_graph_membership(
554 &self,
555 entity_type: &str,
556 entity_id: u64,
557 graph_name: &str,
558 ) -> StorageBackendResult<()>;
559 fn delete_graph_membership_for_graph(&self, graph_name: &str) -> StorageBackendResult<()>;
560 fn load_graph_memberships(&self) -> StorageBackendResult<Vec<(String, u64, String)>>;
561 fn purge_orphan_graph_entities(&self) -> StorageBackendResult<()>;
562 fn replace_named_graph(
563 &self,
564 graph_name: &str,
565 snapshot: &GraphSnapshot,
566 ) -> StorageBackendResult<()>;
567 fn drop_named_graph_data(&self, graph_name: &str) -> StorageBackendResult<()>;
568
569 fn save_analyzer(&self, name: &str, config_json: &str) -> StorageBackendResult<()>;
570 fn drop_analyzer(&self, name: &str) -> StorageBackendResult<()>;
571 fn load_analyzers(&self) -> StorageBackendResult<Vec<(String, String)>>;
572
573 fn save_analyzer_revision(
575 &self,
576 _name: &str,
577 _config_json: &str,
578 _descriptor_json: &str,
579 ) -> StorageBackendResult<()> {
580 Err(StorageBackendError::Other(
581 "durable analyzer descriptors are not supported by this catalog".into(),
582 ))
583 }
584
585 fn load_analyzer_descriptors(&self) -> StorageBackendResult<Vec<(String, String)>> {
587 Ok(Vec::new())
588 }
589
590 fn replace_table_field_analyzer_binding(
592 &self,
593 _table: &str,
594 _field: &str,
595 _phase: &str,
596 _name: &str,
597 _binding_json: &str,
598 ) -> StorageBackendResult<()> {
599 Err(StorageBackendError::Other(
600 "durable analyzer bindings are not supported by this catalog".into(),
601 ))
602 }
603
604 fn load_table_field_analyzer_bindings(
606 &self,
607 ) -> StorageBackendResult<Vec<(String, String, String)>> {
608 Ok(Vec::new())
609 }
610
611 fn save_table_field_analyzer(
612 &self,
613 table_name: &str,
614 field: &str,
615 phase: &str,
616 analyzer_name: &str,
617 ) -> StorageBackendResult<()>;
618 fn replace_table_field_analyzer(
619 &self,
620 table_name: &str,
621 field: &str,
622 phase: &str,
623 analyzer_name: &str,
624 ) -> StorageBackendResult<()>;
625 fn drop_table_field_analyzer_field(
626 &self,
627 table_name: &str,
628 field: &str,
629 ) -> StorageBackendResult<()>;
630 fn drop_table_field_analyzers(&self, table_name: &str) -> StorageBackendResult<()>;
631 fn load_table_field_analyzers(
632 &self,
633 ) -> StorageBackendResult<Vec<(String, String, String, String)>>;
634
635 fn save_foreign_server(
636 &self,
637 name: &str,
638 fdw_type: &str,
639 options_json: &str,
640 ) -> StorageBackendResult<()>;
641 fn drop_foreign_server(&self, name: &str) -> StorageBackendResult<()>;
642 fn load_foreign_servers(&self) -> StorageBackendResult<Vec<(String, String, String)>>;
643
644 fn save_foreign_table(&self, row: &ForeignTableRow) -> StorageBackendResult<()>;
645 fn rename_foreign_table(
647 &self,
648 from: &RelationIdentity,
649 to: &RelationIdentity,
650 ) -> StorageBackendResult<bool>;
651 fn update_foreign_table_security(
652 &self,
653 relation: &RelationIdentity,
654 role_owner: &str,
655 acl: Option<&[TableAclEntry]>,
656 column_acls: &std::collections::BTreeMap<String, Vec<TableAclEntry>>,
657 ) -> StorageBackendResult<bool>;
658 fn drop_foreign_table(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
659 fn load_foreign_tables(&self) -> StorageBackendResult<Vec<ForeignTableRow>>;
660
661 fn save_catalog_index(
662 &self,
663 relation: &RelationIdentity,
664 index_type: &str,
665 table_name: &str,
666 columns_json: &str,
667 parameters_json: &str,
668 ) -> StorageBackendResult<()> {
669 self.save_catalog_index_row(&CatalogIndexRow {
670 relation: relation.clone(),
671 index_type: index_type.to_string(),
672 table_name: table_name.to_string(),
673 columns_json: columns_json.to_string(),
674 parameters_json: parameters_json.to_string(),
675 definition_json: None,
676 })
677 }
678 fn save_catalog_index_row(&self, index: &CatalogIndexRow) -> StorageBackendResult<()>;
679 fn drop_catalog_index(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
680 fn drop_catalog_indexes_for_table(&self, table_name: &str) -> StorageBackendResult<()>;
681 fn load_catalog_indexes(&self) -> StorageBackendResult<Vec<CatalogIndexRow>>;
682
683 fn save_path_index(
684 &self,
685 graph_name: &str,
686 label_sequences_json: &str,
687 ) -> StorageBackendResult<()>;
688 fn drop_path_index(&self, graph_name: &str) -> StorageBackendResult<()>;
689 fn load_path_indexes(&self) -> StorageBackendResult<Vec<(String, String)>>;
690
691 fn clear_path_index_data(&self, index: &str) -> StorageBackendResult<()>;
695 fn save_path_index_pairs(
696 &self,
697 index: &str,
698 sequence: &str,
699 pairs: &[(u64, u64)],
700 ) -> StorageBackendResult<()>;
701 fn finish_path_index_data(
702 &self,
703 index: &str,
704 graph: &str,
705 definition: &str,
706 ) -> StorageBackendResult<()>;
707 fn path_index_data_is_current(
708 &self,
709 index: &str,
710 definition: &str,
711 ) -> StorageBackendResult<bool>;
712 fn path_index_pairs(
713 &self,
714 index: &str,
715 sequence: &str,
716 after: Option<(u64, u64)>,
717 limit: usize,
718 ) -> StorageBackendResult<Vec<(u64, u64)>>;
719
720 fn save_column_stats(&self, stats: ColumnStatsInput<'_>) -> StorageBackendResult<()>;
721 fn replace_column_stats(
724 &self,
725 table_name: &str,
726 stats: &[ColumnStatsInput<'_>],
727 ) -> StorageBackendResult<()>;
728 fn load_column_stats(&self, table_name: &str) -> StorageBackendResult<Vec<ColumnStatsRow>>;
729 fn delete_column_stats(&self, table_name: &str) -> StorageBackendResult<()>;
730}
731
732#[cfg(test)]
733mod tests {
734 use super::{
735 sequence_value_reservation, RelationIdentity, SequenceValuePosition,
736 SequenceValueReservation,
737 };
738
739 const fn sequence_position(
740 current: i64,
741 called: bool,
742 log_count: i64,
743 ) -> SequenceValuePosition {
744 SequenceValuePosition {
745 current,
746 called,
747 log_count,
748 }
749 }
750
751 #[test]
752 fn relation_identity_rendering_is_reversible_and_collision_free() {
753 let left = RelationIdentity::new("a.b", "c");
754 let right = RelationIdentity::new("a", "b.c");
755 assert_eq!(left.qualified_name(), "\"a.b\".c");
756 assert_eq!(right.qualified_name(), "a.\"b.c\"");
757 assert_ne!(left.qualified_name(), right.qualified_name());
758 assert_eq!(
759 RelationIdentity::from_legacy_name(&left.qualified_name()).unwrap(),
760 left
761 );
762 assert_eq!(
763 RelationIdentity::from_legacy_name(&right.qualified_name()).unwrap(),
764 right
765 );
766 }
767
768 #[test]
769 fn relation_identity_preserves_quotes_and_unqualified_public_alias() {
770 let quoted = RelationIdentity::new("public", "a\"b.c");
771 assert_eq!(quoted.qualified_name(), "public.\"a\"\"b.c\"");
772 assert_eq!(
773 quoted.canonical_and_legacy_public_names(),
774 vec![
775 "public.\"a\"\"b.c\"".to_string(),
776 "\"a\"\"b.c\"".to_string()
777 ]
778 );
779 assert_eq!(
780 RelationIdentity::from_legacy_name("ed.qualified_name()).unwrap(),
781 quoted
782 );
783 assert_eq!(
784 RelationIdentity::from_legacy_name("plain").unwrap(),
785 RelationIdentity::new("public", "plain")
786 );
787 assert_eq!(
788 RelationIdentity::new("app", "plain").canonical_and_legacy_public_names(),
789 vec!["app.plain".to_string()]
790 );
791 assert_eq!(
792 RelationIdentity::new("public", "Upper").canonical_and_legacy_public_names(),
793 vec![
794 "public.\"Upper\"".to_string(),
795 "\"Upper\"".to_string(),
796 "Upper".to_string()
797 ]
798 );
799 }
800
801 #[test]
802 fn sequence_reservations_track_postgresql_log_counts() {
803 assert_eq!(
804 sequence_value_reservation(sequence_position(1, false, 0), 1, 1, i64::MAX, false, 1),
805 Some(SequenceValueReservation {
806 first_value: 1,
807 last_value: 1,
808 count: 1,
809 log_count: 32,
810 })
811 );
812 assert_eq!(
813 sequence_value_reservation(sequence_position(1, true, 32), 1, 1, i64::MAX, false, 1),
814 Some(SequenceValueReservation {
815 first_value: 2,
816 last_value: 2,
817 count: 1,
818 log_count: 31,
819 })
820 );
821 assert_eq!(
822 sequence_value_reservation(sequence_position(1, false, 0), 1, 1, i64::MAX, false, 10),
823 Some(SequenceValueReservation {
824 first_value: 1,
825 last_value: 10,
826 count: 10,
827 log_count: 32,
828 })
829 );
830 assert_eq!(
831 sequence_value_reservation(sequence_position(5, false, 0), 2, 3, 9, true, 3),
832 Some(SequenceValueReservation {
833 first_value: 5,
834 last_value: 9,
835 count: 3,
836 log_count: 0,
837 })
838 );
839 assert_eq!(
840 sequence_value_reservation(
841 sequence_position(1, false, 0),
842 1,
843 1,
844 i64::MAX,
845 false,
846 i64::MAX,
847 ),
848 Some(SequenceValueReservation {
849 first_value: 1,
850 last_value: i64::MAX,
851 count: i64::MAX,
852 log_count: 0,
853 })
854 );
855 }
856}