1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
// lib.rs // // This file is a part of the eXtremeDB source code // Copyright (c) 2020 McObject LLC // All Rights Reserved //! `extremedb` is a wrapper for the [McObject]'s *e*X*treme*DB database //! management system. //! //! This crate currently supports the *e*X*treme*DB SQL APIs, as well //! as parts of the core functionality necessary for configuring the //! *e*X*treme*DB runtime, creating and opening a database, and managing //! database connections. Core data manipulation APIs are planned for a future //! release. //! //! For more information on the *e*X*treme*DB database management system, //! refer to the [documentation pages]. //! //! [documentation pages]: https://www.mcobject.com/docs //! [McObject]: https://www.mcobject.com //! //! # Building //! //! This crate depends on the `extremedb_sys` crate, which has some //! prerequisites, as well as a few environment variables to be set. //! Refer to the `extremedb_sys` crate reference for more information. //! //! # Optional Features //! //! - **`sql`** — SQL engine. //! - **`rsql`** — Remote SQL engine (SQL server and client). //! - **`sequences`** — Sequences (vertical storage). //! //! # SQL Example //! //! The example below demonstrates creating an in-memory database and using the //! DDL and DML SQL statements to manipulate its schema and contents. //! //! ``` //! use extremedb::connection::Connection; //! use extremedb::database::{Database, Params}; //! use extremedb::device::{Assignment, Device}; //! use extremedb::runtime::Runtime; //! use extremedb::sql::engine::{Engine, LocalEngine}; //! use extremedb::Result; //! //! fn main() -> Result<()> { //! // Size of the memory region to allocate for the database. //! const DB_SIZE: usize = 1024 * 1024; //! //! // eXtremeDB runtime must be started before any operations can be performed. //! let runtime = Runtime::start(vec![]); //! //! # if runtime.info().multiprocess_access_supported() { //! # return Ok(()); //! # } //! // This example creates a conventional memory device, and will not work with the //! // shared memory runtime. //! assert!(!runtime.info().multiprocess_access_supported()); //! //! // Initialize database parameters. SQL databases require these three //! // parameters to be explicitly set. //! let mut db_params = Params::new(); //! db_params //! .ddl_dict_size(32768) //! .max_classes(100) //! .max_indexes(1000); //! //! // Allocate one in-memory device for the database. //! let mut devs = vec![Device::new_mem_conv(Assignment::Database, DB_SIZE)?]; //! //! // Open the database using the parameters and devices defined above. //! let db = Database::open(&runtime, "test_db", None, &mut devs, db_params)?; //! //! // Establish a connection to the database. //! let conn = Connection::new(&db)?; //! //! // Create a local SQL engine on top of the database connection. //! let engine = LocalEngine::new(&conn)?; //! //! // Use SQL DDL to create a new table. //! engine.execute_statement("CREATE TABLE TestTable(i integer, s string);", &[])?; //! //! // Insert two rows into the table. //! engine.execute_statement("INSERT INTO TestTable(i, s) VALUES(?, ?);", &[&1, &"Hello"])?; //! engine.execute_statement("INSERT INTO TestTable(i, s) VALUES(?, ?);", &[&2, &"World"])?; //! //! // Select the rows. //! let ds = engine.execute_query("SELECT i, s FROM TestTable ORDER BY i;", &[])?; //! //! // The returned data source is not expected to be empty in case of a //! // SELECT statement. //! assert!(ds.is_some()); //! let ds = ds.unwrap(); //! //! // Create a cursor to iterate over the data source. //! let mut cur = ds.cursor()?; //! //! // Read the first row. //! { //! // Advance the cursor to the first row. It is positioned //! // before-the-first row initially. //! assert_eq!(cur.advance()?, true); //! //! // Get reference to the record which the cursor points at. //! // Since cur.advance() above has returned true, the current record //! // must not be None. //! let rec = cur.current_record(); //! assert!(rec.is_some()); //! let rec = rec.unwrap(); //! //! // Get the references to the values in the columns at indexes 0 and 1. //! let i_val = rec.get_at(0)?; //! let s_val = rec.get_at(1)?; //! //! // SQL value references must be explicitly converted to target types. //! assert_eq!(i_val.to_i64()?, 1); //! assert_eq!(s_val.to_string()?, "Hello"); //! } //! //! // The second row must be available. //! assert_eq!(cur.advance()?, true); //! //! // No more rows are expected. //! assert_eq!(cur.advance()?, false); //! //! Ok(()) //! } //! ``` use extremedb_sys as exdb_sys; use std::error; use std::ffi::CStr; use std::fmt::{Display, Error as FmtError, Formatter}; use std::str; /// Core return codes (generated by bindgen from `MCO_RET` in *mco.h*). pub use exdb_sys::MCO_RET_E_ as mco_ret; pub mod connection; pub mod database; pub mod device; pub mod dict; pub mod runtime; #[cfg(feature = "sql")] pub mod sql; mod util; #[cfg(feature = "sql")] use sql::{McoSqlStatusCode, SqlError}; /// Type alias for the *e*X*treme*DB status codes returned by most functions. /// /// The actual codes are imported by `bindgen` from the *mco.h* header, /// and are available in the [`mco_ret`] module. /// /// [`mco_ret`]: ./mco_ret/index.html pub type McoRetCode = mco_ret::Type; /// An `Error`-compatible wrapper for the *e*X*treme*DB status codes. #[derive(Debug)] pub struct CoreError(McoRetCode); impl CoreError { pub(crate) fn new(rc: McoRetCode) -> Self { CoreError(rc) } /// Returns the underlying *e*X*treme*DB status code (one of the constants /// defined in the [`mco_ret`] module). /// /// [`mco_ret`]: ./mco_ret/index.html pub fn code(&self) -> McoRetCode { self.0 } fn strerror(&self) -> &'static str { let cstr = unsafe { CStr::from_ptr(exdb_sys::mco_strerror(self.0)) }; let res = cstr.to_str(); debug_assert!(res.is_ok(), "invalid string received from mco_strerror()"); match res { Err(_) => "<malformed error string>", Ok(s) => s, } } } impl error::Error for CoreError {} impl Display for CoreError { fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), FmtError> { write!(f, "{}", self.strerror()) } } /// An error type for the core database management functions. #[derive(Debug)] pub enum Error { /// A core API error. Core(CoreError), /// An SQL API error. #[cfg(feature = "sql")] Sql(SqlError), } impl Error { pub(crate) fn new_core(rc: McoRetCode) -> Self { Error::Core(CoreError::new(rc)) } #[cfg(feature = "sql")] pub(crate) fn new_sql(rc: McoSqlStatusCode) -> Self { Error::Sql(SqlError::new(rc)) } } impl error::Error for Error {} impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), FmtError> { match self { Error::Core(e) => e.fmt(f), #[cfg(feature = "sql")] Error::Sql(e) => e.fmt(f), } } } /// Type alias for `std::result::Result` used by the *e*X*treme*DB APIs. pub type Result<T> = std::result::Result<T, Error>; fn result_from_code(return_code: McoRetCode) -> Result<()> { match return_code { mco_ret::MCO_S_OK => Ok(()), _ => Err(Error::Core(CoreError::new(return_code))), } }