Ensure queries are evaluated completely

next
Sayan Nandan 10 months ago
parent 23f4296982
commit 6ea9758714
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

@ -27,9 +27,25 @@ All changes in this project will be noted in this file.
- **All actions removed**: All the prior `SET`, `GET` and other actions have been removed in favor of the new query language
- The following queries were added:
- `INSERT INTO <space>.<model>(col, col2, col3, ...)`
- Insert queries can use several ways of insertion including:
- a standard order of declared columns like this:
```sql
INSERT INTO myspace.mymodel(col1, col2, col3, ...)
```
- using a map:
```sql
INSERT INTO myspace.mymodel { col1: val1, col2: val2, col4: val4, col3: val3 }
```
- Inserts can also make use of function calls during inserts:
- It can be called like this: `INSERT INTO myspace.mymodel(@uuidstr, ...)`
- The following functions are available:
- `@uuidstr`: returns a string with a randomly generated v4 UUID
- `@uuidbin`: same as `@uuidstr` but returns it as a blob
- `@timesec`: returns a 64-bit integer with the current time in seconds
- `SELECT field1, field2, ... FROM <space>.<model> WHERE <primary_key_column> = <value>`
- New data manipulation via `UPDATE` allows arithmetic operations, string manipulation and more!:
- New data manipulation via `UPDATE` allows arithmetic operations, string manipulation and more! Examples:
- `UPDATE <space>.<model> SET col_num += 1 WHERE <primary_key_column> = <value>`
- `UPDATE <space>.<model> SET mystring += " last part of string" WHERE ...`
- `DELETE FROM <space>.<model> WHERE <primary_key_column> = <value>`
- DCL:
- `SYSCTL CREATE USER <name> WITH { password: <password> }`

@ -29,12 +29,24 @@ use crate::engine::{
error::{QueryError, QueryResult},
fractal::GlobalInstanceLike,
net::protocol::ClientLocalState,
ql::dcl::{UserAdd, UserDel},
ql::dcl::{SysctlCommand, UserAdd, UserDel},
};
const KEY_PASSWORD: &str = "password";
pub fn create_user(global: &impl GlobalInstanceLike, mut user_add: UserAdd<'_>) -> QueryResult<()> {
pub fn exec<G: GlobalInstanceLike>(
g: G,
current_user: &ClientLocalState,
cmd: SysctlCommand,
) -> QueryResult<()> {
match cmd {
SysctlCommand::CreateUser(new) => create_user(&g, new),
SysctlCommand::DropUser(drop) => drop_user(&g, current_user, drop),
SysctlCommand::ReportStatus => Ok(()),
}
}
fn create_user(global: &impl GlobalInstanceLike, mut user_add: UserAdd<'_>) -> QueryResult<()> {
let username = user_add.username().to_owned();
let password = match user_add.options_mut().remove(KEY_PASSWORD) {
Some(DictEntryGeneric::Data(d))
@ -48,7 +60,7 @@ pub fn create_user(global: &impl GlobalInstanceLike, mut user_add: UserAdd<'_>)
global.sys_store().create_new_user(username, password)
}
pub fn drop_user(
fn drop_user(
global: &impl GlobalInstanceLike,
cstate: &ClientLocalState,
user_del: UserDel<'_>,

@ -82,7 +82,7 @@ fn _call<A: ASTNode<'static> + core::fmt::Debug, T>(
state: &mut State<'static, InplaceData>,
f: impl FnOnce(&Global, A) -> Result<T, QueryError>,
) -> QueryResult<T> {
let cs = ASTNode::from_state(state)?;
let cs = ASTNode::parse_from_state_hardened(state)?;
f(&g, cs)
}
@ -143,30 +143,8 @@ fn blocking_exec_sysctl(
tokens: RawSlice<Token<'static>>,
) -> QueryResult<()> {
let mut state = State::new_inplace(&tokens);
/*
currently supported: sysctl create user, sysctl drop user
*/
if state.remaining() < 2 {
return Err(QueryError::QLInvalidSyntax);
}
let (a, b) = (state.fw_read(), state.fw_read());
match (a, b) {
(Token![create], Token::Ident(id)) if id.eq_ignore_ascii_case("user") => {
let useradd = ASTNode::from_state(&mut state)?;
super::dcl::create_user(&g, useradd)
}
(Token![drop], Token::Ident(id)) if id.eq_ignore_ascii_case("user") => {
let userdel = ASTNode::from_state(&mut state)?;
super::dcl::drop_user(&g, cstate, userdel)
}
(Token::Ident(k1), Token::Ident(k2))
if k1.eq_ignore_ascii_case("report") && k2.eq_ignore_ascii_case("status") =>
{
// TODO(@ohsayan): replace dummy endpoint with actual `system report status` responses
Ok(())
}
_ => Err(QueryError::QLUnknownStatement),
}
let r = ASTNode::parse_from_state_hardened(&mut state)?;
super::dcl::exec(g, cstate, r)
}
/*

@ -26,8 +26,6 @@
pub mod traits;
#[cfg(debug_assertions)]
use self::traits::ASTNode;
#[cfg(test)]
pub use traits::{parse_ast_node_full, parse_ast_node_multiple_full};
use {
@ -504,9 +502,10 @@ pub enum Statement<'a> {
}
#[inline(always)]
#[cfg(debug_assertions)]
#[cfg(test)]
#[allow(dead_code)] // TODO(@ohsayan): get rid of this
pub fn compile<'a, Qd: QueryData<'a>>(tok: &'a [Token<'a>], d: Qd) -> QueryResult<Statement<'a>> {
use self::traits::ASTNode;
if compiler::unlikely(tok.len() < 2) {
return Err(QueryError::QLUnexpectedEndOfStatement);
}
@ -515,13 +514,13 @@ pub fn compile<'a, Qd: QueryData<'a>>(tok: &'a [Token<'a>], d: Qd) -> QueryResul
// DDL
Token![use] => Entity::parse_from_state_rounded_result(&mut state).map(Statement::Use),
Token![create] => match state.fw_read() {
Token![model] => ASTNode::from_state(&mut state).map(Statement::CreateModel),
Token![space] => ASTNode::from_state(&mut state).map(Statement::CreateSpace),
Token![model] => ASTNode::test_parse_from_state(&mut state).map(Statement::CreateModel),
Token![space] => ASTNode::test_parse_from_state(&mut state).map(Statement::CreateSpace),
_ => compiler::cold_rerr(QueryError::QLUnknownStatement),
},
Token![alter] => match state.fw_read() {
Token![model] => ASTNode::from_state(&mut state).map(Statement::AlterModel),
Token![space] => ASTNode::from_state(&mut state).map(Statement::AlterSpace),
Token![model] => ASTNode::test_parse_from_state(&mut state).map(Statement::AlterModel),
Token![space] => ASTNode::test_parse_from_state(&mut state).map(Statement::AlterSpace),
_ => compiler::cold_rerr(QueryError::QLUnknownStatement),
},
Token![drop] if state.remaining() >= 2 => ddl::drop::parse_drop(&mut state),
@ -529,10 +528,10 @@ pub fn compile<'a, Qd: QueryData<'a>>(tok: &'a [Token<'a>], d: Qd) -> QueryResul
ddl::ins::parse_inspect(&mut state)
}
// DML
Token![insert] => ASTNode::from_state(&mut state).map(Statement::Insert),
Token![select] => ASTNode::from_state(&mut state).map(Statement::Select),
Token![update] => ASTNode::from_state(&mut state).map(Statement::Update),
Token![delete] => ASTNode::from_state(&mut state).map(Statement::Delete),
Token![insert] => ASTNode::test_parse_from_state(&mut state).map(Statement::Insert),
Token![select] => ASTNode::test_parse_from_state(&mut state).map(Statement::Select),
Token![update] => ASTNode::test_parse_from_state(&mut state).map(Statement::Update),
Token![delete] => ASTNode::test_parse_from_state(&mut state).map(Statement::Delete),
_ => compiler::cold_rerr(QueryError::QLUnknownStatement),
}
}

@ -33,17 +33,47 @@ use crate::engine::{
/// An AST node
pub trait ASTNode<'a>: Sized {
const VERIFY: bool = false;
/// This AST node MUST use the full token range
const MUST_USE_FULL_TOKEN_RANGE: bool;
/// This AST node MUST use the full token range, and it also verifies that this is the case
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool;
/// This AST node doesn't handle "deep errors" (for example, recursive collections)
const VERIFY_STATE_BEFORE_RETURN: bool = false;
/// A hardened parse that guarantees:
/// - The result is verified (even if it is a deep error)
/// - The result utilizes the full token range
fn parse_from_state_hardened<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let r = Self::__base_impl_parse_from_state(state)?;
if Self::VERIFY_STATE_BEFORE_RETURN {
// must verify
if !state.okay() {
return Err(QueryError::QLInvalidSyntax);
}
}
if Self::MUST_USE_FULL_TOKEN_RANGE {
if !Self::VERIFIES_FULL_TOKEN_RANGE_USAGE {
if state.not_exhausted() {
return Err(QueryError::QLInvalidSyntax);
}
}
}
Ok(r)
}
/// Parse this AST node from the given state
///
/// Note to implementors:
/// - If the implementor uses a cow style parse, then set [`ASTNode::VERIFY`] to
/// true
/// - Try to propagate errors via [`State`] if possible
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self>;
fn from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
let r = <Self as ASTNode>::_from_state(state);
if Self::VERIFY {
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self>;
#[cfg(test)]
fn test_parse_from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
let r = <Self as ASTNode>::__base_impl_parse_from_state(state);
if Self::VERIFY_STATE_BEFORE_RETURN {
return if state.okay() {
r
} else {
@ -60,7 +90,7 @@ pub trait ASTNode<'a>: Sized {
#[cfg(test)]
fn multiple_from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Vec<Self>> {
let r = <Self as ASTNode>::_multiple_from_state(state);
if Self::VERIFY {
if Self::VERIFY_STATE_BEFORE_RETURN {
return if state.okay() {
r
} else {
@ -73,7 +103,7 @@ pub trait ASTNode<'a>: Sized {
/// Parse this AST node utilizing the full token-stream. Intended for the test suite.
fn from_insecure_tokens_full(tok: &'a [Token<'a>]) -> QueryResult<Self> {
let mut state = State::new(tok, InplaceData::new());
let r = <Self as ASTNode>::from_state(&mut state)?;
let r = <Self as ASTNode>::test_parse_from_state(&mut state)?;
assert!(state.exhausted());
Ok(r)
}

@ -36,6 +36,47 @@ use crate::engine::{
},
};
#[derive(Debug, PartialEq)]
pub enum SysctlCommand<'a> {
/// `sysctl create user ...`
CreateUser(UserAdd<'a>),
/// `sysctl drop user ...`
DropUser(UserDel<'a>),
/// `sysctl status`
ReportStatus,
}
impl<'a> traits::ASTNode<'a> for SysctlCommand<'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> {
if state.remaining() < 1 {
return Err(QueryError::QLUnexpectedEndOfStatement);
}
let token = state.fw_read();
let create = Token![create].eq(token);
let drop = Token![drop].eq(token);
let status = token.ident_eq("status");
if status {
return Ok(SysctlCommand::ReportStatus);
}
if state.exhausted() {
return Err(QueryError::QLUnexpectedEndOfStatement);
}
let create_or_drop = state.fw_read();
if !create_or_drop.ident_eq("user") & !(create | drop) {
return Err(QueryError::QLUnknownStatement);
}
if create {
UserAdd::parse(state).map(SysctlCommand::CreateUser)
} else {
UserDel::parse(state).map(SysctlCommand::DropUser)
}
}
}
fn parse<'a, Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<UserMeta<'a>> {
/*
[username] with { password: [password], ... }
@ -106,12 +147,6 @@ impl<'a> UserAdd<'a> {
}
}
impl<'a> traits::ASTNode<'a> for UserAdd<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
Self::parse(state)
}
}
#[derive(Debug, PartialEq)]
pub struct UserDel<'a> {
username: &'a str,
@ -144,9 +179,3 @@ impl<'a> UserDel<'a> {
self.username
}
}
impl<'a> traits::ASTNode<'a> for UserDel<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
Self::parse(state)
}
}

@ -196,12 +196,20 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for AlterModel<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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)
}
}
impl<'a> ASTNode<'a> for AlterSpace<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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)
}
}

@ -162,12 +162,20 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for CreateSpace<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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)
}
}
impl<'a> ASTNode<'a> for CreateModel<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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)
}
}

@ -52,15 +52,13 @@ impl<'a> DropSpace<'a> {
let force = state.cursor_rounded_eq(Token::Ident(Ident::from("force")));
state.cursor_ahead_if(force);
// either `force` or nothing
if state.exhausted() {
return Ok(DropSpace::new(
unsafe {
// UNSAFE(@ohsayan): Safe because the if predicate ensures that tok[0] (relative) is indeed an ident
ident.uck_read_ident()
},
force,
));
}
return Ok(DropSpace::new(
unsafe {
// UNSAFE(@ohsayan): Safe because the if predicate ensures that tok[0] (relative) is indeed an ident
ident.uck_read_ident()
},
force,
));
}
Err(QueryError::QLInvalidSyntax)
}
@ -81,11 +79,7 @@ impl<'a> DropModel<'a> {
let e = Entity::parse_from_state_rounded_result(state)?;
let force = state.cursor_rounded_eq(Token::Ident(Ident::from("force")));
state.cursor_ahead_if(force);
if state.exhausted() {
return Ok(DropModel::new(e, force));
} else {
Err(QueryError::QLInvalidSyntax)
}
Ok(DropModel::new(e, force))
}
}
@ -112,19 +106,31 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for DropModel<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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)
}
}
impl<'a> ASTNode<'a> for DropSpace<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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)
}
}
#[derive(sky_macros::Wrapper, Debug)]
pub struct DropStatementAST<'a>(Statement<'a>);
impl<'a> ASTNode<'a> for DropStatementAST<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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> {
super::parse_drop(state).map(Self)
}
}

@ -80,7 +80,11 @@ mod impls {
#[derive(sky_macros::Wrapper, Debug)]
pub struct InspectStatementAST<'a>(Statement<'a>);
impl<'a> ASTNode<'a> for InspectStatementAST<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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> {
super::parse_inspect(state).map(Self)
}
}

@ -535,7 +535,11 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for ExpandedField<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
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)
}
fn _multiple_from_state<Qd: QueryData<'a>>(
@ -545,9 +549,13 @@ mod impls {
}
}
impl<'a> ASTNode<'a> for LayerSpec<'a> {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let mut layers = Vec::new();
rfold_layers(state, &mut layers);
assert!(layers.len() == 1);
@ -564,9 +572,13 @@ mod impls {
#[derive(sky_macros::Wrapper, Debug)]
pub struct DictBasic(DictGeneric);
impl<'a> ASTNode<'a> for DictBasic {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let mut dict = DictGeneric::new();
rfold_dict(DictFoldState::OB, state, &mut dict);
Ok(Self(dict))
@ -575,9 +587,13 @@ mod impls {
#[derive(sky_macros::Wrapper, Debug)]
pub struct DictTypeMetaSplit(DictGeneric);
impl<'a> ASTNode<'a> for DictTypeMetaSplit {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let mut dict = DictGeneric::new();
rfold_tymeta(DictFoldState::CB_OR_IDENT, state, &mut dict);
Ok(Self(dict))
@ -586,16 +602,24 @@ mod impls {
#[derive(sky_macros::Wrapper, Debug)]
pub struct DictTypeMeta(DictGeneric);
impl<'a> ASTNode<'a> for DictTypeMeta {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let mut dict = DictGeneric::new();
rfold_tymeta(DictFoldState::OB, state, &mut dict);
Ok(Self(dict))
}
}
impl<'a> ASTNode<'a> for FieldSpec<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
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)
}
}

@ -113,7 +113,11 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for DeleteStatement<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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_delete(state)
}
}

@ -400,7 +400,11 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for InsertStatement<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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_insert(state)
}
}
@ -418,9 +422,13 @@ mod impls {
#[derive(sky_macros::Wrapper, Debug)]
pub struct List(Vec<Datacell>);
impl<'a> ASTNode<'a> for List {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let mut l = Vec::new();
parse_list(state, &mut l);
Ok(List(l))
@ -429,9 +437,13 @@ mod impls {
#[derive(sky_macros::Wrapper, Debug)]
pub struct DataTuple(Vec<Datacell>);
impl<'a> ASTNode<'a> for DataTuple {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let r = parse_data_tuple_syntax(state);
Ok(Self(r))
}
@ -439,9 +451,13 @@ mod impls {
#[derive(sky_macros::Wrapper, Debug)]
pub struct DataMap(HashMap<Box<str>, Datacell>);
impl<'a> ASTNode<'a> for DataMap {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let r = parse_data_map_syntax(state);
Ok(Self(
r.into_iter()

@ -170,15 +170,23 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for WhereClause<'a> {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let wh = Self::parse_where(state);
Ok(wh)
}
}
impl<'a> ASTNode<'a> for RelationalExpr<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
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::try_parse(state).ok_or(QueryError::QLInvalidSyntax)
}
}

@ -161,7 +161,11 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for SelectStatement<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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_select(state)
}
}

@ -219,7 +219,11 @@ mod impls {
},
};
impl<'a> ASTNode<'a> for UpdateStatement<'a> {
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
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_update(state)
}
}
@ -227,9 +231,13 @@ mod impls {
mod test {
use super::{super::AssignmentExpression, ASTNode, QueryData, QueryResult, State};
impl<'a> ASTNode<'a> for AssignmentExpression<'a> {
const MUST_USE_FULL_TOKEN_RANGE: bool = false;
const VERIFIES_FULL_TOKEN_RANGE_USAGE: bool = false;
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> QueryResult<Self> {
const VERIFY_STATE_BEFORE_RETURN: bool = true;
fn __base_impl_parse_from_state<Qd: QueryData<'a>>(
state: &mut State<'a, Qd>,
) -> QueryResult<Self> {
let mut expr = Vec::new();
AssignmentExpression::parse_and_append_expression(state, &mut expr);
state.poison_if_not(expr.len() == 1);

@ -135,6 +135,9 @@ impl<'a> Token<'a> {
pub unsafe fn uck_read_lit(&self) -> &Lit<'a> {
extract!(self, Self::Lit(l) => l)
}
pub fn ident_eq(&self, ident: &str) -> bool {
matches!(self, Token::Ident(id) if id.eq_ignore_ascii_case(ident))
}
}
impl<'a> ToString for Token<'a> {

@ -24,21 +24,35 @@
*
*/
use crate::engine::ql::{ast, dcl, tests::lex_insecure};
use crate::engine::ql::{
ast,
dcl::{self, SysctlCommand},
tests::lex_insecure,
};
#[test]
fn report_status_simple() {
let query = lex_insecure(b"sysctl status").unwrap();
let q = ast::parse_ast_node_full::<dcl::SysctlCommand>(&query[1..]).unwrap();
assert_eq!(q, SysctlCommand::ReportStatus)
}
#[test]
fn create_user_simple() {
let query = lex_insecure(b"create user 'sayan' with { password: 'mypass123' }").unwrap();
let q = ast::parse_ast_node_full::<dcl::UserAdd>(&query[2..]).unwrap();
let query = lex_insecure(b"sysctl create user 'sayan' with { password: 'mypass123' }").unwrap();
let q = ast::parse_ast_node_full::<dcl::SysctlCommand>(&query[1..]).unwrap();
assert_eq!(
q,
dcl::UserAdd::new("sayan", into_dict!("password" => lit!("mypass123")))
SysctlCommand::CreateUser(dcl::UserAdd::new(
"sayan",
into_dict!("password" => lit!("mypass123"))
))
)
}
#[test]
fn delete_user_simple() {
let query = lex_insecure(b"delete user 'monster'").unwrap();
let q = ast::parse_ast_node_full::<dcl::UserDel>(&query[2..]).unwrap();
assert_eq!(q, dcl::UserDel::new("monster"))
let query = lex_insecure(b"sysctl drop user 'monster'").unwrap();
let q = ast::parse_ast_node_full::<dcl::SysctlCommand>(&query[1..]).unwrap();
assert_eq!(q, SysctlCommand::DropUser(dcl::UserDel::new("monster")));
}

@ -142,12 +142,12 @@ mod tymeta {
// ^^^^^^^^^^^^^^^^^^ cursor should be at string
let tok = lex_insecure(br#"{maxlen: 100, type: string, unique: true }"#).unwrap();
let mut state = State::new_inplace(&tok);
let tymeta: DictTypeMeta = ASTNode::from_state(&mut state).unwrap();
let tymeta: DictTypeMeta = ASTNode::test_parse_from_state(&mut state).unwrap();
assert_eq!(state.cursor(), 6);
assert!(Token![:].eq(state.fw_read()));
assert!(Token::Ident(Ident::from("string")).eq(state.fw_read()));
assert!(Token![,].eq(state.fw_read()));
let tymeta2: DictTypeMetaSplit = ASTNode::from_state(&mut state).unwrap();
let tymeta2: DictTypeMetaSplit = ASTNode::test_parse_from_state(&mut state).unwrap();
assert!(state.exhausted());
let mut final_ret = tymeta.into_inner();
final_ret.extend(tymeta2.into_inner());
@ -167,12 +167,12 @@ mod tymeta {
lex_insecure(br#"{maxlen: 100, this: { is: "cool" }, type: string, unique: true }"#)
.unwrap();
let mut state = State::new_inplace(&tok);
let tymeta: DictTypeMeta = ASTNode::from_state(&mut state).unwrap();
let tymeta: DictTypeMeta = ASTNode::test_parse_from_state(&mut state).unwrap();
assert_eq!(state.cursor(), 14);
assert!(Token![:].eq(state.fw_read()));
assert!(Token::Ident(Ident::from("string")).eq(state.fw_read()));
assert!(Token![,].eq(state.fw_read()));
let tymeta2: DictTypeMetaSplit = ASTNode::from_state(&mut state).unwrap();
let tymeta2: DictTypeMetaSplit = ASTNode::test_parse_from_state(&mut state).unwrap();
assert!(state.exhausted());
let mut final_ret = tymeta.into_inner();
final_ret.extend(tymeta2.into_inner());

Loading…
Cancel
Save