Add inspection queries

This lets the user explore a keyspace/table.
next
Sayan Nandan 3 years ago
parent c36cbe69e4
commit c9e55451f3

@ -288,7 +288,7 @@ impl<T, const N: usize> Array<T, N> {
// of the array // of the array
/// Get self as a slice. Super safe because we guarantee that all the other invarians /// Get self as a slice. Super safe because we guarantee that all the other invarians
/// are upheld /// are upheld
fn as_slice(&self) -> &[T] { pub fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) } unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
} }
/// Get self as a mutable slice. Super safe (see comment above) /// Get self as a mutable slice. Super safe (see comment above)

@ -164,6 +164,9 @@ impl Corestore {
} }
Ok(()) Ok(())
} }
pub fn get_keyspace(&self, ksid: ObjectID) -> Option<Arc<Keyspace>> {
self.store.get_keyspace_atomic_ref(&ksid)
}
/// Get an atomic reference to a table /// Get an atomic reference to a table
pub fn get_table(&self, entity: EntityGroup) -> KeyspaceResult<Arc<Table>> { pub fn get_table(&self, entity: EntityGroup) -> KeyspaceResult<Arc<Table>> {
match entity { match entity {

@ -62,6 +62,16 @@ impl Table {
DataModel::KV(kv) => kv.len(), DataModel::KV(kv) => kv.len(),
} }
} }
/// Returns this table's _description_
pub fn describe_self(&self) -> &'static str {
match self.get_model_code() {
0 => r#"KeyValue(binstr,binstr)"#,
1 => r#"KeyValue(binstr,str)"#,
2 => r#"KeyValue(str,str)"#,
3 => r#"KeyValue(str,binstr)"#,
_ => unsafe { impossible!() },
}
}
pub fn truncate_table(&self) { pub fn truncate_table(&self) {
match self.model_store { match self.model_store {
DataModel::KV(ref kv) => kv.truncate_table(), DataModel::KV(ref kv) => kv.truncate_table(),

@ -158,6 +158,7 @@ pub fn pre_shutdown_cleanup(pid_file: fs::File, mr: Option<&Memstore>) {
process::exit(0x01); process::exit(0x01);
} }
if let Some(mr) = mr { if let Some(mr) = mr {
log::info!("Compacting tree");
if let Err(e) = storage::interface::cleanup_tree(mr) { if let Err(e) = storage::interface::cleanup_tree(mr) {
log::error!("Failed to compact tree: {}", e); log::error!("Failed to compact tree: {}", e);
process::exit(0x01); process::exit(0x01);

@ -79,6 +79,7 @@ pub mod groups {
pub const TOO_MANY_ARGUMENTS: &[u8] = "!13\ntoo-many-args\n".as_bytes(); pub const TOO_MANY_ARGUMENTS: &[u8] = "!13\ntoo-many-args\n".as_bytes();
pub const CONTAINER_NAME_TOO_LONG: &[u8] = "!23\ncontainer-name-too-long\n".as_bytes(); pub const CONTAINER_NAME_TOO_LONG: &[u8] = "!23\ncontainer-name-too-long\n".as_bytes();
pub const BAD_CONTAINER_NAME: &[u8] = "!18\nbad-container-name\n".as_bytes(); pub const BAD_CONTAINER_NAME: &[u8] = "!18\nbad-container-name\n".as_bytes();
pub const UNKNOWN_INSPECT_QUERY: &[u8] = "!21\nunknown-inspect-query\n".as_bytes();
} }
pub mod full_responses { pub mod full_responses {

@ -32,8 +32,8 @@ use crate::dbnet::connection::prelude::*;
use crate::kvengine::encoding; use crate::kvengine::encoding;
use core::str; use core::str;
const TABLE: &[u8] = "TABLE".as_bytes(); pub const TABLE: &[u8] = "TABLE".as_bytes();
const KEYSPACE: &[u8] = "KEYSPACE".as_bytes(); pub const KEYSPACE: &[u8] = "KEYSPACE".as_bytes();
action!( action!(
/// Handle `create table <tableid> <model>(args)` and `create keyspace <ksid>` /// Handle `create table <tableid> <model>(args)` and `create keyspace <ksid>`

@ -0,0 +1,101 @@
/*
* Created on Tue Jul 27 2021
*
* 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) 2021, 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 super::ddl::{KEYSPACE, TABLE};
use crate::corestore::memstore::ObjectID;
use crate::dbnet::connection::prelude::*;
const KEYSPACES: &[u8] = "KEYSPACES".as_bytes();
action! {
fn inspect(handle: &Corestore, con: &mut T, mut act: ActionIter) {
match act.next() {
Some(inspect_what) => {
let mut inspect_what = inspect_what.to_vec();
inspect_what.make_ascii_uppercase();
match inspect_what.as_ref() {
KEYSPACE => inspect_keyspace(handle, con, act).await?,
TABLE => inspect_table(handle, con, act).await?,
KEYSPACES => {
// let's return what all keyspaces exist
let ks_list: Vec<ObjectID> = handle
.get_store()
.keyspaces
.iter()
.map(|kv| kv.key().clone())
.collect();
con.write_flat_array_length(ks_list.len()).await?;
for tbl in ks_list {
con.write_response(tbl).await?;
}
}
_ => conwrite!(con, responses::groups::UNKNOWN_INSPECT_QUERY)?,
}
}
None => aerr!(con, aerr),
}
Ok(())
}
}
action! {
fn inspect_keyspace(handle: &Corestore, con: &mut T, mut act: ActionIter) {
match act.next() {
Some(keyspace_name) => {
let ksid = if keyspace_name.len() > 64 {
return conwrite!(con, responses::groups::BAD_CONTAINER_NAME);
} else {
unsafe {
ObjectID::from_slice(keyspace_name)
}
};
let ks = match handle.get_keyspace(ksid) {
Some(kspace) => kspace,
None => return conwrite!(con, responses::groups::CONTAINER_NOT_FOUND),
};
let tbl_list: Vec<ObjectID> = ks.tables.iter().map(|kv| kv.key().clone()).collect();
con.write_flat_array_length(tbl_list.len()).await?;
for tbl in tbl_list {
con.write_response(tbl).await?;
}
},
None => aerr!(con, aerr),
}
Ok(())
}
}
action! {
fn inspect_table(handle: &Corestore, con: &mut T, mut act: ActionIter) {
match act.next() {
Some(entity) => {
let entity = handle_entity!(con, entity);
conwrite!(con, get_tbl!(entity, handle, con).describe_self())?;
},
None => aerr!(con, aerr),
}
Ok(())
}
}

@ -34,6 +34,7 @@ use crate::protocol::Element;
use crate::{actions, admin}; use crate::{actions, admin};
use bytes::Bytes; use bytes::Bytes;
mod ddl; mod ddl;
mod inspect;
pub mod parser; pub mod parser;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -105,7 +106,8 @@ where
POP => actions::pop::pop, POP => actions::pop::pop,
CREATE => ddl::create, CREATE => ddl::create,
DROP => ddl::ddl_drop, DROP => ddl::ddl_drop,
USE => self::entity_swap USE => self::entity_swap,
INSPECT => inspect::inspect
); );
Ok(()) Ok(())
} }

@ -27,6 +27,7 @@
//! Utilities for generating responses, which are only used by the `server` //! Utilities for generating responses, which are only used by the `server`
//! //!
use crate::corestore::buffers::Integer64; use crate::corestore::buffers::Integer64;
use crate::corestore::memstore::ObjectID;
use bytes::Bytes; use bytes::Bytes;
use skytable::RespCode; use skytable::RespCode;
use std::future::Future; use std::future::Future;
@ -124,6 +125,33 @@ impl Writable for &'static [u8] {
} }
} }
impl Writable for &'static str {
fn write<'s>(
self,
con: &'s mut impl IsConnection,
) -> Pin<Box<(dyn Future<Output = Result<(), IoError>> + Send + Sync + 's)>> {
async fn write_bytes(con: &mut impl IsConnection, bytes: &str) -> Result<(), IoError> {
// First write a `+` character to the stream since this is a
// string (we represent `String`s as `Byte` objects internally)
// and since `Bytes` are effectively `String`s we will append the
// type operator `+` to the stream
con.write_lowlevel(&[b'+']).await?;
// Now get the size of the Bytes object as bytes
let size = Integer64::from(bytes.len());
// Write this to the stream
con.write_lowlevel(&size).await?;
// Now write a LF character
con.write_lowlevel(&[b'\n']).await?;
// Now write the REAL bytes (of the object)
con.write_lowlevel(bytes.as_bytes()).await?;
// Now write another LF
con.write_lowlevel(&[b'\n']).await?;
Ok(())
}
Box::pin(write_bytes(con, self))
}
}
impl Writable for BytesWrapper { impl Writable for BytesWrapper {
fn write<'s>( fn write<'s>(
self, self,
@ -231,3 +259,30 @@ impl Writable for u64 {
Box::pin(write_bytes(con, self)) Box::pin(write_bytes(con, self))
} }
} }
impl Writable for ObjectID {
fn write<'s>(
self,
con: &'s mut impl IsConnection,
) -> Pin<Box<(dyn Future<Output = Result<(), IoError>> + Send + Sync + 's)>> {
async fn write_bytes(con: &mut impl IsConnection, bytes: ObjectID) -> Result<(), IoError> {
// First write a `+` character to the stream since this is a
// string (we represent `String`s as `Byte` objects internally)
// and since `Bytes` are effectively `String`s we will append the
// type operator `+` to the stream
con.write_lowlevel(&[b'+']).await?;
// Now get the size of the Bytes object as bytes
let size = Integer64::from(bytes.len());
// Write this to the stream
con.write_lowlevel(&size).await?;
// Now write a LF character
con.write_lowlevel(&[b'\n']).await?;
// Now write the REAL bytes (of the object)
con.write_lowlevel(&bytes).await?;
// Now write another LF
con.write_lowlevel(&[b'\n']).await?;
Ok(())
}
Box::pin(write_bytes(con, self))
}
}

Loading…
Cancel
Save