Skip to content

Commit 024620a

Browse files
authored
Translate collection literals to Value when lowering (#380)
1 parent dc1edc4 commit 024620a

File tree

6 files changed

+359
-192
lines changed

6 files changed

+359
-192
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99
### Changed
1010
- *BREAKING:* partiql-eval: `evaluate` on `Evaluable` returns a `Value` rather than an `Option<Value>`
11+
- *BREAKING:* partiql-ast: changes the modeling of Bag/List/Tuple literals
1112
### Added
1213
- Ability to add and view errors during evaluation with partiql-eval's `EvalContext`
14+
- AST sub-trees representing literal values are lowered to `Value`s during planning
1315
### Fixes
1416

1517
## [0.4.1] - 2023-05-25

partiql-ast/src/ast.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -444,19 +444,16 @@ pub enum Lit {
444444
#[visit(skip)]
445445
HexStringLit(String),
446446
#[visit(skip)]
447-
CollectionLit(CollectionLit),
447+
StructLit(AstNode<Struct>),
448+
#[visit(skip)]
449+
BagLit(AstNode<Bag>),
450+
#[visit(skip)]
451+
ListLit(AstNode<List>),
448452
/// E.g. `TIME WITH TIME ZONE` in `SELECT TIME WITH TIME ZONE '12:00' FROM ...`
449453
#[visit(skip)]
450454
TypedLit(String, Type),
451455
}
452456

453-
#[derive(Clone, Debug, PartialEq, Eq)]
454-
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
455-
pub enum CollectionLit {
456-
ArrayLit(String),
457-
BagLit(String),
458-
}
459-
460457
#[derive(Visit, Clone, Debug, PartialEq, Eq)]
461458
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
462459
pub struct VarRef {

partiql-logical-planner/src/lower.rs

Lines changed: 91 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use partiql_ast::ast;
66
use partiql_ast::ast::{
77
Assignment, Bag, Between, BinOp, BinOpKind, Call, CallAgg, CallArg, CallArgNamed,
88
CaseSensitivity, CreateIndex, CreateTable, Ddl, DdlOp, Delete, Dml, DmlOp, DropIndex,
9-
DropTable, FromClause, FromLet, FromLetKind, GroupByExpr, GroupKey, GroupingStrategy, Insert,
10-
InsertValue, Item, Join, JoinKind, JoinSpec, Like, List, Lit, NodeId, NullOrderingSpec,
9+
DropTable, Expr, FromClause, FromLet, FromLetKind, GroupByExpr, GroupKey, GroupingStrategy,
10+
Insert, InsertValue, Item, Join, JoinKind, JoinSpec, Like, List, Lit, NodeId, NullOrderingSpec,
1111
OnConflict, OrderByExpr, OrderingSpec, Path, PathStep, ProjectExpr, Projection, ProjectionKind,
1212
Query, QuerySet, Remove, SearchedCase, Select, Set, SetExpr, SetQuantifier, Sexp, SimpleCase,
1313
SortSpec, Struct, SymbolPrimitive, UniOp, UniOpKind, VarRef,
@@ -979,48 +979,12 @@ impl<'a, 'ast> Visitor<'ast> for AstToLogical<'a> {
979979

980980
// Values & Value Constructors
981981

982-
fn enter_lit(&mut self, _lit: &'ast Lit) -> Traverse {
983-
let val = match _lit {
984-
Lit::Null => Value::Null,
985-
Lit::Missing => Value::Missing,
986-
Lit::Int8Lit(n) => Value::Integer(*n as i64),
987-
Lit::Int16Lit(n) => Value::Integer(*n as i64),
988-
Lit::Int32Lit(n) => Value::Integer(*n as i64),
989-
Lit::Int64Lit(n) => Value::Integer(*n),
990-
Lit::DecimalLit(d) => Value::Decimal(*d),
991-
Lit::NumericLit(n) => Value::Decimal(*n),
992-
Lit::RealLit(f) => Value::Real(OrderedFloat::from(*f as f64)),
993-
Lit::FloatLit(f) => Value::Real(OrderedFloat::from(*f as f64)),
994-
Lit::DoubleLit(f) => Value::Real(OrderedFloat::from(*f)),
995-
Lit::BoolLit(b) => Value::Boolean(*b),
996-
Lit::IonStringLit(s) => match parse_embedded_ion_str(s) {
997-
Ok(v) => v,
998-
Err(e) => {
999-
// Report error but allow visitor to continue
1000-
self.errors.push(e);
1001-
Value::Missing
1002-
}
1003-
},
1004-
Lit::CharStringLit(s) => Value::String(Box::new(s.clone())),
1005-
Lit::NationalCharStringLit(s) => Value::String(Box::new(s.clone())),
1006-
Lit::BitStringLit(_) => {
1007-
// Report error but allow visitor to continue
1008-
not_yet_implemented_err!(self, "Lit::BitStringLit".to_string());
1009-
Value::Missing
1010-
}
1011-
Lit::HexStringLit(_) => {
1012-
// Report error but allow visitor to continue
1013-
not_yet_implemented_err!(self, "Lit::HexStringLit".to_string());
1014-
Value::Missing
1015-
}
1016-
Lit::CollectionLit(_) => {
982+
fn enter_lit(&mut self, lit: &'ast Lit) -> Traverse {
983+
let val = match lit_to_value(lit) {
984+
Ok(v) => v,
985+
Err(e) => {
1017986
// Report error but allow visitor to continue
1018-
not_yet_implemented_err!(self, "Lit::CollectionLit".to_string());
1019-
Value::Missing
1020-
}
1021-
Lit::TypedLit(_, _) => {
1022-
// Report error but allow visitor to continue
1023-
not_yet_implemented_err!(self, "Lit::TypedLit".to_string());
987+
self.errors.push(e);
1024988
Value::Missing
1025989
}
1026990
};
@@ -1714,6 +1678,90 @@ impl<'a, 'ast> Visitor<'ast> for AstToLogical<'a> {
17141678
}
17151679
}
17161680

1681+
fn lit_to_value(lit: &Lit) -> Result<Value, LowerError> {
1682+
fn expect_lit(v: &Expr) -> Result<Value, LowerError> {
1683+
match v {
1684+
Expr::Lit(l) => lit_to_value(&l.node),
1685+
_ => Err(LowerError::IllegalState(
1686+
"non literal in literal aggregate".to_string(),
1687+
)),
1688+
}
1689+
}
1690+
1691+
fn tuple_pair(pair: &ast::ExprPair) -> Option<Result<(String, Value), LowerError>> {
1692+
let key = match expect_lit(pair.first.as_ref()) {
1693+
Ok(Value::String(s)) => s.as_ref().clone(),
1694+
Ok(_) => {
1695+
return Some(Err(LowerError::IllegalState(
1696+
"non string literal in literal struct key".to_string(),
1697+
)))
1698+
}
1699+
Err(e) => return Some(Err(e)),
1700+
};
1701+
1702+
match expect_lit(pair.second.as_ref()) {
1703+
Ok(Value::Missing) => None,
1704+
Ok(val) => Some(Ok((key, val))),
1705+
Err(e) => Some(Err(e)),
1706+
}
1707+
}
1708+
1709+
let val = match lit {
1710+
Lit::Null => Value::Null,
1711+
Lit::Missing => Value::Missing,
1712+
Lit::Int8Lit(n) => Value::Integer(*n as i64),
1713+
Lit::Int16Lit(n) => Value::Integer(*n as i64),
1714+
Lit::Int32Lit(n) => Value::Integer(*n as i64),
1715+
Lit::Int64Lit(n) => Value::Integer(*n),
1716+
Lit::DecimalLit(d) => Value::Decimal(*d),
1717+
Lit::NumericLit(n) => Value::Decimal(*n),
1718+
Lit::RealLit(f) => Value::Real(OrderedFloat::from(*f as f64)),
1719+
Lit::FloatLit(f) => Value::Real(OrderedFloat::from(*f as f64)),
1720+
Lit::DoubleLit(f) => Value::Real(OrderedFloat::from(*f)),
1721+
Lit::BoolLit(b) => Value::Boolean(*b),
1722+
Lit::IonStringLit(s) => parse_embedded_ion_str(s)?,
1723+
Lit::CharStringLit(s) => Value::String(Box::new(s.clone())),
1724+
Lit::NationalCharStringLit(s) => Value::String(Box::new(s.clone())),
1725+
Lit::BitStringLit(_) => {
1726+
return Err(LowerError::NotYetImplemented(
1727+
"Lit::BitStringLit".to_string(),
1728+
))
1729+
}
1730+
Lit::HexStringLit(_) => {
1731+
return Err(LowerError::NotYetImplemented(
1732+
"Lit::HexStringLit".to_string(),
1733+
))
1734+
}
1735+
Lit::BagLit(b) => {
1736+
let bag: Result<partiql_value::Bag, _> = b
1737+
.node
1738+
.values
1739+
.iter()
1740+
.map(|l| expect_lit(l.as_ref()))
1741+
.collect();
1742+
Value::from(bag?)
1743+
}
1744+
Lit::ListLit(l) => {
1745+
let l: Result<partiql_value::List, _> = l
1746+
.node
1747+
.values
1748+
.iter()
1749+
.map(|l| expect_lit(l.as_ref()))
1750+
.collect();
1751+
Value::from(l?)
1752+
}
1753+
Lit::StructLit(s) => {
1754+
let tuple: Result<partiql_value::Tuple, _> =
1755+
s.node.fields.iter().filter_map(tuple_pair).collect();
1756+
Value::from(tuple?)
1757+
}
1758+
Lit::TypedLit(_, _) => {
1759+
return Err(LowerError::NotYetImplemented("Lit::TypedLit".to_string()))
1760+
}
1761+
};
1762+
Ok(val)
1763+
}
1764+
17171765
fn parse_embedded_ion_str(contents: &str) -> Result<Value, LowerError> {
17181766
fn lit_err(literal: &str, err: impl std::error::Error) -> LowerError {
17191767
LowerError::Literal {

partiql-parser/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ num-bigint = "~0.4.0"
3535
bigdecimal = "~0.2.0"
3636
rust_decimal = { version = "1.25.0", default-features = false, features = ["std"] }
3737

38+
bitflags = "2"
39+
3840
lalrpop-util = "0.20"
3941
logos = "0.12"
4042

partiql-parser/src/parse/parse_util.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,64 @@
11
use partiql_ast::ast;
22

3+
use bitflags::bitflags;
4+
5+
bitflags! {
6+
/// Set of AST node attributes to use as synthesized attributes.
7+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
8+
pub(crate) struct Attrs: u8 {
9+
const LIT = 0b00000001;
10+
11+
const INTERSECTABLE = Self::LIT.bits();
12+
const UNIONABLE = 0;
13+
}
14+
}
15+
16+
impl Attrs {
17+
/// Combine attributes from two nodes.
18+
#[inline]
19+
pub fn synthesize(self, other: Self) -> Attrs {
20+
((self & Attrs::INTERSECTABLE) & (other & Attrs::INTERSECTABLE))
21+
| ((self & Attrs::UNIONABLE) | (other & Attrs::UNIONABLE))
22+
}
23+
}
24+
25+
/// Wrapper attaching synthesized attributes `Attrs` with an AST node.
26+
pub(crate) struct Synth<T> {
27+
pub(crate) data: T,
28+
pub(crate) attrs: Attrs,
29+
}
30+
31+
impl<T> Synth<T> {
32+
#[inline]
33+
pub fn new(data: T, attrs: Attrs) -> Self {
34+
Synth { data, attrs }
35+
}
36+
37+
#[inline]
38+
pub fn empty(data: T) -> Self {
39+
Self::new(data, Attrs::empty())
40+
}
41+
}
42+
43+
impl<T> FromIterator<Synth<T>> for Synth<Vec<T>> {
44+
#[inline]
45+
fn from_iter<I: IntoIterator<Item = Synth<T>>>(iter: I) -> Synth<Vec<T>> {
46+
let mut attrs = Attrs::all();
47+
let iterator = iter.into_iter().map(|Synth { data, attrs: a }| {
48+
attrs = attrs.synthesize(a);
49+
data
50+
});
51+
let data = iterator.collect::<Vec<_>>();
52+
Synth { data, attrs }
53+
}
54+
}
55+
356
pub(crate) enum CallSite {
457
Call(ast::Call),
558
CallAgg(ast::CallAgg),
659
}
760

61+
#[inline]
862
// if this is just a parenthesized expr, lift it out of the query AST, otherwise return input
963
// e.g. `(1+2)` should be a ExprKind::Expr, not wrapped deep in a ExprKind::Query
1064
pub(crate) fn strip_query(q: Box<ast::Expr>) -> Box<ast::Expr> {

0 commit comments

Comments
 (0)