-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathvariant_object_construct.rs
More file actions
152 lines (121 loc) · 4.56 KB
/
Copy pathvariant_object_construct.rs
File metadata and controls
152 lines (121 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::sync::Arc;
use arrow::array::StructArray;
use arrow_schema::{DataType, Field, Fields};
use datafusion::{
common::exec_datafusion_err,
error::{DataFusionError, Result},
logical_expr::{
ColumnarValue, ReturnFieldArgs, ScalarUDFImpl, Signature, TypeSignature, Volatility,
},
scalar::ScalarValue,
};
use parquet_variant::{Variant, VariantBuilder};
use parquet_variant_compute::{VariantArray, VariantType};
use crate::shared::{ensure, try_parse_string_scalar, try_parse_variant_scalar};
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct VariantObjectConstruct {
signature: Signature,
}
impl Default for VariantObjectConstruct {
fn default() -> Self {
Self {
signature: Signature::new(TypeSignature::VariadicAny, Volatility::Immutable),
}
}
}
impl ScalarUDFImpl for VariantObjectConstruct {
fn name(&self) -> &str {
"variant_object_construct"
}
fn signature(&self) -> &Signature {
&self.signature
}
fn return_type(&self, _arg_types: &[arrow_schema::DataType]) -> Result<arrow_schema::DataType> {
Err(DataFusionError::Internal(
"implemented return_field_from_args instead".into(),
))
}
fn return_field_from_args(&self, _args: ReturnFieldArgs) -> Result<Arc<Field>> {
let data_type = DataType::Struct(Fields::from(vec![
Field::new("metadata", DataType::BinaryView, false),
Field::new("value", DataType::BinaryView, false),
]));
Ok(Arc::new(
Field::new(self.name(), data_type, true).with_extension_type(VariantType),
))
}
fn invoke_with_args(
&self,
args: datafusion::logical_expr::ScalarFunctionArgs,
) -> Result<ColumnarValue> {
// validate arguments
let argument_fields = args.arg_fields;
let argument_values = args.args;
ensure(
argument_fields.len() == argument_values.len(),
"argument fields and values must be of same length",
)?;
ensure(
argument_fields.len() & 1 == 0,
"list of arguments must be (key, value) pair",
)?;
let (_key_fields, value_fields): (Vec<_>, Vec<_>) = argument_fields
.into_iter()
.enumerate()
.partition(|(i, _)| i & 1 == 0);
let all_value_fields_have_variant_ext = value_fields
.iter()
.all(|(_, v)| matches!(v.extension_type(), VariantType));
ensure(
all_value_fields_have_variant_ext,
"expected all values in (key, value) to have a Variant ext type",
)?;
// sometimes field metadata is super redundant. Why check if the key fields are String
// when you can just try to parse out the ScalarValues?
let all_arguments_scalar = argument_values
.iter()
.all(|v| matches!(v, ColumnarValue::Scalar(_)));
// for now, let's just handle the scalar case
ensure(
all_arguments_scalar,
"all arguments must be scalar, todo: how do array arguments look like?",
)?;
let (key_values, variant_values): (Vec<_>, Vec<_>) = argument_values
.into_iter()
.enumerate()
.partition(|(i, _)| i & 1 == 0);
let object_keys = key_values
.into_iter()
.map(|(_, v)| {
let ColumnarValue::Scalar(sv) = v else {
unreachable!()
};
try_parse_string_scalar(&sv).and_then(|opt| {
opt.cloned()
.ok_or_else(|| exec_datafusion_err!("expected non null string"))
})
})
.collect::<Result<Vec<_>, _>>()?;
let object_values = variant_values
.into_iter()
.map(|(_, v)| {
let ColumnarValue::Scalar(sv) = v else {
unreachable!()
};
try_parse_variant_scalar(&sv)
})
.collect::<Result<Vec<_>, _>>()?;
// note, should we have the ability to configure behavior for duplicate keys?
let mut v = VariantBuilder::new();
let mut o = v.new_object();
for (k, v) in object_keys.iter().zip(object_values) {
let v = v.value(0);
o.try_insert(k, v)?;
}
o.finish();
let (m, v) = v.finish();
let v = Variant::new(m.as_ref(), v.as_ref());
let out: StructArray = VariantArray::from_iter([v]).into();
Ok(ColumnarValue::Scalar(ScalarValue::Struct(Arc::new(out))))
}
}