Skip to main content

rudb_common/
session.rs

1//! What a session has set, for the tables and functions that read a setting back.
2//!
3//! Here for the layer rule and not because a setting is a kind of value, which is the same reason
4//! [`crate::Cancel`] is here. The thing that fills this in is the embedding API at rank 13, which is
5//! the only place that knows what `SET memory_limit` left behind, and the thing that reads it is the
6//! executor at rank 12, where `duckdb_settings()` is built. No two crates in between can see each
7//! other, so the only place both of them can see is the bottom.
8//!
9//! Strings on both sides, rather than a value per setting. A setting is written as text by `SET`,
10//! read back as text by `current_setting()` and printed as text by `duckdb_settings()`, and the one
11//! place the type matters is the `input_type` column, which is a fact about the setting rather than
12//! about the session. Holding a `Value` here would mean the rendering happened twice, once for each
13//! reader, and the two would eventually disagree about how many decimal places a memory limit has.
14
15use std::collections::BTreeMap;
16
17/// The settings a session has, by name.
18///
19/// Every setting the engine has, not only the ones somebody changed. A reader of this is answering
20/// "what is it now", so a name that is missing means the engine does not have that setting rather
21/// than that it is at its default.
22#[derive(Debug, Clone, Default, PartialEq, Eq)]
23pub struct Session {
24    values: BTreeMap<String, String>,
25}
26
27impl Session {
28    /// A session that knows nothing, which is what a caller with no database behind it has.
29    #[must_use]
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    /// Records what one setting is now.
35    pub fn set(&mut self, name: &str, value: impl Into<String>) {
36        self.values.insert(name.to_string(), value.into());
37    }
38
39    /// What that setting is now, and `None` for a name this session has no answer for.
40    #[must_use]
41    pub fn get(&self, name: &str) -> Option<&str> {
42        self.values.get(name).map(String::as_str)
43    }
44
45    /// Every setting and its value, in name order.
46    pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
47        self.values.iter().map(|(name, value)| (name.as_str(), value.as_str()))
48    }
49
50    /// Whether nothing has been recorded.
51    #[must_use]
52    pub fn is_empty(&self) -> bool {
53        self.values.is_empty()
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::Session;
60
61    #[test]
62    fn a_session_hands_back_what_was_put_in_and_says_nothing_about_a_name_it_has_not_got() {
63        let mut session = Session::new();
64        assert!(session.is_empty());
65        session.set("threads", "8");
66        session.set("memory_limit", "1.0 GiB");
67        assert_eq!(session.get("threads"), Some("8"));
68        assert_eq!(session.get("nothing_called_this"), None);
69        // Name order, because the one reader of this is a catalog table that comes out sorted and
70        // sorting it twice would be sorting it once too many.
71        let pairs: Vec<(&str, &str)> = session.iter().collect();
72        assert_eq!(pairs, [("memory_limit", "1.0 GiB"), ("threads", "8")]);
73    }
74}