1use crate::basic::Encoding;
19use crate::bloom_filter::Sbbf;
20use crate::column::writer::encoder::{
21 ColumnValueEncoder, DataPageValues, DictionaryPage, create_bloom_filter,
22};
23use crate::data_type::{AsBytes, ByteArray, Int32Type};
24use crate::encodings::encoding::{DeltaBitPackEncoder, Encoder};
25use crate::encodings::rle::RleEncoder;
26use crate::errors::{ParquetError, Result};
27use crate::file::properties::{EnabledStatistics, WriterProperties, WriterVersion};
28use crate::geospatial::accumulator::{GeoStatsAccumulator, try_new_geo_stats_accumulator};
29use crate::geospatial::statistics::GeospatialStatistics;
30use crate::schema::types::ColumnDescPtr;
31use crate::util::bit_util::num_required_bits;
32use crate::util::interner::{Interner, Storage};
33use crate::util::prefix::common_prefix_length;
34use arrow_array::types::ByteArrayType;
35use arrow_array::{
36 Array, ArrayAccessor, BinaryArray, BinaryViewArray, DictionaryArray, FixedSizeBinaryArray,
37 GenericByteArray, LargeBinaryArray, LargeStringArray, StringArray, StringViewArray,
38};
39use arrow_buffer::{ArrowNativeType, Buffer};
40use arrow_schema::DataType;
41
42macro_rules! downcast_dict_impl {
43 ($array:ident, $key:ident, $val:ident, $op:expr $(, $arg:expr)*) => {{
44 $op($array
45 .as_any()
46 .downcast_ref::<DictionaryArray<arrow_array::types::$key>>()
47 .unwrap()
48 .downcast_dict::<$val>()
49 .unwrap()$(, $arg)*)
50 }};
51}
52
53macro_rules! downcast_dict_op {
54 ($key_type:expr, $val:ident, $array:ident, $op:expr $(, $arg:expr)*) => {
55 match $key_type.as_ref() {
56 DataType::UInt8 => downcast_dict_impl!($array, UInt8Type, $val, $op$(, $arg)*),
57 DataType::UInt16 => downcast_dict_impl!($array, UInt16Type, $val, $op$(, $arg)*),
58 DataType::UInt32 => downcast_dict_impl!($array, UInt32Type, $val, $op$(, $arg)*),
59 DataType::UInt64 => downcast_dict_impl!($array, UInt64Type, $val, $op$(, $arg)*),
60 DataType::Int8 => downcast_dict_impl!($array, Int8Type, $val, $op$(, $arg)*),
61 DataType::Int16 => downcast_dict_impl!($array, Int16Type, $val, $op$(, $arg)*),
62 DataType::Int32 => downcast_dict_impl!($array, Int32Type, $val, $op$(, $arg)*),
63 DataType::Int64 => downcast_dict_impl!($array, Int64Type, $val, $op$(, $arg)*),
64 _ => unreachable!(),
65 }
66 };
67}
68
69macro_rules! downcast_op {
70 ($data_type:expr, $array:ident, $op:expr $(, $arg:expr)*) => {
71 match $data_type {
72 DataType::Utf8 => $op($array.as_any().downcast_ref::<StringArray>().unwrap()$(, $arg)*),
73 DataType::LargeUtf8 => {
74 $op($array.as_any().downcast_ref::<LargeStringArray>().unwrap()$(, $arg)*)
75 }
76 DataType::Utf8View => $op($array.as_any().downcast_ref::<StringViewArray>().unwrap()$(, $arg)*),
77 DataType::Binary => {
78 $op($array.as_any().downcast_ref::<BinaryArray>().unwrap()$(, $arg)*)
79 }
80 DataType::LargeBinary => {
81 $op($array.as_any().downcast_ref::<LargeBinaryArray>().unwrap()$(, $arg)*)
82 }
83 DataType::BinaryView => {
84 $op($array.as_any().downcast_ref::<BinaryViewArray>().unwrap()$(, $arg)*)
85 }
86 DataType::Dictionary(key, value) => match value.as_ref() {
87 DataType::Utf8 => downcast_dict_op!(key, StringArray, $array, $op$(, $arg)*),
88 DataType::LargeUtf8 => {
89 downcast_dict_op!(key, LargeStringArray, $array, $op$(, $arg)*)
90 }
91 DataType::Binary => downcast_dict_op!(key, BinaryArray, $array, $op$(, $arg)*),
92 DataType::LargeBinary => {
93 downcast_dict_op!(key, LargeBinaryArray, $array, $op$(, $arg)*)
94 }
95 DataType::FixedSizeBinary(_) => {
96 downcast_dict_op!(key, FixedSizeBinaryArray, $array, $op$(, $arg)*)
97 }
98 d => unreachable!("cannot downcast {} dictionary value to byte array", d),
99 },
100 d => unreachable!("cannot downcast {} to byte array", d),
101 }
102 };
103}
104
105struct FallbackEncoder {
107 encoder: FallbackEncoderImpl,
108 num_values: usize,
109 variable_length_bytes: i64,
110}
111
112enum FallbackEncoderImpl {
116 Plain {
117 buffer: Vec<u8>,
118 },
119 DeltaLength {
120 buffer: Vec<u8>,
121 lengths: Box<DeltaBitPackEncoder<Int32Type>>,
122 },
123 Delta {
124 buffer: Vec<u8>,
125 last_value: Vec<u8>,
126 prefix_lengths: Box<DeltaBitPackEncoder<Int32Type>>,
127 suffix_lengths: Box<DeltaBitPackEncoder<Int32Type>>,
128 },
129}
130
131impl FallbackEncoder {
132 fn new(descr: &ColumnDescPtr, props: &WriterProperties) -> Result<Self> {
134 let encoding =
136 props
137 .encoding(descr.path())
138 .unwrap_or_else(|| match props.writer_version() {
139 WriterVersion::PARQUET_1_0 => Encoding::PLAIN,
140 WriterVersion::PARQUET_2_0 => Encoding::DELTA_BYTE_ARRAY,
141 });
142
143 let encoder = match encoding {
144 Encoding::PLAIN => FallbackEncoderImpl::Plain { buffer: vec![] },
145 Encoding::DELTA_LENGTH_BYTE_ARRAY => FallbackEncoderImpl::DeltaLength {
146 buffer: vec![],
147 lengths: Box::new(DeltaBitPackEncoder::new()),
148 },
149 Encoding::DELTA_BYTE_ARRAY => FallbackEncoderImpl::Delta {
150 buffer: vec![],
151 last_value: vec![],
152 prefix_lengths: Box::new(DeltaBitPackEncoder::new()),
153 suffix_lengths: Box::new(DeltaBitPackEncoder::new()),
154 },
155 _ => {
156 return Err(general_err!(
157 "unsupported encoding {} for byte array",
158 encoding
159 ));
160 }
161 };
162
163 Ok(Self {
164 encoder,
165 num_values: 0,
166 variable_length_bytes: 0,
167 })
168 }
169
170 fn encode<T>(&mut self, values: T, indices: impl ExactSizeIterator<Item = usize>)
172 where
173 T: ArrayAccessor + Copy,
174 T::Item: AsRef<[u8]>,
175 {
176 self.num_values += indices.len();
177 match &mut self.encoder {
178 FallbackEncoderImpl::Plain { buffer } => {
179 for idx in indices {
180 let value = values.value(idx);
181 let value = value.as_ref();
182 buffer.extend_from_slice((value.len() as u32).as_bytes());
183 buffer.extend_from_slice(value);
184 self.variable_length_bytes += value.len() as i64;
185 }
186 }
187 FallbackEncoderImpl::DeltaLength { buffer, lengths } => {
188 for idx in indices {
189 let value = values.value(idx);
190 let value = value.as_ref();
191 lengths.put(&[value.len() as i32]).unwrap();
192 buffer.extend_from_slice(value);
193 self.variable_length_bytes += value.len() as i64;
194 }
195 }
196 FallbackEncoderImpl::Delta {
197 buffer,
198 last_value,
199 prefix_lengths,
200 suffix_lengths,
201 } => {
202 for idx in indices {
203 let value = values.value(idx);
204 let value = value.as_ref();
205
206 let prefix_length = common_prefix_length(last_value, value);
207 let suffix_length = value.len() - prefix_length;
208
209 last_value.clear();
210 last_value.extend_from_slice(value);
211
212 buffer.extend_from_slice(&value[prefix_length..]);
213 prefix_lengths.put(&[prefix_length as i32]).unwrap();
214 suffix_lengths.put(&[suffix_length as i32]).unwrap();
215 self.variable_length_bytes += value.len() as i64;
216 }
217 }
218 }
219 }
220
221 fn estimated_data_page_size(&self) -> usize {
226 match &self.encoder {
227 FallbackEncoderImpl::Plain { buffer, .. } => buffer.len(),
228 FallbackEncoderImpl::DeltaLength { buffer, lengths } => {
229 buffer.len() + lengths.estimated_data_encoded_size()
230 }
231 FallbackEncoderImpl::Delta {
232 buffer,
233 prefix_lengths,
234 suffix_lengths,
235 ..
236 } => {
237 buffer.len()
238 + prefix_lengths.estimated_data_encoded_size()
239 + suffix_lengths.estimated_data_encoded_size()
240 }
241 }
242 }
243
244 fn flush_data_page(
245 &mut self,
246 min_value: Option<ByteArray>,
247 max_value: Option<ByteArray>,
248 ) -> Result<DataPageValues<ByteArray>> {
249 let (buf, encoding) = match &mut self.encoder {
250 FallbackEncoderImpl::Plain { buffer } => (std::mem::take(buffer), Encoding::PLAIN),
251 FallbackEncoderImpl::DeltaLength { buffer, lengths } => {
252 let lengths = lengths.flush_buffer()?;
253
254 let mut out = Vec::with_capacity(lengths.len() + buffer.len());
255 out.extend_from_slice(&lengths);
256 out.extend_from_slice(buffer);
257 buffer.clear();
258 (out, Encoding::DELTA_LENGTH_BYTE_ARRAY)
259 }
260 FallbackEncoderImpl::Delta {
261 buffer,
262 prefix_lengths,
263 suffix_lengths,
264 last_value,
265 } => {
266 let prefix_lengths = prefix_lengths.flush_buffer()?;
267 let suffix_lengths = suffix_lengths.flush_buffer()?;
268
269 let mut out =
270 Vec::with_capacity(prefix_lengths.len() + suffix_lengths.len() + buffer.len());
271 out.extend_from_slice(&prefix_lengths);
272 out.extend_from_slice(&suffix_lengths);
273 out.extend_from_slice(buffer);
274 buffer.clear();
275 last_value.clear();
276 (out, Encoding::DELTA_BYTE_ARRAY)
277 }
278 };
279
280 let variable_length_bytes = Some(self.variable_length_bytes);
282 self.variable_length_bytes = 0;
283
284 Ok(DataPageValues {
285 buf: buf.into(),
286 num_values: std::mem::take(&mut self.num_values),
287 encoding,
288 min_value,
289 max_value,
290 nan_count: None,
291 variable_length_bytes,
292 })
293 }
294}
295
296#[derive(Debug, Default)]
298struct ByteArrayStorage {
299 page: Vec<u8>,
301
302 values: Vec<std::ops::Range<usize>>,
303}
304
305impl Storage for ByteArrayStorage {
306 type Key = u64;
307 type Value = [u8];
308
309 fn get(&self, idx: Self::Key) -> &Self::Value {
310 &self.page[self.values[idx as usize].clone()]
311 }
312
313 fn push(&mut self, value: &Self::Value) -> Self::Key {
314 let key = self.values.len();
315
316 self.page.reserve(4 + value.len());
317 self.page.extend_from_slice((value.len() as u32).as_bytes());
318
319 let start = self.page.len();
320 self.page.extend_from_slice(value);
321 self.values.push(start..self.page.len());
322
323 key as u64
324 }
325
326 #[allow(dead_code)] fn estimated_memory_size(&self) -> usize {
328 self.page.capacity() * std::mem::size_of::<u8>()
329 + self.values.capacity() * std::mem::size_of::<std::ops::Range<usize>>()
330 }
331}
332
333#[derive(Debug, Default)]
335struct DictEncoder {
336 interner: Interner<ByteArrayStorage>,
337 indices: Vec<u64>,
338 variable_length_bytes: i64,
339}
340
341impl DictEncoder {
342 fn encode<T>(&mut self, values: T, indices: impl ExactSizeIterator<Item = usize>)
344 where
345 T: ArrayAccessor + Copy,
346 T::Item: AsRef<[u8]>,
347 {
348 self.indices.reserve(indices.len());
349
350 for idx in indices {
351 let value = values.value(idx);
352 let interned = self.interner.intern(value.as_ref());
353 self.indices.push(interned);
354 self.variable_length_bytes += value.as_ref().len() as i64;
355 }
356 }
357
358 fn bit_width(&self) -> u8 {
359 let length = self.interner.storage().values.len();
360 num_required_bits(length.saturating_sub(1) as u64)
361 }
362
363 fn estimated_memory_size(&self) -> usize {
364 self.interner.estimated_memory_size() + self.indices.capacity() * std::mem::size_of::<u64>()
365 }
366
367 fn estimated_data_page_size(&self) -> usize {
368 let bit_width = self.bit_width();
369 1 + RleEncoder::max_buffer_size(bit_width, self.indices.len())
370 }
371
372 fn estimated_dict_page_size(&self) -> usize {
373 self.interner.storage().page.len()
374 }
375
376 fn flush_dict_page(self) -> DictionaryPage {
377 let storage = self.interner.into_inner();
378
379 DictionaryPage {
380 buf: storage.page.into(),
381 num_values: storage.values.len(),
382 is_sorted: false,
383 }
384 }
385
386 fn flush_data_page(
387 &mut self,
388 min_value: Option<ByteArray>,
389 max_value: Option<ByteArray>,
390 ) -> DataPageValues<ByteArray> {
391 let num_values = self.indices.len();
392 let buffer_len = self.estimated_data_page_size();
393 let mut buffer = Vec::with_capacity(buffer_len);
394 buffer.push(self.bit_width());
395
396 let mut encoder = RleEncoder::new_from_buf(self.bit_width(), buffer);
397 for index in &self.indices {
398 encoder.put(*index)
399 }
400
401 self.indices.clear();
402
403 let variable_length_bytes = Some(self.variable_length_bytes);
405 self.variable_length_bytes = 0;
406
407 DataPageValues {
408 buf: encoder.consume().into(),
409 num_values,
410 encoding: Encoding::RLE_DICTIONARY,
411 min_value,
412 max_value,
413 nan_count: None,
414 variable_length_bytes,
415 }
416 }
417}
418
419pub struct ByteArrayEncoder {
420 fallback: FallbackEncoder,
421 dict_encoder: Option<DictEncoder>,
422 statistics_enabled: EnabledStatistics,
423 min_value: Option<ByteArray>,
424 max_value: Option<ByteArray>,
425 bloom_filter: Option<Sbbf>,
426 bloom_filter_target_fpp: f64,
427 geo_stats_accumulator: Option<Box<dyn GeoStatsAccumulator>>,
428}
429
430impl ColumnValueEncoder for ByteArrayEncoder {
431 type T = ByteArray;
432 type Values = dyn Array;
433 fn flush_bloom_filter(&mut self) -> Option<Sbbf> {
434 let mut sbbf = self.bloom_filter.take()?;
435 sbbf.fold_to_target_fpp(self.bloom_filter_target_fpp);
436 Some(sbbf)
437 }
438
439 fn try_new(descr: &ColumnDescPtr, props: &WriterProperties) -> Result<Self>
440 where
441 Self: Sized,
442 {
443 let dictionary = props
444 .dictionary_enabled(descr.path())
445 .then(DictEncoder::default);
446
447 let fallback = FallbackEncoder::new(descr, props)?;
448
449 let (bloom_filter, bloom_filter_target_fpp) = create_bloom_filter(props, descr)?;
450
451 let statistics_enabled = props.statistics_enabled(descr.path());
452
453 let geo_stats_accumulator = try_new_geo_stats_accumulator(descr);
454
455 Ok(Self {
456 fallback,
457 statistics_enabled,
458 bloom_filter,
459 bloom_filter_target_fpp,
460 dict_encoder: dictionary,
461 min_value: None,
462 max_value: None,
463 geo_stats_accumulator,
464 })
465 }
466
467 fn write(&mut self, _values: &Self::Values, _offset: usize, _len: usize) -> Result<()> {
468 unreachable!("should call write_gather instead")
469 }
470
471 fn write_gather(&mut self, values: &Self::Values, indices: &[usize]) -> Result<()> {
472 downcast_op!(
473 values.data_type(),
474 values,
475 encode,
476 indices.iter().copied(),
477 self
478 );
479 Ok(())
480 }
481
482 fn count_values_within_byte_budget_gather(
483 values: &Self::Values,
484 indices: &[usize],
485 byte_budget: usize,
486 ) -> Option<usize> {
487 let count = match values.data_type() {
501 DataType::Utf8 => count_within_budget_offsets(
502 values.as_any().downcast_ref::<StringArray>().unwrap(),
503 indices,
504 byte_budget,
505 ),
506 DataType::LargeUtf8 => count_within_budget_offsets(
507 values.as_any().downcast_ref::<LargeStringArray>().unwrap(),
508 indices,
509 byte_budget,
510 ),
511 DataType::Binary => count_within_budget_offsets(
512 values.as_any().downcast_ref::<BinaryArray>().unwrap(),
513 indices,
514 byte_budget,
515 ),
516 DataType::LargeBinary => count_within_budget_offsets(
517 values.as_any().downcast_ref::<LargeBinaryArray>().unwrap(),
518 indices,
519 byte_budget,
520 ),
521 DataType::Utf8View => {
526 let array = values.as_any().downcast_ref::<StringViewArray>().unwrap();
527 count_within_budget_views(
528 array.views(),
529 indices,
530 byte_budget,
531 max_view_value_len(array.data_buffers()),
532 )
533 }
534 DataType::BinaryView => {
535 let array = values.as_any().downcast_ref::<BinaryViewArray>().unwrap();
536 count_within_budget_views(
537 array.views(),
538 indices,
539 byte_budget,
540 max_view_value_len(array.data_buffers()),
541 )
542 }
543 DataType::Dictionary(_, _) => indices.len(),
549 data_type => unreachable!("ByteArrayEncoder cannot be constructed for {data_type:?}"),
556 };
557 Some(count)
558 }
559
560 fn num_values(&self) -> usize {
561 match &self.dict_encoder {
562 Some(encoder) => encoder.indices.len(),
563 None => self.fallback.num_values,
564 }
565 }
566
567 fn has_dictionary(&self) -> bool {
568 self.dict_encoder.is_some()
569 }
570
571 fn estimated_memory_size(&self) -> usize {
572 let encoder_size = match &self.dict_encoder {
573 Some(encoder) => encoder.estimated_memory_size(),
574 None => self.fallback.estimated_data_page_size(),
577 };
578
579 let bloom_filter_size = self
580 .bloom_filter
581 .as_ref()
582 .map(|bf| bf.estimated_memory_size())
583 .unwrap_or_default();
584
585 let stats_size = self.min_value.as_ref().map(|v| v.len()).unwrap_or_default()
586 + self.max_value.as_ref().map(|v| v.len()).unwrap_or_default();
587
588 encoder_size + bloom_filter_size + stats_size
589 }
590
591 fn estimated_dict_page_size(&self) -> Option<usize> {
592 Some(self.dict_encoder.as_ref()?.estimated_dict_page_size())
593 }
594
595 fn estimated_data_page_size(&self) -> usize {
600 match &self.dict_encoder {
601 Some(encoder) => encoder.estimated_data_page_size(),
602 None => self.fallback.estimated_data_page_size(),
603 }
604 }
605
606 fn flush_dict_page(&mut self) -> Result<Option<DictionaryPage>> {
607 match self.dict_encoder.take() {
608 Some(encoder) => {
609 if !encoder.indices.is_empty() {
610 return Err(general_err!(
611 "Must flush data pages before flushing dictionary"
612 ));
613 }
614
615 Ok(Some(encoder.flush_dict_page()))
616 }
617 _ => Ok(None),
618 }
619 }
620
621 fn flush_data_page(&mut self) -> Result<DataPageValues<ByteArray>> {
622 let min_value = self.min_value.take();
623 let max_value = self.max_value.take();
624
625 match &mut self.dict_encoder {
626 Some(encoder) => Ok(encoder.flush_data_page(min_value, max_value)),
627 _ => self.fallback.flush_data_page(min_value, max_value),
628 }
629 }
630
631 fn flush_geospatial_statistics(&mut self) -> Option<Box<GeospatialStatistics>> {
632 self.geo_stats_accumulator.as_mut().map(|a| a.finish())?
633 }
634}
635
636fn encode<T, I>(values: T, indices: I, encoder: &mut ByteArrayEncoder)
640where
641 T: ArrayAccessor + Copy,
642 T::Item: Copy + Ord + AsRef<[u8]>,
643 I: ExactSizeIterator<Item = usize> + Clone,
644{
645 if encoder.statistics_enabled != EnabledStatistics::None {
646 if let Some(accumulator) = encoder.geo_stats_accumulator.as_mut() {
647 update_geo_stats_accumulator(accumulator.as_mut(), values, indices.clone());
648 } else if let Some((min, max)) = compute_min_max(values, indices.clone()) {
649 if encoder.min_value.as_ref().is_none_or(|m| m > &min) {
650 encoder.min_value = Some(min);
651 }
652
653 if encoder.max_value.as_ref().is_none_or(|m| m < &max) {
654 encoder.max_value = Some(max);
655 }
656 }
657 }
658
659 if let Some(bloom_filter) = &mut encoder.bloom_filter {
661 for idx in indices.clone() {
662 bloom_filter.insert(values.value(idx).as_ref());
663 }
664 }
665
666 match &mut encoder.dict_encoder {
667 Some(dict_encoder) => dict_encoder.encode(values, indices),
668 None => encoder.fallback.encode(values, indices),
669 }
670}
671
672fn max_view_value_len(buffers: &[Buffer]) -> usize {
674 const MAX_INLINE_VIEW_LEN: usize = 12;
676 buffers
681 .iter()
682 .map(|b| b.len())
683 .max()
684 .unwrap_or(0)
685 .max(MAX_INLINE_VIEW_LEN)
686}
687
688fn count_within_budget_views(
692 views: &[u128],
693 indices: &[usize],
694 byte_budget: usize,
695 max_value_len: usize,
696) -> usize {
697 let per_value = max_value_len + std::mem::size_of::<u32>();
708 if indices.len().saturating_mul(per_value) <= byte_budget {
709 return indices.len();
710 }
711 let mut cum: usize = 0;
714 for (i, idx) in indices.iter().enumerate() {
715 let len = (views[*idx] as u32) as usize;
716 cum = cum.saturating_add(len + std::mem::size_of::<u32>());
717 if cum > byte_budget {
718 return i + 1;
719 }
720 }
721 indices.len()
722}
723
724fn count_within_budget_offsets<T: ByteArrayType>(
731 values: &GenericByteArray<T>,
732 indices: &[usize],
733 byte_budget: usize,
734) -> usize {
735 if indices.is_empty() {
736 return 0;
737 }
738 let n = indices.len();
739 let first = indices[0];
740 let last = indices[n - 1];
741 let offsets = values.value_offsets();
742 let prefix_overhead = std::mem::size_of::<u32>();
744
745 if last >= first {
753 let payload = (offsets[last + 1] - offsets[first]).as_usize();
754 if payload + n * prefix_overhead <= byte_budget {
755 return n;
756 }
757 }
758
759 let mut cum: usize = 0;
761 for (i, idx) in indices.iter().enumerate() {
762 let len = (offsets[idx + 1] - offsets[*idx]).as_usize() + prefix_overhead;
763 cum = cum.saturating_add(len);
764 if cum > byte_budget {
765 return i + 1;
766 }
767 }
768 n
769}
770
771fn compute_min_max<T>(
775 array: T,
776 mut valid: impl Iterator<Item = usize>,
777) -> Option<(ByteArray, ByteArray)>
778where
779 T: ArrayAccessor,
780 T::Item: Copy + Ord + AsRef<[u8]>,
781{
782 let first_idx = valid.next()?;
783
784 let first_val = array.value(first_idx);
785 let mut min = first_val;
786 let mut max = first_val;
787 for idx in valid {
788 let val = array.value(idx);
789 min = min.min(val);
790 max = max.max(val);
791 }
792 Some((min.as_ref().to_vec().into(), max.as_ref().to_vec().into()))
793}
794
795fn update_geo_stats_accumulator<T>(
797 bounder: &mut dyn GeoStatsAccumulator,
798 array: T,
799 valid: impl Iterator<Item = usize>,
800) where
801 T: ArrayAccessor,
802 T::Item: Copy + Ord + AsRef<[u8]>,
803{
804 if bounder.is_valid() {
805 for idx in valid {
806 let val = array.value(idx);
807 bounder.update_wkb(val.as_ref());
808 }
809 }
810}