From 3879b3b2cf9e33fcc57e5e4e30e01e94a30f5dea Mon Sep 17 00:00:00 2001 From: Raphael Taylor-Davies Date: Wed, 12 Jan 2022 15:01:26 +0000 Subject: [PATCH] Preserve dictionary encoding from parquet (#171) --- parquet/src/arrow/array_reader.rs | 56 +-- .../array_reader/byte_array_dictionary.rs | 319 ++++++++++++++++++ .../arrow/array_reader/dictionary_buffer.rs | 184 ++++++++++ parquet/src/arrow/arrow_reader.rs | 97 ++++-- parquet/src/arrow/record_reader/buffer.rs | 4 + 5 files changed, 600 insertions(+), 60 deletions(-) create mode 100644 parquet/src/arrow/array_reader/byte_array_dictionary.rs create mode 100644 parquet/src/arrow/array_reader/dictionary_buffer.rs diff --git a/parquet/src/arrow/array_reader.rs b/parquet/src/arrow/array_reader.rs index 4586d00596ec..11a078bdbfae 100644 --- a/parquet/src/arrow/array_reader.rs +++ b/parquet/src/arrow/array_reader.rs @@ -56,11 +56,10 @@ use arrow::datatypes::{ use arrow::util::bit_util; use crate::arrow::converter::{ - BinaryArrayConverter, BinaryConverter, Converter, DecimalArrayConverter, - DecimalConverter, FixedLenBinaryConverter, FixedSizeArrayConverter, - Int96ArrayConverter, Int96Converter, IntervalDayTimeArrayConverter, - IntervalDayTimeConverter, IntervalYearMonthArrayConverter, - IntervalYearMonthConverter, Utf8ArrayConverter, Utf8Converter, + Converter, DecimalArrayConverter, DecimalConverter, FixedLenBinaryConverter, + FixedSizeArrayConverter, Int96ArrayConverter, Int96Converter, + IntervalDayTimeArrayConverter, IntervalDayTimeConverter, + IntervalYearMonthArrayConverter, IntervalYearMonthConverter, }; use crate::arrow::record_reader::buffer::{ScalarValue, ValuesBuffer}; use crate::arrow::record_reader::{GenericRecordReader, RecordReader}; @@ -70,8 +69,8 @@ use crate::column::page::PageIterator; use crate::column::reader::decoder::ColumnValueDecoder; use crate::column::reader::ColumnReaderImpl; use crate::data_type::{ - BoolType, ByteArrayType, DataType, DoubleType, FixedLenByteArrayType, FloatType, - Int32Type, Int64Type, Int96Type, + BoolType, DataType, DoubleType, FixedLenByteArrayType, FloatType, Int32Type, + Int64Type, Int96Type, }; use crate::errors::{ParquetError, ParquetError::ArrowError, Result}; use crate::file::reader::{FilePageIterator, FileReader}; @@ -81,9 +80,12 @@ use crate::schema::types::{ use crate::schema::visitor::TypeVisitor; mod byte_array; +mod byte_array_dictionary; +mod dictionary_buffer; mod offset_buffer; pub use byte_array::make_byte_array_reader; +pub use byte_array_dictionary::make_byte_array_dictionary_reader; /// Array reader reads parquet data into arrow array. pub trait ArrayReader { @@ -271,7 +273,8 @@ where .clone(), }; - let record_reader = RecordReader::::new_with_options(column_desc.clone(), null_mask_only); + let record_reader = + RecordReader::::new_with_options(column_desc.clone(), null_mask_only); Ok(Self { data_type, @@ -1754,35 +1757,12 @@ impl<'a> ArrayReaderBuilder { )?, )), PhysicalType::BYTE_ARRAY => match arrow_type { - // TODO: Replace with optimised dictionary reader (#171) - Some(ArrowType::Dictionary(_, _)) => { - match cur_type.get_basic_info().converted_type() { - ConvertedType::UTF8 => { - let converter = Utf8Converter::new(Utf8ArrayConverter {}); - Ok(Box::new(ComplexObjectArrayReader::< - ByteArrayType, - Utf8Converter, - >::new( - page_iterator, - column_desc, - converter, - arrow_type, - )?)) - } - _ => { - let converter = BinaryConverter::new(BinaryArrayConverter {}); - Ok(Box::new(ComplexObjectArrayReader::< - ByteArrayType, - BinaryConverter, - >::new( - page_iterator, - column_desc, - converter, - arrow_type, - )?)) - } - } - } + Some(ArrowType::Dictionary(_, _)) => make_byte_array_dictionary_reader( + page_iterator, + column_desc, + arrow_type, + null_mask_only, + ), _ => make_byte_array_reader( page_iterator, column_desc, @@ -1996,7 +1976,7 @@ mod tests { use crate::arrow::schema::parquet_to_arrow_schema; use crate::basic::{Encoding, Type as PhysicalType}; use crate::column::page::{Page, PageReader}; - use crate::data_type::{ByteArray, DataType, Int32Type, Int64Type}; + use crate::data_type::{ByteArray, ByteArrayType, DataType, Int32Type, Int64Type}; use crate::errors::Result; use crate::file::reader::{FileReader, SerializedFileReader}; use crate::schema::parser::parse_message_type; diff --git a/parquet/src/arrow/array_reader/byte_array_dictionary.rs b/parquet/src/arrow/array_reader/byte_array_dictionary.rs new file mode 100644 index 000000000000..36a5c0a8ee93 --- /dev/null +++ b/parquet/src/arrow/array_reader/byte_array_dictionary.rs @@ -0,0 +1,319 @@ +use std::any::Any; +use std::marker::PhantomData; +use std::ops::Range; +use std::sync::Arc; + +use arrow::array::{ArrayData, ArrayDataBuilder, ArrayRef, OffsetSizeTrait}; +use arrow::buffer::Buffer; +use arrow::datatypes::{ArrowNativeType, DataType as ArrowType}; + +use crate::arrow::array_reader::dictionary_buffer::{DictOrValues, DictionaryBuffer}; +use crate::arrow::array_reader::{ + byte_array::{ByteArrayDecoder, ByteArrayDecoderPlain}, + offset_buffer::OffsetBuffer, +}; +use crate::arrow::array_reader::{read_records, ArrayReader}; +use crate::arrow::record_reader::buffer::{BufferQueue, ScalarValue}; +use crate::arrow::record_reader::GenericRecordReader; +use crate::arrow::schema::parquet_to_arrow_field; +use crate::basic::{ConvertedType, Encoding}; +use crate::column::page::PageIterator; +use crate::column::reader::decoder::ColumnValueDecoder; +use crate::encodings::rle::RleDecoder; +use crate::errors::{ParquetError, Result}; +use crate::schema::types::ColumnDescPtr; +use crate::util::bit_util::FromBytes; +use crate::util::memory::ByteBufferPtr; + +macro_rules! make_reader { + ( + ($pages:expr, $column_desc:expr, $data_type:expr, $null_mask_only:expr) => match ($k:expr, $v:expr) { + $(($key_arrow:pat, $value_arrow:pat) => ($key_type:ty, $value_type:ty),)+ + } + ) => { + match (($k, $v)) { + $( + ($key_arrow, $value_arrow) => { + let reader = GenericRecordReader::new_with_options( + $column_desc, + $null_mask_only, + ); + Ok(Box::new(ByteArrayDictionaryReader::<$key_type, $value_type>::new( + $pages, $data_type, reader, + ))) + } + )+ + _ => Err(general_err!( + "unsupported data type for byte array dictionary reader - {}", + $data_type + )), + } + } +} + +pub fn make_byte_array_dictionary_reader( + pages: Box, + column_desc: ColumnDescPtr, + arrow_type: Option, + null_mask_only: bool, +) -> Result> { + // Check if Arrow type is specified, else create it from Parquet type + let data_type = match arrow_type { + Some(t) => t, + None => parquet_to_arrow_field(column_desc.as_ref())? + .data_type() + .clone(), + }; + + match &data_type { + ArrowType::Dictionary(key_type, value_type) => { + make_reader! { + (pages, column_desc, data_type, null_mask_only) => match (key_type.as_ref(), value_type.as_ref()) { + (ArrowType::UInt8, ArrowType::Binary | ArrowType::Utf8) => (u8, i32), + (ArrowType::UInt8, ArrowType::LargeBinary | ArrowType::LargeUtf8) => (u8, i64), + (ArrowType::Int8, ArrowType::Binary | ArrowType::Utf8) => (i8, i32), + (ArrowType::Int8, ArrowType::LargeBinary | ArrowType::LargeUtf8) => (i8, i64), + (ArrowType::UInt16, ArrowType::Binary | ArrowType::Utf8) => (u16, i32), + (ArrowType::UInt16, ArrowType::LargeBinary | ArrowType::LargeUtf8) => (u16, i64), + (ArrowType::Int16, ArrowType::Binary | ArrowType::Utf8) => (i16, i32), + (ArrowType::Int16, ArrowType::LargeBinary | ArrowType::LargeUtf8) => (i16, i64), + (ArrowType::UInt32, ArrowType::Binary | ArrowType::Utf8) => (u32, i32), + (ArrowType::UInt32, ArrowType::LargeBinary | ArrowType::LargeUtf8) => (u32, i64), + (ArrowType::Int32, ArrowType::Binary | ArrowType::Utf8) => (i32, i32), + (ArrowType::Int32, ArrowType::LargeBinary | ArrowType::LargeUtf8) => (i32, i64), + (ArrowType::UInt64, ArrowType::Binary | ArrowType::Utf8) => (u64, i32), + (ArrowType::UInt64, ArrowType::LargeBinary | ArrowType::LargeUtf8) => (u64, i64), + (ArrowType::Int64, ArrowType::Binary | ArrowType::Utf8) => (i64, i32), + (ArrowType::Int64, ArrowType::LargeBinary | ArrowType::LargeUtf8) => (i64, i64), + } + } + } + _ => Err(general_err!( + "invalid non-dictionary data type for byte array dictionary reader - {}", + data_type + )), + } +} + +struct ByteArrayDictionaryReader { + data_type: ArrowType, + pages: Box, + def_levels_buffer: Option, + rep_levels_buffer: Option, + record_reader: GenericRecordReader, DictionaryDecoder>, +} + +impl ByteArrayDictionaryReader +where + K: FromBytes + ScalarValue + ArrowNativeType, + V: ScalarValue + OffsetSizeTrait, +{ + fn new( + pages: Box, + data_type: ArrowType, + record_reader: GenericRecordReader< + DictionaryBuffer, + DictionaryDecoder, + >, + ) -> Self { + Self { + data_type, + pages, + def_levels_buffer: None, + rep_levels_buffer: None, + record_reader, + } + } +} + +impl ArrayReader for ByteArrayDictionaryReader +where + K: FromBytes + ScalarValue + ArrowNativeType, + V: ScalarValue + OffsetSizeTrait, +{ + fn as_any(&self) -> &dyn Any { + self + } + + fn get_data_type(&self) -> &ArrowType { + &self.data_type + } + + fn next_batch(&mut self, batch_size: usize) -> Result { + read_records(&mut self.record_reader, self.pages.as_mut(), batch_size)?; + let buffer = self.record_reader.consume_record_data()?; + let null_buffer = self.record_reader.consume_bitmap_buffer()?; + let array = buffer.into_array(null_buffer, &self.data_type); + + self.def_levels_buffer = self.record_reader.consume_def_levels()?; + self.rep_levels_buffer = self.record_reader.consume_rep_levels()?; + self.record_reader.reset(); + + Ok(array) + } + + fn get_def_levels(&self) -> Option<&[i16]> { + self.def_levels_buffer + .as_ref() + .map(|buf| unsafe { buf.typed_data() }) + } + + fn get_rep_levels(&self) -> Option<&[i16]> { + self.rep_levels_buffer + .as_ref() + .map(|buf| unsafe { buf.typed_data() }) + } +} + +enum MaybeRLEDecoder { + RLE(RleDecoder), + Fallback(ByteArrayDecoder), +} + +struct DictionaryDecoder { + /// The current dictionary + dict: Option>, + + /// Dictionary decoder + decoder: Option, + + validate_utf8: bool, + + value_type: ArrowType, + + phantom: PhantomData<(K, V)>, +} + +impl ColumnValueDecoder for DictionaryDecoder +where + K: FromBytes + ScalarValue + ArrowNativeType, + V: ScalarValue + OffsetSizeTrait, +{ + type Slice = DictionaryBuffer; + + fn new(col: &ColumnDescPtr) -> Self { + let validate_utf8 = col.converted_type() == ConvertedType::UTF8; + + let value_type = + match (V::is_large(), col.converted_type() == ConvertedType::UTF8) { + (true, true) => ArrowType::LargeUtf8, + (true, false) => ArrowType::LargeBinary, + (false, true) => ArrowType::Utf8, + (false, false) => ArrowType::Binary, + }; + + Self { + dict: None, + decoder: None, + validate_utf8, + value_type, + phantom: Default::default(), + } + } + + fn set_dict( + &mut self, + buf: ByteBufferPtr, + num_values: u32, + encoding: Encoding, + _is_sorted: bool, + ) -> Result<()> { + if !matches!( + encoding, + Encoding::PLAIN | Encoding::RLE_DICTIONARY | Encoding::PLAIN_DICTIONARY + ) { + return Err(nyi_err!( + "Invalid/Unsupported encoding type for dictionary: {}", + encoding + )); + } + + let len = num_values as usize; + let mut buffer = OffsetBuffer::::default(); + let mut decoder = + ByteArrayDecoderPlain::new(buf, len, Some(len), self.validate_utf8); + decoder.read(&mut buffer, usize::MAX)?; + + let data = ArrayDataBuilder::new(self.value_type.clone()) + .len(buffer.len()) + .add_buffer(buffer.offsets.into()) + .add_buffer(buffer.values.into()) + .build()?; + + self.dict = Some(Arc::new(data)); + Ok(()) + } + + fn set_data( + &mut self, + encoding: Encoding, + data: ByteBufferPtr, + num_levels: usize, + num_values: Option, + ) -> Result<()> { + let decoder = match encoding { + Encoding::RLE_DICTIONARY => { + let bit_width = data[0]; + let mut decoder = RleDecoder::new(bit_width); + decoder.set_data(data.start_from(1)); + MaybeRLEDecoder::RLE(decoder) + } + _ => MaybeRLEDecoder::Fallback(ByteArrayDecoder::new( + encoding, + data, + num_levels, + num_values, + self.validate_utf8, + )?), + }; + + self.decoder = Some(decoder); + Ok(()) + } + + fn read(&mut self, out: &mut Self::Slice, range: Range) -> Result { + let len = range.end - range.start; + match self.decoder.as_mut().expect("decoder set") { + MaybeRLEDecoder::Fallback(decoder) => { + let output = out.data.get_or_insert_with(|| DictOrValues::Values { + values: Default::default(), + }); + decoder.read(output.spill_values()?, range.end - range.start, None) + } + MaybeRLEDecoder::RLE(decoder) => { + let dict = self.dict.as_ref().expect("dictionary set"); + assert_eq!(dict.data_type(), &self.value_type); + + let output = out.data.get_or_insert_with(|| DictOrValues::Dict { + keys: Default::default(), + values: Arc::clone(dict), + }); + + match output { + DictOrValues::Dict { keys, values } if Arc::ptr_eq(values, dict) => { + // Happy path - can just copy keys + decoder.get_batch(keys.spare_capacity_mut(len)) + } + _ => { + // Sad path - need to recompute dictionary + let values = output.spill_values()?; + let mut keys = vec![K::default(); len]; + let len = decoder.get_batch(&mut keys)?; + + assert_eq!(dict.data_type(), &self.value_type); + let dict_offsets = unsafe { dict.buffers()[0].typed_data::() }; + let dict_values = &dict.buffers()[1].as_slice(); + + values.extend_from_dictionary( + &keys[..len], + dict_offsets, + dict_values, + )?; + + Ok(len) + } + } + } + } + } +} diff --git a/parquet/src/arrow/array_reader/dictionary_buffer.rs b/parquet/src/arrow/array_reader/dictionary_buffer.rs new file mode 100644 index 000000000000..7cfa73ff8706 --- /dev/null +++ b/parquet/src/arrow/array_reader/dictionary_buffer.rs @@ -0,0 +1,184 @@ +use crate::arrow::array_reader::offset_buffer::OffsetBuffer; +use crate::arrow::record_reader::buffer::{ + BufferQueue, ScalarBuffer, ScalarValue, ValuesBuffer, +}; +use crate::column::reader::decoder::ValuesBufferSlice; +use crate::errors::Result; +use arrow::array::{make_array, ArrayData, ArrayDataBuilder, ArrayRef, OffsetSizeTrait}; +use arrow::buffer::Buffer; +use arrow::datatypes::{ArrowNativeType, DataType as ArrowType}; +use std::sync::Arc; + +pub enum DictOrValues { + Dict { + keys: ScalarBuffer, + values: Arc, + }, + Values { + values: OffsetBuffer, + }, +} + +impl + DictOrValues +{ + pub fn spill_values(&mut self) -> Result<&mut OffsetBuffer> { + match self { + Self::Values { values } => Ok(values), + Self::Dict { keys, values } => { + let mut spilled = OffsetBuffer::default(); + let dict_offsets = unsafe { values.buffers()[0].typed_data::() }; + let dict_values = &values.buffers()[1].as_slice(); + + spilled.extend_from_dictionary( + keys.as_slice(), + dict_offsets, + dict_values, + )?; + + *self = Self::Values { values: spilled }; + match self { + Self::Values { values } => Ok(values), + _ => unreachable!(), + } + } + } + } +} + +#[derive(Default)] +pub struct DictionaryBuffer { + /// Whilst decoding we may encounter new dictionaries or pages + /// that aren't dictionary encoded + /// + /// The output buffer therefore needs to be able to accommodate this + pub data: Option>, +} + +impl DictionaryBuffer { + pub fn len(&self) -> usize { + match &self.data { + Some(DictOrValues::Dict { keys, .. }) => keys.len(), + Some(DictOrValues::Values { values }) => values.len(), + None => 0, + } + } + + pub fn into_array( + self, + null_buffer: Option, + data_type: &ArrowType, + ) -> ArrayRef { + match self.data { + Some(DictOrValues::Dict { keys, values }) => { + let mut builder = ArrayDataBuilder::new(data_type.clone()) + .len(keys.len()) + .add_buffer(keys.into()) + .add_child_data(values.as_ref().clone()); + + if let Some(buffer) = null_buffer { + builder = builder.null_bit_buffer(buffer); + } + + let data = match cfg!(debug_assertions) { + true => builder.build().unwrap(), + false => unsafe { builder.build_unchecked() }, + }; + + make_array(data) + } + Some(DictOrValues::Values { values }) => { + let value_type = match data_type { + ArrowType::Dictionary(_, v) => v.as_ref().clone(), + _ => unreachable!(), + }; + + arrow::compute::cast( + &values.into_array(null_buffer, value_type), + data_type, + ) + .expect("cast should be infallible") + } + None => Arc::new(make_array(ArrayData::new_empty(data_type))), + } + } +} + +impl ValuesBufferSlice for DictionaryBuffer { + fn capacity(&self) -> usize { + usize::MAX + } +} + +impl ValuesBuffer + for DictionaryBuffer +{ + fn pad_nulls( + &mut self, + read_offset: usize, + values_read: usize, + levels_read: usize, + rev_valid_position_iter: impl Iterator, + ) { + match self.data.as_mut() { + Some(DictOrValues::Dict { keys, .. }) => { + keys.resize(read_offset + levels_read); + keys.pad_nulls( + read_offset, + values_read, + levels_read, + rev_valid_position_iter, + ) + } + Some(DictOrValues::Values { values, .. }) => values.pad_nulls( + read_offset, + values_read, + levels_read, + rev_valid_position_iter, + ), + None => {} + } + } +} + +impl BufferQueue + for DictionaryBuffer +{ + type Output = Self; + type Slice = Self; + + fn split_off(&mut self, len: usize) -> Self::Output { + let ret = match &mut self.data { + Some(DictOrValues::Dict { keys, values }) => Self { + data: Some(DictOrValues::Dict { + keys: keys.take(len), + values: values.clone(), + }), + }, + Some(DictOrValues::Values { values }) => Self { + data: Some(DictOrValues::Values { + values: values.split_off(len), + }), + }, + None => Self { data: None }, + }; + + if self.len() == 0 { + self.data = None + } + + ret + } + + fn spare_capacity_mut(&mut self, _batch_size: usize) -> &mut Self::Slice { + self + } + + fn set_len(&mut self, len: usize) { + match &mut self.data { + Some(DictOrValues::Dict { keys, .. }) => keys.set_len(len), + Some(DictOrValues::Values { values }) => values.set_len(len), + None => assert_eq!(len, 0), + } + } +} diff --git a/parquet/src/arrow/arrow_reader.rs b/parquet/src/arrow/arrow_reader.rs index 476cf08b4ade..01eef9a3a3b0 100644 --- a/parquet/src/arrow/arrow_reader.rs +++ b/parquet/src/arrow/arrow_reader.rs @@ -423,13 +423,13 @@ mod tests { RandUtf8Gen, >(2, ConvertedType::NONE, None, &converter, encodings); - let converter = Utf8ArrayConverter {}; + let utf8_converter = Utf8ArrayConverter {}; run_single_column_reader_tests::< ByteArrayType, StringArray, Utf8ArrayConverter, RandUtf8Gen, - >(2, ConvertedType::UTF8, None, &converter, encodings); + >(2, ConvertedType::UTF8, None, &utf8_converter, encodings); run_single_column_reader_tests::< ByteArrayType, @@ -440,27 +440,11 @@ mod tests { 2, ConvertedType::UTF8, Some(ArrowDataType::Utf8), - &converter, + &utf8_converter, encodings, ); - run_single_column_reader_tests::< - ByteArrayType, - StringArray, - Utf8ArrayConverter, - RandUtf8Gen, - >( - 2, - ConvertedType::UTF8, - Some(ArrowDataType::Dictionary( - Box::new(ArrowDataType::Int32), - Box::new(ArrowDataType::Utf8), - )), - &converter, - encodings, - ); - - let converter = LargeUtf8ArrayConverter {}; + let large_utf8_converter = LargeUtf8ArrayConverter {}; run_single_column_reader_tests::< ByteArrayType, LargeStringArray, @@ -470,9 +454,78 @@ mod tests { 2, ConvertedType::UTF8, Some(ArrowDataType::LargeUtf8), - &converter, + &large_utf8_converter, encodings, ); + + let small_key_types = [ArrowDataType::Int8, ArrowDataType::UInt8]; + for key in &small_key_types { + for encoding in encodings { + let mut opts = TestOptions::new(2, 20, 15).with_null_percent(50); + opts.encoding = *encoding; + + // Cannot run full test suite as keys overflow, run small test instead + single_column_reader_test::< + ByteArrayType, + StringArray, + Utf8ArrayConverter, + RandUtf8Gen, + >( + opts, + 2, + ConvertedType::UTF8, + Some(ArrowDataType::Dictionary( + Box::new(key.clone()), + Box::new(ArrowDataType::Utf8), + )), + &utf8_converter, + ); + } + } + + let key_types = [ + ArrowDataType::Int16, + ArrowDataType::UInt16, + ArrowDataType::Int32, + ArrowDataType::UInt32, + ArrowDataType::Int64, + ArrowDataType::UInt64, + ]; + + for key in &key_types { + run_single_column_reader_tests::< + ByteArrayType, + StringArray, + Utf8ArrayConverter, + RandUtf8Gen, + >( + 2, + ConvertedType::UTF8, + Some(ArrowDataType::Dictionary( + Box::new(key.clone()), + Box::new(ArrowDataType::Utf8), + )), + &utf8_converter, + encodings, + ); + + // https://github.com/apache/arrow-rs/issues/1179 + // run_single_column_reader_tests::< + // ByteArrayType, + // LargeStringArray, + // LargeUtf8ArrayConverter, + // RandUtf8Gen, + // >( + // 2, + // ConvertedType::UTF8, + // Some(ArrowDataType::Dictionary( + // Box::new(key.clone()), + // Box::new(ArrowDataType::LargeUtf8), + // )), + // &large_utf8_converter, + // encodings + // ); + } } #[test] @@ -792,7 +845,7 @@ mod tests { } } assert_eq!(a.data_type(), b.data_type()); - assert_eq!(a.data(), b.data()); + assert_eq!(a.data(), b.data(), "{:#?} vs {:#?}", a.data(), b.data()); total_read = end; } else { diff --git a/parquet/src/arrow/record_reader/buffer.rs b/parquet/src/arrow/record_reader/buffer.rs index 5c69dfad43aa..ff97d9b19afe 100644 --- a/parquet/src/arrow/record_reader/buffer.rs +++ b/parquet/src/arrow/record_reader/buffer.rs @@ -75,8 +75,12 @@ pub trait BufferQueue: Sized { pub trait ScalarValue {} impl ScalarValue for bool {} impl ScalarValue for u8 {} +impl ScalarValue for i8 {} +impl ScalarValue for u16 {} impl ScalarValue for i16 {} +impl ScalarValue for u32 {} impl ScalarValue for i32 {} +impl ScalarValue for u64 {} impl ScalarValue for i64 {} impl ScalarValue for f32 {} impl ScalarValue for f64 {}