1use super::{
10 params, Catalog, CatalogIndexRow, ForeignTableRow, RelationIdentity, RelationKind, Result,
11 SQLiteError, TableAclEntry,
12};
13
14impl Catalog {
15 pub fn save_foreign_server(
18 &self,
19 name: &str,
20 fdw_type: &str,
21 options_json: &str,
22 ) -> Result<()> {
23 self.conn.with(|c| {
24 c.execute(
25 "INSERT OR REPLACE INTO _foreign_servers (name, fdw_type, options) \
26 VALUES (?1, ?2, ?3)",
27 params![name, fdw_type, options_json],
28 )?;
29 Ok(())
30 })
31 }
32
33 pub fn drop_foreign_server(&self, name: &str) -> Result<()> {
34 self.conn.with(|c| {
35 c.execute(
36 "DELETE FROM _foreign_servers WHERE name = ?1",
37 params![name],
38 )?;
39 Ok(())
40 })
41 }
42
43 pub fn load_foreign_servers(&self) -> Result<Vec<(String, String, String)>> {
44 self.conn.with(|c| {
45 let mut stmt =
46 c.prepare("SELECT name, fdw_type, options FROM _foreign_servers ORDER BY name")?;
47 let rows = stmt.query_map([], |r| {
48 Ok((
49 r.get::<_, String>(0)?,
50 r.get::<_, String>(1)?,
51 r.get::<_, String>(2)?,
52 ))
53 })?;
54 let mut out = Vec::new();
55 for row in rows {
56 out.push(row?);
57 }
58 Ok(out)
59 })
60 }
61
62 pub fn save_foreign_table(&self, row: &ForeignTableRow) -> Result<()> {
65 self.conn.with_mut(|c| {
66 let tx = c.savepoint()?;
67 Self::claim_relation(&tx, &row.relation, RelationKind::ForeignTable)?;
68 let acl_json = row.acl.as_deref().map(serde_json::to_string).transpose()?;
69 let column_acls_json = serde_json::to_string(&row.column_acls)?;
70 tx.execute(
71 "INSERT OR REPLACE INTO _foreign_tables \
72 (schema_name, relation_name, kind, role_owner, acl_json, column_acls_json, server_name, columns_json, options) \
73 VALUES (?1, ?2, 'foreign_table', ?3, ?4, ?5, ?6, ?7, ?8)",
74 params![
75 row.relation.schema,
76 row.relation.name,
77 row.role_owner,
78 acl_json,
79 column_acls_json,
80 row.server_name,
81 row.columns_json,
82 row.options_json
83 ],
84 )?;
85 tx.commit()?;
86 Ok(())
87 })
88 }
89
90 pub fn update_foreign_table_security(
91 &self,
92 relation: &RelationIdentity,
93 role_owner: &str,
94 acl: Option<&[TableAclEntry]>,
95 column_acls: &std::collections::BTreeMap<String, Vec<TableAclEntry>>,
96 ) -> Result<bool> {
97 self.conn.with_mut(|connection| {
98 let acl_json = acl.map(serde_json::to_string).transpose()?;
99 let column_acls_json = serde_json::to_string(column_acls)?;
100 Ok(connection.execute(
101 "UPDATE _foreign_tables
102 SET role_owner = ?3, acl_json = ?4, column_acls_json = ?5
103 WHERE schema_name = ?1 AND relation_name = ?2",
104 params![
105 relation.schema,
106 relation.name,
107 role_owner,
108 acl_json,
109 column_acls_json
110 ],
111 )? != 0)
112 })
113 }
114
115 pub fn rename_foreign_table(
116 &self,
117 from: &RelationIdentity,
118 to: &RelationIdentity,
119 ) -> Result<bool> {
120 if from.schema != to.schema {
121 return Err(SQLiteError::StorageBackend(
122 "moving a foreign table between schemas is not supported by the catalog".into(),
123 ));
124 }
125 self.conn.with_mut(|connection| {
126 let source_exists = connection.query_row(
127 "SELECT EXISTS(SELECT 1 FROM _foreign_tables WHERE schema_name = ?1 AND relation_name = ?2)",
128 params![from.schema, from.name],
129 |row| row.get::<_, bool>(0),
130 )?;
131 if from == to || !source_exists {
132 return Ok(source_exists);
133 }
134 let target_exists = connection.query_row(
135 "SELECT EXISTS(SELECT 1 FROM _relations WHERE schema_name = ?1 AND relation_name = ?2)",
136 params![to.schema, to.name],
137 |row| row.get::<_, bool>(0),
138 )?;
139 if target_exists {
140 return Err(SQLiteError::StorageBackend(format!(
141 "relation `{}` already exists",
142 to.qualified_name()
143 )));
144 }
145 let tx = connection.savepoint()?;
146 Self::claim_relation(&tx, to, RelationKind::ForeignTable)?;
147 let updated = tx.execute(
148 "UPDATE _foreign_tables SET schema_name = ?3, relation_name = ?4 WHERE schema_name = ?1 AND relation_name = ?2",
149 params![from.schema, from.name, to.schema, to.name],
150 )?;
151 if updated != 1 {
152 return Err(SQLiteError::StorageBackend(format!(
153 "foreign table `{}` disappeared during rename",
154 from.qualified_name()
155 )));
156 }
157 Self::release_relation(&tx, from, RelationKind::ForeignTable)?;
158 tx.commit()?;
159 Ok(true)
160 })
161 }
162
163 pub fn drop_foreign_table(&self, relation: &RelationIdentity) -> Result<()> {
164 self.conn.with_mut(|c| {
165 let tx = c.savepoint()?;
166 let removed = tx.execute(
167 "DELETE FROM _foreign_tables
168 WHERE schema_name = ?1 AND relation_name = ?2",
169 params![relation.schema, relation.name],
170 )? != 0;
171 if removed {
172 Self::release_relation(&tx, relation, RelationKind::ForeignTable)?;
173 }
174 tx.commit()?;
175 Ok(())
176 })
177 }
178
179 pub fn load_foreign_tables(&self) -> Result<Vec<ForeignTableRow>> {
180 self.conn.with(|c| {
181 let mut stmt = c.prepare(
182 "SELECT schema_name, relation_name, role_owner, acl_json, column_acls_json, server_name, columns_json, options
183 FROM _foreign_tables ORDER BY schema_name, relation_name",
184 )?;
185 let rows = stmt.query_map([], |r| {
186 Ok((
187 r.get::<_, String>(0)?,
188 r.get::<_, String>(1)?,
189 r.get::<_, String>(2)?,
190 r.get::<_, Option<String>>(3)?,
191 r.get::<_, String>(4)?,
192 r.get::<_, String>(5)?,
193 r.get::<_, String>(6)?,
194 r.get::<_, String>(7)?,
195 ))
196 })?;
197 let mut out = Vec::new();
198 for row in rows {
199 let (schema, name, owner, acl_json, column_acls_json, server, cols, opts) = row?;
200 out.push(ForeignTableRow {
201 relation: RelationIdentity::new(schema, name),
202 role_owner: owner,
203 acl: acl_json
204 .as_deref()
205 .map(serde_json::from_str)
206 .transpose()?,
207 column_acls: serde_json::from_str(&column_acls_json)?,
208 server_name: server,
209 columns_json: cols,
210 options_json: opts,
211 });
212 }
213 Ok(out)
214 })
215 }
216
217 pub fn save_catalog_index(
220 &self,
221 relation: &RelationIdentity,
222 index_type: &str,
223 table_name: &str,
224 columns_json: &str,
225 parameters_json: &str,
226 ) -> Result<()> {
227 self.save_catalog_index_row(&CatalogIndexRow {
228 relation: relation.clone(),
229 index_type: index_type.to_string(),
230 table_name: table_name.to_string(),
231 columns_json: columns_json.to_string(),
232 parameters_json: parameters_json.to_string(),
233 definition_json: None,
234 })
235 }
236
237 pub fn save_catalog_index_row(&self, index: &CatalogIndexRow) -> Result<()> {
238 let CatalogIndexRow {
239 relation,
240 index_type,
241 table_name,
242 columns_json,
243 parameters_json,
244 definition_json,
245 } = index;
246 let table =
247 RelationIdentity::from_legacy_name(table_name).map_err(SQLiteError::StorageBackend)?;
248 if relation.schema != table.schema {
249 return Err(SQLiteError::StorageBackend(format!(
250 "catalog index `{}` cannot belong to a different schema than table `{}`",
251 relation.qualified_name(),
252 table.qualified_name()
253 )));
254 }
255 self.conn.with_mut(|c| {
256 let tx = c.savepoint()?;
257 Self::claim_relation(&tx, relation, RelationKind::Index)?;
258 tx.execute(
259 "INSERT INTO _catalog_indexes
260 (schema_name, relation_name, kind, index_type, table_schema_name,
261 table_relation_name, columns, parameters, definition)
262 VALUES (?1, ?2, 'index', ?3, ?4, ?5, ?6, ?7, ?8)
263 ON CONFLICT(schema_name, relation_name) DO UPDATE SET
264 index_type = excluded.index_type,
265 table_schema_name = excluded.table_schema_name,
266 table_relation_name = excluded.table_relation_name,
267 columns = excluded.columns,
268 parameters = excluded.parameters,
269 definition = excluded.definition",
270 params![
271 relation.schema,
272 relation.name,
273 index_type,
274 table.schema,
275 table.name,
276 columns_json,
277 parameters_json,
278 definition_json
279 ],
280 )?;
281 tx.commit()?;
282 Ok(())
283 })
284 }
285
286 pub fn drop_catalog_index(&self, relation: &RelationIdentity) -> Result<()> {
287 self.conn.with_mut(|c| {
288 let tx = c.savepoint()?;
289 tx.execute(
290 "DELETE FROM _catalog_indexes
291 WHERE schema_name = ?1 AND relation_name = ?2",
292 params![relation.schema, relation.name],
293 )?;
294 Self::release_relation(&tx, relation, RelationKind::Index)?;
295 tx.commit()?;
296 Ok(())
297 })
298 }
299
300 pub fn drop_catalog_indexes_for_table(&self, table_name: &str) -> Result<()> {
301 let table =
302 RelationIdentity::from_legacy_name(table_name).map_err(SQLiteError::StorageBackend)?;
303 self.conn.with_mut(|c| {
304 let tx = c.savepoint()?;
305 Self::drop_catalog_index_rows_for_table(&tx, &table)?;
306 tx.commit()?;
307 Ok(())
308 })
309 }
310
311 pub(in crate::sqlite::catalog) fn drop_catalog_index_rows_for_table(
312 conn: &rusqlite::Connection,
313 table: &RelationIdentity,
314 ) -> Result<()> {
315 let indexes = {
316 let mut statement = conn.prepare(
317 "SELECT schema_name, relation_name
318 FROM _catalog_indexes
319 WHERE table_schema_name = ?1 AND table_relation_name = ?2",
320 )?;
321 let indexes = statement
322 .query_map(params![table.schema, table.name], |row| {
323 Ok(RelationIdentity::new(
324 row.get::<_, String>(0)?,
325 row.get::<_, String>(1)?,
326 ))
327 })?
328 .collect::<rusqlite::Result<Vec<_>>>()?;
329 indexes
330 };
331 for index in indexes {
332 conn.execute(
333 "DELETE FROM _catalog_indexes
334 WHERE schema_name = ?1 AND relation_name = ?2",
335 params![index.schema, index.name],
336 )?;
337 Self::release_relation(conn, &index, RelationKind::Index)?;
338 }
339 Ok(())
340 }
341
342 pub fn load_catalog_indexes(&self) -> Result<Vec<CatalogIndexRow>> {
343 self.conn.with(|c| {
344 let mut stmt = c.prepare(
345 "SELECT schema_name, relation_name, index_type,
346 table_schema_name, table_relation_name, columns, parameters, definition
347 FROM _catalog_indexes ORDER BY schema_name, relation_name",
348 )?;
349 let rows = stmt.query_map([], |r| {
350 Ok((
351 r.get::<_, String>(0)?,
352 r.get::<_, String>(1)?,
353 r.get::<_, String>(2)?,
354 r.get::<_, String>(3)?,
355 r.get::<_, String>(4)?,
356 r.get::<_, String>(5)?,
357 r.get::<_, String>(6)?,
358 r.get::<_, Option<String>>(7)?,
359 ))
360 })?;
361 let mut out = Vec::new();
362 for row in rows {
363 let (schema, name, ty, table_schema, table_name, cols, params_json, definition) =
364 row?;
365 out.push(CatalogIndexRow {
366 relation: RelationIdentity::new(schema, name),
367 index_type: ty,
368 table_name: RelationIdentity::new(table_schema, table_name).qualified_name(),
369 columns_json: cols,
370 parameters_json: params_json,
371 definition_json: definition,
372 });
373 }
374 Ok(out)
375 })
376 }
377
378 pub fn save_path_index(&self, graph_name: &str, label_sequences_json: &str) -> Result<()> {
381 self.conn.with(|c| {
382 c.execute(
383 "INSERT OR REPLACE INTO _path_indexes (graph_name, label_sequences) \
384 VALUES (?1, ?2)",
385 params![graph_name, label_sequences_json],
386 )?;
387 Ok(())
388 })
389 }
390
391 pub fn drop_path_index(&self, graph_name: &str) -> Result<()> {
392 self.conn.with(|c| {
393 c.execute(
394 "DELETE FROM _path_indexes WHERE graph_name = ?1",
395 params![graph_name],
396 )?;
397 Ok(())
398 })
399 }
400
401 pub fn load_path_indexes(&self) -> Result<Vec<(String, String)>> {
403 self.conn.with(|c| {
404 let mut stmt = c.prepare(
405 "SELECT graph_name, label_sequences FROM _path_indexes ORDER BY graph_name",
406 )?;
407 let rows =
408 stmt.query_map([], |r| Ok((r.get::<_, String>(0)?, r.get::<_, String>(1)?)))?;
409 let mut out = Vec::new();
410 for row in rows {
411 out.push(row?);
412 }
413 Ok(out)
414 })
415 }
416}