Implement limits for parsing depth in the new parser. (#3762)
This commit is contained in:
parent
567832825a
commit
1528da9b95
8 changed files with 351 additions and 10 deletions
|
@ -74,6 +74,8 @@ pub enum ParseErrorKind {
|
||||||
idiom: String,
|
idiom: String,
|
||||||
kind: MissingKind,
|
kind: MissingKind,
|
||||||
},
|
},
|
||||||
|
ExceededObjectDepthLimit,
|
||||||
|
ExceededQueryDepthLimit,
|
||||||
NoWhitespace,
|
NoWhitespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,6 +248,24 @@ impl ParseError {
|
||||||
snippets: vec![snippet],
|
snippets: vec![snippet],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ParseErrorKind::ExceededObjectDepthLimit => {
|
||||||
|
let text = "Parsing exceeded the depth limit for objects";
|
||||||
|
let locations = Location::range_of_span(source, at);
|
||||||
|
let snippet = Snippet::from_source_location_range(source, locations, None);
|
||||||
|
RenderedError {
|
||||||
|
text: text.to_string(),
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseErrorKind::ExceededQueryDepthLimit => {
|
||||||
|
let text = "Parsing exceeded the depth limit for queries";
|
||||||
|
let locations = Location::range_of_span(source, at);
|
||||||
|
let snippet = Snippet::from_source_location_range(source, locations, None);
|
||||||
|
RenderedError {
|
||||||
|
text: text.to_string(),
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
ParseErrorKind::MissingField {
|
ParseErrorKind::MissingField {
|
||||||
field,
|
field,
|
||||||
idiom,
|
idiom,
|
||||||
|
|
|
@ -74,6 +74,83 @@ macro_rules! test_parse {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! enter_object_recursion {
|
||||||
|
($name:ident = $this:expr => { $($t:tt)* }) => {{
|
||||||
|
if $this.object_recursion == 0 {
|
||||||
|
return Err($crate::syn::v2::parser::ParseError::new(
|
||||||
|
$crate::syn::v2::parser::ParseErrorKind::ExceededObjectDepthLimit,
|
||||||
|
$this.last_span(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
struct Dropper<'a, 'b>(&'a mut $crate::syn::v2::parser::Parser<'b>);
|
||||||
|
impl Drop for Dropper<'_, '_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.0.object_recursion += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> ::std::ops::Deref for Dropper<'_,'a>{
|
||||||
|
type Target = $crate::syn::v2::parser::Parser<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target{
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ::std::ops::DerefMut for Dropper<'_,'a>{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target{
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this.object_recursion -= 1;
|
||||||
|
let mut $name = Dropper($this);
|
||||||
|
{
|
||||||
|
$($t)*
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! enter_query_recursion {
|
||||||
|
($name:ident = $this:expr => { $($t:tt)* }) => {{
|
||||||
|
|
||||||
|
println!("{} = {}",$this.query_recursion, std::backtrace::Backtrace::force_capture());
|
||||||
|
if $this.query_recursion == 0 {
|
||||||
|
return Err($crate::syn::v2::parser::ParseError::new(
|
||||||
|
$crate::syn::v2::parser::ParseErrorKind::ExceededQueryDepthLimit,
|
||||||
|
$this.last_span(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
struct Dropper<'a, 'b>(&'a mut $crate::syn::v2::parser::Parser<'b>);
|
||||||
|
impl Drop for Dropper<'_, '_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.0.query_recursion += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> ::std::ops::Deref for Dropper<'_,'a>{
|
||||||
|
type Target = $crate::syn::v2::parser::Parser<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target{
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ::std::ops::DerefMut for Dropper<'_,'a>{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target{
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this.query_recursion -= 1;
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut $name = Dropper($this);
|
||||||
|
{
|
||||||
|
$($t)*
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) use expected;
|
pub(super) use expected;
|
||||||
pub(super) use unexpected;
|
pub(super) use unexpected;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ use crate::{
|
||||||
token::{t, Span, Token, TokenKind},
|
token::{t, Span, Token, TokenKind},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use reblessive::Stk;
|
||||||
|
|
||||||
mod basic;
|
mod basic;
|
||||||
mod builtin;
|
mod builtin;
|
||||||
|
@ -44,7 +45,6 @@ mod token_buffer;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
pub use error::{IntErrorKind, ParseError, ParseErrorKind};
|
pub use error::{IntErrorKind, ParseError, ParseErrorKind};
|
||||||
use reblessive::Stk;
|
|
||||||
|
|
||||||
/// The result returned by most parser function.
|
/// The result returned by most parser function.
|
||||||
pub type ParseResult<T> = Result<T, ParseError>;
|
pub type ParseResult<T> = Result<T, ParseError>;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use geo_types::{LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Po
|
||||||
use reblessive::Stk;
|
use reblessive::Stk;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
enter_object_recursion,
|
||||||
sql::{Block, Geometry, Object, Strand, Value},
|
sql::{Block, Geometry, Object, Strand, Value},
|
||||||
syn::v2::{
|
syn::v2::{
|
||||||
parser::{mac::expected, ParseError, ParseErrorKind, ParseResult, Parser},
|
parser::{mac::expected, ParseError, ParseErrorKind, ParseResult, Parser},
|
||||||
|
@ -24,12 +25,16 @@ impl Parser<'_> {
|
||||||
) -> ParseResult<Value> {
|
) -> ParseResult<Value> {
|
||||||
if self.eat(t!("}")) {
|
if self.eat(t!("}")) {
|
||||||
// empty object, just return
|
// empty object, just return
|
||||||
|
enter_object_recursion!(_this = self => {
|
||||||
return Ok(Value::Object(Object::default()));
|
return Ok(Value::Object(Object::default()));
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check first if it can be an object.
|
// Check first if it can be an object.
|
||||||
if self.peek_token_at(1).kind == t!(":") {
|
if self.peek_token_at(1).kind == t!(":") {
|
||||||
return self.parse_object_or_geometry(ctx, start).await;
|
enter_object_recursion!(this = self => {
|
||||||
|
return this.parse_object_or_geometry(ctx, start).await;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// not an object so instead parse as a block.
|
// not an object so instead parse as a block.
|
||||||
|
@ -582,7 +587,9 @@ impl Parser<'_> {
|
||||||
/// # Parser state
|
/// # Parser state
|
||||||
/// Expects the first `{` to already have been eaten.
|
/// Expects the first `{` to already have been eaten.
|
||||||
pub(super) async fn parse_object(&mut self, ctx: &mut Stk, start: Span) -> ParseResult<Object> {
|
pub(super) async fn parse_object(&mut self, ctx: &mut Stk, start: Span) -> ParseResult<Object> {
|
||||||
self.parse_object_from_map(ctx, BTreeMap::new(), start).await
|
enter_object_recursion!(this = self => {
|
||||||
|
this.parse_object_from_map(ctx, BTreeMap::new(), start).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn parse_object_from_map(
|
async fn parse_object_from_map(
|
||||||
|
|
|
@ -3,6 +3,7 @@ use reblessive::Stk;
|
||||||
|
|
||||||
use super::{ParseResult, Parser};
|
use super::{ParseResult, Parser};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
enter_query_recursion,
|
||||||
sql::{
|
sql::{
|
||||||
Array, Dir, Function, Geometry, Ident, Idiom, Mock, Part, Script, Strand, Subquery, Table,
|
Array, Dir, Function, Geometry, Ident, Idiom, Mock, Part, Script, Strand, Subquery, Table,
|
||||||
Value,
|
Value,
|
||||||
|
@ -221,9 +222,11 @@ impl Parser<'_> {
|
||||||
self.parse_mock(token.span).map(Value::Mock)?
|
self.parse_mock(token.span).map(Value::Mock)?
|
||||||
}
|
}
|
||||||
t!("IF") => {
|
t!("IF") => {
|
||||||
self.pop_peek();
|
enter_query_recursion!(this = self => {
|
||||||
let stmt = ctx.run(|ctx| self.parse_if_stmt(ctx)).await?;
|
this.pop_peek();
|
||||||
|
let stmt = ctx.run(|ctx| this.parse_if_stmt(ctx)).await?;
|
||||||
Value::Subquery(Box::new(Subquery::Ifelse(stmt)))
|
Value::Subquery(Box::new(Subquery::Ifelse(stmt)))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
t!("(") => {
|
t!("(") => {
|
||||||
self.pop_peek();
|
self.pop_peek();
|
||||||
|
@ -341,9 +344,11 @@ impl Parser<'_> {
|
||||||
self.parse_inner_subquery(ctx, Some(peek.span)).await
|
self.parse_inner_subquery(ctx, Some(peek.span)).await
|
||||||
}
|
}
|
||||||
t!("IF") => {
|
t!("IF") => {
|
||||||
self.pop_peek();
|
enter_query_recursion!(this = self => {
|
||||||
let if_stmt = ctx.run(|ctx| self.parse_if_stmt(ctx)).await?;
|
this.pop_peek();
|
||||||
|
let if_stmt = ctx.run(|ctx| this.parse_if_stmt(ctx)).await?;
|
||||||
Ok(Subquery::Ifelse(if_stmt))
|
Ok(Subquery::Ifelse(if_stmt))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => self.parse_inner_subquery(ctx, None).await,
|
_ => self.parse_inner_subquery(ctx, None).await,
|
||||||
}
|
}
|
||||||
|
@ -353,6 +358,16 @@ impl Parser<'_> {
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &mut Stk,
|
ctx: &mut Stk,
|
||||||
start: Span,
|
start: Span,
|
||||||
|
) -> ParseResult<Value> {
|
||||||
|
enter_query_recursion!(this = self => {
|
||||||
|
this.parse_inner_subquery_or_coordinate_inner(ctx,start).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn parse_inner_subquery_or_coordinate_inner(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut Stk,
|
||||||
|
start: Span,
|
||||||
) -> ParseResult<Value> {
|
) -> ParseResult<Value> {
|
||||||
let peek = self.peek();
|
let peek = self.peek();
|
||||||
let res = match peek.kind {
|
let res = match peek.kind {
|
||||||
|
@ -489,6 +504,16 @@ impl Parser<'_> {
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &mut Stk,
|
ctx: &mut Stk,
|
||||||
start: Option<Span>,
|
start: Option<Span>,
|
||||||
|
) -> ParseResult<Subquery> {
|
||||||
|
enter_query_recursion!(this = self => {
|
||||||
|
this.parse_inner_subquery_inner(ctx,start).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn parse_inner_subquery_inner(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut Stk,
|
||||||
|
start: Option<Span>,
|
||||||
) -> ParseResult<Subquery> {
|
) -> ParseResult<Subquery> {
|
||||||
let peek = self.peek();
|
let peek = self.peek();
|
||||||
let res = match peek.kind {
|
let res = match peek.kind {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use reblessive::Stk;
|
use reblessive::Stk;
|
||||||
|
|
||||||
|
use crate::enter_query_recursion;
|
||||||
use crate::sql::block::Entry;
|
use crate::sql::block::Entry;
|
||||||
use crate::sql::statements::show::{ShowSince, ShowStatement};
|
use crate::sql::statements::show::{ShowSince, ShowStatement};
|
||||||
use crate::sql::statements::sleep::SleepStatement;
|
use crate::sql::statements::sleep::SleepStatement;
|
||||||
|
@ -92,6 +93,12 @@ impl Parser<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn parse_stmt(&mut self, ctx: &mut Stk) -> ParseResult<Statement> {
|
pub(super) async fn parse_stmt(&mut self, ctx: &mut Stk) -> ParseResult<Statement> {
|
||||||
|
enter_query_recursion!(this = self => {
|
||||||
|
this.parse_stmt_inner(ctx).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn parse_stmt_inner(&mut self, ctx: &mut Stk) -> ParseResult<Statement> {
|
||||||
let token = self.peek();
|
let token = self.peek();
|
||||||
match token.kind {
|
match token.kind {
|
||||||
t!("ANALYZE") => {
|
t!("ANALYZE") => {
|
||||||
|
@ -207,6 +214,12 @@ impl Parser<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn parse_entry(&mut self, ctx: &mut Stk) -> ParseResult<Entry> {
|
pub(super) async fn parse_entry(&mut self, ctx: &mut Stk) -> ParseResult<Entry> {
|
||||||
|
enter_query_recursion!(this = self => {
|
||||||
|
this.parse_entry_inner(ctx).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn parse_entry_inner(&mut self, ctx: &mut Stk) -> ParseResult<Entry> {
|
||||||
let token = self.peek();
|
let token = self.peek();
|
||||||
match token.kind {
|
match token.kind {
|
||||||
t!("BREAK") => {
|
t!("BREAK") => {
|
||||||
|
|
198
core/src/syn/v2/parser/test/limit.rs
Normal file
198
core/src/syn/v2/parser/test/limit.rs
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
use reblessive::Stack;
|
||||||
|
|
||||||
|
use crate::syn::v2::parser::Parser;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_depth() {
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
let source = r#"
|
||||||
|
RETURN {
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_object_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect("recursion limit of 5 couldn't parse 5 deep object");
|
||||||
|
|
||||||
|
let source = r#"
|
||||||
|
RETURN {
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
e: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_object_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect_err("recursion limit of 5 didn't trigger on 6 deep object");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_depth_succeed_then_fail() {
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
let source = r#"
|
||||||
|
RETURN {
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RETURN {
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_object_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect("recursion limit of 5 couldn't parse 5 deep object");
|
||||||
|
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
let source = r#"
|
||||||
|
RETURN {
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RETURN {
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
e: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_object_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect_err("recursion limit of 5 didn't trigger on 6 deep object");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn query_depth_subquery() {
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
let source = r#"
|
||||||
|
RETURN select (select (select ( select foo from bar ) from bar ) from bar) from bar
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_query_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect("recursion limit of 5 couldn't parse 5 deep query");
|
||||||
|
|
||||||
|
let source = r#"
|
||||||
|
RETURN select (select (select ( select (select foo from bar) from bar ) from bar ) from bar) from bar
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_query_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect_err("recursion limit of 5 didn't trigger on 6 deep query");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn query_depth_block() {
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
let source = r#"
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
RETURN "foo";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_query_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect("recursion limit of 5 couldn't parse 5 deep query");
|
||||||
|
|
||||||
|
let source = r#"
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
RETURN "foo";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_query_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect_err("recursion limit of 5 didn't trigger on 6 deep query");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn query_depth_if() {
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
let source = r#"
|
||||||
|
IF IF IF IF IF true THEN false END { false } { false } { false } { false }
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_query_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect("recursion limit of 5 couldn't parse 5 deep query");
|
||||||
|
|
||||||
|
let source = r#"
|
||||||
|
IF IF IF IF IF IF true THEN false END { false } { false } { false } { false } { false }
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source.as_bytes()).with_query_recursion_limit(5);
|
||||||
|
stack
|
||||||
|
.enter(|stk| parser.parse_query(stk))
|
||||||
|
.finish()
|
||||||
|
.expect_err("recursion limit of 5 didn't trigger on 6 deep query");
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod limit;
|
||||||
mod stmt;
|
mod stmt;
|
||||||
mod streaming;
|
mod streaming;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
Loading…
Reference in a new issue