Add parsing for select queries

next
Sayan Nandan 2 years ago
parent fe3e20ffd7
commit ee7196d499
No known key found for this signature in database
GPG Key ID: 8BC07A0A4D41DD52

@ -61,16 +61,16 @@ impl Entity {
Entity::Partial(extract!(&sl[1], Token::Ident(sl) => sl.clone()))
}
#[inline(always)]
pub(super) fn tokens_with_partial(sl: &[Token]) -> bool {
sl.len() > 1 && sl[0] == Token![:] && sl[1].is_ident()
pub(super) fn tokens_with_partial(tok: &[Token]) -> bool {
tok.len() > 1 && tok[0] == Token![:] && tok[1].is_ident()
}
#[inline(always)]
pub(super) fn tokens_with_single(sl: &[Token]) -> bool {
!sl.is_empty() && sl[0].is_ident()
pub(super) fn tokens_with_single(tok: &[Token]) -> bool {
!tok.is_empty() && tok[0].is_ident()
}
#[inline(always)]
pub(super) fn tokens_with_full(sl: &[Token]) -> bool {
sl.len() > 2 && sl[0].is_ident() && sl[1] == Token![.] && sl[2].is_ident()
pub(super) fn tokens_with_full(tok: &[Token]) -> bool {
tok.len() > 2 && tok[0].is_ident() && tok[1] == Token![.] && tok[2].is_ident()
}
pub(super) fn parse(cm: &mut Compiler) -> LangResult<Self> {
let sl = cm.remslice();

@ -24,21 +24,42 @@
*
*/
use crate::util::MaybeInit;
/*
TODO(@ohsayan): For now we've settled for an imprecise error site reporting for simplicity, which we
should augment in future revisions of the QL engine
*/
use {
super::{
ast::Entity,
lexer::{Lit, Symbol, Token},
LangError, LangResult,
LangError, LangResult, RawSlice,
},
crate::engine::memory::DataType,
crate::{engine::memory::DataType, util::MaybeInit},
std::{
collections::HashMap,
mem::{discriminant, Discriminant},
},
};
/*
Misc
*/
#[inline(always)]
fn process_entity(tok: &[Token], d: &mut MaybeInit<Entity>, i: &mut usize) -> bool {
let is_full = Entity::tokens_with_full(tok);
let is_single = Entity::tokens_with_single(tok);
if is_full {
*i += 3;
*d = MaybeInit::new(unsafe { Entity::full_entity_from_slice(tok) })
} else if is_single {
*i += 1;
*d = MaybeInit::new(unsafe { Entity::single_entity_from_slice(tok) });
}
is_full | is_single
}
/*
Impls for insert
*/
@ -155,7 +176,8 @@ pub(super) fn parse_data_tuple_syntax(tok: &[Token]) -> (Vec<Option<DataType>>,
#[cfg(test)]
pub(super) fn parse_data_tuple_syntax_full(tok: &[Token]) -> Option<Vec<Option<DataType>>> {
let (ret, cnt, okay) = parse_data_tuple_syntax(tok);
if cnt == tok.len() && okay {
assert!(cnt == tok.len(), "didn't use full length");
if okay {
Some(ret)
} else {
None
@ -222,7 +244,8 @@ pub(super) fn parse_data_map_syntax_full(
tok: &[Token],
) -> Option<HashMap<Box<str>, Option<DataType>>> {
let (dat, i, ok) = parse_data_map_syntax(tok);
if i == tok.len() && ok {
assert!(i == tok.len(), "didn't use full length");
if ok {
Some(
dat.into_iter()
.map(|(ident, val)| {
@ -280,13 +303,7 @@ pub(super) fn parse_insert<'a>(
let mut i = 0;
let mut entity = MaybeInit::uninit();
if is_full {
i += 3;
entity = MaybeInit::new(unsafe { Entity::full_entity_from_slice(src) });
} else if is_half {
i += 1;
entity = MaybeInit::new(unsafe { Entity::single_entity_from_slice(src) });
}
okay &= process_entity(&src[i..], &mut entity, &mut i);
// primary key is a lit; atleast lit + (<oparen><cparen>) | (<obrace><cbrace>)
okay &= l >= (i + 4);
@ -334,9 +351,84 @@ pub(super) fn parse_insert<'a>(
pub(super) fn parse_insert_full<'a>(tok: &'a [Token]) -> Option<InsertStatement<'a>> {
let mut z = 0;
let s = self::parse_insert(tok, &mut z);
if z == tok.len() {
s.ok()
assert!(z == tok.len(), "didn't use full length");
s.ok()
}
/*
Impls for select
*/
#[derive(Debug, PartialEq)]
pub(super) struct SelectStatement<'a> {
/// the primary key
pub(super) primary_key: &'a Lit,
/// the entity
pub(super) entity: Entity,
/// fields in order of querying. will be zero when wildcard is set
pub(super) fields: Vec<RawSlice>,
/// whether a wildcard was passed
pub(super) wildcard: bool,
}
/// Parse a `select` query. The cursor should have already passed the `select` token when this
/// function is called.
pub(super) fn parse_select<'a>(
tok: &'a [Token],
counter: &mut usize,
) -> LangResult<SelectStatement<'a>> {
let l = tok.len();
let mut i = 0_usize;
let mut okay = l > 4;
let mut fields = Vec::new();
let is_wildcard = i < l && tok[i] == Token![*];
i += is_wildcard as usize;
while okay && i < l && tok[i].is_ident() && !is_wildcard {
unsafe {
fields.push(extract!(&tok[i], Token::Ident(id) => id.clone()));
}
i += 1;
// skip comma
let nx_comma = i < l && tok[i] == Token![,];
let nx_from = i < l && tok[i] == Token![from];
okay &= nx_comma | nx_from;
i += nx_comma as usize;
}
okay &= i < l && tok[i] == Token![from];
i += okay as usize;
// parsed upto select a, b, c from ...; now parse entity and select
let mut entity = MaybeInit::uninit();
okay &= process_entity(&tok[i..], &mut entity, &mut i);
// now primary key
okay &= i < l && tok[i] == Token![:];
i += okay as usize;
okay &= i < l && tok[i].is_lit();
*counter += i + okay as usize;
if okay {
let primary_key = unsafe { extract!(tok[i], Token::Lit(ref l) => l) };
Ok(SelectStatement {
primary_key,
entity: unsafe { entity.assume_init() },
fields,
wildcard: is_wildcard,
})
} else {
None
Err(LangError::UnexpectedToken)
}
}
#[cfg(test)]
/// **test-mode only** parse for a `select` where the full token stream is exhausted
pub(super) fn parse_select_full<'a>(tok: &'a [Token]) -> Option<SelectStatement<'a>> {
let mut i = 0;
let r = self::parse_select(tok, &mut i);
assert!(i == tok.len(), "didn't use full length");
r.ok()
}

@ -1954,4 +1954,75 @@ mod dml_tests {
assert_eq!(r, e);
}
}
mod stmt_select {
use {
super::*,
crate::engine::ql::{
ast::Entity,
dml::{self, SelectStatement},
lexer::Lit,
},
};
#[test]
fn select_mini() {
let tok = lex(br#"
select * from user:"sayan"
"#)
.unwrap();
let r = dml::parse_select_full(&tok[1..]).unwrap();
let e = SelectStatement {
primary_key: &Lit::Str("sayan".into()),
entity: Entity::Single("user".into()),
fields: [].to_vec(),
wildcard: true,
};
assert_eq!(r, e);
}
#[test]
fn select() {
let tok = lex(br#"
select field1 from user:"sayan"
"#)
.unwrap();
let r = dml::parse_select_full(&tok[1..]).unwrap();
let e = SelectStatement {
primary_key: &Lit::Str("sayan".into()),
entity: Entity::Single("user".into()),
fields: ["field1".into()].to_vec(),
wildcard: false,
};
assert_eq!(r, e);
}
#[test]
fn select_pro() {
let tok = lex(br#"
select field1 from twitter.user:"sayan"
"#)
.unwrap();
let r = dml::parse_select_full(&tok[1..]).unwrap();
let e = SelectStatement {
primary_key: &Lit::Str("sayan".into()),
entity: Entity::Full("twitter".into(), "user".into()),
fields: ["field1".into()].to_vec(),
wildcard: false,
};
assert_eq!(r, e);
}
#[test]
fn select_pro_max() {
let tok = lex(br#"
select field1, field2 from twitter.user:"sayan"
"#)
.unwrap();
let r = dml::parse_select_full(&tok[1..]).unwrap();
let e = SelectStatement {
primary_key: &Lit::Str("sayan".into()),
entity: Entity::Full("twitter".into(), "user".into()),
fields: ["field1".into(), "field2".into()].to_vec(),
wildcard: false,
};
assert_eq!(r, e);
}
}
}

@ -28,9 +28,13 @@
macro_rules! impossible {
() => {{
if cfg!(debug_assertions) {
panic!("called unreachable code at: {}:{}", ::core::file!(), ::core::line!());
panic!(
"reached unreachable case at: {}:{}",
::core::file!(),
::core::line!()
);
} else {
core::hint::unreachable_unchecked()
::core::hint::unreachable_unchecked()
}
}};
}

@ -219,6 +219,9 @@ impl<'a, T: PartialEq> PartialEq<T> for Life<'a, T> {
unsafe impl<'a, T: Send> Send for Life<'a, T> {}
unsafe impl<'a, T: Sync> Sync for Life<'a, T> {}
/// [`MaybeInit`] is a structure that is like an [`Option`] in debug mode and like
/// [`MaybeUninit`] in release mode. This means that provided there are good enough test cases, most
/// incorrect `assume_init` calls should be detected in the test phase.
pub struct MaybeInit<T> {
#[cfg(test)]
is_init: bool,
@ -226,9 +229,7 @@ pub struct MaybeInit<T> {
}
impl<T> MaybeInit<T> {
#[cfg(not(test))]
const _SZ_REL: () =
assert!(core::mem::size_of::<Self>() == core::mem::size_of::<MaybeUninit<T>>());
/// Initialize a new uninitialized variant
pub const fn uninit() -> Self {
Self {
#[cfg(test)]
@ -236,6 +237,7 @@ impl<T> MaybeInit<T> {
base: MaybeUninit::uninit(),
}
}
/// Initialize with a value
pub const fn new(val: T) -> Self {
Self {
#[cfg(test)]
@ -243,6 +245,11 @@ impl<T> MaybeInit<T> {
base: MaybeUninit::new(val),
}
}
/// Assume that `self` is initialized and return the inner value
///
/// ## Safety
///
/// Caller needs to ensure that the data is actually initialized
pub const unsafe fn assume_init(self) -> T {
#[cfg(test)]
{
@ -252,6 +259,11 @@ impl<T> MaybeInit<T> {
}
self.base.assume_init()
}
/// Assume that `self` is initialized and return a reference
///
/// ## Safety
///
/// Caller needs to ensure that the data is actually initialized
pub const unsafe fn assume_init_ref(&self) -> &T {
#[cfg(test)]
{
@ -277,6 +289,7 @@ impl<T: fmt::Debug> fmt::Debug for MaybeInit<T> {
.finish()
}
}
#[cfg(not(test))]
impl<T: fmt::Debug> fmt::Debug for MaybeInit<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

Loading…
Cancel
Save