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
/* *********************************************************************
* Copyright (c) 2024 Mimer Information Technology
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* See license for more details.
* *********************************************************************/
//! A Rust API for the Mimer SQL database.
//!
//! # Introduction
//! The Mimer SQL Rust API is built as a wrapper around the Mimer C API. It consists of three crates:
//! 1. `mimerrust-sys`: This crate is responsible for the low-level wrapping of the C library into compatible Rust concepts.
//! It is not meant for direct use, but rather as an intermediary wrapping step.
//! 2. `mimerrust-bindings`: This crate is the one that is running bindgen to generated the C bindings using by `mimerrust-sys`.
//! This is put in a separate crate so we don't have to run `bindgen` all the time. The reason is that `bindgen` requires LLVM and Clang and that is typically not installed on Windows. Instead we generate and keep the bindings, unless explicitly building this crate.
//! 3. `mimerrust`: This crate uses the raw wrappers from `mimerrust-sys` to create a high level, safe interface.
//! A small example of how to use Mimer SQL Rust API can look like this:
//! ```
//! use mimerrust::{Connection, ToSql, CursorMode};
//!
//! fn main() {
//! print!("Connecting to database\n");
//! let mut conn =
//! Connection::open("", "RUSTUSER", "RUSTPASSWORD").unwrap_or_else(|ec| panic!("{}", ec));
//!
//! conn.execute_statement("DROP TABLE test_table").ok();
//! println!("Creating table");
//! conn.execute_statement("CREATE TABLE test_table (id INT primary key, text NVARCHAR(30))")
//! .ok();
//! println!("Inserting rows");
//! let insert_stmt = conn.prepare("INSERT INTO test_table (id, text) VALUES(:id, :text)",
//! CursorMode::Forward).expect("Error preparing statement");
//!
//! let mut text = "Hello";
//! let mut id = 1;
//! let params: &[&dyn ToSql] = &[&id,&text];
//! insert_stmt.execute_bind(params).expect("Error inserting first row");
//!
//! text = "World!";
//! id = 2;
//! let params: &[&dyn ToSql] = &[&id,&text];
//! insert_stmt.execute_bind(params).expect("Error inserting second row");
//!
//! let stmt = conn
//! .prepare("SELECT * from test_table", CursorMode::Forward)
//! .unwrap();
//! let mut cursor = stmt.open_cursor().unwrap();
//! println!("Fetching all rows");
//! while let Some(row) = cursor.next_row().unwrap() {
//! let id: i32 = row.get(1).unwrap().unwrap();
//! let text: String = row.get(2).unwrap().unwrap();
//! println!("id: {}, text: {}", id, text);
//! }
//! }
//! ```
//! All examples and tests uses an ident called `RUSTUSER` with the password `RUSTPASSWORD`. To create the user in Mimer SQL, run `bsql` or DbVisualizer as SYSADM, or an other ident with proper privileges, and create the ident as follows:
//! ```SQL
//! create ident RUSTUSER as user using 'RUSTPASSWORD';
//! grant databank to RUSTUSER;
//! ```
//! To run the examples you need a databank as well. Log into Mimer SQL using `bsql` or DbVisualizer as `RUSTUSER` and run:
//! ```SQL
//! create datank rustdb
//! ```
//! The tests create the databanks needed.
//!
//! # Requirements
//! This API requires the Mimer SQL C API to be installed on the system. The API is tested with Mimer SQL 11.0.8D.
//! Furthermore, bindings to the Mimer SQL C API are generated at compile time using [bindgen](https://docs.rs/bindgen/latest/bindgen/), which requires [clang](https://clang.llvm.org/docs/index.html) to be installed on the system.
//! The bindings are not re-built automatically, instead a pre-generated binding is used. This is to avoid requirements on having Clang on for example Windows.
//! To generate new bindings, go into the `mimerrust-bindings` and run `cargo build`.
//!
pub(crate) mod common;
pub(crate) mod connection;
pub(crate) mod cursor;
pub(crate) mod inner_connection;
pub(crate) mod inner_statement;
pub(crate) mod mimer_error;
pub(crate) mod row;
pub(crate) mod statement;
pub(crate) mod testing;
pub(crate) mod transaction;
/// Handles datatypes and their conversions between Rust and Mimer SQL.
///
/// This module contains definitions for the [ToSql] and [FromSql] traits, which defines how a rust datatype should be converted to a Mimer SQL type and vice versa.
/// These traits are implemented for a variety of types as described in the documentation for each trait, but also allows for custom implementations by the user.
/// Below follows an example of how this can be done:
///
/// 1. Define a custom type (e.g. a struct):
/// ```
/// #[derive(Debug, PartialEq)]
/// struct CustomType {
/// first_value: i32,
/// second_value: i32,
/// }
/// ```
///
/// 2. Implement the [ToSql] and [FromSql] trait for the custom type:
/// The ToSql trait defines how the custom datatype should be stored in the database (e.g. if the data is to be stored as a string-variant or integer-variant),
/// whereas the FromSql trait defines from what Mimerdatatype the custom datatype can be converted from.
///
/// ```
/// impl ToSql for CustomType {
/// fn to_sql(&self) -> MimerDatatype {
/// let mut bytes: [u8; 8] = [0; 8];
/// bytes[..4].copy_from_slice(&self.first_value.to_le_bytes());
/// bytes[4..].copy_from_slice(&self.second_value.to_le_bytes());
/// MimerDatatype::BinaryArray(bytes.to_vec())
/// }
/// }
/// # use mimerrust::*;
/// # #[derive(Debug, PartialEq)]
/// # struct CustomType {
/// # first_value: i32,
/// # second_value: i32,
/// # }
/// impl FromSql for CustomType {
/// fn from_sql(value: MimerDatatype) -> Result<Self, i32> {
/// match value {
/// MimerDatatype::BinaryArray(v) => {
/// if v.len() != 8 {
/// return Err(-26200); // Mimer Rust API error code for unsupported type conversion.
/// }
/// Ok(CustomType {
/// first_value: i32::from_le_bytes(v[0..4].try_into().unwrap()),
/// second_value: i32::from_le_bytes(v[4..8].try_into().unwrap())
/// }
/// )
/// }
/// _ => Err(-26200), // Mimer Rust API error code for unsupported type conversion.
/// }
/// }
/// }
/// ```
///
/// 3. Once both the ToSql and FromSql traits are implemented, the custom type can be used in the API. An example of this is shown below:
/// ```
/// # use mimerrust::*;
/// # #[derive(Debug, PartialEq)]
/// # struct CustomType {
/// # first_value: i32,
/// # second_value: i32,
/// # }
/// # impl ToSql for CustomType {
/// # fn to_sql(&self) -> MimerDatatype {
/// # let mut bytes: [u8; 8] = [0; 8];
/// # bytes[..4].copy_from_slice(&self.first_value.to_le_bytes());
/// # bytes[4..].copy_from_slice(&self.second_value.to_le_bytes());
/// # MimerDatatype::BinaryArray(bytes.to_vec())
/// # }
/// # }
/// # impl FromSql for CustomType {
/// # fn from_sql(value: MimerDatatype) -> Result<Self, i32> {
/// # match value {
/// # MimerDatatype::BinaryArray(v) => {
/// # if v.len() != 8 {
/// # return Err(-26200); // Mimer Rust API error code for unsupported type conversion.
/// # }
/// # Ok(CustomType {
/// # first_value: i32::from_le_bytes(v[0..4].try_into().unwrap()),
/// # second_value: i32::from_le_bytes(v[4..8].try_into().unwrap())
/// # }
/// # )
/// # }
/// # _ => Err(-26200), // Mimer Rust API error code for unsupported type conversion.
/// # }
/// # }
/// # }
/// # let db = &std::env::var("MIMER_DATABASE").unwrap();
/// # let ident = "RUSTUSER";
/// # let pass = "RUSTPASSWORD";
/// let mut conn = Connection::open(db, ident, pass).unwrap();
/// _ = conn.execute_statement("DROP TABLE my_table");
/// conn.execute_statement("CREATE TABLE my_table (my_custom_column BINARY(8))").unwrap();
///
/// let custom_type = CustomType {
/// first_value: 1,
/// second_value: 2,
/// };
///
/// let stmnt = conn.prepare("INSERT INTO my_table (my_custom_column) VALUES(:param)", CursorMode::Forward).unwrap();
/// stmnt.execute_bind(&[&custom_type]).unwrap();
///
/// let stmnt = conn.prepare("SELECT * FROM my_table", CursorMode::Forward).unwrap();
/// let mut cursor = stmnt.open_cursor().unwrap();
/// let row = cursor.next_row().unwrap().unwrap();
/// let fetched_custom_type = row.get::<CustomType>(1).unwrap().unwrap();
///
/// assert_eq!(custom_type, fetched_custom_type);
/// ```
pub mod types;
pub use common::mimer_options::*;
pub use common::return_codes::*;
pub use connection::Connection;
pub use cursor::Cursor;
pub use mimer_error::MimerError;
pub use row::Row;
pub use statement::Statement;
pub use transaction::Transaction;
pub use types::*;