partiql/planner/
filter.rs1use crate::sql::re_from_str;
2use crate::sql::Env;
3use crate::sql::Expr;
4use crate::sql::Selector;
5use crate::sql::WhereCond;
6use crate::value::PqlValue;
7
8#[derive(Debug, Default, Clone)]
9pub struct Filter(pub Option<Box<WhereCond>>);
10
11impl Filter {
12 pub fn execute(self, value: PqlValue, env: &Env) -> PqlValue {
13 match &self.0 {
14 None => value,
15 Some(box WhereCond::Eq { expr, right }) => match expr {
16 Expr::Selector(selector) => {
17 let selector = selector.expand_fullpath2(&env);
18 let cond = WhereCond::Eq {
19 expr: expr.to_owned(),
20 right: right.to_owned(),
21 };
22 value
23 .restrict(&selector, &Some(cond))
24 .expect("restricted value")
25 }
26 _ => {
27 todo!();
28 }
29 },
30 Some(box WhereCond::Like { expr, right }) => match expr {
31 Expr::Selector(selector) => {
32 let selector = selector.expand_fullpath2(&env);
33 let cond = WhereCond::Like {
34 expr: expr.to_owned(),
35 right: right.to_owned(),
36 };
37 value
38 .restrict(&selector, &Some(cond))
39 .expect("restricted value")
40 }
41 _ => {
42 todo!();
43 }
44 },
45 _ => {
46 dbg!(&self);
47 todo!()
48 }
49 }
50 }
51}
52
53pub fn restrict(
54 value: Option<PqlValue>,
55 path: &Selector,
56 cond: &Option<WhereCond>,
57) -> Option<PqlValue> {
58 match value {
59 None => None,
60 Some(PqlValue::Boolean(boolean)) if boolean => Some(PqlValue::Boolean(boolean)),
61 Some(PqlValue::Boolean(_)) => None,
62 Some(PqlValue::Null) => None,
63 Some(PqlValue::Str(string)) => {
64 let is_match = match cond {
65 Some(WhereCond::Eq { expr: _, right }) => {
66 PqlValue::Str(string.clone()) == right.to_owned()
67 }
68 Some(WhereCond::Like { expr: _, right }) => re_from_str(&right).is_match(&string),
69
70 _ => unreachable!(),
71 };
72 if is_match {
73 Some(PqlValue::Str(string.to_owned()))
74 } else {
75 None
76 }
77 }
78 Some(PqlValue::Float(float)) => Some(PqlValue::Float(float)),
79 Some(PqlValue::Array(array)) => {
80 let arr = array
81 .into_iter()
82 .filter_map(|v| {
83 let vv = restrict(Some(v), path, cond);
84 vv
85 })
86 .collect::<Vec<_>>();
87
88 if arr.is_empty() {
89 None
90 } else {
91 Some(PqlValue::Array(arr))
92 }
93 }
94 Some(PqlValue::Object(mut object)) => {
95 if let Some((head, tail)) = &path.split_first() {
96 if let Some(value) = object.get(&head.to_string()) {
97 match restrict(Some(value.to_owned()), &tail, cond) {
98 Some(v) if tail.to_vec().len() > 0 => {
99 let it = object.get_mut(&head.to_string()).unwrap();
100 *it = v.to_owned();
101 Some(PqlValue::Object(object))
102 }
103 Some(_v) => Some(PqlValue::Object(object.to_owned())),
104 _ => None,
105 }
106 } else {
107 None
108 }
109 } else {
110 unreachable!()
111 }
112 }
113 _ => {
114 todo!();
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use std::str::FromStr;
122
123 use super::restrict;
124 use crate::pqlir_parser;
125 use crate::sql::Expr;
126 use crate::sql::Selector;
127 use crate::sql::WhereCond;
128 use crate::value::PqlValue;
129
130 #[test]
131 fn boolean() -> anyhow::Result<()> {
132 let value = PqlValue::from_str(
133 "
134 <<true, false, null>>
135 ",
136 )?;
137
138 let res = value.restrict(&Selector::default(), &None);
139 assert_eq!(res, Some(PqlValue::from_str(r#"<<true>>"#)?));
140 Ok(())
141 }
142
143 #[test]
144 fn missing() -> anyhow::Result<()> {
145 let value = PqlValue::from_str(
146 "
147{
148 'top': <<
149 {'a': 1, 'b': true, 'c': 'alpha'},
150 {'a': 2, 'b': null, 'c': 'beta'},
151 {'a': 3, 'c': 'gamma'}
152 >>
153}
154 ",
155 )?;
156 let res = value.restrict(&Selector::from("top.b"), &None);
157 let expected = pqlir_parser::pql_value(
158 "
159{
160 'top': <<
161 {'a': 1, 'b': true, 'c': 'alpha'}
162 >>
163}
164 ",
165 )?;
166 assert_eq!(res, Some(expected));
167
168 Ok(())
169 }
170
171 #[test]
172 fn pattern_string() -> anyhow::Result<()> {
173 let value = PqlValue::from_str(
174 "
175{
176 'hr': {
177 'employeesNest': <<
178 {
179 'id': 3,
180 'name': 'Bob Smith',
181 'title': null,
182 'projects': [ { 'name': 'AWS Redshift Spectrum querying' },
183 { 'name': 'AWS Redshift security' },
184 { 'name': 'AWS Aurora security' }
185 ]
186 },
187 {
188 'id': 4,
189 'name': 'Susan Smith',
190 'title': 'Dev Mgr',
191 'projects': []
192 },
193 {
194 'id': 6,
195 'name': 'Jane Smith',
196 'title': 'Software Eng 2',
197 'projects': [ { 'name': 'AWS Redshift security' } ]
198 }
199 >>
200 }
201}
202 ",
203 )?;
204 let selector = Selector::from("hr.employeesNest.projects.name");
205 let cond = WhereCond::Like {
206 expr: Expr::default(),
207 right: "%security%".to_owned(),
208 };
209 let res = value.restrict(&selector, &Some(cond));
210 let expected = pqlir_parser::pql_value(
211 "
212{
213 'hr': {
214 'employeesNest': <<
215 {
216 'id': 3,
217 'name': 'Bob Smith',
218 'title': null,
219 'projects': [
220 { 'name': 'AWS Redshift security' },
221 { 'name': 'AWS Aurora security' }
222 ]
223 },
224 {
225 'id': 6,
226 'name': 'Jane Smith',
227 'title': 'Software Eng 2',
228 'projects': [ { 'name': 'AWS Redshift security' } ]
229 }
230 >>
231 }
232}
233 ",
234 )?;
235 assert_eq!(res, Some(expected));
236
237 Ok(())
238 }
239}