Use BlueQL for all DDL operations

All tests for the same were updated
next
Sayan Nandan 2 years ago
parent aa30822f0e
commit e7f40c575a
No known key found for this signature in database
GPG Key ID: 8BC07A0A4D41DD52

@ -36,7 +36,7 @@ action!(
} else {
let raw_entity = unsafe { act.next().unsafe_unwrap() };
let entity = handle_entity!(con, raw_entity);
con.write_usize(get_tbl!(entity, handle, con).count())
con.write_usize(get_tbl!(&entity, handle, con).count())
.await?;
}
Ok(())

@ -39,7 +39,7 @@ action!(
// flush the entity
let raw_entity = unsafe { act.next_unchecked() };
let entity = handle_entity!(con, raw_entity);
get_tbl!(entity, handle, con).truncate_table();
get_tbl!(&entity, handle, con).truncate_table();
}
con._write_raw(P::RCODE_OKAY).await?;
} else {

@ -50,7 +50,7 @@ action!(
} else {
// sigh, an entity
let entity = handle_entity!(con, nextret);
(get_tbl!(entity, handle, con), DEFAULT_COUNT)
(get_tbl!(&entity, handle, con), DEFAULT_COUNT)
}
} else {
// an entity and a count, gosh this fella is really trying us
@ -62,7 +62,7 @@ action!(
} else {
return util::err(P::RCODE_WRONGTYPE_ERR);
};
(get_tbl!(entity, handle, con), count)
(get_tbl!(&entity, handle, con), count)
};
let tsymbol = match table.get_model_ref() {
DataModel::KV(kv) => kv.get_value_tsymbol(),

@ -74,7 +74,7 @@ macro_rules! get_tbl_ref {
#[macro_export]
macro_rules! handle_entity {
($con:expr, $ident:expr) => {{
match $crate::queryengine::parser::Entity::from_slice::<P>(&$ident) {
match $crate::blueql::util::from_slice_action_result::<P>(&$ident) {
Ok(e) => e,
Err(e) => return Err(e.into()),
}

@ -128,14 +128,6 @@ pub fn ensure_boolean_or_aerr<P: ProtocolSpec>(boolean: bool) -> ActionResult<()
}
}
pub fn ensure_cond_or_err(cond: bool, err: &'static [u8]) -> ActionResult<()> {
if util::compiler::likely(cond) {
Ok(())
} else {
util::err(err)
}
}
pub mod heya {
//! Respond to `HEYA` queries
use crate::dbnet::connection::prelude::*;

@ -72,6 +72,9 @@ pub enum Entity {
impl Entity {
const MAX_LENGTH_EX: usize = 65;
pub fn from_slice(slice: &[u8]) -> LangResult<Self> {
Compiler::new(&Lexer::lex(slice)?).parse_entity_name()
}
}
#[derive(Debug)]

@ -58,7 +58,7 @@ pub type LangResult<T> = Result<T, LangError>;
#[inline(never)]
#[cold]
const fn cold_err<P: ProtocolSpec>(e: LangError) -> &'static [u8] {
pub(super) const fn cold_err<P: ProtocolSpec>(e: LangError) -> &'static [u8] {
match e {
LangError::BadExpression => P::BQL_BAD_EXPRESSION,
LangError::ExpectedStatement => P::BQL_EXPECTED_STMT,

@ -50,6 +50,7 @@ where
let statement = error::map_ql_err_to_resp::<StatementLT, P>(blueql::compile(maybe_statement))?;
let system_health_okay = registry::state_okay();
let result = match statement.as_ref() {
Statement::Use(entity) => handle.swap_entity(entity),
Statement::CreateSpace(space_name) if system_health_okay => {
// ret okay
handle.create_keyspace(unsafe { ObjectID::from_slice(space_name.as_slice()) })
@ -65,7 +66,7 @@ where
}
Statement::DropModel { entity, force } if system_health_okay => {
// ret okay
handle.drop_table(entity.into(), *force)
handle.drop_table(entity, *force)
}
Statement::CreateModel {
entity,
@ -74,7 +75,7 @@ where
} if system_health_okay => {
match model.get_model_code() {
// ret okay
Ok(code) => handle.create_table(entity.into(), code, *volatile),
Ok(code) => handle.create_table(entity, code, *volatile),
Err(e) => return error::map_ql_err_to_resp::<(), P>(Err(e)),
}
}
@ -95,11 +96,10 @@ where
}
Statement::InspectModel(model) => {
// ret directly
con.write_string(&handle.describe_table::<P>(model.as_ref().map(|v| v.into()))?)
con.write_string(&handle.describe_table::<P>(model)?)
.await?;
return Ok(());
}
Statement::Use(entity) => handle.swap_entity(entity.into()),
_ => {
// the server is broken
con._write_raw(P::RCODE_SERVER_ERR).await?;

@ -105,8 +105,8 @@ pub struct TypeExpression(pub Vec<Type>);
impl Keyword {
/// Attempt to parse a keyword from the given slice
#[inline(always)]
pub const fn try_from_slice(slice: &[u8]) -> Option<Self> {
let r = match slice {
pub fn try_from_slice(slice: &[u8]) -> Option<Self> {
let r = match slice.to_ascii_lowercase().as_slice() {
b"create" => Keyword::Create,
b"drop" => Keyword::Drop,
b"inspect" => Keyword::Inspect,

@ -28,6 +28,7 @@ mod ast;
mod error;
mod executor;
mod lexer;
pub mod util;
// test modules
#[cfg(test)]
mod tests;
@ -40,7 +41,7 @@ pub use {ast::Compiler, ast::Entity, executor::execute};
#[cfg(test)]
use core::fmt;
use core::slice;
use core::{mem, slice};
#[allow(clippy::needless_lifetimes)]
#[inline(always)]
@ -73,6 +74,7 @@ impl PartialEq for RawSlice {
}
impl RawSlice {
const _ENSURE_ALIGN: () = assert!(mem::align_of::<RawSlice>() == mem::align_of::<&[u8]>());
pub const unsafe fn new(ptr: *const u8, len: usize) -> Self {
Self { ptr, len }
}

@ -0,0 +1,41 @@
/*
* Created on Mon Jun 20 2022
*
* 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) 2022, 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::{ast::Entity, error},
crate::{
actions::{ActionError, ActionResult},
protocol::interface::ProtocolSpec,
util::Life,
},
};
pub fn from_slice_action_result<P: ProtocolSpec>(slice: &[u8]) -> ActionResult<Life<'_, Entity>> {
match Entity::from_slice(slice) {
Ok(slc) => Ok(Life::new(slc)),
Err(e) => Err(ActionError::ActionError(error::cold_err::<P>(e))),
}
}

@ -27,12 +27,12 @@
use {
crate::{
actions::{translate_ddl_error, ActionResult},
blueql::Entity,
corestore::{
memstore::{DdlError, Keyspace, Memstore, ObjectID, DEFAULT},
table::{DescribeTable, Table},
},
protocol::interface::ProtocolSpec,
queryengine::parser::{Entity, OwnedEntity},
registry,
storage::{
self,
@ -135,39 +135,34 @@ impl Corestore {
///
/// If the table is non-existent or the default keyspace was unset, then
/// false is returned. Else true is returned
pub fn swap_entity(&mut self, entity: Entity<'_>) -> KeyspaceResult<()> {
pub fn swap_entity(&mut self, entity: &Entity) -> KeyspaceResult<()> {
match entity {
// Switch to the provided keyspace
Entity::Single(ks) => match self.store.get_keyspace_atomic_ref(ks) {
Some(ksref) => self
.estate
.set_ks(ksref, unsafe { ObjectID::from_slice(ks) }),
None => return Err(DdlError::ObjectNotFound),
},
Entity::Current(ks) => {
match self.store.get_keyspace_atomic_ref(unsafe { ks.as_slice() }) {
Some(ksref) => self
.estate
.set_ks(ksref, unsafe { ObjectID::from_slice(ks.as_slice()) }),
None => return Err(DdlError::ObjectNotFound),
}
}
// Switch to the provided table in the given keyspace
Entity::Full(ks, tbl) => match self.store.get_keyspace_atomic_ref(ks) {
Some(kspace) => match kspace.get_table_atomic_ref(tbl) {
Some(tblref) => unsafe {
self.estate.set_table(
kspace,
ObjectID::from_slice(ks),
tblref,
ObjectID::from_slice(tbl),
)
Entity::Full(ks, tbl) => {
match self.store.get_keyspace_atomic_ref(unsafe { ks.as_slice() }) {
Some(kspace) => match kspace.get_table_atomic_ref(unsafe { tbl.as_slice() }) {
Some(tblref) => unsafe {
self.estate.set_table(
kspace,
ObjectID::from_slice(ks.as_slice()),
tblref,
ObjectID::from_slice(tbl.as_slice()),
)
},
None => return Err(DdlError::ObjectNotFound),
},
None => return Err(DdlError::ObjectNotFound),
},
None => return Err(DdlError::ObjectNotFound),
},
Entity::Partial(tbl) => match &self.estate.ks {
Some((_, ks)) => match ks.get_table_atomic_ref(tbl) {
Some(tblref) => {
self.estate.table = Some((unsafe { ObjectID::from_slice(tbl) }, tblref));
}
None => return Err(DdlError::ObjectNotFound),
},
None => return Err(DdlError::DefaultNotFound),
},
}
}
}
Ok(())
}
@ -193,17 +188,22 @@ impl Corestore {
self.store.get_keyspace_atomic_ref(ksid)
}
/// Get an atomic reference to a table
pub fn get_table(&self, entity: Entity<'_>) -> KeyspaceResult<Arc<Table>> {
pub fn get_table(&self, entity: &Entity) -> KeyspaceResult<Arc<Table>> {
match entity {
Entity::Full(ksid, table) => match self.store.get_keyspace_atomic_ref(ksid) {
Some(ks) => match ks.get_table_atomic_ref(table) {
Some(tbl) => Ok(tbl),
Entity::Full(ksid, table) => {
match self
.store
.get_keyspace_atomic_ref(unsafe { ksid.as_slice() })
{
Some(ks) => match ks.get_table_atomic_ref(unsafe { table.as_slice() }) {
Some(tbl) => Ok(tbl),
None => Err(DdlError::ObjectNotFound),
},
None => Err(DdlError::ObjectNotFound),
},
None => Err(DdlError::ObjectNotFound),
},
Entity::Single(tbl) | Entity::Partial(tbl) => match &self.estate.ks {
Some((_, ks)) => match ks.get_table_atomic_ref(tbl) {
}
}
Entity::Current(tbl) => match &self.estate.ks {
Some((_, ks)) => match ks.get_table_atomic_ref(unsafe { tbl.as_slice() }) {
Some(tbl) => Ok(tbl),
None => Err(DdlError::ObjectNotFound),
},
@ -231,21 +231,23 @@ impl Corestore {
/// **Trip switch handled:** Yes
pub fn create_table(
&self,
entity: Entity<'_>,
entity: &Entity,
modelcode: u8,
volatile: bool,
) -> KeyspaceResult<()> {
let entity = entity.into_owned();
// first lock the global flush state
let flush_lock = registry::lock_flush_state();
let ret = match entity {
// Important: create table <tblname> is only ks
OwnedEntity::Single(tblid) | OwnedEntity::Partial(tblid) => {
Entity::Current(tblid) => {
match &self.estate.ks {
Some((_, ks)) => {
let tbl = Table::from_model_code(modelcode, volatile);
if let Some(tbl) = tbl {
if ks.create_table(tblid, tbl) {
if ks.create_table(
unsafe { ObjectID::from_slice(tblid.as_slice()) },
tbl,
) {
// we need to re-init tree; so trip
registry::get_preload_tripswitch().trip();
Ok(())
@ -259,12 +261,18 @@ impl Corestore {
None => Err(DdlError::DefaultNotFound),
}
}
OwnedEntity::Full(ksid, tblid) => {
match self.store.get_keyspace_atomic_ref(&ksid) {
Entity::Full(ksid, tblid) => {
match self
.store
.get_keyspace_atomic_ref(unsafe { ksid.as_slice() })
{
Some(kspace) => {
let tbl = Table::from_model_code(modelcode, volatile);
if let Some(tbl) = tbl {
if kspace.create_table(tblid, tbl) {
if kspace.create_table(
unsafe { ObjectID::from_slice(tblid.as_slice()) },
tbl,
) {
// trip the preload switch
registry::get_preload_tripswitch().trip();
Ok(())
@ -285,16 +293,21 @@ impl Corestore {
}
/// Drop a table
pub fn drop_table(&self, entity: Entity<'_>, force: bool) -> KeyspaceResult<()> {
pub fn drop_table(&self, entity: &Entity, force: bool) -> KeyspaceResult<()> {
match entity {
Entity::Single(tblid) | Entity::Partial(tblid) => match &self.estate.ks {
Some((_, ks)) => ks.drop_table(tblid, force),
Entity::Current(tblid) => match &self.estate.ks {
Some((_, ks)) => ks.drop_table(unsafe { tblid.as_slice() }, force),
None => Err(DdlError::DefaultNotFound),
},
Entity::Full(ksid, tblid) => match self.store.get_keyspace_atomic_ref(ksid) {
Some(ks) => ks.drop_table(tblid, force),
None => Err(DdlError::ObjectNotFound),
},
Entity::Full(ksid, tblid) => {
match self
.store
.get_keyspace_atomic_ref(unsafe { ksid.as_slice() })
{
Some(ks) => ks.drop_table(unsafe { tblid.as_slice() }, force),
None => Err(DdlError::ObjectNotFound),
}
}
}
}
@ -356,7 +369,7 @@ impl Corestore {
}
})
}
pub fn describe_table<P: ProtocolSpec>(&self, table: Option<Entity>) -> ActionResult<String> {
pub fn describe_table<P: ProtocolSpec>(&self, table: &Option<Entity>) -> ActionResult<String> {
let r = match table {
Some(tbl) => translate_ddl_error::<P, Arc<Table>>(self.get_table(tbl))?.describe_self(),
None => translate_ddl_error::<P, &Table>(self.get_ctable_result())?.describe_self(),

@ -107,7 +107,7 @@ pub mod prelude {
//! This module is hollow itself, it only re-exports from `dbnet::con` and `tokio::io`
pub use super::{AuthProviderHandle, ClientConnection, Stream};
pub use crate::{
actions::{ensure_boolean_or_aerr, ensure_cond_or_err, ensure_length, translate_ddl_error},
actions::{ensure_boolean_or_aerr, ensure_length, translate_ddl_error},
corestore::{
table::{KVEBlob, KVEList},
Corestore,

@ -1,167 +0,0 @@
/*
* 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::parser;
use super::parser::VALID_CONTAINER_NAME;
use crate::corestore::memstore::ObjectID;
use crate::dbnet::connection::prelude::*;
use crate::kvengine::encoding;
use crate::registry;
use core::str;
pub const TABLE: &[u8] = "TABLE".as_bytes();
pub const KEYSPACE: &[u8] = "KEYSPACE".as_bytes();
const VOLATILE: &[u8] = "volatile".as_bytes();
const FORCE_REMOVE: &[u8] = "force".as_bytes();
action! {
/// Handle `create table <tableid> <model>(args)` and `create keyspace <ksid>`
/// like queries
fn create(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
// minlength is 2 (create has already been checked)
ensure_length::<P>(act.len(), |size| size > 1)?;
let mut create_what = unsafe { act.next().unsafe_unwrap() }.to_vec();
create_what.make_ascii_uppercase();
match create_what.as_ref() {
TABLE => create_table(handle, con, act).await?,
KEYSPACE => create_keyspace(handle, con, act).await?,
_ => {
con._write_raw(P::RSTRING_UNKNOWN_DDL_QUERY).await?;
}
}
Ok(())
}
/// Handle `drop table <tableid>` and `drop keyspace <ksid>`
/// like queries
fn ddl_drop(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
// minlength is 2 (create has already been checked)
ensure_length::<P>(act.len(), |size| size > 1)?;
let mut create_what = unsafe { act.next().unsafe_unwrap() }.to_vec();
create_what.make_ascii_uppercase();
match create_what.as_ref() {
TABLE => drop_table(handle, con, act).await?,
KEYSPACE => drop_keyspace(handle, con, act).await?,
_ => {
con._write_raw(P::RSTRING_UNKNOWN_DDL_QUERY).await?;
}
}
Ok(())
}
/// We should have `<tableid> <model>(args) properties`
fn create_table(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
ensure_length::<P>(act.len(), |size| size > 1 && size < 4)?;
let table_name = unsafe { act.next().unsafe_unwrap() };
let model_name = unsafe { act.next().unsafe_unwrap() };
let (table_entity, model_code) = parser::parse_table_args::<P>(table_name, model_name)?;
let is_volatile = match act.next() {
Some(maybe_volatile) => {
ensure_cond_or_err(maybe_volatile.eq(VOLATILE), P::RSTRING_UNKNOWN_PROPERTY)?;
true
}
None => false,
};
if registry::state_okay() {
translate_ddl_error::<P, ()>(handle.create_table(table_entity, model_code, is_volatile))?;
con._write_raw(P::RCODE_OKAY).await?;
} else {
return util::err(P::RCODE_SERVER_ERR);
}
Ok(())
}
/// We should have `<ksid>`
fn create_keyspace(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
ensure_length::<P>(act.len(), |len| len == 1)?;
match act.next() {
Some(ksid) => {
ensure_cond_or_err(encoding::is_utf8(&ksid), P::RCODE_ENCODING_ERROR)?;
let ksid_str = unsafe { str::from_utf8_unchecked(ksid) };
ensure_cond_or_err(VALID_CONTAINER_NAME.is_match(ksid_str), P::RSTRING_BAD_EXPRESSION)?;
ensure_cond_or_err(ksid.len() < 64, P::RSTRING_CONTAINER_NAME_TOO_LONG)?;
let ksid = unsafe { ObjectID::from_slice(ksid_str) };
if registry::state_okay() {
translate_ddl_error::<P, ()>(handle.create_keyspace(ksid))?;
con._write_raw(P::RCODE_OKAY).await?
} else {
return util::err(P::RCODE_SERVER_ERR);
}
}
None => return util::err(P::RCODE_ACTION_ERR),
}
Ok(())
}
/// Drop a table (`<tblid>` only)
fn drop_table(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
ensure_length::<P>(act.len(), |size| size == 1)?;
match act.next() {
Some(eg) => {
let entity_group = parser::Entity::from_slice::<P>(eg)?;
if registry::state_okay() {
translate_ddl_error::<P, ()>(handle.drop_table(entity_group, false))?;
con._write_raw(P::RCODE_OKAY).await?;
} else {
return util::err(P::RCODE_SERVER_ERR);
}
},
None => return util::err(P::RCODE_ACTION_ERR),
}
Ok(())
}
/// Drop a keyspace (`<ksid>` only)
fn drop_keyspace(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
ensure_length::<P>(act.len(), |size| size == 1)?;
match act.next() {
Some(ksid) => {
ensure_cond_or_err(ksid.len() < 64, P::RSTRING_CONTAINER_NAME_TOO_LONG)?;
let force_remove = match act.next() {
Some(bts) if bts.eq(FORCE_REMOVE) => true,
None => false,
_ => {
return util::err(P::RCODE_UNKNOWN_ACTION);
}
};
if registry::state_okay() {
let objid = unsafe {ObjectID::from_slice(ksid)};
let result = if force_remove {
handle.force_drop_keyspace(objid)
} else {
handle.drop_keyspace(objid)
};
translate_ddl_error::<P, ()>(result)?;
con._write_raw(P::RCODE_OKAY).await?;
} else {
return util::err(P::RCODE_SERVER_ERR);
}
},
None => return util::err(P::RCODE_ACTION_ERR),
}
Ok(())
}
}

@ -1,83 +0,0 @@
/*
* 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},
parser::Entity,
},
crate::dbnet::connection::prelude::*,
};
const KEYSPACES: &[u8] = "KEYSPACES".as_bytes();
action! {
/// Runs an inspect query:
/// - `INSPECT KEYSPACES` is run by this function itself
/// - `INSPECT TABLE <tblid>` is delegated to self::inspect_table
/// - `INSPECT KEYSPACE <ksid>` is delegated to self::inspect_keyspace
fn inspect(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
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 => {
ensure_length::<P>(act.len(), |len| len == 0)?;
// let's return what all keyspaces exist
con.write_typed_non_null_array(
handle.get_store().list_keyspaces(),
b'+'
).await?
}
_ => return util::err(P::RSTRING_UNKNOWN_INSPECT_QUERY),
}
}
None => return util::err(P::RCODE_ACTION_ERR),
}
Ok(())
}
/// INSPECT a keyspace. This should only have the keyspace ID
fn inspect_keyspace(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
ensure_length::<P>(act.len(), |len| len < 2)?;
con.write_typed_non_null_array(handle.list_tables::<P>(act.next())?, b'+').await?;
Ok(())
}
/// INSPECT a table. This should only have the table ID
fn inspect_table(handle: &Corestore, con: &'a mut T, mut act: ActionIter<'a>) {
ensure_length::<P>(act.len(), |len| len < 2)?;
let maybe_entity = match act.next() {
Some(e) => Some(Entity::from_slice::<P>(e)?),
None => None,
};
con.write_string(&handle.describe_table::<P>(maybe_entity)?).await?;
Ok(())
}
}

@ -33,13 +33,7 @@ use crate::{
actions::{self, ActionError, ActionResult},
admin, auth, blueql,
protocol::{iter::AnyArrayIter, PipelinedQuery, SimpleQuery, UnsafeSlice},
queryengine::parser::Entity,
};
mod ddl;
mod inspect;
pub mod parser;
#[cfg(test)]
mod tests;
pub type ActionIter<'a> = AnyArrayIter<'a>;
@ -139,10 +133,6 @@ async fn execute_stage<'a, P: ProtocolSpec, T: 'a + ClientConnection<P, Strm>, S
MKSNAP => admin::mksnap::mksnap,
LSKEYS => actions::lskeys::lskeys,
POP => actions::pop::pop,
CREATE => ddl::create,
DROP => ddl::ddl_drop,
USE => self::entity_swap,
INSPECT => inspect::inspect,
MPOP => actions::mpop::mpop,
LSET => actions::lists::lset,
LGET => actions::lists::lget::lget,
@ -158,20 +148,6 @@ async fn execute_stage<'a, P: ProtocolSpec, T: 'a + ClientConnection<P, Strm>, S
Ok(())
}
action! {
/// Handle `use <entity>` like queries
fn entity_swap(handle: &mut Corestore, con: &mut T, mut act: ActionIter<'a>) {
ensure_length::<P>(act.len(), |len| len == 1)?;
let entity = unsafe {
// SAFETY: Already checked len
act.next_unchecked()
};
translate_ddl_error::<P, ()>(handle.swap_entity(Entity::from_slice::<P>(entity)?))?;
con._write_raw(P::RCODE_OKAY).await?;
Ok(())
}
}
/// Execute a stage **completely**. This means that action errors are never propagated
/// over the try operator
async fn execute_stage_pedantic<

@ -1,258 +0,0 @@
/*
* 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 crate::corestore::{lazy::Lazy, memstore::ObjectID};
use crate::kvengine::encoding;
use crate::queryengine::ProtocolSpec;
use crate::util::{
self,
compiler::{self, cold_err},
};
use core::{fmt, str};
use regex::Regex;
type LazyRegexFn = Lazy<Regex, fn() -> Regex>;
const KEYMAP: &[u8] = "keymap".as_bytes();
const BINSTR: &[u8] = "binstr".as_bytes();
const STR: &[u8] = "str".as_bytes();
const LIST_STR: &[u8] = "list<str>".as_bytes();
const LIST_BINSTR: &[u8] = "list<binstr>".as_bytes();
pub(super) static VALID_CONTAINER_NAME: LazyRegexFn =
LazyRegexFn::new(|| Regex::new("^[a-zA-Z_][a-zA-Z_0-9]*$").unwrap());
pub(super) static VALID_TYPENAME: LazyRegexFn =
LazyRegexFn::new(|| Regex::new("^<[a-zA-Z][a-zA-Z0-9]+[^>\\s]?>{1}$").unwrap());
pub(super) fn parse_table_args<'a, P: ProtocolSpec>(
table_name: &'a [u8],
model_name: &'a [u8],
) -> Result<(Entity<'a>, u8), &'static [u8]> {
if compiler::unlikely(!encoding::is_utf8(&table_name) || !encoding::is_utf8(&model_name)) {
return Err(P::RCODE_ENCODING_ERROR);
}
let model_name_str = unsafe { str::from_utf8_unchecked(model_name) };
// get the entity group
let entity_group = Entity::from_slice::<P>(table_name)?;
let splits: Vec<&str> = model_name_str.split('(').collect();
if compiler::unlikely(splits.len() != 2) {
return Err(P::RSTRING_BAD_EXPRESSION);
}
let model_name_split = unsafe { ucidx!(splits, 0) };
let model_args_split = unsafe { ucidx!(splits, 1) };
// model name has to have at least one char while model args should have
// atleast `)` 1 chars (for example if the model takes no arguments: `smh()`)
if compiler::unlikely(model_name_split.is_empty() || model_args_split.is_empty()) {
return Err(P::RSTRING_BAD_EXPRESSION);
}
// THIS IS WHERE WE HANDLE THE NEWER MODELS
if model_name_split.as_bytes() != KEYMAP {
return Err(P::RSTRING_UNKNOWN_MODEL);
}
let non_bracketed_end =
unsafe { ucidx!(*model_args_split.as_bytes(), model_args_split.len() - 1) != b')' };
if compiler::unlikely(non_bracketed_end) {
return Err(P::RSTRING_BAD_EXPRESSION);
}
// should be (ty1, ty2)
let model_args: Vec<&str> = model_args_split[..model_args_split.len() - 1]
.split(',')
.map(|v| v.trim())
.collect();
if compiler::unlikely(model_args.len() != 2) {
// nope, someone had fun with commas or they added more args
// let's check if it was comma fun or if it was arg fun
return cold_err({
let all_nonzero = model_args.into_iter().all(|v| !v.is_empty());
if all_nonzero {
// arg fun
Err(P::RSTRING_TOO_MANY_ARGUMENTS)
} else {
// comma fun
Err(P::RSTRING_BAD_EXPRESSION)
}
});
}
let key_ty = unsafe { ucidx!(model_args, 0) };
let val_ty = unsafe { ucidx!(model_args, 1) };
let valid_key_ty = if let Some(idx) = key_ty.chars().position(|v| v.eq(&'<')) {
VALID_CONTAINER_NAME.is_match(&key_ty[..idx]) && VALID_TYPENAME.is_match(&key_ty[idx..])
} else {
VALID_CONTAINER_NAME.is_match(key_ty)
};
let valid_val_ty = if let Some(idx) = val_ty.chars().position(|v| v.eq(&'<')) {
VALID_CONTAINER_NAME.is_match(&val_ty[..idx]) && VALID_TYPENAME.is_match(&val_ty[idx..])
} else {
VALID_CONTAINER_NAME.is_match(val_ty)
};
if compiler::unlikely(!(valid_key_ty || valid_val_ty)) {
return Err(P::RSTRING_BAD_EXPRESSION);
}
let key_ty = key_ty.as_bytes();
let val_ty = val_ty.as_bytes();
let model_code: u8 = match (key_ty, val_ty) {
// pure KVEBlob
(BINSTR, BINSTR) => 0,
(BINSTR, STR) => 1,
(STR, STR) => 2,
(STR, BINSTR) => 3,
// KVExt: listmap
(BINSTR, LIST_BINSTR) => 4,
(BINSTR, LIST_STR) => 5,
(STR, LIST_BINSTR) => 6,
(STR, LIST_STR) => 7,
// KVExt bad keytypes (we can't use lists as keys for obvious reasons)
(LIST_STR, _) | (LIST_BINSTR, _) => return Err(P::RSTRING_BAD_TYPE_FOR_KEY),
_ => return Err(P::RCODE_UNKNOWN_DATA_TYPE),
};
Ok((entity_group, model_code))
}
type ByteSlice<'a> = &'a [u8];
#[derive(PartialEq)]
pub enum Entity<'a> {
/// Fully qualified syntax (ks:table)
Full(ByteSlice<'a>, ByteSlice<'a>),
/// Half entity syntax (only ks/table)
Single(ByteSlice<'a>),
/// Partial entity syntax (`:table`)
Partial(ByteSlice<'a>),
}
impl<'a> From<&'a crate::blueql::Entity> for Entity<'a> {
fn from(e: &'a crate::blueql::Entity) -> Entity<'a> {
unsafe {
// UNSAFE(@ohsayan): Lifetime guarantees safety
match e {
crate::blueql::Entity::Current(c) => Entity::Single(c.as_slice()),
crate::blueql::Entity::Full(k, t) => Entity::Full(k.as_slice(), t.as_slice()),
}
}
}
}
#[derive(PartialEq)]
pub enum OwnedEntity {
Full(ObjectID, ObjectID),
Single(ObjectID),
Partial(ObjectID),
}
impl fmt::Debug for OwnedEntity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OwnedEntity::Full(a, b) => write!(
f,
"Full('{}:{}')",
String::from_utf8_lossy(a),
String::from_utf8_lossy(b)
),
OwnedEntity::Single(a) => write!(f, "Single('{}')", String::from_utf8_lossy(a)),
OwnedEntity::Partial(a) => write!(f, "Partial(':{}')", String::from_utf8_lossy(a)),
}
}
}
impl<'a> fmt::Debug for Entity<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.as_owned())
}
}
impl<'a> Entity<'a> {
pub fn from_slice<P: ProtocolSpec>(input: ByteSlice<'a>) -> Result<Entity<'a>, &'static [u8]> {
let parts: Vec<&[u8]> = input.split(|b| *b == b':').collect();
if compiler::unlikely(parts.is_empty() || parts.len() > 2) {
return util::err(P::RSTRING_BAD_EXPRESSION);
}
// just the table
let first_entity = unsafe { ucidx!(parts, 0) };
if parts.len() == 1 {
Ok(Entity::Single(Self::verify_entity_name::<P>(first_entity)?))
} else {
let second_entity = Self::verify_entity_name::<P>(unsafe { ucidx!(parts, 1) })?;
if first_entity.is_empty() {
// partial syntax; so the table is in the second position
Ok(Entity::Partial(second_entity))
} else {
let keyspace = Self::verify_entity_name::<P>(first_entity)?;
let table = Self::verify_entity_name::<P>(second_entity)?;
Ok(Entity::Full(keyspace, table))
}
}
}
#[inline(always)]
fn verify_entity_name<P: ProtocolSpec>(input: &[u8]) -> Result<&[u8], &'static [u8]> {
let mut valid_name = input.len() < 65
&& encoding::is_utf8(input)
&& unsafe { VALID_CONTAINER_NAME.is_match(str::from_utf8_unchecked(input)) };
#[cfg(windows)]
{
// paths on Windows are case insensitive that's why this is necessary
valid_name &=
!(input.eq_ignore_ascii_case(b"PRELOAD") || input.eq_ignore_ascii_case(b"PARTMAP"));
}
#[cfg(not(windows))]
{
valid_name &= (input != b"PRELOAD") && (input != b"PARTMAP");
}
if compiler::likely(valid_name && !input.is_empty()) {
// valid name
Ok(input)
} else if compiler::unlikely(input.is_empty()) {
// bad expression (something like `:`)
util::err(P::RSTRING_BAD_EXPRESSION)
} else if compiler::unlikely(input.eq(b"system")) {
// system cannot be switched to
util::err(P::RSTRING_PROTECTED_OBJECT)
} else {
// the container has a bad name
util::err(P::RSTRING_BAD_CONTAINER_NAME)
}
}
pub fn as_owned(&self) -> OwnedEntity {
unsafe {
match self {
Self::Full(a, b) => {
OwnedEntity::Full(ObjectID::from_slice(a), ObjectID::from_slice(b))
}
Self::Single(a) => OwnedEntity::Single(ObjectID::from_slice(a)),
Self::Partial(a) => OwnedEntity::Partial(ObjectID::from_slice(a)),
}
}
}
pub fn into_owned(self) -> OwnedEntity {
self.as_owned()
}
}

@ -1,392 +0,0 @@
/*
* 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::parser;
mod parser_ddl_tests {
use super::parser::Entity;
use crate::protocol::interface::ProtocolSpec;
use crate::protocol::Skyhash2;
macro_rules! byvec {
($($element:expr),*) => {
vec![
$(
$element.as_bytes()
),*
]
};
}
fn parse_table_args_test(input: Vec<&'static [u8]>) -> Result<(Entity<'_>, u8), &'static [u8]> {
super::parser::parse_table_args::<Skyhash2>(input[0], input[1])
}
#[test]
fn test_table_args_valid() {
// binstr, binstr
let it = byvec!("mytbl", "keymap(binstr,binstr)");
let (tbl_name, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tbl_name, Entity::Single(b"mytbl"));
assert_eq!(mcode, 0);
// binstr, str
let it = byvec!("mytbl", "keymap(binstr,str)");
let (tbl_name, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tbl_name, Entity::Single(b"mytbl"));
assert_eq!(mcode, 1);
// str, str
let it = byvec!("mytbl", "keymap(str,str)");
let (tbl_name, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tbl_name, Entity::Single(b"mytbl"));
assert_eq!(mcode, 2);
// str, binstr
let it = byvec!("mytbl", "keymap(str,binstr)");
let (tbl_name, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tbl_name, Entity::Single(b"mytbl"));
assert_eq!(mcode, 3);
// now test kvext: listmap
// binstr, list<binstr>
let it = byvec!("mytbl", "keymap(binstr,list<binstr>)");
let (tbl_name, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tbl_name, Entity::Single(b"mytbl"));
assert_eq!(mcode, 4);
// binstr, list<str>
let it = byvec!("mytbl", "keymap(binstr,list<str>)");
let (tbl_name, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tbl_name, Entity::Single(b"mytbl"));
assert_eq!(mcode, 5);
// str, list<binstr>
let it = byvec!("mytbl", "keymap(str,list<binstr>)");
let (tbl_name, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tbl_name, Entity::Single(b"mytbl"));
assert_eq!(mcode, 6);
// str, list<str>
let it = byvec!("mytbl", "keymap(str,list<str>)");
let (tbl_name, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tbl_name, Entity::Single(b"mytbl"));
assert_eq!(mcode, 7);
}
#[test]
fn test_table_bad_ident() {
let it = byvec!("1one", "keymap(binstr,binstr)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_CONTAINER_NAME
);
let it = byvec!("%whywouldsomeone", "keymap(binstr,binstr)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_CONTAINER_NAME
);
}
#[test]
fn test_table_whitespaced_datatypes() {
let it = byvec!("mycooltbl", "keymap(binstr, binstr)");
let (tblid, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tblid, Entity::Single(b"mycooltbl"));
assert_eq!(mcode, 0);
let it = byvec!("mycooltbl", "keymap(binstr, str)");
let (tblid, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tblid, Entity::Single(b"mycooltbl"));
assert_eq!(mcode, 1);
let it = byvec!("mycooltbl", "keymap(str, str)");
let (tblid, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tblid, Entity::Single(b"mycooltbl"));
assert_eq!(mcode, 2);
let it = byvec!("mycooltbl", "keymap(str, binstr)");
let (tblid, mcode) = parse_table_args_test(it).unwrap();
assert_eq!(tblid, Entity::Single(b"mycooltbl"));
assert_eq!(mcode, 3);
}
#[test]
fn test_table_badty() {
let it = byvec!("mycooltbl", "keymap(wth, str)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RCODE_UNKNOWN_DATA_TYPE
);
let it = byvec!("mycooltbl", "keymap(wth, wth)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RCODE_UNKNOWN_DATA_TYPE
);
let it = byvec!("mycooltbl", "keymap(str, wth)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RCODE_UNKNOWN_DATA_TYPE
);
let it = byvec!("mycooltbl", "keymap(wth1, wth2)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RCODE_UNKNOWN_DATA_TYPE
);
}
#[test]
fn test_table_bad_model() {
let it = byvec!("mycooltbl", "wthmap(wth, wth)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_UNKNOWN_MODEL
);
let it = byvec!("mycooltbl", "wthmap(str, str)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_UNKNOWN_MODEL
);
let it = byvec!("mycooltbl", "wthmap()");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_UNKNOWN_MODEL
);
}
#[test]
fn test_table_malformed_expr() {
let it = byvec!("mycooltbl", "keymap(");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(,");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(,,");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap),");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap),,");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap),,)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(,)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(,,)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap,");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap,,");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap,,)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(str,");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(str,str");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(str,str,");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(str,str,)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let it = byvec!("mycooltbl", "keymap(str,str,),");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
}
#[test]
fn test_table_too_many_args() {
let it = byvec!("mycooltbl", "keymap(str, str, str)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_TOO_MANY_ARGUMENTS
);
// this should be valid for not-yet-known data types too
let it = byvec!("mycooltbl", "keymap(wth, wth, wth)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_TOO_MANY_ARGUMENTS
);
}
#[test]
fn test_bad_key_type() {
let it = byvec!("myverycooltbl", "keymap(list<str>, str)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_TYPE_FOR_KEY
);
let it = byvec!("myverycooltbl", "keymap(list<binstr>, binstr)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_TYPE_FOR_KEY
);
// for consistency checks
let it = byvec!("myverycooltbl", "keymap(list<str>, binstr)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_TYPE_FOR_KEY
);
let it = byvec!("myverycooltbl", "keymap(list<binstr>, str)");
assert_eq!(
parse_table_args_test(it).unwrap_err(),
Skyhash2::RSTRING_BAD_TYPE_FOR_KEY
);
}
}
mod entity_parser_tests {
use super::parser::Entity;
use crate::protocol::interface::ProtocolSpec;
use crate::protocol::Skyhash2;
#[test]
fn test_query_full_entity_okay() {
let x = byt!("ks:tbl");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap(),
Entity::Full(b"ks", b"tbl")
);
}
#[test]
fn test_query_half_entity() {
let x = byt!("tbl");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap(),
Entity::Single(b"tbl")
)
}
#[test]
fn test_query_partial_entity() {
let x = byt!(":tbl");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap(),
Entity::Partial(b"tbl")
)
}
#[test]
fn test_query_entity_badexpr() {
let x = byt!("ks:");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let x = byt!(":");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let x = byt!("::");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let x = byt!("::ks");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let x = byt!("ks::tbl");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let x = byt!("ks::");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let x = byt!("ks::tbl::");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
let x = byt!("::ks::tbl::");
assert_eq!(
Entity::from_slice::<Skyhash2>(&x).unwrap_err(),
Skyhash2::RSTRING_BAD_EXPRESSION
);
}
#[test]
fn test_bad_entity_name() {
let ename = byt!("$var");
assert_eq!(
Entity::from_slice::<Skyhash2>(&ename).unwrap_err(),
Skyhash2::RSTRING_BAD_CONTAINER_NAME
);
}
#[test]
fn ks_or_table_with_preload_or_partmap() {
let badname = byt!("PARTMAP");
assert_eq!(
Entity::from_slice::<Skyhash2>(&badname).unwrap_err(),
Skyhash2::RSTRING_BAD_CONTAINER_NAME
);
let badname = byt!("PRELOAD");
assert_eq!(
Entity::from_slice::<Skyhash2>(&badname).unwrap_err(),
Skyhash2::RSTRING_BAD_CONTAINER_NAME
);
}
}

@ -32,9 +32,7 @@ mod __private {
async fn test_create_keyspace() {
let mut rng = rand::thread_rng();
let ksname = utils::rand_alphastring(10, &mut rng);
query.push("create");
query.push("keyspace");
query.push(&ksname);
query.push(format!("create space {ksname}"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
@ -43,17 +41,13 @@ mod __private {
async fn test_drop_keyspace() {
let mut rng = rand::thread_rng();
let ksname = utils::rand_alphastring(10, &mut rng);
query.push("create");
query.push("keyspace");
query.push(&ksname);
query.push(format!("create space {ksname}"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
);
let mut query = Query::new();
query.push("drop");
query.push("keyspace");
query.push(ksname);
query.push(format!("drop space {ksname}"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
@ -62,10 +56,7 @@ mod __private {
async fn test_create_table() {
let mut rng = rand::thread_rng();
let tblname = utils::rand_alphastring(10, &mut rng);
query.push("create");
query.push("table");
query.push(&tblname);
query.push("keymap(str,str)");
query.push(format!("create model {tblname}(string, string)"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
@ -74,11 +65,7 @@ mod __private {
async fn test_create_volatile() {
let mut rng = rand::thread_rng();
let tblname = utils::rand_alphastring(10, &mut rng);
query.push("create");
query.push("table");
query.push(&tblname);
query.push("keymap(str,str)");
query.push("volatile");
query.push(format!("create model {tblname}(string, string) volatile"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
@ -87,10 +74,7 @@ mod __private {
async fn test_create_table_fully_qualified_entity() {
let mut rng = rand::thread_rng();
let tblname = utils::rand_alphastring(10, &mut rng);
query.push("create");
query.push("table");
query.push(__MYKS__.to_owned() + ":" + &tblname);
query.push("keymap(str,str)");
query.push(format!("create model {__MYKS__}.{tblname}(string, string)"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
@ -99,11 +83,9 @@ mod __private {
async fn test_create_table_volatile_fully_qualified_entity() {
let mut rng = rand::thread_rng();
let tblname = utils::rand_alphastring(10, &mut rng);
query.push("create");
query.push("table");
query.push(__MYKS__.to_owned() + ":" + &tblname);
query.push("keymap(str,str)");
query.push("volatile");
query.push(format!(
"create model {__MYKS__}.{tblname}(string, string) volatile"
));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
@ -112,18 +94,12 @@ mod __private {
async fn test_drop_table() {
let mut rng = rand::thread_rng();
let tblname = utils::rand_alphastring(10, &mut rng);
query.push("create");
query.push("table");
query.push(&tblname);
query.push("keymap(str,str)");
query.push(format!("create model {tblname}(string, string)"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
);
let mut query = Query::new();
query.push("drop");
query.push("table");
query.push(&tblname);
let query = Query::from(format!("drop model {tblname}"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
@ -132,39 +108,30 @@ mod __private {
async fn test_drop_table_fully_qualified_entity() {
let mut rng = rand::thread_rng();
let tblname = utils::rand_alphastring(10, &mut rng);
let my_fqe = __MYKS__.to_owned() + ":" + &tblname;
query.push("create");
query.push("table");
query.push(&my_fqe);
query.push("keymap(str,str)");
let my_fqe = __MYKS__.to_owned() + "." + &tblname;
query.push(format!("create model {my_fqe}(string, string)"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
);
let mut query = Query::new();
query.push("drop");
query.push("table");
query.push(my_fqe);
let query = Query::from(format!("drop model {my_fqe}"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
);
}
async fn test_use() {
query.push("USE");
query.push(&__MYENTITY__);
query.push(format!("USE {__MYENTITY__}"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::Okay)
)
}
async fn test_use_syntax_error() {
query.push("USE");
query.push(&__MYENTITY__);
query.push("wiwofjwjfio");
query.push(format!("USE {__MYENTITY__} wiwofjwjfio"));
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::ActionError)
Element::RespCode(RespCode::ErrorString("bql-invalid-syntax".into()))
)
}
async fn test_whereami() {
@ -175,7 +142,7 @@ mod __private {
);
runeq!(
con,
query!("use", "default"),
query!("use default"),
Element::RespCode(RespCode::Okay)
);
runeq!(
@ -185,7 +152,7 @@ mod __private {
);
runeq!(
con,
query!("use", "default:default"),
query!("use default.default"),
Element::RespCode(RespCode::Okay)
);
runeq!(

@ -30,32 +30,26 @@ const TABLE_DECL_KM_STR_STR_VOLATILE: &str = "Keymap { data:(str,str), volatile:
mod __private {
use skytable::{types::Array, Element, RespCode};
async fn test_inspect_keyspaces() {
query.push("INSPECT");
query.push("KEYSPACES");
query.push("INSPECT SPACES");
assert!(matches!(
con.run_query_raw(&query).await.unwrap(),
Element::Array(Array::NonNullStr(_))
))
}
async fn test_inspect_keyspace() {
query.push("INSPECT");
query.push("KEYSPACE");
query.push(&__MYKS__);
query.push(format!("INSPECT SPACE {__MYKS__}"));
assert!(matches!(
con.run_query_raw(&query).await.unwrap(),
Element::Array(Array::NonNullStr(_))
))
}
async fn test_inspect_current_keyspace() {
query.push("INSPECT");
query.push("KEYSPACE");
query.push("INSPECT SPACE");
let ret: Vec<String> = con.run_query(&query).await.unwrap();
assert!(ret.contains(&__MYTABLE__));
}
async fn test_inspect_table() {
query.push("INSPECT");
query.push("TABLE");
query.push(__MYTABLE__);
query.push(format!("INSPECT MODEL {__MYTABLE__}"));
match con.run_query_raw(&query).await.unwrap() {
Element::String(st) => {
assert_eq!(st, TABLE_DECL_KM_STR_STR_VOLATILE.to_owned())
@ -64,15 +58,12 @@ mod __private {
}
}
async fn test_inspect_current_table() {
query.push("INSPECT");
query.push("TABLE");
query.push("INSPECT MODEL");
let ret: String = con.run_query(&query).await.unwrap();
assert_eq!(ret, TABLE_DECL_KM_STR_STR_VOLATILE);
}
async fn test_inspect_table_fully_qualified_entity() {
query.push("INSPECT");
query.push("TABLE");
query.push(__MYENTITY__);
query.push(format!("INSPECT MODEL {__MYENTITY__}"));
match con.run_query_raw(&query).await.unwrap() {
Element::String(st) => {
assert_eq!(st, TABLE_DECL_KM_STR_STR_VOLATILE.to_owned())
@ -81,32 +72,24 @@ mod __private {
}
}
async fn test_inspect_keyspaces_syntax_error() {
query.push("INSPECT");
query.push("KEYSPACES");
query.push("iowjfjofoe");
query.push("INSPECT SPACES iowjfjofoe");
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::ActionError)
Element::RespCode(RespCode::ErrorString("bql-invalid-syntax".into()))
);
}
async fn test_inspect_keyspace_syntax_error() {
query.push("INSPECT");
query.push("KEYSPACE");
query.push("ijfwijifwjo");
query.push("oijfwirfjwo");
query.push("INSPECT SPACE ijfwijifwjo oijfwirfjwo");
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::ActionError)
Element::RespCode(RespCode::ErrorString("bql-invalid-syntax".into()))
);
}
async fn test_inspect_table_syntax_error() {
query.push("INSPECT");
query.push("TABLE");
query.push("ijfwijifwjo");
query.push("oijfwirfjwo");
query.push("INSPECT MODEL ijfwijifwjo oijfwirfjwo");
assert_eq!(
con.run_query_raw(&query).await.unwrap(),
Element::RespCode(RespCode::ActionError)
Element::RespCode(RespCode::ErrorString("bql-invalid-syntax".into()))
);
}
}

@ -24,7 +24,7 @@
*
*/
#[sky_macros::dbtest_module(table = "keymap(str,str)")]
#[sky_macros::dbtest_module(table = "(string, string)")]
mod __private {
use skytable::types::RawString;
use skytable::{Element, RespCode};

@ -36,7 +36,7 @@ macro_rules! lset {
}
}
#[sky_macros::dbtest_module(table = "keymap(str,list<str>)")]
#[sky_macros::dbtest_module(table = "(string,list<string>)")]
mod __private {
use skytable::{query, types::Array, Element, RespCode};

@ -59,7 +59,7 @@ macro_rules! switch_entity {
($con:expr, $entity:expr) => {
runeq!(
$con,
::skytable::query!("use", $entity),
::skytable::query!(format!("use {}", $entity)),
::skytable::Element::RespCode(::skytable::RespCode::Okay)
)
};
@ -69,7 +69,7 @@ macro_rules! create_table_and_switch {
($con:expr, $table:expr, $decl:expr) => {{
runeq!(
$con,
::skytable::query!("create", "table", $table, $decl),
::skytable::query!(format!("create model {}{}", $table, $decl)),
::skytable::Element::RespCode(::skytable::RespCode::Okay)
);
switch_entity!($con, $table);

@ -27,7 +27,7 @@
use super::{persist_load, persist_store, PERSIST_TEST_SET_SIZE};
use sky_macros::dbtest_func as dbtest;
const PERSIST_CFG_KEYMAP_BIN_BIN_TABLE: &str = "testsuite:persist_bin_bin_tbl";
const PERSIST_CFG_KEYMAP_BIN_BIN_TABLE: &str = "testsuite.persist_bin_bin_tbl";
const PERSIST_DATA_KEYMAP_BIN_BIN_TABLE: [(&[u8], &[u8]); PERSIST_TEST_SET_SIZE] = [
(bin!(b"mykey1"), bin!(b"myval1")),
(bin!(b"mykey2"), bin!(b"myval2")),
@ -40,7 +40,7 @@ async fn store_keymap_bin_bin() {
persist_store(
&mut con,
PERSIST_CFG_KEYMAP_BIN_BIN_TABLE,
"keymap(binstr,binstr)",
"(binary, binary)",
PERSIST_DATA_KEYMAP_BIN_BIN_TABLE,
)
.await;
@ -56,7 +56,7 @@ async fn load_keymap_bin_bin() {
.await;
}
const PERSIST_CFG_KEYMAP_BIN_STR_TABLE: &str = "testsuite:persist_bin_str_tbl";
const PERSIST_CFG_KEYMAP_BIN_STR_TABLE: &str = "testsuite.persist_bin_str_tbl";
const PERSIST_DATA_KEYMAP_BIN_STR_TABLE: [(&[u8], &str); PERSIST_TEST_SET_SIZE] = [
(bin!(b"mykey1"), "myval1"),
(bin!(b"mykey2"), "myval2"),
@ -69,7 +69,7 @@ async fn store_keymap_bin_str() {
persist_store(
&mut con,
PERSIST_CFG_KEYMAP_BIN_STR_TABLE,
"keymap(binstr,str)",
"(binary, string)",
PERSIST_DATA_KEYMAP_BIN_STR_TABLE,
)
.await;
@ -85,7 +85,7 @@ async fn load_keymap_bin_str() {
.await;
}
const PERSIST_CFG_KEYMAP_STR_STR_TABLE: &str = "testsuite:persist_str_str_tbl";
const PERSIST_CFG_KEYMAP_STR_STR_TABLE: &str = "testsuite.persist_str_str_tbl";
const PERSIST_DATA_KEYMAP_STR_STR_TABLE: [(&str, &str); PERSIST_TEST_SET_SIZE] = [
("mykey1", "myval1"),
("mykey2", "myval2"),
@ -98,7 +98,7 @@ async fn store_keymap_str_str() {
persist_store(
&mut con,
PERSIST_CFG_KEYMAP_STR_STR_TABLE,
"keymap(str,str)",
"(string, string)",
PERSIST_DATA_KEYMAP_STR_STR_TABLE,
)
.await;
@ -114,7 +114,7 @@ async fn load_keymap_str_str() {
.await;
}
const PERSIST_CFG_KEYMAP_STR_BIN_TABLE: &str = "testsuite:persist_str_bin_tbl";
const PERSIST_CFG_KEYMAP_STR_BIN_TABLE: &str = "testsuite.persist_str_bin_tbl";
const PERSIST_DATA_KEYMAP_STR_BIN_TABLE: [(&str, &[u8]); PERSIST_TEST_SET_SIZE] = [
("mykey1", bin!(b"myval1")),
("mykey2", bin!(b"myval2")),
@ -127,7 +127,7 @@ async fn store_keymap_str_bin() {
persist_store(
&mut con,
PERSIST_CFG_KEYMAP_STR_BIN_TABLE,
"keymap(str,binstr)",
"(string, binary)",
PERSIST_DATA_KEYMAP_STR_BIN_TABLE,
)
.await;

@ -69,14 +69,14 @@ const DATA_BIN_LISTBIN: ListData<ListIDBin, Bin> = listdata!(
binid!(b"list3") => binlist!(b"e1", b"e2", b"e3", b"e4"),
binid!(b"list4") => binlist!(b"e1", b"e2", b"e3", b"e4")
);
const TABLE_BIN_LISTBIN: &str = "testsuite:persist_bin_listbin";
const TABLE_BIN_LISTBIN: &str = "testsuite.persist_bin_listbin";
#[dbtest(skip_if_cfg = "persist-suite", norun = true)]
async fn store_bin_bin() {
persist_store(
&mut con,
TABLE_BIN_LISTBIN,
"keymap(binstr,list<binstr>)",
"(binary, list<binary>)",
DATA_BIN_LISTBIN,
)
.await;
@ -95,14 +95,14 @@ const DATA_BIN_LISTSTR: ListData<ListIDBin, Str> = listdata!(
binid!(b"list4") => ["e1", "e2", "e3", "e4"]
);
const TABLE_BIN_LISTSTR: &str = "testsuite:persist_bin_liststr";
const TABLE_BIN_LISTSTR: &str = "testsuite.persist_bin_liststr";
#[dbtest(skip_if_cfg = "persist-suite", norun = true)]
async fn store_bin_str() {
persist_store(
&mut con,
TABLE_BIN_LISTSTR,
"keymap(binstr,list<str>)",
"(binary, list<string>)",
DATA_BIN_LISTSTR,
)
.await;
@ -121,14 +121,14 @@ const DATA_STR_LISTBIN: ListData<ListIDStr, Bin> = listdata!(
ListIDStr("list4") => binlist!(b"e1", b"e2", b"e3", b"e4")
);
const TABLE_STR_LISTBIN: &str = "testsuite:persist_str_listbin";
const TABLE_STR_LISTBIN: &str = "testsuite.persist_str_listbin";
#[dbtest(skip_if_cfg = "persist-suite", norun = true)]
async fn store_str_bin() {
persist_store(
&mut con,
TABLE_STR_LISTBIN,
"keymap(str,list<binstr>)",
"(string, list<binary>)",
DATA_STR_LISTBIN,
)
.await;
@ -147,14 +147,14 @@ const DATA_STR_LISTSTR: ListData<ListIDStr, Str> = listdata!(
ListIDStr("list4") => ["e1", "e2", "e3", "e4"]
);
const TABLE_STR_LISTSTR: &str = "testsuite:persist_str_liststr";
const TABLE_STR_LISTSTR: &str = "testsuite.persist_str_liststr";
#[dbtest(skip_if_cfg = "persist-suite", norun = true)]
async fn store_str_str() {
persist_store(
&mut con,
TABLE_STR_LISTSTR,
"keymap(str,list<str>)",
"(string, list<string>)",
DATA_STR_LISTSTR,
)
.await;

@ -34,20 +34,19 @@ use skytable::{
#[dbtest(skip_if_cfg = "persist-suite", norun = true, port = 2007)]
async fn store_keyspace() {
assert_okay!(con, query!("create", "keyspace", "universe"));
assert_okay!(con, query!("create space universe"));
switch_entity!(con, "universe");
assert_okay!(con, query!("create", "table", "warp", "keymap(str,str)"));
switch_entity!(con, "universe:warp");
assert_okay!(con, query!("create model warp(string,string)"));
switch_entity!(con, "universe.warp");
assert_okay!(con, query!("set", "x", "100"));
}
#[dbtest(run_if_cfg = "persist-suite", norun = true, port = 2007)]
async fn load_keyspace() {
switch_entity!(con, "universe:warp");
switch_entity!(con, "universe.warp");
runeq!(con, query!("get", "x"), Element::String("100".to_owned()));
switch_entity!(con, "default");
assert_okay!(con, query!("flushdb", "universe:warp"));
assert_okay!(con, query!("drop", "table", "universe:warp"));
assert_okay!(con, query!("drop", "keyspace", "universe"));
assert_okay!(con, query!("drop model universe.warp force"));
assert_okay!(con, query!("drop space universe"));
}
macro_rules! bin {
@ -228,11 +227,10 @@ async fn persist_load<K: PersistKey, V: PersistValue>(
runeq!(con, q, value.response_load());
}
// now delete this table, freeing it up for the next suite run
switch_entity!(con, "default:default");
assert_okay!(con, query!("flushdb", table_id));
switch_entity!(con, "default.default");
runeq!(
con,
query!("drop", "table", table_id),
query!(format!("drop model {table_id} force")),
Element::RespCode(RespCode::Okay)
);
}

@ -46,7 +46,7 @@ pub struct DBTestFunctionConfig {
impl DBTestFunctionConfig {
pub fn default() -> Self {
Self {
table_decl: "keymap(str,str)".to_owned(),
table_decl: "(string, string)".to_owned(),
port: 2003,
host: "127.0.0.1".to_owned(),
tls_cert: None,
@ -82,13 +82,7 @@ impl DBTestFunctionConfig {
let Self { table_decl, .. } = self;
quote! {
con.run_query_raw(
&skytable::query!(
"create",
"table",
#table_name,
#table_decl,
"volatile"
)
&skytable::query!(format!("create model {}{} volatile", #table_name, #table_decl))
).await.unwrap()
}
}
@ -242,7 +236,7 @@ fn generate_dbtest(
#body
let __create_ks =
con.run_query_raw(
&skytable::query!("create", "keyspace", "testsuite")
&skytable::query!("create space testsuite")
).await.unwrap();
if !(
__create_ks == skytable::Element::RespCode(skytable::RespCode::Okay) ||
@ -260,7 +254,7 @@ fn generate_dbtest(
#body
let __switch_ks =
con.run_query_raw(
&skytable::query!("use", "testsuite")
&skytable::query!("use testsuite")
).await.unwrap();
if (__switch_ks != skytable::Element::RespCode(skytable::RespCode::Okay)) {
panic!("Failed to switch keyspace: {:?}", __switch_ks);
@ -280,7 +274,7 @@ fn generate_dbtest(
body = quote! {
#body
let mut __concat_entity = std::string::String::new();
__concat_entity.push_str("testsuite:");
__concat_entity.push_str("testsuite.");
__concat_entity.push_str(&#rand_string);
let __MYTABLE__ : String = #rand_string.to_string();
let __MYKS__: String = "testsuite".to_owned();
@ -291,7 +285,7 @@ fn generate_dbtest(
#body
let __switch_entity =
con.run_query_raw(
&skytable::query!("use", __concat_entity)
&skytable::query!(format!("use {}", __concat_entity))
).await.unwrap();
assert_eq!(
__switch_entity, skytable::Element::RespCode(skytable::RespCode::Okay), "Failed to switch"

Loading…
Cancel
Save