diff --git a/examples/io_odbc.rs b/examples/io_odbc.rs index 996fa7130c6..9305fab6e24 100644 --- a/examples/io_odbc.rs +++ b/examples/io_odbc.rs @@ -1,4 +1,4 @@ -//! Example showing how to write to, and read from, an ODBC connector +//! Demo of how to write to, and read from, an ODBC connector //! //! On an Ubuntu, you need to run the following (to install the driver): //! ```bash @@ -28,13 +28,14 @@ fn main() -> Result<()> { let query = "INSERT INTO example (c1, c2) VALUES (?, ?)"; let prepared = connection.prepare(query).unwrap(); - // first, initialize buffers from odbc-api + // secondly, we initialize buffers from odbc-api let fields = vec![ // (for now) the types here must match the tables' schema Field::new("unused", DataType::Int32, true), Field::new("unused", DataType::LargeUtf8, true), ]; + // third, we initialize the writer let mut writer = write::Writer::try_new(prepared, fields)?; // say we have (or receive from a channel) a chunk: diff --git a/guide/src/io/README.md b/guide/src/io/README.md index f8e8bb8ca64..5a8cd477949 100644 --- a/guide/src/io/README.md +++ b/guide/src/io/README.md @@ -7,5 +7,6 @@ This crate offers optional features that enable interoperability with different * Parquet (`io_parquet`) * JSON and NDJSON (`io_json`) * Avro (`io_avro` and `io_avro_async`) +* ODBC-compliant databases (`io_odbc`) In this section you can find a guide and examples for each one of them. diff --git a/guide/src/io/odbc.md b/guide/src/io/odbc.md new file mode 100644 index 00000000000..7e362daf7c6 --- /dev/null +++ b/guide/src/io/odbc.md @@ -0,0 +1,8 @@ +# ODBC + +When compiled with feature `io_odbc`, this crate can be used to read from, and write to +any [ODBC](https://en.wikipedia.org/wiki/Open_Database_Connectivity) interface: + +```rust +{{#include ../../../examples/odbc.rs}} +``` diff --git a/src/io/odbc/write/mod.rs b/src/io/odbc/write/mod.rs index 18d2ebdbdaf..245f2455bb8 100644 --- a/src/io/odbc/write/mod.rs +++ b/src/io/odbc/write/mod.rs @@ -9,6 +9,8 @@ pub use schema::infer_descriptions; pub use serialize::serialize; /// Creates a [`api::buffers::ColumnarBuffer`] from [`api::ColumnDescription`]s. +/// +/// This is useful when separating the serialization (CPU-bounded) to writing to the DB (IO-bounded). pub fn buffer_from_description( descriptions: Vec, capacity: usize, @@ -23,7 +25,10 @@ pub fn buffer_from_description( api::buffers::buffer_from_description(capacity, descs) } -/// A writer of [`Chunk`] to an ODBC prepared statement. +/// A writer of [`Chunk`]s to an ODBC [`api::Prepared`] statement. +/// # Implementation +/// This struct mixes CPU-bounded and IO-bounded tasks and is not ideal +/// for an `async` context. pub struct Writer<'a> { fields: Vec, buffer: api::buffers::ColumnarBuffer, @@ -31,7 +36,9 @@ pub struct Writer<'a> { } impl<'a> Writer<'a> { - /// Creates a new [`Writer`] + /// Creates a new [`Writer`]. + /// # Errors + /// Errors iff any of the types from [`Field`] is not supported. pub fn try_new(prepared: api::Prepared<'a>, fields: Vec) -> Result { let buffer = buffer_from_description(infer_descriptions(&fields)?, 0); Ok(Self { @@ -41,7 +48,9 @@ impl<'a> Writer<'a> { }) } - /// Writes a chunk to the writter. + /// Writes a chunk to the writer. + /// # Errors + /// Errors iff the execution of the statement fails. pub fn write>(&mut self, chunk: &Chunk) -> Result<()> { if chunk.len() > self.buffer.num_rows() { // if the chunk is larger, we re-allocate new buffers to hold it diff --git a/src/io/odbc/write/schema.rs b/src/io/odbc/write/schema.rs index 546c6d2b427..9e4b61f704e 100644 --- a/src/io/odbc/write/schema.rs +++ b/src/io/odbc/write/schema.rs @@ -3,7 +3,7 @@ use super::super::api; use crate::datatypes::{DataType, Field}; use crate::error::{ArrowError, Result}; -/// Infers the [`ColumnDescription`] from the fields +/// Infers the [`api::ColumnDescription`] from the fields pub fn infer_descriptions(fields: &[Field]) -> Result> { fields .iter() diff --git a/src/io/odbc/write/serialize.rs b/src/io/odbc/write/serialize.rs index f3a68fc1de2..3128ceb964b 100644 --- a/src/io/odbc/write/serialize.rs +++ b/src/io/odbc/write/serialize.rs @@ -121,12 +121,13 @@ fn bool(array: &BooleanArray, values: &mut [api::Bit]) { } fn bool_optional(array: &BooleanArray, values: &mut NullableSliceMut) { + let (values, indicators) = values.raw_values(); array .values() .iter() - .zip(values.values().iter_mut()) + .zip(values.iter_mut()) .for_each(|(from, to)| *to = api::Bit(from as u8)); - write_validity(array.validity(), values.indicators()); + write_validity(array.validity(), indicators); } fn primitive(array: &PrimitiveArray, values: &mut [T]) { @@ -145,8 +146,9 @@ fn write_validity(validity: Option<&Bitmap>, indicators: &mut [isize]) { } fn primitive_optional(array: &PrimitiveArray, values: &mut NullableSliceMut) { - values.values().copy_from_slice(array.values()); - write_validity(array.validity(), values.indicators()); + let (values, indicators) = values.raw_values(); + values.copy_from_slice(array.values()); + write_validity(array.validity(), indicators); } fn fixed_binary(array: &FixedSizeBinaryArray, writer: &mut BinColumnWriter) {