Skip to main content

lance_table/transaction/
update_map.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4//! Incremental edits to the string maps a manifest carries.
5//!
6//! Dataset config, table metadata, schema metadata and per-field metadata are all
7//! `HashMap<String, String>`, and all four are updated the same way: a list of
8//! entries where a `None` value means delete the key, plus a flag choosing between
9//! merging into the existing map and replacing it outright.
10
11use lance_core::deepsize::DeepSizeOf;
12
13/// An entry for a map update. If value is None, the key will be removed from the map.
14#[derive(Debug, Clone, DeepSizeOf, PartialEq)]
15pub struct UpdateMapEntry {
16    /// The key of the map entry to update.
17    pub key: String,
18    /// The value to set for the key.
19    pub value: Option<String>,
20}
21
22impl From<(String, Option<String>)> for UpdateMapEntry {
23    fn from((key, value): (String, Option<String>)) -> Self {
24        Self { key, value }
25    }
26}
27
28impl From<(String, String)> for UpdateMapEntry {
29    fn from((key, value): (String, String)) -> Self {
30        Self::from((key, Some(value)))
31    }
32}
33
34impl From<(&str, Option<&str>)> for UpdateMapEntry {
35    fn from((key, value): (&str, Option<&str>)) -> Self {
36        Self {
37            key: key.to_string(),
38            value: value.map(str::to_owned),
39        }
40    }
41}
42
43impl From<(&str, &str)> for UpdateMapEntry {
44    fn from((key, value): (&str, &str)) -> Self {
45        Self::from((key, Some(value)))
46    }
47}
48
49/// Represents updates to a map (either incremental or replacement)
50#[derive(Debug, Clone, DeepSizeOf, PartialEq)]
51pub struct UpdateMap {
52    pub update_entries: Vec<UpdateMapEntry>,
53    /// If true, the map will be replaced entirely with the new entries.
54    /// If false, the new entries will be merged with the existing map.
55    pub replace: bool,
56}
57
58/// Helper function to apply UpdateMap changes to a HashMap<String, String>
59pub(super) fn apply_update_map(
60    target: &mut std::collections::HashMap<String, String>,
61    update_map: &UpdateMap,
62) {
63    if update_map.replace {
64        // Full replacement - clear existing and replace with new entries that have values
65        target.clear();
66        for entry in &update_map.update_entries {
67            if let Some(value) = &entry.value {
68                target.insert(entry.key.clone(), value.clone());
69            }
70        }
71    } else {
72        // Incremental update - merge entries
73        for entry in &update_map.update_entries {
74            if let Some(value) = &entry.value {
75                target.insert(entry.key.clone(), value.clone());
76            } else {
77                target.remove(&entry.key);
78            }
79        }
80    }
81}
82
83/// Helper function to translate old-style config updates to new UpdateMap format
84pub fn translate_config_updates(
85    upsert_values: &std::collections::HashMap<String, String>,
86    delete_keys: &[String],
87) -> UpdateMap {
88    let mut update_entries = Vec::new();
89
90    // Add upsert entries (with values)
91    for (key, value) in upsert_values {
92        update_entries.push(UpdateMapEntry {
93            key: key.clone(),
94            value: Some(value.clone()),
95        });
96    }
97
98    // Add delete entries (without values)
99    for key in delete_keys {
100        update_entries.push(UpdateMapEntry {
101            key: key.clone(),
102            value: None,
103        });
104    }
105
106    UpdateMap {
107        update_entries,
108        replace: false, // Old style was always incremental
109    }
110}
111
112/// Helper function to translate old-style schema metadata to new UpdateMap format
113pub fn translate_schema_metadata_updates(
114    schema_metadata: &std::collections::HashMap<String, String>,
115) -> UpdateMap {
116    let update_entries = schema_metadata
117        .iter()
118        .map(|(key, value)| UpdateMapEntry {
119            key: key.clone(),
120            value: Some(value.clone()),
121        })
122        .collect();
123
124    UpdateMap {
125        update_entries,
126        replace: true, // Old style schema metadata was full replacement
127    }
128}