1use vortex_error::VortexExpect;
5use vortex_error::VortexResult;
6use vortex_error::vortex_bail;
7use vortex_error::vortex_ensure;
8use vortex_error::vortex_ensure_eq;
9
10use crate::dtype::DType;
11use crate::dtype::PType;
12use crate::scalar::PValue;
13use crate::scalar::Scalar;
14use crate::scalar::ScalarValue;
15
16impl Scalar {
17 pub fn validate(dtype: &DType, value: Option<&ScalarValue>) -> VortexResult<()> {
19 let Some(value) = value else {
20 vortex_ensure!(
21 dtype.is_nullable(),
22 "non-nullable dtype {dtype} cannot hold a null value",
23 );
24 return Ok(());
25 };
26
27 match dtype {
29 DType::Null => {
30 vortex_bail!("null dtype cannot hold a non-null value {value}");
31 }
32 DType::Bool(_) => {
33 vortex_ensure!(
34 matches!(value, ScalarValue::Bool(_)),
35 "bool dtype expected Bool value, got {value}",
36 );
37 }
38 DType::Primitive(ptype, _) => {
39 let ScalarValue::Primitive(pvalue) = value else {
40 vortex_bail!("primitive dtype {ptype} expected Primitive value, got {value}",);
41 };
42
43 let f16_backcompat_still_works =
48 matches!(ptype, &PType::F16) && matches!(pvalue, PValue::U64(_));
49
50 vortex_ensure!(
51 f16_backcompat_still_works || pvalue.ptype() == *ptype,
52 "primitive dtype {ptype} is not compatible with value {pvalue}",
53 );
54 }
55 DType::Decimal(dec_dtype, _) => {
56 let ScalarValue::Decimal(dvalue) = value else {
57 vortex_bail!("decimal dtype expected Decimal value, got {value}");
58 };
59
60 vortex_ensure!(
61 dvalue.fits_in_precision(*dec_dtype),
62 "decimal value {dvalue} does not fit in precision of {dec_dtype}",
63 );
64 }
65 DType::Utf8(_) => {
66 vortex_ensure!(
67 matches!(value, ScalarValue::Utf8(_)),
68 "utf8 dtype expected Utf8 value, got {value}",
69 );
70 }
71 DType::Binary(_) => {
72 vortex_ensure!(
73 matches!(value, ScalarValue::Binary(_)),
74 "binary dtype expected Binary value, got {value}",
75 );
76 }
77 DType::List(elem_dtype, _) => {
78 let ScalarValue::Tuple(elements) = value else {
79 vortex_bail!("list dtype expected Tuple value, got {value}");
80 };
81
82 for (i, element) in elements.iter().enumerate() {
83 Self::validate(elem_dtype.as_ref(), element.as_ref())
84 .map_err(|e| vortex_error::vortex_err!("list element at index {i}: {e}"))?;
85 }
86 }
87 DType::FixedSizeList(elem_dtype, size, _) => {
88 let ScalarValue::Tuple(elements) = value else {
89 vortex_bail!("fixed-size list dtype expected Tuple value, got {value}",);
90 };
91
92 let len = elements.len();
93 vortex_ensure_eq!(
94 len,
95 *size as usize,
96 "fixed-size list dtype expected {size} elements, got {len}",
97 );
98
99 for (i, element) in elements.iter().enumerate() {
100 Self::validate(elem_dtype.as_ref(), element.as_ref()).map_err(|e| {
101 vortex_error::vortex_err!("fixed-size list element at index {i}: {e}",)
102 })?;
103 }
104 }
105 DType::Map(map, _) => {
106 let ScalarValue::Tuple(entries) = value else {
107 vortex_bail!("map dtype expected Tuple value, got {value}");
108 };
109 let key_dtype = map.key_dtype();
110 let value_dtype = map.value_dtype();
111
112 for (index, entry) in entries.iter().enumerate() {
113 let entry = entry.as_ref().ok_or_else(|| {
114 vortex_error::vortex_err!("map entry at index {index} cannot be null")
115 })?;
116 let ScalarValue::Tuple(values) = entry else {
117 vortex_bail!(
118 "map entry at index {index} expected Tuple value, got {entry}"
119 );
120 };
121 vortex_ensure_eq!(
122 values.len(),
123 2,
124 "map entry at index {index} expected 2 values, got {}",
125 values.len(),
126 );
127
128 Self::validate(&key_dtype, values[0].as_ref()).map_err(|error| {
129 vortex_error::vortex_err!("map key at entry {index}: {error}")
130 })?;
131 Self::validate(&value_dtype, values[1].as_ref()).map_err(|error| {
132 vortex_error::vortex_err!("map value at entry {index}: {error}")
133 })?;
134 }
135 }
136 DType::Struct(fields, _) => {
137 let ScalarValue::Tuple(values) = value else {
138 vortex_bail!("struct dtype expected Tuple value, got {value}");
139 };
140
141 let nfields = fields.nfields();
142 let nvalues = values.len();
143 vortex_ensure_eq!(
144 nvalues,
145 nfields,
146 "struct dtype expected {nfields} fields, got {nvalues}",
147 );
148
149 for (field, field_value) in fields.fields().zip(values.iter()) {
150 Self::validate(&field, field_value.as_ref())?;
151 }
152 }
153 DType::Union(variants, _) => {
154 let ScalarValue::Union(union_value) = value else {
155 vortex_bail!("union dtype expected Union value, got {value}");
156 };
157
158 let type_id = union_value.type_id();
159 let Some(child_index) = variants.tag_to_child_index(type_id) else {
160 vortex_bail!(
161 "union value has unknown type ID {type_id}; expected one of {:?}",
162 variants.type_ids()
163 );
164 };
165
166 let child_dtype = variants
167 .variant_by_index(child_index)
168 .vortex_expect("resolved union child index must be valid");
169
170 Self::validate(&child_dtype, union_value.child_value()).map_err(|error| {
171 vortex_error::vortex_err!(
172 "union value for type ID {type_id} is invalid for dtype {child_dtype}: \
173 {error}"
174 )
175 })?;
176 }
177 DType::Variant(_) => {
178 let ScalarValue::Variant(inner) = value else {
179 vortex_bail!("variant dtype expected Variant value, got {value}");
180 };
181
182 Self::validate(inner.dtype(), inner.value())?;
183 vortex_ensure!(
184 !inner.is_null() || matches!(inner.dtype(), DType::Null),
185 "variant nulls must use a nested null scalar, got {}",
186 inner.dtype(),
187 );
188 }
189 DType::Extension(ext_dtype) => ext_dtype.validate_storage_value(value)?,
190 }
191
192 Ok(())
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use vortex_error::VortexResult;
199
200 use crate::dtype::DType;
201 use crate::dtype::Nullability;
202 use crate::dtype::PType;
203 use crate::dtype::UnionVariants;
204 use crate::scalar::Scalar;
205 use crate::scalar::ScalarValue;
206 use crate::scalar::UnionValue;
207
208 #[test]
209 fn union_rejects_unknown_tag_and_wrong_value() -> VortexResult<()> {
210 let variants = UnionVariants::try_new(
211 ["int", "string"].into(),
212 vec![
213 DType::Primitive(PType::I32, Nullability::Nullable),
214 DType::Utf8(Nullability::NonNullable),
215 ],
216 vec![5, 9],
217 )?;
218 let dtype = DType::Union(variants, Nullability::NonNullable);
219
220 assert!(
221 Scalar::try_new(
222 dtype.clone(),
223 Some(ScalarValue::Union(UnionValue::new(
224 7,
225 Scalar::primitive(42_i32, Nullability::Nullable).into_value(),
226 ))),
227 )
228 .is_err()
229 );
230
231 assert!(
232 Scalar::try_new(
233 dtype,
234 Some(ScalarValue::Union(UnionValue::new(
235 5,
236 Scalar::utf8("wrong", Nullability::NonNullable).into_value(),
237 ))),
238 )
239 .is_err()
240 );
241
242 Ok(())
243 }
244}