Skip to content

Commit

Permalink
Add Parsing of EXCLUDE (#480)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpschorr authored Jul 25, 2024
1 parent 3ca67b7 commit 979c0cb
Show file tree
Hide file tree
Showing 13 changed files with 832 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
### Changed
- *BREAKING:* partiql-ast: added modeling of `EXCLUDE`
- *BREAKING:* partiql-ast: added pretty-printing of `EXCLUDE`

### Added
- *BREAKING:* partiql-parser: added parsing of `EXCLUDE`

### Fixed

Expand Down
28 changes: 28 additions & 0 deletions partiql-ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ pub enum SetQuantifier {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Select {
pub project: AstNode<Projection>,
pub exclude: Option<AstNode<Exclusion>>,
pub from: Option<AstNode<FromClause>>,
pub from_let: Option<AstNode<Let>>,
pub where_clause: Option<Box<AstNode<WhereClause>>>,
Expand Down Expand Up @@ -375,6 +376,12 @@ pub struct ProjectExpr {
pub as_alias: Option<SymbolPrimitive>,
}

#[derive(Visit, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Exclusion {
pub items: Vec<AstNode<ExcludePath>>,
}

/// The expressions that can result in values.
#[derive(Visit, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down Expand Up @@ -690,6 +697,27 @@ pub struct PathExpr {
pub index: Box<Expr>,
}

#[derive(Visit, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ExcludePath {
pub root: AstNode<VarRef>,
pub steps: Vec<ExcludePathStep>,
}

/// A "step" within an exclude path; that is the components of the exclude path following the root.
#[derive(Visit, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ExcludePathStep {
#[visit(skip)]
PathProject(AstNode<SymbolPrimitive>),
#[visit(skip)]
PathIndex(AstNode<Lit>),
#[visit(skip)]
PathForEach,
#[visit(skip)]
PathUnpivot,
}

#[derive(Visit, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Let {
Expand Down
52 changes: 52 additions & 0 deletions partiql-ast/src/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,19 @@ impl PrettyDoc for Select {
D::Doc: Clone,
A: Clone,
{
fn delegate<'b, C, D, A>(child: &'b Option<C>, arena: &'b D) -> Option<DocBuilder<'b, D, A>>
where
D: DocAllocator<'b, A>,
D::Doc: Clone,
A: Clone,
C: PrettyDoc,
{
child.as_ref().map(|inner| inner.pretty_doc(arena).group())
}

let Select {
project,
exclude,
from,
from_let,
where_clause,
Expand All @@ -223,6 +234,7 @@ impl PrettyDoc for Select {
} = self;
let clauses = [
Some(project.pretty_doc(arena).group()),
delegate(exclude, arena),
from.as_ref().map(|inner| inner.pretty_doc(arena).group()),
from_let
.as_ref()
Expand Down Expand Up @@ -313,6 +325,46 @@ impl PrettyDoc for ProjectExpr {
}
}

impl PrettyDoc for Exclusion {
fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A>
where
D: DocAllocator<'b, A>,
D::Doc: Clone,
A: Clone,
{
pretty_annotated_doc(
"EXCLUDE",
pretty_list(&self.items, MINOR_NEST_INDENT, arena),
arena,
)
}
}

impl PrettyDoc for ExcludePath {
fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A>
where
D: DocAllocator<'b, A>,
D::Doc: Clone,
A: Clone,
{
let ExcludePath { root, steps } = self;
let mut path = root.pretty_doc(arena);
for step in steps {
path = path.append(match step {
ExcludePathStep::PathProject(e) => arena.text(".").append(e.pretty_doc(arena)),
ExcludePathStep::PathIndex(e) => arena
.text("[")
.append(e.pretty_doc(arena))
.append(arena.text("]")),
ExcludePathStep::PathForEach => arena.text("[*]"),
ExcludePathStep::PathUnpivot => arena.text(".*"),
});
}

path
}
}

impl PrettyDoc for Expr {
fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A>
where
Expand Down
18 changes: 18 additions & 0 deletions partiql-ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,24 @@ pub trait Visitor<'ast> {
fn exit_project_expr(&mut self, _project_expr: &'ast ast::ProjectExpr) -> Traverse {
Traverse::Continue
}
fn enter_exclusion(&mut self, _exclusion: &'ast ast::Exclusion) -> Traverse {
Traverse::Continue
}
fn exit_exclusion(&mut self, _exclusion: &'ast ast::Exclusion) -> Traverse {
Traverse::Continue
}
fn enter_exclude_path(&mut self, _path: &'ast ast::ExcludePath) -> Traverse {
Traverse::Continue
}
fn exit_exclude_path(&mut self, _path: &'ast ast::ExcludePath) -> Traverse {
Traverse::Continue
}
fn enter_exclude_path_step(&mut self, _step: &'ast ast::ExcludePathStep) -> Traverse {
Traverse::Continue
}
fn exit_exclude_path_step(&mut self, _step: &'ast ast::ExcludePathStep) -> Traverse {
Traverse::Continue
}
fn enter_expr(&mut self, _expr: &'ast ast::Expr) -> Traverse {
Traverse::Continue
}
Expand Down
6 changes: 5 additions & 1 deletion partiql-logical-planner/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use partiql_ast::ast;
use partiql_ast::ast::{
Assignment, Bag, BagOpExpr, BagOperator, Between, BinOp, BinOpKind, Call, CallAgg, CallArg,
CallArgNamed, CaseSensitivity, CreateIndex, CreateTable, Ddl, DdlOp, Delete, Dml, DmlOp,
DropIndex, DropTable, Expr, FromClause, FromLet, FromLetKind, GroupByExpr, GroupKey,
DropIndex, DropTable, Exclusion, Expr, FromClause, FromLet, FromLetKind, GroupByExpr, GroupKey,
GroupingStrategy, Insert, InsertValue, Item, Join, JoinKind, JoinSpec, Like, List, Lit, NodeId,
NullOrderingSpec, OnConflict, OrderByExpr, OrderingSpec, Path, PathStep, ProjectExpr,
Projection, ProjectionKind, Query, QuerySet, Remove, SearchedCase, Select, Set, SetQuantifier,
Expand Down Expand Up @@ -810,6 +810,10 @@ impl<'a, 'ast> Visitor<'ast> for AstToLogical<'a> {
Traverse::Continue
}

fn enter_exclusion(&mut self, _exclusion: &'ast Exclusion) -> Traverse {
not_yet_implemented_fault!(self, "EXCLUDE");
}

fn enter_select(&mut self, select: &'ast Select) -> Traverse {
if select.having.is_some() && select.group_by.is_none() {
self.errors.push(AstTransformError::HavingWithoutGroupBy);
Expand Down
3 changes: 3 additions & 0 deletions partiql-parser/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,8 @@ pub enum Token<'input> {
Escape,
#[regex("(?i:Except)")]
Except,
#[regex("(?i:Exclude)")]
Exclude,
#[regex("(?i:False)")]
False,
#[regex("(?i:First)")]
Expand Down Expand Up @@ -776,6 +778,7 @@ impl<'input> fmt::Display for Token<'input> {
| Token::End
| Token::Escape
| Token::Except
| Token::Exclude
| Token::False
| Token::First
| Token::For
Expand Down
66 changes: 58 additions & 8 deletions partiql-parser/src/parse/partiql.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,15 @@ SfwQuery: ast::AstNode<ast::Select> = {
SfwClauses: ast::AstNode<ast::Select> = {
<lo:@L>
<project:SelectClause>
<exclude:ExcludeClause?>
<from:FromClause?>
<where_clause:WhereClause?>
<group_by:GroupClause?>
<having:HavingClause?>
<hi:@R> => {
state.node(ast::Select {
project,
exclude,
from,
from_let: None,
where_clause,
Expand All @@ -206,9 +208,11 @@ FwsClauses: ast::AstNode<ast::Select> = {
<group_by:GroupClause?>
<having:HavingClause?>
<project:SelectClause>
<exclude:ExcludeClause?>
<hi:@R> => {
state.node(ast::Select {
project,
exclude,
from: Some(from),
from_let: None,
where_clause,
Expand Down Expand Up @@ -255,6 +259,13 @@ Projection: ast::AstNode<ast::ProjectItem> = {
},
}

// ------------------------------------------------------------------------------ //
// Exclude //
// ------------------------------------------------------------------------------ //
ExcludeClause: ast::AstNode<ast::Exclusion> = {
<lo:@L> "EXCLUDE" <items:CommaSepPlus<ExcludePath>> <hi:@R> => state.node(ast::Exclusion {items}, lo..hi),
}

// ------------------------------------------------------------------------------ //
// FROM //
// ------------------------------------------------------------------------------ //
Expand Down Expand Up @@ -1121,22 +1132,60 @@ PathExprVarRef: ast::Expr = {
}

VarRefExpr: ast::Expr = {
<lo:@L> <ident:"UnquotedIdent"> <hi:@R> => ast::Expr::VarRef(state.node(ast::VarRef {
<varref:VarRef> => ast::Expr::VarRef(varref),
}

VarRef: ast::AstNode<ast::VarRef> = {
<lo:@L> <ident:"UnquotedIdent"> <hi:@R> => state.node(ast::VarRef {
name: ast::SymbolPrimitive { value: ident.to_owned(), case: ast::CaseSensitivity::CaseInsensitive },
qualifier: ast::ScopeQualifier::Unqualified
}, lo..hi)),
<lo:@L> <ident:"QuotedIdent"> <hi:@R> => ast::Expr::VarRef(state.node(ast::VarRef {
}, lo..hi),
<lo:@L> <ident:"QuotedIdent"> <hi:@R> => state.node(ast::VarRef {
name: ast::SymbolPrimitive { value: ident.to_owned(), case: ast::CaseSensitivity::CaseSensitive },
qualifier: ast::ScopeQualifier::Unqualified
}, lo..hi)),
<lo:@L> <ident:"UnquotedAtIdentifier"> <hi:@R> => ast::Expr::VarRef(state.node(ast::VarRef {
}, lo..hi),
<lo:@L> <ident:"UnquotedAtIdentifier"> <hi:@R> => state.node(ast::VarRef {
name: ast::SymbolPrimitive { value: ident.to_owned(), case: ast::CaseSensitivity::CaseInsensitive },
qualifier: ast::ScopeQualifier::Unqualified
}, lo..hi)),
<lo:@L> <ident:"QuotedAtIdentifier"> <hi:@R> => ast::Expr::VarRef(state.node(ast::VarRef {
}, lo..hi),
<lo:@L> <ident:"QuotedAtIdentifier"> <hi:@R> => state.node(ast::VarRef {
name: ast::SymbolPrimitive { value: ident.to_owned(), case: ast::CaseSensitivity::CaseSensitive },
qualifier: ast::ScopeQualifier::Unqualified
},lo..hi)),
},lo..hi),
}

ExcludePath: ast::AstNode<ast::ExcludePath> = {
<lo:@L> <root:VarRef> <steps:ExcludePathSteps> <hi:@R> => state.node(ast::ExcludePath { root, steps },lo..hi),
}

ExcludePathSteps: Vec<ast::ExcludePathStep> = {
<path:ExcludePathSteps> <step:ExcludePathStep> => {
let mut steps = path;
steps.push(step);
steps
},
<step:ExcludePathStep> => {
vec![step]
},
}

ExcludePathStep: ast::ExcludePathStep = {
"." <lo:@L> <s:SymbolPrimitive> <hi:@R> => {
ast::ExcludePathStep::PathProject( state.node(s, lo..hi) )
},
"[" "*" "]" => {
ast::ExcludePathStep::PathForEach
},
"." "*" => {
ast::ExcludePathStep::PathUnpivot
},
"[" <lo:@L> <l:LiteralNumber> <hi:@R> "]" => {
ast::ExcludePathStep::PathIndex( state.node(l, lo..hi) )
},
"[" <lo:@L> <s:"String"> <hi:@R> "]" => {
let sym = ast::SymbolPrimitive { value: s.to_string(), case: ast::CaseSensitivity::CaseSensitive };
ast::ExcludePathStep::PathProject( state.node(sym, lo..hi) )
},
}

// ------------------------------------------------------------------------------ //
Expand Down Expand Up @@ -1392,6 +1441,7 @@ extern {
"DATE" => lexer::Token::Date,
"DESC" => lexer::Token::Desc,
"DISTINCT" => lexer::Token::Distinct,
"EXCLUDE" => lexer::Token::Exclude,
"ELSE" => lexer::Token::Else,
"END" => lexer::Token::End,
"ESCAPE" => lexer::Token::Escape,
Expand Down
4 changes: 2 additions & 2 deletions partiql-parser/src/preprocessor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,9 +489,9 @@ where
///
/// * `tok` - The current [`Token`] being considered for matching.
/// * `is_nested` - Whether the preprocessor is considering [`Tokens`] inside a nested expression
/// (i.e., inside parens).
/// (i.e., inside parens).
/// * `is_init_arg` - Whether this is the first argument being considered for the function expression's
/// parameters.
/// parameters.
/// * `matchers` - A slice of the remaining arguments for a single pattern for the function expression.
#[allow(clippy::only_used_in_recursion)]
fn match_arg(
Expand Down
Loading

1 comment on commit 979c0cb

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PartiQL (rust) Benchmark

Benchmark suite Current: 979c0cb Previous: 3ca67b7 Ratio
arith_agg-avg 770772 ns/iter (± 2036) 775927 ns/iter (± 3468) 0.99
arith_agg-avg_distinct 855654 ns/iter (± 1802) 856683 ns/iter (± 2372) 1.00
arith_agg-count 821448 ns/iter (± 14841) 818625 ns/iter (± 15375) 1.00
arith_agg-count_distinct 852586 ns/iter (± 17919) 851347 ns/iter (± 4987) 1.00
arith_agg-min 825473 ns/iter (± 1830) 825917 ns/iter (± 2576) 1.00
arith_agg-min_distinct 854880 ns/iter (± 2705) 850563 ns/iter (± 7003) 1.01
arith_agg-max 832107 ns/iter (± 3311) 827990 ns/iter (± 34600) 1.00
arith_agg-max_distinct 864136 ns/iter (± 3303) 864413 ns/iter (± 11462) 1.00
arith_agg-sum 824732 ns/iter (± 17967) 829453 ns/iter (± 16364) 0.99
arith_agg-sum_distinct 859648 ns/iter (± 2287) 857194 ns/iter (± 2443) 1.00
arith_agg-avg-count-min-max-sum 965929 ns/iter (± 3494) 968022 ns/iter (± 3875) 1.00
arith_agg-avg-count-min-max-sum-group_by 1234556 ns/iter (± 17076) 1207285 ns/iter (± 18484) 1.02
arith_agg-avg-count-min-max-sum-group_by-group_as 1825909 ns/iter (± 15262) 1825661 ns/iter (± 17209) 1.00
arith_agg-avg_distinct-count_distinct-min_distinct-max_distinct-sum_distinct 1226847 ns/iter (± 9659) 1240635 ns/iter (± 74432) 0.99
arith_agg-avg_distinct-count_distinct-min_distinct-max_distinct-sum_distinct-group_by 1517280 ns/iter (± 7336) 1520707 ns/iter (± 16588) 1.00
arith_agg-avg_distinct-count_distinct-min_distinct-max_distinct-sum_distinct-group_by-group_as 2111135 ns/iter (± 10607) 2139065 ns/iter (± 10755) 0.99
parse-1 5471 ns/iter (± 28) 4221 ns/iter (± 74) 1.30
parse-15 48888 ns/iter (± 154) 40063 ns/iter (± 732) 1.22
parse-30 93314 ns/iter (± 476) 81388 ns/iter (± 712) 1.15
compile-1 4337 ns/iter (± 28) 4344 ns/iter (± 62) 1.00
compile-15 33665 ns/iter (± 174) 33761 ns/iter (± 218) 1.00
compile-30 70423 ns/iter (± 2461) 70343 ns/iter (± 191) 1.00
plan-1 67596 ns/iter (± 287) 67749 ns/iter (± 363) 1.00
plan-15 1057775 ns/iter (± 21462) 1050246 ns/iter (± 23555) 1.01
plan-30 2119337 ns/iter (± 13062) 2103307 ns/iter (± 10523) 1.01
eval-1 13007144 ns/iter (± 118130) 12792235 ns/iter (± 113115) 1.02
eval-15 87319711 ns/iter (± 1064507) 86096723 ns/iter (± 982386) 1.01
eval-30 167286656 ns/iter (± 695881) 165076640 ns/iter (± 2722727) 1.01
join 9810 ns/iter (± 410) 9756 ns/iter (± 531) 1.01
simple 2476 ns/iter (± 12) 2477 ns/iter (± 18) 1.00
simple-no 439 ns/iter (± 2) 425 ns/iter (± 3) 1.03
numbers 57 ns/iter (± 1) 57 ns/iter (± 0) 1
parse-simple 654 ns/iter (± 5) 566 ns/iter (± 1) 1.16
parse-ion 2259 ns/iter (± 60) 1729 ns/iter (± 3) 1.31
parse-group 6975 ns/iter (± 24) 5812 ns/iter (± 14) 1.20
parse-complex 18265 ns/iter (± 63) 14670 ns/iter (± 164) 1.25
parse-complex-fexpr 25502 ns/iter (± 250) 21437 ns/iter (± 119) 1.19

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.