From 76b962eb6e9032b47c6876fc58ef51056b947e29 Mon Sep 17 00:00:00 2001 From: Mees Delzenne Date: Mon, 21 Aug 2023 12:33:57 +0200 Subject: [PATCH] Lower parsing complexity for binary values and idioms (#2475) --- lib/src/sql/expression.rs | 2 +- lib/src/sql/idiom.rs | 37 ++++++++++++++--- lib/src/sql/part.rs | 5 --- lib/src/sql/value/value.rs | 83 +++++++++++++++++++++++--------------- 4 files changed, 83 insertions(+), 44 deletions(-) diff --git a/lib/src/sql/expression.rs b/lib/src/sql/expression.rs index 3aa0f2b2..75e400cd 100644 --- a/lib/src/sql/expression.rs +++ b/lib/src/sql/expression.rs @@ -50,7 +50,7 @@ impl Expression { } } /// Augment an existing expression - fn augment(mut self, l: Value, o: Operator) -> Self { + pub(crate) fn augment(mut self, l: Value, o: Operator) -> Self { match &mut self { Self::Binary { l: left, diff --git a/lib/src/sql/idiom.rs b/lib/src/sql/idiom.rs index bdde3196..669c49af 100644 --- a/lib/src/sql/idiom.rs +++ b/lib/src/sql/idiom.rs @@ -5,7 +5,7 @@ use crate::err::Error; use crate::sql::common::commas; use crate::sql::error::IResult; use crate::sql::fmt::{fmt_separated_by, Fmt}; -use crate::sql::part::{all, field, first, graph, index, last, part, start, Part}; +use crate::sql::part::{all, field, first, graph, index, last, part, Part}; use crate::sql::part::{flatten, Next}; use crate::sql::paths::{ID, IN, META, OUT}; use crate::sql::value::Value; @@ -210,8 +210,21 @@ pub fn plain(i: &str) -> IResult<&str, Idiom> { Ok((i, Idiom::from(v))) } -/// A complex idiom with graph or many parts -pub fn multi(i: &str) -> IResult<&str, Idiom> { +/// Reparse a value which might part of an idiom. +pub fn reparse_idiom_start(start: Value, i: &str) -> IResult<&str, Value> { + if start.can_start_idiom() { + if let (i, Some(mut parts)) = opt(many1(part))(i)? { + let start = Part::Start(start); + parts.insert(0, start); + let v = Value::from(Idiom::from(parts)); + return Ok((i, v)); + } + } + Ok((i, start)) +} + +/// A complex idiom with graph or many parts excluding idioms which start with a value. +pub fn multi_without_start(i: &str) -> IResult<&str, Idiom> { alt(( |i| { let (i, p) = graph(i)?; @@ -220,7 +233,7 @@ pub fn multi(i: &str) -> IResult<&str, Idiom> { Ok((i, Idiom::from(v))) }, |i| { - let (i, p) = alt((first, start))(i)?; + let (i, p) = first(i)?; let (i, mut v) = many1(part)(i)?; v.insert(0, p); Ok((i, Idiom::from(v))) @@ -239,7 +252,21 @@ pub fn path(i: &str) -> IResult<&str, Idiom> { /// A full complex idiom with any number of parts #[cfg(test)] pub fn idiom(i: &str) -> IResult<&str, Idiom> { - alt((plain, multi))(i) + use nom::combinator::fail; + + use crate::sql::value::value; + + alt(( + plain, + alt((multi_without_start, |i| { + let (i, v) = value(i)?; + let (i, v) = reparse_idiom_start(v, i)?; + if let Value::Idiom(x) = v { + return Ok((i, x)); + } + fail(i) + })), + ))(i) } #[cfg(test)] diff --git a/lib/src/sql/part.rs b/lib/src/sql/part.rs index d13da462..590eab67 100644 --- a/lib/src/sql/part.rs +++ b/lib/src/sql/part.rs @@ -217,11 +217,6 @@ pub fn value(i: &str) -> IResult<&str, Part> { Ok((i, Part::Value(v))) } -pub fn start(i: &str) -> IResult<&str, Part> { - let (i, v) = value::start(i)?; - Ok((i, Part::Start(v))) -} - pub fn graph(i: &str) -> IResult<&str, Part> { let (i, v) = graph::graph(i)?; Ok((i, Part::Graph(v))) diff --git a/lib/src/sql/value/value.rs b/lib/src/sql/value/value.rs index 5f596c92..b21ca8a3 100644 --- a/lib/src/sql/value/value.rs +++ b/lib/src/sql/value/value.rs @@ -20,11 +20,11 @@ use crate::sql::ending::keyword; use crate::sql::error::IResult; use crate::sql::expression::{binary, unary, Expression}; use crate::sql::fmt::{Fmt, Pretty}; -use crate::sql::function::{self, function, Function}; +use crate::sql::function::{function, Function}; use crate::sql::future::{future, Future}; use crate::sql::geometry::{geometry, Geometry}; use crate::sql::id::{Gen, Id}; -use crate::sql::idiom::{self, Idiom}; +use crate::sql::idiom::{self, reparse_idiom_start, Idiom}; use crate::sql::kind::Kind; use crate::sql::model::{model, Model}; use crate::sql::number::decimal_is_integer; @@ -40,7 +40,7 @@ use crate::sql::subquery::{subquery, Subquery}; use crate::sql::table::{table, Table}; use crate::sql::thing::{thing, Thing}; use crate::sql::uuid::{uuid as unique, Uuid}; -use crate::sql::Query; +use crate::sql::{operator, Query}; use async_recursion::async_recursion; use chrono::{DateTime, Utc}; use derive::Store; @@ -1044,6 +1044,25 @@ impl Value { } } + /// Returns if this value can be the start of a idiom production. + pub fn can_start_idiom(&self) -> bool { + match self { + Value::Function(x) => !x.is_script(), + Value::Subquery(_) + | Value::Constant(_) + | Value::Datetime(_) + | Value::Duration(_) + | Value::Uuid(_) + | Value::Number(_) + | Value::Object(_) + | Value::Array(_) + | Value::Param(_) + | Value::Edges(_) + | Value::Thing(_) => true, + _ => false, + } + } + /// Try to convert this Value into a set of JSONPatch operations pub fn to_operations(&self) -> Result, Error> { match self { @@ -2709,12 +2728,27 @@ impl TryNeg for Value { /// Parse any `Value` including expressions pub fn value(i: &str) -> IResult<&str, Value> { - alt((map(binary, Value::from), single))(i) + let (i, start) = single(i)?; + let (i, expr_tail) = opt(|i| { + let (i, o) = operator::binary(i)?; + let (i, r) = value(i)?; + Ok((i, (o, r))) + })(i)?; + let v = if let Some((o, r)) = expr_tail { + let expr = match r { + Value::Expression(r) => r.augment(start, o), + _ => Expression::new(start, o, r), + }; + Value::from(expr) + } else { + start + }; + Ok((i, v)) } /// Parse any `Value` excluding binary expressions pub fn single(i: &str) -> IResult<&str, Value> { - alt(( + let (i, v) = alt(( alt(( terminated( alt(( @@ -2725,7 +2759,7 @@ pub fn single(i: &str) -> IResult<&str, Value> { )), keyword, ), - map(idiom::multi, Value::from), + map(idiom::multi_without_start, Value::from), )), alt(( map(cast, Value::from), @@ -2753,11 +2787,12 @@ pub fn single(i: &str) -> IResult<&str, Value> { map(strand, Value::from), map(idiom::path, Value::from), )), - ))(i) + ))(i)?; + reparse_idiom_start(v, i) } pub fn select(i: &str) -> IResult<&str, Value> { - alt(( + let (i, v) = alt(( alt(( map(unary, Value::from), map(binary, Value::from), @@ -2765,7 +2800,7 @@ pub fn select(i: &str) -> IResult<&str, Value> { map(tag_no_case("NULL"), |_| Value::Null), map(tag_no_case("true"), |_| Value::Bool(true)), map(tag_no_case("false"), |_| Value::Bool(false)), - map(idiom::multi, Value::from), + map(idiom::multi_without_start, Value::from), )), alt(( map(cast, Value::from), @@ -2790,33 +2825,14 @@ pub fn select(i: &str) -> IResult<&str, Value> { map(table, Value::from), map(strand, Value::from), )), - ))(i) -} - -/// Used as the starting part of a complex Idiom -pub fn start(i: &str) -> IResult<&str, Value> { - alt(( - map(function::normal, Value::from), - map(function::custom, Value::from), - map(subquery, Value::from), - map(constant, Value::from), - map(datetime, Value::from), - map(duration, Value::from), - map(unique, Value::from), - map(number, Value::from), - map(strand, Value::from), - map(object, Value::from), - map(array, Value::from), - map(param, Value::from), - map(edges, Value::from), - map(thing, Value::from), - ))(i) + ))(i)?; + reparse_idiom_start(v, i) } /// Used in CREATE, UPDATE, and DELETE clauses pub fn what(i: &str) -> IResult<&str, Value> { - alt(( - map(idiom::multi, Value::from), + let (i, v) = alt(( + map(idiom::multi_without_start, Value::from), map(function, Value::from), map(subquery, Value::from), map(constant, Value::from), @@ -2830,7 +2846,8 @@ pub fn what(i: &str) -> IResult<&str, Value> { map(range, Value::from), map(thing, Value::from), map(table, Value::from), - ))(i) + ))(i)?; + reparse_idiom_start(v, i) } /// Used to parse any simple JSON-like value