Skip to content

Commit

Permalink
add index to column allocation buffer error
Browse files Browse the repository at this point in the history
  • Loading branch information
pacman82 committed Apr 24, 2022
1 parent d1747bf commit 4f41ba7
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 22 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.38.0

* Add column buffer index to allocation errors to provide more context

## 0.37.0

* Add fallibale allocation of binary columns.
Expand Down
2 changes: 1 addition & 1 deletion odbc-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "odbc-api"
version = "0.37.0"
version = "0.38.0"
authors = ["Markus Klein"]
edition = "2021"
license = "MIT"
Expand Down
16 changes: 10 additions & 6 deletions odbc-api/src/buffers/any_column_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{collections::HashSet, ffi::c_void};
use odbc_sys::{CDataType, Date, Time, Timestamp};

use crate::{
error::TooLargeBufferSize,
handles::{CData, CDataMut, HasDataType},
Bit, DataType, Error,
};
Expand Down Expand Up @@ -59,7 +60,10 @@ pub enum AnyColumnBuffer {

impl AnyColumnBuffer {
/// Map buffer description to actual buffer.
pub fn from_description(max_rows: usize, desc: BufferDescription) -> Result<Self, Error> {
pub fn from_description(
max_rows: usize,
desc: BufferDescription,
) -> Result<Self, TooLargeBufferSize> {
let buffer = match (desc.kind, desc.nullable) {
(BufferKind::Binary { length }, _) => {
AnyColumnBuffer::Binary(BinColumn::new(max_rows as usize, length)?)
Expand Down Expand Up @@ -270,11 +274,10 @@ pub fn buffer_from_description(
let mut column_index = 0;
let columns = descs
.map(move |desc| {
let buffer = AnyColumnBuffer::from_description(capacity, desc)
.map_err(|source| source.add_context(column_index))?;
column_index += 1;
Ok((
column_index,
AnyColumnBuffer::from_description(capacity, desc)?,
))
Ok((column_index, buffer))
})
.collect::<Result<_, _>>()?;
Ok(unsafe { ColumnarBuffer::new_unchecked(capacity, columns) })
Expand All @@ -292,7 +295,8 @@ pub fn buffer_from_description_and_indices(
.map(|(col_index, buffer_desc)| {
Ok((
col_index,
AnyColumnBuffer::from_description(max_rows, buffer_desc)?,
AnyColumnBuffer::from_description(max_rows, buffer_desc)
.map_err(|source| source.add_context(col_index - 1))?,
))
})
.collect::<Result<_, _>>()?;
Expand Down
11 changes: 6 additions & 5 deletions odbc-api/src/buffers/bin_column.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{
buffers::Indicator,
error::TooLargeBufferSize,
handles::{CData, CDataMut, HasDataType},
DataType, Error,
DataType,
};

use log::debug;
Expand All @@ -26,7 +27,7 @@ pub struct BinColumn {
impl BinColumn {
/// This will allocate a value and indicator buffer for `batch_size` elements. Each value may
/// have a maximum length of `max_len`.
pub fn new(batch_size: usize, element_size: usize) -> Result<Self, Error> {
pub fn new(batch_size: usize, element_size: usize) -> Result<Self, TooLargeBufferSize> {
// Use a fallibale allocation for creating the buffer. In applications often the max_len
// size of the buffer, might be directly inspired by the maximum size of the type, as
// reported, by ODBC. Which might get exceedingly large for types like VARBINARY(MAX), or
Expand All @@ -35,7 +36,7 @@ impl BinColumn {
let mut values = Vec::new();
values
.try_reserve_exact(len)
.map_err(|_| Error::TooLargeColumnBufferSize {
.map_err(|_| TooLargeBufferSize {
num_elements: batch_size,
element_size,
})?;
Expand Down Expand Up @@ -454,7 +455,7 @@ unsafe impl CDataMut for BinColumn {

#[cfg(test)]
mod test {
use crate::Error;
use crate::error::TooLargeBufferSize;

use super::BinColumn;

Expand All @@ -465,7 +466,7 @@ mod test {
let error = result.unwrap_err();
assert!(matches!(
error,
Error::TooLargeColumnBufferSize {
TooLargeBufferSize {
num_elements: 10_000,
element_size: 2_147_483_648
}
Expand Down
14 changes: 12 additions & 2 deletions odbc-api/src/buffers/columnar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,16 @@ impl TextRowSet {
let max_str_len = max_str_len
.map(|limit| min(limit, reported_len))
.unwrap_or(reported_len);
Ok((col_index, TextColumn::new(batch_size, max_str_len)?))
Ok((
col_index,
TextColumn::new(batch_size, max_str_len).map_err(|source| {
Error::TooLargeColumnBufferSize {
buffer_index: col_index - 1,
num_elements: source.num_elements,
element_size: source.element_size,
}
})?,
))
})
.collect::<Result<_, Error>>()?;
Ok(TextRowSet {
Expand All @@ -432,7 +441,8 @@ impl TextRowSet {
.map(|(index, max_str_len)| {
Ok((
(index + 1).try_into().unwrap(),
TextColumn::new(row_capacity, max_str_len)?,
TextColumn::new(row_capacity, max_str_len)
.map_err(|source| source.add_context(index.try_into().unwrap()))?,
))
})
.collect::<Result<_, _>>()?;
Expand Down
7 changes: 4 additions & 3 deletions odbc-api/src/buffers/text_column.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
error::TooLargeBufferSize,
handles::{CData, CDataMut, HasDataType},
DataType, Error,
DataType,
};

use super::{ColumnBuffer, ColumnProjections, Indicator};
Expand Down Expand Up @@ -41,7 +42,7 @@ impl<C> TextColumn<C> {
/// This will allocate a value and indicator buffer for `batch_size` elements. Each value may
/// have a maximum length of `max_str_len`. This implies that `max_str_len` is increased by
/// one in order to make space for the null terminating zero at the end of strings.
pub fn new(batch_size: usize, max_str_len: usize) -> Result<Self, Error>
pub fn new(batch_size: usize, max_str_len: usize) -> Result<Self, TooLargeBufferSize>
where
C: Default + Copy,
{
Expand All @@ -55,7 +56,7 @@ impl<C> TextColumn<C> {
let mut values = Vec::new();
values
.try_reserve_exact(len)
.map_err(|_| Error::TooLargeColumnBufferSize {
.map_err(|_| TooLargeBufferSize {
num_elements: batch_size,
// We want the element size in bytes
element_size: element_size * size_of::<C>(),
Expand Down
24 changes: 24 additions & 0 deletions odbc-api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ use thiserror::Error as ThisError;

use crate::handles::{log_diagnostics, AsHandle, Record as DiagnosticRecord, SqlResult};

/// Error indicating a failed allocation for a column buffer
#[derive(Debug)]
pub struct TooLargeBufferSize {
/// Number of elements supposed to be in the buffer.
pub num_elements: usize,
/// Element size in the buffer in bytes.
pub element_size: usize,
}

impl TooLargeBufferSize {
/// Map the column allocation error to an [`odbc_api::Error`] adding the context of which column
/// caused the allocation error.
pub fn add_context(self, buffer_index: u16) -> Error {
Error::TooLargeColumnBufferSize {
buffer_index,
num_elements: self.num_elements,
element_size: self.element_size,
}
}
}

#[derive(Debug, ThisError)]
/// Error type used to indicate a low level ODBC call returned with SQL_ERROR.
pub enum Error {
Expand Down Expand Up @@ -82,6 +103,9 @@ pub enum Error {
{element_size}."
)]
TooLargeColumnBufferSize {
/// Zero based column buffer index. Note that this is different from the 1 based column
/// index.
buffer_index: u16,
num_elements: usize,
element_size: usize,
},
Expand Down
2 changes: 1 addition & 1 deletion odbc-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub use self::{
cursor::{Cursor, CursorImpl, CursorRow, RowSetBuffer, RowSetCursor},
driver_complete_option::DriverCompleteOption,
environment::{DataSourceInfo, DriverInfo, Environment},
error::Error,
error::{Error, TooLargeBufferSize},
fixed_sized::Bit,
handles::{ColumnDescription, DataType, Nullability},
into_parameter::IntoParameter,
Expand Down
4 changes: 2 additions & 2 deletions odbcsv/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "odbcsv"
version = "0.3.67"
version = "0.3.68"
authors = ["Markus Klein"]
edition = "2021"
license = "MIT"
Expand Down Expand Up @@ -29,7 +29,7 @@ readme = "Readme.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
odbc-api = { version = "0.37.0", path = "../odbc-api" }
odbc-api = { version = "0.38.0", path = "../odbc-api" }
csv = "1.1.6"
anyhow = "1.0.57"
stderrlog = "0.5.1"
Expand Down
4 changes: 4 additions & 0 deletions odbcsv/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.3.68

* Update dependencies

## 0.3.67

* Do not panic if allocation of column buffers fails. Gracefully abort instead, freeing allocated resources.
Expand Down

0 comments on commit 4f41ba7

Please sign in to comment.