1use std::sync::Arc;
10
11use parking_lot::Mutex;
12use serde::{Deserialize, Serialize};
13
14use crate::catalog::{
15 CatalogFacade, CatalogIndexRow, ColumnStatsInput, ColumnStatsRow, EdgeRow, ForeignTableRow,
16 GraphSnapshot, RelationIdentity, RelationKind, SchemaRow, SequenceOptions,
17 SequenceReservationResult, SequenceRow, SequenceSetValueResult, TableSchema, ViewRow,
18};
19use crate::{StorageBackendError, StorageBackendResult};
20
21use super::codec::{
22 decode_stored_document_value, decode_string, decode_value, doc_length_key,
23 doc_length_key_prefix, document_key_prefix, encode_stored_document_value, encode_value,
24 field_stats_key, field_stats_key_prefix, key_with_tag, posting_cluster_positions_field_prefix,
25 posting_cluster_positions_key_prefix, posting_cluster_score_field_prefix,
26 posting_cluster_score_key_prefix, posting_document_key, posting_document_key_prefix,
27 posting_field_prefix, posting_key_prefix, push_str, push_u64, read_str, read_u64,
28 reverse_posting_key, reverse_posting_key_prefix, single_str_key, string_value,
29 vector_field_prefix, vector_key_prefix,
30};
31use super::{
32 KeyValueBatch, KeyValueStore, TAG_ANALYZER, TAG_ANALYZER_DESCRIPTOR, TAG_CATALOG_INDEX,
33 TAG_COLUMN_STATS, TAG_EDGE, TAG_FIELD_ANALYZER_BINDING, TAG_FOREIGN_SERVER, TAG_FOREIGN_TABLE,
34 TAG_GRAPH_MEMBERSHIP, TAG_METADATA, TAG_MODEL, TAG_NAMED_GRAPH, TAG_PATH_INDEX, TAG_RELATION,
35 TAG_SCHEMA, TAG_SCORING_PARAMS, TAG_SEQUENCE, TAG_TABLE, TAG_TABLE_FIELD_ANALYZER, TAG_VERTEX,
36 TAG_VIEW,
37};
38
39mod analyzers;
40pub(super) mod diskann;
41mod foreign;
42mod graph_access;
43mod graph_guards;
44mod graph_observations;
45mod graph_view;
46mod graphs;
47mod indexes;
48pub(super) mod keys;
49mod migration;
50mod models;
51mod occurrence_lifecycle;
52mod path_index_data;
53mod physical_indexes;
54mod records;
55mod relation_acl;
56mod relations;
57mod schema_table;
58mod sequences;
59mod table_data;
60mod tables;
61mod views;
62
63use keys::{
64 batch_put_or_keep_existing, batch_rekey_prefix, batch_rekey_prefix_or_keep_existing,
65 catalog_index_references_column, catalog_index_rename_column, column_stats_key,
66 column_stats_prefix, decode_catalog_relation_key, decode_relation_key, edge_key,
67 ensure_prefix_absent, graph_membership_graph_prefix, graph_membership_key,
68 graph_membership_prefix, load_single_string_rows, register_migration_relation, relation_key,
69 table_field_analyzer_field_prefix, table_field_analyzer_key, table_field_analyzer_prefix,
70 vertex_key,
71};
72use migration::{
73 apply_relation_migrations, collect_relation_migrations, validate_relation_parents,
74};
75use records::{
76 StoredCatalogIndex, StoredColumnStats, StoredEdge, StoredForeignServer, StoredForeignTable,
77 StoredRelation, StoredSequence, StoredVertex, StoredView,
78 STORED_FOREIGN_TABLE_SECURITY_VERSION,
79};
80
81#[derive(Clone)]
82pub struct KeyValueCatalog {
83 store: Arc<dyn KeyValueStore>,
84 graph_indexes_lock: Arc<Mutex<()>>,
85}
86
87impl KeyValueCatalog {
88 pub fn new(store: Arc<dyn KeyValueStore>) -> Self {
89 Self {
90 store,
91 graph_indexes_lock: Arc::new(Mutex::new(())),
92 }
93 }
94
95 pub fn store(&self) -> Arc<dyn KeyValueStore> {
96 Arc::clone(&self.store)
97 }
98}
99
100impl CatalogFacade for KeyValueCatalog {
101 fn transaction_model(&self) -> crate::StorageTransactionModel {
102 self.store.transaction_model()
103 }
104
105 fn transaction_affinity(&self) -> Option<crate::StorageSessionAffinity> {
106 self.store.transaction_affinity()
107 }
108
109 fn clear_path_index_data(&self, index: &str) -> StorageBackendResult<()> {
110 self.clear_path_index_data_impl(index)
111 }
112
113 fn save_path_index_pairs(
114 &self,
115 index: &str,
116 sequence: &str,
117 pairs: &[(u64, u64)],
118 ) -> StorageBackendResult<()> {
119 self.save_path_index_pairs_impl(index, sequence, pairs)
120 }
121
122 fn finish_path_index_data(
123 &self,
124 index: &str,
125 graph: &str,
126 definition: &str,
127 ) -> StorageBackendResult<()> {
128 self.finish_path_index_data_impl(index, graph, definition)
129 }
130
131 fn path_index_data_is_current(
132 &self,
133 index: &str,
134 definition: &str,
135 ) -> StorageBackendResult<bool> {
136 self.path_index_data_is_current_impl(index, definition)
137 }
138
139 fn path_index_pairs(
140 &self,
141 index: &str,
142 sequence: &str,
143 after: Option<(u64, u64)>,
144 limit: usize,
145 ) -> StorageBackendResult<Vec<(u64, u64)>> {
146 self.path_index_pairs_impl(index, sequence, after, limit)
147 }
148
149 fn graph_vertex(&self, id: u64) -> StorageBackendResult<Option<crate::GraphVertexRow>> {
150 self.graph_vertex_impl(id)
151 }
152
153 fn graph_edge(&self, id: u64) -> StorageBackendResult<Option<EdgeRow>> {
154 self.graph_edge_impl(id)
155 }
156
157 fn graph_entity_ids(
158 &self,
159 filter: crate::GraphEntityFilter<'_>,
160 after: Option<u64>,
161 limit: usize,
162 ) -> StorageBackendResult<Vec<u64>> {
163 self.graph_entity_ids_impl(filter, after, limit)
164 }
165
166 fn graph_entity_count(
167 &self,
168 filter: crate::GraphEntityFilter<'_>,
169 ) -> StorageBackendResult<u64> {
170 self.with_graph_read(|read| {
171 let mut after = None;
172 let mut count = 0_u64;
173 loop {
174 let ids = read.ids(filter, after, 256)?;
175 if ids.is_empty() {
176 return Ok(count);
177 }
178 after = ids.last().copied();
179 count = count
180 .checked_add(
181 u64::try_from(ids.len())
182 .map_err(|error| StorageBackendError::Other(error.to_string()))?,
183 )
184 .ok_or_else(|| {
185 StorageBackendError::Other("graph entity count overflow".into())
186 })?;
187 }
188 })
189 }
190
191 fn graph_entity_max_id(
192 &self,
193 kind: crate::GraphEntityKind,
194 ) -> StorageBackendResult<Option<u64>> {
195 self.with_graph_read(|read| {
196 let filter = crate::GraphEntityFilter::new(kind, None);
197 let mut after = None;
198 loop {
199 let ids = read.ids(filter, after, 256)?;
200 if ids.is_empty() {
201 return Ok(after);
202 }
203 after = ids.last().copied();
204 }
205 })
206 }
207
208 fn graph_entity_memberships(
209 &self,
210 kind: crate::GraphEntityKind,
211 id: u64,
212 ) -> StorageBackendResult<Vec<String>> {
213 self.graph_entity_memberships_impl(kind, id)
214 }
215
216 fn graph_has_membership(
217 &self,
218 kind: crate::GraphEntityKind,
219 id: u64,
220 graph: &str,
221 ) -> StorageBackendResult<bool> {
222 self.store
223 .contains_key(&graph_membership_key(kind.as_str(), id, graph)?)
224 }
225
226 fn set_metadata(&self, key: &str, value: &str) -> StorageBackendResult<()> {
227 self.set_metadata_impl(key, value)
228 }
229
230 fn delete_metadata(&self, key: &str) -> StorageBackendResult<()> {
231 self.delete_metadata_impl(key)
232 }
233
234 fn get_metadata(&self, key: &str) -> StorageBackendResult<Option<String>> {
235 self.get_metadata_impl(key)
236 }
237
238 fn metadata_has_private_changes(&self, key: &str) -> StorageBackendResult<bool> {
239 self.metadata_has_private_changes_impl(key)
240 }
241 fn metadata_with_prefix(&self, prefix: &str) -> StorageBackendResult<Vec<(String, String)>> {
242 self.metadata_with_prefix_impl(prefix)
243 }
244
245 fn save_statistics_maintenance(
246 &self,
247 table: &str,
248 state: &crate::statistics_maintenance::StatisticsMaintenance,
249 ) -> StorageBackendResult<()> {
250 let key = single_str_key(
251 TAG_METADATA,
252 &crate::statistics_maintenance::StatisticsMaintenance::key(table),
253 )?;
254 self.store.with_mutation(&mut |read, batch| {
255 let value = state
256 .encode(read.control())
257 .map_err(crate::mvcc::VersionError::into_storage_error)?;
258 batch.replace_statistics_maintenance(&key, &value)
259 })
260 }
261
262 fn migrate_relation_namespace(&self) -> StorageBackendResult<()> {
263 self.migrate_relation_namespace_impl()?;
264 self.ensure_graph_lookup_indexes()
265 }
266
267 fn save_schema_row(&self, schema: &SchemaRow) -> StorageBackendResult<()> {
268 self.save_schema_row_impl(schema)
269 }
270
271 fn schema_has_private_changes(&self, name: &str) -> StorageBackendResult<bool> {
272 self.named_record_has_private_changes(TAG_SCHEMA, name)
273 }
274
275 fn drop_schema(&self, name: &str) -> StorageBackendResult<()> {
276 self.drop_schema_impl(name)
277 }
278
279 fn load_schema_rows(&self) -> StorageBackendResult<Vec<SchemaRow>> {
280 self.load_schema_rows_impl()
281 }
282
283 fn save_table(&self, schema: &TableSchema) -> StorageBackendResult<()> {
284 self.save_table_impl(schema)
285 }
286
287 fn load_tables(&self) -> StorageBackendResult<Vec<TableSchema>> {
288 self.load_tables_impl()
289 }
290
291 fn drop_table(&self, name: &str) -> StorageBackendResult<()> {
292 self.drop_table_impl(name)
293 }
294
295 fn drop_table_and_data(&self, name: &str) -> StorageBackendResult<()> {
296 self.drop_table_and_data_impl(name)
297 }
298
299 fn purge_table_data(&self, name: &str) -> StorageBackendResult<()> {
300 self.purge_table_data_impl(name)
301 }
302
303 fn rename_table_data(&self, from: &str, to: &str) -> StorageBackendResult<()> {
304 self.rename_table_data_impl(from, to)
305 }
306
307 fn drop_column_data(&self, table_name: &str, column_name: &str) -> StorageBackendResult<()> {
308 self.drop_column_data_impl(table_name, column_name)
309 }
310
311 fn rename_column_data(
312 &self,
313 table_name: &str,
314 from: &str,
315 to: &str,
316 ) -> StorageBackendResult<()> {
317 self.rename_column_data_impl(table_name, from, to)
318 }
319
320 fn save_model(&self, name: &str, json: &str) -> StorageBackendResult<()> {
321 self.save_model_impl(name, json)
322 }
323
324 fn load_models(&self) -> StorageBackendResult<Vec<(String, String)>> {
325 self.load_models_impl()
326 }
327
328 fn load_model(&self, name: &str) -> StorageBackendResult<Option<String>> {
329 self.load_model_impl(name)
330 }
331
332 fn drop_model(&self, name: &str) -> StorageBackendResult<()> {
333 self.drop_model_impl(name)
334 }
335
336 fn save_scoring_params(&self, name: &str, params_json: &str) -> StorageBackendResult<()> {
337 self.save_scoring_params_impl(name, params_json)
338 }
339
340 fn load_scoring_params(&self, name: &str) -> StorageBackendResult<Option<String>> {
341 self.load_scoring_params_impl(name)
342 }
343
344 fn load_all_scoring_params(&self) -> StorageBackendResult<Vec<(String, String)>> {
345 self.load_all_scoring_params_impl()
346 }
347
348 fn drop_scoring_params(&self, name: &str) -> StorageBackendResult<()> {
349 self.drop_scoring_params_impl(name)
350 }
351
352 fn create_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool> {
353 self.create_sequence_row_impl(sequence)
354 }
355
356 fn replace_sequence_row(&self, sequence: &SequenceRow) -> StorageBackendResult<bool> {
357 self.replace_sequence_row_impl(sequence)
358 }
359
360 fn rename_sequence_row(&self, from: &str, to: &str) -> StorageBackendResult<bool> {
361 self.rename_sequence_row_impl(from, to)
362 }
363
364 fn drop_sequence_row(&self, name: &str) -> StorageBackendResult<bool> {
365 self.drop_sequence_row_impl(name)
366 }
367
368 fn load_sequence_rows(&self) -> StorageBackendResult<Vec<SequenceRow>> {
369 self.load_sequence_rows_impl()
370 }
371
372 fn sequence_has_private_changes(
373 &self,
374 relation: &RelationIdentity,
375 _object_id: [u8; 16],
376 ) -> StorageBackendResult<bool> {
377 self.sequence_has_private_changes_impl(relation)
378 }
379
380 fn reserve_sequence_values(
381 &self,
382 name: &str,
383 object_id: [u8; 16],
384 definition_generation: [u8; 16],
385 ) -> StorageBackendResult<SequenceReservationResult> {
386 self.reserve_sequence_values_impl(name, object_id, definition_generation)
387 }
388
389 fn set_sequence_value(
390 &self,
391 name: &str,
392 object_id: [u8; 16],
393 definition_generation: [u8; 16],
394 value: i64,
395 called: bool,
396 log_count: i64,
397 ) -> StorageBackendResult<SequenceSetValueResult> {
398 self.set_sequence_value_impl(
399 name,
400 object_id,
401 definition_generation,
402 value,
403 called,
404 log_count,
405 )
406 }
407
408 fn log_sequence_values(
409 &self,
410 name: &str,
411 object_id: [u8; 16],
412 definition_generation: [u8; 16],
413 expected: (i64, bool),
414 logged: crate::catalog::SequenceValuePosition,
415 ) -> StorageBackendResult<crate::catalog::SequenceLogResult> {
416 self.log_sequence_values_impl(name, object_id, definition_generation, expected, logged)
417 }
418
419 fn save_view(&self, view: &ViewRow) -> StorageBackendResult<()> {
420 self.save_view_impl(view)
421 }
422
423 fn rename_view(
424 &self,
425 from: &RelationIdentity,
426 to: &RelationIdentity,
427 ) -> StorageBackendResult<bool> {
428 self.rename_view_impl(from, to)
429 }
430
431 fn drop_view(&self, relation: &RelationIdentity) -> StorageBackendResult<bool> {
432 self.drop_view_impl(relation)
433 }
434
435 fn load_views(&self) -> StorageBackendResult<Vec<ViewRow>> {
436 self.load_views_impl()
437 }
438
439 fn save_named_graph(&self, name: &str) -> StorageBackendResult<()> {
440 self.save_named_graph_impl(name)
441 }
442 fn guard_graph_definition(&self, graph: Option<&str>) -> StorageBackendResult<()> {
443 self.guard_graph_definition_impl(graph)
444 }
445 fn load_named_graph_snapshot(&self, name: &str) -> StorageBackendResult<Option<GraphSnapshot>> {
446 let (mut result, namespace) =
447 self.with_graph_read(|read| Ok((read.snapshot(name)?, read.identifier_namespace()?)))?;
448 if let (Some(snapshot), Some(allocator)) =
449 (result.as_mut(), self.store.identifier_allocator())
450 {
451 snapshot.label_registry_json = crate::catalog::graph_identifiers::export_registry(
452 &snapshot.label_registry_json,
453 name,
454 namespace,
455 |key| allocator.identifier_watermark(key),
456 )?;
457 }
458 Ok(result)
459 }
460
461 fn drop_named_graph(&self, name: &str) -> StorageBackendResult<()> {
462 self.drop_named_graph_impl(name)
463 }
464
465 fn load_named_graphs(&self) -> StorageBackendResult<Vec<String>> {
466 self.load_named_graphs_impl()
467 }
468 fn named_graph_exists(&self, name: &str) -> StorageBackendResult<bool> {
469 self.store
470 .contains_key(&single_str_key(TAG_NAMED_GRAPH, name)?)
471 }
472
473 fn save_vertex(
474 &self,
475 vertex_id: u64,
476 label: &str,
477 properties_json: &str,
478 ) -> StorageBackendResult<()> {
479 self.save_vertex_impl(vertex_id, label, properties_json)
480 }
481
482 fn delete_vertex(&self, vertex_id: u64) -> StorageBackendResult<()> {
483 self.delete_vertex_impl(vertex_id)
484 }
485
486 fn load_vertices(&self) -> StorageBackendResult<Vec<(u64, String, String)>> {
487 self.load_vertices_impl()
488 }
489
490 fn save_edge(
491 &self,
492 edge_id: u64,
493 source_id: u64,
494 target_id: u64,
495 label: &str,
496 properties_json: &str,
497 ) -> StorageBackendResult<()> {
498 self.save_edge_impl(edge_id, source_id, target_id, label, properties_json)
499 }
500
501 fn delete_edge(&self, edge_id: u64) -> StorageBackendResult<()> {
502 self.delete_edge_impl(edge_id)
503 }
504
505 fn load_edges(&self) -> StorageBackendResult<Vec<EdgeRow>> {
506 self.load_edges_impl()
507 }
508
509 fn save_graph_membership(
510 &self,
511 entity_type: &str,
512 entity_id: u64,
513 graph_name: &str,
514 ) -> StorageBackendResult<()> {
515 self.save_graph_membership_impl(entity_type, entity_id, graph_name)
516 }
517
518 fn delete_graph_membership(
519 &self,
520 entity_type: &str,
521 entity_id: u64,
522 graph_name: &str,
523 ) -> StorageBackendResult<()> {
524 self.delete_graph_membership_impl(entity_type, entity_id, graph_name)
525 }
526
527 fn delete_graph_membership_for_graph(&self, graph_name: &str) -> StorageBackendResult<()> {
528 self.delete_graph_membership_for_graph_impl(graph_name)
529 }
530
531 fn load_graph_memberships(&self) -> StorageBackendResult<Vec<(String, u64, String)>> {
532 self.load_graph_memberships_impl()
533 }
534
535 fn purge_orphan_graph_entities(&self) -> StorageBackendResult<()> {
536 self.purge_orphan_graph_entities_impl()
537 }
538
539 fn replace_named_graph(
540 &self,
541 graph_name: &str,
542 snapshot: &GraphSnapshot,
543 ) -> StorageBackendResult<()> {
544 self.replace_named_graph_impl(graph_name, snapshot)
545 }
546
547 fn drop_named_graph_data(&self, graph_name: &str) -> StorageBackendResult<()> {
548 self.drop_named_graph_data_impl(graph_name)
549 }
550
551 fn save_analyzer(&self, name: &str, config_json: &str) -> StorageBackendResult<()> {
552 self.save_analyzer_impl(name, config_json)
553 }
554
555 fn drop_analyzer(&self, name: &str) -> StorageBackendResult<()> {
556 self.drop_analyzer_impl(name)
557 }
558
559 fn load_analyzers(&self) -> StorageBackendResult<Vec<(String, String)>> {
560 self.load_analyzers_impl()
561 }
562
563 fn save_analyzer_revision(
564 &self,
565 name: &str,
566 config_json: &str,
567 descriptor_json: &str,
568 ) -> StorageBackendResult<()> {
569 self.save_analyzer_revision_impl(name, config_json, descriptor_json)
570 }
571
572 fn load_analyzer_descriptors(&self) -> StorageBackendResult<Vec<(String, String)>> {
573 load_single_string_rows(self.store.as_ref(), TAG_ANALYZER_DESCRIPTOR)
574 }
575
576 fn replace_table_field_analyzer_binding(
577 &self,
578 table: &str,
579 field: &str,
580 phase: &str,
581 name: &str,
582 binding_json: &str,
583 ) -> StorageBackendResult<()> {
584 self.replace_table_field_analyzer_binding_impl(table, field, phase, name, binding_json)
585 }
586
587 fn load_table_field_analyzer_bindings(
588 &self,
589 ) -> StorageBackendResult<Vec<(String, String, String)>> {
590 self.load_table_field_analyzer_bindings_impl()
591 }
592
593 fn save_table_field_analyzer(
594 &self,
595 table_name: &str,
596 field: &str,
597 phase: &str,
598 analyzer_name: &str,
599 ) -> StorageBackendResult<()> {
600 self.save_table_field_analyzer_impl(table_name, field, phase, analyzer_name)
601 }
602
603 fn replace_table_field_analyzer(
604 &self,
605 table_name: &str,
606 field: &str,
607 phase: &str,
608 analyzer_name: &str,
609 ) -> StorageBackendResult<()> {
610 self.replace_table_field_analyzer_impl(table_name, field, phase, analyzer_name)
611 }
612
613 fn drop_table_field_analyzer_field(
614 &self,
615 table_name: &str,
616 field: &str,
617 ) -> StorageBackendResult<()> {
618 self.drop_table_field_analyzer_field_impl(table_name, field)
619 }
620
621 fn drop_table_field_analyzers(&self, table_name: &str) -> StorageBackendResult<()> {
622 self.drop_table_field_analyzers_impl(table_name)
623 }
624
625 fn load_table_field_analyzers(
626 &self,
627 ) -> StorageBackendResult<Vec<(String, String, String, String)>> {
628 self.load_table_field_analyzers_impl()
629 }
630
631 fn save_foreign_server(
632 &self,
633 name: &str,
634 fdw_type: &str,
635 options_json: &str,
636 ) -> StorageBackendResult<()> {
637 self.save_foreign_server_impl(name, fdw_type, options_json)
638 }
639
640 fn drop_foreign_server(&self, name: &str) -> StorageBackendResult<()> {
641 self.drop_foreign_server_impl(name)
642 }
643
644 fn load_foreign_servers(&self) -> StorageBackendResult<Vec<(String, String, String)>> {
645 self.load_foreign_servers_impl()
646 }
647
648 fn save_foreign_table(&self, row: &ForeignTableRow) -> StorageBackendResult<()> {
649 self.save_foreign_table_impl(row)
650 }
651
652 fn rename_foreign_table(
653 &self,
654 from: &RelationIdentity,
655 to: &RelationIdentity,
656 ) -> StorageBackendResult<bool> {
657 self.rename_foreign_table_impl(from, to)
658 }
659
660 fn update_foreign_table_security(
661 &self,
662 relation: &RelationIdentity,
663 security: &crate::RelationSecurityRow,
664 ) -> StorageBackendResult<bool> {
665 self.update_foreign_table_security_impl(relation, security)
666 }
667
668 fn drop_foreign_table(&self, relation: &RelationIdentity) -> StorageBackendResult<()> {
669 self.drop_foreign_table_impl(relation)
670 }
671
672 fn load_foreign_tables(&self) -> StorageBackendResult<Vec<ForeignTableRow>> {
673 self.load_foreign_tables_impl()
674 }
675
676 fn save_catalog_index_row(&self, index: &CatalogIndexRow) -> StorageBackendResult<()> {
677 self.save_catalog_index_impl(index)
678 }
679
680 fn drop_catalog_index(&self, relation: &RelationIdentity) -> StorageBackendResult<()> {
681 self.drop_catalog_index_impl(relation)
682 }
683
684 fn drop_catalog_indexes_for_table(&self, table_name: &str) -> StorageBackendResult<()> {
685 self.drop_catalog_indexes_for_table_impl(table_name)
686 }
687
688 fn load_catalog_indexes(&self) -> StorageBackendResult<Vec<CatalogIndexRow>> {
689 self.load_catalog_indexes_impl()
690 }
691
692 fn save_path_index(
693 &self,
694 graph_name: &str,
695 label_sequences_json: &str,
696 ) -> StorageBackendResult<()> {
697 self.save_path_index_impl(graph_name, label_sequences_json)
698 }
699
700 fn drop_path_index(&self, graph_name: &str) -> StorageBackendResult<()> {
701 self.drop_path_index_impl(graph_name)
702 }
703
704 fn load_path_indexes(&self) -> StorageBackendResult<Vec<(String, String)>> {
705 self.load_path_indexes_impl()
706 }
707
708 fn save_column_stats(&self, stats: ColumnStatsInput<'_>) -> StorageBackendResult<()> {
709 self.save_column_stats_impl(stats)
710 }
711
712 fn replace_column_stats(
713 &self,
714 table_name: &str,
715 stats: &[ColumnStatsInput<'_>],
716 ) -> StorageBackendResult<()> {
717 self.replace_column_stats_impl(table_name, stats)
718 }
719
720 fn load_column_stats(&self, table_name: &str) -> StorageBackendResult<Vec<ColumnStatsRow>> {
721 self.load_column_stats_impl(table_name)
722 }
723
724 fn delete_column_stats(&self, table_name: &str) -> StorageBackendResult<()> {
725 self.delete_column_stats_impl(table_name)
726 }
727}
728
729#[cfg(test)]
730mod tests;