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