use std::ffi::{c_void, CStr, CString};
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::ptr;
use crate::device::Device;
use crate::dict;
use crate::runtime::Runtime;
use crate::util::BitMask32;
use crate::{exdb_sys, mco_ret, result_from_code, Error, Result};
use exdb_sys::MCO_COMMIT_POLICY_E as mco_commit_policy;
use exdb_sys::MCO_COMPRESSION_MASK_ as mco_compression_mask;
use exdb_sys::MCO_DB_MODE_MASK_ as mco_db_mode_mask;
use exdb_sys::MCO_LOG_TYPE_ as mco_log_type;
use exdb_sys::MCO_TRANS_SCHED_POLICY_E_ as mco_trans_sched_policy;
macro_rules! db_param_scalar {
($(#[$p_meta:meta])* $param:ident, $(#[$g_meta:meta])* $getter:ident, $ty:ty, $mco_ty:ty) => {
$(#[$p_meta])*
pub fn $param(&mut self, $param: $ty) -> &mut Self {
self.p.$param = $param as $mco_ty;
self
}
$(#[$g_meta])*
pub fn $getter(&self) -> $ty {
self.p.$param as $ty
}
};
($(#[$p_meta:meta])* $param:ident, $(#[$g_meta:meta])* $getter:ident, $ty:ty) => {
db_param_scalar!($(#[$p_meta])* $param, $(#[$g_meta])* $getter, $ty, $ty);
};
}
pub enum LogType {
None = mco_log_type::NO_LOG as isize,
Redo = mco_log_type::REDO_LOG as isize,
Undo = mco_log_type::UNDO_LOG as isize,
}
impl LogType {
fn from_mco(t: mco_log_type::Type) -> Option<Self> {
match t {
mco_log_type::NO_LOG => Some(LogType::None),
mco_log_type::REDO_LOG => Some(LogType::Redo),
mco_log_type::UNDO_LOG => Some(LogType::Undo),
_ => None,
}
}
}
pub enum CommitPolicy {
SyncFlush = mco_commit_policy::MCO_COMMIT_SYNC_FLUSH as isize,
Buffered = mco_commit_policy::MCO_COMMIT_BUFFERED as isize,
Delayed = mco_commit_policy::MCO_COMMIT_DELAYED as isize,
NoSync = mco_commit_policy::MCO_COMMIT_NO_SYNC as isize,
}
impl CommitPolicy {
fn from_mco(p: mco_commit_policy::Type) -> Option<Self> {
match p {
mco_commit_policy::MCO_COMMIT_SYNC_FLUSH => Some(CommitPolicy::SyncFlush),
mco_commit_policy::MCO_COMMIT_BUFFERED => Some(CommitPolicy::Buffered),
mco_commit_policy::MCO_COMMIT_DELAYED => Some(CommitPolicy::Delayed),
mco_commit_policy::MCO_COMMIT_NO_SYNC => Some(CommitPolicy::NoSync),
_ => None,
}
}
}
pub enum TransSchedPolicy {
FIFO = mco_trans_sched_policy::MCO_SCHED_FIFO as isize,
ReaderFavor = mco_trans_sched_policy::MCO_SCHED_READER_FAVOR as isize,
WriterFavor = mco_trans_sched_policy::MCO_SCHED_WRITER_FAVOR as isize,
}
impl TransSchedPolicy {
fn from_mco(p: mco_trans_sched_policy::Type) -> Option<Self> {
match p {
mco_trans_sched_policy::MCO_SCHED_FIFO => Some(TransSchedPolicy::FIFO),
mco_trans_sched_policy::MCO_SCHED_READER_FAVOR => Some(TransSchedPolicy::ReaderFavor),
mco_trans_sched_policy::MCO_SCHED_WRITER_FAVOR => Some(TransSchedPolicy::WriterFavor),
_ => None,
}
}
}
pub struct LogParams {
p: exdb_sys::mco_log_params_t,
}
impl LogParams {
pub fn new() -> Self {
LogParams {
p: exdb_sys::mco_log_params_t {
default_commit_policy: mco_commit_policy::MCO_COMMIT_SYNC_FLUSH,
redo_log_limit: 16 * 1024 * 1024,
delayed_commit_threshold: 0,
max_delayed_transactions: 0,
max_commit_delay: 0,
},
}
}
fn with_params(p: &exdb_sys::mco_log_params_t) -> Self {
LogParams { p: *p }
}
pub fn default_commit_policy(&mut self, default_commit_policy: CommitPolicy) -> &mut Self {
self.p.default_commit_policy = default_commit_policy as mco_commit_policy::Type;
self
}
pub fn get_default_commit_policy(&self) -> Option<CommitPolicy> {
CommitPolicy::from_mco(self.p.default_commit_policy)
}
db_param_scalar!(
redo_log_limit,
get_redo_log_limit,
usize,
exdb_sys::mco_offs_t
);
db_param_scalar!(
delayed_commit_threshold,
get_delayed_commit_threshold,
usize,
exdb_sys::mco_offs_t
);
db_param_scalar!(
max_delayed_transactions,
get_max_delayed_transactions,
u32,
exdb_sys::mco_counter32_t
);
db_param_scalar!(
max_commit_delay,
get_max_commit_delay,
u32
);
}
macro_rules! bitmask_flag {
($(#[$f_meta:meta])* $flag:ident, $(#[$g_meta:meta])* $getter:ident, $f:expr) => {
$(#[$f_meta])*
pub fn $flag(&mut self, v: bool) -> &mut Self {
self.0.set_bit_value($f, v);
self
}
$(#[$g_meta])*
pub fn $getter(&self) -> bool {
self.0.get_bit_value($f)
}
};
}
pub struct CompressionMask(BitMask32);
impl CompressionMask {
pub fn new() -> Self {
CompressionMask(BitMask32::new())
}
fn from_mco(m: u32) -> Self {
CompressionMask(BitMask32::from(m))
}
bitmask_flag!(
obj_head,
get_obj_head,
mco_compression_mask::MCO_COMPRESSION_OBJ_HEAD as u32
);
bitmask_flag!(
obj_node,
get_obj_node,
mco_compression_mask::MCO_COMPRESSION_OBJ_NODE as u32
);
bitmask_flag!(
blob_head,
get_blob_head,
mco_compression_mask::MCO_COMPRESSION_BLOB_HEAD as u32
);
bitmask_flag!(
blob_tail,
get_blob_tail,
mco_compression_mask::MCO_COMPRESSION_BLOB_TAIL as u32
);
bitmask_flag!(
fixed_rec_set,
get_fixed_rec_set,
mco_compression_mask::MCO_COMPRESSION_FIXEDRECSET as u32
);
}
pub struct ModeMask(BitMask32);
impl ModeMask {
pub fn new() -> Self {
ModeMask(BitMask32::new())
}
fn from_mco(m: u32) -> Self {
ModeMask(BitMask32::from(m))
}
bitmask_flag!(
mode_mvcc_auto_vacuum,
get_mode_mvcc_auto_vacuum,
mco_db_mode_mask::MCO_DB_MODE_MVCC_AUTO_VACUUM as u32
);
bitmask_flag!(
mode_smart_index_insert,
get_mode_smart_index_insert,
mco_db_mode_mask::MCO_DB_MODE_SMART_INDEX_INSERT as u32
);
bitmask_flag!(
open_existing,
get_open_existing,
mco_db_mode_mask::MCO_DB_OPEN_EXISTING as u32
);
bitmask_flag!(
use_crc_check,
get_use_crc_check,
mco_db_mode_mask::MCO_DB_USE_CRC_CHECK as u32
);
bitmask_flag!(
transient,
get_transient,
mco_db_mode_mask::MCO_DB_TRANSIENT as u32
);
bitmask_flag!(
lazy_mem_initialization,
get_lazy_mem_initialization,
mco_db_mode_mask::MCO_DB_LAZY_MEM_INITIALIZATION as u32
);
bitmask_flag!(
mursiw_disk_commit_optimization,
get_mursiw_disk_commit_optimization,
mco_db_mode_mask::MCO_DB_MURSIW_DISK_COMMIT_OPTIMIZATION as u32
);
bitmask_flag!(
bulk_write_modified_pages,
get_bulk_write_modified_pages,
mco_db_mode_mask::MCO_DB_BULK_WRITE_MODIFIED_PAGES as u32
);
bitmask_flag!(
index_preload,
get_index_preload,
mco_db_mode_mask::MCO_DB_INDEX_PRELOAD as u32
);
bitmask_flag!(
disable_nested_transactions,
get_disable_nested_transactions,
mco_db_mode_mask::MCO_DB_DISABLE_NESTED_TRANSACTIONS as u32
);
bitmask_flag!(
disable_implicit_rollback,
get_disable_implicit_rollback,
mco_db_mode_mask::MCO_DB_DISABLE_IMPLICIT_ROLLBACK as u32
);
bitmask_flag!(
inmemory_protection,
get_inmemory_protection,
mco_db_mode_mask::MCO_DB_INMEMORY_PROTECTION as u32
);
bitmask_flag!(
inclusive_btree,
get_inclusive_btree,
mco_db_mode_mask::MCO_DB_INCLUSIVE_BTREE as u32
);
bitmask_flag!(
inmemory_compression,
get_inmemory_compression,
mco_db_mode_mask::MCO_DB_INMEMORY_COMPRESSION as u32
);
bitmask_flag!(
separate_bitmap,
get_separate_bitmap,
mco_db_mode_mask::MCO_DB_SEPARATE_BITMAP as u32
);
bitmask_flag!(
disable_btree_rebalance_on_delete,
get_disable_btree_rebalance_on_delete,
mco_db_mode_mask::MCO_DB_DISABLE_BTREE_REBALANCE_ON_DELETE as u32
);
bitmask_flag!(
auto_rollback_first_phase,
get_auto_rollback_first_phase,
mco_db_mode_mask::MCO_DB_AUTO_ROLLBACK_FIRST_PHASE as u32
);
bitmask_flag!(
mvcc_compatibility_mode,
get_mvcc_compatibility_mode,
mco_db_mode_mask::MCO_DB_MVCC_COMPATIBILITY_MODE as u32
);
bitmask_flag!(
redo_log_optimization,
get_redo_log_optimization,
mco_db_mode_mask::MCO_DB_REDO_LOG_OPTIMIZATION as u32
);
bitmask_flag!(
disable_hot_updates,
get_disable_hot_updates,
mco_db_mode_mask::MCO_DB_DISABLE_HOT_UPDATES as u32
);
bitmask_flag!(
sql_autocheckpoint,
get_sql_autocheckpoint,
mco_db_mode_mask::MCO_DB_SQL_AUTOCHECKPOINT as u32
);
bitmask_flag!(
mode_read_only,
get_mode_read_only,
mco_db_mode_mask::MCO_DB_MODE_READ_ONLY as u32
);
bitmask_flag!(
use_aio,
get_use_aio,
mco_db_mode_mask::MCO_DB_USE_AIO as u32
);
bitmask_flag!(
incremental_backup,
get_incremental_backup,
mco_db_mode_mask::MCO_DB_INCREMENTAL_BACKUP as u32
);
bitmask_flag!(
mvcc_table_level_locking,
get_mvcc_table_level_locking,
mco_db_mode_mask::MCO_DB_MVCC_TABLE_LEVEL_LOCKING as u32
);
bitmask_flag!(
disable_smart_alloc,
get_disable_smart_alloc,
mco_db_mode_mask::MCO_DB_DISABLE_SMART_ALLOC as u32
);
}
pub struct Params {
p: exdb_sys::mco_db_params_t,
}
impl Params {
pub fn new() -> Self {
let mut p = MaybeUninit::uninit();
unsafe {
exdb_sys::mco_db_params_init(p.as_mut_ptr());
}
Params {
p: unsafe { p.assume_init() },
}
}
fn replace_c_string(p: *mut *mut i8, s: Option<&str>) -> Result<()> {
let new_p = match s {
Some(s) => CString::new(s)
.or(Err(Error::new_core(mco_ret::MCO_E_ILLEGAL_PARAM)))?
.into_raw(),
None => ptr::null_mut(),
};
unsafe {
Params::drop_c_string_if_not_null(*p);
*p = new_p;
}
Ok(())
}
fn drop_c_string_if_not_null(p: *mut i8) {
unsafe {
if !p.is_null() {
let cs = CString::from_raw(p);
drop(cs);
}
}
}
fn get_c_string(&self, p: *mut i8) -> Result<Option<&str>> {
if p.is_null() {
Ok(None)
} else {
unsafe {
let s = CStr::from_ptr(p)
.to_str()
.or(Err(Error::new_core(mco_ret::MCO_E_ILLEGAL_PARAM)))?;
Ok(Some(s))
}
}
}
db_param_scalar!(
mem_page_size,
get_mem_page_size,
u16
);
db_param_scalar!(
disk_page_size,
get_disk_page_size,
u32
);
db_param_scalar!(
db_max_connections,
get_db_max_connections,
u32
);
db_param_scalar!(
disk_max_database_size,
get_disk_max_database_size,
usize,
exdb_sys::mco_offs_t
);
db_param_scalar!(
file_extension_quantum,
get_file_extension_quantum,
usize,
exdb_sys::mco_offs_t
);
pub fn db_log_type(&mut self, db_log_type: LogType) -> &mut Self {
self.p.db_log_type = db_log_type as mco_log_type::Type;
self
}
pub fn get_db_log_type(&self) -> Option<LogType> {
LogType::from_mco(self.p.db_log_type)
}
pub fn connection_context_size_for_recovery(&mut self, enable: bool) -> &mut Self {
if enable {
self.p.connection_context_size = mem::size_of::<*const c_void>() as u16;
} else {
self.p.connection_context_size = 0;
}
self
}
pub fn get_connection_context_size(&self) -> u16 {
self.p.connection_context_size
}
db_param_scalar!(
hash_load_factor,
get_hash_load_factor,
u16
);
db_param_scalar!(
index_optimistic_lock_threshold,
get_index_optimistic_lock_threshold,
u16
);
pub fn log_params(&mut self, log_params: LogParams) -> &mut Self {
self.p.log_params = log_params.p;
self
}
pub fn get_log_params(&self) -> LogParams {
LogParams::with_params(&self.p.log_params)
}
pub fn mode_mask(&mut self, mode_mask: ModeMask) -> &mut Self {
self.p.mode_mask = mode_mask.0.bit_mask() as i32;
self
}
pub fn get_mode_mask(&self) -> ModeMask {
ModeMask::from_mco(self.p.mode_mask as u32)
}
db_param_scalar!(
min_conn_local_pages,
get_min_conn_local_pages,
u32,
i32
);
db_param_scalar!(
max_conn_local_pages,
get_max_conn_local_pages,
u32,
i32
);
db_param_scalar!(
allocation_bitmap_caching_priority,
get_allocation_bitmap_caching_priority,
u32,
i32
);
db_param_scalar!(
index_caching_priority,
get_index_caching_priority,
u32,
i32
);
db_param_scalar!(
object_caching_priority,
get_object_caching_priority,
u32,
i32
);
db_param_scalar!(
ddl_dict_size,
get_ddl_dict_size,
usize,
exdb_sys::mco_size_t
);
pub fn cipher_key(&mut self, cipher_key: Option<&str>) -> Result<()> {
Params::replace_c_string(&mut self.p.cipher_key, cipher_key)
}
pub fn get_cipher_key(&self) -> Result<Option<&str>> {
self.get_c_string(self.p.cipher_key)
}
pub fn dynamic_hash(&mut self, dynamic_hash: bool) -> &mut Self {
self.p.dynamic_hash = dynamic_hash as i32;
self
}
pub fn get_dynamic_hash(&self) -> bool {
match self.p.dynamic_hash {
0 => false,
_ => true,
}
}
pub fn license_key(&mut self, license_key: Option<&str>) -> Result<()> {
Params::replace_c_string(&mut self.p.license_key, license_key)
}
pub fn get_license_key(&self) -> Result<Option<&str>> {
self.get_c_string(self.p.license_key)
}
db_param_scalar!(
max_classes,
get_max_classes,
i32
);
db_param_scalar!(
max_indexes,
get_max_indexes,
i32
);
db_param_scalar!(
autocompact_threshold,
get_autocompact_threshold,
usize,
exdb_sys::mco_size_t
);
pub fn trans_sched_policy(&mut self, trans_sched_policy: TransSchedPolicy) -> &mut Self {
self.p.trans_sched_policy = trans_sched_policy as mco_trans_sched_policy::Type;
self
}
pub fn get_trans_sched_policy(&self) -> Option<TransSchedPolicy> {
TransSchedPolicy::from_mco(self.p.trans_sched_policy)
}
db_param_scalar!(
max_trans_time,
get_max_trans_time,
u64
);
db_param_scalar!(
max_active_pages,
get_max_active_pages,
u32,
i32
);
db_param_scalar!(
page_hash_bundles,
get_page_hash_bundles,
u32,
i32
);
db_param_scalar!(
compression_level,
get_compression_level,
i32
);
pub fn compression_mask(&mut self, compression_mask: CompressionMask) -> &mut Self {
self.p.compression_mask = compression_mask.0.bit_mask() as i32;
self
}
pub fn get_compression_mask(&self) -> CompressionMask {
CompressionMask::from_mco(self.p.compression_mask as u32)
}
db_param_scalar!(
expected_compression_ratio,
get_expected_compression_ratio,
u32,
i32
);
db_param_scalar!(
btree_cursor_read_ahead_size,
get_btree_cursor_read_ahead_size,
u8
);
db_param_scalar!(
mvcc_bitmap_size,
get_mvcc_bitmap_size,
u32,
i32
);
db_param_scalar!(
additional_heap_size,
get_additional_heap_size,
u32,
i32
);
db_param_scalar!(
cow_pagemap_size,
get_cow_pagemap_size,
usize,
exdb_sys::mco_size_t
);
db_param_scalar!(
backup_map_size,
get_backup_map_size,
usize,
exdb_sys::mco_size_t
);
db_param_scalar!(
backup_min_pages,
get_backup_min_pages,
u32
);
db_param_scalar!(
backup_max_passes,
get_backup_max_passes,
u32
);
pub fn backup_map_filename(&mut self, backup_map_filename: &str) -> Result<()> {
if backup_map_filename.len() >= self.p.backup_map_filename.len() {
return Err(Error::new_core(mco_ret::MCO_E_ILLEGAL_PARAM));
}
unsafe {
ptr::copy_nonoverlapping(
backup_map_filename.as_ptr(),
self.p.backup_map_filename.as_mut_ptr() as *mut u8,
backup_map_filename.len(),
)
}
Ok(())
}
pub fn get_backup_map_filename(&self) -> Result<&str> {
if self.p.backup_map_filename[0] == 0 {
Ok("")
} else {
let cstr = unsafe { CStr::from_ptr(self.p.backup_map_filename.as_ptr()) };
cstr.to_str()
.or(Err(Error::new_core(mco_ret::MCO_E_ILLEGAL_PARAM)))
}
}
db_param_scalar!(
iot_agent_id,
get_iot_agent_id,
u64
);
db_param_scalar!(
iot_level,
get_iot_level,
u16
);
db_param_scalar!(
file_backup_delay,
get_file_backup_delay,
u32
);
}
impl Drop for Params {
fn drop(&mut self) {
Params::drop_c_string_if_not_null(self.p.cipher_key);
Params::drop_c_string_if_not_null(self.p.license_key);
}
}
pub struct Database<'a> {
runtime: PhantomData<&'a Runtime>,
devices: PhantomData<&'a mut Vec<Device>>, name: CString,
}
impl<'a> Database<'a> {
pub fn open(
_runtime: &'a Runtime,
name: &str,
dict: Option<&'a dict::Dictionary>,
devs: &'a mut Vec<Device>,
params: Params,
) -> Result<Self> {
if !name.is_ascii() {
return Err(Error::new_core(mco_ret::MCO_E_ILLEGAL_PARAM));
}
let cname = CString::new(name).unwrap();
let mut params = params;
let dict_p = match dict {
Some(d) => &d.nested as *const exdb_sys::mco_dictionary_t,
None => ptr::null_mut(),
};
result_from_code(unsafe {
exdb_sys::mco_db_open_dev(
cname.as_ptr(),
dict_p as *mut exdb_sys::mco_dictionary_t,
devs.as_mut_ptr() as *mut exdb_sys::mco_device_t,
devs.len() as exdb_sys::mco_size_t,
&mut params.p,
)
})?;
Ok(Database {
runtime: PhantomData,
devices: PhantomData,
name: cname,
})
}
pub unsafe fn kill(name: &str) -> Result<()> {
if !name.is_ascii() {
Err(Error::new_core(mco_ret::MCO_E_ILLEGAL_PARAM))
} else {
let cname = CString::new(name).unwrap();
result_from_code(exdb_sys::mco_db_kill(cname.as_ptr()))
}
}
pub fn name(&self) -> &CStr {
&self.name
}
}
impl<'a> Drop for Database<'a> {
fn drop(&mut self) {
let rc = unsafe { exdb_sys::mco_db_close(self.name.as_ptr()) };
debug_assert_eq!(mco_ret::MCO_S_OK, rc);
}
}