Merge pull request #173 from skytable/actions/pop

Add the `pop` action
next
Glydr 3 years ago committed by GitHub
commit ce9d830b5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -147,5 +147,12 @@
"args": "LSKEYS <limit>",
"desc": "Returns a flat string array of keys present in the database. If no <limit> is given, then a maximum of 10 keys are returned. If a limit is specified, then a maximum of <limit> keys are returned",
"return": "Returns a maximum of 10 keys if no limit is specified or returns a maximum number of keys for the given limit. The order of keys returned is meaningless."
},
{
"name": "POP",
"complexity": "O(n)",
"args": "POP <key1> <key2> ...",
"desc": "Deletes and returns the values of the provided keys. If the database is poisoned, this will return a server error. An exceptional scenario can arise when the database fails in-between removing all the keys. In that case, you get the server error response code instead of the keys. If the server recovers inbetween, then the appropriate values (if any) will be returned. In all other cases a NIL error is returned (code 1)",
"return": "Returns an array with either the values or response codes as the elements"
}
]

@ -41,6 +41,7 @@ pub mod lskeys;
pub mod mget;
pub mod mset;
pub mod mupdate;
pub mod pop;
pub mod set;
pub mod strong;
pub mod update;

@ -0,0 +1,61 @@
/*
* Created on Mon Jun 14 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::coredb;
use crate::dbnet::connection::prelude::*;
use crate::protocol::responses;
use crate::queryengine::ActionIter;
use crate::resp::BytesWrapper;
/// Run a POP action
pub async fn pop<T, Strm>(
handle: &coredb::CoreDB,
con: &mut T,
act: ActionIter,
) -> std::io::Result<()>
where
T: ProtocolConnectionExt<Strm>,
Strm: AsyncReadExt + AsyncWriteExt + Unpin + Send + Sync,
{
crate::err_if_len_is!(act, con, eq 0);
if handle.is_poisoned() {
// don't begin the operation at all if the database is poisoned
return con.write_response(&**responses::groups::SERVER_ERR).await;
}
con.write_array_length(act.len()).await?;
for key in act {
if handle.is_poisoned() {
// we keep this check just in case the server fails in-between running a
// pop operation
con.write_response(&**responses::groups::SERVER_ERR).await?;
} else if let Some((_key, val)) = handle.get_ref().remove(key.as_bytes()) {
con.write_response(BytesWrapper(val.into_inner())).await?;
} else {
con.write_response(&**responses::groups::NIL).await?;
}
}
Ok(())
}

@ -436,6 +436,9 @@ impl Data {
pub const fn get_blob(&self) -> &Bytes {
&self.blob
}
pub fn into_inner(self) -> Bytes {
self.blob
}
}
impl Eq for Data {}

@ -190,7 +190,7 @@ where
Box::pin(async move {
let mv_self = self;
let ret: IoResult<()> = {
mv_self.write_response(&SIMPLE_QUERY_HEADER[..]).await?;
mv_self.write_response(SIMPLE_QUERY_HEADER).await?;
Ok(())
};
ret
@ -208,9 +208,9 @@ where
Box::pin(async move {
let mv_self = self;
let ret: IoResult<()> = {
mv_self.write_response(&[b'_'][..]).await?;
mv_self.write_response([b'_']).await?;
mv_self.write_response(len.to_string().into_bytes()).await?;
mv_self.write_response(&[b'\n'][..]).await?;
mv_self.write_response([b'\n']).await?;
Ok(())
};
ret
@ -228,9 +228,9 @@ where
Box::pin(async move {
let mv_self = self;
let ret: IoResult<()> = {
mv_self.write_response(&[b'&'][..]).await?;
mv_self.write_response([b'&']).await?;
mv_self.write_response(len.to_string().into_bytes()).await?;
mv_self.write_response(&[b'\n'][..]).await?;
mv_self.write_response([b'\n']).await?;
Ok(())
};
ret

@ -93,7 +93,8 @@ where
USET => actions::uset::uset,
KEYLEN => actions::keylen::keylen,
MKSNAP => admin::mksnap::mksnap,
LSKEYS => actions::lskeys::lskeys
LSKEYS => actions::lskeys::lskeys,
POP => actions::pop::pop
);
Ok(())
}

@ -101,6 +101,15 @@ impl Writable for Vec<u8> {
}
}
impl<const N: usize> Writable for [u8; N] {
fn write<'s>(
self,
con: &'s mut impl IsConnection,
) -> Pin<Box<(dyn Future<Output = Result<(), IoError>> + Send + Sync + 's)>> {
Box::pin(async move { con.write_lowlevel(&self).await })
}
}
impl Writable for &'static [u8] {
fn write<'s>(
self,

@ -34,6 +34,22 @@
#[sky_macros::dbtest]
mod __private {
macro_rules! setkeys {
($con:ident, $($key:literal:$value:literal),*) => {
let mut q = Query::new();
q.push("MSET");
let mut count = 0;
$(
q.push($key);
q.push($value);
count += 1;
)*
assert_eq!(
$con.run_simple_query(&q).await.unwrap(),
Response::Item(Element::UnsignedInt(count))
);
};
}
#[cfg(test)]
use skytable::{Element, Query, RespCode, Response};
/// Test a HEYA query: The server should return HEY!
@ -1054,4 +1070,48 @@ mod __private {
Response::Item(Element::RespCode(RespCode::ActionError))
);
}
async fn test_pop_syntax_error() {
query.push("pop");
assert_eq!(
con.run_simple_query(&query).await.unwrap(),
Response::Item(Element::RespCode(RespCode::ActionError))
);
}
async fn test_pop_all_success() {
setkeys!(
con,
"x":100,
"y":200,
"z":300
);
query.push(vec!["pop", "x", "y", "z"]);
assert_eq!(
con.run_simple_query(&query).await.unwrap(),
Response::Item(Element::Array(vec![
Element::String("100".to_owned()),
Element::String("200".to_owned()),
Element::String("300".to_owned())
]))
)
}
async fn test_pop_mixed() {
setkeys!(
con,
"x":100,
"y":200,
"z":300
);
query.push(vec!["pop", "apple", "arnold", "x", "madonna", "y", "z"]);
assert_eq!(
con.run_simple_query(&query).await.unwrap(),
Response::Item(Element::Array(vec![
Element::RespCode(RespCode::NotFound),
Element::RespCode(RespCode::NotFound),
Element::String("100".to_owned()),
Element::RespCode(RespCode::NotFound),
Element::String("200".to_owned()),
Element::String("300".to_owned())
]))
);
}
}

Loading…
Cancel
Save