1use serde::{Deserialize, Serialize};
15
16use crate::backend::StorageBackendResult;
17
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
24pub struct RelationIdentity {
25 pub schema: String,
26 pub name: String,
27}
28
29impl RelationIdentity {
30 pub fn new(schema: impl Into<String>, name: impl Into<String>) -> Self {
31 Self {
32 schema: schema.into(),
33 name: name.into(),
34 }
35 }
36
37 pub fn qualified_name(&self) -> String {
38 format!(
39 "{}.{}",
40 render_relation_component(&self.schema),
41 render_relation_component(&self.name)
42 )
43 }
44
45 pub(crate) fn canonical_and_legacy_public_names(&self) -> Vec<String> {
50 let canonical = self.qualified_name();
51 if self.schema != "public" {
52 return vec![canonical];
53 }
54 let mut names = vec![canonical];
55 let rendered_alias = render_relation_component(&self.name);
56 if !names.contains(&rendered_alias) {
57 names.push(rendered_alias);
58 }
59 if RelationIdentity::from_legacy_name(&self.name).is_ok_and(|raw| raw == *self)
64 && !names.contains(&self.name)
65 {
66 names.push(self.name.clone());
67 }
68 names
69 }
70
71 pub fn from_legacy_name(value: &str) -> Result<Self, String> {
76 let (schema, name) = Self::parse_reference(value)?;
77 Ok(Self::new(
78 schema.unwrap_or_else(|| "public".to_string()),
79 name,
80 ))
81 }
82
83 pub fn parse_reference(value: &str) -> Result<(Option<String>, String), String> {
86 let components = parse_relation_components(value)?;
87 match components.as_slice() {
88 [name] => Ok((None, name.clone())),
89 [schema, name] => Ok((Some(schema.clone()), name.clone())),
90 _ => Err(format!("invalid persisted relation name `{value}`")),
91 }
92 }
93}
94
95fn render_relation_component(component: &str) -> String {
96 let can_render_bare = component
97 .bytes()
98 .enumerate()
99 .all(|(index, byte)| match byte {
100 b'a'..=b'z' | b'_' => true,
101 b'0'..=b'9' | b'$' => index != 0,
102 _ => false,
103 });
104 if can_render_bare && !component.is_empty() {
105 component.to_string()
106 } else {
107 format!("\"{}\"", component.replace('"', "\"\""))
108 }
109}
110
111fn parse_relation_components(value: &str) -> Result<Vec<String>, String> {
112 if value.is_empty() {
113 return Err("persisted relation name is empty".to_string());
114 }
115 let mut components = Vec::with_capacity(2);
116 let mut chars = value.char_indices().peekable();
117 while chars.peek().is_some() {
118 let mut component = String::new();
119 if chars.peek().is_some_and(|(_, ch)| *ch == '"') {
120 chars.next();
121 let mut terminated = false;
122 while let Some((_, ch)) = chars.next() {
123 if ch != '"' {
124 component.push(ch);
125 continue;
126 }
127 if chars.peek().is_some_and(|(_, next)| *next == '"') {
128 chars.next();
129 component.push('"');
130 } else {
131 terminated = true;
132 break;
133 }
134 }
135 if !terminated {
136 return Err(format!("unterminated quoted relation name `{value}`"));
137 }
138 if chars.peek().is_some_and(|(_, ch)| *ch != '.') {
139 return Err(format!("invalid persisted relation name `{value}`"));
140 }
141 } else {
142 while let Some((_, ch)) = chars.peek() {
143 if *ch == '.' {
144 break;
145 }
146 if *ch == '"' {
147 return Err(format!("invalid persisted relation name `{value}`"));
148 }
149 component.push(*ch);
150 chars.next();
151 }
152 }
153 if component.is_empty() {
154 return Err(format!("invalid persisted relation name `{value}`"));
155 }
156 components.push(component);
157 if components.len() > 2 {
158 return Err(format!("invalid persisted relation name `{value}`"));
159 }
160 match chars.next() {
161 Some((_, '.')) if chars.peek().is_some() => {}
162 Some(_) => return Err(format!("invalid persisted relation name `{value}`")),
163 None => break,
164 }
165 }
166 Ok(components)
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
170#[serde(rename_all = "snake_case")]
171pub enum RelationKind {
172 Table,
173 View,
174 Sequence,
175 ForeignTable,
176}
177
178impl RelationKind {
179 pub fn as_str(self) -> &'static str {
180 match self {
181 Self::Table => "table",
182 Self::View => "view",
183 Self::Sequence => "sequence",
184 Self::ForeignTable => "foreign_table",
185 }
186 }
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct TableSchema {
191 pub relation: RelationIdentity,
192 #[serde(default)]
194 pub object_id: [u8; 16],
195 #[serde(default)]
197 pub storage_generation: [u8; 16],
198 pub analyzer_json: String,
199 pub fts_fields: Vec<String>,
200 pub vector_fields: Vec<VectorFieldSchema>,
201 #[serde(default)]
206 pub columns_json: String,
207 #[serde(default)]
210 pub constraints_json: String,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct VectorFieldSchema {
215 pub field: String,
216 pub dimensions: u32,
217}
218
219#[derive(Debug, Clone)]
222pub struct EdgeRow {
223 pub edge_id: u64,
224 pub source_id: u64,
225 pub target_id: u64,
226 pub label: String,
227 pub properties_json: String,
228}
229
230#[derive(Debug, Clone)]
232pub struct GraphVertexRow {
233 pub vertex_id: u64,
234 pub label: String,
235 pub properties_json: String,
236}
237
238#[derive(Debug, Clone)]
241pub struct GraphSnapshot {
242 pub vertices: Vec<GraphVertexRow>,
243 pub edges: Vec<EdgeRow>,
244 pub label_registry_json: String,
245}
246
247#[derive(Debug, Clone)]
249pub struct ForeignTableRow {
250 pub relation: RelationIdentity,
251 pub server_name: String,
252 pub columns_json: String,
253 pub options_json: String,
254}
255
256#[derive(Debug, Clone)]
259pub struct ViewRow {
260 pub relation: RelationIdentity,
261 pub definition_json: String,
262}
263
264#[derive(Debug, Clone)]
266pub struct CatalogIndexRow {
267 pub name: String,
268 pub index_type: String,
269 pub table_name: String,
270 pub columns_json: String,
271 pub parameters_json: String,
272}
273
274#[derive(Debug, Clone, Copy)]
276pub struct ColumnStatsInput<'a> {
277 pub table_name: &'a str,
278 pub column_name: &'a str,
279 pub distinct_count: i64,
280 pub null_count: i64,
281 pub min_value: Option<&'a str>,
282 pub max_value: Option<&'a str>,
283 pub row_count: i64,
284 pub histogram_json: &'a str,
285 pub mcv_values_json: &'a str,
286 pub mcv_frequencies_json: &'a str,
287}
288
289impl<'a> ColumnStatsInput<'a> {
290 pub fn basic(
291 table_name: &'a str,
292 column_name: &'a str,
293 distinct_count: i64,
294 null_count: i64,
295 min_value: Option<&'a str>,
296 max_value: Option<&'a str>,
297 row_count: i64,
298 ) -> Self {
299 Self {
300 table_name,
301 column_name,
302 distinct_count,
303 null_count,
304 min_value,
305 max_value,
306 row_count,
307 histogram_json: "[]",
308 mcv_values_json: "[]",
309 mcv_frequencies_json: "[]",
310 }
311 }
312}
313
314#[derive(Debug, Clone, PartialEq)]
316pub struct ColumnStatsRow {
317 pub column_name: String,
318 pub distinct_count: i64,
319 pub null_count: i64,
320 pub min_value: Option<String>,
321 pub max_value: Option<String>,
322 pub row_count: i64,
323 pub histogram_json: String,
324 pub mcv_values_json: String,
325 pub mcv_frequencies_json: String,
326}
327
328#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
332pub struct SequenceRow {
333 pub relation: RelationIdentity,
334 pub start: i64,
335 pub increment: i64,
336 pub current: i64,
337 pub called: bool,
339 pub persistence: String,
341}
342
343pub trait CatalogFacade: Send + Sync {
345 fn set_metadata(&self, key: &str, value: &str) -> StorageBackendResult<()>;
346 fn get_metadata(&self, key: &str) -> StorageBackendResult<Option<String>>;
347 fn fts_storage_was_reset(&self) -> bool {
348 false
349 }
350
351 fn migrate_relation_namespace(&self) -> StorageBackendResult<()>;
355
356 fn save_schema(&self, name: &str) -> StorageBackendResult<()>;
357 fn drop_schema(&self, name: &str) -> StorageBackendResult<()>;
358 fn load_schemas(&self) -> StorageBackendResult<Vec<String>>;
359
360 fn save_table(&self, schema: &TableSchema) -> StorageBackendResult<()>;
361 fn load_tables(&self) -> StorageBackendResult<Vec<TableSchema>>;
362 fn drop_table(&self, name: &str) -> StorageBackendResult<()>;
363 fn drop_table_and_data(&self, name: &str) -> StorageBackendResult<()>;
366 fn purge_table_data(&self, name: &str) -> StorageBackendResult<()>;
367 fn rename_table_data(&self, from: &str, to: &str) -> StorageBackendResult<()>;
368 fn drop_column_data(&self, table_name: &str, column_name: &str) -> StorageBackendResult<()>;
369 fn rename_column_data(
370 &self,
371 table_name: &str,
372 from: &str,
373 to: &str,
374 ) -> StorageBackendResult<()>;
375
376 fn save_model(&self, name: &str, json: &str) -> StorageBackendResult<()>;
377 fn load_models(&self) -> StorageBackendResult<Vec<(String, String)>>;
378 fn load_model(&self, name: &str) -> StorageBackendResult<Option<String>>;
379 fn drop_model(&self, name: &str) -> StorageBackendResult<()>;
380
381 fn save_scoring_params(&self, name: &str, params_json: &str) -> StorageBackendResult<()>;
382 fn load_scoring_params(&self, name: &str) -> StorageBackendResult<Option<String>>;
383 fn load_all_scoring_params(&self) -> StorageBackendResult<Vec<(String, String)>>;
384 fn drop_scoring_params(&self, name: &str) -> StorageBackendResult<()>;
385
386 fn create_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
387 fn replace_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool>;
388 fn drop_sequence_row(&self, name: &str) -> StorageBackendResult<bool>;
389 fn load_sequence_rows(&self) -> StorageBackendResult<Vec<SequenceRow>>;
390 fn next_sequence_value(&self, name: &str) -> StorageBackendResult<Option<i64>>;
391 fn set_sequence_value(&self, name: &str, value: i64) -> StorageBackendResult<Option<i64>>;
392
393 fn save_view(&self, view: &ViewRow) -> StorageBackendResult<()>;
394 fn drop_view(&self, relation: &RelationIdentity) -> StorageBackendResult<bool>;
395 fn load_views(&self) -> StorageBackendResult<Vec<ViewRow>>;
396
397 fn save_named_graph(&self, name: &str) -> StorageBackendResult<()>;
398 fn drop_named_graph(&self, name: &str) -> StorageBackendResult<()>;
399 fn load_named_graphs(&self) -> StorageBackendResult<Vec<String>>;
400 fn save_vertex(
401 &self,
402 vertex_id: u64,
403 label: &str,
404 properties_json: &str,
405 ) -> StorageBackendResult<()>;
406 fn delete_vertex(&self, vertex_id: u64) -> StorageBackendResult<()>;
407 fn load_vertices(&self) -> StorageBackendResult<Vec<(u64, String, String)>>;
408 fn save_edge(
409 &self,
410 edge_id: u64,
411 source_id: u64,
412 target_id: u64,
413 label: &str,
414 properties_json: &str,
415 ) -> StorageBackendResult<()>;
416 fn delete_edge(&self, edge_id: u64) -> StorageBackendResult<()>;
417 fn load_edges(&self) -> StorageBackendResult<Vec<EdgeRow>>;
418 fn save_graph_membership(
419 &self,
420 entity_type: &str,
421 entity_id: u64,
422 graph_name: &str,
423 ) -> StorageBackendResult<()>;
424 fn delete_graph_membership(
425 &self,
426 entity_type: &str,
427 entity_id: u64,
428 graph_name: &str,
429 ) -> StorageBackendResult<()>;
430 fn delete_graph_membership_for_graph(&self, graph_name: &str) -> StorageBackendResult<()>;
431 fn load_graph_memberships(&self) -> StorageBackendResult<Vec<(String, u64, String)>>;
432 fn purge_orphan_graph_entities(&self) -> StorageBackendResult<()>;
433 fn replace_named_graph(
434 &self,
435 graph_name: &str,
436 snapshot: &GraphSnapshot,
437 ) -> StorageBackendResult<()>;
438 fn drop_named_graph_data(&self, graph_name: &str) -> StorageBackendResult<()>;
439
440 fn save_analyzer(&self, name: &str, config_json: &str) -> StorageBackendResult<()>;
441 fn drop_analyzer(&self, name: &str) -> StorageBackendResult<()>;
442 fn load_analyzers(&self) -> StorageBackendResult<Vec<(String, String)>>;
443
444 fn save_table_field_analyzer(
445 &self,
446 table_name: &str,
447 field: &str,
448 phase: &str,
449 analyzer_name: &str,
450 ) -> StorageBackendResult<()>;
451 fn replace_table_field_analyzer(
452 &self,
453 table_name: &str,
454 field: &str,
455 phase: &str,
456 analyzer_name: &str,
457 ) -> StorageBackendResult<()>;
458 fn drop_table_field_analyzer_field(
459 &self,
460 table_name: &str,
461 field: &str,
462 ) -> StorageBackendResult<()>;
463 fn drop_table_field_analyzers(&self, table_name: &str) -> StorageBackendResult<()>;
464 fn load_table_field_analyzers(
465 &self,
466 ) -> StorageBackendResult<Vec<(String, String, String, String)>>;
467
468 fn save_foreign_server(
469 &self,
470 name: &str,
471 fdw_type: &str,
472 options_json: &str,
473 ) -> StorageBackendResult<()>;
474 fn drop_foreign_server(&self, name: &str) -> StorageBackendResult<()>;
475 fn load_foreign_servers(&self) -> StorageBackendResult<Vec<(String, String, String)>>;
476
477 fn save_foreign_table(
478 &self,
479 relation: &RelationIdentity,
480 server_name: &str,
481 columns_json: &str,
482 options_json: &str,
483 ) -> StorageBackendResult<()>;
484 fn drop_foreign_table(&self, relation: &RelationIdentity) -> StorageBackendResult<()>;
485 fn load_foreign_tables(&self) -> StorageBackendResult<Vec<ForeignTableRow>>;
486
487 fn save_catalog_index(
488 &self,
489 name: &str,
490 index_type: &str,
491 table_name: &str,
492 columns_json: &str,
493 parameters_json: &str,
494 ) -> StorageBackendResult<()>;
495 fn drop_catalog_index(&self, name: &str) -> StorageBackendResult<()>;
496 fn drop_catalog_indexes_for_table(&self, table_name: &str) -> StorageBackendResult<()>;
497 fn load_catalog_indexes(&self) -> StorageBackendResult<Vec<CatalogIndexRow>>;
498
499 fn save_path_index(
500 &self,
501 graph_name: &str,
502 label_sequences_json: &str,
503 ) -> StorageBackendResult<()>;
504 fn drop_path_index(&self, graph_name: &str) -> StorageBackendResult<()>;
505 fn load_path_indexes(&self) -> StorageBackendResult<Vec<(String, String)>>;
506
507 fn save_column_stats(&self, stats: ColumnStatsInput<'_>) -> StorageBackendResult<()>;
508 fn replace_column_stats(
511 &self,
512 table_name: &str,
513 stats: &[ColumnStatsInput<'_>],
514 ) -> StorageBackendResult<()>;
515 fn load_column_stats(&self, table_name: &str) -> StorageBackendResult<Vec<ColumnStatsRow>>;
516 fn delete_column_stats(&self, table_name: &str) -> StorageBackendResult<()>;
517}
518
519#[cfg(test)]
520mod tests {
521 use super::RelationIdentity;
522
523 #[test]
524 fn relation_identity_rendering_is_reversible_and_collision_free() {
525 let left = RelationIdentity::new("a.b", "c");
526 let right = RelationIdentity::new("a", "b.c");
527 assert_eq!(left.qualified_name(), "\"a.b\".c");
528 assert_eq!(right.qualified_name(), "a.\"b.c\"");
529 assert_ne!(left.qualified_name(), right.qualified_name());
530 assert_eq!(
531 RelationIdentity::from_legacy_name(&left.qualified_name()).unwrap(),
532 left
533 );
534 assert_eq!(
535 RelationIdentity::from_legacy_name(&right.qualified_name()).unwrap(),
536 right
537 );
538 }
539
540 #[test]
541 fn relation_identity_preserves_quotes_and_unqualified_public_alias() {
542 let quoted = RelationIdentity::new("public", "a\"b.c");
543 assert_eq!(quoted.qualified_name(), "public.\"a\"\"b.c\"");
544 assert_eq!(
545 quoted.canonical_and_legacy_public_names(),
546 vec![
547 "public.\"a\"\"b.c\"".to_string(),
548 "\"a\"\"b.c\"".to_string()
549 ]
550 );
551 assert_eq!(
552 RelationIdentity::from_legacy_name("ed.qualified_name()).unwrap(),
553 quoted
554 );
555 assert_eq!(
556 RelationIdentity::from_legacy_name("plain").unwrap(),
557 RelationIdentity::new("public", "plain")
558 );
559 assert_eq!(
560 RelationIdentity::new("app", "plain").canonical_and_legacy_public_names(),
561 vec!["app.plain".to_string()]
562 );
563 assert_eq!(
564 RelationIdentity::new("public", "Upper").canonical_and_legacy_public_names(),
565 vec![
566 "public.\"Upper\"".to_string(),
567 "\"Upper\"".to_string(),
568 "Upper".to_string()
569 ]
570 );
571 }
572}