Skip to content

Commit

Permalink
Added raw_values to SliceMut
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgecarleitao committed Mar 5, 2022
1 parent e8ab681 commit 5ab661c
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
31 changes: 31 additions & 0 deletions odbc-api/src/buffers/column_with_indicator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,37 @@ impl<'a, T> NullableSliceMut<'a, T> {
pub fn len(&self) -> usize {
self.values.len()
}

/// Write access to the underlying raw value and indicator buffer.
///
/// The number of elements in the buffer is equal to `len`.
///
/// This method is useful for writing performant bindings to datastructures with similar binary
/// layout, as it allows for using memcopy rather than iterating over individual values.
///
/// # Example
///
/// ```
/// use odbc_api::{buffers::NullableSliceMut, sys::NULL_DATA};
///
/// // Memcopy the values into the buffer, and set indicators according to mask
/// // values.
/// fn copy_values_and_make_mask(new_values: &[i32], mask: &[bool], odbc_slice: &mut NullableSliceMut<i32>) {
/// let (values, indicators) = odbc_slice.raw_values();
/// values.copy_from_slice(new_values);
/// // Create array of bools indicating null values.
/// indicators.iter_mut().zip(mask.iter()).for_each(|(indicator, &mask)| {
/// *indicator = if mask {
/// 1
/// } else {
/// NULL_DATA
/// }
/// });
/// }
/// ```
pub fn raw_values(&mut self) -> (&mut [T], &mut [isize]) {
(&mut self.values, &mut self.indicators)
}
}

impl<'a, T> NullableSliceMut<'a, T> {
Expand Down
46 changes: 46 additions & 0 deletions odbc-api/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod common;

use odbc_sys::{SqlDataType, Timestamp};
use sys::NULL_DATA;
use tempfile::NamedTempFile;
use test_case::test_case;

Expand Down Expand Up @@ -769,6 +770,51 @@ fn columnar_insert_timestamp(profile: &Profile) {
assert_eq!(expected, actual);
}

/// Insert values into a i32 column using a columnar buffer's raw values
#[test_case(MSSQL; "Microsoft SQL Server")]
// #[test_case(MARIADB; "Maria DB")] No DATEIME2 type
// #[test_case(SQLITE_3; "SQLite 3")] default precision of 3 instead 7
fn columnar_insert_int_raw(profile: &Profile) {
let table_name = "Int";
// Setup
let conn = profile.setup_empty_table(table_name, &["INT"]).unwrap();

// Fill buffer with values
let desc = BufferDescription {
kind: BufferKind::I32,
nullable: true,
};
let mut buffer = buffer_from_description(10, iter::once(desc));

// Input values to insert.
let input_values = [1, 0, 3];
let mask = [true, false, true];

buffer.set_num_rows(input_values.len());
if let AnyColumnViewMut::NullableI32(mut writer) = buffer.column_mut(0) {
let (values, indicators) = writer.raw_values();
values.copy_from_slice(&input_values);
indicators
.iter_mut()
.zip(mask.iter())
.for_each(|(indicator, &mask)| *indicator = if mask { 1 } else { NULL_DATA })
} else {
panic!("Expected i32 column writer");
};

// Bind buffer and insert values.
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();

// Query values and compare with expectation
let actual = table_to_string(&conn, table_name, &["a"]);
let expected = "1\nNULL\n3";
assert_eq!(expected, actual);
}

/// Insert values into a DATETIME2(3) column using a columnar buffer. Milliseconds precision is
/// different from the default precision 7 (100ns).
#[test_case(MSSQL; "Microsoft SQL Server")]
Expand Down

0 comments on commit 5ab661c

Please sign in to comment.