Basic DCL impls

next
Sayan Nandan 1 year ago
parent 597a49a91b
commit 6a01d9d513
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

@ -52,11 +52,7 @@ impl PrimaryIndex {
pub fn remove<'a>(&self, key: Lit<'a>, g: &Guard) -> bool {
self.data.mt_delete(&key, g)
}
pub fn select<'a, 'v, 't: 'v, 'g: 't>(
&'t self,
key: Lit<'a>,
g: &'g Guard,
) -> Option<&'v Row> {
pub fn select<'a, 'v, 't: 'v, 'g: 't>(&'t self, key: Lit<'a>, g: &'g Guard) -> Option<&'v Row> {
self.data.mt_get_element(&key, g)
}
pub fn __raw_index(&self) -> &IndexMTRaw<row::Row> {

@ -45,6 +45,15 @@ pub enum DictEntryGeneric {
Map(DictGeneric),
}
impl DictEntryGeneric {
pub fn as_dict_mut(&mut self) -> Option<&mut DictGeneric> {
match self {
Self::Map(m) => Some(m),
_ => None,
}
}
}
/*
patchsets
*/

@ -93,6 +93,7 @@ pub enum Error {
TransactionalError,
/// storage subsystem error
StorageSubsystemError,
SysAuthError,
}
direct_from! {

@ -24,24 +24,134 @@
*
*/
use crate::engine::storage::v1::header_meta::HostRunMode;
use {
crate::engine::{
error::{Error, QueryResult},
storage::v1::header_meta::HostRunMode,
},
parking_lot::RwLock,
std::collections::{hash_map::Entry, HashMap},
};
pub struct ServerConfig {
host_settings_version: u32,
host_run_mode: HostRunMode,
host_startup_counter: u64,
#[derive(Debug)]
/// The global system configuration
pub struct SysConfig {
auth_data: RwLock<Option<SysAuth>>,
host_data: SysHostData,
}
impl ServerConfig {
pub fn new(
host_settings_version: u32,
host_run_mode: HostRunMode,
host_startup_counter: u64,
) -> Self {
impl SysConfig {
/// Initialize a new system config
pub fn new(auth_data: RwLock<Option<SysAuth>>, host_data: SysHostData) -> Self {
Self {
host_settings_version,
host_run_mode,
host_startup_counter,
auth_data,
host_data,
}
}
#[cfg(test)]
/// A test-mode default setting with auth disabled
pub(super) fn test_default() -> Self {
Self {
auth_data: RwLock::new(None),
host_data: SysHostData::new(0, HostRunMode::Prod, 0),
}
}
/// Returns a handle to the authentication data
pub fn auth_data(&self) -> &RwLock<Option<SysAuth>> {
&self.auth_data
}
/// Returns a reference to host data
pub fn host_data(&self) -> &SysHostData {
&self.host_data
}
}
#[derive(Debug, PartialEq)]
/// The host data section (system.host)
pub struct SysHostData {
startup_counter: u64,
run_mode: HostRunMode,
settings_version: u32,
}
impl SysHostData {
/// New [`SysHostData`]
pub fn new(startup_counter: u64, run_mode: HostRunMode, settings_version: u32) -> Self {
Self {
startup_counter,
run_mode,
settings_version,
}
}
pub fn startup_counter(&self) -> u64 {
self.startup_counter
}
pub fn run_mode(&self) -> HostRunMode {
self.run_mode
}
pub fn settings_version(&self) -> u32 {
self.settings_version
}
}
/*
auth
*/
#[derive(Debug, PartialEq)]
/// The auth data section (system.auth)
pub struct SysAuth {
root_key: Box<[u8]>,
users: HashMap<Box<str>, SysAuthUser>,
}
impl SysAuth {
/// New [`SysAuth`] with the given settings
pub fn new(root_key: Box<[u8]>, users: HashMap<Box<str>, SysAuthUser>) -> Self {
Self { root_key, users }
}
/// Create a new user with the given details
pub fn create_new_user(&mut self, username: &str, password: &str) -> QueryResult<()> {
match self.users.entry(username.into()) {
Entry::Vacant(ve) => {
ve.insert(SysAuthUser::new(
rcrypt::hash(password, rcrypt::DEFAULT_COST)
.unwrap()
.into_boxed_slice(),
));
Ok(())
}
Entry::Occupied(_) => Err(Error::SysAuthError),
}
}
/// Verify the user with the given details
pub fn verify_user(&self, username: &str, password: &str) -> QueryResult<()> {
match self.users.get(username) {
Some(user) if rcrypt::verify(password, user.key()).unwrap() => Ok(()),
Some(_) | None => Err(Error::SysAuthError),
}
}
pub fn root_key(&self) -> &[u8] {
&self.root_key
}
pub fn users(&self) -> &HashMap<Box<str>, SysAuthUser> {
&self.users
}
}
#[derive(Debug, PartialEq)]
/// The auth user
pub struct SysAuthUser {
key: Box<[u8]>,
}
impl SysAuthUser {
/// Create a new [`SysAuthUser`]
fn new(key: Box<[u8]>) -> Self {
Self { key }
}
/// Get the key
pub fn key(&self) -> &[u8] {
self.key.as_ref()
}
}

@ -43,7 +43,7 @@ mod mgr;
pub mod test_utils;
mod util;
pub use {
config::ServerConfig,
config::SysConfig,
drivers::FractalModelDriver,
mgr::{CriticalTask, GenericTask, Task},
util::FractalToken,
@ -71,7 +71,7 @@ pub struct GlobalStateStart {
/// Must be called iff this is the only thread calling it
pub unsafe fn enable_and_start_all(
gns: GlobalNS,
config: config::ServerConfig,
config: config::SysConfig,
gns_driver: GNSTransactionDriverAnyFS<LocalFS>,
model_drivers: ModelDrivers,
) -> GlobalStateStart {
@ -124,6 +124,8 @@ pub trait GlobalInstanceLike {
observed_len,
)))
}
// config handle
fn sys_cfg(&self) -> &config::SysConfig;
}
impl GlobalInstanceLike for Global {
@ -146,6 +148,10 @@ impl GlobalInstanceLike for Global {
fn get_max_delta_size(&self) -> usize {
self._get_max_delta_size()
}
// sys
fn sys_cfg(&self) -> &config::SysConfig {
&self.get_state().config
}
}
#[derive(Debug, Clone, Copy)]
@ -194,7 +200,7 @@ struct GlobalState {
gns_driver: drivers::FractalGNSDriver,
mdl_driver: RwLock<ModelDrivers>,
task_mgr: mgr::FractalMgr,
config: config::ServerConfig,
config: config::SysConfig,
}
impl GlobalState {
@ -203,7 +209,7 @@ impl GlobalState {
gns_driver: drivers::FractalGNSDriver,
mdl_driver: RwLock<ModelDrivers>,
task_mgr: mgr::FractalMgr,
config: config::ServerConfig,
config: config::SysConfig,
) -> Self {
Self {
gns,

@ -25,7 +25,7 @@
*/
use {
super::{CriticalTask, GenericTask, GlobalInstanceLike, Task},
super::{CriticalTask, GenericTask, GlobalInstanceLike, SysConfig, Task},
crate::engine::{
core::GlobalNS,
storage::v1::{
@ -45,6 +45,7 @@ pub struct TestGlobal<Fs: RawFSInterface = VirtualFS> {
lp_queue: RwLock<Vec<Task<GenericTask>>>,
max_delta_size: usize,
txn_driver: Mutex<GNSTransactionDriverAnyFS<Fs>>,
sys_cfg: super::SysConfig,
}
impl<Fs: RawFSInterface> TestGlobal<Fs> {
@ -59,6 +60,7 @@ impl<Fs: RawFSInterface> TestGlobal<Fs> {
lp_queue: RwLock::default(),
max_delta_size,
txn_driver: Mutex::new(txn_driver),
sys_cfg: SysConfig::test_default(),
}
}
}
@ -110,6 +112,9 @@ impl<Fs: RawFSInterface> GlobalInstanceLike for TestGlobal<Fs> {
fn get_max_delta_size(&self) -> usize {
100
}
fn sys_cfg(&self) -> &super::config::SysConfig {
&self.sys_cfg
}
}
impl<Fs: RawFSInterface> Drop for TestGlobal<Fs> {

@ -0,0 +1,140 @@
/*
* Created on Thu Sep 21 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use crate::engine::{
data::{
tag::{DataTag, TagClass},
DictGeneric,
},
error::{Error, QueryResult},
ql::{
ast::{traits, QueryData, State},
ddl::syn,
},
};
fn parse<'a, Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<UserMeta<'a>> {
/*
user add [username] with { password: [password], ... }
^cursor
7 tokens
*/
if state.remaining() < 7 {
return Err(Error::QLInvalidSyntax);
}
let token_buffer = state.current();
// initial sig
let signature_okay = token_buffer[0].is_lit()
& token_buffer[1].eq(&Token![with])
& token_buffer[2].eq(&Token![open {}]);
// get props
state.poison_if_not(signature_okay);
state.cursor_ahead_by(2);
let Some(dict) = syn::parse_dict(state) else {
return Err(Error::QLInvalidCollectionSyntax);
};
let maybe_username = unsafe {
// UNSAFE(@ohsayan): the dict parse ensures state correctness
token_buffer[0].uck_read_lit()
};
state.poison_if_not(maybe_username.kind().tag_class() == TagClass::Str);
if state.not_exhausted() & !state.okay() {
// we shouldn't have more tokens
return Err(Error::QLInvalidSyntax);
}
Ok(UserMeta {
username: unsafe {
// UNSAFE(@ohsayan): +tagck in state
maybe_username.str()
},
options: dict,
})
}
struct UserMeta<'a> {
username: &'a str,
options: DictGeneric,
}
#[derive(Debug, PartialEq)]
pub struct UserAdd<'a> {
username: &'a str,
options: DictGeneric,
}
impl<'a> UserAdd<'a> {
pub(in crate::engine::ql) fn new(username: &'a str, options: DictGeneric) -> Self {
Self { username, options }
}
/// Parse a `user add` DCL command
///
/// MUSTENDSTREAM: YES
pub fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
parse(state).map(|UserMeta { username, options }: UserMeta| Self::new(username, options))
}
}
impl<'a> traits::ASTNode<'a> for UserAdd<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
Self::parse(state)
}
}
#[derive(Debug, PartialEq)]
pub struct UserDel<'a> {
username: &'a str,
}
impl<'a> UserDel<'a> {
pub(in crate::engine::ql) fn new(username: &'a str) -> Self {
Self { username }
}
/// Parse a `user del` DCL command
///
/// MUSTENDSTREAM: YES
pub fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
if state.can_read_lit_rounded() & (state.remaining() == 1) {
let lit = unsafe {
// UNSAFE(@ohsayan): +boundck
state.read_cursor_lit_unchecked()
};
state.cursor_ahead();
if lit.kind().tag_class() == TagClass::Str {
return Ok(Self::new(unsafe {
// UNSAFE(@ohsayan): +tagck
lit.str()
}));
}
}
Err(Error::QLInvalidSyntax)
}
}
impl<'a> traits::ASTNode<'a> for UserDel<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
Self::parse(state)
}
}

@ -231,6 +231,16 @@ pub(super) fn rfold_dict<'a, Qd: QueryData<'a>>(
_rfold_dict::<Qd, NoBreakpoint>(mstate, state, dict);
}
pub fn parse_dict<'a, Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> Option<DictGeneric> {
let mut d = DictGeneric::new();
rfold_dict(DictFoldState::OB, state, &mut d);
if state.okay() {
Some(d)
} else {
None
}
}
pub(super) fn rfold_tymeta<'a, Qd: QueryData<'a>>(
mstate: DictFoldState,
state: &mut State<'a, Qd>,

@ -339,3 +339,10 @@ macro_rules! into_vec {
v
}}
}
#[cfg(test)]
macro_rules! lit {
($lit:expr) => {
$crate::engine::data::lit::Lit::from($lit)
};
}

@ -30,6 +30,7 @@ pub(super) mod ast;
#[cfg(feature = "nightly")]
#[cfg(test)]
mod benches;
pub(super) mod dcl;
pub(super) mod ddl;
pub(super) mod dml;
pub(super) mod lex;

@ -33,6 +33,7 @@ use {
rand::{self, Rng},
};
mod dcl;
mod dml_tests;
mod entity;
mod lexer_tests;

@ -0,0 +1,44 @@
/*
* Created on Fri Sep 22 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use crate::engine::ql::{ast, dcl, tests::lex_insecure};
#[test]
fn create_user_simple() {
let query = lex_insecure(b"create user 'sayan' with { password: 'mypass123' }").unwrap();
let q = ast::parse_ast_node_full::<dcl::UserAdd>(&query[2..]).unwrap();
assert_eq!(
q,
dcl::UserAdd::new("sayan", into_dict!("password" => lit!("mypass123")))
)
}
#[test]
fn delete_user_simple() {
let query = lex_insecure(b"delete user 'monster'").unwrap();
let q = ast::parse_ast_node_full::<dcl::UserDel>(&query[2..]).unwrap();
assert_eq!(q, dcl::UserDel::new("monster"))
}

@ -73,6 +73,7 @@ mod dr;
pub enum FileScope {
Journal = 0,
DataBatch = 1,
FlatmapData = 2,
}
impl FileScope {
@ -80,6 +81,7 @@ impl FileScope {
Some(match id {
0 => Self::Journal,
1 => Self::DataBatch,
2 => Self::FlatmapData,
_ => return None,
})
}
@ -96,6 +98,7 @@ impl FileScope {
pub enum FileSpecifier {
GNSTxnLog = 0,
TableDataBatch = 1,
SysDB = 2,
#[cfg(test)]
TestTransactionLog = 0xFF,
}
@ -105,6 +108,7 @@ impl FileSpecifier {
Some(match v {
0 => Self::GNSTxnLog,
1 => Self::TableDataBatch,
2 => Self::SysDB,
#[cfg(test)]
0xFF => Self::TestTransactionLog,
_ => return None,

@ -37,7 +37,10 @@ use {
},
parking_lot::RwLock,
std::{
collections::{hash_map::Entry, HashMap},
collections::{
hash_map::{Entry, OccupiedEntry},
HashMap,
},
io::{Error, ErrorKind},
},
};
@ -74,6 +77,23 @@ impl VNode {
}
}
/*
errors
*/
fn err_item_is_not_file<T>() -> super::SDSSResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "found directory, not a file").into())
}
fn err_file_in_dir_path<T>() -> super::SDSSResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "found file in directory path").into())
}
fn err_dir_missing_in_path<T>() -> super::SDSSResult<T> {
Err(Error::new(ErrorKind::InvalidInput, "could not find directory in path").into())
}
fn err_could_not_find_item<T>() -> super::SDSSResult<T> {
Err(Error::new(ErrorKind::NotFound, "could not find item").into())
}
/*
vfs impl:
- nested directory structure
@ -97,6 +117,32 @@ pub struct VirtualFS;
impl RawFSInterface for VirtualFS {
type File = VFileDescriptor;
fn fs_rename_file(from: &str, to: &str) -> SDSSResult<()> {
// get file data
let data = with_file(from, |f| Ok(f.data.clone()))?;
// create new file
let file = VirtualFS::fs_fopen_or_create_rw(to)?;
match file {
RawFileOpen::Created(mut c) => {
c.fw_write_all(&data)?;
}
RawFileOpen::Existing(mut e) => {
e.fw_truncate_to(0)?;
e.fw_write_all(&data)?;
}
}
// delete old file
Self::fs_remove_file(from)
}
fn fs_remove_file(fpath: &str) -> SDSSResult<()> {
handle_item_mut(fpath, |e| match e.get() {
VNode::File(_) => {
e.remove();
Ok(())
}
_ => return err_item_is_not_file(),
})
}
fn fs_create_dir(fpath: &str) -> super::SDSSResult<()> {
// get vfs
let mut vfs = VFS.write();
@ -109,14 +155,8 @@ impl RawFSInterface for VirtualFS {
Some(VNode::Dir(d)) => {
current = d;
}
Some(VNode::File(_)) => {
return Err(Error::new(ErrorKind::InvalidInput, "found file in path").into())
}
None => {
return Err(
Error::new(ErrorKind::NotFound, "could not find directory in path").into(),
)
}
Some(VNode::File(_)) => return err_file_in_dir_path(),
None => return err_dir_missing_in_path(),
}
}
match current.entry(target.into()) {
@ -146,9 +186,7 @@ impl RawFSInterface for VirtualFS {
}
return create_ahead(ahead, d);
}
Some(VNode::File(_)) => {
return Err(Error::new(ErrorKind::InvalidInput, "found file in path").into())
}
Some(VNode::File(_)) => return err_file_in_dir_path(),
None => {
let _ = current.insert(this.into(), VNode::Dir(into_dict!()));
let dir = current.get_mut(this).unwrap().as_dir_mut().unwrap();
@ -177,11 +215,7 @@ impl RawFSInterface for VirtualFS {
f.write = true;
Ok(RawFileOpen::Existing(VFileDescriptor(fpath.into())))
}
VNode::Dir(_) => {
return Err(
Error::new(ErrorKind::InvalidInput, "found directory, not a file").into(),
)
}
VNode::Dir(_) => return err_item_is_not_file(),
},
Entry::Vacant(v) => {
v.insert(VNode::File(VFile::new(true, true, vec![], 0)));
@ -198,14 +232,8 @@ fn find_target_dir_mut<'a>(
for component in components {
match current.get_mut(component) {
Some(VNode::Dir(d)) => current = d,
Some(VNode::File(_)) => {
return Err(Error::new(ErrorKind::InvalidInput, "found file in path").into())
}
None => {
return Err(
Error::new(ErrorKind::NotFound, "could not find directory in path").into(),
)
}
Some(VNode::File(_)) => return err_file_in_dir_path(),
None => return err_dir_missing_in_path(),
}
}
Ok(current)
@ -218,20 +246,17 @@ fn find_target_dir<'a>(
for component in components {
match current.get(component) {
Some(VNode::Dir(d)) => current = d,
Some(VNode::File(_)) => {
return Err(Error::new(ErrorKind::InvalidInput, "found file in path").into())
}
None => {
return Err(
Error::new(ErrorKind::NotFound, "could not find directory in path").into(),
)
}
Some(VNode::File(_)) => return err_file_in_dir_path(),
None => return err_dir_missing_in_path(),
}
}
Ok(current)
}
fn delete_dir(fpath: &str, allow_if_non_empty: bool) -> Result<(), super::SDSSError> {
fn handle_item_mut<T>(
fpath: &str,
f: impl Fn(OccupiedEntry<Box<str>, VNode>) -> super::SDSSResult<T>,
) -> super::SDSSResult<T> {
let mut vfs = VFS.write();
let mut current = &mut *vfs;
// process components
@ -241,33 +266,45 @@ fn delete_dir(fpath: &str, allow_if_non_empty: bool) -> Result<(), super::SDSSEr
Some(VNode::Dir(dir)) => {
current = dir;
}
Some(VNode::File(_)) => {
return Err(Error::new(ErrorKind::InvalidInput, "found file in path").into())
}
None => {
return Err(
Error::new(ErrorKind::NotFound, "could not find directory in path").into(),
)
}
Some(VNode::File(_)) => return err_file_in_dir_path(),
None => return err_dir_missing_in_path(),
}
}
match current.entry(target.into()) {
Entry::Occupied(dir) => match dir.get() {
VNode::Dir(d) => {
if allow_if_non_empty || d.is_empty() {
dir.remove();
return Ok(());
}
return Err(Error::new(ErrorKind::InvalidInput, "directory is not empty").into());
}
VNode::File(_) => {
return Err(Error::new(ErrorKind::InvalidInput, "found file in path").into())
Entry::Occupied(item) => return f(item),
Entry::Vacant(_) => return err_could_not_find_item(),
}
}
fn handle_item<T>(fpath: &str, f: impl Fn(&VNode) -> super::SDSSResult<T>) -> super::SDSSResult<T> {
let vfs = VFS.read();
let mut current = &*vfs;
// process components
let (target, components) = split_target_and_components(fpath);
for component in components {
match current.get(component) {
Some(VNode::Dir(dir)) => {
current = dir;
}
},
Entry::Vacant(_) => {
return Err(Error::new(ErrorKind::NotFound, "could not find directory in path").into())
Some(VNode::File(_)) => return err_file_in_dir_path(),
None => return err_dir_missing_in_path(),
}
}
match current.get(target) {
Some(item) => return f(item),
None => return err_could_not_find_item(),
}
}
fn delete_dir(fpath: &str, allow_if_non_empty: bool) -> Result<(), super::SDSSError> {
handle_item_mut(fpath, |node| match node.get() {
VNode::Dir(d) => {
if allow_if_non_empty || d.is_empty() {
node.remove();
return Ok(());
}
return Err(Error::new(ErrorKind::InvalidInput, "directory is not empty").into());
}
VNode::File(_) => return err_file_in_dir_path(),
})
}
/*
@ -318,9 +355,7 @@ fn with_file_mut<T>(fpath: &str, mut f: impl FnMut(&mut VFile) -> SDSSResult<T>)
let target_dir = find_target_dir_mut(components, &mut vfs)?;
match target_dir.get_mut(target_file) {
Some(VNode::File(file)) => f(file),
Some(VNode::Dir(_)) => {
return Err(Error::new(ErrorKind::InvalidInput, "found directory, not a file").into())
}
Some(VNode::Dir(_)) => return err_item_is_not_file(),
None => return Err(Error::from(ErrorKind::NotFound).into()),
}
}
@ -331,9 +366,7 @@ fn with_file<T>(fpath: &str, mut f: impl FnMut(&VFile) -> SDSSResult<T>) -> SDSS
let target_dir = find_target_dir(components, &vfs)?;
match target_dir.get(target_file) {
Some(VNode::File(file)) => f(file),
Some(VNode::Dir(_)) => {
return Err(Error::new(ErrorKind::InvalidInput, "found directory, not a file").into())
}
Some(VNode::Dir(_)) => return err_item_is_not_file(),
None => return Err(Error::from(ErrorKind::NotFound).into()),
}
}
@ -445,6 +478,12 @@ pub struct NullFS;
pub struct NullFile;
impl RawFSInterface for NullFS {
type File = NullFile;
fn fs_rename_file(_: &str, _: &str) -> SDSSResult<()> {
Ok(())
}
fn fs_remove_file(_: &str) -> SDSSResult<()> {
Ok(())
}
fn fs_create_dir(_: &str) -> SDSSResult<()> {
Ok(())
}

@ -31,6 +31,7 @@ mod batch_jrnl;
mod journal;
mod loader;
mod rw;
mod sysdb;
// hl
pub mod inf;
mod start_stop;
@ -94,7 +95,7 @@ pub enum SDSSError {
/// A corrupted file
CorruptedFile(&'static str),
// process errors
StartupError(&'static str),
OtherError(&'static str),
// header
/// The entire header is corrupted
HeaderDecodeCorruptedHeader,

@ -74,6 +74,8 @@ pub enum RawFileOpen<F> {
pub trait RawFSInterface {
const NOT_NULL: bool = true;
type File: RawFileInterface;
fn fs_remove_file(fpath: &str) -> SDSSResult<()>;
fn fs_rename_file(from: &str, to: &str) -> SDSSResult<()>;
fn fs_create_dir(fpath: &str) -> SDSSResult<()>;
fn fs_create_dir_all(fpath: &str) -> SDSSResult<()>;
fn fs_delete_dir(fpath: &str) -> SDSSResult<()>;
@ -141,6 +143,12 @@ pub struct LocalFS;
impl RawFSInterface for LocalFS {
type File = File;
fn fs_remove_file(fpath: &str) -> SDSSResult<()> {
cvt(fs::remove_file(fpath))
}
fn fs_rename_file(from: &str, to: &str) -> SDSSResult<()> {
cvt(fs::rename(from, to))
}
fn fs_create_dir(fpath: &str) -> SDSSResult<()> {
cvt(fs::create_dir(fpath))
}

@ -122,7 +122,7 @@ impl StartStop {
(ReadNX::Read(_, time_start), ReadNX::Read(_, time_stop))
if time_start == time_stop => {}
(ReadNX::Created(_), ReadNX::Created(_)) => {}
_ => return Err(SDSSError::StartupError(EMSG_FAILED_VERIFY)),
_ => return Err(SDSSError::OtherError(EMSG_FAILED_VERIFY)),
}
start_file
.file_mut()

@ -0,0 +1,96 @@
/*
* Created on Fri Sep 22 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use crate::engine::{
data::{cell::Datacell, DictEntryGeneric, DictGeneric},
fractal::SysConfig,
storage::v1::{
header_meta::{FileScope, FileSpecifier, FileSpecifierVersion},
RawFSInterface, SDSSError, SDSSFileIO, SDSSResult,
},
};
const SYSDB_PATH: &str = "sys.db";
const SYSDB_COW_PATH: &str = "sys.db.cow";
pub fn sync_system_database<Fs: RawFSInterface>(cfg: &SysConfig) -> SDSSResult<()> {
// get auth data
let auth_data = cfg.auth_data().read();
// prepare our flat file
let mut map: DictGeneric = into_dict!(
"host" => DictEntryGeneric::Map(into_dict!(
"settings_version" => Datacell::new_uint(cfg.host_data().settings_version() as _),
"startup_counter" => Datacell::new_uint(cfg.host_data().startup_counter() as _),
)),
"auth" => DictGeneric::new(),
);
let auth_key = map.get_mut("auth").unwrap();
match &*auth_data {
None => *auth_key = Datacell::null().into(),
Some(auth) => {
let auth_key = auth_key.as_dict_mut().unwrap();
auth_key.insert(
"root".into(),
DictEntryGeneric::Data(Datacell::new_bin(auth.root_key().into())),
);
auth_key.insert(
"users".into(),
DictEntryGeneric::Map(
auth.users()
.iter()
.map(|(username, user)| {
(
username.to_owned(),
DictEntryGeneric::Data(Datacell::new_list(vec![
Datacell::new_bin(user.key().into()),
])),
)
})
.collect(),
),
);
}
}
// open file
let mut file = SDSSFileIO::<Fs>::open_or_create_perm_rw::<false>(
SYSDB_COW_PATH,
FileScope::FlatmapData,
FileSpecifier::SysDB,
FileSpecifierVersion::__new(0),
cfg.host_data().settings_version(),
cfg.host_data().run_mode(),
cfg.host_data().startup_counter(),
)?
.into_created()
.ok_or(SDSSError::OtherError(
"sys.db.cow already exists. please remove this file.",
))?;
// write
let buf = super::inf::enc::enc_dict_full::<super::inf::map::GenericDictSpec>(&map);
file.fsynced_write(&buf)?;
// replace
Fs::fs_rename_file(SYSDB_COW_PATH, SYSDB_PATH)
}
Loading…
Cancel
Save