|
|
@ -38,8 +38,8 @@
|
|
|
|
use crate::coredb::Data;
|
|
|
|
use crate::coredb::Data;
|
|
|
|
use crate::dbnet::connection::prelude::*;
|
|
|
|
use crate::dbnet::connection::prelude::*;
|
|
|
|
use crate::protocol::responses;
|
|
|
|
use crate::protocol::responses;
|
|
|
|
|
|
|
|
use crate::queryengine::ActionIter;
|
|
|
|
use std::hint::unreachable_unchecked;
|
|
|
|
use core::hint::unreachable_unchecked;
|
|
|
|
|
|
|
|
|
|
|
|
/// Run an `SSET` query
|
|
|
|
/// Run an `SSET` query
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -48,17 +48,17 @@ use std::hint::unreachable_unchecked;
|
|
|
|
pub async fn sset<T, Strm>(
|
|
|
|
pub async fn sset<T, Strm>(
|
|
|
|
handle: &crate::coredb::CoreDB,
|
|
|
|
handle: &crate::coredb::CoreDB,
|
|
|
|
con: &mut T,
|
|
|
|
con: &mut T,
|
|
|
|
act: Vec<String>,
|
|
|
|
mut act: ActionIter,
|
|
|
|
) -> std::io::Result<()>
|
|
|
|
) -> std::io::Result<()>
|
|
|
|
where
|
|
|
|
where
|
|
|
|
T: ProtocolConnectionExt<Strm>,
|
|
|
|
T: ProtocolConnectionExt<Strm>,
|
|
|
|
Strm: AsyncReadExt + AsyncWriteExt + Unpin + Send + Sync,
|
|
|
|
Strm: AsyncReadExt + AsyncWriteExt + Unpin + Send + Sync,
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let howmany = act.len() - 1;
|
|
|
|
let howmany = act.len();
|
|
|
|
if howmany & 1 == 1 || howmany == 0 {
|
|
|
|
if howmany & 1 == 1 || howmany == 0 {
|
|
|
|
return con.write_response(&**responses::groups::ACTION_ERR).await;
|
|
|
|
return con.write_response(&**responses::groups::ACTION_ERR).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut failed = Some(false);
|
|
|
|
let failed;
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// We use this additional scope to tell the compiler that the write lock
|
|
|
|
// We use this additional scope to tell the compiler that the write lock
|
|
|
|
// doesn't go beyond the scope of this function - and is never used across
|
|
|
|
// doesn't go beyond the scope of this function - and is never used across
|
|
|
@ -66,34 +66,16 @@ where
|
|
|
|
|
|
|
|
|
|
|
|
// This iterator gives us the keys and values, skipping the first argument which
|
|
|
|
// This iterator gives us the keys and values, skipping the first argument which
|
|
|
|
// is the action name
|
|
|
|
// is the action name
|
|
|
|
let mut key_iter = act
|
|
|
|
let mut key_iter = act.as_ref().iter();
|
|
|
|
.get(1..)
|
|
|
|
|
|
|
|
.unwrap_or_else(|| unsafe {
|
|
|
|
|
|
|
|
// UNSAFE(@ohsayan): We've already checked if the action group contains more than one arugment
|
|
|
|
|
|
|
|
unreachable_unchecked()
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.iter();
|
|
|
|
|
|
|
|
if handle.is_poisoned() {
|
|
|
|
if handle.is_poisoned() {
|
|
|
|
failed = None;
|
|
|
|
failed = None;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
let mut_table = handle.get_ref();
|
|
|
|
let mut_table = handle.get_ref();
|
|
|
|
while let Some(key) = key_iter.next() {
|
|
|
|
if key_iter.all(|key| !mut_table.contains_key(key.as_bytes())) {
|
|
|
|
if mut_table.contains_key(key.as_bytes()) {
|
|
|
|
failed = Some(false);
|
|
|
|
// With one of the keys existing - this action can't clearly be done
|
|
|
|
|
|
|
|
// So we'll set `failed` to true and ensure that we check this while
|
|
|
|
|
|
|
|
// writing a response back to the client
|
|
|
|
|
|
|
|
failed = Some(true);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if !failed.unwrap_or_else(|| unsafe {
|
|
|
|
|
|
|
|
// UNSAFE(@ohsayan): Completely safe because we've already set a value for `failed` earlier
|
|
|
|
|
|
|
|
unreachable_unchecked()
|
|
|
|
|
|
|
|
}) {
|
|
|
|
|
|
|
|
// Since the failed flag is false, none of the keys existed
|
|
|
|
// Since the failed flag is false, none of the keys existed
|
|
|
|
// So we can safely set the keys
|
|
|
|
// So we can safely set the keys
|
|
|
|
let mut iter = act.into_iter().skip(1);
|
|
|
|
while let (Some(key), Some(value)) = (act.next(), act.next()) {
|
|
|
|
while let (Some(key), Some(value)) = (iter.next(), iter.next()) {
|
|
|
|
|
|
|
|
if !mut_table.true_if_insert(Data::from(key), Data::from_string(value)) {
|
|
|
|
if !mut_table.true_if_insert(Data::from(key), Data::from_string(value)) {
|
|
|
|
// Tell the compiler that this will never be the case
|
|
|
|
// Tell the compiler that this will never be the case
|
|
|
|
unsafe {
|
|
|
|
unsafe {
|
|
|
@ -104,6 +86,8 @@ where
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
failed = Some(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -126,49 +110,31 @@ where
|
|
|
|
pub async fn sdel<T, Strm>(
|
|
|
|
pub async fn sdel<T, Strm>(
|
|
|
|
handle: &crate::coredb::CoreDB,
|
|
|
|
handle: &crate::coredb::CoreDB,
|
|
|
|
con: &mut T,
|
|
|
|
con: &mut T,
|
|
|
|
act: Vec<String>,
|
|
|
|
act: ActionIter,
|
|
|
|
) -> std::io::Result<()>
|
|
|
|
) -> std::io::Result<()>
|
|
|
|
where
|
|
|
|
where
|
|
|
|
T: ProtocolConnectionExt<Strm>,
|
|
|
|
T: ProtocolConnectionExt<Strm>,
|
|
|
|
Strm: AsyncReadExt + AsyncWriteExt + Unpin + Send + Sync,
|
|
|
|
Strm: AsyncReadExt + AsyncWriteExt + Unpin + Send + Sync,
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let howmany = act.len() - 1;
|
|
|
|
let howmany = act.len();
|
|
|
|
if howmany == 0 {
|
|
|
|
if howmany == 0 {
|
|
|
|
return con.write_response(&**responses::groups::ACTION_ERR).await;
|
|
|
|
return con.write_response(&**responses::groups::ACTION_ERR).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut failed = Some(false);
|
|
|
|
let failed;
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// We use this additional scope to tell the compiler that the write lock
|
|
|
|
// We use this additional scope to tell the compiler that the write lock
|
|
|
|
// doesn't go beyond the scope of this function - and is never used across
|
|
|
|
// doesn't go beyond the scope of this function - and is never used across
|
|
|
|
// an await: cause, the compiler ain't as smart as we are ;)
|
|
|
|
// an await: cause, the compiler ain't as smart as we are ;)
|
|
|
|
let mut key_iter = act
|
|
|
|
let mut key_iter = act.as_ref().iter();
|
|
|
|
.get(1..)
|
|
|
|
|
|
|
|
.unwrap_or_else(|| unsafe {
|
|
|
|
|
|
|
|
// UNSAFE(@ohsayan): We've already checked if the action group contains more than one arugment
|
|
|
|
|
|
|
|
unreachable_unchecked()
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.iter();
|
|
|
|
|
|
|
|
if handle.is_poisoned() {
|
|
|
|
if handle.is_poisoned() {
|
|
|
|
failed = None;
|
|
|
|
failed = None;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
let mut_table = handle.get_ref();
|
|
|
|
let mut_table = handle.get_ref();
|
|
|
|
while let Some(key) = key_iter.next() {
|
|
|
|
if key_iter.all(|key| mut_table.contains_key(key.as_bytes())) {
|
|
|
|
if !mut_table.contains_key(key.as_bytes()) {
|
|
|
|
failed = Some(false);
|
|
|
|
// With one of the keys not existing - this action can't clearly be done
|
|
|
|
|
|
|
|
// So we'll set `failed` to true and ensure that we check this while
|
|
|
|
|
|
|
|
// writing a response back to the client
|
|
|
|
|
|
|
|
failed = Some(true);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if !failed.unwrap_or_else(|| unsafe {
|
|
|
|
|
|
|
|
// UNSAFE(@ohsayan): Again, completely safe as we always assign a value to
|
|
|
|
|
|
|
|
// `failed`
|
|
|
|
|
|
|
|
unreachable_unchecked()
|
|
|
|
|
|
|
|
}) {
|
|
|
|
|
|
|
|
// Since the failed flag is false, all of the keys exist
|
|
|
|
// Since the failed flag is false, all of the keys exist
|
|
|
|
// So we can safely delete the keys
|
|
|
|
// So we can safely delete the keys
|
|
|
|
act.into_iter().skip(1).for_each(|key| {
|
|
|
|
act.into_iter().for_each(|key| {
|
|
|
|
// Since we've already checked that the keys don't exist
|
|
|
|
// Since we've already checked that the keys don't exist
|
|
|
|
// We'll tell the compiler to optimize this
|
|
|
|
// We'll tell the compiler to optimize this
|
|
|
|
let _ = mut_table.remove(key.as_bytes()).unwrap_or_else(|| unsafe {
|
|
|
|
let _ = mut_table.remove(key.as_bytes()).unwrap_or_else(|| unsafe {
|
|
|
@ -177,6 +143,8 @@ where
|
|
|
|
unreachable_unchecked()
|
|
|
|
unreachable_unchecked()
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
failed = Some(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -198,13 +166,13 @@ where
|
|
|
|
pub async fn supdate<T, Strm>(
|
|
|
|
pub async fn supdate<T, Strm>(
|
|
|
|
handle: &crate::coredb::CoreDB,
|
|
|
|
handle: &crate::coredb::CoreDB,
|
|
|
|
con: &mut T,
|
|
|
|
con: &mut T,
|
|
|
|
act: Vec<String>,
|
|
|
|
mut act: ActionIter,
|
|
|
|
) -> std::io::Result<()>
|
|
|
|
) -> std::io::Result<()>
|
|
|
|
where
|
|
|
|
where
|
|
|
|
T: ProtocolConnectionExt<Strm>,
|
|
|
|
T: ProtocolConnectionExt<Strm>,
|
|
|
|
Strm: AsyncReadExt + AsyncWriteExt + Unpin + Send + Sync,
|
|
|
|
Strm: AsyncReadExt + AsyncWriteExt + Unpin + Send + Sync,
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let howmany = act.len() - 1;
|
|
|
|
let howmany = act.len();
|
|
|
|
if howmany & 1 == 1 || howmany == 0 {
|
|
|
|
if howmany & 1 == 1 || howmany == 0 {
|
|
|
|
return con.write_response(&**responses::groups::ACTION_ERR).await;
|
|
|
|
return con.write_response(&**responses::groups::ACTION_ERR).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -213,13 +181,7 @@ where
|
|
|
|
// We use this additional scope to tell the compiler that the write lock
|
|
|
|
// We use this additional scope to tell the compiler that the write lock
|
|
|
|
// doesn't go beyond the scope of this function - and is never used across
|
|
|
|
// doesn't go beyond the scope of this function - and is never used across
|
|
|
|
// an await: cause, the compiler ain't as smart as we are ;)
|
|
|
|
// an await: cause, the compiler ain't as smart as we are ;)
|
|
|
|
let mut key_iter = act
|
|
|
|
let mut key_iter = act.as_ref().iter();
|
|
|
|
.get(1..)
|
|
|
|
|
|
|
|
.unwrap_or_else(|| unsafe {
|
|
|
|
|
|
|
|
// UNSAFE(@ohsayan): We've already checked if the action group contains more than one arugment
|
|
|
|
|
|
|
|
unreachable_unchecked()
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.iter();
|
|
|
|
|
|
|
|
if handle.is_poisoned() {
|
|
|
|
if handle.is_poisoned() {
|
|
|
|
failed = None;
|
|
|
|
failed = None;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -245,8 +207,7 @@ where
|
|
|
|
}) {
|
|
|
|
}) {
|
|
|
|
// Since the failed flag is false, none of the keys existed
|
|
|
|
// Since the failed flag is false, none of the keys existed
|
|
|
|
// So we can safely update the keys
|
|
|
|
// So we can safely update the keys
|
|
|
|
let mut iter = act.into_iter().skip(1);
|
|
|
|
while let (Some(key), Some(value)) = (act.next(), act.next()) {
|
|
|
|
while let (Some(key), Some(value)) = (iter.next(), iter.next()) {
|
|
|
|
|
|
|
|
if !mut_table.true_if_update(Data::from(key), Data::from_string(value)) {
|
|
|
|
if !mut_table.true_if_update(Data::from(key), Data::from_string(value)) {
|
|
|
|
// Tell the compiler that this will never be the case
|
|
|
|
// Tell the compiler that this will never be the case
|
|
|
|
unsafe { unreachable_unchecked() }
|
|
|
|
unsafe { unreachable_unchecked() }
|