1use serde::{Deserialize, Serialize};
15
16use crate::backend::{StorageBackendError, StorageBackendResult};
17
18mod schema;
19mod table;
20
21pub use schema::{SchemaAclEntry, SchemaPrivileges, SchemaRow};
22pub use table::{TableAclEntry, TablePrivileges};
23
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
30pub struct RelationIdentity {
31 pub schema: String,
32 pub name: String,
33}
34
35impl RelationIdentity {
36 pub fn new(schema: impl Into<String>, name: impl Into<String>) -> Self {
37 Self {
38 schema: schema.into(),
39 name: name.into(),
40 }
41 }
42
43 pub fn qualified_name(&self) -> String {
44 format!(
45 "{}.{}",
46 render_relation_component(&self.schema),
47 render_relation_component(&self.name)
48 )
49 }
50
51 pub(crate) fn canonical_and_legacy_public_names(&self) -> Vec<String> {
56 let canonical = self.qualified_name();
57 if self.schema != "public" {
58 return vec![canonical];
59 }
60 let mut names = vec![canonical];
61 let rendered_alias = render_relation_component(&self.name);
62 if !names.contains(&rendered_alias) {
63 names.push(rendered_alias);
64 }
65 if RelationIdentity::from_legacy_name(&self.name).is_ok_and(|raw| raw == *self)
70 && !names.contains(&self.name)
71 {
72 names.push(self.name.clone());
73 }
74 names
75 }
76
77 pub fn from_legacy_name(value: &str) -> Result<Self, String> {
82 let (schema, name) = Self::parse_reference(value)?;
83 Ok(Self::new(
84 schema.unwrap_or_else(|| "public".to_string()),
85 name,
86 ))
87 }
88
89 pub(crate) fn from_legacy_index_name(value: &str, table: &Self) -> Self {
91 Self::new(&table.schema, value)
92 }
93
94 pub fn parse_reference(value: &str) -> Result<(Option<String>, String), String> {
97 let components = parse_relation_components(value)?;
98 match components.as_slice() {
99 [name] => Ok((None, name.clone())),
100 [schema, name] => Ok((Some(schema.clone()), name.clone())),
101 _ => Err(format!("invalid persisted relation name `{value}`")),
102 }
103 }
104}
105
106fn render_relation_component(component: &str) -> String {
107 let can_render_bare = component
108 .bytes()
109 .enumerate()
110 .all(|(index, byte)| match byte {
111 b'a'..=b'z' | b'_' => true,
112 b'0'..=b'9' | b'$' => index != 0,
113 _ => false,
114 });
115 if can_render_bare && !component.is_empty() {
116 component.to_string()
117 } else {
118 format!("\"{}\"", component.replace('"', "\"\""))
119 }
120}
121
122fn parse_relation_components(value: &str) -> Result<Vec<String>, String> {
123 if value.is_empty() {
124 return Err("persisted relation name is empty".to_string());
125 }
126 let mut components = Vec::with_capacity(2);
127 let mut chars = value.char_indices().peekable();
128 while chars.peek().is_some() {
129 let mut component = String::new();
130 if chars.peek().is_some_and(|(_, ch)| *ch == '"') {
131 chars.next();
132 let mut terminated = false;
133 while let Some((_, ch)) = chars.next() {
134 if ch != '"' {
135 component.push(ch);
136 continue;
137 }
138 if chars.peek().is_some_and(|(_, next)| *next == '"') {
139 chars.next();
140 component.push('"');
141 } else {
142 terminated = true;
143 break;
144 }
145 }
146 if !terminated {
147 return Err(format!("unterminated quoted relation name `{value}`"));
148 }
149 if chars.peek().is_some_and(|(_, ch)| *ch != '.') {
150 return Err(format!("invalid persisted relation name `{value}`"));
151 }
152 } else {
153 while let Some((_, ch)) = chars.peek() {
154 if *ch == '.' {
155 break;
156 }
157 if *ch == '"' {
158 return Err(format!("invalid persisted relation name `{value}`"));
159 }
160 component.push(*ch);
161 chars.next();
162 }
163 }
164 if component.is_empty() {
165 return Err(format!("invalid persisted relation name `{value}`"));
166 }
167 components.push(component);
168 if components.len() > 2 {
169 return Err(format!("invalid persisted relation name `{value}`"));
170 }
171 match chars.next() {
172 Some((_, '.')) if chars.peek().is_some() => {}
173 Some(_) => return Err(format!("invalid persisted relation name `{value}`")),
174 None => break,
175 }
176 }
177 Ok(components)
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
181#[serde(rename_all = "snake_case")]
182pub enum RelationKind {
183 Table,
184 View,
185 Sequence,
186 ForeignTable,
187 Index,
188}
189
190impl RelationKind {
191 pub fn as_str(self) -> &'static str {
192 match self {
193 Self::Table => "table",
194 Self::View => "view",
195 Self::Sequence => "sequence",
196 Self::ForeignTable => "foreign_table",
197 Self::Index => "index",
198 }
199 }
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct TableSchema {
204 pub relation: RelationIdentity,
205 #[serde(default = "legacy_table_role_owner")]
207 pub role_owner: String,
208 #[serde(default, skip_serializing_if = "Option::is_none")]
210 pub acl: Option<Vec<TableAclEntry>>,
211 #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
213 pub column_acls: std::collections::BTreeMap<String, Vec<TableAclEntry>>,
214 #[serde(default)]
216 pub object_id: [u8; 16],
217 #[serde(default)]
219 pub storage_generation: [u8; 16],
220 pub analyzer_json: String,
221 pub fts_fields: Vec<String>,
222 pub vector_fields: Vec<VectorFieldSchema>,
223 #[serde(default)]
228 pub columns_json: String,
229 #[serde(default)]
232 pub constraints_json: String,
233}
234
235fn legacy_table_role_owner() -> String {
236 "uqa".into()
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct VectorFieldSchema {
241 pub field: String,
242 pub dimensions: u32,
243}
244
245#[derive(Debug, Clone)]
248pub struct EdgeRow {
249 pub edge_id: u64,
250 pub source_id: u64,
251 pub target_id: u64,
252 pub label: String,
253 pub properties_json: String,
254}
255
256#[derive(Debug, Clone)]
258pub struct GraphVertexRow {
259 pub vertex_id: u64,
260 pub label: String,
261 pub properties_json: String,
262}
263
264#[derive(Debug, Clone)]
267pub struct GraphSnapshot {
268 pub vertices: Vec<GraphVertexRow>,
269 pub edges: Vec<EdgeRow>,
270 pub label_registry_json: String,
271}
272
273#[derive(Debug, Clone)]
275pub struct ForeignTableRow {
276 pub relation: RelationIdentity,
277 pub role_owner: String,
279 pub acl: Option<Vec<TableAclEntry>>,
281 pub column_acls: std::collections::BTreeMap<String, Vec<TableAclEntry>>,
283 pub server_name: String,
284 pub columns_json: String,
285 pub options_json: String,
286}
287
288#[derive(Debug, Clone)]
291pub struct ViewRow {
292 pub relation: RelationIdentity,
293 pub role_owner: String,
295 pub acl: Option<Vec<TableAclEntry>>,
297 pub column_acls: std::collections::BTreeMap<String, Vec<TableAclEntry>>,
299 pub definition_json: String,
300}
301
302#[derive(Debug, Clone)]
304pub struct CatalogIndexRow {
305 pub relation: RelationIdentity,
306 pub index_type: String,
307 pub table_name: String,
308 pub columns_json: String,
309 pub parameters_json: String,
310 pub definition_json: Option<String>,
312}
313
314#[derive(Debug, Clone, Copy)]
316pub struct ColumnStatsInput<'a> {
317 pub table_name: &'a str,
318 pub column_name: &'a str,
319 pub distinct_count: i64,
320 pub null_count: i64,
321 pub min_value: Option<&'a str>,
322 pub max_value: Option<&'a str>,
323 pub row_count: i64,
324 pub histogram_json: &'a str,
325 pub mcv_values_json: &'a str,
326 pub mcv_frequencies_json: &'a str,
327}
328
329impl<'a> ColumnStatsInput<'a> {
330 pub fn basic(
331 table_name: &'a str,
332 column_name: &'a str,
333 distinct_count: i64,
334 null_count: i64,
335 min_value: Option<&'a str>,
336 max_value: Option<&'a str>,
337 row_count: i64,
338 ) -> Self {
339 Self {
340 table_name,
341 column_name,
342 distinct_count,
343 null_count,
344 min_value,
345 max_value,
346 row_count,
347 histogram_json: "[]",
348 mcv_values_json: "[]",
349 mcv_frequencies_json: "[]",
350 }
351 }
352}
353
354#[derive(Debug, Clone, PartialEq)]
356pub struct ColumnStatsRow {
357 pub column_name: String,
358 pub distinct_count: i64,
359 pub null_count: i64,
360 pub min_value: Option<String>,
361 pub max_value: Option<String>,
362 pub row_count: i64,
363 pub histogram_json: String,
364 pub mcv_values_json: String,
365 pub mcv_frequencies_json: String,
366}
367
368#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
372pub struct SequenceOptions {
373 pub data_type: String,
374 pub min_value: Option<i64>,
376 pub max_value: Option<i64>,
378 pub cycle: bool,
379 #[serde(default = "default_sequence_cache_size")]
380 pub cache_size: i64,
381}
382
383const fn default_sequence_cache_size() -> i64 {
384 1
385}
386
387impl Default for SequenceOptions {
388 fn default() -> Self {
389 Self {
390 data_type: "bigint".into(),
391 min_value: None,
392 max_value: None,
393 cycle: false,
394 cache_size: default_sequence_cache_size(),
395 }
396 }
397}
398
399#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
401#[serde(rename_all = "snake_case")]
402pub enum SequenceOwnerDependency {
403 #[default]
404 Automatic,
405 Internal,
406}
407
408impl SequenceOwnerDependency {
409 #[must_use]
410 pub const fn catalog_code(self) -> &'static str {
411 match self {
412 Self::Automatic => "a",
413 Self::Internal => "i",
414 }
415 }
416}
417
418#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
420pub struct SequenceOwner {
421 pub table_object_id: [u8; 16],
422 pub column_object_id: [u8; 16],
423 #[serde(default)]
424 pub dependency: SequenceOwnerDependency,
425}
426
427#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
429pub struct SequencePrivileges {
430 #[serde(default)]
431 pub select: bool,
432 #[serde(default)]
433 pub update: bool,
434 #[serde(default)]
435 pub usage: bool,
436}
437
438impl SequencePrivileges {
439 pub const ALL: Self = Self {
440 select: true,
441 update: true,
442 usage: true,
443 };
444
445 #[must_use]
446 pub const fn is_empty(self) -> bool {
447 !self.select && !self.update && !self.usage
448 }
449
450 #[must_use]
451 pub const fn intersects(self, other: Self) -> bool {
452 self.select && other.select || self.update && other.update || self.usage && other.usage
453 }
454
455 pub fn insert(&mut self, other: Self) {
456 self.select |= other.select;
457 self.update |= other.update;
458 self.usage |= other.usage;
459 }
460
461 pub fn remove(&mut self, other: Self) {
462 self.select &= !other.select;
463 self.update &= !other.update;
464 self.usage &= !other.usage;
465 }
466}
467
468#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
470pub struct SequenceAclEntry {
471 pub role: String,
472 #[serde(default, skip_serializing_if = "Option::is_none")]
474 pub grantor: Option<String>,
475 #[serde(default)]
476 pub privileges: SequencePrivileges,
477 #[serde(default)]
478 pub grant_options: SequencePrivileges,
479}
480
481#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
482pub struct SequenceRow {
483 pub relation: RelationIdentity,
484 #[serde(default = "default_sequence_role_owner")]
486 pub role_owner: String,
487 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub acl: Option<Vec<SequenceAclEntry>>,
490 #[serde(default)]
492 pub object_id: [u8; 16],
493 #[serde(default)]
495 pub definition_generation: [u8; 16],
496 pub start: i64,
497 pub increment: i64,
498 pub current: i64,
499 pub called: bool,
501 #[serde(default)]
503 pub log_count: i64,
504 pub persistence: String,
506 #[serde(default)]
507 pub owner: Option<SequenceOwner>,
508 #[serde(default)]
509 pub options: SequenceOptions,
510}
511
512fn default_sequence_role_owner() -> String {
513 "uqa".into()
514}
515
516#[derive(Debug, Clone, Copy, PartialEq, Eq)]
518pub struct SequenceValuePosition {
519 pub current: i64,
520 pub called: bool,
521 pub log_count: i64,
522}
523
524#[derive(Debug, Clone, Copy, PartialEq, Eq)]
526pub struct SequenceValueReservation {
527 pub first_value: i64,
528 pub last_value: i64,
529 pub count: i64,
530 pub log_count: i64,
531}
532
533#[derive(Debug, Clone, Copy, PartialEq, Eq)]
534pub enum SequenceReservationResult {
535 Missing,
536 DefinitionChanged,
537 Exhausted,
538 Reserved(SequenceValueReservation),
539}
540
541#[must_use]
543pub fn sequence_value_reservation(
544 position: SequenceValuePosition,
545 increment: i64,
546 min_value: i64,
547 max_value: i64,
548 cycle: bool,
549 cache_size: i64,
550) -> Option<SequenceValueReservation> {
551 let SequenceValuePosition {
552 current,
553 called,
554 log_count,
555 } = position;
556 debug_assert_ne!(increment, 0);
557 debug_assert!(cache_size > 0);
558 let first_value = if called {
559 match current
560 .checked_add(increment)
561 .filter(|value| (min_value..=max_value).contains(value))
562 {
563 Some(value) => value,
564 None if cycle && increment > 0 => min_value,
565 None if cycle => max_value,
566 None => return None,
567 }
568 } else {
569 current
570 };
571 let distance = if increment > 0 {
572 i128::from(max_value) - i128::from(first_value)
573 } else {
574 i128::from(first_value) - i128::from(min_value)
575 };
576 let step = i128::from(increment).abs();
577 let available = distance / step + 1;
578 let count = available.min(i128::from(cache_size));
579 let last_value = i128::from(first_value) + i128::from(increment) * (count - 1);
580 let initial_count = i128::from(!called);
581 let cache_fetch = i128::from(cache_size) - initial_count;
582 let mut fetch = cache_fetch;
583 let mut next_log_count = i128::from(log_count);
584 if i128::from(log_count) < cache_fetch || !called {
585 fetch += 32;
586 next_log_count = fetch;
587 }
588 let fetched = fetch.min(available - initial_count);
589 next_log_count -= fetched.min(cache_fetch);
590 next_log_count -= fetch - fetched;
591 Some(SequenceValueReservation {
592 first_value,
593 last_value: i64::try_from(last_value).expect("reserved sequence value stays in bounds"),
594 count: i64::try_from(count).expect("reservation count cannot exceed cache size"),
595 log_count: i64::try_from(next_log_count)
596 .expect("persisted sequence log count cannot exceed the cache request"),
597 })
598}
599
600pub trait CatalogFacade: Send + Sync {
602 fn set_metadata(&self, key: &str, value: &str) -> StorageBackendResult<()>;
603 fn get_metadata(&self, key: &str) -> StorageBackendResult<Option<String>>;
604 fn fts_storage_was_reset(&self) -> bool {
605 false
606 }
607
608 fn migrate_relation_namespace(&self) -> StorageBackendResult<()>;
612
613 fn save_schema_row(&self, schema: &SchemaRow) -> StorageBackendResult<()>;
614 fn drop_schema(&self, name: &str) -> StorageBackendResult<()>;
615 fn load_schema_rows(&self) -> StorageBackendResult<Vec<SchemaRow>>;
616
617 fn save_schema(&self, name: &str) -> StorageBackendResult<()> {
618 self.save_schema_row(&SchemaRow::legacy(name))
619 }
620
621 fn load_schemas(&self) -> StorageBackendResult<Vec<String>> {
622 Ok(self
623 .load_schema_rows()?
624 .into_iter()
625 .map(|schema| schema.name)
626 .collect())
627 }
628
629 fn save_table(&self, schema: &TableSchema) -> StorageBackendResult<()>;
630 fn load_tables(&self) -> StorageBackendResult<Vec<TableSchema>>;
631 fn drop_table(&self, name: &str) -> StorageBackendResult<()>;
632 fn drop_table_and_data(&self, name: &str) -> StorageBackendResult<()>;
635 fn purge_table_data(&self, name: &str) -> StorageBackendResult<()>;
636 fn rename_table_data(&self, from: &str, to: &str) -> StorageBackendResult<()>;
637 fn drop_column_data(&self, table_name: &str, column_name: &str) -> StorageBackendResult<()>;
638 fn rename_column_data(
639 &self,
640 table_name: &str,
641 from: &str,
642 to: &str,
643 ) -> StorageBackendResult<()>;
644
645 fn save_model(&self, name: &str, json: &str) -> StorageBackendResult<()>;
646 fn load_models(&self) -> StorageBackendResult<Vec<(String, String)>>;
647 fn load_model(&self, name: &str) -> StorageBackendResult<Option<String>>;
648 fn drop_model(&self, name: &str) -> StorageBackendResult<()>;
649
650 fn save_scoring_params(&self, name: &str, params_json: &str) -> StorageBackendResult<()>;
651 fn load_scoring_params(&self, name: &str) -> StorageBackendResult<Option<String>>;
652 fn load_all_scoring_params(&self) -> StorageBackendResult<Vec<(String, String)>>;
653 fn drop_scoring_params(&self, name: &str) -> StorageBackendResult<()>;
654
655 fn create_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
656 fn replace_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
657 fn rename_sequence_row(&self, from: &str, to: &str) -> StorageBackendResult<bool>;
659 fn drop_sequence_row(&self, name: &str) -> StorageBackendResult<bool>;
660 fn load_sequence_rows(&self) -> StorageBackendResult<Vec<SequenceRow>>;
661 fn reserve_sequence_values(
662 &self,
663 name: &str,
664 object_id: [u8; 16],
665 definition_generation: [u8; 16],
666 ) -> StorageBackendResult<SequenceReservationResult>;
667 fn next_sequence_value(
669 &self,
670 name: &str,
671 object_id: [u8; 16],
672 ) -> StorageBackendResult<Option<i64>> {
673 loop {
674 let relation =
675 RelationIdentity::from_legacy_name(name).map_err(StorageBackendError::Other)?;
676 let Some(row) = self
677 .load_sequence_rows()?
678 .into_iter()
679 .find(|row| row.relation == relation && row.object_id == object_id)
680 else {
681 return Ok(None);
682 };
683 match self.reserve_sequence_values(name, object_id, row.definition_generation)? {
684 SequenceReservationResult::Reserved(reservation) => {
685 return Ok(Some(reservation.first_value));
686 }
687 SequenceReservationResult::DefinitionChanged => {}
688 SequenceReservationResult::Missing => return Ok(None),
689 SequenceReservationResult::Exhausted => {
690 return Err(StorageBackendError::Other(format!(
691 "sequence `{name}` exhausted"
692 )));
693 }
694 }
695 }
696 }
697 fn set_sequence_value(
698 &self,
699 name: &str,
700 object_id: [u8; 16],
701 value: i64,
702 called: bool,
703 log_count: i64,
704 ) -> StorageBackendResult<Option<i64>>;
705
706 fn save_view(&self, view: &ViewRow) -> StorageBackendResult<()>;
707 fn rename_view(
709 &self,
710 from: &RelationIdentity,
711 to: &RelationIdentity,
712 ) -> StorageBackendResult<bool>;
713 fn drop_view(&self, relation: &RelationIdentity) -> StorageBackendResult<bool>;
714 fn load_views(&self) -> StorageBackendResult<Vec<ViewRow>>;
715
716 fn save_named_graph(&self, name: &str) -> StorageBackendResult<()>;
717 fn drop_named_graph(&self, name: &str) -> StorageBackendResult<()>;
718 fn load_named_graphs(&self) -> StorageBackendResult<Vec<String>>;
719 fn save_vertex(
720 &self,
721 vertex_id: u64,
722 label: &str,
723 properties_json: &str,
724 ) -> StorageBackendResult<()>;
725 fn delete_vertex(&self, vertex_id: u64) -> StorageBackendResult<()>;
726 fn load_vertices(&self) -> StorageBackendResult<Vec<(u64, String, String)>>;
727 fn save_edge(
728 &self,
729 edge_id: u64,
730 source_id: u64,
731 target_id: u64,
732 label: &str,
733 properties_json: &str,
734 ) -> StorageBackendResult<()>;
735 fn delete_edge(&self, edge_id: u64) -> StorageBackendResult<()>;
736 fn load_edges(&self) -> StorageBackendResult<Vec<EdgeRow>>;
737 fn save_graph_membership(
738 &self,
739 entity_type: &str,
740 entity_id: u64,
741 graph_name: &str,
742 ) -> StorageBackendResult<()>;
743 fn delete_graph_membership(
744 &self,
745 entity_type: &str,
746 entity_id: u64,
747 graph_name: &str,
748 ) -> StorageBackendResult<()>;
749 fn delete_graph_membership_for_graph(&self, graph_name: &str) -> StorageBackendResult<()>;
750 fn load_graph_memberships(&self) -> StorageBackendResult<Vec<(String, u64, String)>>;
751 fn purge_orphan_graph_entities(&self) -> StorageBackendResult<()>;
752 fn replace_named_graph(
753 &self,
754 graph_name: &str,
755 snapshot: &GraphSnapshot,
756 ) -> StorageBackendResult<()>;
757 fn drop_named_graph_data(&self, graph_name: &str) -> StorageBackendResult<()>;
758
759 fn save_analyzer(&self, name: &str, config_json: &str) -> StorageBackendResult<()>;
760 fn drop_analyzer(&self, name: &str) -> StorageBackendResult<()>;
761 fn load_analyzers(&self) -> StorageBackendResult<Vec<(String, String)>>;
762
763 fn save_table_field_analyzer(
764 &self,
765 table_name: &str,
766 field: &str,
767 phase: &str,
768 analyzer_name: &str,
769 ) -> StorageBackendResult<()>;
770 fn replace_table_field_analyzer(
771 &self,
772 table_name: &str,
773 field: &str,
774 phase: &str,
775 analyzer_name: &str,
776 ) -> StorageBackendResult<()>;
777 fn drop_table_field_analyzer_field(
778 &self,
779 table_name: &str,
780 field: &str,
781 ) -> StorageBackendResult<()>;
782 fn drop_table_field_analyzers(&self, table_name: &str) -> StorageBackendResult<()>;
783 fn load_table_field_analyzers(
784 &self,
785 ) -> StorageBackendResult<Vec<(String, String, String, String)>>;
786
787 fn save_foreign_server(
788 &self,
789 name: &str,
790 fdw_type: &str,
791 options_json: &str,
792 ) -> StorageBackendResult<()>;
793 fn drop_foreign_server(&self, name: &str) -> StorageBackendResult<()>;
794 fn load_foreign_servers(&self) -> StorageBackendResult<Vec<(String, String, String)>>;
795
796 fn save_foreign_table(&self, row: &ForeignTableRow) -> StorageBackendResult<()>;
797 fn rename_foreign_table(
799 &self,
800 from: &RelationIdentity,
801 to: &RelationIdentity,
802 ) -> StorageBackendResult<bool>;
803 fn update_foreign_table_security(
804 &self,
805 relation: &RelationIdentity,
806 role_owner: &str,
807 acl: Option<&[TableAclEntry]>,
808 column_acls: &std::collections::BTreeMap<String, Vec<TableAclEntry>>,
809 ) -> StorageBackendResult<bool>;
810 fn drop_foreign_table(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
811 fn load_foreign_tables(&self) -> StorageBackendResult<Vec<ForeignTableRow>>;
812
813 fn save_catalog_index(
814 &self,
815 relation: &RelationIdentity,
816 index_type: &str,
817 table_name: &str,
818 columns_json: &str,
819 parameters_json: &str,
820 ) -> StorageBackendResult<()> {
821 self.save_catalog_index_row(&CatalogIndexRow {
822 relation: relation.clone(),
823 index_type: index_type.to_string(),
824 table_name: table_name.to_string(),
825 columns_json: columns_json.to_string(),
826 parameters_json: parameters_json.to_string(),
827 definition_json: None,
828 })
829 }
830 fn save_catalog_index_row(&self, index: &CatalogIndexRow) -> StorageBackendResult<()>;
831 fn drop_catalog_index(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
832 fn drop_catalog_indexes_for_table(&self, table_name: &str) -> StorageBackendResult<()>;
833 fn load_catalog_indexes(&self) -> StorageBackendResult<Vec<CatalogIndexRow>>;
834
835 fn save_path_index(
836 &self,
837 graph_name: &str,
838 label_sequences_json: &str,
839 ) -> StorageBackendResult<()>;
840 fn drop_path_index(&self, graph_name: &str) -> StorageBackendResult<()>;
841 fn load_path_indexes(&self) -> StorageBackendResult<Vec<(String, String)>>;
842
843 fn save_column_stats(&self, stats: ColumnStatsInput<'_>) -> StorageBackendResult<()>;
844 fn replace_column_stats(
847 &self,
848 table_name: &str,
849 stats: &[ColumnStatsInput<'_>],
850 ) -> StorageBackendResult<()>;
851 fn load_column_stats(&self, table_name: &str) -> StorageBackendResult<Vec<ColumnStatsRow>>;
852 fn delete_column_stats(&self, table_name: &str) -> StorageBackendResult<()>;
853}
854
855#[cfg(test)]
856mod tests {
857 use super::{
858 sequence_value_reservation, RelationIdentity, SequenceValuePosition,
859 SequenceValueReservation,
860 };
861
862 const fn sequence_position(
863 current: i64,
864 called: bool,
865 log_count: i64,
866 ) -> SequenceValuePosition {
867 SequenceValuePosition {
868 current,
869 called,
870 log_count,
871 }
872 }
873
874 #[test]
875 fn relation_identity_rendering_is_reversible_and_collision_free() {
876 let left = RelationIdentity::new("a.b", "c");
877 let right = RelationIdentity::new("a", "b.c");
878 assert_eq!(left.qualified_name(), "\"a.b\".c");
879 assert_eq!(right.qualified_name(), "a.\"b.c\"");
880 assert_ne!(left.qualified_name(), right.qualified_name());
881 assert_eq!(
882 RelationIdentity::from_legacy_name(&left.qualified_name()).unwrap(),
883 left
884 );
885 assert_eq!(
886 RelationIdentity::from_legacy_name(&right.qualified_name()).unwrap(),
887 right
888 );
889 }
890
891 #[test]
892 fn relation_identity_preserves_quotes_and_unqualified_public_alias() {
893 let quoted = RelationIdentity::new("public", "a\"b.c");
894 assert_eq!(quoted.qualified_name(), "public.\"a\"\"b.c\"");
895 assert_eq!(
896 quoted.canonical_and_legacy_public_names(),
897 vec![
898 "public.\"a\"\"b.c\"".to_string(),
899 "\"a\"\"b.c\"".to_string()
900 ]
901 );
902 assert_eq!(
903 RelationIdentity::from_legacy_name("ed.qualified_name()).unwrap(),
904 quoted
905 );
906 assert_eq!(
907 RelationIdentity::from_legacy_name("plain").unwrap(),
908 RelationIdentity::new("public", "plain")
909 );
910 assert_eq!(
911 RelationIdentity::new("app", "plain").canonical_and_legacy_public_names(),
912 vec!["app.plain".to_string()]
913 );
914 assert_eq!(
915 RelationIdentity::new("public", "Upper").canonical_and_legacy_public_names(),
916 vec![
917 "public.\"Upper\"".to_string(),
918 "\"Upper\"".to_string(),
919 "Upper".to_string()
920 ]
921 );
922 }
923
924 #[test]
925 fn sequence_reservations_track_postgresql_log_counts() {
926 assert_eq!(
927 sequence_value_reservation(sequence_position(1, false, 0), 1, 1, i64::MAX, false, 1),
928 Some(SequenceValueReservation {
929 first_value: 1,
930 last_value: 1,
931 count: 1,
932 log_count: 32,
933 })
934 );
935 assert_eq!(
936 sequence_value_reservation(sequence_position(1, true, 32), 1, 1, i64::MAX, false, 1),
937 Some(SequenceValueReservation {
938 first_value: 2,
939 last_value: 2,
940 count: 1,
941 log_count: 31,
942 })
943 );
944 assert_eq!(
945 sequence_value_reservation(sequence_position(1, false, 0), 1, 1, i64::MAX, false, 10),
946 Some(SequenceValueReservation {
947 first_value: 1,
948 last_value: 10,
949 count: 10,
950 log_count: 32,
951 })
952 );
953 assert_eq!(
954 sequence_value_reservation(sequence_position(5, false, 0), 2, 3, 9, true, 3),
955 Some(SequenceValueReservation {
956 first_value: 5,
957 last_value: 9,
958 count: 3,
959 log_count: 0,
960 })
961 );
962 assert_eq!(
963 sequence_value_reservation(
964 sequence_position(1, false, 0),
965 1,
966 1,
967 i64::MAX,
968 false,
969 i64::MAX,
970 ),
971 Some(SequenceValueReservation {
972 first_value: 1,
973 last_value: i64::MAX,
974 count: i64::MAX,
975 log_count: 0,
976 })
977 );
978 }
979}