Skip to main content

uqa_storage_sqlite/catalog/
analyzers.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Named and per-field analyzer configuration.
8
9use super::{params, Catalog, Result};
10
11impl Catalog {
12    // -- Named analyzers ---------------------------------------------------
13
14    /// Persist a named analyzer configuration.
15    pub fn save_analyzer(&self, name: &str, config_json: &str) -> Result<()> {
16        self.conn.with(|c| {
17            c.execute(
18                "INSERT OR REPLACE INTO _analyzers (name, config_json) VALUES (?1, ?2)",
19                params![name, config_json],
20            )?;
21            Ok(())
22        })
23    }
24
25    pub fn drop_analyzer(&self, name: &str) -> Result<()> {
26        self.conn.with(|c| {
27            c.execute("DELETE FROM _analyzers WHERE name = ?1", params![name])?;
28            Ok(())
29        })
30    }
31
32    pub fn load_analyzers(&self) -> Result<Vec<(String, String)>> {
33        self.conn.with(|c| {
34            let mut stmt = c.prepare("SELECT name, config_json FROM _analyzers ORDER BY name")?;
35            let rows =
36                stmt.query_map([], |r| Ok((r.get::<_, String>(0)?, r.get::<_, String>(1)?)))?;
37            let mut out = Vec::new();
38            for row in rows {
39                out.push(row?);
40            }
41            Ok(out)
42        })
43    }
44
45    // -- Per-field analyzer overrides --------------------------------------
46
47    /// Persist a `(table, field, phase) -> analyzer_name` mapping.
48    pub fn save_table_field_analyzer(
49        &self,
50        table_name: &str,
51        field: &str,
52        phase: &str,
53        analyzer_name: &str,
54    ) -> Result<()> {
55        self.conn.with_mut(|connection| {
56            let tx = connection.savepoint()?;
57            tx.execute(
58                "UPDATE _table_field_analyzers SET binding_json = NULL WHERE table_name = ?1 AND field = ?2",
59                params![table_name, field],
60            )?;
61            tx.execute(
62                "INSERT OR REPLACE INTO _table_field_analyzers \
63                    (table_name, field, phase, analyzer_name) \
64                 VALUES (?1, ?2, ?3, ?4)",
65                params![table_name, field, phase, analyzer_name],
66            )?;
67            tx.commit()?;
68            Ok(())
69        })
70    }
71
72    pub fn drop_table_field_analyzers(&self, table_name: &str) -> Result<()> {
73        self.conn.with(|c| {
74            c.execute(
75                "DELETE FROM _table_field_analyzers WHERE table_name = ?1",
76                params![table_name],
77            )?;
78            Ok(())
79        })
80    }
81
82    pub fn replace_table_field_analyzer(
83        &self,
84        table_name: &str,
85        field: &str,
86        phase: &str,
87        analyzer_name: &str,
88    ) -> Result<()> {
89        self.conn.with_mut(|c| {
90            let tx = c.savepoint()?;
91            tx.execute(
92                "DELETE FROM _table_field_analyzers
93                  WHERE table_name = ?1 AND field = ?2",
94                params![table_name, field],
95            )?;
96            tx.execute(
97                "INSERT INTO _table_field_analyzers
98                    (table_name, field, phase, analyzer_name)
99                 VALUES (?1, ?2, ?3, ?4)",
100                params![table_name, field, phase, analyzer_name],
101            )?;
102            tx.commit()?;
103            Ok(())
104        })
105    }
106
107    pub fn drop_table_field_analyzer_field(&self, table_name: &str, field: &str) -> Result<()> {
108        self.conn.with(|c| {
109            c.execute(
110                "DELETE FROM _table_field_analyzers
111                  WHERE table_name = ?1 AND field = ?2",
112                params![table_name, field],
113            )?;
114            Ok(())
115        })
116    }
117
118    /// Every `(table_name, field, phase, analyzer_name)` row sorted by
119    /// `(table_name, field, phase)`.
120    pub fn load_table_field_analyzers(&self) -> Result<Vec<(String, String, String, String)>> {
121        self.conn.with(|c| {
122            let mut stmt = c.prepare(
123                "SELECT table_name, field, phase, analyzer_name FROM _table_field_analyzers \
124                  ORDER BY table_name, field, phase",
125            )?;
126            let rows = stmt.query_map([], |r| {
127                Ok((
128                    r.get::<_, String>(0)?,
129                    r.get::<_, String>(1)?,
130                    r.get::<_, String>(2)?,
131                    r.get::<_, String>(3)?,
132                ))
133            })?;
134            let mut out = Vec::new();
135            for row in rows {
136                out.push(row?);
137            }
138            Ok(out)
139        })
140    }
141}
142
143impl Catalog {
144    pub fn save_analyzer_revision(
145        &self,
146        name: &str,
147        config_json: &str,
148        descriptor_json: &str,
149    ) -> Result<()> {
150        self.conn.with(|connection| {
151            connection.execute("INSERT OR REPLACE INTO _analyzers (name, config_json, descriptor_json) VALUES (?1, ?2, ?3)", params![name, config_json, descriptor_json])?;
152            Ok(())
153        })
154    }
155
156    pub fn load_analyzer_descriptors(&self) -> Result<Vec<(String, String)>> {
157        self.conn.with(|connection| {
158            let mut statement = connection.prepare("SELECT name, descriptor_json FROM _analyzers WHERE descriptor_json IS NOT NULL ORDER BY name")?;
159            let rows = statement.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?.collect::<std::result::Result<Vec<_>, _>>()?;
160            Ok(rows)
161        })
162    }
163
164    pub fn replace_table_field_analyzer_binding(
165        &self,
166        table: &str,
167        field: &str,
168        phase: &str,
169        name: &str,
170        binding_json: &str,
171    ) -> Result<()> {
172        self.conn.with_mut(|connection| {
173            let tx = connection.savepoint()?;
174            tx.execute("DELETE FROM _table_field_analyzers WHERE table_name = ?1 AND field = ?2", params![table, field])?;
175            tx.execute("INSERT INTO _table_field_analyzers (table_name, field, phase, analyzer_name, binding_json) VALUES (?1, ?2, ?3, ?4, ?5)", params![table, field, phase, name, binding_json])?;
176            tx.commit()?;
177            Ok(())
178        })
179    }
180
181    pub fn load_table_field_analyzer_bindings(&self) -> Result<Vec<(String, String, String)>> {
182        self.conn.with(|connection| {
183            let mut statement = connection.prepare("SELECT table_name, field, binding_json FROM _table_field_analyzers WHERE binding_json IS NOT NULL ORDER BY table_name, field")?;
184            let rows = statement.query_map([], |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)))?.collect::<std::result::Result<Vec<_>, _>>()?;
185            Ok(rows)
186        })
187    }
188}