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