Skip to content

Commit 670fd25

Browse files
authored
Add built-in functions: position, ocetet_len, bit_len (#282)
- Add implementations - Update partiql-tests
1 parent d49566e commit 670fd25

File tree

6 files changed

+135
-7
lines changed

6 files changed

+135
-7
lines changed

partiql-eval/src/eval.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,32 @@ impl EvalExpr for EvalFnCharLength {
12181218
}
12191219
}
12201220

1221+
/// Represents a built-in octet length string function, e.g. `octet_length('123456789')`.
1222+
#[derive(Debug)]
1223+
pub struct EvalFnOctetLength {
1224+
pub value: Box<dyn EvalExpr>,
1225+
}
1226+
1227+
impl EvalExpr for EvalFnOctetLength {
1228+
#[inline]
1229+
fn evaluate(&self, bindings: &Tuple, ctx: &dyn EvalContext) -> Value {
1230+
string_transform(self.value.evaluate(bindings, ctx), |s| s.len().into())
1231+
}
1232+
}
1233+
1234+
/// Represents a built-in bit length string function, e.g. `bit_length('123456789')`.
1235+
#[derive(Debug)]
1236+
pub struct EvalFnBitLength {
1237+
pub value: Box<dyn EvalExpr>,
1238+
}
1239+
1240+
impl EvalExpr for EvalFnBitLength {
1241+
#[inline]
1242+
fn evaluate(&self, bindings: &Tuple, ctx: &dyn EvalContext) -> Value {
1243+
string_transform(self.value.evaluate(bindings, ctx), |s| (s.len() * 8).into())
1244+
}
1245+
}
1246+
12211247
/// Represents a built-in substring string function, e.g. `substring('123456789' FROM 2)`.
12221248
#[derive(Debug)]
12231249
pub struct EvalFnSubstring {
@@ -1276,6 +1302,38 @@ impl EvalExpr for EvalFnSubstring {
12761302
}
12771303
}
12781304

1305+
/// Represents a built-in position string function, e.g. `position('3' IN '123456789')`.
1306+
#[derive(Debug)]
1307+
pub struct EvalFnPosition {
1308+
pub needle: Box<dyn EvalExpr>,
1309+
pub haystack: Box<dyn EvalExpr>,
1310+
}
1311+
1312+
impl EvalExpr for EvalFnPosition {
1313+
#[inline]
1314+
fn evaluate(&self, bindings: &Tuple, ctx: &dyn EvalContext) -> Value {
1315+
let needle = match self.needle.evaluate(bindings, ctx) {
1316+
Null => None,
1317+
Value::String(s) => Some(s),
1318+
_ => return Value::Missing,
1319+
};
1320+
let haystack = match self.haystack.evaluate(bindings, ctx) {
1321+
Value::Null => return Value::Null,
1322+
Value::String(s) => s,
1323+
_ => return Value::Missing,
1324+
};
1325+
if let Some(needle) = needle {
1326+
haystack
1327+
.find(needle.as_ref())
1328+
.map(|l| l + 1)
1329+
.unwrap_or(0)
1330+
.into()
1331+
} else {
1332+
Value::Null
1333+
}
1334+
}
1335+
}
1336+
12791337
#[inline]
12801338
#[track_caller]
12811339
fn trim<FnTrim>(value: Value, to_trim: Value, trim_fn: FnTrim) -> Value

partiql-eval/src/plan.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ use partiql_logical::{
1111
use crate::eval;
1212
use crate::eval::{
1313
EvalBagExpr, EvalBetweenExpr, EvalBinOp, EvalBinOpExpr, EvalDynamicLookup, EvalExpr,
14-
EvalFnBtrim, EvalFnCharLength, EvalFnExists, EvalFnLower, EvalFnLtrim, EvalFnRtrim,
15-
EvalFnSubstring, EvalFnUpper, EvalIsTypeExpr, EvalJoinKind, EvalLikeMatch, EvalListExpr,
16-
EvalLitExpr, EvalPath, EvalPlan, EvalSearchedCaseExpr, EvalSubQueryExpr, EvalTupleExpr,
17-
EvalUnaryOp, EvalUnaryOpExpr, EvalVarRef, Evaluable,
14+
EvalFnBitLength, EvalFnBtrim, EvalFnCharLength, EvalFnExists, EvalFnLower, EvalFnLtrim,
15+
EvalFnOctetLength, EvalFnPosition, EvalFnRtrim, EvalFnSubstring, EvalFnUpper, EvalIsTypeExpr,
16+
EvalJoinKind, EvalLikeMatch, EvalListExpr, EvalLitExpr, EvalPath, EvalPlan,
17+
EvalSearchedCaseExpr, EvalSubQueryExpr, EvalTupleExpr, EvalUnaryOp, EvalUnaryOpExpr,
18+
EvalVarRef, Evaluable,
1819
};
1920
use crate::pattern_match::like_to_re_pattern;
2021
use partiql_value::Value::Null;
@@ -362,6 +363,18 @@ impl EvaluatorPlanner {
362363
value: args.pop().unwrap(),
363364
})
364365
}
366+
CallName::OctetLength => {
367+
assert_eq!(args.len(), 1);
368+
Box::new(EvalFnOctetLength {
369+
value: args.pop().unwrap(),
370+
})
371+
}
372+
CallName::BitLength => {
373+
assert_eq!(args.len(), 1);
374+
Box::new(EvalFnBitLength {
375+
value: args.pop().unwrap(),
376+
})
377+
}
365378
CallName::LTrim => {
366379
assert_eq!(args.len(), 2);
367380
let value = args.pop().unwrap();
@@ -397,6 +410,12 @@ impl EvaluatorPlanner {
397410
length,
398411
})
399412
}
413+
CallName::Position => {
414+
assert_eq!(args.len(), 2);
415+
let haystack = args.pop().unwrap();
416+
let needle = args.pop().unwrap();
417+
Box::new(EvalFnPosition { needle, haystack })
418+
}
400419
CallName::Exists => {
401420
assert_eq!(args.len(), 1);
402421
Box::new(EvalFnExists {

partiql-logical-planner/src/call_defs.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,36 @@ fn function_call_def_char_len() -> CallDef {
9696
}
9797
}
9898

99+
fn function_call_def_octet_len() -> CallDef {
100+
CallDef {
101+
names: vec!["octet_length"],
102+
overloads: vec![CallSpec {
103+
input: vec![CallSpecArg::Positional],
104+
output: Box::new(|args| {
105+
logical::ValueExpr::Call(logical::CallExpr {
106+
name: logical::CallName::OctetLength,
107+
arguments: args,
108+
})
109+
}),
110+
}],
111+
}
112+
}
113+
114+
fn function_call_def_bit_len() -> CallDef {
115+
CallDef {
116+
names: vec!["bit_length"],
117+
overloads: vec![CallSpec {
118+
input: vec![CallSpecArg::Positional],
119+
output: Box::new(|args| {
120+
logical::ValueExpr::Call(logical::CallExpr {
121+
name: logical::CallName::BitLength,
122+
arguments: args,
123+
})
124+
}),
125+
}],
126+
}
127+
}
128+
99129
fn function_call_def_lower() -> CallDef {
100130
CallDef {
101131
names: vec!["lower"],
@@ -188,6 +218,21 @@ fn function_call_def_substring() -> CallDef {
188218
}
189219
}
190220

221+
fn function_call_def_position() -> CallDef {
222+
CallDef {
223+
names: vec!["position"],
224+
overloads: vec![CallSpec {
225+
input: vec![CallSpecArg::Positional, CallSpecArg::Named("in".into())],
226+
output: Box::new(|args| {
227+
logical::ValueExpr::Call(logical::CallExpr {
228+
name: logical::CallName::Position,
229+
arguments: args,
230+
})
231+
}),
232+
}],
233+
}
234+
}
235+
191236
fn function_call_def_trim() -> CallDef {
192237
CallDef {
193238
names: vec!["trim"],
@@ -335,9 +380,12 @@ pub fn function_call_def() -> FnSymTab {
335380

336381
for def in [
337382
function_call_def_char_len(),
383+
function_call_def_octet_len(),
384+
function_call_def_bit_len(),
338385
function_call_def_lower(),
339386
function_call_def_upper(),
340387
function_call_def_substring(),
388+
function_call_def_position(),
341389
function_call_def_trim(),
342390
function_call_def_coalesce(),
343391
function_call_def_nullif(),

partiql-logical/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,10 +466,13 @@ pub enum CallName {
466466
Lower,
467467
Upper,
468468
CharLength,
469+
OctetLength,
470+
BitLength,
469471
LTrim,
470472
BTrim,
471473
RTrim,
472474
Substring,
475+
Position,
473476
Exists,
474477
}
475478

partiql-parser/src/preprocessor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::token_parser::{BufferedToken, TokenParser};
1313
use once_cell::sync::Lazy;
1414
use partiql_source_map::line_offset_tracker::LineOffsetTracker;
1515

16-
pub(crate) static BUILT_INS: Lazy<FnExprSet<'static>> = Lazy::new(|| built_ins());
16+
pub(crate) static BUILT_INS: Lazy<FnExprSet<'static>> = Lazy::new(built_ins);
1717

1818
/// A single "function expression" argument match.
1919
#[derive(Debug, Clone)]
@@ -107,7 +107,7 @@ mod built_ins {
107107
#[rustfmt::skip]
108108
patterns: vec![
109109
// e.g. position('foo' in 'xyzfooxyz') => position('foo', in: 'xyzfooxyz')
110-
vec![AnyOne(false), AnyStar(false), Kw(Token::In), AnyOne(true), AnyStar(false)]
110+
vec![AnyOne(true), AnyStar(false), Kw(Token::In), AnyOne(true), AnyStar(false)]
111111
],
112112
}
113113
}

0 commit comments

Comments
 (0)