vortex_array/expr/
proto.rs1use itertools::Itertools;
5use vortex_error::VortexResult;
6use vortex_error::vortex_err;
7use vortex_proto::expr as pb;
8use vortex_session::VortexSession;
9
10use crate::expr::Expression;
11use crate::scalar_fn::ForeignScalarFnVTable;
12use crate::scalar_fn::ScalarFnId;
13use crate::scalar_fn::session::ScalarFnSessionExt;
14
15pub trait ExprSerializeProtoExt {
16 fn serialize_proto(&self) -> VortexResult<pb::Expr>;
18}
19
20impl ExprSerializeProtoExt for Expression {
21 fn serialize_proto(&self) -> VortexResult<pb::Expr> {
22 let children = self
23 .children()
24 .iter()
25 .map(|child| child.serialize_proto())
26 .try_collect()?;
27
28 let metadata = self.options().serialize()?.ok_or_else(|| {
29 vortex_err!("Expression '{}' is not serializable: {}", self.id(), self)
30 })?;
31
32 Ok(pb::Expr {
33 id: self.id().to_string(),
34 children,
35 metadata: Some(metadata),
36 })
37 }
38}
39
40impl Expression {
41 pub fn from_proto(expr: &pb::Expr, session: &VortexSession) -> VortexResult<Expression> {
42 #[expect(clippy::disallowed_methods, reason = "interning a dynamic id")]
43 let expr_id = ScalarFnId::new(expr.id.as_str());
44 let children = expr
45 .children
46 .iter()
47 .map(|e| Expression::from_proto(e, session))
48 .collect::<VortexResult<Vec<_>>>()?;
49
50 let scalar_fn = if let Some(vtable) = session.scalar_fns().registry().find(&expr_id) {
51 vtable.deserialize(expr.metadata(), session)?
52 } else if session.allows_unknown() {
53 ForeignScalarFnVTable::make_scalar_fn(expr_id, expr.metadata().to_vec(), children.len())
54 } else {
55 return Err(vortex_err!("unknown expression id: {}", expr_id));
56 };
57
58 Expression::try_new(scalar_fn, children)
59 }
60}
61
62#[deprecated(note = "Use Expression::from_proto instead")]
64pub fn deserialize_expr_proto(
65 expr: &pb::Expr,
66 session: &VortexSession,
67) -> VortexResult<Expression> {
68 Expression::from_proto(expr, session)
69}
70
71#[cfg(test)]
72mod tests {
73 use prost::Message;
74 use vortex_proto::expr as pb;
75 use vortex_session::VortexSession;
76
77 use super::ExprSerializeProtoExt;
78 use crate::array_session;
79 use crate::expr::Expression;
80 use crate::expr::and;
81 use crate::expr::between;
82 use crate::expr::eq;
83 use crate::expr::get_item;
84 use crate::expr::lit;
85 use crate::expr::or;
86 use crate::expr::root;
87 use crate::scalar_fn::fns::between::BetweenOptions;
88 use crate::scalar_fn::fns::between::StrictComparison;
89 use crate::scalar_fn::session::ScalarFnSession;
90
91 #[test]
92 fn expression_serde() {
93 let expr: Expression = or(
94 and(
95 between(
96 lit(1),
97 root(),
98 get_item("a", root()),
99 BetweenOptions {
100 lower_strict: StrictComparison::Strict,
101 upper_strict: StrictComparison::Strict,
102 },
103 ),
104 lit(1),
105 ),
106 eq(lit(1), root()),
107 );
108
109 let s_expr = expr.serialize_proto().unwrap();
110 let buf = s_expr.encode_to_vec();
111 let s_expr = pb::Expr::decode(buf.as_slice()).unwrap();
112 let deser_expr = Expression::from_proto(&s_expr, &array_session()).unwrap();
113
114 assert_eq!(&deser_expr, &expr);
115 }
116
117 #[test]
118 fn unknown_expression_id_allow_unknown() {
119 let session = VortexSession::empty()
120 .with::<ScalarFnSession>()
121 .allow_unknown();
122
123 let expr_proto = pb::Expr {
124 id: "vortex.test.foreign_scalar_fn".to_string(),
125 metadata: Some(vec![1, 2, 3, 4]),
126 children: vec![root().serialize_proto().unwrap()],
127 };
128
129 let expr = Expression::from_proto(&expr_proto, &session).unwrap();
130 assert_eq!(expr.id().as_ref(), "vortex.test.foreign_scalar_fn");
131
132 let roundtrip = expr.serialize_proto().unwrap();
133 assert_eq!(roundtrip.id, expr_proto.id);
134 assert_eq!(roundtrip.metadata(), expr_proto.metadata());
135 assert_eq!(roundtrip.children.len(), 1);
136 }
137}