Skip to main content

typedb_driver/database/
database.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::{
21    io::{BufWriter, Write},
22    path::Path,
23    sync::Arc,
24};
25
26use prost::Message;
27
28use crate::{
29    common::{Error, Result, info::DatabaseInfo},
30    connection::server::{server_manager::ServerManager, server_routing::ServerRouting},
31    database::migration::{DatabaseExportAnswer, try_create_export_file, try_open_existing_export_file},
32    error::MigrationError,
33    resolve,
34};
35
36/// A TypeDB database.
37#[derive(Debug, Clone)]
38pub struct Database {
39    name: String,
40    server_manager: Arc<ServerManager>,
41}
42
43impl Database {
44    pub(super) fn new(database_info: DatabaseInfo, server_manager: Arc<ServerManager>) -> Result<Self> {
45        Ok(Self { name: database_info.name, server_manager })
46    }
47
48    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
49    pub(super) async fn get(name: String, server_manager: Arc<ServerManager>) -> Result<Self> {
50        Ok(Self { name, server_manager })
51    }
52
53    /// Retrieves the database name as a string.
54    pub fn name(&self) -> &str {
55        self.name.as_str()
56    }
57
58    /// Deletes this database.
59    ///
60    /// # Examples
61    ///
62    /// ```rust
63    #[cfg_attr(feature = "sync", doc = "database.delete();")]
64    #[cfg_attr(not(feature = "sync"), doc = "database.delete().await;")]
65    /// ```
66    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
67    pub async fn delete(self: Arc<Self>) -> Result {
68        self.server_manager
69            .execute(ServerRouting::Auto, |server_connection| {
70                let name = self.name.clone();
71                async move { server_connection.delete_database(name).await }
72            })
73            .await
74    }
75
76    /// Returns a full schema text as a valid TypeQL define query string.
77    ///
78    /// # Examples
79    ///
80    /// ```rust
81    #[cfg_attr(feature = "sync", doc = "database.schema();")]
82    #[cfg_attr(not(feature = "sync"), doc = "database.schema().await;")]
83    /// ```
84    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
85    pub async fn schema(&self) -> Result<String> {
86        self.server_manager
87            .execute(ServerRouting::Auto, |server_connection| {
88                let name = self.name.clone();
89                async move { server_connection.database_schema(name).await }
90            })
91            .await
92    }
93
94    /// Returns the types in the schema as a valid TypeQL define query string.
95    ///
96    /// # Examples
97    ///
98    /// ```rust
99    #[cfg_attr(feature = "sync", doc = "database.type_schema();")]
100    #[cfg_attr(not(feature = "sync"), doc = "database.type_schema().await;")]
101    /// ```
102    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
103    pub async fn type_schema(&self) -> Result<String> {
104        self.server_manager
105            .execute(ServerRouting::Auto, |server_connection| {
106                let name = self.name.clone();
107                async move { server_connection.database_type_schema(name).await }
108            })
109            .await
110    }
111
112    /// Export a database into a schema definition and a data files saved to the disk.
113    /// This is a blocking operation and may take a significant amount of time depending on the database size.
114    ///
115    /// # Arguments
116    ///
117    /// * `schema_file_path` — The path to the schema definition file to be created
118    /// * `data_file_path` — The path to the data file to be created
119    ///
120    /// # Examples
121    ///
122    /// ```rust
123    #[cfg_attr(feature = "sync", doc = "database.export_to_file(schema_path, data_path);")]
124    #[cfg_attr(not(feature = "sync"), doc = "database.export_to_file(schema_path, data_path).await;")]
125    /// ```
126    #[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
127    pub async fn export_to_file(&self, schema_file_path: impl AsRef<Path>, data_file_path: impl AsRef<Path>) -> Result {
128        let schema_file_path = schema_file_path.as_ref();
129        let data_file_path = data_file_path.as_ref();
130        if schema_file_path == data_file_path {
131            return Err(Error::Migration(MigrationError::CannotExportToTheSameFile));
132        }
133
134        let _ = try_create_export_file(schema_file_path)?;
135        if let Err(err) = try_create_export_file(data_file_path) {
136            let _ = std::fs::remove_file(schema_file_path);
137            return Err(err);
138        }
139
140        let result = self
141            .server_manager
142            .execute(ServerRouting::Auto, |server_connection| {
143                let name = self.name.clone();
144                async move {
145                    // File opening should be idempotent for multiple function invocations
146                    let mut schema_file = try_open_existing_export_file(schema_file_path)?;
147                    let data_file = try_open_existing_export_file(data_file_path)?;
148                    let mut export_stream = server_connection.database_export(name).await?;
149                    let mut data_writer = BufWriter::new(data_file);
150
151                    loop {
152                        match resolve!(export_stream.next())? {
153                            DatabaseExportAnswer::Done => break,
154                            DatabaseExportAnswer::Schema(schema) => {
155                                schema_file.write_all(schema.as_bytes())?;
156                                schema_file.flush()?;
157                            }
158                            DatabaseExportAnswer::Items(items) => {
159                                for item in items {
160                                    let mut buf = Vec::new();
161                                    item.encode_length_delimited(&mut buf)
162                                        .map_err(|_| Error::Migration(MigrationError::CannotEncodeExportedConcept))?;
163                                    data_writer.write_all(&buf)?;
164                                }
165                            }
166                        }
167                    }
168
169                    data_writer.flush()?;
170                    Ok(())
171                }
172            })
173            .await;
174
175        if result.is_err() {
176            let _ = std::fs::remove_file(schema_file_path);
177            let _ = std::fs::remove_file(data_file_path);
178        }
179        result
180    }
181}