Skip to main content

typedb_driver/database/
database_manager.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20use std::{io::BufReader, path::Path, sync::Arc};
21
22use itertools::Itertools;
23use typedb_protocol::migration::Item;
24
25use super::Database;
26use crate::{
27    common::Result,
28    connection::server::{server_manager::ServerManager, server_routing::ServerRouting},
29    database::migration::{ProtoMessageIterator, try_open_import_file},
30    info::DatabaseInfo,
31    resolve,
32};
33
34/// Provides access to all database management methods.
35#[derive(Debug)]
36pub struct DatabaseManager {
37    server_manager: Arc<ServerManager>,
38}
39
40/// Provides access to all database management methods.
41impl DatabaseManager {
42    pub(crate) fn new(server_manager: Arc<ServerManager>) -> Result<Self> {
43        Ok(Self { server_manager })
44    }
45
46    /// Retrieves all databases present on the TypeDB server.
47    ///
48    /// # Examples
49    ///
50    /// ```rust
51    #[cfg_attr(feature = "sync", doc = "driver.databases().all();")]
52    #[cfg_attr(not(feature = "sync"), doc = "driver.databases().all().await;")]
53    /// ```
54    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
55    pub async fn all(&self) -> Result<Vec<Arc<Database>>> {
56        self.server_manager
57            .execute(ServerRouting::Auto, move |server_connection| async move {
58                server_connection
59                    .all_databases()
60                    .await?
61                    .into_iter()
62                    .map(|database_info| self.try_build_database(database_info))
63                    .try_collect()
64            })
65            .await
66    }
67
68    /// Retrieves the database with the given name.
69    ///
70    /// # Arguments
71    ///
72    /// * `name` — The name of the database to retrieve
73    ///
74    /// # Examples
75    ///
76    /// ```rust
77    #[cfg_attr(feature = "sync", doc = "driver.databases().get(name);")]
78    #[cfg_attr(not(feature = "sync"), doc = "driver.databases().get(name).await;")]
79    /// ```
80    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
81    pub async fn get(&self, name: impl Into<String>) -> Result<Arc<Database>> {
82        let name = name.into();
83        let database_info = self
84            .server_manager
85            .execute(ServerRouting::Auto, move |server_connection| {
86                let name = name.clone();
87                async move { server_connection.get_database(name).await }
88            })
89            .await?;
90        self.try_build_database(database_info)
91    }
92
93    /// Checks if a database with the given name exists.
94    ///
95    /// # Arguments
96    ///
97    /// * `name` — The database name to be checked
98    ///
99    /// # Examples
100    ///
101    /// ```rust
102    #[cfg_attr(feature = "sync", doc = "driver.databases().contains(name);")]
103    #[cfg_attr(not(feature = "sync"), doc = "driver.databases().contains(name).await;")]
104    /// ```
105    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
106    pub async fn contains(&self, name: impl Into<String>) -> Result<bool> {
107        let name = name.into();
108        self.server_manager
109            .execute(ServerRouting::Auto, move |server_connection| {
110                let name = name.clone();
111                async move { server_connection.contains_database(name).await }
112            })
113            .await
114    }
115
116    /// Creates a database with the given name.
117    ///
118    /// # Arguments
119    ///
120    /// * `name` — The name of the database to be created
121    ///
122    /// # Examples
123    ///
124    /// ```rust
125    #[cfg_attr(feature = "sync", doc = "driver.databases().create(name);")]
126    #[cfg_attr(not(feature = "sync"), doc = "driver.databases().create(name).await;")]
127    /// ```
128    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
129    pub async fn create(&self, name: impl Into<String>) -> Result {
130        let name = name.into();
131        self.server_manager
132            .execute(ServerRouting::Auto, move |server_connection| {
133                let name = name.clone();
134                async move { server_connection.create_database(name).await }
135            })
136            .await
137    }
138
139    /// Creates a database with the given name based on previously exported another database's data
140    /// loaded from a file.
141    /// This is a blocking operation and may take a significant amount of time depending on the
142    /// database size.
143    ///
144    /// # Arguments
145    ///
146    /// * `name` — The name of the database to be created
147    /// * `schema` — The schema definition query string for the database
148    /// * `data_file_path` — The exported database file to import the data from
149    ///
150    /// # Examples
151    ///
152    /// ```rust
153    #[cfg_attr(feature = "sync", doc = "driver.databases().import_from_file(name, schema, data_path);")]
154    #[cfg_attr(not(feature = "sync"), doc = "driver.databases().import_from_file(name, schema, data_path).await;")]
155    /// ```
156    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
157    pub async fn import_from_file(
158        &self,
159        name: impl Into<String>,
160        schema: impl Into<String>,
161        data_file_path: impl AsRef<Path>,
162    ) -> Result {
163        const ITEM_BATCH_SIZE: usize = 250;
164
165        let name = name.into();
166        let schema: String = schema.into();
167        let schema_ref: &str = schema.as_ref();
168        let data_file_path = data_file_path.as_ref();
169
170        self.server_manager
171            .execute(ServerRouting::Auto, move |server_connection| {
172                let name = name.clone();
173                async move {
174                    let file = try_open_import_file(data_file_path)?;
175                    let mut import_stream = server_connection.import_database(name, schema_ref.to_string()).await?;
176
177                    let mut item_buffer = Vec::with_capacity(ITEM_BATCH_SIZE);
178                    let mut read_item_iterator = ProtoMessageIterator::<Item, _>::new(BufReader::new(file));
179
180                    while let Some(item) = read_item_iterator.next() {
181                        let item = item?;
182                        item_buffer.push(item);
183                        if item_buffer.len() >= ITEM_BATCH_SIZE {
184                            import_stream.send_items(item_buffer.split_off(0))?;
185                        }
186                    }
187
188                    if !item_buffer.is_empty() {
189                        import_stream.send_items(item_buffer)?;
190                    }
191
192                    resolve!(import_stream.done())
193                }
194            })
195            .await
196    }
197
198    fn try_build_database(&self, database_info: DatabaseInfo) -> Result<Arc<Database>> {
199        Database::new(database_info, self.server_manager.clone()).map(Arc::new)
200    }
201}