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 record_revisions;
26mod relation;
27pub mod relation_acl;
28mod relation_security;
29mod schema;
30mod sequence_security;
31mod sequence_values;
32
33pub use cache_revisions::CatalogCacheRevisions;
34pub use graph_access::validate_graph_page;
35pub use graph_access::{
36 GraphEntityFilter, GraphEntityKind, GraphEntitySelector, MAX_GRAPH_ID_PAGE,
37};
38pub use identity::new_nonzero_catalog_identity;
39pub use record_revisions::{CatalogRecordRef, CatalogRecordRevision};
40pub use relation::RelationIdentity;
41pub use relation_security::{BoundRelationSecurity, LegacyRelationSecurity, RelationSecurityRow};
42pub use sequence_security::{BoundSequenceSecurity, LegacySequenceSecurity, SequenceSecurityRow};
43pub use sequence_values::{
44 sequence_value_allocation, sequence_value_reservation, SequenceLogResult,
45 SequenceReservationResult, SequenceSetValueResult, SequenceValueAllocation,
46 SequenceValuePosition, SequenceValueReservation,
47};
48mod table;
49
50pub use schema::{BoundSchemaRow, SchemaAclEntry, SchemaPrivileges, SchemaRow};
51pub use table::{TableAclEntry, TablePrivileges};
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(rename_all = "snake_case")]
55pub enum RelationKind {
56 Table,
57 View,
58 Sequence,
59 ForeignTable,
60 Index,
61}
62
63impl RelationKind {
64 pub fn as_str(self) -> &'static str {
65 match self {
66 Self::Table => "table",
67 Self::View => "view",
68 Self::Sequence => "sequence",
69 Self::ForeignTable => "foreign_table",
70 Self::Index => "index",
71 }
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct TableSchema {
77 pub relation: RelationIdentity,
78 #[serde(flatten)]
79 pub security: RelationSecurityRow,
80 #[serde(default)]
82 pub object_id: [u8; 16],
83 #[serde(default)]
85 pub storage_generation: [u8; 16],
86 pub analyzer_json: String,
87 pub fts_fields: Vec<String>,
88 pub vector_fields: Vec<VectorFieldSchema>,
89 #[serde(default)]
94 pub columns_json: String,
95 #[serde(default)]
98 pub constraints_json: String,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct VectorFieldSchema {
103 pub field: String,
104 pub dimensions: u32,
105}
106
107#[derive(Debug, Clone)]
110pub struct EdgeRow {
111 pub edge_id: u64,
112 pub source_id: u64,
113 pub target_id: u64,
114 pub label: String,
115 pub properties_json: String,
116}
117
118#[derive(Debug, Clone)]
120pub struct GraphVertexRow {
121 pub vertex_id: u64,
122 pub label: String,
123 pub properties_json: String,
124}
125
126#[derive(Debug, Clone)]
129pub struct GraphSnapshot {
130 pub vertices: Vec<GraphVertexRow>,
131 pub edges: Vec<EdgeRow>,
132 pub label_registry_json: String,
133}
134
135#[derive(Debug, Clone)]
137pub struct ForeignTableRow {
138 pub relation: RelationIdentity,
139 pub security: RelationSecurityRow,
140 pub server_name: String,
141 pub columns_json: String,
142 pub options_json: String,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq)]
147pub struct ForeignServerRow {
148 pub name: String,
149 pub fdw_type: String,
150 pub options_json: String,
151 pub metadata_json: Option<String>,
152}
153
154#[derive(Debug, Clone)]
157pub struct ViewRow {
158 pub relation: RelationIdentity,
159 pub security: RelationSecurityRow,
160 pub definition_json: String,
161}
162
163pub use uqa_core::catalog_index::CatalogIndexRow;
164
165#[derive(Debug, Clone, Copy)]
167pub struct ColumnStatsInput<'a> {
168 pub table_name: &'a str,
169 pub column_name: &'a str,
170 pub distinct_count: i64,
171 pub null_count: i64,
172 pub min_value: Option<&'a str>,
173 pub max_value: Option<&'a str>,
174 pub row_count: i64,
175 pub histogram_json: &'a str,
176 pub mcv_values_json: &'a str,
177 pub mcv_frequencies_json: &'a str,
178}
179
180impl<'a> ColumnStatsInput<'a> {
181 pub fn basic(
182 table_name: &'a str,
183 column_name: &'a str,
184 distinct_count: i64,
185 null_count: i64,
186 min_value: Option<&'a str>,
187 max_value: Option<&'a str>,
188 row_count: i64,
189 ) -> Self {
190 Self {
191 table_name,
192 column_name,
193 distinct_count,
194 null_count,
195 min_value,
196 max_value,
197 row_count,
198 histogram_json: "[]",
199 mcv_values_json: "[]",
200 mcv_frequencies_json: "[]",
201 }
202 }
203}
204
205#[derive(Debug, Clone, PartialEq)]
207pub struct ColumnStatsRow {
208 pub column_name: String,
209 pub distinct_count: i64,
210 pub null_count: i64,
211 pub min_value: Option<String>,
212 pub max_value: Option<String>,
213 pub row_count: i64,
214 pub histogram_json: String,
215 pub mcv_values_json: String,
216 pub mcv_frequencies_json: String,
217}
218
219#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
223pub struct SequenceOptions {
224 pub data_type: String,
225 pub min_value: Option<i64>,
227 pub max_value: Option<i64>,
229 pub cycle: bool,
230 #[serde(default = "default_sequence_cache_size")]
231 pub cache_size: i64,
232}
233
234const fn default_sequence_cache_size() -> i64 {
235 1
236}
237
238impl Default for SequenceOptions {
239 fn default() -> Self {
240 Self {
241 data_type: "bigint".into(),
242 min_value: None,
243 max_value: None,
244 cycle: false,
245 cache_size: default_sequence_cache_size(),
246 }
247 }
248}
249
250pub use uqa_core::catalog_sequence::{
251 SequenceAclEntry, SequenceOwner, SequenceOwnerDependency, SequencePrivileges,
252};
253
254#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
255pub struct SequenceRow {
256 pub relation: RelationIdentity,
257 #[serde(flatten)]
259 pub security: SequenceSecurityRow,
260 #[serde(default)]
262 pub object_id: [u8; 16],
263 #[serde(default)]
265 pub definition_generation: [u8; 16],
266 pub start: i64,
267 pub increment: i64,
268 pub current: i64,
269 pub called: bool,
271 #[serde(default)]
273 pub log_count: i64,
274 pub persistence: String,
276 #[serde(default)]
277 pub owner: Option<SequenceOwner>,
278 #[serde(default)]
279 pub options: SequenceOptions,
280}
281
282pub trait CatalogFacade: Send + Sync {
284 fn transaction_model(&self) -> crate::StorageTransactionModel {
286 crate::StorageTransactionModel::ProviderSerialized
287 }
288
289 fn transaction_affinity(&self) -> Option<crate::StorageSessionAffinity> {
291 None
292 }
293
294 fn initialize_storage(&self) -> StorageBackendResult<()> {
296 Ok(())
297 }
298
299 fn cache_revisions(&self) -> StorageBackendResult<Option<CatalogCacheRevisions>> {
302 Ok(None)
303 }
304
305 fn record_revisions(
307 &self,
308 _records: &[CatalogRecordRef<'_>],
309 ) -> StorageBackendResult<Option<Vec<Option<CatalogRecordRevision>>>> {
310 Ok(None)
311 }
312
313 fn set_metadata(&self, key: &str, value: &str) -> StorageBackendResult<()>;
314 fn delete_metadata(&self, key: &str) -> StorageBackendResult<()>;
316 fn get_metadata(&self, key: &str) -> StorageBackendResult<Option<String>>;
317 fn metadata_with_prefix(&self, prefix: &str) -> StorageBackendResult<Vec<(String, String)>>;
319 fn save_relation_acl(
321 &self,
322 relation: &RelationIdentity,
323 column: Option<&str>,
324 entry: &relation_acl::RelationAclTuple,
325 ) -> StorageBackendResult<()> {
326 if !self.transaction_model().is_versioned() {
327 return relation_acl::save_serialized(self, relation, column, entry);
328 }
329 self.set_metadata(&relation_acl::key(relation, column), &entry.encode(column)?)
330 }
331 fn metadata_has_private_changes(&self, _key: &str) -> StorageBackendResult<bool> {
333 if self.transaction_model().is_versioned() {
334 return Err(StorageBackendError::Other(
335 "private metadata provenance is not supported by this catalog".into(),
336 ));
337 }
338 Ok(false)
339 }
340 fn save_statistics_maintenance(
342 &self,
343 table: &str,
344 state: &crate::statistics_maintenance::StatisticsMaintenance,
345 ) -> StorageBackendResult<()> {
346 self.set_metadata(
347 &crate::statistics_maintenance::StatisticsMaintenance::key(table),
348 &serde_json::to_string(state)?,
349 )
350 }
351 fn fts_storage_was_reset(&self) -> bool {
352 false
353 }
354
355 fn migrate_relation_namespace(&self) -> StorageBackendResult<()>;
359
360 fn save_schema_row(&self, schema: &SchemaRow) -> StorageBackendResult<()>;
361 fn drop_schema(&self, name: &str) -> StorageBackendResult<()>;
362 fn load_schema_rows(&self) -> StorageBackendResult<Vec<SchemaRow>>;
363
364 fn schema_has_private_changes(&self, _name: &str) -> StorageBackendResult<bool> {
366 if self.transaction_model().is_versioned() {
367 return Err(StorageBackendError::Other(
368 "private schema provenance is not supported by this catalog".into(),
369 ));
370 }
371 Ok(false)
372 }
373
374 fn save_schema(&self, name: &str) -> StorageBackendResult<()> {
375 self.save_schema_row(&SchemaRow::bootstrap(name))
376 }
377
378 fn load_schemas(&self) -> StorageBackendResult<Vec<String>> {
379 Ok(self
380 .load_schema_rows()?
381 .into_iter()
382 .map(|schema| schema.name().to_owned())
383 .collect())
384 }
385
386 fn save_table(&self, schema: &TableSchema) -> StorageBackendResult<()>;
387 fn load_tables(&self) -> StorageBackendResult<Vec<TableSchema>>;
388 fn drop_table(&self, name: &str) -> StorageBackendResult<()>;
389 fn drop_table_and_data(&self, name: &str) -> StorageBackendResult<()>;
392 fn purge_table_data(&self, name: &str) -> StorageBackendResult<()>;
393 fn rename_table_data(&self, from: &str, to: &str) -> StorageBackendResult<()>;
394 fn drop_column_data(&self, table_name: &str, column_name: &str) -> StorageBackendResult<()>;
395 fn rename_column_data(
396 &self,
397 table_name: &str,
398 from: &str,
399 to: &str,
400 ) -> StorageBackendResult<()>;
401
402 fn save_model(&self, name: &str, json: &str) -> StorageBackendResult<()>;
403 fn load_models(&self) -> StorageBackendResult<Vec<(String, String)>>;
404 fn load_model(&self, name: &str) -> StorageBackendResult<Option<String>>;
405 fn drop_model(&self, name: &str) -> StorageBackendResult<()>;
406
407 fn save_scoring_params(&self, name: &str, params_json: &str) -> StorageBackendResult<()>;
408 fn load_scoring_params(&self, name: &str) -> StorageBackendResult<Option<String>>;
409 fn load_all_scoring_params(&self) -> StorageBackendResult<Vec<(String, String)>>;
410 fn drop_scoring_params(&self, name: &str) -> StorageBackendResult<()>;
411
412 fn create_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
414 fn replace_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
416 fn rename_sequence_row(&self, from: &str, to: &str) -> StorageBackendResult<bool>;
418 fn drop_sequence_row(&self, name: &str) -> StorageBackendResult<bool>;
420 fn load_sequence_rows(&self) -> StorageBackendResult<Vec<SequenceRow>>;
422
423 fn sequence_has_private_changes(
425 &self,
426 _relation: &RelationIdentity,
427 _object_id: [u8; 16],
428 ) -> StorageBackendResult<bool> {
429 if self.transaction_model().is_versioned() {
430 return Err(StorageBackendError::Other(
431 "private sequence provenance is not supported by this catalog".into(),
432 ));
433 }
434 Ok(false)
435 }
436
437 fn sequence_value_has_private_changes(
439 &self,
440 _object_id: [u8; 16],
441 _definition_generation: [u8; 16],
442 ) -> StorageBackendResult<bool> {
443 if self.transaction_model().is_versioned() {
444 return Err(StorageBackendError::Other(
445 "private sequence value provenance is not supported by this catalog".into(),
446 ));
447 }
448 Ok(false)
449 }
450
451 fn migrate_sequence_values(&self) -> StorageBackendResult<()> {
453 Ok(())
454 }
455 fn reserve_sequence_values(
457 &self,
458 name: &str,
459 object_id: [u8; 16],
460 definition_generation: [u8; 16],
461 ) -> StorageBackendResult<SequenceReservationResult>;
462 fn next_sequence_value(
464 &self,
465 name: &str,
466 object_id: [u8; 16],
467 ) -> StorageBackendResult<Option<i64>> {
468 loop {
469 let relation =
470 RelationIdentity::from_legacy_name(name).map_err(StorageBackendError::Other)?;
471 let Some(row) = self
472 .load_sequence_rows()?
473 .into_iter()
474 .find(|row| row.relation == relation && row.object_id == object_id)
475 else {
476 return Ok(None);
477 };
478 match self.reserve_sequence_values(name, object_id, row.definition_generation)? {
479 SequenceReservationResult::Reserved(reservation) => {
480 return Ok(Some(reservation.first_value));
481 }
482 SequenceReservationResult::DefinitionChanged => {}
483 SequenceReservationResult::Missing => return Ok(None),
484 SequenceReservationResult::Exhausted => {
485 return Err(StorageBackendError::Other(format!(
486 "sequence `{name}` exhausted"
487 )));
488 }
489 }
490 }
491 }
492 fn set_sequence_value(
494 &self,
495 name: &str,
496 object_id: [u8; 16],
497 definition_generation: [u8; 16],
498 value: i64,
499 called: bool,
500 log_count: i64,
501 ) -> StorageBackendResult<SequenceSetValueResult>;
502 fn log_sequence_values(
504 &self,
505 name: &str,
506 object_id: [u8; 16],
507 definition_generation: [u8; 16],
508 expected: (i64, bool),
509 logged: SequenceValuePosition,
510 ) -> StorageBackendResult<SequenceLogResult>;
511
512 fn save_view(&self, view: &ViewRow) -> StorageBackendResult<()>;
513 fn rename_view(
515 &self,
516 from: &RelationIdentity,
517 to: &RelationIdentity,
518 ) -> StorageBackendResult<bool>;
519 fn drop_view(&self, relation: &RelationIdentity) -> StorageBackendResult<bool>;
520 fn load_views(&self) -> StorageBackendResult<Vec<ViewRow>>;
521
522 fn save_named_graph(&self, name: &str) -> StorageBackendResult<()>;
523 fn guard_graph_definition(&self, _graph: Option<&str>) -> StorageBackendResult<()> {
525 Err(crate::StorageBackendError::Other(
526 "graph definition guards are not supported".into(),
527 ))
528 }
529 fn drop_named_graph(&self, name: &str) -> StorageBackendResult<()>;
530 fn load_named_graphs(&self) -> StorageBackendResult<Vec<String>>;
531 fn named_graph_exists(&self, name: &str) -> StorageBackendResult<bool>;
532
533 fn graph_vertex(&self, id: u64) -> StorageBackendResult<Option<GraphVertexRow>>;
535
536 fn for_each_graph_vertex_borrowed(
542 &self,
543 _ids: &[u64],
544 _visit: &mut dyn FnMut(u64, Option<&GraphVertexRow>) -> bool,
545 ) -> StorageBackendResult<Option<usize>> {
546 Ok(None)
547 }
548
549 fn graph_edge(&self, id: u64) -> StorageBackendResult<Option<EdgeRow>>;
550
551 fn graph_entity_ids(
554 &self,
555 filter: GraphEntityFilter<'_>,
556 after: Option<u64>,
557 limit: usize,
558 ) -> StorageBackendResult<Vec<u64>>;
559
560 fn graph_entity_count(&self, filter: GraphEntityFilter<'_>) -> StorageBackendResult<u64>;
561 fn graph_entity_max_id(&self, kind: GraphEntityKind) -> StorageBackendResult<Option<u64>>;
562 fn graph_entity_memberships(
563 &self,
564 kind: GraphEntityKind,
565 id: u64,
566 ) -> StorageBackendResult<Vec<String>>;
567 fn graph_has_membership(
568 &self,
569 kind: GraphEntityKind,
570 id: u64,
571 graph: &str,
572 ) -> StorageBackendResult<bool>;
573 fn load_named_graph_snapshot(&self, name: &str) -> StorageBackendResult<Option<GraphSnapshot>> {
577 graph_snapshot::load(self, name)
578 }
579 fn save_vertex(
580 &self,
581 vertex_id: u64,
582 label: &str,
583 properties_json: &str,
584 ) -> StorageBackendResult<()>;
585 fn delete_vertex(&self, vertex_id: u64) -> StorageBackendResult<()>;
586 fn load_vertices(&self) -> StorageBackendResult<Vec<(u64, String, String)>>;
587 fn save_edge(
588 &self,
589 edge_id: u64,
590 source_id: u64,
591 target_id: u64,
592 label: &str,
593 properties_json: &str,
594 ) -> StorageBackendResult<()>;
595 fn delete_edge(&self, edge_id: u64) -> StorageBackendResult<()>;
596 fn load_edges(&self) -> StorageBackendResult<Vec<EdgeRow>>;
597 fn save_graph_membership(
598 &self,
599 entity_type: &str,
600 entity_id: u64,
601 graph_name: &str,
602 ) -> StorageBackendResult<()>;
603 fn delete_graph_membership(
604 &self,
605 entity_type: &str,
606 entity_id: u64,
607 graph_name: &str,
608 ) -> StorageBackendResult<()>;
609 fn delete_graph_membership_for_graph(&self, graph_name: &str) -> StorageBackendResult<()>;
610 fn load_graph_memberships(&self) -> StorageBackendResult<Vec<(String, u64, String)>>;
611 fn purge_orphan_graph_entities(&self) -> StorageBackendResult<()>;
612 fn replace_named_graph(
613 &self,
614 graph_name: &str,
615 snapshot: &GraphSnapshot,
616 ) -> StorageBackendResult<()>;
617 fn drop_named_graph_data(&self, graph_name: &str) -> StorageBackendResult<()>;
618
619 fn save_analyzer(&self, name: &str, config_json: &str) -> StorageBackendResult<()>;
620 fn drop_analyzer(&self, name: &str) -> StorageBackendResult<()>;
621 fn load_analyzers(&self) -> StorageBackendResult<Vec<(String, String)>>;
622
623 fn save_analyzer_revision(
625 &self,
626 _name: &str,
627 _config_json: &str,
628 _descriptor_json: &str,
629 ) -> StorageBackendResult<()> {
630 Err(StorageBackendError::Other(
631 "durable analyzer descriptors are not supported by this catalog".into(),
632 ))
633 }
634
635 fn load_analyzer_descriptors(&self) -> StorageBackendResult<Vec<(String, String)>> {
637 Ok(Vec::new())
638 }
639
640 fn replace_table_field_analyzer_binding(
642 &self,
643 _table: &str,
644 _field: &str,
645 _phase: &str,
646 _name: &str,
647 _binding_json: &str,
648 ) -> StorageBackendResult<()> {
649 Err(StorageBackendError::Other(
650 "durable analyzer bindings are not supported by this catalog".into(),
651 ))
652 }
653
654 fn load_table_field_analyzer_bindings(
656 &self,
657 ) -> StorageBackendResult<Vec<(String, String, String)>> {
658 Ok(Vec::new())
659 }
660
661 fn save_table_field_analyzer(
662 &self,
663 table_name: &str,
664 field: &str,
665 phase: &str,
666 analyzer_name: &str,
667 ) -> StorageBackendResult<()>;
668 fn replace_table_field_analyzer(
669 &self,
670 table_name: &str,
671 field: &str,
672 phase: &str,
673 analyzer_name: &str,
674 ) -> StorageBackendResult<()>;
675 fn drop_table_field_analyzer_field(
676 &self,
677 table_name: &str,
678 field: &str,
679 ) -> StorageBackendResult<()>;
680 fn drop_table_field_analyzers(&self, table_name: &str) -> StorageBackendResult<()>;
681 fn load_table_field_analyzers(
682 &self,
683 ) -> StorageBackendResult<Vec<(String, String, String, String)>>;
684
685 fn save_foreign_server(
686 &self,
687 name: &str,
688 fdw_type: &str,
689 options_json: &str,
690 ) -> StorageBackendResult<()>;
691 fn drop_foreign_server(&self, name: &str) -> StorageBackendResult<()>;
692 fn load_foreign_servers(&self) -> StorageBackendResult<Vec<(String, String, String)>>;
693
694 fn save_foreign_server_row(&self, row: &ForeignServerRow) -> StorageBackendResult<()> {
696 if row.metadata_json.is_some() {
697 return Err(StorageBackendError::Other(
698 "foreign-server metadata is not supported by this catalog".into(),
699 ));
700 }
701 self.save_foreign_server(&row.name, &row.fdw_type, &row.options_json)
702 }
703
704 fn load_foreign_server_rows(&self) -> StorageBackendResult<Vec<ForeignServerRow>> {
706 Ok(self
707 .load_foreign_servers()?
708 .into_iter()
709 .map(|(name, fdw_type, options_json)| ForeignServerRow {
710 name,
711 fdw_type,
712 options_json,
713 metadata_json: None,
714 })
715 .collect())
716 }
717
718 fn save_foreign_table(&self, row: &ForeignTableRow) -> StorageBackendResult<()>;
719 fn rename_foreign_table(
721 &self,
722 from: &RelationIdentity,
723 to: &RelationIdentity,
724 ) -> StorageBackendResult<bool>;
725 fn update_foreign_table_security(
726 &self,
727 relation: &RelationIdentity,
728 security: &RelationSecurityRow,
729 ) -> StorageBackendResult<bool>;
730 fn drop_foreign_table(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
731 fn load_foreign_tables(&self) -> StorageBackendResult<Vec<ForeignTableRow>>;
732
733 fn save_catalog_index(
734 &self,
735 relation: &RelationIdentity,
736 index_type: &str,
737 table_name: &str,
738 columns_json: &str,
739 parameters_json: &str,
740 ) -> StorageBackendResult<()> {
741 self.save_catalog_index_row(&CatalogIndexRow {
742 relation: relation.clone(),
743 index_type: index_type.to_string(),
744 table_name: table_name.to_string(),
745 columns_json: columns_json.to_string(),
746 parameters_json: parameters_json.to_string(),
747 definition_json: None,
748 })
749 }
750 fn save_catalog_index_row(&self, index: &CatalogIndexRow) -> StorageBackendResult<()>;
751 fn drop_catalog_index(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
752 fn drop_catalog_indexes_for_table(&self, table_name: &str) -> StorageBackendResult<()>;
753 fn load_catalog_indexes(&self) -> StorageBackendResult<Vec<CatalogIndexRow>>;
754
755 fn save_path_index(
756 &self,
757 graph_name: &str,
758 label_sequences_json: &str,
759 ) -> StorageBackendResult<()>;
760 fn drop_path_index(&self, graph_name: &str) -> StorageBackendResult<()>;
761 fn load_path_indexes(&self) -> StorageBackendResult<Vec<(String, String)>>;
762
763 fn clear_path_index_data(&self, index: &str) -> StorageBackendResult<()>;
767 fn save_path_index_pairs(
768 &self,
769 index: &str,
770 sequence: &str,
771 pairs: &[(u64, u64)],
772 ) -> StorageBackendResult<()>;
773 fn finish_path_index_data(
774 &self,
775 index: &str,
776 graph: &str,
777 definition: &str,
778 ) -> StorageBackendResult<()>;
779 fn path_index_data_is_current(
780 &self,
781 index: &str,
782 definition: &str,
783 ) -> StorageBackendResult<bool>;
784 fn path_index_pairs(
785 &self,
786 index: &str,
787 sequence: &str,
788 after: Option<(u64, u64)>,
789 limit: usize,
790 ) -> StorageBackendResult<Vec<(u64, u64)>>;
791
792 fn save_column_stats(&self, stats: ColumnStatsInput<'_>) -> StorageBackendResult<()>;
793 fn replace_column_stats(
796 &self,
797 table_name: &str,
798 stats: &[ColumnStatsInput<'_>],
799 ) -> StorageBackendResult<()>;
800 fn load_column_stats(&self, table_name: &str) -> StorageBackendResult<Vec<ColumnStatsRow>>;
801 fn delete_column_stats(&self, table_name: &str) -> StorageBackendResult<()>;
802}
803
804#[cfg(test)]
805mod tests {
806 use super::RelationIdentity;
807
808 #[test]
809 fn relation_identity_rendering_is_reversible_and_collision_free() {
810 let left = RelationIdentity::new("a.b", "c");
811 let right = RelationIdentity::new("a", "b.c");
812 assert_eq!(left.qualified_name(), "\"a.b\".c");
813 assert_eq!(right.qualified_name(), "a.\"b.c\"");
814 assert_ne!(left.qualified_name(), right.qualified_name());
815 assert_eq!(
816 RelationIdentity::from_legacy_name(&left.qualified_name()).unwrap(),
817 left
818 );
819 assert_eq!(
820 RelationIdentity::from_legacy_name(&right.qualified_name()).unwrap(),
821 right
822 );
823 }
824
825 #[test]
826 fn relation_identity_preserves_quotes_and_unqualified_public_alias() {
827 let quoted = RelationIdentity::new("public", "a\"b.c");
828 assert_eq!(quoted.qualified_name(), "public.\"a\"\"b.c\"");
829 assert_eq!(
830 quoted.canonical_and_legacy_public_names(),
831 vec![
832 "public.\"a\"\"b.c\"".to_string(),
833 "\"a\"\"b.c\"".to_string()
834 ]
835 );
836 assert_eq!(
837 RelationIdentity::from_legacy_name("ed.qualified_name()).unwrap(),
838 quoted
839 );
840 assert_eq!(
841 RelationIdentity::from_legacy_name("plain").unwrap(),
842 RelationIdentity::new("public", "plain")
843 );
844 assert_eq!(
845 RelationIdentity::new("app", "plain").canonical_and_legacy_public_names(),
846 vec!["app.plain".to_string()]
847 );
848 assert_eq!(
849 RelationIdentity::new("public", "Upper").canonical_and_legacy_public_names(),
850 vec![
851 "public.\"Upper\"".to_string(),
852 "\"Upper\"".to_string(),
853 "Upper".to_string()
854 ]
855 );
856 }
857}