Skip to content

Commit 3cd62ae

Browse files
committed
substr
1 parent 2ee2033 commit 3cd62ae

6 files changed

Lines changed: 339 additions & 3 deletions

File tree

vortex-array/public-api.lock

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12892,6 +12892,8 @@ pub fn vortex_array::expr::select_exclude(impl core::convert::Into<vortex_array:
1289212892

1289312893
pub fn vortex_array::expr::split_conjunction(&vortex_array::expr::Expression) -> alloc::vec::Vec<vortex_array::expr::Expression>
1289412894

12895+
pub fn vortex_array::expr::substr(vortex_array::expr::Expression, vortex_array::expr::Expression, core::option::Option<vortex_array::expr::Expression>) -> vortex_array::expr::Expression
12896+
1289512897
pub fn vortex_array::expr::zip_expr(vortex_array::expr::Expression, vortex_array::expr::Expression, vortex_array::expr::Expression) -> vortex_array::expr::Expression
1289612898

1289712899
pub type vortex_array::expr::Annotations<'a, A> = vortex_utils::aliases::hash_map::HashMap<&'a vortex_array::expr::Expression, vortex_utils::aliases::hash_set::HashSet<A>>
@@ -17800,6 +17802,52 @@ pub fn vortex_array::scalar_fn::fns::select::Select::stat_falsification(&self, &
1780017802

1780117803
pub fn vortex_array::scalar_fn::fns::select::Select::validity(&self, &Self::Options, &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
1780217804

17805+
pub mod vortex_array::scalar_fn::fns::substring
17806+
17807+
pub struct vortex_array::scalar_fn::fns::substring::Substring
17808+
17809+
impl core::clone::Clone for vortex_array::scalar_fn::fns::substring::Substring
17810+
17811+
pub fn vortex_array::scalar_fn::fns::substring::Substring::clone(&self) -> vortex_array::scalar_fn::fns::substring::Substring
17812+
17813+
impl vortex_array::scalar_fn::ScalarFnVTable for vortex_array::scalar_fn::fns::substring::Substring
17814+
17815+
pub type vortex_array::scalar_fn::fns::substring::Substring::Options = vortex_array::scalar_fn::EmptyOptions
17816+
17817+
pub fn vortex_array::scalar_fn::fns::substring::Substring::arity(&self, &Self::Options) -> vortex_array::scalar_fn::Arity
17818+
17819+
pub fn vortex_array::scalar_fn::fns::substring::Substring::child_name(&self, &Self::Options, usize) -> vortex_array::scalar_fn::ChildName
17820+
17821+
pub fn vortex_array::scalar_fn::fns::substring::Substring::coerce_args(&self, &Self::Options, &[vortex_array::dtype::DType]) -> vortex_error::VortexResult<alloc::vec::Vec<vortex_array::dtype::DType>>
17822+
17823+
pub fn vortex_array::scalar_fn::fns::substring::Substring::deserialize(&self, &[u8], &vortex_session::VortexSession) -> vortex_error::VortexResult<Self::Options>
17824+
17825+
pub fn vortex_array::scalar_fn::fns::substring::Substring::execute(&self, &Self::Options, &dyn vortex_array::scalar_fn::ExecutionArgs, &mut vortex_array::ExecutionCtx) -> vortex_error::VortexResult<vortex_array::ArrayRef>
17826+
17827+
pub fn vortex_array::scalar_fn::fns::substring::Substring::fmt_sql(&self, &Self::Options, &vortex_array::expr::Expression, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
17828+
17829+
pub fn vortex_array::scalar_fn::fns::substring::Substring::id(&self) -> vortex_array::scalar_fn::ScalarFnId
17830+
17831+
pub fn vortex_array::scalar_fn::fns::substring::Substring::is_fallible(&self, &Self::Options) -> bool
17832+
17833+
pub fn vortex_array::scalar_fn::fns::substring::Substring::is_null_sensitive(&self, &Self::Options) -> bool
17834+
17835+
pub fn vortex_array::scalar_fn::fns::substring::Substring::reduce(&self, &Self::Options, &dyn vortex_array::scalar_fn::ReduceNode, &dyn vortex_array::scalar_fn::ReduceCtx) -> vortex_error::VortexResult<core::option::Option<vortex_array::scalar_fn::ReduceNodeRef>>
17836+
17837+
pub fn vortex_array::scalar_fn::fns::substring::Substring::return_dtype(&self, &Self::Options, &[vortex_array::dtype::DType]) -> vortex_error::VortexResult<vortex_array::dtype::DType>
17838+
17839+
pub fn vortex_array::scalar_fn::fns::substring::Substring::serialize(&self, &Self::Options) -> vortex_error::VortexResult<core::option::Option<alloc::vec::Vec<u8>>>
17840+
17841+
pub fn vortex_array::scalar_fn::fns::substring::Substring::simplify(&self, &Self::Options, &vortex_array::expr::Expression, &dyn vortex_array::scalar_fn::SimplifyCtx) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
17842+
17843+
pub fn vortex_array::scalar_fn::fns::substring::Substring::simplify_untyped(&self, &Self::Options, &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
17844+
17845+
pub fn vortex_array::scalar_fn::fns::substring::Substring::stat_expression(&self, &Self::Options, &vortex_array::expr::Expression, vortex_array::expr::stats::Stat, &dyn vortex_array::expr::pruning::StatsCatalog) -> core::option::Option<vortex_array::expr::Expression>
17846+
17847+
pub fn vortex_array::scalar_fn::fns::substring::Substring::stat_falsification(&self, &Self::Options, &vortex_array::expr::Expression, &dyn vortex_array::expr::pruning::StatsCatalog) -> core::option::Option<vortex_array::expr::Expression>
17848+
17849+
pub fn vortex_array::scalar_fn::fns::substring::Substring::validity(&self, &Self::Options, &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
17850+
1780317851
pub mod vortex_array::scalar_fn::fns::zip
1780417852

1780517853
pub struct vortex_array::scalar_fn::fns::zip::Zip
@@ -19066,6 +19114,44 @@ pub fn vortex_array::scalar_fn::fns::select::Select::stat_falsification(&self, &
1906619114

1906719115
pub fn vortex_array::scalar_fn::fns::select::Select::validity(&self, &Self::Options, &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
1906819116

19117+
impl vortex_array::scalar_fn::ScalarFnVTable for vortex_array::scalar_fn::fns::substring::Substring
19118+
19119+
pub type vortex_array::scalar_fn::fns::substring::Substring::Options = vortex_array::scalar_fn::EmptyOptions
19120+
19121+
pub fn vortex_array::scalar_fn::fns::substring::Substring::arity(&self, &Self::Options) -> vortex_array::scalar_fn::Arity
19122+
19123+
pub fn vortex_array::scalar_fn::fns::substring::Substring::child_name(&self, &Self::Options, usize) -> vortex_array::scalar_fn::ChildName
19124+
19125+
pub fn vortex_array::scalar_fn::fns::substring::Substring::coerce_args(&self, &Self::Options, &[vortex_array::dtype::DType]) -> vortex_error::VortexResult<alloc::vec::Vec<vortex_array::dtype::DType>>
19126+
19127+
pub fn vortex_array::scalar_fn::fns::substring::Substring::deserialize(&self, &[u8], &vortex_session::VortexSession) -> vortex_error::VortexResult<Self::Options>
19128+
19129+
pub fn vortex_array::scalar_fn::fns::substring::Substring::execute(&self, &Self::Options, &dyn vortex_array::scalar_fn::ExecutionArgs, &mut vortex_array::ExecutionCtx) -> vortex_error::VortexResult<vortex_array::ArrayRef>
19130+
19131+
pub fn vortex_array::scalar_fn::fns::substring::Substring::fmt_sql(&self, &Self::Options, &vortex_array::expr::Expression, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
19132+
19133+
pub fn vortex_array::scalar_fn::fns::substring::Substring::id(&self) -> vortex_array::scalar_fn::ScalarFnId
19134+
19135+
pub fn vortex_array::scalar_fn::fns::substring::Substring::is_fallible(&self, &Self::Options) -> bool
19136+
19137+
pub fn vortex_array::scalar_fn::fns::substring::Substring::is_null_sensitive(&self, &Self::Options) -> bool
19138+
19139+
pub fn vortex_array::scalar_fn::fns::substring::Substring::reduce(&self, &Self::Options, &dyn vortex_array::scalar_fn::ReduceNode, &dyn vortex_array::scalar_fn::ReduceCtx) -> vortex_error::VortexResult<core::option::Option<vortex_array::scalar_fn::ReduceNodeRef>>
19140+
19141+
pub fn vortex_array::scalar_fn::fns::substring::Substring::return_dtype(&self, &Self::Options, &[vortex_array::dtype::DType]) -> vortex_error::VortexResult<vortex_array::dtype::DType>
19142+
19143+
pub fn vortex_array::scalar_fn::fns::substring::Substring::serialize(&self, &Self::Options) -> vortex_error::VortexResult<core::option::Option<alloc::vec::Vec<u8>>>
19144+
19145+
pub fn vortex_array::scalar_fn::fns::substring::Substring::simplify(&self, &Self::Options, &vortex_array::expr::Expression, &dyn vortex_array::scalar_fn::SimplifyCtx) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
19146+
19147+
pub fn vortex_array::scalar_fn::fns::substring::Substring::simplify_untyped(&self, &Self::Options, &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
19148+
19149+
pub fn vortex_array::scalar_fn::fns::substring::Substring::stat_expression(&self, &Self::Options, &vortex_array::expr::Expression, vortex_array::expr::stats::Stat, &dyn vortex_array::expr::pruning::StatsCatalog) -> core::option::Option<vortex_array::expr::Expression>
19150+
19151+
pub fn vortex_array::scalar_fn::fns::substring::Substring::stat_falsification(&self, &Self::Options, &vortex_array::expr::Expression, &dyn vortex_array::expr::pruning::StatsCatalog) -> core::option::Option<vortex_array::expr::Expression>
19152+
19153+
pub fn vortex_array::scalar_fn::fns::substring::Substring::validity(&self, &Self::Options, &vortex_array::expr::Expression) -> vortex_error::VortexResult<core::option::Option<vortex_array::expr::Expression>>
19154+
1906919155
impl vortex_array::scalar_fn::ScalarFnVTable for vortex_array::scalar_fn::fns::zip::Zip
1907019156

1907119157
pub type vortex_array::scalar_fn::fns::zip::Zip::Options = vortex_array::scalar_fn::EmptyOptions

vortex-array/src/expr/exprs.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use crate::scalar_fn::fns::pack::PackOptions;
4646
use crate::scalar_fn::fns::root::Root;
4747
use crate::scalar_fn::fns::select::FieldSelection;
4848
use crate::scalar_fn::fns::select::Select;
49+
use crate::scalar_fn::fns::substring::Substring;
4950
use crate::scalar_fn::fns::zip::Zip;
5051

5152
// ---- Root ----
@@ -625,6 +626,15 @@ pub fn mask(array: Expression, mask: Expression) -> Expression {
625626
Mask.new_expr(EmptyOptions, [array, mask])
626627
}
627628

629+
/// Creates a SQL SUBSTR(string, start [, length]) expression. "start" is 1-based.
630+
pub fn substr(string: Expression, start: Expression, length: Option<Expression>) -> Expression {
631+
if let Some(len) = length {
632+
Substring.new_expr(EmptyOptions, [string, start, len])
633+
} else {
634+
Substring.new_expr(EmptyOptions, [string, start])
635+
}
636+
}
637+
628638
// ---- Merge ----
629639

630640
/// Creates an expression that merges struct expressions into a single struct.

vortex-array/src/scalar_fn/fns/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ pub mod operators;
2020
pub mod pack;
2121
pub mod root;
2222
pub mod select;
23+
pub mod substring;
2324
pub mod zip;
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::fmt::Formatter;
5+
6+
use arrow_schema::DataType;
7+
use vortex_error::VortexResult;
8+
use vortex_error::vortex_bail;
9+
use vortex_error::vortex_ensure;
10+
use vortex_error::vortex_err;
11+
use vortex_session::VortexSession;
12+
13+
use crate::ArrayRef;
14+
use crate::ExecutionCtx;
15+
use crate::arrow::ArrowArrayExecutor;
16+
use crate::arrow::from_arrow_array_with_len;
17+
use crate::dtype::DType;
18+
use crate::expr::Expression;
19+
use crate::expr::and;
20+
use crate::scalar_fn::Arity;
21+
use crate::scalar_fn::ChildName;
22+
use crate::scalar_fn::EmptyOptions;
23+
use crate::scalar_fn::ExecutionArgs;
24+
use crate::scalar_fn::ScalarFnId;
25+
use crate::scalar_fn::ScalarFnVTable;
26+
27+
/// SQL SUBSTRING / SUBSTR expression.
28+
#[derive(Clone)]
29+
pub struct Substring;
30+
31+
impl ScalarFnVTable for Substring {
32+
type Options = EmptyOptions;
33+
34+
fn id(&self) -> ScalarFnId {
35+
ScalarFnId::new("vortex.substring")
36+
}
37+
38+
fn serialize(&self, _instance: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
39+
Ok(Some(vec![]))
40+
}
41+
42+
fn deserialize(
43+
&self,
44+
_metadata: &[u8],
45+
_session: &VortexSession,
46+
) -> VortexResult<Self::Options> {
47+
Ok(EmptyOptions)
48+
}
49+
50+
fn arity(&self, _options: &Self::Options) -> Arity {
51+
Arity::Variadic {
52+
min: 2,
53+
max: Some(3),
54+
}
55+
}
56+
57+
fn child_name(&self, _instance: &Self::Options, child_idx: usize) -> ChildName {
58+
match child_idx {
59+
0 => ChildName::from("string"),
60+
1 => ChildName::from("start"),
61+
2 => ChildName::from("length"),
62+
_ => unreachable!("Invalid child index {child_idx} for Substring expression"),
63+
}
64+
}
65+
66+
fn fmt_sql(
67+
&self,
68+
_options: &Self::Options,
69+
expr: &Expression,
70+
f: &mut Formatter<'_>,
71+
) -> std::fmt::Result {
72+
write!(f, "substr(")?;
73+
expr.child(0).fmt_sql(f)?;
74+
write!(f, ", ")?;
75+
expr.child(1).fmt_sql(f)?;
76+
if expr.children().len() > 2 {
77+
write!(f, ", ")?;
78+
expr.child(2).fmt_sql(f)?;
79+
}
80+
write!(f, ")")
81+
}
82+
83+
fn return_dtype(&self, _options: &Self::Options, arg_dtypes: &[DType]) -> VortexResult<DType> {
84+
let input = &arg_dtypes[0];
85+
vortex_ensure!(input.is_utf8(), "Substring: expected UTF8 input");
86+
let nullable = arg_dtypes.iter().any(|dt| dt.is_nullable());
87+
Ok(DType::Utf8(nullable.into()))
88+
}
89+
90+
fn execute(
91+
&self,
92+
_options: &Self::Options,
93+
args: &dyn ExecutionArgs,
94+
ctx: &mut ExecutionCtx,
95+
) -> VortexResult<ArrayRef> {
96+
let string_arr = args.get(0)?;
97+
let start_arr = args.get(1)?;
98+
let len = args.row_count();
99+
100+
let start = start_arr
101+
.as_constant()
102+
.ok_or_else(|| vortex_err!("Substring: start must be a constant"))?
103+
.as_primitive_opt()
104+
.ok_or_else(|| vortex_err!("Substring: start must be a primitive integer"))?
105+
.as_::<i64>()
106+
.ok_or_else(|| vortex_err!("Substring: start must be non-null"))?;
107+
vortex_ensure!(start >= 1, "Substring: start must be >0, got {start}");
108+
109+
let length = if args.num_inputs() > 2 {
110+
let length = args
111+
.get(2)?
112+
.as_constant()
113+
.ok_or_else(|| vortex_err!("Substring: length must be a constant"))?
114+
.as_primitive_opt()
115+
.ok_or_else(|| vortex_err!("Substring: length must be a primitive integer"))?
116+
.as_::<u64>()
117+
.ok_or_else(|| vortex_err!("Substring: length must be non-null"))?;
118+
vortex_ensure!(
119+
length > 0,
120+
"Substring: length must be non-negative, got {length}"
121+
);
122+
Some(length)
123+
} else {
124+
None
125+
};
126+
127+
let nullable = string_arr.dtype().is_nullable();
128+
// Execute string array to Arrow as Utf8: arrow_string::substring does
129+
// not support Utf8View.
130+
let arrow_array = string_arr.execute_arrow(Some(&DataType::Utf8), ctx)?;
131+
let result = arrow_string::substring::substring(arrow_array.as_ref(), start - 1, length)?;
132+
from_arrow_array_with_len(result.as_ref(), len, nullable)
133+
}
134+
135+
fn validity(
136+
&self,
137+
_options: &Self::Options,
138+
expression: &Expression,
139+
) -> VortexResult<Option<Expression>> {
140+
let string_validity = expression.child(0).validity()?;
141+
let start_validity = expression.child(1).validity()?;
142+
let combined = and(string_validity, start_validity);
143+
if expression.children().len() > 2 {
144+
let length_validity = expression.child(2).validity()?;
145+
Ok(Some(and(combined, length_validity)))
146+
} else {
147+
Ok(Some(combined))
148+
}
149+
}
150+
151+
fn is_null_sensitive(&self, _instance: &Self::Options) -> bool {
152+
false
153+
}
154+
155+
fn is_fallible(&self, _options: &Self::Options) -> bool {
156+
false
157+
}
158+
}
159+
160+
#[cfg(test)]
161+
mod tests {
162+
use std::sync::LazyLock;
163+
164+
use vortex_error::VortexResult;
165+
use vortex_session::VortexSession;
166+
167+
use crate::IntoArray;
168+
use crate::VortexSessionExecute;
169+
use crate::arrays::VarBinViewArray;
170+
use crate::assert_arrays_eq;
171+
use crate::expr::lit;
172+
use crate::expr::root;
173+
use crate::expr::substr;
174+
175+
static SESSION: LazyLock<VortexSession> = LazyLock::new(|| VortexSession::empty());
176+
177+
#[test]
178+
fn test_display() {
179+
let expr = substr(root(), lit(1i64), None);
180+
assert_eq!(expr.to_string(), "substr($, 1i64)");
181+
182+
let expr = substr(root(), lit(1i64), Some(lit(3i64)));
183+
assert_eq!(expr.to_string(), "substr($, 1i64, 3i64)");
184+
}
185+
186+
#[test]
187+
fn test_start() -> VortexResult<()> {
188+
let arr = VarBinViewArray::from_iter_str(["hello", "world"]).into_array();
189+
let result = arr
190+
.apply(&substr(root(), lit(2i64), None))?
191+
.execute::<VarBinViewArray>(&mut SESSION.create_execution_ctx())?;
192+
assert_arrays_eq!(result, VarBinViewArray::from_iter_str(["ello", "orld"]));
193+
Ok(())
194+
}
195+
196+
#[test]
197+
fn test_start_length() -> VortexResult<()> {
198+
let arr = VarBinViewArray::from_iter_str(["hello", "world"]).into_array();
199+
let result = arr
200+
.apply(&substr(root(), lit(2i64), Some(lit(3i64))))?
201+
.execute::<VarBinViewArray>(&mut SESSION.create_execution_ctx())?;
202+
assert_arrays_eq!(result, VarBinViewArray::from_iter_str(["ell", "orl"]));
203+
Ok(())
204+
}
205+
}

0 commit comments

Comments
 (0)