1#[cfg(feature = "ffi")]
2use crate::list::encode_borrowed_list_for_write;
3use crate::list::{
4 LIST_OPERATOR_ID, ListConfig, decode_list_for_read, encode_list_for_write, list_operator,
5 list_operator_from_metadata, transform_list_elements,
6};
7use crate::priority_queue::{
8 PriorityQueue, priority_queue_column_family_name, priority_queue_column_family_options,
9 validate_priority_queue_column_family,
10};
11use arc_swap::ArcSwapOption;
12use bytes::Bytes;
13use cobble::{
14 BytesMergeOperator, ColumnEvolution, Config, Db, DbBuilder, DbIterator, Error, MemtableType,
15 MergeOperatorResolver, ReadOptions, RecoveryMode, Result, ScanOptions, Schema, SchemaBuilder,
16 ShardSnapshotMetadata, TransformSpec, WriteBatch, WriteOptions,
17};
18use serde::{Deserialize, Serialize};
19use serde_json::Value as JsonValue;
20use std::collections::BTreeMap;
21use std::ops::{Range, RangeInclusive};
22use std::sync::Arc;
23
24const DEFAULT_COLUMN_FAMILY_ID: u8 = 0;
25const DEFAULT_COLUMN_FAMILY_NAME: &str = "default";
26
27#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
28pub struct StructuredSchema {
29 pub column_family_ids: BTreeMap<String, u8>,
30 pub column_families: BTreeMap<u8, StructuredColumnFamilySchema>,
31}
32
33#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
34pub struct StructuredColumnFamilySchema {
35 pub columns: BTreeMap<u16, StructuredColumnType>,
36}
37
38#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
39#[serde(tag = "kind", rename_all = "snake_case")]
40pub enum StructuredColumnType {
41 #[default]
42 Bytes,
43 List(ListConfig),
44}
45
46impl StructuredColumnType {
47 pub fn list_element_transform<F>(
55 config: ListConfig,
56 transform: F,
57 ) -> impl Fn(Option<Bytes>) -> Result<Option<Bytes>> + Send + Sync + 'static
58 where
59 F: Fn(Bytes) -> Result<Bytes> + Send + Sync + 'static,
60 {
61 move |value| {
62 value
63 .map(|payload| {
64 transform_list_elements(payload, config.preserve_element_ttl, &transform)
65 })
66 .transpose()
67 }
68 }
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
72pub enum StructuredColumnValue {
73 Bytes(Bytes),
74 List(Vec<Bytes>),
75}
76
77impl StructuredColumnFamilySchema {
78 fn structured_column_type(&self, column: u16) -> &StructuredColumnType {
79 self.columns
80 .get(&column)
81 .unwrap_or(&StructuredColumnType::Bytes)
82 }
83
84 fn insert_column(&mut self, column: u16, column_type: StructuredColumnType) {
85 let mut shifted = BTreeMap::new();
86 for (existing_column, existing_type) in std::mem::take(&mut self.columns) {
87 let shifted_column = if existing_column >= column {
88 existing_column + 1
89 } else {
90 existing_column
91 };
92 shifted.insert(shifted_column, existing_type);
93 }
94 if !matches!(column_type, StructuredColumnType::Bytes) {
95 shifted.insert(column, column_type);
96 }
97 self.columns = shifted;
98 }
99
100 fn delete_column(&mut self, column: u16) {
101 let mut shifted = BTreeMap::new();
102 for (existing_column, existing_type) in std::mem::take(&mut self.columns) {
103 if existing_column == column {
104 continue;
105 }
106 let shifted_column = if existing_column > column {
107 existing_column - 1
108 } else {
109 existing_column
110 };
111 shifted.insert(shifted_column, existing_type);
112 }
113 self.columns = shifted;
114 }
115
116 fn set_column_type(&mut self, column: u16, column_type: StructuredColumnType) {
117 if matches!(column_type, StructuredColumnType::Bytes) {
118 self.columns.remove(&column);
119 } else {
120 self.columns.insert(column, column_type);
121 }
122 }
123}
124
125impl StructuredSchema {
126 pub fn column_families(&self) -> BTreeMap<String, StructuredColumnFamilySchema> {
132 let mut families = BTreeMap::new();
133 families.insert(
134 DEFAULT_COLUMN_FAMILY_NAME.to_string(),
135 self.column_families
136 .get(&DEFAULT_COLUMN_FAMILY_ID)
137 .cloned()
138 .unwrap_or_default(),
139 );
140 for (name, &id) in &self.column_family_ids {
141 if name == DEFAULT_COLUMN_FAMILY_NAME {
142 continue;
143 }
144 families.insert(
145 name.clone(),
146 self.column_families.get(&id).cloned().unwrap_or_default(),
147 );
148 }
149 families
150 }
151
152 pub(crate) fn resolve_column_family_id(&self, column_family: Option<&str>) -> Result<u8> {
153 match column_family {
154 None => Ok(DEFAULT_COLUMN_FAMILY_ID),
155 Some(column_family) if column_family == DEFAULT_COLUMN_FAMILY_NAME => {
156 Ok(DEFAULT_COLUMN_FAMILY_ID)
157 }
158 Some(column_family) => self
159 .column_family_ids
160 .get(column_family)
161 .copied()
162 .ok_or_else(|| {
163 Error::IoError(format!("Unknown column family '{}'", column_family))
164 }),
165 }
166 }
167
168 pub(crate) fn projected(
169 &self,
170 column_family_id: u8,
171 column_indices: Option<&[usize]>,
172 ) -> StructuredColumnFamilySchema {
173 let columns = self
174 .column_families
175 .get(&column_family_id)
176 .map(|family| &family.columns);
177 let Some(indices) = column_indices else {
178 return columns
179 .cloned()
180 .map(|columns| StructuredColumnFamilySchema { columns })
181 .unwrap_or_default();
182 };
183 let mut projected = BTreeMap::new();
184 for (projected_idx, original_idx) in indices.iter().enumerate() {
185 if let Some(column_type) =
186 columns.and_then(|columns| columns.get(&(*original_idx as u16)))
187 {
188 projected.insert(projected_idx as u16, column_type.clone());
189 }
190 }
191 StructuredColumnFamilySchema { columns: projected }
192 }
193
194 fn insert_structured_column(
195 &mut self,
196 column_family_id: u8,
197 column: u16,
198 column_type: StructuredColumnType,
199 ) {
200 self.column_families
201 .entry(column_family_id)
202 .or_default()
203 .insert_column(column, column_type);
204 }
205
206 fn delete_structured_column(&mut self, column_family_id: u8, column: u16) {
207 self.column_families
208 .entry(column_family_id)
209 .or_default()
210 .delete_column(column);
211 }
212
213 pub(crate) fn project_structured_family(
214 &self,
215 column_family: Option<&str>,
216 column_indices: Option<&[usize]>,
217 ) -> Result<Arc<StructuredColumnFamilySchema>> {
218 let column_family_id = self.resolve_column_family_id(column_family)?;
219 Ok(Arc::new(self.projected(column_family_id, column_indices)))
220 }
221}
222
223impl Default for StructuredSchema {
224 fn default() -> Self {
225 Self {
226 column_family_ids: BTreeMap::from([(
227 DEFAULT_COLUMN_FAMILY_NAME.to_string(),
228 DEFAULT_COLUMN_FAMILY_ID,
229 )]),
230 column_families: BTreeMap::new(),
231 }
232 }
233}
234
235pub(crate) fn encode_for_write(
236 schema: &StructuredSchema,
237 column_family: Option<&str>,
238 now_seconds: u32,
239 column: u16,
240 value: StructuredColumnValue,
241 ttl_seconds: Option<u32>,
242) -> Result<Bytes> {
243 let column_family_id = schema.resolve_column_family_id(column_family)?;
244 match (
245 schema
246 .column_families
247 .get(&column_family_id)
248 .map(|family| family.structured_column_type(column))
249 .unwrap_or(&StructuredColumnType::Bytes),
250 value,
251 ) {
252 (StructuredColumnType::Bytes, StructuredColumnValue::Bytes(value)) => Ok(value),
253 (StructuredColumnType::List(config), StructuredColumnValue::List(elements)) => {
254 encode_list_for_write(elements, config, ttl_seconds, now_seconds)
255 }
256 (_, _) => Err(Error::InputError(format!(
257 "column {} expects a different type of value",
258 column,
259 ))),
260 }
261}
262
263#[cfg(feature = "ffi")]
264fn ensure_list_column(
265 schema: &StructuredSchema,
266 column_family: Option<&str>,
267 column: u16,
268) -> Result<()> {
269 let column_family_id = schema.resolve_column_family_id(column_family)?;
270 match schema
271 .column_families
272 .get(&column_family_id)
273 .map(|family| family.structured_column_type(column))
274 .unwrap_or(&StructuredColumnType::Bytes)
275 {
276 StructuredColumnType::List(_) => Ok(()),
277 StructuredColumnType::Bytes => Err(Error::InputError(format!(
278 "column {} is not a LIST column",
279 column,
280 ))),
281 }
282}
283
284pub(crate) fn ensure_bytes_column(
285 schema: &StructuredSchema,
286 column_family: Option<&str>,
287 column: u16,
288) -> Result<()> {
289 let column_family_id = schema.resolve_column_family_id(column_family)?;
290 match schema
291 .column_families
292 .get(&column_family_id)
293 .map(|family| family.structured_column_type(column))
294 .unwrap_or(&StructuredColumnType::Bytes)
295 {
296 StructuredColumnType::Bytes => Ok(()),
297 StructuredColumnType::List(_) => Err(Error::InputError(format!(
298 "column {} is not a BYTES column",
299 column,
300 ))),
301 }
302}
303
304#[cfg(feature = "ffi")]
305pub(crate) fn list_column_config(
306 schema: &StructuredSchema,
307 column_family: Option<&str>,
308 column: u16,
309) -> Result<ListConfig> {
310 let column_family_id = schema.resolve_column_family_id(column_family)?;
311 match schema
312 .column_families
313 .get(&column_family_id)
314 .map(|family| family.structured_column_type(column))
315 .unwrap_or(&StructuredColumnType::Bytes)
316 {
317 StructuredColumnType::List(config) => Ok(config.clone()),
318 StructuredColumnType::Bytes => Err(Error::InputError(format!(
319 "column {} is not a LIST column",
320 column,
321 ))),
322 }
323}
324
325pub(crate) fn decode_row(
326 schema: &StructuredColumnFamilySchema,
327 now_seconds: u32,
328 columns: Vec<Option<Bytes>>,
329) -> Result<Vec<Option<StructuredColumnValue>>> {
330 columns
331 .into_iter()
332 .enumerate()
333 .map(|(idx, column)| {
334 let Some(raw) = column else {
335 return Ok(None);
336 };
337 match schema.structured_column_type(idx as u16) {
338 StructuredColumnType::Bytes => Ok(Some(StructuredColumnValue::Bytes(raw))),
339 StructuredColumnType::List(config) => Ok(Some(StructuredColumnValue::List(
340 decode_list_for_read(&raw, config, now_seconds)?,
341 ))),
342 }
343 })
344 .collect()
345}
346
347pub(crate) fn load_structured_schema_from_cobble_schema(
348 schema: &Schema,
349) -> Result<StructuredSchema> {
350 let mut structured_schema = StructuredSchema {
351 column_family_ids: schema.column_family_ids(),
352 ..StructuredSchema::default()
353 };
354 let column_family_ids = structured_schema.column_family_ids.clone();
355 for (column_family, num_columns) in schema.column_families() {
356 let column_family_id = column_family_ids
357 .get(&column_family)
358 .copied()
359 .ok_or_else(|| {
360 Error::FileFormatError(format!("missing column family id for {}", column_family))
361 })?;
362 let operator_ids = if column_family == DEFAULT_COLUMN_FAMILY_NAME {
363 schema.all_operator_ids()
364 } else {
365 schema.operator_ids_in_family(&column_family)?
366 };
367 let mut columns = BTreeMap::new();
368 for column_idx in 0..num_columns {
369 let operator_id = operator_ids
370 .get(column_idx)
371 .map(|s| s.as_str())
372 .unwrap_or("");
373 if operator_id == LIST_OPERATOR_ID {
374 let metadata_value = schema
375 .column_metadata_at(Some(column_family.as_str()), column_idx)?
376 .ok_or_else(|| {
377 Error::FileFormatError(format!(
378 "list column {} in column family {} missing metadata",
379 column_idx, column_family
380 ))
381 })?;
382 let config = serde_json::from_value::<ListConfig>(metadata_value.clone()).map_err(
383 |err| {
384 Error::FileFormatError(format!(
385 "failed to decode list config at column {} in column family {}: {}",
386 column_idx, column_family, err
387 ))
388 },
389 )?;
390 columns.insert(column_idx as u16, StructuredColumnType::List(config));
391 }
392 }
393 structured_schema
394 .column_families
395 .insert(column_family_id, StructuredColumnFamilySchema { columns });
396 }
397 Ok(structured_schema)
398}
399
400pub(crate) fn combined_resolver(
401 custom: Option<Arc<dyn MergeOperatorResolver>>,
402) -> Arc<dyn MergeOperatorResolver> {
403 Arc::new(move |id: &str, metadata: Option<&JsonValue>| {
404 if let Some(operator) = list_operator_from_metadata(id, metadata) {
405 return Some(operator);
406 }
407 custom
408 .as_ref()
409 .and_then(|resolver| resolver.resolve(id, metadata))
410 })
411}
412
413pub fn structured_merge_operator_resolver() -> Arc<dyn MergeOperatorResolver> {
416 combined_resolver(None)
417}
418
419pub fn structured_resolvable_operator_ids() -> Vec<String> {
421 vec![LIST_OPERATOR_ID.to_string()]
422}
423
424pub struct StructuredWriteBatch {
431 structured_schema: Arc<StructuredSchema>,
432 now_seconds: u32,
433 inner: WriteBatch,
434}
435
436impl StructuredWriteBatch {
437 pub(crate) fn new(structured_schema: Arc<StructuredSchema>, now_seconds: u32) -> Self {
438 Self {
439 structured_schema,
440 now_seconds,
441 inner: WriteBatch::new(),
442 }
443 }
444
445 pub fn put<K, V>(&mut self, bucket: u16, key: K, column: u16, value: V) -> Result<()>
446 where
447 K: AsRef<[u8]>,
448 V: Into<StructuredColumnValue>,
449 {
450 self.put_with_options(
451 bucket,
452 key,
453 column,
454 value,
455 &StructuredWriteOptions::default(),
456 )
457 }
458
459 pub fn put_with_options<K, V>(
460 &mut self,
461 bucket: u16,
462 key: K,
463 column: u16,
464 value: V,
465 options: &StructuredWriteOptions,
466 ) -> Result<()>
467 where
468 K: AsRef<[u8]>,
469 V: Into<StructuredColumnValue>,
470 {
471 let encoded = encode_for_write(
472 &self.structured_schema,
473 options.column_family(),
474 self.now_seconds,
475 column,
476 value.into(),
477 options.ttl_seconds(),
478 )?;
479 self.inner
480 .put_with_options(bucket, key, column, encoded, options.as_cobble());
481 Ok(())
482 }
483
484 #[cfg(feature = "ffi")]
485 pub(crate) fn put_borrowed_bytes_with_options<K>(
486 &mut self,
487 bucket: u16,
488 key: K,
489 column: u16,
490 value: &[u8],
491 options: &StructuredWriteOptions,
492 ) -> Result<()>
493 where
494 K: AsRef<[u8]>,
495 {
496 ensure_bytes_column(&self.structured_schema, options.column_family(), column)?;
497 self.inner
498 .put_with_options(bucket, key, column, value, options.as_cobble());
499 Ok(())
500 }
501
502 #[cfg(feature = "ffi")]
503 pub(crate) fn put_borrowed_list_with_options<K>(
504 &mut self,
505 bucket: u16,
506 key: K,
507 column: u16,
508 elements: &[&[u8]],
509 options: &StructuredWriteOptions,
510 ) -> Result<()>
511 where
512 K: AsRef<[u8]>,
513 {
514 let config = list_column_config(&self.structured_schema, options.column_family(), column)?;
515 let encoded = encode_borrowed_list_for_write(
516 elements,
517 &config,
518 options.ttl_seconds(),
519 self.now_seconds,
520 )?;
521 self.inner
522 .put_with_options(bucket, key, column, encoded, options.as_cobble());
523 Ok(())
524 }
525
526 pub fn delete<K>(&mut self, bucket: u16, key: K, column: u16)
527 where
528 K: AsRef<[u8]>,
529 {
530 self.delete_with_options(bucket, key, column, &StructuredWriteOptions::default());
531 }
532
533 pub fn delete_with_options<K>(
534 &mut self,
535 bucket: u16,
536 key: K,
537 column: u16,
538 options: &StructuredWriteOptions,
539 ) where
540 K: AsRef<[u8]>,
541 {
542 self.inner
543 .delete_with_options(bucket, key, column, options.as_cobble());
544 }
545
546 pub fn merge<K, V>(&mut self, bucket: u16, key: K, column: u16, value: V) -> Result<()>
547 where
548 K: AsRef<[u8]>,
549 V: Into<StructuredColumnValue>,
550 {
551 self.merge_with_options(
552 bucket,
553 key,
554 column,
555 value,
556 &StructuredWriteOptions::default(),
557 )
558 }
559
560 pub fn merge_with_options<K, V>(
561 &mut self,
562 bucket: u16,
563 key: K,
564 column: u16,
565 value: V,
566 options: &StructuredWriteOptions,
567 ) -> Result<()>
568 where
569 K: AsRef<[u8]>,
570 V: Into<StructuredColumnValue>,
571 {
572 let encoded = encode_for_write(
573 &self.structured_schema,
574 options.column_family(),
575 self.now_seconds,
576 column,
577 value.into(),
578 options.ttl_seconds(),
579 )?;
580 self.inner
581 .merge_with_options(bucket, key, column, encoded, options.as_cobble());
582 Ok(())
583 }
584
585 #[cfg(feature = "ffi")]
586 pub(crate) fn merge_borrowed_bytes_with_options<K>(
587 &mut self,
588 bucket: u16,
589 key: K,
590 column: u16,
591 value: &[u8],
592 options: &StructuredWriteOptions,
593 ) -> Result<()>
594 where
595 K: AsRef<[u8]>,
596 {
597 ensure_bytes_column(&self.structured_schema, options.column_family(), column)?;
598 self.inner
599 .merge_with_options(bucket, key, column, value, options.as_cobble());
600 Ok(())
601 }
602
603 #[cfg(feature = "ffi")]
604 pub(crate) fn merge_borrowed_list_with_options<K>(
605 &mut self,
606 bucket: u16,
607 key: K,
608 column: u16,
609 elements: &[&[u8]],
610 options: &StructuredWriteOptions,
611 ) -> Result<()>
612 where
613 K: AsRef<[u8]>,
614 {
615 let config = list_column_config(&self.structured_schema, options.column_family(), column)?;
616 let encoded = encode_borrowed_list_for_write(
617 elements,
618 &config,
619 options.ttl_seconds(),
620 self.now_seconds,
621 )?;
622 self.inner
623 .merge_with_options(bucket, key, column, encoded, options.as_cobble());
624 Ok(())
625 }
626
627 pub(crate) fn into_inner(self) -> WriteBatch {
628 self.inner
629 }
630}
631
632impl From<Bytes> for StructuredColumnValue {
635 fn from(value: Bytes) -> Self {
636 Self::Bytes(value)
637 }
638}
639
640impl From<Vec<u8>> for StructuredColumnValue {
641 fn from(value: Vec<u8>) -> Self {
642 Self::Bytes(Bytes::from(value))
643 }
644}
645
646impl From<Vec<Bytes>> for StructuredColumnValue {
647 fn from(value: Vec<Bytes>) -> Self {
648 Self::List(value)
649 }
650}
651
652impl From<Vec<Vec<u8>>> for StructuredColumnValue {
653 fn from(value: Vec<Vec<u8>>) -> Self {
654 Self::List(value.into_iter().map(Bytes::from).collect())
655 }
656}
657
658pub struct StructuredDb {
661 db: Db,
662 structured_schema: Arc<StructuredSchema>,
663 default_write_options: StructuredWriteOptions,
664 default_read_options: StructuredReadOptions,
665 default_scan_options: StructuredScanOptions,
666}
667
668pub struct StructuredDbBuilder {
670 inner: DbBuilder,
671}
672
673impl StructuredDbBuilder {
674 pub fn new(config: Config) -> Self {
675 Self {
676 inner: DbBuilder::new(config).merge_operator_resolver(combined_resolver(None)),
677 }
678 }
679
680 pub fn bucket_ranges(mut self, bucket_ranges: Vec<RangeInclusive<u16>>) -> Self {
681 self.inner = self.inner.bucket_ranges(bucket_ranges);
682 self
683 }
684
685 pub fn db_id(mut self, db_id: impl Into<String>) -> Self {
686 self.inner = self.inner.db_id(db_id);
687 self
688 }
689
690 pub fn merge_operator_resolver(mut self, resolver: Arc<dyn MergeOperatorResolver>) -> Self {
691 self.inner = self
692 .inner
693 .merge_operator_resolver(combined_resolver(Some(resolver)));
694 self
695 }
696
697 pub fn register_schema_transform<F, T>(
699 mut self,
700 transform_type: impl Into<String>,
701 factory: F,
702 ) -> Result<Self>
703 where
704 F: Fn(&[u8]) -> Result<T> + Send + Sync + 'static,
705 T: Fn(Option<Bytes>) -> Result<Option<Bytes>> + Send + Sync + 'static,
706 {
707 self.inner = self
708 .inner
709 .register_schema_transform(transform_type, factory)?;
710 Ok(self)
711 }
712
713 pub fn open(self) -> Result<StructuredDb> {
714 StructuredDb::from_db(self.inner.open()?)
715 }
716
717 pub fn open_from_snapshot(self, snapshot_id: u64) -> Result<StructuredDb> {
718 StructuredDb::from_db(self.inner.open_from_snapshot(snapshot_id)?)
719 }
720
721 pub fn open_from_snapshot_with_recovery_mode(
722 self,
723 snapshot_id: u64,
724 recovery_mode: RecoveryMode,
725 ) -> Result<StructuredDb> {
726 StructuredDb::from_db(
727 self.inner
728 .open_from_snapshot_with_recovery_mode(snapshot_id, recovery_mode)?,
729 )
730 }
731
732 pub fn resume(self) -> Result<StructuredDb> {
733 StructuredDb::from_db(self.inner.resume()?)
734 }
735
736 pub fn resume_with_recovery_mode(self, recovery_mode: RecoveryMode) -> Result<StructuredDb> {
737 StructuredDb::from_db(self.inner.resume_with_recovery_mode(recovery_mode)?)
738 }
739
740 pub fn resume_from_snapshot(self, snapshot_id: u64) -> Result<StructuredDb> {
741 StructuredDb::from_db(self.inner.resume_from_snapshot(snapshot_id)?)
742 }
743
744 pub fn resume_from_snapshot_with_recovery_mode(
745 self,
746 snapshot_id: u64,
747 recovery_mode: RecoveryMode,
748 ) -> Result<StructuredDb> {
749 StructuredDb::from_db(
750 self.inner
751 .resume_from_snapshot_with_recovery_mode(snapshot_id, recovery_mode)?,
752 )
753 }
754}
755
756#[derive(Clone, Debug)]
757struct StructuredProjectionCacheEntry {
758 schema: Arc<StructuredSchema>,
759 projected_schema: Arc<StructuredColumnFamilySchema>,
760}
761
762fn resolve_structured_projection_cached(
763 cache: &Arc<ArcSwapOption<StructuredProjectionCacheEntry>>,
764 structured_schema: &Arc<StructuredSchema>,
765 column_family: Option<&str>,
766 column_indices: Option<&[usize]>,
767) -> Result<Arc<StructuredColumnFamilySchema>> {
768 if let Some(entry) = cache.load_full()
769 && Arc::ptr_eq(&entry.schema, structured_schema)
770 {
771 return Ok(Arc::clone(&entry.projected_schema));
772 }
773 let projected = structured_schema.project_structured_family(column_family, column_indices)?;
774 cache.store(Some(Arc::new(StructuredProjectionCacheEntry {
775 schema: Arc::clone(structured_schema),
776 projected_schema: Arc::clone(&projected),
777 })));
778 Ok(projected)
779}
780
781#[derive(Clone, Debug, Default)]
782pub struct StructuredWriteOptions {
783 inner: WriteOptions,
784}
785
786impl StructuredWriteOptions {
787 pub fn with_ttl(ttl_seconds: u32) -> Self {
788 Self {
789 inner: WriteOptions::with_ttl(ttl_seconds),
790 }
791 }
792
793 pub fn with_column_family(column_family: impl Into<String>) -> Self {
794 Self {
795 inner: WriteOptions::with_column_family(column_family),
796 }
797 }
798
799 pub fn with_await_durable(mut self, await_durable: bool) -> Self {
800 self.inner = self.inner.with_await_durable(await_durable);
801 self
802 }
803
804 pub fn as_cobble(&self) -> &WriteOptions {
805 &self.inner
806 }
807
808 pub fn into_cobble(self) -> WriteOptions {
809 self.inner
810 }
811
812 pub fn ttl_seconds(&self) -> Option<u32> {
813 self.inner.ttl_seconds
814 }
815
816 pub fn column_family(&self) -> Option<&str> {
817 self.inner.column_family.as_deref()
818 }
819
820 pub fn await_durable(&self) -> bool {
821 self.inner.await_durable
822 }
823}
824
825impl From<WriteOptions> for StructuredWriteOptions {
826 fn from(value: WriteOptions) -> Self {
827 Self { inner: value }
828 }
829}
830
831impl From<StructuredWriteOptions> for WriteOptions {
832 fn from(value: StructuredWriteOptions) -> Self {
833 value.inner
834 }
835}
836
837#[derive(Clone, Debug)]
838pub struct StructuredReadOptions {
839 inner: ReadOptions,
840 projected_schema_cache: Arc<ArcSwapOption<StructuredProjectionCacheEntry>>,
841}
842
843impl Default for StructuredReadOptions {
844 fn default() -> Self {
845 Self::from(ReadOptions::default())
846 }
847}
848
849impl StructuredReadOptions {
850 pub fn for_column(column_index: usize) -> Self {
851 Self::from(ReadOptions::for_column(column_index))
852 }
853
854 pub fn for_columns(column_indices: Vec<usize>) -> Self {
855 Self::from(ReadOptions::for_columns(column_indices))
856 }
857
858 pub fn for_column_in_family(column_family: impl Into<String>, column_index: usize) -> Self {
859 Self::from(ReadOptions::for_column_in_family(
860 column_family,
861 column_index,
862 ))
863 }
864
865 pub fn for_columns_in_family(
866 column_family: impl Into<String>,
867 column_indices: Vec<usize>,
868 ) -> Self {
869 Self::from(ReadOptions::for_columns_in_family(
870 column_family,
871 column_indices,
872 ))
873 }
874
875 pub fn with_column_family(mut self, column_family: impl Into<String>) -> Self {
876 self.inner = self.inner.with_column_family(column_family);
877 self.projected_schema_cache = Arc::new(ArcSwapOption::empty());
878 self
879 }
880
881 pub fn as_cobble(&self) -> &ReadOptions {
882 &self.inner
883 }
884
885 pub fn into_cobble(self) -> ReadOptions {
886 self.inner
887 }
888
889 pub(crate) fn resolve_projected_schema_cached(
890 &self,
891 structured_schema: &Arc<StructuredSchema>,
892 ) -> Result<Arc<StructuredColumnFamilySchema>> {
893 resolve_structured_projection_cached(
894 &self.projected_schema_cache,
895 structured_schema,
896 self.inner.column_family.as_deref(),
897 self.inner.column_indices.as_deref(),
898 )
899 }
900}
901
902impl From<ReadOptions> for StructuredReadOptions {
903 fn from(value: ReadOptions) -> Self {
904 Self {
905 inner: value,
906 projected_schema_cache: Arc::new(ArcSwapOption::empty()),
907 }
908 }
909}
910
911impl From<StructuredReadOptions> for ReadOptions {
912 fn from(value: StructuredReadOptions) -> Self {
913 value.inner
914 }
915}
916
917#[derive(Clone, Debug)]
918pub struct StructuredScanOptions {
919 inner: ScanOptions,
920 projected_schema_cache: Arc<ArcSwapOption<StructuredProjectionCacheEntry>>,
921}
922
923impl Default for StructuredScanOptions {
924 fn default() -> Self {
925 Self::from(ScanOptions::default())
926 }
927}
928
929impl StructuredScanOptions {
930 pub fn for_column(column_index: usize) -> Self {
931 Self::from(ScanOptions::for_column(column_index))
932 }
933
934 pub fn for_columns(column_indices: Vec<usize>) -> Self {
935 Self::from(ScanOptions::for_columns(column_indices))
936 }
937
938 pub fn with_column_family(mut self, column_family: impl Into<String>) -> Self {
939 self.inner = self.inner.with_column_family(column_family);
940 self.projected_schema_cache = Arc::new(ArcSwapOption::empty());
941 self
942 }
943
944 pub fn with_preload_scan_cursor_block(mut self, enabled: bool) -> Self {
945 self.inner = self.inner.with_preload_scan_cursor_block(enabled);
946 self
947 }
948
949 pub fn with_stop_at_block_boundary(mut self, enabled: bool) -> Self {
957 self.inner = self.inner.with_stop_at_block_boundary(enabled);
958 self
959 }
960
961 pub fn as_cobble(&self) -> &ScanOptions {
962 &self.inner
963 }
964
965 pub fn into_cobble(self) -> ScanOptions {
966 self.inner
967 }
968
969 pub(crate) fn resolve_projected_schema_cached(
970 &self,
971 structured_schema: &Arc<StructuredSchema>,
972 ) -> Result<Arc<StructuredColumnFamilySchema>> {
973 resolve_structured_projection_cached(
974 &self.projected_schema_cache,
975 structured_schema,
976 self.inner.column_family.as_deref(),
977 self.inner.column_indices.as_deref(),
978 )
979 }
980}
981
982impl From<ScanOptions> for StructuredScanOptions {
983 fn from(value: ScanOptions) -> Self {
984 Self {
985 inner: value,
986 projected_schema_cache: Arc::new(ArcSwapOption::empty()),
987 }
988 }
989}
990
991impl From<StructuredScanOptions> for ScanOptions {
992 fn from(value: StructuredScanOptions) -> Self {
993 value.inner
994 }
995}
996
997pub trait StructuredSchemaOwner {
998 fn current_structured_schema(&self) -> StructuredSchema;
999 fn begin_core_schema_update(&self) -> SchemaBuilder;
1000 fn install_committed_structured_schema(&mut self, schema: StructuredSchema)
1001 -> StructuredSchema;
1002}
1003
1004pub struct StructuredSchemaBuilder<'a, O: StructuredSchemaOwner> {
1005 owner: &'a mut O,
1006 schema: StructuredSchema,
1007 inner: Option<SchemaBuilder>,
1008 pending_error: Option<Error>,
1009}
1010
1011impl<'a, O: StructuredSchemaOwner> StructuredSchemaBuilder<'a, O> {
1012 pub fn new(owner: &'a mut O) -> Self {
1013 let schema = owner.current_structured_schema();
1014 let inner = owner.begin_core_schema_update();
1015 Self {
1016 owner,
1017 schema,
1018 inner: Some(inner),
1019 pending_error: None,
1020 }
1021 }
1022
1023 pub fn add_bytes_column(&mut self, column_family: Option<String>, column: u16) -> &mut Self {
1024 let Some((column_family_name, core_column_family)) =
1025 self.normalize_column_family_or_record_error(column_family)
1026 else {
1027 return self;
1028 };
1029 let Some(column_family_id) = self.apply_inner(|inner| {
1030 let column_family_id = match core_column_family.as_ref() {
1031 Some(column_family) => inner.ensure_column_family_exists(column_family.clone())?,
1032 None => DEFAULT_COLUMN_FAMILY_ID,
1033 };
1034 inner.add_column(
1035 column as usize,
1036 Some(Arc::new(BytesMergeOperator)),
1037 None,
1038 core_column_family.clone(),
1039 )?;
1040 Ok(column_family_id)
1041 }) else {
1042 return self;
1043 };
1044 self.sync_structured_column_family_id(&column_family_name, column_family_id);
1045 if let Some(column_family_id) =
1046 self.structured_column_family_id_or_record_error(&column_family_name)
1047 {
1048 self.schema.insert_structured_column(
1049 column_family_id,
1050 column,
1051 StructuredColumnType::Bytes,
1052 );
1053 }
1054 self
1055 }
1056
1057 pub fn add_list_column(
1058 &mut self,
1059 column_family: Option<String>,
1060 column: u16,
1061 config: ListConfig,
1062 ) -> &mut Self {
1063 let Some((column_family_name, core_column_family)) =
1064 self.normalize_column_family_or_record_error(column_family)
1065 else {
1066 return self;
1067 };
1068 let Some(column_family_id) = self.apply_inner(|inner| {
1069 let column_family_id = match core_column_family.as_ref() {
1070 Some(column_family) => inner.ensure_column_family_exists(column_family.clone())?,
1071 None => DEFAULT_COLUMN_FAMILY_ID,
1072 };
1073 inner.add_column(
1074 column as usize,
1075 Some(list_operator(config.clone())),
1076 None,
1077 core_column_family.clone(),
1078 )?;
1079 Ok(column_family_id)
1080 }) else {
1081 return self;
1082 };
1083 self.sync_structured_column_family_id(&column_family_name, column_family_id);
1084 if let Some(column_family_id) =
1085 self.structured_column_family_id_or_record_error(&column_family_name)
1086 {
1087 self.schema.insert_structured_column(
1088 column_family_id,
1089 column,
1090 StructuredColumnType::List(config),
1091 );
1092 }
1093 self
1094 }
1095
1096 pub fn delete_column(&mut self, column_family: Option<String>, column: u16) -> &mut Self {
1097 let Some((column_family_name, core_column_family)) =
1098 self.normalize_column_family_or_record_error(column_family)
1099 else {
1100 return self;
1101 };
1102 let Some(()) = self
1103 .apply_inner(|inner| inner.delete_column(core_column_family.clone(), column as usize))
1104 else {
1105 return self;
1106 };
1107 if let Some(column_family_id) =
1108 self.structured_column_family_id_or_record_error(&column_family_name)
1109 {
1110 self.schema
1111 .delete_structured_column(column_family_id, column);
1112 }
1113 self
1114 }
1115
1116 pub fn transform_column(
1124 &mut self,
1125 column_family: Option<String>,
1126 column: u16,
1127 target_type: StructuredColumnType,
1128 transform_id: impl Into<String>,
1129 ) -> &mut Self {
1130 let Some((column_family_name, core_column_family)) =
1131 self.normalize_column_family_or_record_error(column_family)
1132 else {
1133 return self;
1134 };
1135 let transform_id = transform_id.into();
1136 let Some(()) = self.apply_inner(|inner| {
1137 let column_count = inner.num_columns_in_family(core_column_family.clone())?;
1138 let column_index = usize::from(column);
1139 if column_index >= column_count {
1140 return Err(Error::InvalidState(format!(
1141 "Cannot transform column {} in column family {} with {} columns",
1142 column, column_family_name, column_count
1143 )));
1144 }
1145 let columns = (0..column_count)
1146 .map(|source_index| ColumnEvolution::Source {
1147 source_index,
1148 transform: (source_index == column_index).then(|| TransformSpec {
1149 transform_type: transform_id.clone(),
1150 spec: Bytes::new(),
1151 }),
1152 })
1153 .collect();
1154 inner.remap_columns(core_column_family.clone(), columns)?;
1155 apply_structured_column_type(
1156 inner,
1157 core_column_family.clone(),
1158 column_index,
1159 &target_type,
1160 )
1161 }) else {
1162 return self;
1163 };
1164 let Some(column_family_id) =
1165 self.structured_column_family_id_or_record_error(&column_family_name)
1166 else {
1167 return self;
1168 };
1169 self.schema
1170 .column_families
1171 .entry(column_family_id)
1172 .or_default()
1173 .set_column_type(column, target_type);
1174 self
1175 }
1176
1177 pub fn set_column_family_options(
1178 &mut self,
1179 column_family: Option<String>,
1180 options: cobble::ColumnFamilyOptions,
1181 ) -> &mut Self {
1182 let Some((column_family_name, core_column_family)) =
1183 self.normalize_column_family_or_record_error(column_family)
1184 else {
1185 return self;
1186 };
1187 let Some(column_family_id) = self.apply_inner(|inner| {
1188 let column_family_id = match core_column_family.as_ref() {
1189 Some(column_family) => inner.ensure_column_family_exists(column_family.clone())?,
1190 None => DEFAULT_COLUMN_FAMILY_ID,
1191 };
1192 inner.set_column_family_options(core_column_family.clone(), options.clone())?;
1193 Ok(column_family_id)
1194 }) else {
1195 return self;
1196 };
1197 self.sync_structured_column_family_id(&column_family_name, column_family_id);
1198 self
1199 }
1200
1201 pub fn current_schema(&self) -> &StructuredSchema {
1202 &self.schema
1203 }
1204
1205 fn ensure_structured_column_family_id(&self, column_family: &str) -> Result<u8> {
1206 if column_family == DEFAULT_COLUMN_FAMILY_NAME {
1207 return Ok(DEFAULT_COLUMN_FAMILY_ID);
1208 }
1209 self.schema
1210 .column_family_ids
1211 .get(column_family)
1212 .copied()
1213 .ok_or_else(|| {
1214 Error::InvalidState(format!(
1215 "structured schema missing column family id for {}",
1216 column_family
1217 ))
1218 })
1219 }
1220
1221 fn sync_structured_column_family_id(&mut self, column_family: &str, column_family_id: u8) {
1222 if column_family == DEFAULT_COLUMN_FAMILY_NAME {
1223 return;
1224 }
1225 self.schema
1226 .column_family_ids
1227 .insert(column_family.to_string(), column_family_id);
1228 self.schema
1229 .column_families
1230 .entry(column_family_id)
1231 .or_default();
1232 }
1233
1234 fn normalize_column_family_or_record_error(
1235 &mut self,
1236 column_family: Option<String>,
1237 ) -> Option<(String, Option<String>)> {
1238 match normalize_structured_column_family_name(column_family) {
1239 Ok(names) => Some(names),
1240 Err(err) => {
1241 self.pending_error = Some(err);
1242 None
1243 }
1244 }
1245 }
1246
1247 fn structured_column_family_id_or_record_error(&mut self, column_family: &str) -> Option<u8> {
1248 match self.ensure_structured_column_family_id(column_family) {
1249 Ok(column_family_id) => Some(column_family_id),
1250 Err(err) => {
1251 self.pending_error = Some(err);
1252 None
1253 }
1254 }
1255 }
1256
1257 pub fn commit(&mut self) -> Result<StructuredSchema> {
1258 if let Some(err) = self.pending_error.take() {
1259 self.inner.take();
1262 return Err(err);
1263 }
1264 let inner = self
1265 .inner
1266 .take()
1267 .ok_or_else(|| Error::InvalidState("schema builder already committed".to_string()))?;
1268 inner.commit();
1269 Ok(self
1270 .owner
1271 .install_committed_structured_schema(self.schema.clone()))
1272 }
1273
1274 fn apply_inner<T, F>(&mut self, f: F) -> Option<T>
1275 where
1276 F: FnOnce(&mut SchemaBuilder) -> Result<T>,
1277 {
1278 if self.pending_error.is_some() {
1279 return None;
1280 }
1281 let Some(inner) = self.inner.as_mut() else {
1282 self.pending_error = Some(Error::InvalidState(
1283 "schema builder already committed".to_string(),
1284 ));
1285 return None;
1286 };
1287 match f(inner) {
1288 Ok(value) => Some(value),
1289 Err(err) => {
1290 self.pending_error = Some(err);
1291 None
1292 }
1293 }
1294 }
1295}
1296
1297fn normalize_structured_column_family_name(
1298 column_family: Option<String>,
1299) -> Result<(String, Option<String>)> {
1300 let normalized = match column_family {
1301 Some(column_family) => {
1302 let normalized = column_family.trim().to_string();
1303 if normalized.is_empty() {
1304 return Err(Error::InvalidState(
1305 "column family name cannot be empty".to_string(),
1306 ));
1307 }
1308 normalized
1309 }
1310 None => DEFAULT_COLUMN_FAMILY_NAME.to_string(),
1311 };
1312 let core_column_family = if normalized == DEFAULT_COLUMN_FAMILY_NAME {
1313 None
1314 } else {
1315 Some(normalized.clone())
1316 };
1317 Ok((normalized, core_column_family))
1318}
1319
1320impl StructuredDb {
1321 fn from_db(db: Db) -> Result<Self> {
1322 let structured_schema = Arc::new(load_structured_schema_from_cobble_schema(
1323 &db.current_schema(),
1324 )?);
1325 Ok(Self {
1326 db,
1327 structured_schema,
1328 default_write_options: StructuredWriteOptions::default(),
1329 default_read_options: StructuredReadOptions::default(),
1330 default_scan_options: StructuredScanOptions::default(),
1331 })
1332 }
1333
1334 fn reset_default_options(&mut self) {
1335 self.default_write_options = StructuredWriteOptions::default();
1336 self.default_read_options = StructuredReadOptions::default();
1337 self.default_scan_options = StructuredScanOptions::default();
1338 }
1339
1340 pub fn open(config: Config, bucket_ranges: Vec<RangeInclusive<u16>>) -> Result<Self> {
1341 Self::from_db(Db::open(config, bucket_ranges)?)
1342 }
1343
1344 pub fn open_from_snapshot(
1345 config: Config,
1346 snapshot_id: u64,
1347 db_id: impl Into<String>,
1348 ) -> Result<Self> {
1349 Self::open_from_snapshot_with_resolver(config, snapshot_id, db_id, None)
1350 }
1351
1352 pub fn open_from_snapshot_with_resolver(
1353 config: Config,
1354 snapshot_id: u64,
1355 db_id: impl Into<String>,
1356 resolver: Option<Arc<dyn MergeOperatorResolver>>,
1357 ) -> Result<Self> {
1358 Self::from_db(Db::open_from_snapshot_with_resolver(
1359 config,
1360 snapshot_id,
1361 db_id,
1362 Some(combined_resolver(resolver)),
1363 )?)
1364 }
1365
1366 pub fn open_from_snapshot_with_recovery_mode(
1368 config: Config,
1369 snapshot_id: u64,
1370 db_id: impl Into<String>,
1371 recovery_mode: RecoveryMode,
1372 ) -> Result<Self> {
1373 Self::from_db(Db::open_from_snapshot_with_recovery_mode_and_resolver(
1374 config,
1375 snapshot_id,
1376 db_id,
1377 recovery_mode,
1378 Some(combined_resolver(None)),
1379 )?)
1380 }
1381
1382 pub fn open_new_with_snapshot(
1383 config: Config,
1384 snapshot_id: u64,
1385 db_id: impl Into<String>,
1386 ) -> Result<Self> {
1387 Self::open_new_with_snapshot_with_resolver(config, snapshot_id, db_id, None)
1388 }
1389
1390 pub fn open_new_with_snapshot_with_resolver(
1391 config: Config,
1392 snapshot_id: u64,
1393 db_id: impl Into<String>,
1394 resolver: Option<Arc<dyn MergeOperatorResolver>>,
1395 ) -> Result<Self> {
1396 let db_id = db_id.into();
1397 Self::from_db(Db::open_new_with_snapshot_with_resolver(
1398 config,
1399 snapshot_id,
1400 &db_id,
1401 Some(combined_resolver(resolver)),
1402 )?)
1403 }
1404
1405 pub fn open_new_with_manifest_path(
1406 config: Config,
1407 manifest_path: impl Into<String>,
1408 ) -> Result<Self> {
1409 Self::open_new_with_manifest_path_with_resolver(config, manifest_path, None)
1410 }
1411
1412 pub fn open_new_with_manifest_path_with_resolver(
1413 config: Config,
1414 manifest_path: impl Into<String>,
1415 resolver: Option<Arc<dyn MergeOperatorResolver>>,
1416 ) -> Result<Self> {
1417 Self::from_db(Db::open_new_with_manifest_path_with_resolver(
1418 config,
1419 manifest_path,
1420 Some(combined_resolver(resolver)),
1421 )?)
1422 }
1423
1424 pub fn resume(config: Config, db_id: impl Into<String>) -> Result<Self> {
1425 Self::resume_with_resolver(config, db_id, None)
1426 }
1427
1428 pub fn resume_with_resolver(
1429 config: Config,
1430 db_id: impl Into<String>,
1431 resolver: Option<Arc<dyn MergeOperatorResolver>>,
1432 ) -> Result<Self> {
1433 Self::from_db(Db::resume_with_resolver(
1434 config,
1435 db_id,
1436 Some(combined_resolver(resolver)),
1437 )?)
1438 }
1439
1440 pub fn resume_with_recovery_mode(
1442 config: Config,
1443 db_id: impl Into<String>,
1444 recovery_mode: RecoveryMode,
1445 ) -> Result<Self> {
1446 Self::from_db(Db::resume_with_recovery_mode_and_resolver(
1447 config,
1448 db_id,
1449 recovery_mode,
1450 Some(combined_resolver(None)),
1451 )?)
1452 }
1453
1454 pub fn resume_from_snapshot(
1456 config: Config,
1457 snapshot_id: u64,
1458 db_id: impl Into<String>,
1459 ) -> Result<Self> {
1460 Self::resume_from_snapshot_with_resolver(config, snapshot_id, db_id, None)
1461 }
1462
1463 pub fn resume_from_snapshot_with_resolver(
1465 config: Config,
1466 snapshot_id: u64,
1467 db_id: impl Into<String>,
1468 resolver: Option<Arc<dyn MergeOperatorResolver>>,
1469 ) -> Result<Self> {
1470 Self::from_db(Db::resume_from_snapshot_with_resolver(
1471 config,
1472 snapshot_id,
1473 db_id,
1474 Some(combined_resolver(resolver)),
1475 )?)
1476 }
1477
1478 pub fn resume_from_snapshot_with_recovery_mode(
1480 config: Config,
1481 snapshot_id: u64,
1482 db_id: impl Into<String>,
1483 recovery_mode: RecoveryMode,
1484 ) -> Result<Self> {
1485 Self::from_db(Db::resume_from_snapshot_with_recovery_mode_and_resolver(
1486 config,
1487 snapshot_id,
1488 db_id,
1489 recovery_mode,
1490 Some(combined_resolver(None)),
1491 )?)
1492 }
1493
1494 pub fn expand_bucket(
1495 &self,
1496 source_db_id: impl Into<String>,
1497 snapshot_id: Option<u64>,
1498 ranges: Option<Vec<RangeInclusive<u16>>>,
1499 ) -> Result<u64> {
1500 self.db.expand_bucket(source_db_id, snapshot_id, ranges)
1501 }
1502
1503 pub fn expand_bucket_with_storage_mode(
1504 &self,
1505 source_db_id: impl Into<String>,
1506 snapshot_id: Option<u64>,
1507 ranges: Option<Vec<RangeInclusive<u16>>>,
1508 storage_mode: cobble::ExpandStorageMode,
1509 ) -> Result<u64> {
1510 self.db
1511 .expand_bucket_with_storage_mode(source_db_id, snapshot_id, ranges, storage_mode)
1512 }
1513
1514 pub fn wait_for_expand_adoption(&self, timeout: std::time::Duration) -> Result<()> {
1515 self.db.wait_for_expand_adoption(timeout)
1516 }
1517
1518 pub fn shrink_bucket(&self, ranges: Vec<RangeInclusive<u16>>) -> Result<u64> {
1519 self.db.shrink_bucket(ranges)
1520 }
1521
1522 pub fn id(&self) -> &str {
1523 self.db.id()
1524 }
1525
1526 pub fn metrics(&self) -> Vec<cobble::MetricSample> {
1528 self.db.metrics()
1529 }
1530
1531 pub fn current_schema(&self) -> StructuredSchema {
1532 self.structured_schema.as_ref().clone()
1533 }
1534
1535 pub fn register_schema_transform<F, T>(
1537 &self,
1538 transform_type: impl Into<String>,
1539 factory: F,
1540 ) -> Result<()>
1541 where
1542 F: Fn(&[u8]) -> Result<T> + Send + Sync + 'static,
1543 T: Fn(Option<Bytes>) -> Result<Option<Bytes>> + Send + Sync + 'static,
1544 {
1545 self.db.register_schema_transform(transform_type, factory)
1546 }
1547
1548 pub fn update_schema(&mut self) -> StructuredSchemaBuilder<'_, Self> {
1549 StructuredSchemaBuilder::new(self)
1550 }
1551
1552 pub fn new_priority_queue<'a>(
1553 &'a mut self,
1554 name: impl Into<String>,
1555 ) -> Result<PriorityQueue<'a>> {
1556 let normalized_name = priority_queue_column_family_name(name.into())?;
1557 if self
1558 .db
1559 .current_schema()
1560 .column_family_ids()
1561 .contains_key(normalized_name.as_str())
1562 {
1563 return Err(Error::InvalidState(format!(
1564 "priority queue '{}' already exists",
1565 normalized_name
1566 )));
1567 }
1568
1569 let mut builder = self.update_schema();
1570 builder.add_bytes_column(Some(normalized_name.clone()), 0);
1571 builder.set_column_family_options(
1572 Some(normalized_name.clone()),
1573 priority_queue_column_family_options(),
1574 );
1575 builder.commit()?;
1576 let column_family_id = self
1577 .current_schema()
1578 .resolve_column_family_id(Some(normalized_name.as_str()))?;
1579
1580 Ok(PriorityQueue::from_column_family(
1581 self,
1582 normalized_name,
1583 column_family_id,
1584 ))
1585 }
1586
1587 pub fn get_priority_queue<'a>(&'a self, name: impl Into<String>) -> Result<PriorityQueue<'a>> {
1588 let normalized_name = priority_queue_column_family_name(name.into())?;
1589 let column_family_id = validate_priority_queue_column_family(
1590 self.db.current_schema().as_ref(),
1591 normalized_name.as_str(),
1592 )?;
1593 Ok(PriorityQueue::from_column_family(
1594 self,
1595 normalized_name,
1596 column_family_id,
1597 ))
1598 }
1599
1600 pub fn get_or_new_priority_queue<'a>(
1601 &'a mut self,
1602 name: impl Into<String>,
1603 ) -> Result<PriorityQueue<'a>> {
1604 let normalized_name = priority_queue_column_family_name(name.into())?;
1605 if self
1606 .db
1607 .current_schema()
1608 .column_family_ids()
1609 .contains_key(normalized_name.as_str())
1610 {
1611 let column_family_id = validate_priority_queue_column_family(
1612 self.db.current_schema().as_ref(),
1613 normalized_name.as_str(),
1614 )?;
1615 return Ok(PriorityQueue::from_column_family(
1616 self,
1617 normalized_name,
1618 column_family_id,
1619 ));
1620 } else {
1621 let mut builder = self.update_schema();
1622 builder.add_bytes_column(Some(normalized_name.clone()), 0);
1623 builder.set_column_family_options(
1624 Some(normalized_name.clone()),
1625 priority_queue_column_family_options(),
1626 );
1627 builder.commit()?;
1628 }
1629 let column_family_id = self
1630 .current_schema()
1631 .resolve_column_family_id(Some(normalized_name.as_str()))?;
1632
1633 Ok(PriorityQueue::from_column_family(
1634 self,
1635 normalized_name,
1636 column_family_id,
1637 ))
1638 }
1639
1640 pub fn reload_schema(&mut self) -> Result<()> {
1641 let schema = load_structured_schema_from_cobble_schema(&self.db.current_schema())?;
1642 self.structured_schema = Arc::new(schema);
1643 self.reset_default_options();
1644 Ok(())
1645 }
1646
1647 pub fn apply_schema(
1648 &mut self,
1649 structured_schema: StructuredSchema,
1650 ) -> Result<StructuredSchema> {
1651 persist_structured_schema_on_db(&self.db, &structured_schema)?;
1652 let reloaded = load_structured_schema_from_cobble_schema(&self.db.current_schema())?;
1653 self.structured_schema = Arc::new(reloaded.clone());
1654 self.reset_default_options();
1655 Ok(reloaded)
1656 }
1657
1658 pub fn put<K, V>(&self, bucket: u16, key: K, column: u16, value: V) -> Result<()>
1659 where
1660 K: AsRef<[u8]>,
1661 V: Into<StructuredColumnValue>,
1662 {
1663 self.put_with_options(bucket, key, column, value, &self.default_write_options)
1664 }
1665
1666 pub fn put_with_options<K, V>(
1667 &self,
1668 bucket: u16,
1669 key: K,
1670 column: u16,
1671 value: V,
1672 options: &StructuredWriteOptions,
1673 ) -> Result<()>
1674 where
1675 K: AsRef<[u8]>,
1676 V: Into<StructuredColumnValue>,
1677 {
1678 let encoded = encode_for_write(
1679 &self.structured_schema,
1680 options.column_family(),
1681 self.db.now_seconds(),
1682 column,
1683 value.into(),
1684 options.ttl_seconds(),
1685 )?;
1686 self.db
1687 .put_with_options(bucket, key, column, encoded, options.as_cobble())
1688 }
1689
1690 #[cfg(feature = "ffi")]
1691 pub(crate) fn put_borrowed_bytes_with_options<K>(
1692 &self,
1693 bucket: u16,
1694 key: K,
1695 column: u16,
1696 value: &[u8],
1697 options: &StructuredWriteOptions,
1698 ) -> Result<()>
1699 where
1700 K: AsRef<[u8]>,
1701 {
1702 ensure_bytes_column(&self.structured_schema, options.column_family(), column)?;
1703 self.db
1704 .put_with_options(bucket, key, column, value, options.as_cobble())
1705 }
1706
1707 #[cfg(feature = "ffi")]
1709 pub(crate) fn put_bytes_batch_with_options<'a, I>(
1710 &self,
1711 bucket: u16,
1712 column: u16,
1713 entries: I,
1714 options: &StructuredWriteOptions,
1715 ) -> Result<()>
1716 where
1717 I: IntoIterator<Item = (&'a [u8], &'a [u8])>,
1718 {
1719 let column_family_id = self
1720 .structured_schema
1721 .resolve_column_family_id(options.column_family())?;
1722 if !matches!(
1723 self.structured_schema
1724 .column_families
1725 .get(&column_family_id)
1726 .map(|family| family.structured_column_type(column))
1727 .unwrap_or(&StructuredColumnType::Bytes),
1728 StructuredColumnType::Bytes
1729 ) {
1730 return Err(Error::InputError(format!(
1731 "column {} expects a different type of value",
1732 column,
1733 )));
1734 }
1735 self.db
1736 .put_column_batch_with_options(bucket, column, entries, options.as_cobble())
1737 }
1738
1739 #[cfg(feature = "ffi")]
1740 pub(crate) fn put_encoded_list<K, B>(
1741 &self,
1742 bucket: u16,
1743 key: K,
1744 column: u16,
1745 encoded: B,
1746 ) -> Result<()>
1747 where
1748 K: AsRef<[u8]>,
1749 B: Into<Bytes>,
1750 {
1751 self.put_encoded_list_with_options(
1752 bucket,
1753 key,
1754 column,
1755 encoded,
1756 &self.default_write_options,
1757 )
1758 }
1759
1760 #[cfg(feature = "ffi")]
1761 pub(crate) fn put_encoded_list_with_options<K, B>(
1762 &self,
1763 bucket: u16,
1764 key: K,
1765 column: u16,
1766 encoded: B,
1767 options: &StructuredWriteOptions,
1768 ) -> Result<()>
1769 where
1770 K: AsRef<[u8]>,
1771 B: Into<Bytes>,
1772 {
1773 ensure_list_column(&self.structured_schema, options.column_family(), column)?;
1774 self.db
1775 .put_with_options(bucket, key, column, encoded.into(), options.as_cobble())
1776 }
1777
1778 #[cfg(feature = "ffi")]
1779 pub(crate) fn put_borrowed_list_with_options<K>(
1780 &self,
1781 bucket: u16,
1782 key: K,
1783 column: u16,
1784 elements: &[&[u8]],
1785 options: &StructuredWriteOptions,
1786 ) -> Result<()>
1787 where
1788 K: AsRef<[u8]>,
1789 {
1790 let config = list_column_config(&self.structured_schema, options.column_family(), column)?;
1791 let encoded = encode_borrowed_list_for_write(
1792 elements,
1793 &config,
1794 options.ttl_seconds(),
1795 self.db.now_seconds(),
1796 )?;
1797 self.db
1798 .put_with_options(bucket, key, column, encoded, options.as_cobble())
1799 }
1800
1801 pub fn merge<K, V>(&self, bucket: u16, key: K, column: u16, value: V) -> Result<()>
1802 where
1803 K: AsRef<[u8]>,
1804 V: Into<StructuredColumnValue>,
1805 {
1806 self.merge_with_options(bucket, key, column, value, &self.default_write_options)
1807 }
1808
1809 pub fn merge_with_options<K, V>(
1810 &self,
1811 bucket: u16,
1812 key: K,
1813 column: u16,
1814 value: V,
1815 options: &StructuredWriteOptions,
1816 ) -> Result<()>
1817 where
1818 K: AsRef<[u8]>,
1819 V: Into<StructuredColumnValue>,
1820 {
1821 let encoded = encode_for_write(
1822 &self.structured_schema,
1823 options.column_family(),
1824 self.db.now_seconds(),
1825 column,
1826 value.into(),
1827 options.ttl_seconds(),
1828 )?;
1829 self.db
1830 .merge_with_options(bucket, key, column, encoded, options.as_cobble())
1831 }
1832
1833 pub(crate) fn merge_borrowed_bytes_with_options<K>(
1834 &self,
1835 bucket: u16,
1836 key: K,
1837 column: u16,
1838 value: &[u8],
1839 options: &StructuredWriteOptions,
1840 ) -> Result<()>
1841 where
1842 K: AsRef<[u8]>,
1843 {
1844 ensure_bytes_column(&self.structured_schema, options.column_family(), column)?;
1845 self.db
1846 .merge_with_options(bucket, key, column, value, options.as_cobble())
1847 }
1848
1849 #[cfg(feature = "ffi")]
1850 pub(crate) fn merge_encoded_list<K, B>(
1851 &self,
1852 bucket: u16,
1853 key: K,
1854 column: u16,
1855 encoded: B,
1856 ) -> Result<()>
1857 where
1858 K: AsRef<[u8]>,
1859 B: Into<Bytes>,
1860 {
1861 self.merge_encoded_list_with_options(
1862 bucket,
1863 key,
1864 column,
1865 encoded,
1866 &self.default_write_options,
1867 )
1868 }
1869
1870 #[cfg(feature = "ffi")]
1871 pub(crate) fn merge_encoded_list_with_options<K, B>(
1872 &self,
1873 bucket: u16,
1874 key: K,
1875 column: u16,
1876 encoded: B,
1877 options: &StructuredWriteOptions,
1878 ) -> Result<()>
1879 where
1880 K: AsRef<[u8]>,
1881 B: Into<Bytes>,
1882 {
1883 ensure_list_column(&self.structured_schema, options.column_family(), column)?;
1884 self.db
1885 .merge_with_options(bucket, key, column, encoded.into(), options.as_cobble())
1886 }
1887
1888 #[cfg(feature = "ffi")]
1889 pub(crate) fn merge_borrowed_list_with_options<K>(
1890 &self,
1891 bucket: u16,
1892 key: K,
1893 column: u16,
1894 elements: &[&[u8]],
1895 options: &StructuredWriteOptions,
1896 ) -> Result<()>
1897 where
1898 K: AsRef<[u8]>,
1899 {
1900 let config = list_column_config(&self.structured_schema, options.column_family(), column)?;
1901 let encoded = encode_borrowed_list_for_write(
1902 elements,
1903 &config,
1904 options.ttl_seconds(),
1905 self.db.now_seconds(),
1906 )?;
1907 self.db
1908 .merge_with_options(bucket, key, column, encoded, options.as_cobble())
1909 }
1910
1911 pub fn delete<K>(&self, bucket: u16, key: K, column: u16) -> Result<()>
1912 where
1913 K: AsRef<[u8]>,
1914 {
1915 self.delete_with_options(bucket, key, column, &self.default_write_options)
1916 }
1917
1918 pub fn delete_with_options<K>(
1919 &self,
1920 bucket: u16,
1921 key: K,
1922 column: u16,
1923 options: &StructuredWriteOptions,
1924 ) -> Result<()>
1925 where
1926 K: AsRef<[u8]>,
1927 {
1928 self.db
1929 .delete_with_options(bucket, key, column, options.as_cobble())
1930 }
1931
1932 pub fn new_write_batch(&self) -> StructuredWriteBatch {
1933 StructuredWriteBatch::new(Arc::clone(&self.structured_schema), self.db.now_seconds())
1934 }
1935
1936 pub fn write_batch(&self, batch: StructuredWriteBatch) -> Result<()> {
1937 self.db.write_batch(batch.into_inner())
1938 }
1939
1940 pub fn write_batch_with_options(
1941 &self,
1942 batch: StructuredWriteBatch,
1943 options: &StructuredWriteOptions,
1944 ) -> Result<()> {
1945 self.db
1946 .write_batch_with_options(batch.into_inner(), options.as_cobble())
1947 }
1948
1949 pub fn get<K>(&self, bucket: u16, key: K) -> Result<Option<Vec<Option<StructuredColumnValue>>>>
1950 where
1951 K: AsRef<[u8]>,
1952 {
1953 self.get_with_options(bucket, key, &self.default_read_options)
1954 }
1955
1956 pub fn multi_get<K>(
1957 &self,
1958 keys: &[(u16, K)],
1959 ) -> Result<Vec<Option<Vec<Option<StructuredColumnValue>>>>>
1960 where
1961 K: AsRef<[u8]>,
1962 {
1963 self.multi_get_with_options(keys, &self.default_read_options)
1964 }
1965
1966 pub fn multi_get_with_options<K>(
1967 &self,
1968 keys: &[(u16, K)],
1969 options: &StructuredReadOptions,
1970 ) -> Result<Vec<Option<Vec<Option<StructuredColumnValue>>>>>
1971 where
1972 K: AsRef<[u8]>,
1973 {
1974 let raw_keys = keys
1975 .iter()
1976 .map(|(bucket, key)| (*bucket, key.as_ref()))
1977 .collect::<Vec<_>>();
1978 let projected_schema = options.resolve_projected_schema_cached(&self.structured_schema)?;
1979 self.db
1980 .multi_get_with_options(&raw_keys, options.as_cobble())?
1981 .into_iter()
1982 .map(|raw| {
1983 raw.map(|columns| decode_row(&projected_schema, 0, columns))
1984 .transpose()
1985 })
1986 .collect()
1987 }
1988
1989 pub fn get_with_options<K>(
1990 &self,
1991 bucket: u16,
1992 key: K,
1993 options: &StructuredReadOptions,
1994 ) -> Result<Option<Vec<Option<StructuredColumnValue>>>>
1995 where
1996 K: AsRef<[u8]>,
1997 {
1998 let raw = self
1999 .db
2000 .get_with_options(bucket, key.as_ref(), options.as_cobble())?;
2001 let projected_schema = options.resolve_projected_schema_cached(&self.structured_schema)?;
2002 raw.map(|columns| decode_row(&projected_schema, 0, columns))
2003 .transpose()
2004 }
2005
2006 pub fn scan(&self, bucket: u16, range: Range<&[u8]>) -> Result<StructuredDbIterator> {
2007 self.scan_with_options(bucket, range, &self.default_scan_options)
2008 }
2009
2010 pub fn scan_with_options(
2011 &self,
2012 bucket: u16,
2013 range: Range<&[u8]>,
2014 options: &StructuredScanOptions,
2015 ) -> Result<StructuredDbIterator> {
2016 let inner = self
2017 .db
2018 .scan_with_options(bucket, range, options.as_cobble())?;
2019 let projected_schema = options.resolve_projected_schema_cached(&self.structured_schema)?;
2020 Ok(StructuredDbIterator::new(inner, projected_schema, 0))
2021 }
2022
2023 pub fn scan_bounds(
2024 &self,
2025 bucket: u16,
2026 start_key_inclusive: Option<&[u8]>,
2027 end_key_exclusive: Option<&[u8]>,
2028 ) -> Result<StructuredDbIterator> {
2029 self.scan_with_options_bounds(
2030 bucket,
2031 start_key_inclusive,
2032 end_key_exclusive,
2033 &self.default_scan_options,
2034 )
2035 }
2036
2037 pub fn scan_with_options_bounds(
2038 &self,
2039 bucket: u16,
2040 start_key_inclusive: Option<&[u8]>,
2041 end_key_exclusive: Option<&[u8]>,
2042 options: &StructuredScanOptions,
2043 ) -> Result<StructuredDbIterator> {
2044 let inner = self.db.scan_with_options_bounds(
2045 bucket,
2046 start_key_inclusive,
2047 end_key_exclusive,
2048 options.as_cobble(),
2049 )?;
2050 let projected_schema = options.resolve_projected_schema_cached(&self.structured_schema)?;
2051 Ok(StructuredDbIterator::new(inner, projected_schema, 0))
2052 }
2053
2054 pub fn snapshot(&self) -> Result<u64> {
2055 self.db.snapshot()
2056 }
2057
2058 pub fn switch_to_snapshot(&mut self, snapshot_id: u64) -> Result<()> {
2060 self.db
2061 .switch_to_snapshot_with_resolver(snapshot_id, Some(combined_resolver(None)))?;
2062 self.reload_schema()
2063 }
2064
2065 pub fn switch_memtable_type(
2070 &self,
2071 memtable_type: MemtableType,
2072 flush_current: bool,
2073 ) -> Result<()> {
2074 self.db.switch_memtable_type(memtable_type, flush_current)
2075 }
2076
2077 pub fn load_readonly_files_to_primary(&self) -> Result<usize> {
2080 self.db.load_readonly_files_to_primary()
2081 }
2082
2083 pub fn snapshot_with_callback<F>(&self, callback: F) -> Result<u64>
2084 where
2085 F: Fn(Result<ShardSnapshotMetadata>) + Send + Sync + 'static,
2086 {
2087 self.db.snapshot_with_callback(callback)
2088 }
2089
2090 pub fn cancel_snapshot(&self, snapshot_id: u64) -> Result<bool> {
2091 self.db.cancel_snapshot(snapshot_id)
2092 }
2093
2094 pub fn expire_snapshot(&self, snapshot_id: u64) -> Result<bool> {
2095 self.db.expire_snapshot(snapshot_id)
2096 }
2097
2098 pub fn retain_snapshot(&self, snapshot_id: u64) -> bool {
2099 self.db.retain_snapshot(snapshot_id)
2100 }
2101
2102 pub fn shard_snapshot_metadata(&self, snapshot_id: u64) -> Result<ShardSnapshotMetadata> {
2103 self.db.shard_snapshot_metadata(snapshot_id)
2104 }
2105
2106 pub fn set_time(&self, next: u32) {
2107 self.db.set_time(next);
2108 }
2109
2110 pub fn now_seconds(&self) -> u32 {
2111 self.db.now_seconds()
2112 }
2113
2114 #[cfg(test)]
2115 pub(crate) fn get_raw_with_options(
2116 &self,
2117 bucket: u16,
2118 key: &[u8],
2119 options: &StructuredReadOptions,
2120 ) -> Result<Option<Vec<Option<Bytes>>>> {
2121 self.db.get_with_options(bucket, key, options.as_cobble())
2122 }
2123
2124 pub(crate) fn scan_raw_bounds(
2125 &self,
2126 bucket: u16,
2127 start_key_inclusive: Option<&[u8]>,
2128 end_key_exclusive: Option<&[u8]>,
2129 options: &StructuredScanOptions,
2130 ) -> Result<DbIterator> {
2131 self.db.scan_with_options_bounds(
2132 bucket,
2133 start_key_inclusive,
2134 end_key_exclusive,
2135 options.as_cobble(),
2136 )
2137 }
2138
2139 pub(crate) fn advance_column_family_truncation_cursor_by_id(
2140 &self,
2141 bucket: u16,
2142 column_family_id: u8,
2143 key: &[u8],
2144 ) -> Result<()> {
2145 self.db
2146 .advance_truncation_cursor_by_id(bucket, column_family_id, key)
2147 }
2148
2149 pub(crate) fn column_family_truncation_cursor_by_id(
2150 &self,
2151 bucket: u16,
2152 column_family_id: u8,
2153 ) -> Result<Option<Vec<u8>>> {
2154 self.db.truncation_cursor_by_id(bucket, column_family_id)
2155 }
2156
2157 pub fn close(&self) -> Result<()> {
2158 self.db.close()
2159 }
2160
2161 #[cfg(feature = "ffi")]
2162 pub(crate) fn jni_direct_buffer_pool_config(&self) -> Result<(usize, usize)> {
2163 cobble::ffi::db_direct_buffer_pool_config(&self.db)
2164 }
2165}
2166
2167impl StructuredSchemaOwner for StructuredDb {
2168 fn current_structured_schema(&self) -> StructuredSchema {
2169 self.current_schema()
2170 }
2171
2172 fn begin_core_schema_update(&self) -> SchemaBuilder {
2173 self.db.update_schema()
2174 }
2175
2176 fn install_committed_structured_schema(
2177 &mut self,
2178 schema: StructuredSchema,
2179 ) -> StructuredSchema {
2180 self.structured_schema = Arc::new(schema.clone());
2181 self.reset_default_options();
2182 schema
2183 }
2184}
2185
2186pub type DataStructureDb = StructuredDb;
2188
2189pub struct StructuredDbIterator {
2192 inner: DbIterator,
2193 structured_schema: Arc<StructuredColumnFamilySchema>,
2194 now_seconds: u32,
2195}
2196
2197impl StructuredDbIterator {
2198 pub(crate) fn new(
2199 inner: DbIterator,
2200 structured_schema: Arc<StructuredColumnFamilySchema>,
2201 now_seconds: u32,
2202 ) -> Self {
2203 Self {
2204 inner,
2205 structured_schema,
2206 now_seconds,
2207 }
2208 }
2209
2210 pub fn consume_next_row<T, F>(&mut self, mut consumer: F) -> Result<Option<T>>
2211 where
2212 F: FnMut(&Bytes, &[Option<StructuredColumnValue>]) -> Result<T>,
2213 {
2214 let structured_schema = Arc::clone(&self.structured_schema);
2215 let now_seconds = self.now_seconds;
2216 self.inner.consume_next_row(|key, columns| {
2217 let decoded = decode_row(&structured_schema, now_seconds, columns.to_vec())?;
2218 consumer(key, &decoded)
2219 })
2220 }
2221
2222 pub fn consume_next_row_with_bucket<T, F>(&mut self, mut consumer: F) -> Result<Option<T>>
2223 where
2224 F: FnMut(u16, &Bytes, &[Option<StructuredColumnValue>]) -> Result<T>,
2225 {
2226 let structured_schema = Arc::clone(&self.structured_schema);
2227 let now_seconds = self.now_seconds;
2228 self.inner
2229 .consume_next_row_with_bucket(|bucket, key, columns| {
2230 let decoded = decode_row(&structured_schema, now_seconds, columns.to_vec())?;
2231 consumer(bucket, key, &decoded)
2232 })
2233 }
2234
2235 #[cfg(feature = "ffi")]
2236 pub(crate) fn stopped_at_block_boundary(&self) -> bool {
2237 self.inner.stopped_at_block_boundary()
2238 }
2239
2240 #[cfg(feature = "ffi")]
2241 pub(crate) fn clear_stop_at_block_boundary(&mut self) {
2242 self.inner.clear_stop_at_block_boundary();
2243 }
2244}
2245
2246impl Iterator for StructuredDbIterator {
2247 type Item = Result<(Bytes, Vec<Option<StructuredColumnValue>>)>;
2248
2249 fn next(&mut self) -> Option<Self::Item> {
2250 self.inner.next().map(|item| {
2251 let (key, columns) = item?;
2252 let decoded = decode_row(&self.structured_schema, self.now_seconds, columns)?;
2253 Ok((key, decoded))
2254 })
2255 }
2256}
2257
2258pub(crate) fn persist_structured_schema_on_db(
2261 db: &Db,
2262 structured_schema: &StructuredSchema,
2263) -> Result<()> {
2264 let mut schema = db.update_schema();
2265 apply_structured_schema(&mut schema, structured_schema)?;
2266 schema.commit();
2267 Ok(())
2268}
2269
2270fn apply_structured_schema(
2271 schema: &mut SchemaBuilder,
2272 structured_schema: &StructuredSchema,
2273) -> Result<()> {
2274 let column_family_names_by_id = structured_schema
2275 .column_family_ids
2276 .iter()
2277 .map(|(name, &id)| (id, name.clone()))
2278 .collect::<BTreeMap<_, _>>();
2279 for (column_family_id, family_schema) in &structured_schema.column_families {
2280 let column_family = if *column_family_id == DEFAULT_COLUMN_FAMILY_ID {
2281 None
2282 } else {
2283 Some(
2284 column_family_names_by_id
2285 .get(column_family_id)
2286 .cloned()
2287 .ok_or_else(|| {
2288 Error::InvalidState(format!(
2289 "unknown structured column family id {}",
2290 column_family_id
2291 ))
2292 })?,
2293 )
2294 };
2295 apply_structured_family(schema, column_family, &family_schema.columns)?;
2296 }
2297 Ok(())
2298}
2299
2300fn apply_structured_family(
2301 schema: &mut SchemaBuilder,
2302 column_family: Option<String>,
2303 columns: &BTreeMap<u16, StructuredColumnType>,
2304) -> Result<()> {
2305 if let Some(column_family) = column_family.as_ref()
2306 && !columns.is_empty()
2307 {
2308 schema.ensure_column_family_exists(column_family.clone())?;
2309 }
2310 for (column, column_type) in columns {
2311 match column_type {
2312 StructuredColumnType::Bytes => {
2313 schema.set_column_operator(
2314 column_family.clone(),
2315 *column as usize,
2316 Arc::new(BytesMergeOperator),
2317 )?;
2318 schema.clear_column_metadata(column_family.clone(), *column as usize)?;
2319 }
2320 StructuredColumnType::List(config) => {
2321 schema.set_column_operator(
2322 column_family.clone(),
2323 *column as usize,
2324 list_operator(config.clone()),
2325 )?;
2326 schema.set_column_metadata(
2327 column_family.clone(),
2328 *column as usize,
2329 serde_json::to_value(config).map_err(|err| {
2330 Error::FileFormatError(format!(
2331 "failed to encode list config metadata: {}",
2332 err
2333 ))
2334 })?,
2335 )?;
2336 }
2337 }
2338 }
2339 Ok(())
2340}
2341
2342fn apply_structured_column_type(
2343 schema: &mut SchemaBuilder,
2344 column_family: Option<String>,
2345 column: usize,
2346 column_type: &StructuredColumnType,
2347) -> Result<()> {
2348 let operator = match column_type {
2349 StructuredColumnType::Bytes => Arc::new(BytesMergeOperator) as Arc<_>,
2350 StructuredColumnType::List(config) => list_operator(config.clone()),
2351 };
2352 schema.replace_column(column_family, column, operator)
2353}
2354
2355#[cfg(test)]
2356#[path = "../tests/unit/structured_db.rs"]
2357mod tests;