Repository: libsql/libsql
File: libsql-server/src/namespace/meta_store.rs (Rust)

Code before:

        tracing::debug!("meta handle subscribed");

        MetaStoreHandle {
            namespace,
            inner: HandleState::External(change_tx, rx),
        }
    }

    pub fn remove(&self, namespace: NamespaceName) -> Result<Option<Arc<DatabaseConfig>>> {
        tracing::debug!("removing namespace `{}` from meta store", namespace);

        let mut guard = self.inner.lock();
        let r = if let Some(sender) = guard.configs.get(&namespace) {
            tracing::debug!("removed namespace `{}` from meta store", namespace);
            let config = sender.borrow().clone();
            let tx = guard.conn.transaction()?;
            if let Some(shared_schema) = config.config.shared_schema_name.as_deref() {
                tx.execute(
                    "DELETE FROM shared_schema_links WHERE shared_schema_name = ? AND db_name = ?",
                    [shared_schema, namespace.as_str()],
                )?;
            }
            if config.config.is_shared_schema {
                let res: rusqlite::Result<u32> = tx.query_row(
                    "SELECT COUNT(*) FROM shared_schema_links WHERE shared_schema_name = ?",
                    [namespace.as_str()],
                    |row| row.get(0),
                );
                match res {
                    Ok(0) | Err(rusqlite::Error::QueryReturnedNoRows) => { /* ok */ }
                    Ok(n) => return Err(Error::SharedSchemaDestroyError(namespace.to_string(), n)),
                    Err(e) => return Err(e.into()),
                }
            }
            tx.execute(
                "DELETE FROM namespace_configs WHERE namespace = ?",
                [namespace.as_str()],
            )?;
            tx.commit()?;
            Ok(Some(config.config))
        } else {
            tracing::trace!("namespace `{}` not found in meta store", namespace);
            Ok(None)
        };
        guard.configs.remove(&namespace);
        r
    }

    // TODO: we need to either make sure that the metastore is restored
    // before we start accepting connections or we need to contact bottomless

Review comment (suggestion):
this should not be necessary if:
- we enable foreign keys on the connection (`PRAGMA foreign_keys=ON`)
- add a deletion restriction on the table
    - `FOREIGN KEY (shared_schema_name) REFERENCES namespace_configs (namespace) ON DELETE RESTRICT`
    - `FOREIGN KEY (db_name) REFERENCES namespace_configs (namespace) ON DELETE RESTRICT`

Code after:
            tx.execute(
                "DELETE FROM namespace_configs WHERE namespace = ?",
                [namespace.as_str()],
            )?;
            tx.commit()?;
            Ok(Some(config.config))
        } else {
            tracing::trace!("namespace `{}` not found in meta store", namespace);
            Ok(None)
        };
        guard.configs.remove(&namespace);
        r
    }

    // TODO: we need to either make sure that the metastore is restored
    // before we start accepting connections or we need to contact bottomless
    // here to check if a namespace exists. Preferably the former.
    pub fn exists(&self, namespace: &NamespaceName) -> bool {
        self.inner.lock().configs.contains_key(namespace)
    }

    pub(crate) async fn shutdown(&self) -> crate::Result<()> {
        let replicator = self
            .inner
            .lock()
            .wal_manager
            .wrapper()
            .as_ref()
            .and_then(|b| b.shutdown());

        if let Some(mut replicator) = replicator {
            tracing::info!("Started meta store backup");
            replicator.shutdown_gracefully().await?;
            tracing::info!("meta store backed up");
        }

        Ok(())
    }
}

impl MetaStoreHandle {
    #[cfg(test)]
    pub fn new_test() -> Self {
        Self::internal()
    }

    #[cfg(test)]
    pub fn load(db_path: impl AsRef<std::path::Path>) -> crate::Result<Self> {
        use std::{fs, io};

        let config_path = db_path.as_ref().join("config.json");