1use std::borrow::Cow;
5use std::convert::TryFrom;
6use std::fmt;
7
8use prost::UnknownEnumValue;
9use substrait::proto::aggregate_function::AggregationInvocation;
10use substrait::proto::expression::RexType;
11use substrait::proto::expression::field_reference::ReferenceType as FieldReferenceType;
12use substrait::proto::expression::reference_segment::ReferenceType as SegmentReferenceType;
13use substrait::proto::sort_field::{SortDirection, SortKind};
14use substrait::proto::{
15 AggregateFunction, AggregationPhase, Expression, SortField, join_rel, set_rel,
16};
17
18use super::types::Name;
19use super::{PlanError, Scope, Textify};
20use crate::extensions::{ExtensionColumn, ExtensionValue};
21
22pub trait ValueEnum {
25 fn as_enum_str(&self) -> Result<Cow<'static, str>, PlanError>;
26}
27
28#[derive(Debug, Clone)]
29pub struct NamedArg<'a> {
30 pub name: Cow<'a, str>,
31 pub value: Value<'a>,
32}
33
34#[derive(Debug, Clone)]
35pub enum Value<'a> {
36 TableName(Vec<Name<'a>>),
37 Field(Option<Name<'a>>, Option<&'a substrait::proto::Type>),
38 Tuple(Vec<Value<'a>>),
39 Reference(i32),
40 Expression(&'a Expression),
41 AggregateFunction(&'a AggregateFunction),
42 Missing(PlanError),
44 Enum(Cow<'a, str>),
46 EmptyGroup,
47 Integer(i64),
48 ExtensionArgument(ExtensionValue),
50 ExtColumn(ExtensionColumn),
52}
53
54impl<'a> Value<'a> {
55 pub fn expect(maybe_value: Option<Self>, f: impl FnOnce() -> PlanError) -> Self {
56 match maybe_value {
57 Some(s) => s,
58 None => Value::Missing(f()),
59 }
60 }
61}
62
63impl<'a> From<Result<Vec<Name<'a>>, PlanError>> for Value<'a> {
64 fn from(token: Result<Vec<Name<'a>>, PlanError>) -> Self {
65 match token {
66 Ok(value) => Value::TableName(value),
67 Err(err) => Value::Missing(err),
68 }
69 }
70}
71
72impl<'a> Textify for Value<'a> {
73 fn name() -> &'static str {
74 "Value"
75 }
76
77 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
78 match self {
79 Value::TableName(names) => write!(w, "{}", ctx.separated(names, ".")),
80 Value::Field(name, typ) => {
81 write!(w, "{}:{}", ctx.expect(name.as_ref()), ctx.expect(*typ))
82 }
83 Value::Tuple(values) => write!(w, "({})", ctx.separated(values, ", ")),
84 Value::Reference(i) => write!(w, "${i}"),
86 Value::Expression(e) => write!(w, "{}", ctx.display(*e)),
87 Value::AggregateFunction(agg_fn) => agg_fn.textify(ctx, w),
88 Value::Missing(err) => write!(w, "{}", ctx.failure(err.clone())),
89 Value::Enum(res) => write!(w, "&{res}"),
90 Value::Integer(i) => write!(w, "{i}"),
91 Value::EmptyGroup => write!(w, "_"),
92 Value::ExtensionArgument(ev) => ev.textify(ctx, w),
93 Value::ExtColumn(ec) => ec.textify(ctx, w),
94 }
95 }
96}
97
98#[derive(Debug, Clone, Default)]
101pub struct Arguments<'a> {
102 pub positional: Vec<Value<'a>>,
104 pub named: Vec<NamedArg<'a>>,
106}
107
108impl<'a> Arguments<'a> {
109 pub fn new(positional: Vec<Value<'a>>, named: Vec<NamedArg<'a>>) -> Self {
110 Arguments { positional, named }
111 }
112}
113
114impl<'a> Textify for Arguments<'a> {
115 fn name() -> &'static str {
116 "Arguments"
117 }
118 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
119 if self.positional.is_empty() && self.named.is_empty() {
120 return write!(w, "_");
121 }
122
123 write!(w, "{}", ctx.separated(self.positional.iter(), ", "))?;
124 if !self.positional.is_empty() && !self.named.is_empty() {
125 write!(w, ", ")?;
126 }
127 write!(w, "{}", ctx.separated(self.named.iter(), ", "))
128 }
129}
130
131impl<'a> From<&'a SortField> for Value<'a> {
132 fn from(sf: &'a SortField) -> Self {
133 let field = match &sf.expr {
134 Some(expr) => match &expr.rex_type {
135 Some(RexType::Selection(fref)) => {
136 if let Some(FieldReferenceType::DirectReference(seg)) = &fref.reference_type {
137 if let Some(SegmentReferenceType::StructField(sf)) = &seg.reference_type {
138 Value::Reference(sf.field)
139 } else {
140 Value::Missing(PlanError::unimplemented(
141 "SortField",
142 Some("expr"),
143 "Not a struct field",
144 ))
145 }
146 } else {
147 Value::Missing(PlanError::unimplemented(
148 "SortField",
149 Some("expr"),
150 "Not a direct reference",
151 ))
152 }
153 }
154 _ => Value::Missing(PlanError::unimplemented(
155 "SortField",
156 Some("expr"),
157 "Not a selection",
158 )),
159 },
160 None => Value::Missing(PlanError::unimplemented(
161 "SortField",
162 Some("expr"),
163 "Missing expr",
164 )),
165 };
166 let direction = match &sf.sort_kind {
167 Some(kind) => Value::from(kind),
168 None => Value::Missing(PlanError::invalid(
169 "SortKind",
170 Some(Cow::Borrowed("sort_kind")),
171 "Missing sort_kind",
172 )),
173 };
174 Value::Tuple(vec![field, direction])
175 }
176}
177
178pub(crate) fn enum_str_value<'a>(result: Result<Cow<'static, str>, PlanError>) -> Value<'a> {
182 match result {
183 Ok(s) => Value::Enum(s),
184 Err(e) => Value::Missing(e),
185 }
186}
187
188pub(crate) fn decode_enum_field<'a, T>(
198 raw: i32,
199 message: &'static str,
200 field: &'static str,
201) -> Value<'a>
202where
203 T: TryFrom<i32> + ValueEnum,
204{
205 match T::try_from(raw) {
206 Ok(v) => enum_str_value(v.as_enum_str()),
207 Err(_) => Value::Missing(PlanError::invalid(
208 message,
209 Some(field),
210 format!("Unknown {message}.{field}: {raw}"),
211 )),
212 }
213}
214
215impl<'a, T: ValueEnum + ?Sized> From<&'a T> for Value<'a> {
216 fn from(enum_val: &'a T) -> Self {
217 enum_str_value(enum_val.as_enum_str())
218 }
219}
220
221impl ValueEnum for SortKind {
222 fn as_enum_str(&self) -> Result<Cow<'static, str>, PlanError> {
223 let d = match self {
224 &SortKind::Direction(d) => SortDirection::try_from(d),
225 SortKind::ComparisonFunctionReference(f) => {
226 return Err(PlanError::invalid(
227 "SortKind",
228 Some(Cow::Owned(format!("function reference{f}"))),
229 "SortKind::ComparisonFunctionReference unimplemented",
230 ));
231 }
232 };
233 let s = match d {
234 Err(UnknownEnumValue(d)) => {
235 return Err(PlanError::invalid(
236 "SortKind",
237 Some(Cow::Owned(format!("unknown variant: {d:?}"))),
238 "Unknown SortDirection",
239 ));
240 }
241 Ok(SortDirection::AscNullsFirst) => "AscNullsFirst",
242 Ok(SortDirection::AscNullsLast) => "AscNullsLast",
243 Ok(SortDirection::DescNullsFirst) => "DescNullsFirst",
244 Ok(SortDirection::DescNullsLast) => "DescNullsLast",
245 Ok(SortDirection::Clustered) => "Clustered",
246 Ok(SortDirection::Unspecified) => {
247 return Err(PlanError::invalid(
248 "SortKind",
249 Option::<Cow<str>>::None,
250 "Unspecified SortDirection",
251 ));
252 }
253 };
254 Ok(Cow::Borrowed(s))
255 }
256}
257
258impl ValueEnum for join_rel::JoinType {
259 fn as_enum_str(&self) -> Result<Cow<'static, str>, PlanError> {
260 let s = match self {
261 join_rel::JoinType::Unspecified => {
262 return Err(PlanError::invalid(
263 "JoinType",
264 Option::<Cow<str>>::None,
265 "Unspecified JoinType",
266 ));
267 }
268 join_rel::JoinType::Inner => "Inner",
269 join_rel::JoinType::Outer => "Outer",
270 join_rel::JoinType::Left => "Left",
271 join_rel::JoinType::Right => "Right",
272 join_rel::JoinType::LeftSemi => "LeftSemi",
273 join_rel::JoinType::RightSemi => "RightSemi",
274 join_rel::JoinType::LeftAnti => "LeftAnti",
275 join_rel::JoinType::RightAnti => "RightAnti",
276 join_rel::JoinType::LeftSingle => "LeftSingle",
277 join_rel::JoinType::RightSingle => "RightSingle",
278 join_rel::JoinType::LeftMark => "LeftMark",
279 join_rel::JoinType::RightMark => "RightMark",
280 };
281 Ok(Cow::Borrowed(s))
282 }
283}
284
285impl ValueEnum for set_rel::SetOp {
286 fn as_enum_str(&self) -> Result<Cow<'static, str>, PlanError> {
287 let s = match self {
288 set_rel::SetOp::Unspecified => {
289 return Err(PlanError::invalid(
290 "SetOp",
291 Option::<Cow<str>>::None,
292 "Unspecified SetOp",
293 ));
294 }
295 set_rel::SetOp::MinusPrimary => "MinusPrimary",
296 set_rel::SetOp::MinusPrimaryAll => "MinusPrimaryAll",
297 set_rel::SetOp::MinusMultiset => "MinusMultiset",
298 set_rel::SetOp::IntersectionPrimary => "IntersectionPrimary",
299 set_rel::SetOp::IntersectionMultiset => "IntersectionMultiset",
300 set_rel::SetOp::IntersectionMultisetAll => "IntersectionMultisetAll",
301 set_rel::SetOp::UnionDistinct => "UnionDistinct",
302 set_rel::SetOp::UnionAll => "UnionAll",
303 };
304 Ok(Cow::Borrowed(s))
305 }
306}
307
308impl ValueEnum for AggregationPhase {
309 fn as_enum_str(&self) -> Result<Cow<'static, str>, PlanError> {
310 let s = match self {
311 AggregationPhase::Unspecified => "Unspecified",
312 AggregationPhase::InitialToIntermediate => "InitialToIntermediate",
313 AggregationPhase::IntermediateToIntermediate => "IntermediateToIntermediate",
314 AggregationPhase::InitialToResult => "InitialToResult",
315 AggregationPhase::IntermediateToResult => "IntermediateToResult",
316 };
317 Ok(Cow::Borrowed(s))
318 }
319}
320
321impl ValueEnum for AggregationInvocation {
322 fn as_enum_str(&self) -> Result<Cow<'static, str>, PlanError> {
323 let s = match self {
324 AggregationInvocation::Unspecified => "Unspecified",
325 AggregationInvocation::All => "All",
326 AggregationInvocation::Distinct => "Distinct",
327 };
328 Ok(Cow::Borrowed(s))
329 }
330}
331
332impl<'a> Textify for NamedArg<'a> {
333 fn name() -> &'static str {
334 "NamedArg"
335 }
336 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
337 write!(w, "{}=", self.name)?;
338 self.value.textify(ctx, w)
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345 use crate::fixtures::TestContext;
346
347 #[test]
348 fn test_arguments_textify_positional_only() {
349 let ctx = TestContext::new();
350 let args = Arguments::new(vec![Value::Integer(42), Value::Integer(7)], vec![]);
351 let (result, errors) = ctx.textify(&args);
352 assert!(errors.is_empty(), "Expected no errors, got: {errors:?}");
353 assert_eq!(result, "42, 7");
354 }
355
356 #[test]
357 fn test_arguments_textify_named_only() {
358 let ctx = TestContext::new();
359 let args = Arguments::new(
360 vec![],
361 vec![
362 NamedArg {
363 name: Cow::Borrowed("limit"),
364 value: Value::Integer(10),
365 },
366 NamedArg {
367 name: Cow::Borrowed("offset"),
368 value: Value::Integer(5),
369 },
370 ],
371 );
372 let (result, errors) = ctx.textify(&args);
373 assert!(errors.is_empty(), "Expected no errors, got: {errors:?}");
374 assert_eq!(result, "limit=10, offset=5");
375 }
376
377 #[test]
378 fn test_arguments_textify_both() {
379 let ctx = TestContext::new();
380 let args = Arguments::new(
381 vec![Value::Integer(1)],
382 vec![NamedArg {
383 name: "foo".into(),
384 value: Value::Integer(2),
385 }],
386 );
387 let (result, errors) = ctx.textify(&args);
388 assert!(errors.is_empty(), "Expected no errors, got: {errors:?}");
389 assert_eq!(result, "1, foo=2");
390 }
391
392 #[test]
393 fn test_arguments_textify_empty() {
394 let ctx = TestContext::new();
395 let args = Arguments::new(vec![], vec![]);
396 let (result, errors) = ctx.textify(&args);
397 assert!(errors.is_empty(), "Expected no errors, got: {errors:?}");
398 assert_eq!(result, "_");
399 }
400
401 #[test]
402 fn test_named_arg_textify_error_token() {
403 let ctx = TestContext::new();
404 let named_arg = NamedArg {
405 name: "foo".into(),
406 value: Value::Missing(PlanError::invalid(
407 "my_enum",
408 Some(Cow::Borrowed("my_enum")),
409 Cow::Borrowed("my_enum"),
410 )),
411 };
412 let (result, errors) = ctx.textify(&named_arg);
413 assert!(result.contains("foo=!{my_enum}"), "Output: {result}");
415 assert!(!errors.is_empty(), "Expected error for error token");
417 }
418
419 #[test]
420 fn test_decode_enum_field_known_variant() {
421 let value =
422 decode_enum_field::<set_rel::SetOp>(set_rel::SetOp::UnionAll as i32, "SetRel", "op");
423 match value {
424 Value::Enum(s) => assert_eq!(s, "UnionAll"),
425 other => panic!("Expected Value::Enum, got {other:?}"),
426 }
427 }
428
429 #[test]
430 fn test_decode_enum_field_unknown_variant_names_field() {
431 let value = decode_enum_field::<set_rel::SetOp>(99, "SetRel", "op");
432 match value {
433 Value::Missing(err) => {
434 assert_eq!(err.message, "SetRel");
435 assert_eq!(err.lookup.as_deref(), Some("op"));
436 assert_eq!(err.description, "Unknown SetRel.op: 99");
439 }
440 other => panic!("Expected Value::Missing, got {other:?}"),
441 }
442 }
443}