Add `select all`

next
Sayan Nandan 10 months ago
parent 01c684bd38
commit c2cb63e466
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

2
Cargo.lock generated

@ -1234,7 +1234,7 @@ dependencies = [
[[package]]
name = "skytable"
version = "0.8.0"
source = "git+https://github.com/skytable/client-rust.git?branch=octave#62e4f3152d56127e406b9d5eeea60696acd77031"
source = "git+https://github.com/skytable/client-rust.git?branch=octave#091aa94a696e22abd2ce10845e3040c48ea30720"
dependencies = [
"async-trait",
"bb8",

@ -44,6 +44,17 @@ pub fn format_response(resp: Response, print_special: bool) -> bool {
print_row(r);
println!();
}
Response::Rows(rows) => {
if rows.is_empty() {
println!("{}", "[0 rows returned]".grey().italic());
} else {
for (i, row) in rows.into_iter().enumerate().map(|(i, r)| (i + 1, r)) {
print!("{} ", format!("({i})").grey().bold());
print_row(row);
println!();
}
}
}
};
true
}

@ -45,6 +45,7 @@ pub fn delete(global: &impl GlobalInstanceLike, mut delete: DeleteStatement) ->
core::with_model_for_data_update(global, delete.entity(), |model| {
let g = sync::atm::cpin();
let delta_state = model.delta_state();
let _idx_latch = model.primary_index().acquire_cd();
// create new version
let new_version = delta_state.create_new_data_delta_version();
match model

@ -49,6 +49,7 @@ pub fn insert_resp(
pub fn insert(global: &impl GlobalInstanceLike, insert: InsertStatement) -> QueryResult<()> {
core::with_model_for_data_update(global, insert.entity(), |mdl| {
let (pk, data) = prepare_insert(mdl, insert.data())?;
let _idx_latch = mdl.primary_index().acquire_cd();
let g = cpin();
let ds = mdl.delta_state();
// create new version

@ -43,10 +43,15 @@ use crate::{
pub use {
del::delete,
ins::insert,
sel::select_custom,
sel::{select_custom, select_all},
upd::{collect_trace_path as update_flow_trace, update},
};
pub use {del::delete_resp, ins::insert_resp, sel::select_resp, upd::update_resp};
pub use {
del::delete_resp,
ins::insert_resp,
sel::{select_all_resp, select_resp},
upd::update_resp,
};
impl Model {
pub(self) fn resolve_where<'a>(

@ -25,17 +25,22 @@
*/
use crate::engine::{
core::index::DcFieldIndex,
core::{
index::{
DcFieldIndex, IndexLatchHandleExclusive, PrimaryIndexKey, Row, RowData, RowDataLck,
},
model::Model,
},
data::{
cell::{Datacell, VirtualDatacell},
tag::{DataTag, TagClass},
},
error::{QueryError, QueryResult},
fractal::GlobalInstanceLike,
idx::{STIndex, STIndexSeq},
idx::{IndexMTRaw, MTIndexExt, STIndex, STIndexSeq},
mem::IntegerRepr,
net::protocol::{Response, ResponseType},
ql::dml::sel::SelectStatement,
ql::dml::sel::{SelectAllStatement, SelectStatement},
sync,
};
@ -56,6 +61,83 @@ pub fn select_resp(
})
}
pub fn select_all_resp(
global: &impl GlobalInstanceLike,
select: SelectAllStatement,
) -> QueryResult<Response> {
let mut ret_buf = Vec::new();
let i = self::select_all(
global,
select,
&mut ret_buf,
|buf, mdl| {
IntegerRepr::scoped(mdl.fields().len() as u64, |repr| buf.extend(repr));
buf.push(b'\n');
},
|buf, data, _| encode_cell(buf, data),
)?;
Ok(Response::Serialized {
ty: ResponseType::MultiRow,
size: i,
data: ret_buf,
})
}
pub fn select_all<Fm, F, T>(
global: &impl GlobalInstanceLike,
select: SelectAllStatement,
serialize_target: &mut T,
mut f_mdl: Fm,
mut f: F,
) -> QueryResult<usize>
where
Fm: FnMut(&mut T, &Model),
F: FnMut(&mut T, &Datacell, usize),
{
global.namespace().with_model(select.entity, |mdl| {
let g = sync::atm::cpin();
let mut i = 0;
f_mdl(serialize_target, mdl);
if select.wildcard {
for (key, data) in RowIteratorAll::new(&g, mdl, select.limit as usize) {
let vdc = VirtualDatacell::new_pk(key, mdl.p_tag());
for key in mdl.fields().stseq_ord_key() {
let r = if key.as_str() == mdl.p_key() {
&*vdc
} else {
data.fields().get(key).unwrap()
};
f(serialize_target, r, mdl.fields().len());
}
i += 1;
}
} else {
// schema check
if select.fields.len() > mdl.fields().len()
|| select
.fields
.iter()
.any(|f| !mdl.fields().st_contains(f.as_str()))
{
return Err(QueryError::QExecUnknownField);
}
for (key, data) in RowIteratorAll::new(&g, mdl, select.limit as usize) {
let vdc = VirtualDatacell::new_pk(key, mdl.p_tag());
for key in select.fields.iter() {
let r = if key.as_str() == mdl.p_key() {
&*vdc
} else {
data.fields().st_get(key.as_str()).unwrap()
};
f(serialize_target, r, select.fields.len());
}
i += 1;
}
}
Ok(i)
})
}
fn encode_cell(resp: &mut Vec<u8>, item: &Datacell) {
resp.push((item.tag().tag_selector().value_u8() + 1) * (item.is_init() as u8));
if item.is_null() {
@ -100,7 +182,7 @@ where
{
global.namespace().with_model(select.entity(), |mdl| {
let target_key = mdl.resolve_where(select.clauses_mut())?;
let pkdc = VirtualDatacell::new(target_key.clone());
let pkdc = VirtualDatacell::new(target_key.clone(), mdl.p_tag().tag_unique());
let g = sync::atm::cpin();
let mut read_field = |key, fields: &DcFieldIndex| {
match fields.st_get(key) {
@ -128,3 +210,52 @@ where
Ok(())
})
}
struct RowIteratorAll<'g> {
_g: &'g sync::atm::Guard,
mdl: &'g Model,
iter: <IndexMTRaw<Row> as MTIndexExt<Row, PrimaryIndexKey, RowDataLck>>::IterEntry<'g, 'g, 'g>,
_latch: IndexLatchHandleExclusive<'g>,
limit: usize,
}
impl<'g> RowIteratorAll<'g> {
fn new(g: &'g sync::atm::Guard, mdl: &'g Model, limit: usize) -> Self {
let idx = mdl.primary_index();
let latch = idx.acquire_exclusive();
Self {
_g: g,
mdl,
iter: idx.__raw_index().mt_iter_entry(g),
_latch: latch,
limit,
}
}
fn _next(
&mut self,
) -> Option<(
&'g PrimaryIndexKey,
parking_lot::RwLockReadGuard<'g, RowData>,
)> {
if self.limit == 0 {
return None;
}
self.limit -= 1;
self.iter.next().map(|row| {
(
row.d_key(),
row.resolve_schema_deltas_and_freeze(self.mdl.delta_state()),
)
})
}
}
impl<'g> Iterator for RowIteratorAll<'g> {
type Item = (
&'g PrimaryIndexKey,
parking_lot::RwLockReadGuard<'g, RowData>,
);
fn next(&mut self) -> Option<Self::Item> {
self._next()
}
}

@ -195,15 +195,15 @@ fn cstate_use(
fn run_nb(
global: &Global,
cstate: &mut ClientLocalState,
state: State<'_, InplaceData>,
mut state: State<'_, InplaceData>,
stmt: KeywordStmt,
) -> QueryResult<Response> {
let stmt = stmt.value_u8() - KeywordStmt::Use.value_u8();
let stmt_c = stmt.value_u8() - KeywordStmt::Use.value_u8();
static F: [fn(
&Global,
&mut ClientLocalState,
&mut State<'static, InplaceData>,
) -> QueryResult<Response>; 8] = [
) -> QueryResult<Response>; 9] = [
cstate_use, // use
|g, c, s| _callgcs(g, c, s, ddl_misc::inspect),
|_, _, _| Err(QueryError::QLUnknownStatement), // describe
@ -212,12 +212,16 @@ fn run_nb(
|g, _, s| _callgs(g, s, dml::update_resp),
|g, _, s| _callgs(g, s, dml::delete_resp),
|_, _, _| Err(QueryError::QLUnknownStatement), // exists
|g, _, s| _callgs(g, s, dml::select_all_resp),
];
{
let n_offset_adjust = (stmt == KeywordStmt::Select) & state.cursor_rounded_eq(Token![all]);
state.cursor_ahead_if(n_offset_adjust);
let corrected_offset = (n_offset_adjust as u8 * 8) | (stmt_c * (!n_offset_adjust as u8));
let mut state = unsafe {
// UNSAFE(@ohsayan): this is a lifetime issue with the token handle
core::mem::transmute(state)
};
F[stmt as usize](global, cstate, &mut state)
F[corrected_offset as usize](global, cstate, &mut state)
}
}

@ -80,6 +80,9 @@ impl PrimaryIndexKey {
}
impl PrimaryIndexKey {
pub unsafe fn data(&self) -> SpecialPaddedWord {
core::mem::transmute_copy(&self.data)
}
pub unsafe fn read_uint(&self) -> u64 {
self.data.load()
}

@ -38,17 +38,27 @@ pub use {
row::{DcFieldIndex, Row, RowData},
};
pub type RowDataLck = parking_lot::RwLock<RowData>;
#[derive(Debug)]
pub struct PrimaryIndex {
data: IndexMTRaw<row::Row>,
latch: IndexLatch,
}
impl PrimaryIndex {
pub fn new_empty() -> Self {
Self {
data: IndexMTRaw::idx_init(),
latch: IndexLatch::new(),
}
}
pub fn acquire_cd(&self) -> IndexLatchHandleShared {
self.latch.gl_handle_shared()
}
pub fn acquire_exclusive(&self) -> IndexLatchHandleExclusive {
self.latch.gl_handle_exclusive()
}
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)
}
@ -59,3 +69,27 @@ impl PrimaryIndex {
self.data.mt_len()
}
}
#[derive(Debug)]
pub struct IndexLatchHandleShared<'t>(parking_lot::RwLockReadGuard<'t, ()>);
#[derive(Debug)]
pub struct IndexLatchHandleExclusive<'t>(parking_lot::RwLockWriteGuard<'t, ()>);
#[derive(Debug)]
struct IndexLatch {
glck: parking_lot::RwLock<()>,
}
impl IndexLatch {
fn new() -> Self {
Self {
glck: parking_lot::RwLock::new(()),
}
}
fn gl_handle_shared(&self) -> IndexLatchHandleShared {
IndexLatchHandleShared(self.glck.read())
}
fn gl_handle_exclusive(&self) -> IndexLatchHandleExclusive {
IndexLatchHandleExclusive(self.glck.write())
}
}

@ -356,6 +356,7 @@ impl Model {
space.get_uuid(),
model_name,
model.get_uuid(),
false,
);
}
// update global state

@ -240,12 +240,20 @@ impl Space {
GenericTask::delete_space_dir(&space_name, space.get_uuid()),
));
}
let space_uuid = space.get_uuid();
for model in space.models.into_iter() {
let e: EntityIDRef<'static> = unsafe {
// UNSAFE(@ohsayan): I want to try what the borrow checker has been trying
core::mem::transmute(EntityIDRef::new(space_name.as_str(), &model))
};
let _ = models.st_delete(&e);
let mdl = models.st_delete_return(&e).unwrap();
global.purge_model_driver(
&space_name,
space_uuid,
&model,
mdl.get_uuid(),
true,
);
}
let _ = spaces.st_delete(space_name.as_str());
Ok(())

@ -155,6 +155,32 @@ pub(self) fn exec_select(
_exec_only_select(global, select)
}
pub(self) fn exec_select_all(
global: &impl GlobalInstanceLike,
model: &str,
inserts: &[&str],
select: &str,
) -> QueryResult<Vec<Vec<Datacell>>> {
_exec_only_create_space_model(global, model)?;
for insert in inserts {
_exec_only_insert(global, insert, |_| {})?;
}
let lex_sel = lex_insecure(select.as_bytes()).unwrap();
let select = parse_ast_node_full(&lex_sel[2..]).unwrap();
let mut r: Vec<Vec<Datacell>> = Vec::new();
dml::select_all(
global,
select,
&mut r,
|_, _| {},
|rows, dc, col_cnt| match rows.last_mut() {
Some(row) if row.len() != col_cnt => row.push(dc.clone()),
_ => rows.push(vec![dc.clone()]),
},
)?;
Ok(r)
}
pub(self) fn exec_select_only(
global: &impl GlobalInstanceLike,
select: &str,

@ -24,7 +24,10 @@
*
*/
use crate::engine::{data::cell::Datacell, error::QueryError, fractal::test_utils::TestGlobal};
use {
crate::engine::{data::cell::Datacell, error::QueryError, fractal::test_utils::TestGlobal},
std::collections::HashMap,
};
#[test]
fn simple_select_wildcard() {
@ -100,3 +103,61 @@ fn select_nonexisting() {
QueryError::QExecDmlRowNotFound
);
}
/*
select all
*/
#[test]
fn select_all_wildcard() {
let global = TestGlobal::new_with_tmp_nullfs_driver();
let ret = super::exec_select_all(
&global,
"create model myspace.mymodel(username: string, password: string)",
&[
"insert into myspace.mymodel('sayan', 'password123')",
"insert into myspace.mymodel('robot', 'robot123')",
"insert into myspace.mymodel('douglas', 'galaxy123')",
"insert into myspace.mymodel('hgwells', 'timemachine')",
"insert into myspace.mymodel('orwell', '1984')",
],
"select all * from myspace.mymodel LIMIT 100",
)
.unwrap();
let ret: HashMap<String, Vec<Datacell>> = ret
.into_iter()
.map(|mut d| (d.swap_remove(0).into_str().unwrap(), d))
.collect();
assert_eq!(ret.get("sayan").unwrap(), &intovec!["password123"]);
assert_eq!(ret.get("robot").unwrap(), &intovec!["robot123"]);
assert_eq!(ret.get("douglas").unwrap(), &intovec!["galaxy123"]);
assert_eq!(ret.get("hgwells").unwrap(), &intovec!["timemachine"]);
assert_eq!(ret.get("orwell").unwrap(), &intovec!["1984"]);
}
#[test]
fn select_all_onefield() {
let global = TestGlobal::new_with_tmp_nullfs_driver();
let ret = super::exec_select_all(
&global,
"create model myspace.mymodel(username: string, password: string)",
&[
"insert into myspace.mymodel('sayan', 'password123')",
"insert into myspace.mymodel('robot', 'robot123')",
"insert into myspace.mymodel('douglas', 'galaxy123')",
"insert into myspace.mymodel('hgwells', 'timemachine')",
"insert into myspace.mymodel('orwell', '1984')",
],
"select all username from myspace.mymodel LIMIT 100",
)
.unwrap();
let ret: HashMap<String, Vec<Datacell>> = ret
.into_iter()
.map(|mut d| (d.swap_remove(0).into_str().unwrap(), d))
.collect();
assert_eq!(ret.get("sayan").unwrap(), &intovec![]);
assert_eq!(ret.get("robot").unwrap(), &intovec![]);
assert_eq!(ret.get("douglas").unwrap(), &intovec![]);
assert_eq!(ret.get("hgwells").unwrap(), &intovec![]);
assert_eq!(ret.get("orwell").unwrap(), &intovec![]);
}

@ -24,6 +24,10 @@
*
*/
use crate::engine::core::index::PrimaryIndexKey;
use super::tag::TagUnique;
use {
crate::engine::{
self,
@ -508,7 +512,8 @@ pub struct VirtualDatacell<'a> {
}
impl<'a> VirtualDatacell<'a> {
pub fn new(lit: Lit<'a>) -> Self {
pub fn new(lit: Lit<'a>, tag: TagUnique) -> Self {
debug_assert_eq!(lit.kind().tag_unique(), tag);
Self {
dc: ManuallyDrop::new(unsafe {
// UNSAFE(@ohsayan): this is a "reference" to a "virtual" aka fake DC. this just works because of memory layouts
@ -517,11 +522,14 @@ impl<'a> VirtualDatacell<'a> {
_lt: PhantomData,
}
}
}
impl<'a> From<Lit<'a>> for VirtualDatacell<'a> {
fn from(l: Lit<'a>) -> Self {
Self::new(l)
pub fn new_pk(pk: &'a PrimaryIndexKey, tag: FullTag) -> Self {
debug_assert_eq!(pk.tag(), tag.tag_unique());
Self {
dc: ManuallyDrop::new(unsafe {
Datacell::new(tag, DataRaw::word(pk.data().dwordqn_promote()))
}),
_lt: PhantomData,
}
}
}
@ -547,5 +555,8 @@ impl<'a> Clone for VirtualDatacell<'a> {
#[test]
fn virtual_dc_damn() {
let dc = Lit::new_str("hello, world");
assert_eq!(VirtualDatacell::from(dc), Datacell::from("hello, world"));
assert_eq!(
VirtualDatacell::new(dc, TagUnique::Str),
Datacell::from("hello, world")
);
}

@ -126,6 +126,7 @@ pub trait GlobalInstanceLike {
space_uuid: Uuid,
model_name: &str,
model_uuid: Uuid,
skip_delete: bool,
);
// taskmgr
fn taskmgr_post_high_priority(&self, task: Task<CriticalTask>);
@ -187,6 +188,7 @@ impl GlobalInstanceLike for Global {
space_uuid: Uuid,
model_name: &str,
model_uuid: Uuid,
skip_delete: bool,
) {
let id = ModelUniqueID::new(space_name, model_name, model_uuid);
self.get_state()
@ -194,9 +196,11 @@ impl GlobalInstanceLike for Global {
.write()
.remove(&id)
.expect("tried to remove non existent driver");
self.taskmgr_post_standard_priority(Task::new(GenericTask::delete_model_dir(
space_name, space_uuid, model_name, model_uuid,
)));
if !skip_delete {
self.taskmgr_post_standard_priority(Task::new(GenericTask::delete_model_dir(
space_name, space_uuid, model_name, model_uuid,
)));
}
}
fn initialize_model_driver(
&self,

@ -126,15 +126,18 @@ impl<Fs: RawFSInterface> GlobalInstanceLike for TestGlobal<Fs> {
space_uuid: Uuid,
model_name: &str,
model_uuid: Uuid,
skip_delete: bool,
) {
let id = ModelUniqueID::new(space_name, model_name, model_uuid);
self.model_drivers
.write()
.remove(&id)
.expect("tried to remove non-existent model");
self.taskmgr_post_standard_priority(Task::new(GenericTask::delete_model_dir(
space_name, space_uuid, model_name, model_uuid,
)));
if !skip_delete {
self.taskmgr_post_standard_priority(Task::new(GenericTask::delete_model_dir(
space_name, space_uuid, model_name, model_uuid,
)));
}
}
fn initialize_model_driver(
&self,

@ -150,6 +150,9 @@ pub trait MTIndex<E, K, V>: IndexBaseSpec {
't: 'v,
V: 'v,
Self: 't;
fn mt_iter_kv<'t, 'g, 'v>(&'t self, g: &'g Guard) -> Self::IterKV<'t, 'g, 'v>;
fn mt_iter_key<'t, 'g, 'v>(&'t self, g: &'g Guard) -> Self::IterKey<'t, 'g, 'v>;
fn mt_iter_val<'t, 'g, 'v>(&'t self, g: &'g Guard) -> Self::IterVal<'t, 'g, 'v>;
/// Returns the length of the index
fn mt_len(&self) -> usize;
/// Attempts to compact the backing storage
@ -217,6 +220,18 @@ pub trait MTIndex<E, K, V>: IndexBaseSpec {
'g: 't + 'v;
}
pub trait MTIndexExt<E, K, V>: MTIndex<E, K, V> {
type IterEntry<'t, 'g, 'v>: Iterator<Item = &'v E>
where
'g: 't + 'v,
't: 'v,
K: 'v,
V: 'v,
E: 'v,
Self: 't;
fn mt_iter_entry<'t, 'g, 'v>(&'t self, g: &'g Guard) -> Self::IterEntry<'t, 'g, 'v>;
}
/// An unordered STIndex
pub trait STIndex<K: ?Sized, V>: IndexBaseSpec {
/// An iterator over the keys and values

@ -24,6 +24,8 @@
*
*/
use crate::engine::idx::MTIndexExt;
#[cfg(debug_assertions)]
use super::CHTRuntimeLog;
use {
@ -63,6 +65,20 @@ impl<E, C: Config> IndexBaseSpec for Raw<E, C> {
}
}
impl<E: TreeElement, C: Config> MTIndexExt<E, E::Key, E::Value> for Raw<E, C> {
type IterEntry<'t, 'g, 'v> = super::iter::IterEntry<'t, 'g, 'v, E, C>
where
'g: 't + 'v,
't: 'v,
E::Key: 'v,
E::Value: 'v,
E: 'v,
Self: 't;
fn mt_iter_entry<'t, 'g, 'v>(&'t self, g: &'g Guard) -> Self::IterEntry<'t, 'g, 'v> {
super::iter::IterEntry::new(self, g)
}
}
impl<E: TreeElement, C: Config> MTIndex<E, E::Key, E::Value> for Raw<E, C> {
type IterKV<'t, 'g, 'v> = IterKV<'t, 'g, 'v, E, C>
where
@ -86,6 +102,18 @@ impl<E: TreeElement, C: Config> MTIndex<E, E::Key, E::Value> for Raw<E, C> {
E::Value: 'v,
Self: 't;
fn mt_iter_kv<'t, 'g, 'v>(&'t self, g: &'g Guard) -> Self::IterKV<'t, 'g, 'v> {
super::iter::IterKV::new(self, g)
}
fn mt_iter_key<'t, 'g, 'v>(&'t self, g: &'g Guard) -> Self::IterKey<'t, 'g, 'v> {
super::iter::IterKey::new(self, g)
}
fn mt_iter_val<'t, 'g, 'v>(&'t self, g: &'g Guard) -> Self::IterVal<'t, 'g, 'v> {
super::iter::IterVal::new(self, g)
}
fn mt_len(&self) -> usize {
self.len()
}

@ -74,6 +74,44 @@ where
}
}
pub struct IterEntry<'t, 'g, 'v, T, C>
where
't: 'v,
'g: 'v + 't,
C: Config,
T: TreeElement,
{
i: RawIter<'t, 'g, 'v, T, C, CfgIterEntry>,
}
impl<'t, 'g, 'v, T, C> IterEntry<'t, 'g, 'v, T, C>
where
't: 'v,
'g: 'v + 't,
C: Config,
T: TreeElement,
{
pub fn new(t: &'t RawTree<T, C>, g: &'g Guard) -> Self {
Self {
i: RawIter::new(t, g),
}
}
}
impl<'t, 'g, 'v, T, C> Iterator for IterEntry<'t, 'g, 'v, T, C>
where
't: 'v,
'g: 'v + 't,
C: Config,
T: TreeElement,
{
type Item = &'v T;
fn next(&mut self) -> Option<Self::Item> {
self.i.next()
}
}
pub struct IterKey<'t, 'g, 'v, T, C>
where
't: 'v,
@ -157,6 +195,14 @@ trait IterConfig<T> {
fn some<'a>(v: &'a T) -> Option<Self::Ret<'a>>;
}
struct CfgIterEntry;
impl<T: TreeElement> IterConfig<T> for CfgIterEntry {
type Ret<'a> = &'a T where T: 'a;
fn some<'a>(v: &'a T) -> Option<Self::Ret<'a>> {
Some(v)
}
}
struct CfgIterKV;
impl<T: TreeElement> IterConfig<T> for CfgIterKV {
type Ret<'a> = (&'a T::Key, &'a T::Value) where T: 'a;

@ -26,7 +26,7 @@
mod access;
pub mod imp;
mod iter;
pub(super) mod iter;
pub mod meta;
mod patch;
#[cfg(test)]

@ -326,6 +326,9 @@ macro_rules! Token {
(allow) => {
__kw_misc!(Allow)
};
(all) => {
__kw_misc!(All)
}
}
macro_rules! union {

@ -74,6 +74,7 @@ pub enum ResponseType {
Error = 0x10,
Row = 0x11,
Empty = 0x12,
MultiRow = 0x13,
}
#[derive(Debug, PartialEq)]

@ -224,7 +224,7 @@ impl<'a, Qd: QueryData<'a>> State<'a, Qd> {
/// Check if the current cursor can read a lit (with context from the data source); rounded
pub fn can_read_lit_rounded(&self) -> bool {
let mx = self.round_cursor();
Qd::can_read_lit_from(&self.d, &self.t[mx]) && mx == self.i
Qd::can_read_lit_from(&self.d, &self.t[mx]) & (mx == self.i)
}
#[inline(always)]
/// Check if a lit can be read using the given token with context from the data source
@ -252,7 +252,7 @@ impl<'a, Qd: QueryData<'a>> State<'a, Qd> {
/// Check if the cursor equals the given token; rounded
pub fn cursor_rounded_eq(&self, tok: Token<'a>) -> bool {
let mx = self.round_cursor();
self.t[mx] == tok && mx == self.i
(self.t[mx] == tok) & (mx == self.i)
}
#[inline(always)]
/// Check if the cursor equals the given token
@ -271,7 +271,7 @@ impl<'a, Qd: QueryData<'a>> State<'a, Qd> {
}
#[inline(always)]
pub(crate) fn cursor_has_ident_rounded(&self) -> bool {
self.offset_current_r(0).is_ident() && self.not_exhausted()
self.offset_current_r(0).is_ident() & self.not_exhausted()
}
#[inline(always)]
/// Check if the current token stream matches the signature of an arity(0) fn; rounded
@ -296,7 +296,7 @@ impl<'a, Qd: QueryData<'a>> State<'a, Qd> {
#[inline(always)]
/// Loop condition for tt and non-poisoned state only
pub fn loop_tt(&self) -> bool {
self.not_exhausted() && self.okay()
self.not_exhausted() & self.okay()
}
#[inline(always)]
/// Returns the position of the cursor

@ -152,9 +152,87 @@ impl<'a> SelectStatement<'a> {
}
}
#[derive(Debug, PartialEq)]
pub struct SelectAllStatement<'a> {
pub entity: EntityIDRef<'a>,
pub fields: Vec<Ident<'a>>,
pub wildcard: bool,
pub limit: u64,
}
impl<'a> SelectAllStatement<'a> {
#[cfg(test)]
pub fn test_new(
entity: EntityIDRef<'a>,
fields: Vec<Ident<'a>>,
wildcard: bool,
limit: u64,
) -> Self {
Self::new(entity, fields, wildcard, limit)
}
fn new(entity: EntityIDRef<'a>, fields: Vec<Ident<'a>>, wildcard: bool, limit: u64) -> Self {
Self {
entity,
fields,
wildcard,
limit,
}
}
fn parse<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
/*
smallest query: select all * from mymodel limit 10
*/
if state.remaining() < 5 {
return Err(QueryError::QLUnexpectedEndOfStatement);
}
let mut select_fields = Vec::new();
let is_wildcard = state.cursor_eq(Token![*]);
state.cursor_ahead_if(is_wildcard);
while state.not_exhausted() && state.okay() && !is_wildcard {
match state.read() {
Token::Ident(id) => select_fields.push(*id),
_ => break,
}
state.cursor_ahead();
let nx_comma = state.cursor_rounded_eq(Token![,]);
let nx_from = state.cursor_rounded_eq(Token![from]);
state.poison_if_not(nx_comma | nx_from);
state.cursor_ahead_if(nx_comma);
}
state.poison_if_not(is_wildcard | !select_fields.is_empty());
if state.remaining() < 4 {
return Err(QueryError::QLUnexpectedEndOfStatement);
}
state.poison_if_not(state.cursor_eq(Token![from]));
state.cursor_ahead(); // ignore error
let entity = state.try_entity_buffered_into_state_uninit();
state.poison_if_not(state.cursor_rounded_eq(Token![limit]));
state.cursor_ahead_if(state.okay()); // we did read limit
state.poison_if(state.exhausted()); // we MUST have the limit
if state.okay() {
let lit = unsafe { state.fw_read().uck_read_lit() };
match lit.try_uint() {
Some(limit) => {
return unsafe {
// UNSAFE(@ohsayan): state guarantees this works
Ok(Self::new(
entity.assume_init(),
select_fields,
is_wildcard,
limit,
))
};
}
_ => {}
}
}
Err(QueryError::QLInvalidSyntax)
}
}
mod impls {
use {
super::SelectStatement,
super::{SelectAllStatement, SelectStatement},
crate::engine::{
error::QueryResult,
ql::ast::{traits::ASTNode, QueryData, State},
@ -169,4 +247,13 @@ mod impls {
Self::parse_select(state)
}
}
impl<'a> ASTNode<'a> for SelectAllStatement<'a> {
const MUST_USE_FULL_TOKEN_RANGE: bool = true;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
Self::parse(state)
}
}
}

@ -106,6 +106,9 @@ macro_rules! into_vec {
($ty:ty => ($($v:expr),* $(,)?)) => {{
let v: Vec<$ty> = std::vec![$($v.into(),)*];
v
}};
($($v:expr),*) => {{
std::vec![$($v.into(),)*]
}}
}

@ -957,3 +957,50 @@ mod where_clause {
assert!(parse_ast_node_full::<WhereClause>(&tok).is_err());
}
}
mod select_all {
use {
super::lex_insecure,
crate::engine::{
error::QueryError,
ql::{ast::parse_ast_node_full_with_space, dml::sel::SelectAllStatement},
},
};
#[test]
fn select_all_wildcard() {
let tok = lex_insecure(b"select all * from mymodel limit 100").unwrap();
assert_eq!(
parse_ast_node_full_with_space::<SelectAllStatement>(&tok[2..], "myspace").unwrap(),
SelectAllStatement::test_new(("myspace", "mymodel").into(), vec![], true, 100)
);
}
#[test]
fn select_all_multiple_fields() {
let tok = lex_insecure(b"select all username, password from mymodel limit 100").unwrap();
assert_eq!(
parse_ast_node_full_with_space::<SelectAllStatement>(&tok[2..], "myspace").unwrap(),
SelectAllStatement::test_new(
("myspace", "mymodel").into(),
into_vec!["username", "password"],
false,
100
)
);
}
#[test]
fn select_all_missing_limit() {
let tok = lex_insecure(b"select all * from mymodel").unwrap();
assert_eq!(
parse_ast_node_full_with_space::<SelectAllStatement>(&tok[2..], "myspace").unwrap_err(),
QueryError::QLUnexpectedEndOfStatement
);
let tok = lex_insecure(b"select all username, password from mymodel").unwrap();
assert_eq!(
parse_ast_node_full_with_space::<SelectAllStatement>(&tok[2..], "myspace").unwrap_err(),
QueryError::QLUnexpectedEndOfStatement
);
}
}

Loading…
Cancel
Save